Skip to content
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

Handle strict field initialization in inlined ctors #1369

Merged
merged 1 commit into from
Jul 1, 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
21 changes: 18 additions & 3 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7110,6 +7110,13 @@ export class Compiler extends DiagnosticEmitter {
// Compile the called function's body in the scope of the inlined flow
this.compileFunctionBody(instance, body);

// If a constructor, perform field init checks on its flow directly
if (instance.is(CommonFlags.CONSTRUCTOR)) {
let parent = instance.parent;
assert(parent.kind == ElementKind.CLASS);
this.checkFieldInitializationInFlow(<Class>parent, flow);
}

// Free any new scoped locals and reset to the original flow
if (!flow.is(FlowFlags.TERMINATES)) {
this.performAutoreleases(flow, body);
Expand Down Expand Up @@ -9326,7 +9333,11 @@ export class Compiler extends DiagnosticEmitter {
if (!classInstance) return module.unreachable();
if (contextualType == Type.void) constraints |= Constraints.WILL_DROP;
var ctor = this.ensureConstructor(classInstance, expression);
this.checkFieldInitialization(classInstance, expression);
if (!ctor.hasDecorator(DecoratorFlags.INLINE)) {
// Inlined ctors haven't been compiled yet and are checked upon inline
// compilation of their body instead.
this.checkFieldInitialization(classInstance, expression);
}
return this.compileInstantiate(ctor, expression.args, constraints, expression);
}

Expand Down Expand Up @@ -9453,10 +9464,14 @@ export class Compiler extends DiagnosticEmitter {
checkFieldInitialization(classInstance: Class, relatedNode: Node | null = null): void {
if (classInstance.didCheckFieldInitialization) return;
classInstance.didCheckFieldInitialization = true;
var ctor = assert(classInstance.constructorInstance);
this.checkFieldInitializationInFlow(classInstance, ctor.flow, relatedNode);
}

/** Checks that all class fields have been initialized in the specified flow. */
checkFieldInitializationInFlow(classInstance: Class, flow: Flow, relatedNode: Node | null = null): void {
var members = classInstance.members;
if (members) {
let ctor = assert(classInstance.constructorInstance);
let flow = ctor.flow;
for (let _values = Map_values(members), i = 0, k = _values.length; i < k; ++i) {
let element = _values[i];
if (element.kind == ElementKind.FIELD && element.parent == classInstance) {
Expand Down
1 change: 1 addition & 0 deletions tests/compiler/field-initialization-errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"stderr": [
"TS2564: Property 'field-initialization-errors/Ref.a' has no initializer",
"TS2564: Property 'field-initialization-errors/Ref_Ctor.a' has no initializer",
"TS2564: Property 'field-initialization-errors/Ref_InlineCtor.a' has no initializer",
"TS2564: Property 'field-initialization-errors/Ref_Ctor_Branch.a' has no initializer",
"TS2565: Property 'field-initialization-errors/Ref_Ctor_Use_Init.a' is used before being assigned.",
"TS2564: Property 'field-initialization-errors/Ref_Ctor_Call_Init.a' has no initializer",
Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/field-initialization-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ class Ref_Ctor {
new Ref_Ctor();
}

// Uninitialized with inline ctor
class Ref_InlineCtor {
a: ArrayBuffer; // TS2564
@inline constructor() {
}
}
{
new Ref_InlineCtor();
}

// Uninitialized in any branch
class Ref_Ctor_Branch {
a: ArrayBuffer; // TS2564
Expand Down
46 changes: 46 additions & 0 deletions tests/compiler/field-initialization.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,52 @@
call $~lib/builtins/abort
unreachable
end
i32.const 4
i32.const 23
call $~lib/rt/stub/__alloc
local.tee $0
i32.const 0
i32.const 0
call $~lib/rt/stub/__alloc
i32.store
local.get $0
i32.load
i32.eqz
if
i32.const 0
i32.const 1040
i32.const 218
i32.const 3
call $~lib/builtins/abort
unreachable
end
i32.const 4
i32.const 24
call $~lib/rt/stub/__alloc
local.tee $0
i32.const 0
i32.store
i32.const 0
i32.const 0
call $~lib/rt/stub/__alloc
local.set $1
local.get $0
i32.load
drop
local.get $0
local.get $1
i32.store
local.get $0
i32.load
i32.eqz
if
i32.const 0
i32.const 1040
i32.const 230
i32.const 3
call $~lib/builtins/abort
unreachable
end
)
(func $~start
call $start:field-initialization
Expand Down
25 changes: 25 additions & 0 deletions tests/compiler/field-initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,28 @@ class Flow_Balanced {
let o = new Flow_Balanced(true);
assert(o.a != null);
}

// inlined ctors

class Ref_Init_InlineCtor {
a: ArrayBuffer = new ArrayBuffer(0); // OK
@inline
constructor() {
}
}
{
let o = new Ref_Init_InlineCtor();
assert(o.a != null);
}

class Ref_InlineCtor_Init {
a: ArrayBuffer; // OK (in ctor)
@inline
constructor() {
this.a = new ArrayBuffer(0);
}
}
{
let o = new Ref_InlineCtor_Init();
assert(o.a != null);
}
75 changes: 75 additions & 0 deletions tests/compiler/field-initialization.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,81 @@
end
local.get $4
call $~lib/rt/stub/__release
i32.const 0
local.set $1
local.get $1
i32.eqz
if
i32.const 4
i32.const 23
call $~lib/rt/stub/__alloc
call $~lib/rt/stub/__retain
local.set $1
end
local.get $1
i32.const 0
i32.const 0
call $~lib/arraybuffer/ArrayBuffer#constructor
i32.store
local.get $1
local.set $1
local.get $1
i32.load
i32.const 0
i32.ne
i32.eqz
if
i32.const 0
i32.const 32
i32.const 218
i32.const 3
call $~lib/builtins/abort
unreachable
end
local.get $1
call $~lib/rt/stub/__release
i32.const 0
local.set $0
local.get $0
i32.eqz
if
i32.const 4
i32.const 24
call $~lib/rt/stub/__alloc
call $~lib/rt/stub/__retain
local.set $0
end
local.get $0
i32.const 0
i32.store
local.get $0
local.tee $2
i32.const 0
i32.const 0
call $~lib/arraybuffer/ArrayBuffer#constructor
local.set $3
local.get $2
i32.load
call $~lib/rt/stub/__release
local.get $3
i32.store
local.get $0
local.set $0
local.get $0
i32.load
i32.const 0
i32.ne
i32.eqz
if
i32.const 0
i32.const 32
i32.const 230
i32.const 3
call $~lib/builtins/abort
unreachable
end
local.get $0
call $~lib/rt/stub/__release
)
(func $~start
call $start:field-initialization
Expand Down