From dfb7c45b900613816cd351870ff8fd80c8b7d590 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Sun, 21 Sep 2025 10:44:48 +0200 Subject: [PATCH] Compiler: add support for generalized slice syntax * slice syntax is a[start?:count?] or a[start?:count?:step] * bit selections are mutated during analysis as `BitSelectionExpr` * array slices are not supported in expressions yet --- analyser/conversion_checker_expr.c2 | 11 +- analyser/module_analyser_expr.c2 | 150 ++++++++++-------- analyser/module_analyser_unaryop.c2 | 15 +- ast/array_selection_expr.c2 | 111 +++++++++++++ ast/ast_evaluator.c2 | 32 ++-- ast/bit_selection_expr.c2 | 105 ++++++++++++ ast/bitoffset_expr.c2 | 89 ----------- ast/expr.c2 | 45 ++++-- ast/stmt.c2 | 3 +- ast/utils.c2 | 3 +- common/ast_builder.c2 | 12 +- generator/ast_visitor_expr.c2 | 18 ++- generator/c/c_generator_expr.c2 | 47 +++--- generator/c/dep_finder.c2 | 14 +- generator/c2i/c2i_generator_expr.c2 | 26 ++- generator/ir/ir_generator.c2 | 3 +- generator/ir/ir_generator_expr.c2 | 10 +- parser/c2_parser_expr.c2 | 25 ++- recipe.txt | 3 +- test/expr/bitoffset/bitoffset_invalid_base.c2 | 11 +- .../expr/bitoffset/bitoffset_invalid_range.c2 | 4 +- 21 files changed, 485 insertions(+), 252 deletions(-) create mode 100644 ast/array_selection_expr.c2 create mode 100644 ast/bit_selection_expr.c2 delete mode 100644 ast/bitoffset_expr.c2 diff --git a/analyser/conversion_checker_expr.c2 b/analyser/conversion_checker_expr.c2 index 7b9d9725d..31a6b291f 100644 --- a/analyser/conversion_checker_expr.c2 +++ b/analyser/conversion_checker_expr.c2 @@ -81,15 +81,20 @@ fn ExprWidth getExprWidth(const Expr* e) { // TODO ToContainer -> width = 64 break; case ArraySubscript: - // TODO BitOffset -> specific width - fallthrough; case Member: return getTypeWidth(e.getType()); case Paren: const ParenExpr* p = (ParenExpr*)e; return getExprWidth(p.getInner()); - case BitOffset: + case ArraySelection: break; + case BitSelection: + // if the bit selection is fixed, the value range could + // be computed. + //result.width = ((BitSelectionExpr*)e).getWidth(); + //result.is_signed = false; + //return result; + return getTypeWidth(e.getType()); case ExplicitCast: // TODO: explicit cast may reduce signed values return getTypeWidth(e.getType()); diff --git a/analyser/module_analyser_expr.c2 b/analyser/module_analyser_expr.c2 index 21deec68d..9779a07a1 100644 --- a/analyser/module_analyser_expr.c2 +++ b/analyser/module_analyser_expr.c2 @@ -115,7 +115,12 @@ fn QualType Analyser.analyseExprInner(Analyser* ma, Expr** e_ptr, u32 side) { e.copyValType(inner); e.setEffect(inner.hasEffect()); return qt; - case BitOffset: + case ArraySelection: + // analyse and potentially convert to BitSelection + return ma.analyseArraySelectionExpr(e_ptr, side); + case BitSelection: + // should already be analysed, hence type is already set + assert(0); break; case ExplicitCast: return ma.analyseExplicitCast(e_ptr); @@ -493,16 +498,6 @@ fn QualType Analyser.analyseArraySubscriptExpr(Analyser* ma, Expr** e_ptr, u32 s // Derefence alias types Expr* index = sub.getIndex(); - if (index.isBitOffset()) { - if (side & LHS) { - ma.errorRange(e.getLoc(), e.getRange(), "bitoffset cannot be used as left hand side expression"); - return QualType_Invalid; - } - Expr* base = sub.getBase(); - q = ma.analyseBitOffsetExpr(q, base, index); - e.combineConstantFlags(base, index); - return q; - } q = q.getCanonicalType(); @@ -565,49 +560,80 @@ fn QualType Analyser.analyseArraySubscriptExpr(Analyser* ma, Expr** e_ptr, u32 s return pt.getInner(); } -fn QualType Analyser.analyseBitOffsetExpr(Analyser* ma, QualType ltype, Expr* base, Expr* e) { - BitOffsetExpr* bo = (BitOffsetExpr*)e; - QualType canon = ltype.getCanonicalType(); - - BuiltinType* bi = canon.getBuiltin(); - if (!canon.isBuiltin() || !bi.isUnsigned()) { - ma.error(base.getLoc(), "bitoffsets are only allowed on unsigned integer type"); - return QualType_Invalid; - } +fn QualType Analyser.analyseArraySelectionExpr(Analyser* ma, Expr** e_ptr, u32 side) { + // TODO: convert to BitSelection if lhs is a builtin type + // otherwise reject if lhs is neither an array nor a pointer + // a[b] is an LValue if not an array itself + Expr* e = *e_ptr; + ArraySelectionExpr* sub = (ArraySelectionExpr*)e; - Value lval; - Value rval; - bool lvalid = ma.analyseBitOffsetIndex(bo.getLHS2(), canon, &lval); - bool rvalid = ma.analyseBitOffsetIndex(bo.getRHS2(), canon, &rval); + Expr* orig = sub.getBase(); // save orig (might be wrapped in ImplicitCast(ArrayToPointerDecay) + // array[..] = .. also mark array as used for read (RHS) + QualType ltype = ma.analyseExpr(sub.getBase2(), true, side | RHS); + if (ltype.isInvalid()) return ltype; - if (lvalid && rvalid) { - if (lval.is_less(&rval)) { - ma.error(e.getLoc(), "left bitoffset index is smaller than right index"); + Expr* base = sub.getBase(); + QualType canon = ltype.getCanonicalType(); + BuiltinType* bi = canon.getBuiltin(); + if (canon.isBuiltin()) { + if (!bi.isUnsigned()) { + ma.error(base.getLoc(), "bitoffsets are only allowed on unsigned integer types"); return QualType_Invalid; } - - Value width = lval.minus(&rval); - u64 w = width.as_u64() + 1; - if (w <= 8) { - ltype = getBuiltinQT(UInt8); - } else if (w <= 16) { - ltype = getBuiltinQT(UInt16); - } else if (w <= 32) { - ltype = getBuiltinQT(UInt32); - } else { - ltype = getBuiltinQT(UInt64); + if (!sub.getIndex() || !sub.getCount()) { + ma.error(e.getLoc(), "bitoffsets must have explicit high and low offsets"); + return QualType_Invalid; + } + if (sub.getStep()) { + ma.error(sub.getStep().getLoc(), "bitoffsets cannot have step values"); + return QualType_Invalid; + } + if (side & LHS) { + // TODO: support that + ma.errorRange(e.getLoc(), e.getRange(), "bitoffset cannot be used as left hand side expression"); + return QualType_Invalid; } - bo.setWidth((u8)w); - e.setType(ltype); - } + i32 lval = -1; + i32 rval = -1; + i32 width = -1; + bool lvalid = ma.analyseSelectionIndex(sub.getIndex2(), canon, &lval); + bool rvalid = ma.analyseSelectionIndex(sub.getCount2(), canon, &rval); + if (!lvalid || !rvalid) + return QualType_Invalid; - e.combineConstantFlags(bo.getLHS(), bo.getRHS()); + if (lval >= 0 && rval >= 0) { + if (lval < rval) { + // TODO: accept boundaries in reverse order + ma.error(e.getLoc(), "left bitoffset index %d is smaller than right index %d", lval, rval); + return QualType_Invalid; + } - return ltype; + width = lval - rval + 1; + // TODO: this is semantically incorrect as ltype should not depend on the offsets being constant + if (width <= 8) { + ltype = getBuiltinQT(UInt8); + } else if (width <= 16) { + ltype = getBuiltinQT(UInt16); + } else if (width <= 32) { + ltype = getBuiltinQT(UInt32); + } else { + ltype = getBuiltinQT(UInt64); + } + } + e = ma.builder.actOnBitSelectionExpr(e.getLoc(), sub.getSrcLen(), base, sub.getIndex(), sub.getCount(), width); + e.setType(ltype); + e.combineConstantFlags(base, sub.getIndex()); + //e.combineConstantFlags(base, sub.getCount()); //??? + *e_ptr = e; + return ltype; + } + // TODO: check for pointers and arrays + ma.errorRange(e.getLoc(), e.getRange(), "array selections are not supported yet"); + return QualType_Invalid; } -fn bool Analyser.analyseBitOffsetIndex(Analyser* ma, Expr** e_ptr, QualType baseType, Value* result) { +fn bool Analyser.analyseSelectionIndex(Analyser* ma, Expr** e_ptr, QualType baseType, i32* result) { BuiltinType* base_bi = baseType.getBuiltin(); QualType qt = ma.analyseExpr(e_ptr, true, RHS); @@ -622,26 +648,26 @@ fn bool Analyser.analyseBitOffsetIndex(Analyser* ma, Expr** e_ptr, QualType base return false; } - // TODO only allow CTV expressions - if (!e.isCtv()) return false; - - Value val = ast.evalExpr(e); - if (val.isNegative()) { - ma.errorRange(e.getLoc(), e.getRange(), "bitoffset index value '%s' is negative", val.str()); - return false; - } - if (val.isFloat()) { - return false; - } + if (e.isCtv()) { + Value val = ast.evalExpr(e); + if (val.isNegative()) { + ma.errorRange(e.getLoc(), e.getRange(), "bitoffset index value '%s' is negative", val.str()); + return false; + } + if (val.isFloat()) { + return false; + } - // accept shifting 1 << 31 - if (val.as_u64() >= base_bi.getWidth()) { - ma.errorRange(e.getLoc(), e.getRange(), "bitoffset index value '%s' too large for type '%s'", val.str(), baseType.diagName()); - return false; + // accept shifting 1 << 31 + u64 val64 = val.as_u64(); + if (val64 >= base_bi.getWidth()) { + ma.errorRange(e.getLoc(), e.getRange(), "bitoffset index value '%s' too large for type '%s'", val.str(), baseType.diagName()); + return false; + } + *result = (i32)val64; + } else { + *result = -1; } - - *result = val; - return true; } diff --git a/analyser/module_analyser_unaryop.c2 b/analyser/module_analyser_unaryop.c2 index 34a5ba7c3..c3f19605b 100644 --- a/analyser/module_analyser_unaryop.c2 +++ b/analyser/module_analyser_unaryop.c2 @@ -240,21 +240,20 @@ fn IdentifierKind getInnerExprAddressOf(const Expr* e) { case Builtin: break; case ArraySubscript: - ArraySubscriptExpr* a = (ArraySubscriptExpr*)e; - // a[b] is an LValue if not an array itself and if b is not a BitOffset + // a[b] is an LValue if not an array itself // TODO: reject if a[b] is itself an array - Expr* index = a.getIndex(); - if (index.getKind() != BitOffset) - return Var; - break; + //ArraySubscriptExpr* a = (ArraySubscriptExpr*)e; + return Var; case Member: MemberExpr* m = (MemberExpr*)e; return m.getIdentifierKind(); case Paren: ParenExpr* p = (ParenExpr*)e; return getInnerExprAddressOf(p.getInner()); - case BitOffset: - return Unresolved; + case ArraySelection: + case BitSelection: + // TODO: taking the address of a selection is rejected + break; case ExplicitCast: ExplicitCastExpr* c = (ExplicitCastExpr*)e; // TODO: this seems incorrect, casts should not produce LValues diff --git a/ast/array_selection_expr.c2 b/ast/array_selection_expr.c2 new file mode 100644 index 000000000..10ffd120b --- /dev/null +++ b/ast/array_selection_expr.c2 @@ -0,0 +1,111 @@ +/* Copyright 2022-2025 Bas van den Berg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module ast; + +import ast_context; +import string_buffer; +import src_loc local; + +type ArraySelectionExprBits struct { + u32 : NumExprBits; + u32 src_len : 32 - NumExprBits; +} + +public type ArraySelectionExpr struct @(opaque) { + // Note: loc is that of left bracket + Expr base; + Expr* lhs; + Expr* idx; + Expr* count; + Expr* step; +} + +public fn ArraySelectionExpr* ArraySelectionExpr.create(ast_context.Context* c, + SrcLoc loc, u32 src_len, + Expr* lhs, Expr* idx, Expr* count, Expr* step) +{ + ArraySelectionExpr* e = c.alloc(sizeof(ArraySelectionExpr)); + e.base.init(ExprKind.ArraySelection, loc, 0, 0, 0, ValType.LValue); + e.base.base.arraySelectionExprBits.src_len = src_len; + e.lhs = lhs; + e.idx = idx; + e.count = count; + e.step = step; +#if AstStatistics + Stats.addExpr(ExprKind.ArraySelection, sizeof(ArraySelectionExpr)); +#endif + return e; +} + +fn Expr* ArraySelectionExpr.instantiate(ArraySelectionExpr* e, Instantiator* inst) { + ArraySelectionExpr* a = ArraySelectionExpr.create(inst.c, + e.base.base.loc, + e.base.base.arraySelectionExprBits.src_len, + e.lhs.instantiate(inst), + e.idx ? e.idx.instantiate(inst) : nil, + e.count ? e.count.instantiate(inst) : nil, + e.step ? e.step.instantiate(inst) : nil); + return (Expr*)a; +} + +public fn Expr* ArraySelectionExpr.getBase(const ArraySelectionExpr* e) { return e.lhs; } +public fn Expr** ArraySelectionExpr.getBase2(ArraySelectionExpr* e) { return &e.lhs; } + +public fn Expr* ArraySelectionExpr.getIndex(const ArraySelectionExpr* e) { return e.idx; } +public fn Expr** ArraySelectionExpr.getIndex2(ArraySelectionExpr* e) { return &e.idx; } + +public fn Expr* ArraySelectionExpr.getCount(const ArraySelectionExpr* e) { return e.count; } +public fn Expr** ArraySelectionExpr.getCount2(ArraySelectionExpr* e) { return &e.count; } + +public fn Expr* ArraySelectionExpr.getStep(const ArraySelectionExpr* e) { return e.step; } +//public fn Expr** ArraySelectionExpr.getStep2(ArraySelectionExpr* e) { return &e.step; } + +fn SrcLoc ArraySelectionExpr.getStartLoc(const ArraySelectionExpr* e) { + return e.getBase().getStartLoc(); +} + +public fn u32 ArraySelectionExpr.getSrcLen(const ArraySelectionExpr* e) { + return e.base.base.arraySelectionExprBits.src_len; +} + +fn SrcLoc ArraySelectionExpr.getEndLoc(const ArraySelectionExpr* e) { + return e.base.base.loc + e.base.base.arraySelectionExprBits.src_len; +} + +fn void ArraySelectionExpr.printLiteral(const ArraySelectionExpr* e, string_buffer.Buf* out) { + e.lhs.printLiteral(out); + out.add1('['); + if (e.idx) e.idx.printLiteral(out); + out.add(" : "); + if (e.count) e.count.printLiteral(out); + out.add(" : "); + if (e.step) e.step.printLiteral(out); + out.add1(']'); +} + +fn void ArraySelectionExpr.print(const ArraySelectionExpr* e, string_buffer.Buf* out, u32 indent) { + e.base.printKind(out, indent); + e.base.printTypeBits(out); + out.newline(); + e.lhs.print(out, indent + 1); + if (e.idx) e.idx.print(out, indent + 1); + else { out.indent(indent + 1); out.print("(no index)\n"); } + if (e.count) e.count.print(out, indent + 1); + else { out.indent(indent + 1); out.print("(no count)\n"); } + if (e.step) e.step.print(out, indent + 1); + else { out.indent(indent + 1); out.print("(no step)\n"); } +} + diff --git a/ast/ast_evaluator.c2 b/ast/ast_evaluator.c2 index c24f19411..942ba397d 100644 --- a/ast/ast_evaluator.c2 +++ b/ast/ast_evaluator.c2 @@ -80,31 +80,27 @@ fn Value Evaluator.get_value(Evaluator* eval, const Expr* e) { const BuiltinExpr* bi = (BuiltinExpr*)e; return Value.createUnsigned(bi.getValue()); case ArraySubscript: // a[high:low], both included - // note: can be CTV if BitOffsetExpr - ArraySubscriptExpr* a = (ArraySubscriptExpr*)e; - Expr* index = a.getIndex(); - if (!index.isBitOffset()) break; - Value base = eval.get_value(a.getBase()); - // Dont allow negative/float - if (!base.isInteger() || base.isNegative()) return Value.error("invalid bitoffset"); - BitOffsetExpr* bo = (BitOffsetExpr*)index; - Value high = eval.get_value(bo.getLHS()); - Value low = eval.get_value(bo.getRHS()); - Value width = high.minus(&low); - width.incr(); - // calculate base = (base >> low) & bitmask(width)) - base = base.right_shift(&low); - base.mask(width.as_u32()); - return base; + break; case Member: const MemberExpr* m = (MemberExpr*)e; return eval.get_decl_value(m.getFullDecl()); case Paren: const ParenExpr* p = (ParenExpr*)e; return eval.get_value(p.getInner()); - case BitOffset: - assert(0); // Handled in ArraySubscript + case ArraySelection: break; + case BitSelection: // a[high:low], both included + BitSelectionExpr* bs = (BitSelectionExpr*)e; + Value base = eval.get_value(bs.getBase()); + // Dont allow negative/float + if (!base.isInteger() || base.isNegative()) return Value.error("invalid bitoffset"); + Value high = eval.get_value(bs.getHigh()); + Value low = eval.get_value(bs.getLow()); + u32 width = high.as_u32() - low.as_u32() + 1; + // calculate base = (base >> low) & bitmask(width)) + base = base.right_shift(&low); + base.mask(width); + return base; case ExplicitCast: const ExplicitCastExpr* i = (ExplicitCastExpr*)e; Value inner = eval.get_value(i.getInner()); diff --git a/ast/bit_selection_expr.c2 b/ast/bit_selection_expr.c2 new file mode 100644 index 000000000..966c13c41 --- /dev/null +++ b/ast/bit_selection_expr.c2 @@ -0,0 +1,105 @@ +/* Copyright 2022-2025 Bas van den Berg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +module ast; + +import ast_context; +import string_buffer; +import src_loc local; + +type BitSelectionExprBits struct { + u32 : NumExprBits; + i32 width : 8; // in bits + u32 src_len : 32 - NumExprBits - 8; +} + +public type BitSelectionExpr struct @(opaque) { + // Note: loc is that of left bracket + Expr base; + Expr* lhs; + Expr* high; + Expr* low; +} + +public fn BitSelectionExpr* BitSelectionExpr.create(ast_context.Context* c, + SrcLoc loc, u32 src_len, + Expr* lhs, Expr* high, Expr* low, i32 width) +{ + BitSelectionExpr* e = c.alloc(sizeof(BitSelectionExpr)); + e.base.init(ExprKind.BitSelection, loc, 0, 0, 0, ValType.LValue); + e.base.base.bitSelectionExprBits.src_len = src_len; + e.base.base.bitSelectionExprBits.width = width; + e.lhs = lhs; + e.high = high; + e.low = low; +#if AstStatistics + Stats.addExpr(ExprKind.BitSelection, sizeof(BitSelectionExpr)); +#endif + return e; +} + +fn Expr* BitSelectionExpr.instantiate(BitSelectionExpr* e, Instantiator* inst) { + BitSelectionExpr* a = BitSelectionExpr.create(inst.c, + e.base.base.loc, + e.base.base.bitSelectionExprBits.src_len, + e.lhs.instantiate(inst), + e.high.instantiate(inst), + e.low.instantiate(inst), + e.base.base.bitSelectionExprBits.width); + return (Expr*)a; +} + +public fn Expr* BitSelectionExpr.getBase(const BitSelectionExpr* e) { return e.lhs; } +//public fn Expr** BitSelectionExpr.getBase2(BitSelectionExpr* e) { return &e.lhs; } + +public fn Expr* BitSelectionExpr.getHigh(const BitSelectionExpr* e) { return e.high; } +//public fn Expr** BitSelectionExpr.getHigh2(BitSelectionExpr* e) { return &e.high; } + +public fn Expr* BitSelectionExpr.getLow(const BitSelectionExpr* e) { return e.low; } +//public fn Expr** BitSelectionExpr.getLow2(BitSelectionExpr* e) { return &e.low; } + +public fn i32 BitSelectionExpr.getWidth(const BitSelectionExpr* e) { + return e.base.base.bitSelectionExprBits.width; +} + +fn SrcLoc BitSelectionExpr.getStartLoc(const BitSelectionExpr* e) { + return e.getBase().getStartLoc(); +} + +fn SrcLoc BitSelectionExpr.getEndLoc(const BitSelectionExpr* e) { + return e.base.base.loc + e.base.base.bitSelectionExprBits.src_len; +} + +fn void BitSelectionExpr.printLiteral(const BitSelectionExpr* e, string_buffer.Buf* out) { + e.lhs.printLiteral(out); + out.add1('['); + e.high.printLiteral(out); + out.add1(':'); + e.low.printLiteral(out); + out.add1(']'); +} + +fn void BitSelectionExpr.print(const BitSelectionExpr* e, string_buffer.Buf* out, u32 indent) { + e.base.printKind(out, indent); + e.base.printTypeBits(out); + out.space(); + out.color(col_Calc); + out.print("width=%d", e.base.base.bitSelectionExprBits.width); + out.newline(); + e.lhs.print(out, indent + 1); + e.high.print(out, indent + 1); + e.low.print(out, indent + 1); +} + diff --git a/ast/bitoffset_expr.c2 b/ast/bitoffset_expr.c2 deleted file mode 100644 index 6a69a9fa9..000000000 --- a/ast/bitoffset_expr.c2 +++ /dev/null @@ -1,89 +0,0 @@ -/* Copyright 2022-2026 Bas van den Berg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -module ast; - -import ast_context; -import string_buffer; -import src_loc local; - -type BitOffsetExprBits struct { - u32 : NumExprBits; - u32 width : 8; // in bits -} - -public type BitOffsetExpr struct @(opaque) { - Expr base; - Expr* lhs; - Expr* rhs; -} - -public fn BitOffsetExpr* BitOffsetExpr.create(ast_context.Context* c, SrcLoc loc, Expr* lhs, Expr* rhs) { - BitOffsetExpr* e = c.alloc(sizeof(BitOffsetExpr)); - e.base.init(BitOffset, loc, 0, 0, 0, RValue); - e.lhs = lhs; - e.rhs = rhs; -#if AstStatistics - Stats.addExpr(BitOffset, sizeof(BitOffsetExpr)); -#endif - return e; -} - -fn Expr* BitOffsetExpr.instantiate(BitOffsetExpr* e, Instantiator* inst) { - return (Expr*)BitOffsetExpr.create(inst.c, - e.base.base.loc, - e.lhs.instantiate(inst), - e.rhs.instantiate(inst)); -} - -public fn Expr* BitOffsetExpr.getLHS(const BitOffsetExpr* e) { return e.lhs; } -public fn Expr** BitOffsetExpr.getLHS2(BitOffsetExpr* e) { return &e.lhs; } - -public fn Expr* BitOffsetExpr.getRHS(const BitOffsetExpr* e) { return e.rhs; } -public fn Expr** BitOffsetExpr.getRHS2(BitOffsetExpr* e) { return &e.rhs; } - -public fn void BitOffsetExpr.setWidth(BitOffsetExpr* e, u8 width) { - e.base.base.bitOffsetBits.width = width; -} - -public fn u32 BitOffsetExpr.getWidth(const BitOffsetExpr* e) { - return e.base.base.bitOffsetBits.width; -} - -fn SrcLoc BitOffsetExpr.getStartLoc(const BitOffsetExpr* e) { - return e.lhs.getStartLoc(); -} - -fn SrcLoc BitOffsetExpr.getEndLoc(const BitOffsetExpr* e) { - return e.rhs.getEndLoc(); -} - -fn void BitOffsetExpr.printLiteral(const BitOffsetExpr* e, string_buffer.Buf* out) { - e.lhs.printLiteral(out); - out.add1(':'); - e.rhs.printLiteral(out); -} - -fn void BitOffsetExpr.print(const BitOffsetExpr* e, string_buffer.Buf* out, u32 indent) { - e.base.printKind(out, indent); - e.base.printTypeBits(out); - out.space(); - out.color(col_Calc); - out.print("%d", e.base.base.bitOffsetBits.width); - out.newline(); - e.lhs.print(out, indent + 1); - e.rhs.print(out, indent + 1); -} - diff --git a/ast/expr.c2 b/ast/expr.c2 index f7b1c1a85..728dab37f 100644 --- a/ast/expr.c2 +++ b/ast/expr.c2 @@ -38,7 +38,8 @@ public type ExprKind enum u8 (const char* const name) { ArraySubscript : { "ArraySubscript" }, Member : { "Member" }, Paren : { "Paren" }, - BitOffset : { "BitOffset" }, + ArraySelection : { "ArraySelection" }, + BitSelection : { "BitSelection" }, ExplicitCast : { "ExplicitCast" }, ImplicitCast : { "ImplicitCast" }, Range : { "RangeExpr" }, @@ -140,8 +141,10 @@ fn Expr* Expr.instantiate(Expr* e, Instantiator* inst) { return MemberExpr.instantiate((MemberExpr*)e, inst); case Paren: return ParenExpr.instantiate((ParenExpr*)e, inst); - case BitOffset: - return BitOffsetExpr.instantiate((BitOffsetExpr*)e, inst); + case ArraySelection: + return ArraySelectionExpr.instantiate((ArraySelectionExpr*)e, inst); + case BitSelection: + return BitSelectionExpr.instantiate((BitSelectionExpr*)e, inst); case ExplicitCast: return ExplicitCastExpr.instantiate((ExplicitCastExpr*)e, inst); case ImplicitCast: @@ -246,10 +249,6 @@ public fn bool Expr.isArrayDesignatedInit(const Expr* e) { return e.getKind() == ArrayDesignatedInit; } -public fn bool Expr.isBitOffset(const Expr* e) { - return e.getKind() == BitOffset; -} - fn bool Expr.isParen(const Expr* e) { return e.getKind() == Paren; } @@ -422,7 +421,8 @@ public fn bool Expr.isPositiveOrUB(const Expr* e) { break; case Paren: return ((ParenExpr*)e).getInner().isPositiveOrUB(); - case BitOffset: + case ArraySelection: + case BitSelection: break; case ExplicitCast: case ImplicitCast: @@ -469,8 +469,10 @@ public fn SrcLoc Expr.getStartLoc(const Expr* e) { return ((MemberExpr*)e).getStartLoc(); case Paren: break; - case BitOffset: - return ((BitOffsetExpr*)e).getStartLoc(); + case ArraySelection: + return ((ArraySelectionExpr*)e).getStartLoc(); + case BitSelection: + return ((BitSelectionExpr*)e).getStartLoc(); case ExplicitCast: break; case ImplicitCast: @@ -525,8 +527,10 @@ public fn SrcLoc Expr.getEndLoc(const Expr* e) { return ((MemberExpr*)e).getEndLoc(); case Paren: return ((ParenExpr*)e).getEndLoc(); - case BitOffset: - return ((BitOffsetExpr*)e).getEndLoc(); + case ArraySelection: + return ((ArraySelectionExpr*)e).getEndLoc(); + case BitSelection: + return ((BitSelectionExpr*)e).getEndLoc(); case ExplicitCast: return ((ExplicitCastExpr*)e).getEndLoc(); case ImplicitCast: @@ -578,7 +582,8 @@ public fn bool Expr.needsSemi(const Expr* e) { case ArraySubscript: case Member: case Paren: - case BitOffset: + case ArraySelection: + case BitSelection: case ExplicitCast: case ImplicitCast: case Range: @@ -658,8 +663,11 @@ fn void Expr.print(const Expr* e, string_buffer.Buf* out, u32 indent) { case Paren: ParenExpr.print((ParenExpr*)e, out, indent); break; - case BitOffset: - BitOffsetExpr.print((BitOffsetExpr*)e, out, indent); + case ArraySelection: + ArraySelectionExpr.print((ArraySelectionExpr*)e, out, indent); + break; + case BitSelection: + BitSelectionExpr.print((BitSelectionExpr*)e, out, indent); break; case ExplicitCast: ExplicitCastExpr.print((ExplicitCastExpr*)e, out, indent); @@ -739,8 +747,11 @@ public fn void Expr.printLiteral(const Expr* e, string_buffer.Buf* out) { case Paren: ParenExpr.printLiteral((ParenExpr*)e, out); return; - case BitOffset: - BitOffsetExpr.printLiteral((BitOffsetExpr*)e, out); + case ArraySelection: + ArraySelectionExpr.printLiteral((ArraySelectionExpr*)e, out); + return; + case BitSelection: + BitSelectionExpr.printLiteral((BitSelectionExpr*)e, out); return; case ExplicitCast: ExplicitCastExpr.printLiteral((ExplicitCastExpr*)e, out); diff --git a/ast/stmt.c2 b/ast/stmt.c2 index 17d8cefae..2e906a795 100644 --- a/ast/stmt.c2 +++ b/ast/stmt.c2 @@ -57,9 +57,10 @@ public type Stmt struct @(opaque) { ExprBits exprBits; ArrayDesignatedInitExprBits arrayDesignatedInitExprBits; + ArraySelectionExprBits arraySelectionExprBits; ArraySubscriptExprBits arraySubscriptExprBits; BinaryOperatorBits binaryOperatorBits; - BitOffsetExprBits bitOffsetBits; + BitSelectionExprBits bitSelectionExprBits; BooleanLiteralBits booleanLiteralBits; BuiltinExprBits builtinExprBits; CallExprBits callExprBits; diff --git a/ast/utils.c2 b/ast/utils.c2 index 56d8f0818..4094db088 100644 --- a/ast/utils.c2 +++ b/ast/utils.c2 @@ -68,9 +68,10 @@ static_assert(24, sizeof(StringLiteral)); static_assert(24, sizeof(TypeExpr)); static_assert(24, sizeof(UnaryOperator)); static_assert(32, sizeof(ArrayDesignatedInitExpr)); +static_assert(48, sizeof(ArraySelectionExpr)); static_assert(32, sizeof(ArraySubscriptExpr)); static_assert(32, sizeof(BinaryOperator)); -static_assert(32, sizeof(BitOffsetExpr)); +static_assert(40, sizeof(BitSelectionExpr)); static_assert(32, sizeof(CallExpr)); static_assert(40, sizeof(ExplicitCastExpr)); static_assert(32, sizeof(BuiltinExpr)); diff --git a/common/ast_builder.c2 b/common/ast_builder.c2 index 29af336a8..b4265f7dc 100644 --- a/common/ast_builder.c2 +++ b/common/ast_builder.c2 @@ -1163,14 +1163,18 @@ public fn Expr* Builder.actOnTypeExpr(Builder* b, SrcLoc loc, u32 src_len, const return (Expr*)TypeExpr.create(b.context, loc, src_len, ref); } -public fn Expr* Builder.actOnBitOffsetExpr(Builder* b, SrcLoc loc, Expr* lhs, Expr* rhs) { - return (Expr*)BitOffsetExpr.create(b.context, loc, lhs, rhs); -} - public fn Expr* Builder.actOnArraySubscriptExpr(Builder* b, SrcLoc loc, u32 src_len, Expr* base, Expr* idx) { return (Expr*)ArraySubscriptExpr.create(b.context, loc, src_len, base, idx); } +public fn Expr* Builder.actOnArraySelectionExpr(Builder* b, SrcLoc loc, u32 src_len, Expr* base, Expr* idx, Expr* count, Expr* step) { + return (Expr*)ArraySelectionExpr.create(b.context, loc, src_len, base, idx, count, step); +} + +public fn Expr* Builder.actOnBitSelectionExpr(Builder* b, SrcLoc loc, u32 src_len, Expr* base, Expr* high, Expr* low, i32 width) { + return (Expr*)BitSelectionExpr.create(b.context, loc, src_len, base, high, low, width); +} + public fn Expr* Builder.actOnCallExpr(Builder* b, SrcLoc loc, SrcLoc endLoc, Expr* func, diff --git a/generator/ast_visitor_expr.c2 b/generator/ast_visitor_expr.c2 index 7953cab53..e999e077a 100644 --- a/generator/ast_visitor_expr.c2 +++ b/generator/ast_visitor_expr.c2 @@ -18,7 +18,7 @@ module ast_visitor; import ast local; fn void Visitor.handleExpr(Visitor* v, Expr* e) { - assert(e); + if (!e) return; switch (e.getKind()) { case IntegerLiteral: @@ -92,10 +92,18 @@ fn void Visitor.handleExpr(Visitor* v, Expr* e) { ParenExpr* p = (ParenExpr*)e; v.handleExpr(p.getInner()); break; - case BitOffset: - BitOffsetExpr* bi = (BitOffsetExpr*)e; - v.handleExpr(bi.getLHS()); - v.handleExpr(bi.getRHS()); + case ArraySelection: + ArraySelectionExpr* a = (ArraySelectionExpr*)e; + v.handleExpr(a.getBase()); + v.handleExpr(a.getIndex()); + v.handleExpr(a.getCount()); + v.handleExpr(a.getStep()); + break; + case BitSelection: + BitSelectionExpr* bs = (BitSelectionExpr*)e; + v.handleExpr(bs.getBase()); + v.handleExpr(bs.getHigh()); + v.handleExpr(bs.getLow()); break; case ExplicitCast: ExplicitCastExpr* ec = (ExplicitCastExpr*)e; diff --git a/generator/c/c_generator_expr.c2 b/generator/c/c_generator_expr.c2 index 9ed77454a..e2179afe4 100644 --- a/generator/c/c_generator_expr.c2 +++ b/generator/c/c_generator_expr.c2 @@ -94,15 +94,10 @@ fn void Generator.emitExpr2(Generator* gen, string_buffer.Buf* out, Expr* e, C_P break; case ArraySubscript: ArraySubscriptExpr* a = (ArraySubscriptExpr*)e; - Expr* index = a.getIndex(); - if (index.isBitOffset()) { - gen.emitBitOffset(out, a.getBase(), index, prec); - } else { - gen.emitExpr2(out, a.getBase(), Postfix); - out.add1('['); - gen.emitExpr2(out, index, Comma); - out.add1(']'); - } + gen.emitExpr2(out, a.getBase(), Postfix); + out.add1('['); + gen.emitExpr2(out, a.getIndex(), Comma); + out.add1(']'); break; case Member: MemberContext mc = {} @@ -114,10 +109,14 @@ fn void Generator.emitExpr2(Generator* gen, string_buffer.Buf* out, Expr* e, C_P gen.emitExpr2(out, p.getInner(), Comma); out.rparen(); break; - case BitOffset: - // Already handled in ArraySubscript + case ArraySelection: + // TODO: should only generate in vector operation assert(0); break; + case BitSelection: + BitSelectionExpr* bs = (BitSelectionExpr*)e; + gen.emitBitSelection(out, bs, prec); + break; case ExplicitCast: ExplicitCastExpr* c = (ExplicitCastExpr*)e; if (prec > Prefix) out.lparen(); @@ -187,21 +186,31 @@ fn void Generator.emitStringLiteral(Generator* gen, string_buffer.Buf* out, cons out.encodeBytes(p, len, '"'); } -fn void Generator.emitBitOffset(Generator* gen, string_buffer.Buf* out, Expr* base, Expr* index, C_Prec prec) { +fn void Generator.emitBitSelection(Generator* gen, string_buffer.Buf* out, BitSelectionExpr* bs, C_Prec prec) { // example: a[15:8] -> ((a>>8) & 0xFF) - BitOffsetExpr* bo = (BitOffsetExpr*)index; + // TODO add cast to ensure proper type? // TODO use prec to remove these parentheses + Expr* base = bs.getBase(); + QualType qt = base.getType(); + u32 size = qt.getSize(); + out.lparen(); out.lparen(); // useless gen.emitExpr2(out, base, Shift); out.add(">>"); - gen.emitExpr2(out, bo.getRHS(), Additive); + // TODO: handle shift >= width gracefully + gen.emitExpr2(out, bs.getLow(), Additive); out.rparen(); // useless - out.add(" & "); - u64 mask = 1; - mask <<= bo.getWidth(); - mask--; - out.print("0x%x", mask); + i32 w = bs.getWidth(); + if (w >= 0) { + if ((u32)w < size * 8) out.print(" & 0x%x", ((u64)1 << w) - 1); + } else { + out.print(" & (1U%s << ((", size >= 4 ? "LL" : ""); + gen.emitExpr2(out, bs.getHigh(), Additive); + out.add(" - "); + gen.emitExpr2(out, bs.getLow(), Additive); + out.print(" - 1) & %d))", size * 8 - 1); + } out.rparen(); } diff --git a/generator/c/dep_finder.c2 b/generator/c/dep_finder.c2 index 8b06e995a..be0116071 100644 --- a/generator/c/dep_finder.c2 +++ b/generator/c/dep_finder.c2 @@ -147,6 +147,7 @@ fn void Finder.handleVarDecl(Finder* s, VarDecl* d) { } fn void Finder.handleExpr(Finder* s, Expr* e) { + if (!e) return; switch (e.getKind()) { case IntegerLiteral: case FloatLiteral: @@ -210,7 +211,18 @@ fn void Finder.handleExpr(Finder* s, Expr* e) { ParenExpr* p = (ParenExpr*)e; s.handleExpr(p.getInner()); break; - case BitOffset: + case ArraySelection: + ArraySelectionExpr* a = (ArraySelectionExpr*)e; + s.handleExpr(a.getBase()); + s.handleExpr(a.getIndex()); + s.handleExpr(a.getCount()); + s.handleExpr(a.getStep()); + break; + case BitSelection: + BitSelectionExpr* bs = (BitSelectionExpr*)e; + s.handleExpr(bs.getBase()); + s.handleExpr(bs.getHigh()); + s.handleExpr(bs.getLow()); break; case ExplicitCast: ExplicitCastExpr* c = (ExplicitCastExpr*)e; diff --git a/generator/c2i/c2i_generator_expr.c2 b/generator/c2i/c2i_generator_expr.c2 index 6cc882d95..40d9a3551 100644 --- a/generator/c2i/c2i_generator_expr.c2 +++ b/generator/c2i/c2i_generator_expr.c2 @@ -98,11 +98,27 @@ fn void Generator.emitExpr(Generator* gen, string_buffer.Buf* out, const Expr* e gen.emitExpr(out, p.getInner()); out.rparen(); break; - case BitOffset: - const BitOffsetExpr* bo = (BitOffsetExpr*)e; - gen.emitExpr(out, bo.getLHS()); - out.add(" : "); - gen.emitExpr(out, bo.getRHS()); + case ArraySelection: + const ArraySelectionExpr* a = (ArraySelectionExpr*)e; + gen.emitExpr(out, a.getBase()); + out.add1('['); + if (Expr* index = a.getIndex()) gen.emitExpr(out, index); + out.add1(':'); + if (Expr* count = a.getCount()) gen.emitExpr(out, count); + if (Expr* step = a.getStep()) { + out.add1(':'); + gen.emitExpr(out, step); + } + out.add1(']'); + break; + case BitSelection: + const BitSelectionExpr* bs = (BitSelectionExpr*)e; + gen.emitExpr(out, bs.getBase()); + out.add1('['); + gen.emitExpr(out, bs.getHigh()); + out.add1(':'); + gen.emitExpr(out, bs.getLow()); + out.add1(']'); break; case ExplicitCast: ExplicitCastExpr* c = (ExplicitCastExpr*)e; diff --git a/generator/ir/ir_generator.c2 b/generator/ir/ir_generator.c2 index 1e189bb16..5a4c1f841 100644 --- a/generator/ir/ir_generator.c2 +++ b/generator/ir/ir_generator.c2 @@ -339,7 +339,8 @@ fn void Generator.emitInit(Generator* gen, const Expr* e, u32 size) { gen.ctx.addInitSymbol(sid); break; case Paren: - case BitOffset: + case ArraySelection: + case BitSelection: e.dump(); assert(0); // should be CTV break; diff --git a/generator/ir/ir_generator_expr.c2 b/generator/ir/ir_generator_expr.c2 index a2cd17680..e3a6781ac 100644 --- a/generator/ir/ir_generator_expr.c2 +++ b/generator/ir/ir_generator_expr.c2 @@ -85,8 +85,9 @@ fn void Generator.emitExpr(Generator* gen, ir.Ref* result, const Expr* e) { ParenExpr* p = (ParenExpr*)e; gen.emitExpr(result, p.getInner()); break; - case BitOffset: - assert(0); // TODO + case ArraySelection: + case BitSelection: + assert(0); break; case ExplicitCast: ExplicitCastExpr* ec = (ExplicitCastExpr*)e; @@ -369,7 +370,10 @@ fn void Generator.emitCond(Generator* gen, const Expr* e, BlockId true_blk, Bloc ParenExpr* p = (ParenExpr*)e; gen.emitCond(p.getInner(), true_blk, false_blk, start_blk); return; - case BitOffset: + case ArraySelection: + assert(0); + break; + case BitSelection: case ExplicitCast: case ImplicitCast: break; diff --git a/parser/c2_parser_expr.c2 b/parser/c2_parser_expr.c2 index 749511703..38f3076b8 100644 --- a/parser/c2_parser_expr.c2 +++ b/parser/c2_parser_expr.c2 @@ -325,17 +325,28 @@ fn Expr* Parser.parsePostfixExprSuffix(Parser* p, Expr* lhs, bool couldBeTemplat case LSquare: SrcLoc loc = p.tok.loc; p.consumeToken(); - Expr* idx = p.parseExpr(); + Expr* idx = nil; + if (p.tok.kind != Colon) idx = p.parseExpr(); if (p.tok.kind == Colon) { - // BitOffset : + // BitSelection : + // ArraySelection ? : ? : ? SrcLoc colon_loc = p.tok.loc; p.consumeToken(); - Expr* rhs = p.parseExpr(); - idx = p.builder.actOnBitOffsetExpr(colon_loc, idx, rhs); + Expr* count = nil; + Expr* step = nil; + if (p.tok.kind != Colon && p.tok.kind != RSquare) count = p.parseExpr(); + if (p.tok.kind == Colon) { + p.consumeToken(); + step = p.parseExpr(); + } + u32 src_len = p.tok.loc + 1 - loc; + p.expectAndConsume(RSquare); + lhs = p.builder.actOnArraySelectionExpr(loc, src_len, lhs, idx, count, step); + } else { + u32 src_len = p.tok.loc + 1 - loc; + p.expectAndConsume(RSquare); + lhs = p.builder.actOnArraySubscriptExpr(loc, src_len, lhs, idx); } - u32 src_len = p.tok.loc + 1 - loc; - p.expectAndConsume(RSquare); - lhs = p.builder.actOnArraySubscriptExpr(loc, src_len, lhs, idx); break; case Dot: lhs = p.parseImpureMemberExpr(lhs); diff --git a/recipe.txt b/recipe.txt index 7a47d1411..187c85a6b 100644 --- a/recipe.txt +++ b/recipe.txt @@ -78,8 +78,9 @@ set ast ast/alternate_expr.c2 ast/array_designated_init_expr.c2 ast/array_subscript_expr.c2 + ast/array_selection_expr.c2 ast/binary_operator.c2 - ast/bitoffset_expr.c2 + ast/bit_selection_expr.c2 ast/boolean_literal.c2 ast/builtin_expr.c2 ast/call_expr.c2 diff --git a/test/expr/bitoffset/bitoffset_invalid_base.c2 b/test/expr/bitoffset/bitoffset_invalid_base.c2 index bf09b2eb5..23e31bf2c 100644 --- a/test/expr/bitoffset/bitoffset_invalid_base.c2 +++ b/test/expr/bitoffset/bitoffset_invalid_base.c2 @@ -8,21 +8,22 @@ u32 b = 0; i32 a = 1; fn void test1() { b = b[2:1]; - b = a[2:1]; // @error{bitoffsets are only allowed on unsigned integer type} + b = a[2:1]; // @error{bitoffsets are only allowed on unsigned integer types} } fn void test2() { bool c = true; - b = c[0:0]; // @error{bitoffsets are only allowed on unsigned integer type} + b = c[0:0]; // @error{bitoffsets are only allowed on unsigned integer types} } fn void test3() { const u8* text = "hallo"; - b = text[4:2]; // @error{bitoffsets are only allowed on unsigned integer type} + b = text[4:2]; // @error{array selections are not supported yet} } fn void test4() { - b = test1[4:2]; // @error{bitoffsets are only allowed on unsigned integer type} + // should reject because neither a builtin nor an array nor a pointer + b = test1[4:2]; // @error{array selections are not supported yet} } fn void test5() { @@ -33,6 +34,6 @@ fn void test5() { b = f[2:1]; Int e = 1; - b = e[2:1]; // @error{bitoffsets are only allowed on unsigned integer type} + b = e[2:1]; // @error{bitoffsets are only allowed on unsigned integer types} } diff --git a/test/expr/bitoffset/bitoffset_invalid_range.c2 b/test/expr/bitoffset/bitoffset_invalid_range.c2 index 1172a3ce0..ba2a6c173 100644 --- a/test/expr/bitoffset/bitoffset_invalid_range.c2 +++ b/test/expr/bitoffset/bitoffset_invalid_range.c2 @@ -6,13 +6,13 @@ u32 b = 0; const u32 Index = 4; fn void test1() { - b = a[1:2]; // @error{left bitoffset index is smaller than right index} + b = a[1:2]; // @error{left bitoffset index 1 is smaller than right index 2} b = a[a:10]; } fn void test2() { b = a[Index:2]; - b = a[2:Index]; // @error{left bitoffset index is smaller than right index} + b = a[2:Index]; // @error{left bitoffset index 2 is smaller than right index 4} } fn bool u8_topbit(u8 v) { return v[7:7]; }