diff --git a/src/compiler.ts b/src/compiler.ts index 0123c30bea..136825d3bf 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -6717,6 +6717,8 @@ export class Compiler extends DiagnosticEmitter { // Compile omitted arguments with final argument locals blocked. Doesn't need to take care of // side-effects within earlier expressions because these already happened on set. this.currentFlow = flow; + var isConstructor = instance.is(CommonFlags.CONSTRUCTOR); + if (isConstructor) flow.set(FlowFlags.CTORPARAM_CONTEXT); for (let i = numArguments; i < numParameters; ++i) { let initType = parameterTypes[i]; let initExpr = this.compileExpression( @@ -6729,12 +6731,13 @@ export class Compiler extends DiagnosticEmitter { this.makeLocalAssignment(argumentLocal, initExpr, initType, false) ); } + flow.unset(FlowFlags.CTORPARAM_CONTEXT); // 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)) { + if (isConstructor) { let parent = instance.parent; assert(parent.kind == ElementKind.CLASS); this.checkFieldInitializationInFlow(parent, flow); @@ -6815,6 +6818,7 @@ export class Compiler extends DiagnosticEmitter { // accounting for additional locals and a proper `this` context. var previousFlow = this.currentFlow; var flow = stub.flow; + if (original.is(CommonFlags.CONSTRUCTOR)) flow.set(FlowFlags.CTORPARAM_CONTEXT); this.currentFlow = flow; // create a br_table switching over the number of optional parameters provided @@ -7640,10 +7644,18 @@ export class Compiler extends DiagnosticEmitter { this.currentType = this.options.usizeType; return module.unreachable(); } - if (actualFunction.is(CommonFlags.CONSTRUCTOR) && !(constraints & Constraints.IS_THIS)) { - let parent = actualFunction.parent; - assert(parent.kind == ElementKind.CLASS); - this.checkFieldInitialization(parent, expression); + if (actualFunction.is(CommonFlags.CONSTRUCTOR)) { + if (flow.is(FlowFlags.CTORPARAM_CONTEXT)) { + this.error( + DiagnosticCode._this_cannot_be_referenced_in_constructor_arguments, + expression.range + ); + } + if (!(constraints & Constraints.IS_THIS)) { + let parent = actualFunction.parent; + assert(parent.kind == ElementKind.CLASS); + this.checkFieldInitialization(parent, expression); + } } let thisLocal = assert(flow.lookupLocal(CommonNames.this_)); flow.set(FlowFlags.ACCESSES_THIS); @@ -7651,10 +7663,13 @@ export class Compiler extends DiagnosticEmitter { return module.local_get(thisLocal.index, thisType.toRef()); } case NodeKind.SUPER: { - let flow = this.currentFlow; - let actualFunction = flow.actualFunction; if (actualFunction.is(CommonFlags.CONSTRUCTOR)) { - if (!flow.is(FlowFlags.CALLS_SUPER)) { + if (flow.is(FlowFlags.CTORPARAM_CONTEXT)) { + this.error( + DiagnosticCode._super_cannot_be_referenced_in_constructor_arguments, + expression.range + ); + } else if (!flow.is(FlowFlags.CALLS_SUPER)) { // TS1034 in the parser effectively limits this to property accesses this.error( DiagnosticCode._super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class, @@ -10284,10 +10299,9 @@ export class Compiler extends DiagnosticEmitter { var module = this.module; var flow = this.currentFlow; var isInline = flow.isInline; - var thisLocalIndex = isInline - ? flow.lookupLocal(CommonNames.this_)!.index - : 0; + var thisLocalIndex = isInline ? flow.lookupLocal(CommonNames.this_)!.index : 0; var sizeTypeRef = this.options.sizeTypeRef; + var nonParameterFields: Field[] | null = null; // TODO: for (let member of members.values()) { for (let _values = Map_values(members), i = 0, k = _values.length; i < k; ++i) { @@ -10296,44 +10310,57 @@ export class Compiler extends DiagnosticEmitter { member.kind != ElementKind.FIELD || // not a field member.parent != classInstance // inherited field ) continue; - let field = member; assert(!field.isAny(CommonFlags.CONST)); - let fieldType = field.type; - let fieldTypeRef = fieldType.toRef(); let fieldPrototype = field.prototype; - let initializerNode = fieldPrototype.initializerNode; let parameterIndex = fieldPrototype.parameterIndex; - let initExpr: ExpressionRef; - let typeNode = field.typeNode; - if (typeNode) this.checkTypeSupported(fieldType, typeNode); - - // if declared as a constructor parameter, use its value - if (parameterIndex >= 0) { - initExpr = module.local_get( - isInline - ? flow.lookupLocal(field.name)!.index - : 1 + parameterIndex, // this is local 0 - fieldTypeRef - ); - - // fall back to use initializer if present - } else if (initializerNode) { - initExpr = this.compileExpression(initializerNode, fieldType, Constraints.CONV_IMPLICIT); - // otherwise initialize with zero - } else { - initExpr = this.makeZero(fieldType, fieldPrototype.declaration); + // Defer non-parameter fields until parameter fields are initialized + if (parameterIndex < 0) { + if (!nonParameterFields) nonParameterFields = new Array(); + nonParameterFields.push(field); + continue; } + // Initialize constructor parameter field + let fieldType = field.type; + let fieldTypeRef = fieldType.toRef(); + assert(!fieldPrototype.initializerNode); this.compileFieldSetter(field); stmts.push( module.call(field.internalSetterName, [ module.local_get(thisLocalIndex, sizeTypeRef), - initExpr + module.local_get( + isInline + ? flow.lookupLocal(field.name)!.index + : 1 + parameterIndex, // `this` is local 0 + fieldTypeRef + ) ], TypeRef.None) ); } + + // Initialize deferred non-parameter fields + if (nonParameterFields) { + for (let i = 0, k = nonParameterFields.length; i < k; ++i) { + let field = unchecked(nonParameterFields[i]); + let fieldType = field.type; + let fieldPrototype = field.prototype; + let initializerNode = fieldPrototype.initializerNode; + assert(fieldPrototype.parameterIndex < 0); + this.compileFieldSetter(field); + stmts.push( + module.call(field.internalSetterName, [ + module.local_get(thisLocalIndex, sizeTypeRef), + initializerNode // use initializer if present, otherwise initialize with zero + ? this.compileExpression(initializerNode, fieldType, Constraints.CONV_IMPLICIT) + : this.makeZero(fieldType, fieldPrototype.declaration) + ], TypeRef.None) + ); + } + } + + this.currentType = Type.void; return stmts; } diff --git a/src/diagnosticMessages.generated.ts b/src/diagnosticMessages.generated.ts index 212e2fee28..6690f6d67e 100644 --- a/src/diagnosticMessages.generated.ts +++ b/src/diagnosticMessages.generated.ts @@ -124,7 +124,9 @@ export enum DiagnosticCode { Type_0_is_not_assignable_to_type_1 = 2322, Index_signature_is_missing_in_type_0 = 2329, _this_cannot_be_referenced_in_current_location = 2332, + _this_cannot_be_referenced_in_constructor_arguments = 2333, _super_can_only_be_referenced_in_a_derived_class = 2335, + _super_cannot_be_referenced_in_constructor_arguments = 2336, Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors = 2337, Property_0_does_not_exist_on_type_1 = 2339, Property_0_is_private_and_only_accessible_within_class_1 = 2341, @@ -304,7 +306,9 @@ export function diagnosticCodeToString(code: DiagnosticCode): string { case 2322: return "Type '{0}' is not assignable to type '{1}'."; case 2329: return "Index signature is missing in type '{0}'."; case 2332: return "'this' cannot be referenced in current location."; + case 2333: return "'this' cannot be referenced in constructor arguments."; case 2335: return "'super' can only be referenced in a derived class."; + case 2336: return "'super' cannot be referenced in constructor arguments."; case 2337: return "Super calls are not permitted outside constructors or in nested functions inside constructors."; case 2339: return "Property '{0}' does not exist on type '{1}'."; case 2341: return "Property '{0}' is private and only accessible within class '{1}'."; diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 2607361e6c..8b28f67720 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -122,7 +122,9 @@ "Type '{0}' is not assignable to type '{1}'.": 2322, "Index signature is missing in type '{0}'.": 2329, "'this' cannot be referenced in current location.": 2332, + "'this' cannot be referenced in constructor arguments.": 2333, "'super' can only be referenced in a derived class.": 2335, + "'super' cannot be referenced in constructor arguments.": 2336, "Super calls are not permitted outside constructors or in nested functions inside constructors.": 2337, "Property '{0}' does not exist on type '{1}'.": 2339, "Property '{0}' is private and only accessible within class '{1}'.": 2341, diff --git a/src/flow.ts b/src/flow.ts index 91e64789df..100d97ac8e 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -129,6 +129,8 @@ export const enum FlowFlags { /** This is a flow with explicitly disabled bounds checking. */ UNCHECKED_CONTEXT = 1 << 15, + /** This is a flow compiling a constructor parameter. */ + CTORPARAM_CONTEXT = 1 << 16, // masks @@ -757,7 +759,7 @@ export class Flow { newFlags |= FlowFlags.TERMINATES; } - this.flags = newFlags | (thisFlags & FlowFlags.UNCHECKED_CONTEXT); + this.flags = newFlags | (thisFlags & (FlowFlags.UNCHECKED_CONTEXT | FlowFlags.CTORPARAM_CONTEXT)); // local flags var thisLocalFlags = this.localFlags; @@ -869,7 +871,7 @@ export class Flow { newFlags |= FlowFlags.TERMINATES; } - this.flags = newFlags | (this.flags & FlowFlags.UNCHECKED_CONTEXT); + this.flags = newFlags | (this.flags & (FlowFlags.UNCHECKED_CONTEXT | FlowFlags.CTORPARAM_CONTEXT)); // local flags var thisLocalFlags = this.localFlags; diff --git a/tests/compiler/class.optimized.wat b/tests/compiler/class.optimized.wat index bc0b236d29..2121160275 100644 --- a/tests/compiler/class.optimized.wat +++ b/tests/compiler/class.optimized.wat @@ -1727,7 +1727,7 @@ call $~lib/memory/memory.fill local.get $1 ) - (func $~lib/array/Array#set:buffer (param $0 i32) (param $1 i32) + (func $class/GenericInitializer#set:foo (param $0 i32) (param $1 i32) local.get $0 local.get $1 i32.store @@ -1826,7 +1826,7 @@ i32.store local.get $0 i32.const 0 - call $~lib/array/Array#set:buffer + call $class/GenericInitializer#set:foo local.get $0 i32.const 0 i32.store offset=4 @@ -1847,7 +1847,7 @@ call $~lib/memory/memory.fill local.get $0 local.get $1 - call $~lib/array/Array#set:buffer + call $class/GenericInitializer#set:foo local.get $0 local.get $1 i32.store offset=4 @@ -1863,7 +1863,7 @@ global.set $~lib/memory/__stack_pointer local.get $2 local.get $0 - call $~lib/array/Array#set:buffer + call $class/GenericInitializer#set:foo global.get $~lib/memory/__stack_pointer i32.const 4 i32.add diff --git a/tests/compiler/class.untouched.wat b/tests/compiler/class.untouched.wat index ac65a7b27a..2400078f6a 100644 --- a/tests/compiler/class.untouched.wat +++ b/tests/compiler/class.untouched.wat @@ -2525,6 +2525,15 @@ end end ) + (func $class/GenericInitializer#set:foo (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store + local.get $0 + local.get $1 + i32.const 0 + call $~lib/rt/itcms/__link + ) (func $~lib/array/Array#set:buffer (param $0 i32) (param $1 i32) local.get $0 local.get $1 @@ -2549,15 +2558,6 @@ local.get $1 i32.store offset=12 ) - (func $class/GenericInitializer#set:foo (param $0 i32) (param $1 i32) - local.get $0 - local.get $1 - i32.store - local.get $0 - local.get $1 - i32.const 0 - call $~lib/rt/itcms/__link - ) (func $class/testGenericInitializer i32.const 0 call $class/GenericInitializer#constructor diff --git a/tests/compiler/constructor-errors.json b/tests/compiler/constructor-errors.json new file mode 100644 index 0000000000..dd1ee8dbee --- /dev/null +++ b/tests/compiler/constructor-errors.json @@ -0,0 +1,23 @@ +{ + "asc_flags": [ + ], + "stderr": [ + "TS2333: 'this' cannot be referenced in constructor arguments.", + "b: i32 = this.a, // TS2333", + "TS2333: 'this' cannot be referenced in constructor arguments.", + "c: i32 = this.foo() // TS2333", + "TS2333: 'this' cannot be referenced in constructor arguments.", + "e: i32 = this.d, // TS2333", + "TS2333: 'this' cannot be referenced in constructor arguments.", + "f: i32 = this.bar() // TS2333", + "TS2336: 'super' cannot be referenced in constructor arguments.", + "h: i32 = super.g, // TS2336", + "TS2336: 'super' cannot be referenced in constructor arguments.", + "i: i32 = super.baz() // TS2336", + "TS2336: 'super' cannot be referenced in constructor arguments.", + "j: i32 = super.g, // TS2336", + "TS2336: 'super' cannot be referenced in constructor arguments.", + "k: i32 = super.baz() // TS2336", + "EOF" + ] +} diff --git a/tests/compiler/constructor-errors.ts b/tests/compiler/constructor-errors.ts new file mode 100644 index 0000000000..5dc75efc7f --- /dev/null +++ b/tests/compiler/constructor-errors.ts @@ -0,0 +1,52 @@ +class CtorAccessThis { + a: i32 = 0; + constructor( + public b: i32 = this.a, // TS2333 + public c: i32 = this.foo() // TS2333 + ) {} + foo(): i32 { return 0; } +} + +new CtorAccessThis(); + +class CtorAccessThisInline { + d: i32 = 0; + @inline + constructor( + public e: i32 = this.d, // TS2333 + public f: i32 = this.bar() // TS2333 + ) {} + bar(): i32 { return 0; } +} + +new CtorAccessThisInline(); + +class CtorSuper { + g: i32 = 0; + baz(): i32 { return 0; } +} + +class CtorAccessSuper extends CtorSuper { + constructor( + public h: i32 = super.g, // TS2336 + public i: i32 = super.baz() // TS2336 + ) { + super(); + } +} + +new CtorAccessSuper(); + +class CtorAccessSuperInline extends CtorSuper { + @inline + constructor( + public j: i32 = super.g, // TS2336 + public k: i32 = super.baz() // TS2336 + ) { + super(); + } +} + +new CtorAccessSuperInline(); + +ERROR("EOF"); diff --git a/tests/compiler/constructor.optimized.wat b/tests/compiler/constructor.optimized.wat index 8aa24f1c6f..10e7582938 100644 --- a/tests/compiler/constructor.optimized.wat +++ b/tests/compiler/constructor.optimized.wat @@ -27,7 +27,8 @@ (global $constructor/ctorReturns (mut i32) (i32.const 0)) (global $constructor/ctorConditionallyReturns (mut i32) (i32.const 0)) (global $constructor/ctorConditionallyReturnsThis (mut i32) (i32.const 0)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17932)) + (global $constructor/ctorFieldInitOrder (mut i32) (i32.const 0)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17988)) (memory $0 1) (data (i32.const 1036) "<") (data (i32.const 1048) "\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e") @@ -39,8 +40,10 @@ (data (i32.const 1304) "\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s") (data (i32.const 1372) "<") (data (i32.const 1384) "\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s") - (data (i32.const 1440) "\0d\00\00\00 \00\00\00\00\00\00\00 ") - (data (i32.const 1468) " \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 ") + (data (i32.const 1436) ",") + (data (i32.const 1448) "\01\00\00\00\1c\00\00\00c\00o\00n\00s\00t\00r\00u\00c\00t\00o\00r\00.\00t\00s") + (data (i32.const 1488) "\0e\00\00\00 \00\00\00\00\00\00\00 ") + (data (i32.const 1516) " \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 ") (export "memory" (memory $0)) (start $~start) (func $~lib/rt/itcms/visitRoots @@ -106,6 +109,12 @@ local.get $0 call $~lib/rt/itcms/__visit end + global.get $constructor/ctorFieldInitOrder + local.tee $0 + if + local.get $0 + call $~lib/rt/itcms/__visit + end i32.const 1248 call $~lib/rt/itcms/__visit i32.const 1056 @@ -196,7 +205,7 @@ if i32.const 0 local.get $0 - i32.const 17932 + i32.const 17988 i32.lt_u local.get $0 i32.load offset=8 @@ -247,7 +256,7 @@ i32.const 1 else local.get $2 - i32.const 1440 + i32.const 1488 i32.load i32.gt_u if @@ -261,7 +270,7 @@ local.get $2 i32.const 3 i32.shl - i32.const 1444 + i32.const 1492 i32.add i32.load i32.const 32 @@ -838,10 +847,10 @@ if unreachable end - i32.const 17936 + i32.const 18000 i32.const 0 i32.store - i32.const 19504 + i32.const 19568 i32.const 0 i32.store loop $for-loop|0 @@ -852,7 +861,7 @@ local.get $1 i32.const 2 i32.shl - i32.const 17936 + i32.const 18000 i32.add i32.const 0 i32.store offset=4 @@ -870,7 +879,7 @@ i32.add i32.const 2 i32.shl - i32.const 17936 + i32.const 18000 i32.add i32.const 0 i32.store offset=96 @@ -888,13 +897,13 @@ br $for-loop|0 end end - i32.const 17936 - i32.const 19508 + i32.const 18000 + i32.const 19572 memory.size i32.const 16 i32.shl call $~lib/rt/tlsf/addMemory - i32.const 17936 + i32.const 18000 global.set $~lib/rt/tlsf/ROOT ) (func $~lib/rt/itcms/step (result i32) @@ -978,7 +987,7 @@ local.set $0 loop $while-continue|0 local.get $0 - i32.const 17932 + i32.const 17988 i32.lt_u if local.get $0 @@ -1073,7 +1082,7 @@ unreachable end local.get $0 - i32.const 17932 + i32.const 17988 i32.lt_u if local.get $0 @@ -1096,7 +1105,7 @@ i32.const 4 i32.add local.tee $0 - i32.const 17932 + i32.const 17988 i32.ge_u if global.get $~lib/rt/tlsf/ROOT @@ -1785,7 +1794,7 @@ memory.size i32.const 16 i32.shl - i32.const 17932 + i32.const 17988 i32.sub i32.const 1 i32.shr_u @@ -1820,7 +1829,7 @@ global.set $~lib/memory/__stack_pointer block $folding-inner0 global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -1844,7 +1853,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -1871,7 +1880,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -1898,7 +1907,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -1928,7 +1937,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -1952,7 +1961,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -1979,7 +1988,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -2008,7 +2017,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -2031,7 +2040,7 @@ i32.sub global.set $~lib/memory/__stack_pointer global.get $~lib/memory/__stack_pointer - i32.const 1548 + i32.const 1604 i32.lt_s br_if $folding-inner0 global.get $~lib/memory/__stack_pointer @@ -2050,10 +2059,119 @@ global.set $~lib/memory/__stack_pointer local.get $0 global.set $constructor/ctorConditionallyReturnsThis + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1604 + i32.lt_s + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $0 + i32.const 0 + i32.store + local.get $0 + i32.const 12 + i32.const 13 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store + local.get $0 + i32.const 1 + i32.store offset=4 + local.get $0 + i32.const 2 + i32.store offset=8 + local.get $0 + local.get $0 + i32.load offset=4 + local.get $0 + i32.load offset=8 + i32.add + i32.store + local.get $0 + i32.load offset=4 + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1456 + i32.const 94 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=8 + i32.const 2 + i32.ne + if + i32.const 0 + i32.const 1456 + i32.const 96 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1456 + i32.const 97 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 + global.set $constructor/ctorFieldInitOrder + global.get $constructor/ctorFieldInitOrder + i32.load offset=4 + i32.const 1 + i32.ne + if + i32.const 0 + i32.const 1456 + i32.const 102 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $constructor/ctorFieldInitOrder + i32.load offset=8 + i32.const 2 + i32.ne + if + i32.const 0 + i32.const 1456 + i32.const 103 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $constructor/ctorFieldInitOrder + i32.load + i32.const 3 + i32.ne + if + i32.const 0 + i32.const 1456 + i32.const 104 + i32.const 1 + call $~lib/builtins/abort + unreachable + end return end - i32.const 17952 - i32.const 18000 + i32.const 18016 + i32.const 18064 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -2061,35 +2179,38 @@ ) (func $~lib/rt/__visit_members (param $0 i32) block $invalid - block $constructor/CtorConditionallyReturnsThis - block $constructor/CtorConditionallyReturns - block $constructor/CtorReturns - block $constructor/JustFieldNoInit - block $constructor/JustFieldInit - block $constructor/None - block $constructor/EmptyCtorWithFieldAccess - block $constructor/EmptyCtorWithFieldNoInit - block $constructor/EmptyCtorWithFieldInit - block $constructor/EmptyCtor - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $constructor/EmptyCtor $constructor/EmptyCtorWithFieldInit $constructor/EmptyCtorWithFieldNoInit $constructor/EmptyCtorWithFieldAccess $constructor/None $constructor/JustFieldInit $constructor/JustFieldNoInit $constructor/CtorReturns $constructor/CtorConditionallyReturns $constructor/CtorConditionallyReturnsThis $invalid + block $constructor/CtorFieldInitOrder + block $constructor/CtorConditionallyReturnsThis + block $constructor/CtorConditionallyReturns + block $constructor/CtorReturns + block $constructor/JustFieldNoInit + block $constructor/JustFieldInit + block $constructor/None + block $constructor/EmptyCtorWithFieldAccess + block $constructor/EmptyCtorWithFieldNoInit + block $constructor/EmptyCtorWithFieldInit + block $constructor/EmptyCtor + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $constructor/EmptyCtor $constructor/EmptyCtorWithFieldInit $constructor/EmptyCtorWithFieldNoInit $constructor/EmptyCtorWithFieldAccess $constructor/None $constructor/JustFieldInit $constructor/JustFieldNoInit $constructor/CtorReturns $constructor/CtorConditionallyReturns $constructor/CtorConditionallyReturnsThis $constructor/CtorFieldInitOrder $invalid + end + return end return end - return - end - local.get $0 - i32.load - local.tee $0 - if local.get $0 - call $~lib/rt/itcms/__visit + i32.load + local.tee $0 + if + local.get $0 + call $~lib/rt/itcms/__visit + end + return end return end diff --git a/tests/compiler/constructor.ts b/tests/compiler/constructor.ts index 7a40fbc040..59b7cc29f3 100644 --- a/tests/compiler/constructor.ts +++ b/tests/compiler/constructor.ts @@ -86,3 +86,19 @@ class CtorConditionallyReturnsThis { } var ctorConditionallyReturnsThis = new CtorConditionallyReturnsThis(); + +class CtorFieldInitOrder { + c: i32 = this.a + this.b; + constructor(public a: i32, public b: i32 = 2) { + assert(a == 1); + assert(this.a == 1); + assert(b == 2); + assert(this.b == 2); + assert(this.c == 3); + } +} + +var ctorFieldInitOrder = new CtorFieldInitOrder(1); +assert(ctorFieldInitOrder.a == 1); +assert(ctorFieldInitOrder.b == 2); +assert(ctorFieldInitOrder.c == 3); diff --git a/tests/compiler/constructor.untouched.wat b/tests/compiler/constructor.untouched.wat index b5949afb5d..e774c2657e 100644 --- a/tests/compiler/constructor.untouched.wat +++ b/tests/compiler/constructor.untouched.wat @@ -5,8 +5,8 @@ (type $none_=>_none (func)) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) - (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $none_=>_i32 (func (result i32))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) @@ -32,10 +32,11 @@ (global $constructor/b (mut i32) (i32.const 1)) (global $constructor/ctorConditionallyReturns (mut i32) (i32.const 0)) (global $constructor/ctorConditionallyReturnsThis (mut i32) (i32.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 416)) - (global $~lib/memory/__data_end i32 (i32.const 524)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16908)) - (global $~lib/memory/__heap_base i32 (i32.const 16908)) + (global $constructor/ctorFieldInitOrder (mut i32) (i32.const 0)) + (global $~lib/rt/__rtti_base i32 (i32.const 464)) + (global $~lib/memory/__data_end i32 (i32.const 580)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16964)) + (global $~lib/memory/__heap_base i32 (i32.const 16964)) (memory $0 1) (data (i32.const 12) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00(\00\00\00A\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e\00\00\00\00\00") (data (i32.const 76) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00 \00\00\00~\00l\00i\00b\00/\00r\00t\00/\00i\00t\00c\00m\00s\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00") @@ -45,7 +46,8 @@ (data (i32.const 268) ",\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\14\00\00\00~\00l\00i\00b\00/\00r\00t\00.\00t\00s\00\00\00\00\00\00\00\00\00") (data (i32.const 320) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") (data (i32.const 348) "<\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00~\00l\00i\00b\00/\00r\00t\00/\00t\00l\00s\00f\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 416) "\0d\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00") + (data (i32.const 412) ",\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\1c\00\00\00c\00o\00n\00s\00t\00r\00u\00c\00t\00o\00r\00.\00t\00s\00") + (data (i32.const 464) "\0e\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00 \00\00\00\00\00\00\00") (table $0 1 funcref) (elem $0 (i32.const 1)) (export "memory" (memory $0)) @@ -2383,6 +2385,21 @@ (func $constructor/CtorReturns#constructor (param $0 i32) (result i32) i32.const 0 ) + (func $constructor/CtorFieldInitOrder#set:a (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=4 + ) + (func $constructor/CtorFieldInitOrder#set:b (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store offset=8 + ) + (func $constructor/CtorFieldInitOrder#set:c (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + i32.store + ) (func $start:constructor memory.size i32.const 16 @@ -2431,6 +2448,50 @@ i32.const 0 call $constructor/CtorConditionallyReturnsThis#constructor global.set $constructor/ctorConditionallyReturnsThis + i32.const 0 + i32.const 1 + i32.const 2 + call $constructor/CtorFieldInitOrder#constructor + global.set $constructor/ctorFieldInitOrder + global.get $constructor/ctorFieldInitOrder + i32.load offset=4 + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 102 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $constructor/ctorFieldInitOrder + i32.load offset=8 + i32.const 2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 103 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $constructor/ctorFieldInitOrder + i32.load + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 104 + i32.const 1 + call $~lib/builtins/abort + unreachable + end ) (func $~lib/rt/__visit_globals (param $0 i32) (local $1 i32) @@ -2504,6 +2565,13 @@ local.get $0 call $~lib/rt/itcms/__visit end + global.get $constructor/ctorFieldInitOrder + local.tee $1 + if + local.get $1 + local.get $0 + call $~lib/rt/itcms/__visit + end i32.const 224 local.get $0 call $~lib/rt/itcms/__visit @@ -2524,32 +2592,35 @@ ) (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) block $invalid - block $constructor/CtorConditionallyReturnsThis - block $constructor/CtorConditionallyReturns - block $constructor/CtorReturns - block $constructor/JustFieldNoInit - block $constructor/JustFieldInit - block $constructor/None - block $constructor/EmptyCtorWithFieldAccess - block $constructor/EmptyCtorWithFieldNoInit - block $constructor/EmptyCtorWithFieldInit - block $constructor/EmptyCtor - block $~lib/arraybuffer/ArrayBufferView - block $~lib/string/String - block $~lib/arraybuffer/ArrayBuffer - local.get $0 - i32.const 8 - i32.sub - i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $constructor/EmptyCtor $constructor/EmptyCtorWithFieldInit $constructor/EmptyCtorWithFieldNoInit $constructor/EmptyCtorWithFieldAccess $constructor/None $constructor/JustFieldInit $constructor/JustFieldNoInit $constructor/CtorReturns $constructor/CtorConditionallyReturns $constructor/CtorConditionallyReturnsThis $invalid + block $constructor/CtorFieldInitOrder + block $constructor/CtorConditionallyReturnsThis + block $constructor/CtorConditionallyReturns + block $constructor/CtorReturns + block $constructor/JustFieldNoInit + block $constructor/JustFieldInit + block $constructor/None + block $constructor/EmptyCtorWithFieldAccess + block $constructor/EmptyCtorWithFieldNoInit + block $constructor/EmptyCtorWithFieldInit + block $constructor/EmptyCtor + block $~lib/arraybuffer/ArrayBufferView + block $~lib/string/String + block $~lib/arraybuffer/ArrayBuffer + local.get $0 + i32.const 8 + i32.sub + i32.load + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $~lib/arraybuffer/ArrayBufferView $constructor/EmptyCtor $constructor/EmptyCtorWithFieldInit $constructor/EmptyCtorWithFieldNoInit $constructor/EmptyCtorWithFieldAccess $constructor/None $constructor/JustFieldInit $constructor/JustFieldNoInit $constructor/CtorReturns $constructor/CtorConditionallyReturns $constructor/CtorConditionallyReturnsThis $constructor/CtorFieldInitOrder $invalid + end + return end return end + local.get $0 + local.get $1 + call $~lib/arraybuffer/ArrayBufferView~visit return end - local.get $0 - local.get $1 - call $~lib/arraybuffer/ArrayBufferView~visit return end return @@ -2582,8 +2653,8 @@ global.get $~lib/memory/__data_end i32.lt_s if - i32.const 16928 - i32.const 16976 + i32.const 16992 + i32.const 17040 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -2882,4 +2953,108 @@ global.set $~lib/memory/__stack_pointer local.get $1 ) + (func $constructor/CtorFieldInitOrder#constructor (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + local.get $0 + i32.eqz + if + global.get $~lib/memory/__stack_pointer + i32.const 12 + i32.const 13 + call $~lib/rt/itcms/__new + local.tee $0 + i32.store + end + local.get $0 + local.get $1 + call $constructor/CtorFieldInitOrder#set:a + local.get $0 + local.get $2 + call $constructor/CtorFieldInitOrder#set:b + local.get $0 + local.get $0 + i32.load offset=4 + local.get $0 + i32.load offset=8 + i32.add + call $constructor/CtorFieldInitOrder#set:c + local.get $1 + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 93 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=4 + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 94 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + local.get $2 + i32.const 2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 95 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load offset=8 + i32.const 2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 96 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + local.get $0 + i32.load + i32.const 3 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 432 + i32.const 97 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + local.get $0 + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + ) ) diff --git a/tests/compiler/issues/1225.optimized.wat b/tests/compiler/issues/1225.optimized.wat index b20c57acb5..44a877c3be 100644 --- a/tests/compiler/issues/1225.optimized.wat +++ b/tests/compiler/issues/1225.optimized.wat @@ -1649,15 +1649,15 @@ local.tee $0 i32.store local.get $0 + i32.const 4 + i32.store offset=8 + local.get $0 i32.const 0 i32.store local.get $0 i32.const 0 i32.store offset=4 local.get $0 - i32.const 4 - i32.store offset=8 - local.get $0 local.get $0 i32.load offset=8 i32.store offset=4 diff --git a/tests/compiler/issues/1225.untouched.wat b/tests/compiler/issues/1225.untouched.wat index 4823771c9e..48a6813269 100644 --- a/tests/compiler/issues/1225.untouched.wat +++ b/tests/compiler/issues/1225.untouched.wat @@ -2565,15 +2565,15 @@ i32.store end local.get $0 + local.get $1 + call $issues/1225/X#set:x + local.get $0 i32.const 0 call $issues/1225/X#set:normal local.get $0 i32.const 0 call $issues/1225/X#set:viaThis local.get $0 - local.get $1 - call $issues/1225/X#set:x - local.get $0 local.get $0 i32.load offset=8 call $issues/1225/X#set:viaThis diff --git a/tests/compiler/std/date.optimized.wat b/tests/compiler/std/date.optimized.wat index c89a8613c8..377501f018 100644 --- a/tests/compiler/std/date.optimized.wat +++ b/tests/compiler/std/date.optimized.wat @@ -9321,6 +9321,9 @@ local.tee $1 i32.store local.get $1 + local.get $0 + i64.store offset=16 + local.get $1 i32.const 0 i32.store local.get $1 @@ -9329,9 +9332,6 @@ local.get $1 i32.const 0 i32.store offset=8 - local.get $1 - local.get $0 - i64.store offset=16 local.get $0 i64.const -8640000000000000 i64.lt_s diff --git a/tests/compiler/std/date.untouched.wat b/tests/compiler/std/date.untouched.wat index 877b20efb8..30571f43ea 100644 --- a/tests/compiler/std/date.untouched.wat +++ b/tests/compiler/std/date.untouched.wat @@ -10739,6 +10739,9 @@ i32.store end local.get $0 + local.get $1 + call $~lib/date/Date#set:epochMillis + local.get $0 i32.const 0 call $~lib/date/Date#set:year local.get $0 @@ -10747,9 +10750,6 @@ local.get $0 i32.const 0 call $~lib/date/Date#set:day - local.get $0 - local.get $1 - call $~lib/date/Date#set:epochMillis local.get $1 call $~lib/date/invalidDate if