Skip to content

Remember failed globals so we don't hit assertions #1321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 11, 2020
Merged
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
14 changes: 8 additions & 6 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,23 @@ export enum CommonFlags {
RESOLVED = 1 << 21,
/** Is compiled. */
COMPILED = 1 << 22,
/** Did error. */
ERRORED = 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 stub. */
STUB = 1 << 25,
STUB = 1 << 26,
/** Is a virtual method. */
VIRTUAL = 1 << 26,
VIRTUAL = 1 << 27,
/** Is (part of) a closure. */
CLOSURE = 1 << 27,
CLOSURE = 1 << 28,

// Other

/** Is quoted. */
QUOTED = 1 << 28
QUOTED = 1 << 29
}

/** Path delimiter inserted between file system levels. */
Expand Down
99 changes: 87 additions & 12 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ export class Compiler extends DiagnosticEmitter {
pendingClassInstanceOf: Set<ClassPrototype> = new Set();
/** Functions potentially involving a virtual call. */
virtualCalls: Set<Function> = new Set();
/** Elements currently undergoing compilation. */
pendingElements: Set<Element> = new Set();

/** Compiles a {@link Program} to a {@link Module} using the specified options. */
static compile(program: Program): Module {
Expand Down Expand Up @@ -993,9 +995,12 @@ export class Compiler extends DiagnosticEmitter {

/** Compiles a global variable. */
compileGlobal(global: Global): bool {
if (global.is(CommonFlags.COMPILED)) return true;
if (global.is(CommonFlags.COMPILED)) return !global.is(CommonFlags.ERRORED);
global.set(CommonFlags.COMPILED);

var pendingElements = this.pendingElements;
pendingElements.add(global);

var module = this.module;
var initExpr: ExpressionRef = 0;
var typeNode = global.typeNode;
Expand All @@ -1006,12 +1011,18 @@ export class Compiler extends DiagnosticEmitter {
// Resolve type if annotated
if (typeNode) {
let resolvedType = this.resolver.resolveType(typeNode, global.parent); // reports
if (!resolvedType) return false;
if (!resolvedType) {
global.set(CommonFlags.ERRORED);
pendingElements.delete(global);
return false;
}
if (resolvedType == Type.void) {
this.error(
DiagnosticCode.Type_expected,
typeNode.range
);
global.set(CommonFlags.ERRORED);
pendingElements.delete(global);
return false;
}
global.setType(resolvedType);
Expand All @@ -1032,6 +1043,8 @@ export class Compiler extends DiagnosticEmitter {
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
initializerNode.range, this.currentType.toString(), "<auto>"
);
global.set(CommonFlags.ERRORED);
pendingElements.delete(global);
return false;
}
global.setType(this.currentType);
Expand All @@ -1042,6 +1055,8 @@ export class Compiler extends DiagnosticEmitter {
DiagnosticCode.Type_expected,
global.identifierNode.range.atEnd
);
global.set(CommonFlags.ERRORED);
pendingElements.delete(global);
return false;
}
}
Expand All @@ -1050,6 +1065,7 @@ export class Compiler extends DiagnosticEmitter {
if (global.is(CommonFlags.AMBIENT) && global.hasDecorator(DecoratorFlags.BUILTIN)) {
if (global.internalName == BuiltinNames.heap_base) this.runtimeFeatures |= RuntimeFeatures.HEAP;
else if (global.internalName == BuiltinNames.rtti_base) this.runtimeFeatures |= RuntimeFeatures.RTTI;
pendingElements.delete(global);
return true;
}

Expand All @@ -1072,16 +1088,17 @@ export class Compiler extends DiagnosticEmitter {
nativeType,
!isDeclaredConstant
);
global.set(CommonFlags.COMPILED);
pendingElements.delete(global);
return true;
}

// Importing mutable globals is not supported in the MVP
} else {
this.error(
DiagnosticCode.Feature_0_is_not_enabled,
global.declaration.range, "mutable-globals"
);
}
this.error(
DiagnosticCode.Feature_0_is_not_enabled,
global.declaration.range, "mutable-globals"
);
global.set(CommonFlags.ERRORED);
pendingElements.delete(global);
return false;
}

Expand Down Expand Up @@ -1167,6 +1184,8 @@ export class Compiler extends DiagnosticEmitter {
}
default: {
assert(false);
global.set(CommonFlags.ERRORED);
pendingElements.delete(global);
return false;
}
}
Expand Down Expand Up @@ -1200,16 +1219,20 @@ export class Compiler extends DiagnosticEmitter {
} else if (!isDeclaredInline) { // compile normally
module.addGlobal(internalName, nativeType, !isDeclaredConstant, initExpr);
}
pendingElements.delete(global);
return true;
}

