Skip to content

Commit 0e24be7

Browse files
chqrliebvdberg
authored andcommitted
Analyser: allow enum type as array length in array definitions
* arrays defined with an enum type as the length must be indexed using enum values of this type * enum values are scoped in array subscript expressions and initializers Implements #403
1 parent fc56e8d commit 0e24be7

File tree

7 files changed

+159
-57
lines changed

7 files changed

+159
-57
lines changed

analyser/module_analyser_expr.c2

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,29 @@ fn QualType Analyser.analyseExpr(Analyser* ma, Expr** e_ptr, bool need_rvalue, u
2424
assert(e_ptr);
2525
QualType result = ma.analyseExprInner(e_ptr, side);
2626
if (result.isInvalid()) return result;
27+
(*e_ptr).setType(result);
28+
if (need_rvalue) return ma.convertRvalue(e_ptr, result);
29+
return result;
30+
}
2731

32+
fn QualType Analyser.convertRvalue(Analyser* ma, Expr** e_ptr, QualType result) {
2833
Expr* e = *e_ptr;
29-
e.setType(result);
30-
31-
if (need_rvalue) {
32-
if (e.isLValue()) {
33-
QualType canon = result.getCanonicalType();
34-
assert(canon.isValid());
35-
36-
if (canon.isArray()) {
37-
result = getPointerFromArray(ma.builder, canon);
38-
ma.builder.insertImplicitCast(ArrayToPointerDecay, e_ptr, result);
39-
} else {
40-
// LValueToRValue conversion strips const of type
41-
result.unsetConst();
42-
ma.builder.insertImplicitCast(LValueToRValue, e_ptr, result);
43-
}
44-
} else if (e.isNValue()) {
45-
ma.error(e.getLoc(), "lvalue/rvalue required");
46-
return QualType_Invalid;
34+
if (e.isLValue()) {
35+
QualType canon = result.getCanonicalType();
36+
assert(canon.isValid());
37+
38+
if (canon.isArray()) {
39+
result = getPointerFromArray(ma.builder, canon);
40+
ma.builder.insertImplicitCast(ArrayToPointerDecay, e_ptr, result);
41+
} else {
42+
// LValueToRValue conversion strips const of type
43+
result.unsetConst();
44+
ma.builder.insertImplicitCast(LValueToRValue, e_ptr, result);
4745
}
46+
} else if (e.isNValue()) {
47+
ma.error(e.getLoc(), "lvalue/rvalue required");
48+
return QualType_Invalid;
4849
}
49-
5050
return result;
5151
}
5252

@@ -468,15 +468,34 @@ fn QualType Analyser.analyseArraySubscriptExpr(Analyser* ma, Expr** e_ptr, u32 s
468468
return QualType_Invalid;
469469
}
470470

471-
QualType qidx = ma.analyseExpr(sub.getIndex2(), true, RHS);
471+
// if base is array with enum index_type -> resolve index with enum scope
472+
bool is_enum_index = false;
473+
QualType index_type = QualType_Invalid;
474+
QualType qidx;
475+
QualType otype = orig.getType();
476+
otype = otype.getCanonicalType();
477+
if (otype.isArray()) {
478+
const ArrayType* at = otype.getArrayType();
479+
is_enum_index = at.isEnumIndex();
480+
index_type = at.getIndexType();
481+
}
482+
if (is_enum_index && ma.checkEnumArg(sub.getIndex2(), index_type)) {
483+
qidx = index_type;
484+
} else {
485+
qidx = ma.analyseExpr(sub.getIndex2(), true, RHS);
486+
}
472487
if (qidx.isInvalid()) return qidx;
473488
QualType canon = qidx.getCanonicalType();
489+
index = sub.getIndex();
474490
if (!canon.isInteger() && !canon.isEnum()) {
475-
ma.error(sub.getIndex().getLoc(), "array subscript is not an integer");
491+
ma.error(index.getLoc(), "array subscript is not an integer");
492+
return QualType_Invalid;
493+
}
494+
if (is_enum_index && index_type.getTypeOrNil() != canon.getTypeOrNil()) {
495+
ma.error(index.getLoc(), "array subscript must have enum type '%s'", index_type.diagName());
476496
return QualType_Invalid;
477497
}
478498

479-
index = sub.getIndex();
480499
if (index.isCtv()) {
481500
QualType q2 = orig.getType();
482501
ArrayType* at = q2.getArrayTypeOrNil();

analyser/module_analyser_init.c2

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,15 @@ fn bool Analyser.analyseInitListExpr(Analyser* ma, InitListExpr* ile, QualType e
161161
}
162162

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

167-
QualType qt = ma.analyseExpr(ad.getDesignator2(), false, RHS);
168+
if (isEnumIndex && ma.checkEnumArg(ad.getDesignator2(), indexType)) {
169+
qt = indexType;
170+
} else {
171+
qt = ma.analyseExpr(ad.getDesignator2(), false, RHS);
172+
}
168173
if (qt.isInvalid()) return false;
169174

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

181+
// TODO: compute index value, check if designator is integer and < array length if specified
182+
176183
Expr* val = ad.getInit();
177184

178185
if (val.isInitList()) {
@@ -211,7 +218,7 @@ fn bool Analyser.analyseInitListArray(Analyser* ma, InitListExpr* ile, QualType
211218
continue;
212219
}
213220
if (value.isArrayDesignatedInit()) {
214-
ok &= ma.analyseArrayDesignatedInit(value, et);
221+
ok &= ma.analyseArrayDesignatedInit(value, et, at.isEnumIndex(), at.getIndexType());
215222
have_designators = true;
216223
} else {
217224
ok &= ma.analyseInitExpr(&values[i], et, values[i].getLoc(), false, false);

analyser/module_analyser_type.c2

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -230,36 +230,54 @@ fn QualType Analyser.analyseTypeRef(Analyser* ma, TypeRef* ref) {
230230
u32 num_arrays = ref.getNumArrays();
231231
// Note: iterate in reverse, since outer array comes first
232232
for (u32 i=num_arrays; i>0; i--) {
233-
Expr* sizeExpr = ref.getArray(i-1); // note: ImplicitCast could have been inserted
233+
Expr** sizeExpr_p = ref.getArray2(i - 1);
234+
Expr* sizeExpr = *sizeExpr_p;
234235
u32 size = 0;
236+
bool is_enum = false;
237+
QualType qt = QualType_Invalid;
235238
if (sizeExpr) {
236-
QualType qt = ma.analyseExpr(ref.getArray2(i-1), true, RHS);
239+
qt = ma.analyseExpr(sizeExpr_p, false, RHS);
237240
if (qt.isInvalid()) return qt;
238-
sizeExpr = ref.getArray(i-1); // note: ImplicitCast could have been inserted
239-
240-
// TODO canonical?
241-
if (!qt.isInteger()) {
242-
ma.error(ref.getLoc(), "array size has non-integer type '%s'", qt.diagName());
243-
return QualType_Invalid;
244-
}
245-
246-
if (!sizeExpr.isCtv()) {
247-
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size is not a compile-time value");
248-
return QualType_Invalid;
249-
}
250-
251-
Value value = ast.evalExpr(sizeExpr);
252-
if (value.isNegative()) {
253-
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size has negative value '%s'", value.str());
254-
return QualType_Invalid;
241+
sizeExpr = *sizeExpr_p;
242+
if (sizeExpr.isNValue()) {
243+
const EnumType* et = qt.getEnumTypeOrNil();
244+
if (!et) {
245+
ma.error(ref.getLoc(), "array size must be an integer or an enum type ('%s')", qt.diagName());
246+
return QualType_Invalid;
247+
}
248+
const EnumTypeDecl* etd = et.getDecl();
249+
is_enum = true;
250+
size = etd.getNumConstants();
251+
} else {
252+
qt = ma.convertRvalue(sizeExpr_p, qt);
253+
if (qt.isInvalid()) return qt;
254+
255+
sizeExpr = *sizeExpr_p; // note: ImplicitCast could have been inserted
256+
257+
// TODO canonical?
258+
if (!qt.isInteger()) {
259+
ma.error(ref.getLoc(), "array size has non-integer type '%s'", qt.diagName());
260+
return QualType_Invalid;
261+
}
262+
263+
if (!sizeExpr.isCtv()) {
264+
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size is not a compile-time value");
265+
return QualType_Invalid;
266+
}
267+
268+
Value value = ast.evalExpr(sizeExpr);
269+
if (value.isNegative()) {
270+
ma.errorRange(sizeExpr.getLoc(), sizeExpr.getRange(), "array size has negative value '%s'", value.str());
271+
return QualType_Invalid;
272+
}
273+
size = value.as_u32();
255274
}
256-
size = value.as_u32();
257275
}
258276
if (resolved.isVoid()) {
259277
ma.error(ref.getLoc(), "array element has invalid type 'void'");
260278
return QualType_Invalid;
261279
}
262-
resolved = ma.builder.actOnArrayType(resolved, sizeExpr != nil, size);
280+
resolved = ma.builder.actOnArrayType(resolved, sizeExpr != nil, size, is_enum, qt);
263281
}
264282
if (ref.isIncrArray()) {
265283
resolved = ma.builder.actOnIncrementalArrayType(resolved);
@@ -296,7 +314,7 @@ fn QualType Analyser.analyseIncrTypeRef(Analyser* ma, TypeRef* ref, u32 size) {
296314
return QualType_Invalid;
297315
}
298316
// always insert a one-dimensional array with size entries
299-
resolved = ma.builder.actOnArrayType(resolved, true, size);
317+
resolved = ma.builder.actOnArrayType(resolved, true, size, false, QualType_Invalid);
300318

301319
if (ref.isUser()) ref.setDest(base.getIndex());
302320
return resolved;

ast/array_type.c2

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,37 @@ type ArrayTypeBits struct {
2222
u32 : NumTypeBits;
2323
u32 has_size : 1; // if it has an sizeExpr
2424
u32 is_incremental : 1;
25+
u32 is_enum_index : 1;
2526
}
2627

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

3436
public fn ArrayType* ArrayType.create(ast_context.Context* c,
35-
QualType elem,
36-
bool has_size,
37-
u32 size)
37+
QualType elem,
38+
bool has_size,
39+
u32 length,
40+
bool is_enum_index,
41+
QualType index_type)
3842
{
39-
ArrayType* t = c.alloc(sizeof(ArrayType));
43+
u32 size = sizeof(ArrayType) + is_enum_index * sizeof(QualType);
44+
ArrayType* t = c.alloc(size);
4045
t.base.init(TypeKind.Array);
4146
t.base.arrayTypeBits.is_incremental = false;
4247
t.base.arrayTypeBits.has_size = has_size;
4348
t.elem = elem;
44-
t.size = size;
49+
t.size = length;
50+
if (is_enum_index) {
51+
t.base.arrayTypeBits.is_enum_index = true;
52+
t.index_type[0] = index_type;
53+
}
4554
#if AstStatistics
46-
Stats.addType(TypeKind.Array, sizeof(ArrayType));
55+
Stats.addType(TypeKind.Array, size);
4756
#endif
4857
return t;
4958
}
@@ -84,6 +93,15 @@ public fn void ArrayType.setSize(ArrayType* t, u32 size) {
8493
t.size = size;
8594
}
8695

96+
public fn bool ArrayType.isEnumIndex(const ArrayType* t) {
97+
return t.base.arrayTypeBits.is_enum_index;
98+
}
99+
100+
public fn QualType ArrayType.getIndexType(const ArrayType* t) {
101+
if (t.base.arrayTypeBits.is_enum_index) return t.index_type[0];
102+
return QualType_Invalid;
103+
}
104+
87105
fn void ArrayType.printPreName(const ArrayType* t, string_buffer.Buf* out) {
88106
t.elem.print(out);
89107
}

ast/string_type_pool.c2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ fn QualType StringTypePool.get(StringTypePool* p, u32 len) {
7878
}
7979
if (p.count == p.capacity) p.resize(p.capacity * 2);
8080

81-
Type* t = (Type*)ArrayType.create(p.context, builtins[BuiltinKind.Char], true, len);
81+
Type* t = (Type*)ArrayType.create(p.context, builtins[BuiltinKind.Char], true, len, false, QualType_Invalid);
8282
u32 idx = p.count;
8383
p.slots[idx].len = len;
8484
p.slots[idx].type_ = t;

common/ast_builder.c2

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -685,16 +685,18 @@ public fn QualType Builder.actOnPointerType(Builder*, QualType inner) {
685685
public fn QualType Builder.actOnArrayType(Builder* b,
686686
QualType elem,
687687
bool has_size,
688-
u32 size) {
689-
ArrayType* t = ArrayType.create(b.context, elem, has_size, size);
688+
u32 size,
689+
bool is_enum_index,
690+
QualType index_type) {
691+
ArrayType* t = ArrayType.create(b.context, elem, has_size, size, is_enum_index, index_type);
690692
QualType a = QualType.create((Type*)t);
691693

692694
// canonical can be either self or a pointer to elem's canonical type
693695
QualType canon = elem.getCanonicalType();
694696
if (elem.getTypeOrNil() == canon.getTypeOrNil()) {
695697
canon = a;
696698
} else {
697-
ArrayType* t2 = ArrayType.create(b.context, canon, has_size, size);
699+
ArrayType* t2 = ArrayType.create(b.context, canon, has_size, size, is_enum_index, index_type);
698700
// Note: keep same quals here, even if canonical type may be a PointerType!
699701
canon = QualType.create((Type*)t2);
700702
}

test/expr/array/enum_array.c2

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module test;
2+
3+
import stdio local;
4+
5+
type Enum enum u8 { No, Yes, Maybe }
6+
7+
u32[Enum] a = {
8+
[No]= 0,
9+
[Yes]= 1,
10+
[Maybe]= 2,
11+
}
12+
13+
static_assert(elemsof(a), elemsof(Enum));
14+
15+
public fn i32 main() {
16+
printf("%d\n", a[No]);
17+
printf("%d\n", a[Yes]);
18+
printf("%d\n", a[Maybe]);
19+
a[No] = 3;
20+
a[Yes] = 4;
21+
a[Maybe] = 5;
22+
printf("%d\n", a[No]);
23+
printf("%d\n", a[Yes]);
24+
printf("%d\n", a[Maybe]);
25+
printf("%d\n", a[0 ? Enum.Yes : Enum.No]);
26+
Enum e = Yes;
27+
printf("%d\n", a[e]);
28+
printf("%d\n", a[Enum.max]);
29+
i32 i = 1;
30+
i32[] aa = {
31+
printf("%d\n", a[No + 1]), // @error{use of undeclared identifier 'No'}
32+
printf("%d\n", a[1 ? Yes : // @error{use of undeclared identifier 'Yes'}
33+
No]), // @error{use of undeclared identifier 'No'}
34+
printf("%d\n", a[1]), // @error{array subscript must have enum type 'test.Enum'}
35+
printf("%d\n", a[i]), // @error{array subscript must have enum type 'test.Enum'}
36+
}
37+
return 0;
38+
}

0 commit comments

Comments
 (0)