From b548b5c81fee2fc195fb27eee4f16a23d9e65a72 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Wed, 24 Jan 2018 03:08:09 +0100 Subject: [PATCH] Properly resolve enum value siblings --- src/compiler.ts | 19 ++++++++++++++++++- src/diagnosticMessages.generated.ts | 2 ++ src/diagnosticMessages.json | 1 + src/program.ts | 20 ++++++++++++++------ tests/compiler/enum.optimized.wast | 4 ++++ tests/compiler/enum.ts | 5 +++++ tests/compiler/enum.wast | 6 ++++++ 7 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 95f17c4c..276230df 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -187,6 +187,8 @@ export class Compiler extends DiagnosticEmitter { /** Current function in compilation. */ currentFunction: Function; + /** Current enum in compilation. */ + currentEnum: Enum | null = null; /** Current type in compilation. */ currentType: Type = Type.void; @@ -214,6 +216,7 @@ export class Compiler extends DiagnosticEmitter { // set up start function var startFunctionTemplate = new FunctionPrototype(program, "start", "start", null); var startFunctionInstance = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null); + startFunctionInstance.set(ElementFlags.START); this.currentFunction = this.startFunction = startFunctionInstance; } @@ -472,6 +475,7 @@ export class Compiler extends DiagnosticEmitter { // members might reference each other, triggering another compile element.set(ElementFlags.COMPILED); + this.currentEnum = element; var previousValue: EnumValue | null = null; if (element.members) for (var member of element.members.values()) { @@ -480,6 +484,7 @@ export class Compiler extends DiagnosticEmitter { var initInStart = false; var val = member; var valueDeclaration = val.declaration; + val.set(ElementFlags.COMPILED); if (val.is(ElementFlags.INLINED)) { if (!element.declaration || element.declaration.isTopLevelExport) this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue)); @@ -533,6 +538,7 @@ export class Compiler extends DiagnosticEmitter { this.warning(DiagnosticCode.Cannot_export_a_mutable_global, valueDeclaration.range); } } + this.currentEnum = null; return true; } @@ -2775,7 +2781,7 @@ export class Compiler extends DiagnosticEmitter { } // otherwise resolve - var resolved = this.program.resolveIdentifier(expression, this.currentFunction); // reports + var resolved = this.program.resolveIdentifier(expression, this.currentFunction, this.currentEnum); // reports if (!resolved) return this.module.createUnreachable(); @@ -2799,6 +2805,17 @@ export class Compiler extends DiagnosticEmitter { return this.compileInlineConstant(element, contextualType); this.currentType = (element).type; return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); + + case ElementKind.ENUMVALUE: // here: if referenced from within the same enum + if (!element.is(ElementFlags.COMPILED)) { + this.error(DiagnosticCode.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums, expression.range); + this.currentType = Type.i32; + return this.module.createUnreachable(); + } + this.currentType = Type.i32; + if ((element).is(ElementFlags.INLINED)) + return this.module.createI32((element).constantValue); + return this.module.createGetGlobal((element).internalName, NativeType.I32); } this.error(DiagnosticCode.Operation_not_supported, expression.range); return this.module.createUnreachable(); diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index e99857f5..1519b36a 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -86,6 +86,7 @@ export enum DiagnosticCode { Expected_0_arguments_but_got_1 = 2554, Expected_at_least_0_arguments_but_got_1 = 2555, Expected_0_type_arguments_but_got_1 = 2558, + A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums = 2651, Namespace_0_has_no_exported_member_1 = 2694, File_0_not_found = 6054 } @@ -177,6 +178,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 2554: return "Expected {0} arguments, but got {1}."; case 2555: return "Expected at least {0} arguments, but got {1}."; case 2558: return "Expected {0} type arguments, but got {1}."; + case 2651: return "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums."; case 2694: return "Namespace '{0}' has no exported member '{1}'."; case 6054: return "File '{0}' not found."; default: return ""; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 7591fe0d..397c6067 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -86,6 +86,7 @@ "Expected {0} arguments, but got {1}.": 2554, "Expected at least {0} arguments, but got {1}.": 2555, "Expected {0} type arguments, but got {1}.": 2558, + "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": 2651, "Namespace '{0}' has no exported member '{1}'.": 2694, "File '{0}' not found.": 6054 diff --git a/src/program.ts b/src/program.ts index 5301382e..f48d4bf2 100644 --- a/src/program.ts +++ b/src/program.ts @@ -992,18 +992,24 @@ export class Program extends DiagnosticEmitter { } /** Resolves an identifier to the element it refers to. */ - resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function | null): ResolvedElement | null { + resolveIdentifier(identifier: IdentifierExpression, contextualFunction: Function | null, contextualEnum: Enum | null = null): ResolvedElement | null { var name = identifier.name; var element: Element | null; var namespace: Element | null; + var reference: Element | null; - if (contextualFunction) { + // check siblings + if (contextualEnum) { + + if (contextualEnum.members && (element = contextualEnum.members.get(name)) && element.kind == ElementKind.ENUMVALUE) + return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element); + + } else if (contextualFunction) { // check locals - var local = contextualFunction.flow.getScopedLocal(name); - if (local) - return (resolvedElement || (resolvedElement = new ResolvedElement())).set(local); + if (element = contextualFunction.flow.getScopedLocal(name)) + return (resolvedElement || (resolvedElement = new ResolvedElement())).set(element); // search contextual parent namespaces if applicable if (namespace = contextualFunction.prototype.namespace) { @@ -1259,7 +1265,9 @@ export enum ElementFlags { /** Has already inherited base class static members. */ HAS_STATIC_BASE_MEMBERS = 1 << 18, /** Is scoped. */ - SCOPED = 1 << 19 + SCOPED = 1 << 19, + /** Is the start function. */ + START = 1 << 20 } /** Base class of all program elements. */ diff --git a/tests/compiler/enum.optimized.wast b/tests/compiler/enum.optimized.wast index 8a467ffe..11f60e7d 100644 --- a/tests/compiler/enum.optimized.wast +++ b/tests/compiler/enum.optimized.wast @@ -14,6 +14,8 @@ (global $enum/Mixed.FOUR i32 (i32.const 4)) (global $enum/NonConstant.ZERO (mut i32) (i32.const 0)) (global $enum/NonConstant.ONE (mut i32) (i32.const 0)) + (global $enum/SelfReference.ZERO i32 (i32.const 0)) + (global $enum/SelfReference.ONE i32 (i32.const 1)) (memory $0 1) (export "enum/Implicit.ZERO" (global $enum/Implicit.ZERO)) (export "enum/Implicit.ONE" (global $enum/Implicit.ONE)) @@ -27,6 +29,8 @@ (export "enum/Mixed.ONE" (global $enum/Mixed.ONE)) (export "enum/Mixed.THREE" (global $enum/Mixed.THREE)) (export "enum/Mixed.FOUR" (global $enum/Mixed.FOUR)) + (export "enum/SelfReference.ZERO" (global $enum/SelfReference.ZERO)) + (export "enum/SelfReference.ONE" (global $enum/SelfReference.ONE)) (export "memory" (memory $0)) (start $start) (func $start (; 0 ;) (type $v) diff --git a/tests/compiler/enum.ts b/tests/compiler/enum.ts index 58574179..448abc97 100644 --- a/tests/compiler/enum.ts +++ b/tests/compiler/enum.ts @@ -27,3 +27,8 @@ export enum NonConstant { ZERO = getZero(), // cannot export a mutable global ONE // cannot export a mutable global (tsc doesn't allow this) } + +export enum SelfReference { + ZERO, + ONE = ZERO + 1 +} diff --git a/tests/compiler/enum.wast b/tests/compiler/enum.wast index 1d09bd7e..33c30d3f 100644 --- a/tests/compiler/enum.wast +++ b/tests/compiler/enum.wast @@ -15,6 +15,8 @@ (global $enum/Mixed.FOUR i32 (i32.const 4)) (global $enum/NonConstant.ZERO (mut i32) (i32.const 0)) (global $enum/NonConstant.ONE (mut i32) (i32.const 0)) + (global $enum/SelfReference.ZERO i32 (i32.const 0)) + (global $enum/SelfReference.ONE i32 (i32.const 1)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) (export "enum/Implicit.ZERO" (global $enum/Implicit.ZERO)) @@ -29,6 +31,8 @@ (export "enum/Mixed.ONE" (global $enum/Mixed.ONE)) (export "enum/Mixed.THREE" (global $enum/Mixed.THREE)) (export "enum/Mixed.FOUR" (global $enum/Mixed.FOUR)) + (export "enum/SelfReference.ZERO" (global $enum/SelfReference.ZERO)) + (export "enum/SelfReference.ONE" (global $enum/SelfReference.ONE)) (export "memory" (memory $0)) (start $start) (func $enum/getZero (; 0 ;) (type $i) (result i32) @@ -97,9 +101,11 @@ ENUM: enum/Mixed FUNCTION_PROTOTYPE: enum/getZero ENUM: enum/NonConstant + ENUM: enum/SelfReference [program.exports] ENUM: enum/Implicit ENUM: enum/Explicit ENUM: enum/Mixed ENUM: enum/NonConstant + ENUM: enum/SelfReference ;)