Skip to content

Commit

Permalink
fix: Handle strict field initialization in inlined ctors (#1369)
Browse files Browse the repository at this point in the history
  • Loading branch information
dcodeIO authored Jul 1, 2020
1 parent 57a55d3 commit 36c706d
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 3 deletions.
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

0 comments on commit 36c706d

Please sign in to comment.