// === Enums ====================================================================================

/** Compiles an enum. */
compileEnum(element: Enum): bool {
if (element.is(CommonFlags.COMPILED)) return true;
if (element.is(CommonFlags.COMPILED)) return !element.is(CommonFlags.ERRORED);
element.set(CommonFlags.COMPILED);

var pendingElements = this.pendingElements;
pendingElements.add(element);

var module = this.module;
var previousParent = this.currentParent;
this.currentParent = element;
Expand Down Expand Up @@ -1305,6 +1328,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
this.currentParent = previousParent;
pendingElements.delete(element);
return true;
}

Expand All @@ -1317,7 +1341,8 @@ export class Compiler extends DiagnosticEmitter {
/** Force compilation of stdlib alternative if a builtin. */
forceStdAlternative: bool = false
): bool {
if (instance.is(CommonFlags.COMPILED)) return true;
if (instance.is(CommonFlags.COMPILED)) return !instance.is(CommonFlags.ERRORED);

if (!forceStdAlternative) {
if (instance.hasDecorator(DecoratorFlags.BUILTIN)) return true;
if (instance.hasDecorator(DecoratorFlags.LAZY)) {
Expand All @@ -1326,9 +1351,11 @@ export class Compiler extends DiagnosticEmitter {
}
}

var previousType = this.currentType;
instance.set(CommonFlags.COMPILED);
var pendingElements = this.pendingElements;
pendingElements.add(instance);

var previousType = this.currentType;
var module = this.module;
var signature = instance.signature;
var bodyNode = instance.prototype.bodyNode;
Expand Down Expand Up @@ -1443,10 +1470,12 @@ export class Compiler extends DiagnosticEmitter {
instance.identifierNode.range
);
funcRef = 0; // TODO?
instance.set(CommonFlags.ERRORED);
}

instance.finalize(module, funcRef);
this.currentType = previousType;
pendingElements.delete(instance);
return true;
}

Expand Down Expand Up @@ -2973,18 +3002,29 @@ export class Compiler extends DiagnosticEmitter {
this.checkTypeSupported(type, typeNode);

if (initializerNode) {
let pendingElements = this.pendingElements;
let dummy = flow.addScopedDummyLocal(name, type); // pending dummy
pendingElements.add(dummy);
initExpr = this.compileExpression(initializerNode, type, // reports
Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN
);
initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr);
pendingElements.delete(dummy);
flow.freeScopedDummyLocal(name);
}

// Otherwise infer type from initializer
} else if (initializerNode) {
let pendingElements = this.pendingElements;
let temp = flow.addScopedDummyLocal(name, Type.auto); // pending dummy
pendingElements.add(temp);
initExpr = this.compileExpression(initializerNode, Type.auto,
Constraints.WILL_RETAIN
); // reports
initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr);
pendingElements.delete(temp);
flow.freeScopedDummyLocal(name);

