Skip to content

Commit b548b5c

Browse files
committed
Properly resolve enum value siblings
1 parent 2993594 commit b548b5c

File tree

7 files changed

+50
-7
lines changed

7 files changed

+50
-7
lines changed

src/compiler.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ export class Compiler extends DiagnosticEmitter {
187187

188188
/** Current function in compilation. */
189189
currentFunction: Function;
190+
/** Current enum in compilation. */
191+
currentEnum: Enum | null = null;
190192
/** Current type in compilation. */
191193
currentType: Type = Type.void;
192194

@@ -214,6 +216,7 @@ export class Compiler extends DiagnosticEmitter {
214216
// set up start function
215217
var startFunctionTemplate = new FunctionPrototype(program, "start", "start", null);
216218
var startFunctionInstance = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null);
219+
startFunctionInstance.set(ElementFlags.START);
217220
this.currentFunction = this.startFunction = startFunctionInstance;
218221
}
219222

@@ -472,6 +475,7 @@ export class Compiler extends DiagnosticEmitter {
472475
// members might reference each other, triggering another compile
473476
element.set(ElementFlags.COMPILED);
474477

478+
this.currentEnum = element;
475479
var previousValue: EnumValue | null = null;
476480
if (element.members)
477481
for (var member of element.members.values()) {
@@ -480,6 +484,7 @@ export class Compiler extends DiagnosticEmitter {
480484
var initInStart = false;
481485
var val = <EnumValue>member;
482486
var valueDeclaration = val.declaration;
487+
val.set(ElementFlags.COMPILED);
483488
if (val.is(ElementFlags.INLINED)) {
484489
if (!element.declaration || element.declaration.isTopLevelExport)
485490
this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue));
@@ -533,6 +538,7 @@ export class Compiler extends DiagnosticEmitter {
533538
this.warning(DiagnosticCode.Cannot_export_a_mutable_global, valueDeclaration.range);
534539
}
535540
}
541+
this.currentEnum = null;
536542
return true;
537543
}
538544

@@ -2775,7 +2781,7 @@ export class Compiler extends DiagnosticEmitter {
27752781
}
27762782

27772783
// otherwise resolve
2778-
var resolved = this.program.resolveIdentifier(expression, this.currentFunction); // reports
2784+
var resolved = this.program.resolveIdentifier(expression, this.currentFunction, this.currentEnum); // reports
27792785
if (!resolved)
27802786
return this.module.createUnreachable();
27812787

@@ -2799,6 +2805,17 @@ export class Compiler extends DiagnosticEmitter {
27992805
return this.compileInlineConstant(<Global>element, contextualType);
28002806
this.currentType = (<Global>element).type;
28012807
return this.module.createGetGlobal((<Global>element).internalName, this.currentType.toNativeType());
2808+
2809+
case ElementKind.ENUMVALUE: // here: if referenced from within the same enum
2810+
if (!element.is(ElementFlags.COMPILED)) {
2811+
this.error(DiagnosticCode.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums, expression.range);
2812+
this.currentType = Type.i32;
2813+
return this.module.createUnreachable();
2814+
}
2815+
this.currentType = Type.i32;
2816+
if ((<EnumValue>element).is(ElementFlags.INLINED))
2817+
return this.module.createI32((<EnumValue>element).constantValue);
2818+
return this.module.createGetGlobal((<EnumValue>element).internalName, NativeType.I32);
28022819
}
28032820
this.error(DiagnosticCode.Operation_not_supported, expression.range);
28042821
return this.module.createUnreachable();

src/diagnosticMessages.generated.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export enum DiagnosticCode {
8686
Expected_0_arguments_but_got_1 = 2554,
8787
Expected_at_least_0_arguments_but_got_1 = 2555,
8888
Expected_0_type_arguments_but_got_1 = 2558,
89+
A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums = 2651,
8990
Namespace_0_has_no_exported_member_1 = 2694,
9091
File_0_not_found = 6054
9192
}
@@ -177,6 +178,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
177178
case 2554: return "Expected {0} arguments, but got {1}.";
178179
case 2555: return "Expected at least {0} arguments, but got {1}.";
179180
case 2558: return "Expected {0} type arguments, but got {1}.";
181+
case 2651: return "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.";
180182
case 2694: return "Namespace '{0}' has no exported member '{1}'.";
181183
case 6054: return "File '{0}' not found.";
182184
default: return "";

src/diagnosticMessages.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"Expected {0} arguments, but got {1}.": 2554,
8787
"Expected at least {0} arguments, but got {1}.": 2555,
8888
"Expected {0} type arguments, but got {1}.": 2558,
89+
"A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": 2651,
8990
"Namespace '{0}' has no exported member '{1}'.": 2694,
9091

9192
"File '{0}' not found.": 6054

src/program.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -992,18 +992,24 @@ export class Program extends DiagnosticEmitter {
992992
}
993993

