Skip to content

Commit 36c706d

Browse files
authored
fix: Handle strict field initialization in inlined ctors (#1369)
1 parent 57a55d3 commit 36c706d

6 files changed

+175
-3
lines changed

Diff for: src/compiler.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -7110,6 +7110,13 @@ export class Compiler extends DiagnosticEmitter {
71107110
// Compile the called function's body in the scope of the inlined flow
71117111
this.compileFunctionBody(instance, body);
71127112

7113+
// If a constructor, perform field init checks on its flow directly
7114+
if (instance.is(CommonFlags.CONSTRUCTOR)) {
7115+
let parent = instance.parent;
7116+
assert(parent.kind == ElementKind.CLASS);
7117+
this.checkFieldInitializationInFlow(<Class>parent, flow);
7118+
}
7119+
71137120
// Free any new scoped locals and reset to the original flow
71147121
if (!flow.is(FlowFlags.TERMINATES)) {
71157122
this.performAutoreleases(flow, body);
@@ -9326,7 +9333,11 @@ export class Compiler extends DiagnosticEmitter {
93269333
if (!classInstance) return module.unreachable();
93279334
if (contextualType == Type.void) constraints |= Constraints.WILL_DROP;
93289335
var ctor = this.ensureConstructor(classInstance, expression);
9329-
this.checkFieldInitialization(classInstance, expression);
9336+
if (!ctor.hasDecorator(DecoratorFlags.INLINE)) {
9337+
// Inlined ctors haven't been compiled yet and are checked upon inline
9338+
// compilation of their body instead.
9339+
this.checkFieldInitialization(classInstance, expression);
9340+
}
93309341
return this.compileInstantiate(ctor, expression.args, constraints, expression);
93319342
}
93329343

@@ -9453,10 +9464,14 @@ export class Compiler extends DiagnosticEmitter {
94539464
checkFieldInitialization(classInstance: Class, relatedNode: Node | null = null): void {
94549465
if (classInstance.didCheckFieldInitialization) return;
94559466
classInstance.didCheckFieldInitialization = true;
9467+
var ctor = assert(classInstance.constructorInstance);
9468+
this.checkFieldInitializationInFlow(classInstance, ctor.flow, relatedNode);
9469+
}
9470+
9471+
/** Checks that all class fields have been initialized in the specified flow. */
9472+
checkFieldInitializationInFlow(classInstance: Class, flow: Flow, relatedNode: Node | null = null): void {
94569473
var members = classInstance.members;
94579474
if (members) {
9458-
let ctor = assert(classInstance.constructorInstance);
9459-
let flow = ctor.flow;
94609475
for (let _values = Map_values(members), i = 0, k = _values.length; i < k; ++i) {
94619476
let element = _values[i];
94629477
if (element.kind == ElementKind.FIELD && element.parent == classInstance) {

Diff for: tests/compiler/field-initialization-errors.json

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"stderr": [
66
"TS2564: Property 'field-initialization-errors/Ref.a' has no initializer",
77
"TS2564: Property 'field-initialization-errors/Ref_Ctor.a' has no initializer",
8+
"TS2564: Property 'field-initialization-errors/Ref_InlineCtor.a' has no initializer",
89
"TS2564: Property 'field-initialization-errors/Ref_Ctor_Branch.a' has no initializer",
910
"TS2565: Property 'field-initialization-errors/Ref_Ctor_Use_Init.a' is used before being assigned.",
1011
"TS2564: Property 'field-initialization-errors/Ref_Ctor_Call_Init.a' has no initializer",

Diff for: tests/compiler/field-initialization-errors.ts

+10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ class Ref_Ctor {
1616
new Ref_Ctor();
1717
}
1818

19+
// Uninitialized with inline ctor
20+
class Ref_InlineCtor {
21+
a: ArrayBuffer; // TS2564
22+
@inline constructor() {
23+
}
24+
}
25+
{
26+
new Ref_InlineCtor();
27+
}
28+
1929
// Uninitialized in any branch
2030
class Ref_Ctor_Branch {
2131
a: ArrayBuffer; // TS2564

Diff for: tests/compiler/field-initialization.optimized.wat

+46
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,52 @@
817817
call $~lib/builtins/abort
818818
unreachable
819819
end
820+
i32.const 4
821+
i32.const 23
822+
call $~lib/rt/stub/__alloc
823+
local.tee $0
824+
i32.const 0
825+
i32.const 0
826+
call $~lib/rt/stub/__alloc
827+
i32.store
828+
local.get $0
829+
i32.load
830+
i32.eqz
831+
if
832+
i32.const 0
833+
i32.const 1040
834+
i32.const 218
835+
i32.const 3
836+
call $~lib/builtins/abort
837+
unreachable
838+
end
839+
i32.const 4
840+
i32.const 24
841+
call $~lib/rt/stub/__alloc
842+
local.tee $0
843+
i32.const 0
844+
i32.store
845+
i32.const 0
846+
i32.const 0
847+
call $~lib/rt/stub/__alloc
848+
local.set $1
849+
local.get $0
850+
i32.load
851+
drop
852+
local.get $0
853+
local.get $1
854+
i32.store
855+
local.get $0
856+
i32.load
857+
i32.eqz
858+
if
859+
i32.const 0
860+
i32.const 1040
861+
i32.const 230
862+
i32.const 3
863+
call $~lib/builtins/abort
864+
unreachable
865+
end
820866
)
821867
(func $~start
822868
call $start:field-initialization

Diff for: tests/compiler/field-initialization.ts

+25
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,28 @@ class Flow_Balanced {
204204
let o = new Flow_Balanced(true);
205205
assert(o.a != null);
206206
}
207+
208+
// inlined ctors
209+
210+
class Ref_Init_InlineCtor {
211+
a: ArrayBuffer = new ArrayBuffer(0); // OK
212+
@inline
213+
constructor() {
214+
}
215+
}
216+
{
217+
let o = new Ref_Init_InlineCtor();
218+
assert(o.a != null);
219+
}
220+
221+
class Ref_InlineCtor_Init {
222+
a: ArrayBuffer; // OK (in ctor)
223+
@inline
224+
constructor() {
225+
this.a = new ArrayBuffer(0);
226+
}
227+
}
228+
{
229+
let o = new Ref_InlineCtor_Init();
230+
assert(o.a != null);
231+
}

Diff for: tests/compiler/field-initialization.untouched.wat

+75
Original file line numberDiff line numberDiff line change
@@ -1583,6 +1583,81 @@
15831583
end
15841584
local.get $4
15851585
call $~lib/rt/stub/__release
1586+
i32.const 0
1587+
local.set $1
1588+
local.get $1
1589+
i32.eqz
1590+
if
1591+
i32.const 4
1592+
i32.const 23
1593+
call $~lib/rt/stub/__alloc
1594+
call $~lib/rt/stub/__retain
1595+
local.set $1
1596+
end
1597+
local.get $1
1598+
i32.const 0
1599+
i32.const 0
1600+
call $~lib/arraybuffer/ArrayBuffer#constructor
1601+
i32.store
1602+
local.get $1
1603+
local.set $1
1604+
local.get $1
1605+
i32.load
1606+
i32.const 0
1607+
i32.ne
1608+
i32.eqz
1609+
if
1610+
i32.const 0
1611+
i32.const 32
1612+
i32.const 218
1613+
i32.const 3
1614+
call $~lib/builtins/abort
1615+
unreachable
1616+
end
1617+
local.get $1
1618+
call $~lib/rt/stub/__release
1619+
i32.const 0
1620+
local.set $0
1621+
local.get $0
1622+
i32.eqz
1623+
if
1624+
i32.const 4
1625+
i32.const 24
1626+
call $~lib/rt/stub/__alloc
1627+
call $~lib/rt/stub/__retain
1628+
local.set $0
1629+
end
1630+
local.get $0
1631+
i32.const 0
1632+
i32.store
1633+
local.get $0
1634+
local.tee $2
1635+
i32.const 0
1636+
i32.const 0
1637+
call $~lib/arraybuffer/ArrayBuffer#constructor
1638+
local.set $3
1639+
local.get $2
1640+
i32.load
1641+
call $~lib/rt/stub/__release
1642+
local.get $3
1643+
i32.store
1644+
local.get $0
1645+
local.set $0
1646+
local.get $0
1647+
i32.load
1648+
i32.const 0
1649+
i32.ne
1650+
i32.eqz
1651+
if
1652+
i32.const 0
1653+
i32.const 32
1654+
i32.const 230
1655+
i32.const 3
1656+
call $~lib/builtins/abort
1657+
unreachable
1658+
end
1659+
local.get $0
1660+
call $~lib/rt/stub/__release
15861661
)
15871662
(func $~start
15881663
call $start:field-initialization

0 commit comments

Comments
 (0)