From 0e33806cf6de109816fe2c49390fad26896591c4 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Sat, 1 Dec 2018 13:31:37 +0100 Subject: [PATCH] Add a way to ensure that lazy globals are resolved, fixes #355 This only affects static fields that currently must have a type annotation, while it wouldn't work if there wasn't an annotated type, like on normal globals, which aren't compiled lazily, though. Must be revisted if requirements on type annotations on fields ever become relaxed. --- src/common.ts | 16 +++++++++------- src/compiler.ts | 6 ++++-- src/program.ts | 19 ++++++++++--------- src/resolver.ts | 19 ++++++++++++++++--- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/common.ts b/src/common.ts index af24dd09..925a88b8 100644 --- a/src/common.ts +++ b/src/common.ts @@ -60,22 +60,24 @@ export enum CommonFlags { // Compilation states + /** Is resolved. */ + RESOLVED = 1 << 22, /** Is compiled. */ - COMPILED = 1 << 22, + COMPILED = 1 << 23, /** Has a constant value and is therefore inlined. */ - INLINED = 1 << 23, + INLINED = 1 << 24, /** Is scoped. */ - SCOPED = 1 << 24, + SCOPED = 1 << 25, /** Is a trampoline. */ - TRAMPOLINE = 1 << 25, + TRAMPOLINE = 1 << 26, /** Is a virtual method. */ - VIRTUAL = 1 << 26, + VIRTUAL = 1 << 27, /** Is the main function. */ - MAIN = 1 << 27, + MAIN = 1 << 28, // Other - QUOTED = 1 << 28 + QUOTED = 1 << 29 } /** Path delimiter inserted between file system levels. */ diff --git a/src/compiler.ts b/src/compiler.ts index 94110669..074da6fd 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -696,7 +696,7 @@ export class Compiler extends DiagnosticEmitter { var declaration = global.declaration; var initExpr: ExpressionRef = 0; - if (global.type == Type.void) { // type is void if not yet resolved or not annotated + if (!global.is(CommonFlags.RESOLVED)) { if (declaration) { // resolve now if annotated @@ -711,6 +711,7 @@ export class Compiler extends DiagnosticEmitter { return false; } global.type = resolvedType; + global.set(CommonFlags.RESOLVED); // infer from initializer if not annotated } else if (declaration.initializer) { // infer type using void/NONE for literal inference @@ -727,6 +728,7 @@ export class Compiler extends DiagnosticEmitter { return false; } global.type = this.currentType; + global.set(CommonFlags.RESOLVED); // must either be annotated or have an initializer } else { @@ -737,7 +739,7 @@ export class Compiler extends DiagnosticEmitter { return false; } } else { - assert(false); // must have a declaration if 'void' (and thus resolved later on) + assert(false); // must have a declaration if resolved lazily } } diff --git a/src/program.ts b/src/program.ts index 26b6e735..1a6a6a32 100644 --- a/src/program.ts +++ b/src/program.ts @@ -16,7 +16,8 @@ import { import { Options, - Feature + Feature, + Compiler } from "./compiler"; import { @@ -730,19 +731,19 @@ export class Program extends DiagnosticEmitter { /** Sets a constant integer value. */ setConstantInteger(globalName: string, type: Type, value: I64): void { assert(type.is(TypeFlags.INTEGER)); - this.elementsLookup.set(globalName, - new Global(this, globalName, globalName, type, null, DecoratorFlags.NONE) - .withConstantIntegerValue(value) - ); + var global = new Global(this, globalName, globalName, type, null, DecoratorFlags.NONE) + .withConstantIntegerValue(value); + global.set(CommonFlags.RESOLVED); + this.elementsLookup.set(globalName, global); } /** Sets a constant float value. */ setConstantFloat(globalName: string, type: Type, value: f64): void { assert(type.is(TypeFlags.FLOAT)); - this.elementsLookup.set(globalName, - new Global(this, globalName, globalName, type, null, DecoratorFlags.NONE) - .withConstantFloatValue(value) - ); + var global = new Global(this, globalName, globalName, type, null, DecoratorFlags.NONE) + .withConstantFloatValue(value); + global.set(CommonFlags.RESOLVED); + this.elementsLookup.set(globalName, global); } /** Tries to locate an import by traversing exports and queued exports. */ diff --git a/src/resolver.ts b/src/resolver.ts index 7b79b09c..9e20b914 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -24,7 +24,8 @@ import { Property, DecoratorFlags, FieldPrototype, - Field + Field, + Global } from "./program"; import { @@ -358,6 +359,18 @@ export class Resolver extends DiagnosticEmitter { return null; } + /** Resolves a lazily compiled global, i.e. a static class field. */ + ensureResolvedLazyGlobal(global: Global, reportMode: ReportMode = ReportMode.REPORT): bool { + if (global.is(CommonFlags.RESOLVED)) return true; + var resolveType = assert(global.declaration).type; + if (!resolveType) return false; + var resolvedType = this.resolveType(resolveType, null, reportMode); + if (!resolvedType) return false; + global.type = resolvedType; + global.set(CommonFlags.RESOLVED); + return true; + } + /** Resolves a property access to the element it refers to. */ resolvePropertyAccess( propertyAccess: PropertyAccessExpression, @@ -374,7 +387,7 @@ export class Resolver extends DiagnosticEmitter { // Resolve variable-likes to the class type they reference first switch (target.kind) { - case ElementKind.GLOBAL: + case ElementKind.GLOBAL: if (!this.ensureResolvedLazyGlobal(target, reportMode)) return null; case ElementKind.LOCAL: case ElementKind.FIELD: { let type = (target).type; @@ -494,7 +507,7 @@ export class Resolver extends DiagnosticEmitter { var target = this.resolveExpression(targetExpression, contextualFunction, reportMode); if (!target) return null; switch (target.kind) { - case ElementKind.GLOBAL: + case ElementKind.GLOBAL: if (!this.ensureResolvedLazyGlobal(target, reportMode)) return null; case ElementKind.LOCAL: case ElementKind.FIELD: { let type = (target).type;