994994
/** Resolves an identifier to the element it refers to. */
995-
resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function | null): ResolvedElement | null {
995+
resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function | null, contextualEnum: Enum | null = null): ResolvedElement | null {
996996
var name = identifier.name;
997997

998998
var element: Element | null;
999999
var namespace: Element | null;
1000+
var reference: Element | null;
10001001

1001-
if (contextualFunction) {
1002+
// check siblings
1003+
if (contextualEnum) {
1004+
1005+
if (contextualEnum.members && (element = contextualEnum.members.get(name)) && element.kind == ElementKind.ENUMVALUE)
1006+
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
1007+
1008+
} else if (contextualFunction) {
10021009

10031010
// check locals
1004-
var local = contextualFunction.flow.getScopedLocal(name);
1005-
if (local)
1006-
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(local);
1011+
if (element = contextualFunction.flow.getScopedLocal(name))
1012+
return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element);
10071013

10081014
// search contextual parent namespaces if applicable
10091015
if (namespace = contextualFunction.prototype.namespace) {
@@ -1259,7 +1265,9 @@ export enum ElementFlags {
12591265
/** Has already inherited base class static members. */
12601266
HAS_STATIC_BASE_MEMBERS = 1 << 18,
12611267
/** Is scoped. */
1262-
SCOPED = 1 << 19
1268+
SCOPED = 1 << 19,
1269+
/** Is the start function. */
1270+
START = 1 << 20
12631271
}
12641272

12651273
/** Base class of all program elements. */

tests/compiler/enum.optimized.wast

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
(global $enum/Mixed.FOUR i32 (i32.const 4))
1515
(global $enum/NonConstant.ZERO (mut i32) (i32.const 0))
1616
(global $enum/NonConstant.ONE (mut i32) (i32.const 0))
17+
(global $enum/SelfReference.ZERO i32 (i32.const 0))
18+
(global $enum/SelfReference.ONE i32 (i32.const 1))
1719
(memory $0 1)
1820
(export "enum/Implicit.ZERO" (global $enum/Implicit.ZERO))
1921
(export "enum/Implicit.ONE" (global $enum/Implicit.ONE))
@@ -27,6 +29,8 @@
2729
(export "enum/Mixed.ONE" (global $enum/Mixed.ONE))
2830
(export "enum/Mixed.THREE" (global $enum/Mixed.THREE))
2931
(export "enum/Mixed.FOUR" (global $enum/Mixed.FOUR))
32+
(export "enum/SelfReference.ZERO" (global $enum/SelfReference.ZERO))
33+
(export "enum/SelfReference.ONE" (global $enum/SelfReference.ONE))
3034
(export "memory" (memory $0))
3135
(start $start)
3236
(func $start (; 0 ;) (type $v)

tests/compiler/enum.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ export enum NonConstant {
2727
ZERO = getZero(), // cannot export a mutable global
2828
ONE // cannot export a mutable global (tsc doesn't allow this)
2929
}
30+
31+
export enum SelfReference {
32+
ZERO,
33+
ONE = ZERO + 1
34+
}

tests/compiler/enum.wast

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
(global $enum/Mixed.FOUR i32 (i32.const 4))
1616
(global $enum/NonConstant.ZERO (mut i32) (i32.const 0))
1717
(global $enum/NonConstant.ONE (mut i32) (i32.const 0))
18+
(global $enum/SelfReference.ZERO i32 (i32.const 0))
19+
(global $enum/SelfReference.ONE i32 (i32.const 1))
1820
(global $HEAP_BASE i32 (i32.const 4))
1921
(memory $0 1)
2022
(export "enum/Implicit.ZERO" (global $enum/Implicit.ZERO))
@@ -29,6 +31,8 @@
2931
(export "enum/Mixed.ONE" (global $enum/Mixed.ONE))
3032
(export "enum/Mixed.THREE" (global $enum/Mixed.THREE))
3133
(export "enum/Mixed.FOUR" (global $enum/Mixed.FOUR))
34+
(export "enum/SelfReference.ZERO" (global $enum/SelfReference.ZERO))
35+
(export "enum/SelfReference.ONE" (global $enum/SelfReference.ONE))
3236
(export "memory" (memory $0))
3337
(start $start)
3438
(func $enum/getZero (; 0 ;) (type $i) (result i32)
@@ -97,9 +101,11 @@
97101
ENUM: enum/Mixed
98102
FUNCTION_PROTOTYPE: enum/getZero
99103
ENUM: enum/NonConstant
104+
ENUM: enum/SelfReference
100105
[program.exports]
101106
ENUM: enum/Implicit
102107
ENUM: enum/Explicit
103108
ENUM: enum/Mixed
104109
ENUM: enum/NonConstant
110+
ENUM: enum/SelfReference
105111
;)

0 commit comments

Comments
 (0)