Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions analyser/conversion_checker_expr.c2
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
150 changes: 88 additions & 62 deletions analyser/module_analyser_expr.c2
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down
15 changes: 7 additions & 8 deletions analyser/module_analyser_unaryop.c2
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
111 changes: 111 additions & 0 deletions ast/array_selection_expr.c2
Original file line number Diff line number Diff line change
@@ -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"); }
}

Loading
Loading