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
63 changes: 41 additions & 22 deletions analyser/module_analyser_expr.c2
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,29 @@ fn QualType Analyser.analyseExpr(Analyser* ma, Expr** e_ptr, bool need_rvalue, u
assert(e_ptr);
QualType result = ma.analyseExprInner(e_ptr, side);
if (result.isInvalid()) return result;
(*e_ptr).setType(result);
if (need_rvalue) return ma.convertRvalue(e_ptr, result);
return result;
}

fn QualType Analyser.convertRvalue(Analyser* ma, Expr** e_ptr, QualType result) {
Expr* e = *e_ptr;
e.setType(result);

if (need_rvalue) {
if (e.isLValue()) {
QualType canon = result.getCanonicalType();
assert(canon.isValid());

if (canon.isArray()) {
result = getPointerFromArray(ma.builder, canon);
ma.builder.insertImplicitCast(ArrayToPointerDecay, e_ptr, result);
} else {
// LValueToRValue conversion strips const of type
result.unsetConst();
ma.builder.insertImplicitCast(LValueToRValue, e_ptr, result);
}
} else if (e.isNValue()) {
ma.error(e.getLoc(), "lvalue/rvalue required");
return QualType_Invalid;
if (e.isLValue()) {
QualType canon = result.getCanonicalType();
assert(canon.isValid());

if (canon.isArray()) {
result = getPointerFromArray(ma.builder, canon);
ma.builder.insertImplicitCast(ArrayToPointerDecay, e_ptr, result);
} else {
// LValueToRValue conversion strips const of type
result.unsetConst();
ma.builder.insertImplicitCast(LValueToRValue, e_ptr, result);
}
} else if (e.isNValue()) {
ma.error(e.getLoc(), "lvalue/rvalue required");
return QualType_Invalid;
}

return result;
}

Expand Down Expand Up @@ -468,15 +468,34 @@ fn QualType Analyser.analyseArraySubscriptExpr(Analyser* ma, Expr** e_ptr, u32 s
return QualType_Invalid;
}

QualType qidx = ma.analyseExpr(sub.getIndex2(), true, RHS);
// if base is array with enum index_type -> resolve index with enum scope
bool is_enum_index = false;
QualType index_type = QualType_Invalid;
QualType qidx;
QualType otype = orig.getType();
otype = otype.getCanonicalType();
if (otype.isArray()) {
const ArrayType* at = otype.getArrayType();
is_enum_index = at.isEnumIndex();
index_type = at.getIndexType();
}
if (is_enum_index && ma.checkEnumArg(sub.getIndex2(), index_type)) {
qidx = index_type;
} else {
qidx = ma.analyseExpr(sub.getIndex2(), true, RHS);
}
if (qidx.isInvalid()) return qidx;
QualType canon = qidx.getCanonicalType();
index = sub.getIndex();
if (!canon.isInteger() && !canon.isEnum()) {
ma.error(sub.getIndex().getLoc(), "array subscript is not an integer");
ma.error(index.getLoc(), "array subscript is not an integer");
return QualType_Invalid;
}
if (is_enum_index && index_type.getTypeOrNil() != canon.getTypeOrNil()) {
ma.error(index.getLoc(), "array subscript must have enum type '%s'", index_type.diagName());
return QualType_Invalid;
}

index = sub.getIndex();
if (index.isCtv()) {
QualType q2 = orig.getType();
ArrayType* at = q2.getArrayTypeOrNil();
Expand Down
13 changes: 10 additions & 3 deletions analyser/module_analyser_init.c2
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,15 @@ fn bool Analyser.analyseInitListExpr(Analyser* ma, InitListExpr* ile, QualType e
}

// Note: this function should only be called from analyseInitListArray directly!
fn bool Analyser.analyseArrayDesignatedInit(Analyser* ma, Expr* e, QualType expectedType) {
fn bool Analyser.analyseArrayDesignatedInit(Analyser* ma, Expr* e, QualType expectedType, bool isEnumIndex, QualType indexType) {
ArrayDesignatedInitExpr* ad = (ArrayDesignatedInitExpr*)e;
QualType qt;

QualType qt = ma.analyseExpr(ad.getDesignator2(), false, RHS);
if (isEnumIndex && ma.checkEnumArg(ad.getDesignator2(), indexType)) {
qt = indexType;
} else {
qt = ma.analyseExpr(ad.getDesignator2(), false, RHS);
}
if (qt.isInvalid()) return false;

Expr* de = ad.getDesignator();
Expand All @@ -173,6 +178,8 @@ fn bool Analyser.analyseArrayDesignatedInit(Analyser* ma, Expr* e, QualType expe
return false;
}

// TODO: compute index value, check if designator is integer and < array length if specified

Expr* val = ad.getInit();

if (val.isInitList()) {
Expand Down Expand Up @@ -211,7 +218,7 @@ fn bool Analyser.analyseInitListArray(Analyser* ma, InitListExpr* ile, QualType
continue;
}
if (value.isArrayDesignatedInit()) {
ok &= ma.analyseArrayDesignatedInit(value, et);
ok &= ma.analyseArrayDesignatedInit(value, et, at.isEnumIndex(), at.getIndexType());
have_designators = true;
} else {
ok &= ma.analyseInitExpr(&values[i], et, values[i].getLoc(), false, false);
Expand Down
62 changes: 40 additions & 22 deletions analyser/module_analyser_type.c2
Original file line number Diff line number Diff line change
Expand Up @@ -230,36 +230,54 @@ fn QualType Analyser.analyseTypeRef(Analyser* ma, TypeRef* ref) {
u32 num_arrays = ref.getNumArrays();
// Note: iterate in reverse, since outer array comes first
for (u32 i=num_arrays; i>0; i--) {
Expr* sizeExpr = ref.getArray(i-1); // note: ImplicitCast could have been inserted
Expr** sizeExpr_p = ref.getArray2(i - 1);
Expr* sizeExpr = *sizeExpr_p;
u32 size = 0;
bool is_enum = false;
QualType qt = QualType_Invalid;
if (sizeExpr) {
QualType qt = ma.analyseExpr(ref.getArray2(i-1), true, RHS);
qt = ma.analyseExpr(sizeExpr_p, false, RHS);
if (qt.isInvalid()) return qt;
sizeExpr = ref.getArray(i-1); // note: ImplicitCast could have been inserted

// TODO canonical?
if (!qt.isInteger()) {
ma.error(ref.getLoc(), "array size has non-integer type '%s'", qt.diagName());
return QualType_Invalid;
}

if (!sizeExpr.isCtv()) {
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size is not a compile-time value");
return QualType_Invalid;
}

Value value = ast.evalExpr(sizeExpr);
if (value.isNegative()) {
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size has negative value '%s'", value.str());
return QualType_Invalid;
sizeExpr = *sizeExpr_p;
if (sizeExpr.isNValue()) {
const EnumType* et = qt.getEnumTypeOrNil();
if (!et) {
ma.error(ref.getLoc(), "array size must be an integer or an enum type ('%s')", qt.diagName());
return QualType_Invalid;
}
const EnumTypeDecl* etd = et.getDecl();
is_enum = true;
size = etd.getNumConstants();
} else {
qt = ma.convertRvalue(sizeExpr_p, qt);
if (qt.isInvalid()) return qt;

sizeExpr = *sizeExpr_p; // note: ImplicitCast could have been inserted

// TODO canonical?
if (!qt.isInteger()) {
ma.error(ref.getLoc(), "array size has non-integer type '%s'", qt.diagName());
return QualType_Invalid;
}

if (!sizeExpr.isCtv()) {
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size is not a compile-time value");
return QualType_Invalid;
}

Value value = ast.evalExpr(sizeExpr);
if (value.isNegative()) {
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size has negative value '%s'", value.str());
return QualType_Invalid;
}
size = value.as_u32();
}
size = value.as_u32();
}
if (resolved.isVoid()) {
ma.error(ref.getLoc(), "array element has invalid type 'void'");
return QualType_Invalid;
}
resolved = ma.builder.actOnArrayType(resolved, sizeExpr != nil, size);
resolved = ma.builder.actOnArrayType(resolved, sizeExpr != nil, size, is_enum, qt);
}
if (ref.isIncrArray()) {
resolved = ma.builder.actOnIncrementalArrayType(resolved);
Expand Down Expand Up @@ -296,7 +314,7 @@ fn QualType Analyser.analyseIncrTypeRef(Analyser* ma, TypeRef* ref, u32 size) {
return QualType_Invalid;
}
// always insert a one-dimensional array with size entries
resolved = ma.builder.actOnArrayType(resolved, true, size);
resolved = ma.builder.actOnArrayType(resolved, true, size, false, QualType_Invalid);

if (ref.isUser()) ref.setDest(base.getIndex());
return resolved;
Expand Down
30 changes: 24 additions & 6 deletions ast/array_type.c2
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,37 @@ type ArrayTypeBits struct {
u32 : NumTypeBits;
u32 has_size : 1; // if it has an sizeExpr
u32 is_incremental : 1;
u32 is_enum_index : 1;
}

public type ArrayType struct @(opaque) {
Type base;
QualType elem;
u32 size; // set during analysis, number of elements
// Note: 4 bytes padding here on 64-bit systems
QualType[0] index_type;
}

public fn ArrayType* ArrayType.create(ast_context.Context* c,
QualType elem,
bool has_size,
u32 size)
QualType elem,
bool has_size,
u32 length,
bool is_enum_index,
QualType index_type)
{
ArrayType* t = c.alloc(sizeof(ArrayType));
u32 size = sizeof(ArrayType) + is_enum_index * sizeof(QualType);
ArrayType* t = c.alloc(size);
t.base.init(TypeKind.Array);
t.base.arrayTypeBits.is_incremental = false;
t.base.arrayTypeBits.has_size = has_size;
t.elem = elem;
t.size = size;
t.size = length;
if (is_enum_index) {
t.base.arrayTypeBits.is_enum_index = true;
t.index_type[0] = index_type;
}
#if AstStatistics
Stats.addType(TypeKind.Array, sizeof(ArrayType));
Stats.addType(TypeKind.Array, size);
#endif
return t;
}
Expand Down Expand Up @@ -84,6 +93,15 @@ public fn void ArrayType.setSize(ArrayType* t, u32 size) {
t.size = size;
}

public fn bool ArrayType.isEnumIndex(const ArrayType* t) {
return t.base.arrayTypeBits.is_enum_index;
}

public fn QualType ArrayType.getIndexType(const ArrayType* t) {
if (t.base.arrayTypeBits.is_enum_index) return t.index_type[0];
return QualType_Invalid;
}

fn void ArrayType.printPreName(const ArrayType* t, string_buffer.Buf* out) {
t.elem.print(out);
}
Expand Down
2 changes: 1 addition & 1 deletion ast/string_type_pool.c2
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn QualType StringTypePool.get(StringTypePool* p, u32 len) {
}
if (p.count == p.capacity) p.resize(p.capacity * 2);

Type* t = (Type*)ArrayType.create(p.context, builtins[BuiltinKind.Char], true, len);
Type* t = (Type*)ArrayType.create(p.context, builtins[BuiltinKind.Char], true, len, false, QualType_Invalid);
u32 idx = p.count;
p.slots[idx].len = len;
p.slots[idx].type_ = t;
Expand Down
8 changes: 5 additions & 3 deletions common/ast_builder.c2
Original file line number Diff line number Diff line change
Expand Up @@ -685,16 +685,18 @@ public fn QualType Builder.actOnPointerType(Builder*, QualType inner) {
public fn QualType Builder.actOnArrayType(Builder* b,
QualType elem,
bool has_size,
u32 size) {
ArrayType* t = ArrayType.create(b.context, elem, has_size, size);
u32 size,
bool is_enum_index,
QualType index_type) {
ArrayType* t = ArrayType.create(b.context, elem, has_size, size, is_enum_index, index_type);
QualType a = QualType.create((Type*)t);

// canonical can be either self or a pointer to elem's canonical type
QualType canon = elem.getCanonicalType();
if (elem.getTypeOrNil() == canon.getTypeOrNil()) {
canon = a;
} else {
ArrayType* t2 = ArrayType.create(b.context, canon, has_size, size);
ArrayType* t2 = ArrayType.create(b.context, canon, has_size, size, is_enum_index, index_type);
// Note: keep same quals here, even if canonical type may be a PointerType!
canon = QualType.create((Type*)t2);
}
Expand Down
38 changes: 38 additions & 0 deletions test/expr/array/enum_array.c2
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module test;

import stdio local;

type Enum enum u8 { No, Yes, Maybe }

u32[Enum] a = {
[No]= 0,
[Yes]= 1,
[Maybe]= 2,
}

static_assert(elemsof(a), elemsof(Enum));

public fn i32 main() {
printf("%d\n", a[No]);
printf("%d\n", a[Yes]);
printf("%d\n", a[Maybe]);
a[No] = 3;
a[Yes] = 4;
a[Maybe] = 5;
printf("%d\n", a[No]);
printf("%d\n", a[Yes]);
printf("%d\n", a[Maybe]);
printf("%d\n", a[0 ? Enum.Yes : Enum.No]);
Enum e = Yes;
printf("%d\n", a[e]);
printf("%d\n", a[Enum.max]);
i32 i = 1;
i32[] aa = {
printf("%d\n", a[No + 1]), // @error{use of undeclared identifier 'No'}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could change the diagnostic 'use of undeclared identifier' to 'array subscript must have enum type 'test.Enum'' also? It's weird that a[No] works and a[No +1] says that No is unknown..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the diagnostic is confusing but this problem is not new: Enum e = No + 1; has the same problem.

To solve this, we would need to add the enum at the top of the scope stack so the expression can be resolved. Yet doing so it not completely correct as No would then resolve even in complex expressions where it is possibly ambiguous and inappropriate: Enum e = some_function_that_takes_another_enum(No);

printf("%d\n", a[1 ? Yes : // @error{use of undeclared identifier 'Yes'}
No]), // @error{use of undeclared identifier 'No'}
printf("%d\n", a[1]), // @error{array subscript must have enum type 'test.Enum'}
printf("%d\n", a[i]), // @error{array subscript must have enum type 'test.Enum'}
}
return 0;
}