if (this.currentType == Type.void) {
this.error(
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
Expand Down Expand Up @@ -5916,6 +5956,14 @@ export class Compiler extends DiagnosticEmitter {
}
case ElementKind.LOCAL:
case ElementKind.FIELD: {
if (this.pendingElements.has(target)) {
this.error(
DiagnosticCode.Variable_0_used_before_its_declaration,
expression.range,
target.internalName
);
return this.module.unreachable();
}
targetType = (<VariableLikeElement>target).type;
if (target.hasDecorator(DecoratorFlags.UNSAFE)) this.checkUnsafe(expression);
break;
Expand Down Expand Up @@ -8171,6 +8219,15 @@ export class Compiler extends DiagnosticEmitter {
let local = <Local>target;
let localType = local.type;
assert(localType != Type.void);
if (this.pendingElements.has(local)) {
this.error(
DiagnosticCode.Variable_0_used_before_its_declaration,
expression.range,
local.internalName
);
this.currentType = localType;
return module.unreachable();
}
if (local.is(CommonFlags.INLINED)) {
return this.compileInlineConstant(local, contextualType, constraints);
}
Expand Down Expand Up @@ -8198,6 +8255,15 @@ export class Compiler extends DiagnosticEmitter {
return module.unreachable();
}
let globalType = global.type;
if (this.pendingElements.has(global)) {
this.error(
DiagnosticCode.Variable_0_used_before_its_declaration,
expression.range,
global.internalName
);
this.currentType = globalType;
return module.unreachable();
}
assert(globalType != Type.void);
if (global.is(CommonFlags.INLINED)) {
return this.compileInlineConstant(global, contextualType, constraints);
Expand Down Expand Up @@ -9266,6 +9332,15 @@ export class Compiler extends DiagnosticEmitter {
if (!this.compileGlobal(global)) return module.unreachable(); // reports
let globalType = global.type;
assert(globalType != Type.void);
if (this.pendingElements.has(global)) {
this.error(
DiagnosticCode.Variable_0_used_before_its_declaration,
expression.range,
global.internalName
);
this.currentType = globalType;
return module.unreachable();
}
if (global.is(CommonFlags.INLINED)) {
return this.compileInlineConstant(global, ctxType, constraints);
}
Expand Down
2 changes: 2 additions & 0 deletions src/diagnosticMessages.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export enum DiagnosticCode {
A_class_can_only_implement_an_interface = 2422,
A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged = 2434,
Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses = 2445,
Variable_0_used_before_its_declaration = 2448,
The_type_argument_for_type_parameter_0_cannot_be_inferred_from_the_usage_Consider_specifying_the_type_arguments_explicitly = 2453,
Type_0_has_no_property_1 = 2460,
The_0_operator_cannot_be_applied_to_type_1 = 2469,
Expand Down Expand Up @@ -321,6 +322,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2422: return "A class can only implement an interface.";
case 2434: return "A namespace declaration cannot be located prior to a class or function with which it is merged.";
case 2445: return "Property '{0}' is protected and only accessible within class '{1}' and its subclasses.";
case 2448: return "Variable '{0}' used before its declaration.";
case 2453: return "The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.";
case 2460: return "Type '{0}' has no property '{1}'.";
case 2469: return "The '{0}' operator cannot be applied to type '{1}'.";
Expand Down
1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
"A class can only implement an interface.": 2422,
"A namespace declaration cannot be located prior to a class or function with which it is merged.": 2434,
"Property '{0}' is protected and only accessible within class '{1}' and its subclasses.": 2445,
"Variable '{0}' used before its declaration.": 2448,
"The type argument for type parameter '{0}' cannot be inferred from the usage. Consider specifying the type arguments explicitly.": 2453,
"Type '{0}' has no property '{1}'.": 2460,
"The '{0}' operator cannot be applied to type '{1}'.": 2469,
Expand Down
22 changes: 22 additions & 0 deletions src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ export class Flow {
var parentFunction = this.parentFunction;
var temps: Local[];
assert(local.type != null); // internal error
local.resetTemporaryName();
switch (<u32>local.type.toNativeType()) {
case <u32>NativeType.I32: {
let tempI32s = parentFunction.tempI32s;
Expand Down Expand Up @@ -395,6 +396,7 @@ export class Flow {
/** Adds a new scoped local of the specified name. */
addScopedLocal(name: string, type: Type, except: Set<i32> | null = null): Local {
var scopedLocal = this.getTempLocal(type, except);
scopedLocal.setTemporaryName(name);
var scopedLocals = this.scopedLocals;
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
else assert(!scopedLocals.has(name));
Expand All @@ -403,6 +405,17 @@ export class Flow {
return scopedLocal;
}

/** Adds a new scoped dummy local of the specified name. */
addScopedDummyLocal(name: string, type: Type): Local {
var scopedDummy = new Local(name, -1, type, this.parentFunction);
var scopedLocals = this.scopedLocals;
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
else assert(!scopedLocals.has(name));
scopedDummy.set(CommonFlags.SCOPED);
scopedLocals.set(name, scopedDummy);
return scopedDummy;
}

/** Adds a new scoped alias for the specified local. For example `super` aliased to the `this` local. */
addScopedAlias(name: string, type: Type, index: i32, reportNode: Node | null = null): Local {
var scopedLocals = this.scopedLocals;
Expand Down Expand Up @@ -450,6 +463,15 @@ export class Flow {
return false;
}

/** Frees a single scoped local by its name. */
freeScopedDummyLocal(name: string): void {
var scopedLocals = assert(this.scopedLocals);
assert(scopedLocals.has(name));
let local = assert(scopedLocals.get(name));
assert(local.index == -1);
scopedLocals.delete(name);
}

/** Frees this flow's scoped variables and returns its parent flow. */
freeScopedLocals(): void {
var scopedLocals = this.scopedLocals;
Expand Down
Loading