From 36c706dd3df4048342e871074d29bf0515e86e39 Mon Sep 17 00:00:00 2001 From: Daniel Wirtz Date: Wed, 1 Jul 2020 16:54:44 +0200 Subject: [PATCH] fix: Handle strict field initialization in inlined ctors (#1369) --- src/compiler.ts | 21 +++++- .../compiler/field-initialization-errors.json | 1 + tests/compiler/field-initialization-errors.ts | 10 +++ .../field-initialization.optimized.wat | 46 ++++++++++++ tests/compiler/field-initialization.ts | 25 +++++++ .../field-initialization.untouched.wat | 75 +++++++++++++++++++ 6 files changed, 175 insertions(+), 3 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 4108c3aa2d..c3341934b5 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -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(parent, flow); + } + // Free any new scoped locals and reset to the original flow if (!flow.is(FlowFlags.TERMINATES)) { this.performAutoreleases(flow, body); @@ -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); } @@ -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) { diff --git a/tests/compiler/field-initialization-errors.json b/tests/compiler/field-initialization-errors.json index f4dcc1ced1..c4c505491c 100644 --- a/tests/compiler/field-initialization-errors.json +++ b/tests/compiler/field-initialization-errors.json @@ -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", diff --git a/tests/compiler/field-initialization-errors.ts b/tests/compiler/field-initialization-errors.ts index 3281ac317f..671486b24e 100644 --- a/tests/compiler/field-initialization-errors.ts +++ b/tests/compiler/field-initialization-errors.ts @@ -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 diff --git a/tests/compiler/field-initialization.optimized.wat b/tests/compiler/field-initialization.optimized.wat index d268a19863..6f20599bb8 100644 --- a/tests/compiler/field-initialization.optimized.wat +++ b/tests/compiler/field-initialization.optimized.wat @@ -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 diff --git a/tests/compiler/field-initialization.ts b/tests/compiler/field-initialization.ts index 19f90a57c9..223f336809 100644 --- a/tests/compiler/field-initialization.ts +++ b/tests/compiler/field-initialization.ts @@ -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); +} diff --git a/tests/compiler/field-initialization.untouched.wat b/tests/compiler/field-initialization.untouched.wat index ea917db9da..0517dcbf9c 100644 --- a/tests/compiler/field-initialization.untouched.wat +++ b/tests/compiler/field-initialization.untouched.wat @@ -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