From ef100642340de82787aaebb316118b25e2769fe8 Mon Sep 17 00:00:00 2001 From: dcode Date: Wed, 3 Feb 2021 23:55:12 +0100 Subject: [PATCH 1/3] Properly handle semantically anonymous functions --- src/compiler.ts | 57 +++++--- tests/compiler/function-call.optimized.wat | 4 +- tests/compiler/function-call.untouched.wat | 4 +- .../function-expression.optimized.wat | 79 +++++++---- tests/compiler/function-expression.ts | 11 ++ .../function-expression.untouched.wat | 131 +++++++++++++----- 6 files changed, 203 insertions(+), 83 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index ce51af29c3..a7f1dde511 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -3429,7 +3429,7 @@ export class Compiler extends DiagnosticEmitter { break; } case NodeKind.FUNCTION: { - expr = this.compileFunctionExpression(expression, contextualType.signatureReference, constraints); + expr = this.compileFunctionExpression(expression, contextualType, constraints); break; } case NodeKind.IDENTIFIER: @@ -7311,26 +7311,30 @@ export class Compiler extends DiagnosticEmitter { private compileFunctionExpression( expression: FunctionExpression, - contextualSignature: Signature | null, + contextualType: Type, constraints: Constraints ): ExpressionRef { var declaration = expression.declaration.clone(); // generic contexts can have multiple assert(!declaration.typeParameters); // function expression cannot be generic var flow = this.currentFlow; var actualFunction = flow.actualFunction; + var isNamed = declaration.name.text.length > 0; + var isSemanticallyAnonymous = !isNamed || contextualType != Type.void; var prototype = new FunctionPrototype( - declaration.name.text.length - ? declaration.name.text - : "anonymous|" + (actualFunction.nextAnonymousId++).toString(), + isSemanticallyAnonymous + ? (isNamed ? declaration.name.text + "|" : "anonymous|") + (actualFunction.nextAnonymousId++).toString() + : declaration.name.text, actualFunction, declaration, DecoratorFlags.NONE ); var instance: Function | null; var contextualTypeArguments = uniqueMap(flow.contextualTypeArguments); + var module = this.module; // compile according to context. this differs from a normal function in that omitted parameter // and return types can be inferred and omitted arguments can be replaced with dummies. + var contextualSignature = contextualType.signatureReference; if (contextualSignature) { let signatureNode = prototype.functionTypeNode; let parameterNodes = signatureNode.parameters; @@ -7344,7 +7348,7 @@ export class Compiler extends DiagnosticEmitter { DiagnosticCode.Expected_0_arguments_but_got_1, expression.range, numParameters.toString(), numPresentParameters.toString() ); - return this.module.unreachable(); + return module.unreachable(); } // check non-omitted parameter types @@ -7356,13 +7360,13 @@ export class Compiler extends DiagnosticEmitter { actualFunction.parent, contextualTypeArguments ); - if (!resolvedType) return this.module.unreachable(); + if (!resolvedType) return module.unreachable(); if (!parameterTypes[i].isStrictlyAssignableTo(resolvedType)) { this.error( DiagnosticCode.Type_0_is_not_assignable_to_type_1, parameterNode.range, parameterTypes[i].toString(), resolvedType.toString() ); - return this.module.unreachable(); + return module.unreachable(); } } // any unused parameters are inherited but ignored @@ -7376,7 +7380,7 @@ export class Compiler extends DiagnosticEmitter { actualFunction.parent, contextualTypeArguments ); - if (!resolvedType) return this.module.unreachable(); + if (!resolvedType) return module.unreachable(); if ( returnType == Type.void ? resolvedType != Type.void @@ -7386,7 +7390,7 @@ export class Compiler extends DiagnosticEmitter { DiagnosticCode.Type_0_is_not_assignable_to_type_1, signatureNode.returnType.range, resolvedType.toString(), returnType.toString() ); - return this.module.unreachable(); + return module.unreachable(); } } @@ -7399,20 +7403,20 @@ export class Compiler extends DiagnosticEmitter { DiagnosticCode._this_cannot_be_referenced_in_current_location, thisTypeNode.range ); - return this.module.unreachable(); + return module.unreachable(); } let resolvedType = this.resolver.resolveType( thisTypeNode, actualFunction.parent, contextualTypeArguments ); - if (!resolvedType) return this.module.unreachable(); + if (!resolvedType) return module.unreachable(); if (!thisType.isStrictlyAssignableTo(resolvedType)) { this.error( DiagnosticCode.Type_0_is_not_assignable_to_type_1, thisTypeNode.range, thisType.toString(), resolvedType.toString() ); - return this.module.unreachable(); + return module.unreachable(); } } @@ -7428,7 +7432,7 @@ export class Compiler extends DiagnosticEmitter { instance.flow.outer = flow; let worked = this.compileFunction(instance); this.currentType = contextualSignature.type; - if (!worked) return this.module.unreachable(); + if (!worked) return module.unreachable(); // otherwise compile like a normal function } else { @@ -7437,13 +7441,30 @@ export class Compiler extends DiagnosticEmitter { instance.flow.outer = flow; let worked = this.compileFunction(instance); this.currentType = instance.signature.type; - if (!worked) return this.module.unreachable(); + if (!worked) return module.unreachable(); } var offset = this.ensureRuntimeFunction(instance); // reports - return this.options.isWasm64 - ? this.module.i64(i64_low(offset), i64_high(offset)) - : this.module.i32(i64_low(offset)); + var expr = this.options.isWasm64 + ? module.i64(i64_low(offset), i64_high(offset)) + : module.i32(i64_low(offset)); + + // add a local referring to the function if applicable + if (!isSemanticallyAnonymous) { + let fname = instance.name; + if (flow.lookupLocal(fname)) { + this.error( + DiagnosticCode.Duplicate_identifier_0, + declaration.name.range, fname + ); + } else { + let ftype = instance.type; + let local = flow.addScopedLocal(instance.name, ftype); + expr = module.local_tee(local.index, expr, ftype.isManaged); + } + } + + return expr; } /** Makes sure the enclosing source file of the specified expression has been compiled. */ diff --git a/tests/compiler/function-call.optimized.wat b/tests/compiler/function-call.optimized.wat index 4d1a3f6d86..a6c5a04fdc 100644 --- a/tests/compiler/function-call.optimized.wat +++ b/tests/compiler/function-call.optimized.wat @@ -41,7 +41,7 @@ (data (i32.const 1760) "\t\00\00\00 \00\00\00\00\00\00\00 ") (data (i32.const 1812) " ") (table $0 9 funcref) - (elem (i32.const 1) $start:function-call~anonymous|0 $start:function-call~anonymous|0 $start:function-call~anonymous|2 $start:function-call~anonymous|2 $start:function-call~fn2 $function-call/Foo#fnVoid $start:function-call~fn2 $function-call/Foo#fnRet) + (elem (i32.const 1) $start:function-call~anonymous|0 $start:function-call~anonymous|0 $start:function-call~anonymous|2 $start:function-call~anonymous|2 $start:function-call~fn2|4 $function-call/Foo#fnVoid $start:function-call~fn2|4 $function-call/Foo#fnRet) (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) (global $~lib/rt/itcms/threshold (mut i32) (i32.const 1024)) (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) @@ -64,7 +64,7 @@ local.get $1 i32.add ) - (func $start:function-call~fn2 (param $0 i32) (result i32) + (func $start:function-call~fn2|4 (param $0 i32) (result i32) local.get $0 ) (func $~lib/rt/itcms/initLazy (param $0 i32) (result i32) diff --git a/tests/compiler/function-call.untouched.wat b/tests/compiler/function-call.untouched.wat index 2808a52435..6e8a33cf4b 100644 --- a/tests/compiler/function-call.untouched.wat +++ b/tests/compiler/function-call.untouched.wat @@ -29,7 +29,7 @@ (data (i32.const 700) "\1c\00\00\00\00\00\00\00\00\00\00\00\08\00\00\00\08\00\00\00\08\00\00\00\00\00\00\00\00\00\00\00") (data (i32.const 736) "\t\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 9 funcref) - (elem (i32.const 1) $start:function-call~anonymous|0 $start:function-call~anonymous|1 $start:function-call~anonymous|2 $start:function-call~anonymous|3 $start:function-call~fn2 $function-call/Foo#fnVoid $function-call/Foo#fnThis $function-call/Foo#fnRet) + (elem (i32.const 1) $start:function-call~anonymous|0 $start:function-call~anonymous|1 $start:function-call~anonymous|2 $start:function-call~anonymous|3 $start:function-call~fn2|4 $function-call/Foo#fnVoid $function-call/Foo#fnThis $function-call/Foo#fnRet) (global $function-call/fnVoid (mut i32) (i32.const 32)) (global $~argumentsLength (mut i32) (i32.const 0)) (global $function-call/faVoid (mut i32) (i32.const 64)) @@ -71,7 +71,7 @@ local.get $1 i32.add ) - (func $start:function-call~fn2 (param $0 i32) (result i32) + (func $start:function-call~fn2|4 (param $0 i32) (result i32) local.get $0 ) (func $~lib/rt/itcms/Object#set:nextWithColor (param $0 i32) (param $1 i32) diff --git a/tests/compiler/function-expression.optimized.wat b/tests/compiler/function-expression.optimized.wat index 130350508e..7b7c6f6c09 100644 --- a/tests/compiler/function-expression.optimized.wat +++ b/tests/compiler/function-expression.optimized.wat @@ -7,6 +7,7 @@ (type $i32_=>_none (func (param i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i64_=>_i64 (func (param i64) (result i64))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) (data (i32.const 1036) "\1c") @@ -55,9 +56,13 @@ (data (i32.const 1992) "\03\00\00\00\08\00\00\00\10") (data (i32.const 2012) "\1c") (data (i32.const 2024) "\07\00\00\00\08\00\00\00\11") - (data (i32.const 2048) "\t\00\00\00 \00\00\00\00\00\00\00 ") - (table $0 18 funcref) - (elem (i32.const 1) $start:function-expression~anonymous|0 $start:function-expression~anonymous|0 $start:function-expression~someName $start:function-expression~anonymous|2 $start:function-expression~anonymous|3 $start:function-expression~anonymous|4 $start:function-expression~anonymous|5 $start:function-expression~anonymous|3 $start:function-expression~anonymous|4 $start:function-expression~anonymous|5 $start:function-expression~anonymous|2 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testGlobal~anonymous|0 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testLocal~anonymous|0 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testField~anonymous|0) + (data (i32.const 2044) "\1c") + (data (i32.const 2056) "\03\00\00\00\08\00\00\00\12") + (data (i32.const 2076) "\1c") + (data (i32.const 2088) "\t\00\00\00\08\00\00\00\13") + (data (i32.const 2112) "\n\00\00\00 \00\00\00\00\00\00\00 ") + (table $0 20 funcref) + (elem (i32.const 1) $start:function-expression~anonymous|0 $start:function-expression~anonymous|0 $start:function-expression~someName|2 $start:function-expression~anonymous|3 $start:function-expression~anonymous|4 $start:function-expression~anonymous|5 $start:function-expression~anonymous|6 $start:function-expression~anonymous|4 $start:function-expression~anonymous|5 $start:function-expression~anonymous|6 $start:function-expression~anonymous|3 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testGlobal~anonymous|0 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testLocal~anonymous|0 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testField~anonymous|0 $start:function-expression~anonymous|0 $function-expression/semanticallyAnonymous~fnDecl|0) (global $~lib/rt/itcms/total (mut i32) (i32.const 0)) (global $~lib/rt/itcms/threshold (mut i32) (i32.const 1024)) (global $~lib/rt/itcms/state (mut i32) (i32.const 0)) @@ -68,27 +73,28 @@ (global $~lib/rt/itcms/white (mut i32) (i32.const 0)) (global $~lib/rt/itcms/fromSpace (mut i32) (i32.const 0)) (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 18508)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 18580)) + (export "semanticallyAnonymous" (func $function-expression/semanticallyAnonymous)) (export "memory" (memory $0)) (start $~start) (func $start:function-expression~anonymous|0 (param $0 i32) (result i32) local.get $0 ) - (func $start:function-expression~someName + (func $start:function-expression~someName|2 nop ) - (func $start:function-expression~anonymous|2 (result i32) + (func $start:function-expression~anonymous|3 (result i32) i32.const 1 ) - (func $start:function-expression~anonymous|3 (param $0 i32) (param $1 i32) (result i32) + (func $start:function-expression~anonymous|4 (param $0 i32) (param $1 i32) (result i32) local.get $0 local.get $1 i32.add ) - (func $start:function-expression~anonymous|4 (param $0 i32) (param $1 i32) (result i32) + (func $start:function-expression~anonymous|5 (param $0 i32) (param $1 i32) (result i32) local.get $0 ) - (func $start:function-expression~anonymous|5 (param $0 i32) (param $1 i32) (result i32) + (func $start:function-expression~anonymous|6 (param $0 i32) (param $1 i32) (result i32) i32.const 42 ) (func $function-expression/testGlobal~anonymous|0~anonymous|0 (param $0 i32) (result i32) @@ -222,7 +228,7 @@ if i32.const 0 local.get $0 - i32.const 18508 + i32.const 18580 i32.lt_u local.get $0 i32.load offset=8 @@ -268,7 +274,7 @@ i32.const 1 else local.get $1 - i32.const 2048 + i32.const 2112 i32.load i32.gt_u if @@ -282,7 +288,7 @@ local.get $1 i32.const 3 i32.shl - i32.const 2052 + i32.const 2116 i32.add i32.load i32.const 32 @@ -887,10 +893,10 @@ if unreachable end - i32.const 18512 + i32.const 18592 i32.const 0 i32.store - i32.const 20080 + i32.const 20160 i32.const 0 i32.store loop $for-loop|0 @@ -901,7 +907,7 @@ local.get $1 i32.const 2 i32.shl - i32.const 18512 + i32.const 18592 i32.add i32.const 0 i32.store offset=4 @@ -919,7 +925,7 @@ i32.add i32.const 2 i32.shl - i32.const 18512 + i32.const 18592 i32.add i32.const 0 i32.store offset=96 @@ -937,13 +943,13 @@ br $for-loop|0 end end - i32.const 18512 - i32.const 20084 + i32.const 18592 + i32.const 20164 memory.size i32.const 16 i32.shl call $~lib/rt/tlsf/addMemory - i32.const 18512 + i32.const 18592 global.set $~lib/rt/tlsf/ROOT ) (func $~lib/rt/itcms/step (result i32) @@ -1022,7 +1028,7 @@ local.set $0 loop $while-continue|0 local.get $0 - i32.const 18508 + i32.const 18580 i32.lt_u if local.get $0 @@ -1112,7 +1118,7 @@ unreachable end local.get $0 - i32.const 18508 + i32.const 18580 i32.lt_u if local.get $0 @@ -1135,7 +1141,7 @@ i32.const 4 i32.add local.tee $1 - i32.const 18508 + i32.const 18580 i32.ge_u if global.get $~lib/rt/tlsf/ROOT @@ -1494,6 +1500,26 @@ i32.store8 offset=1 local.get $0 ) + (func $function-expression/semanticallyAnonymous~fnDecl|0 (param $0 i64) (result i64) + local.get $0 + ) + (func $function-expression/semanticallyAnonymous + 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 + global.get $~lib/memory/__stack_pointer + i32.const 2064 + i32.store + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) (func $~lib/rt/__visit_members (param $0 i32) block $folding-inner1 block $folding-inner0 @@ -1504,7 +1530,7 @@ i32.const 8 i32.sub i32.load - br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $folding-inner0 $folding-inner1 $folding-inner1 $folding-inner1 $folding-inner1 $folding-inner1 $folding-inner0 $invalid + br_table $~lib/arraybuffer/ArrayBuffer $~lib/string/String $folding-inner0 $folding-inner1 $folding-inner1 $folding-inner1 $folding-inner1 $folding-inner1 $folding-inner0 $folding-inner1 $invalid end return end @@ -1530,11 +1556,11 @@ ) (func $~stack_check global.get $~lib/memory/__stack_pointer - i32.const 2124 + i32.const 2196 i32.lt_s if - i32.const 18528 - i32.const 18576 + i32.const 18608 + i32.const 18656 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -1881,6 +1907,7 @@ i32.const 12 i32.add global.set $~lib/memory/__stack_pointer + call $function-expression/semanticallyAnonymous global.get $~lib/memory/__stack_pointer i32.const 4 i32.add diff --git a/tests/compiler/function-expression.ts b/tests/compiler/function-expression.ts index 8fe0fdbf2e..b69ec8ca86 100644 --- a/tests/compiler/function-expression.ts +++ b/tests/compiler/function-expression.ts @@ -82,3 +82,14 @@ function testField(): void { assert(fieldInst.fieldFunc()(1) == 25); } testField(); + +export function semanticallyAnonymous(): void { + function fnDecl(val: i32): i32 { + return val; + } + const exprDecl = function fnDecl(val: i64): i64 { // must not shadow + return val; + }; + assert(fnDecl != exprDecl); +} +semanticallyAnonymous(); diff --git a/tests/compiler/function-expression.untouched.wat b/tests/compiler/function-expression.untouched.wat index 610152ae02..6b01cab120 100644 --- a/tests/compiler/function-expression.untouched.wat +++ b/tests/compiler/function-expression.untouched.wat @@ -8,6 +8,7 @@ (type $i32_i32_i32_=>_none (func (param i32 i32 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 $i64_=>_i64 (func (param i64) (result i64))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) (data (i32.const 12) "\1c\00\00\00\00\00\00\00\00\00\00\00\03\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") @@ -36,9 +37,11 @@ (data (i32.const 892) "<\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 956) "\1c\00\00\00\00\00\00\00\00\00\00\00\03\00\00\00\08\00\00\00\10\00\00\00\00\00\00\00\00\00\00\00") (data (i32.const 988) "\1c\00\00\00\00\00\00\00\00\00\00\00\07\00\00\00\08\00\00\00\11\00\00\00\00\00\00\00\00\00\00\00") - (data (i32.const 1024) "\t\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 18 funcref) - (elem (i32.const 1) $start:function-expression~anonymous|0 $start:function-expression~anonymous|1 $start:function-expression~someName $start:function-expression~anonymous|2 $start:function-expression~anonymous|3 $start:function-expression~anonymous|4 $start:function-expression~anonymous|5 $function-expression/testOmittedReturn1~anonymous|0 $function-expression/testOmittedReturn2~anonymous|0 $function-expression/testOmittedReturn3~anonymous|0 $function-expression/testNullable~anonymous|0 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testGlobal~anonymous|0 $function-expression/testLocal~anonymous|0~anonymous|0 $function-expression/testLocal~anonymous|0 $function-expression/testField~anonymous|0~anonymous|0 $function-expression/testField~anonymous|0) + (data (i32.const 1020) "\1c\00\00\00\00\00\00\00\00\00\00\00\03\00\00\00\08\00\00\00\12\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 1052) "\1c\00\00\00\00\00\00\00\00\00\00\00\t\00\00\00\08\00\00\00\13\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 1088) "\n\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 20 funcref) + (elem (i32.const 1) $start:function-expression~anonymous|0 $start:function-expression~anonymous|1 $start:function-expression~someName|2 $start:function-expression~anonymous|3 $start:function-expression~anonymous|4 $start:function-expression~anonymous|5 $start:function-expression~anonymous|6 $function-expression/testOmittedReturn1~anonymous|0 $function-expression/testOmittedReturn2~anonymous|0 $function-expression/testOmittedReturn3~anonymous|0 $function-expression/testNullable~anonymous|0 $function-expression/testGlobal~anonymous|0~anonymous|0 $function-expression/testGlobal~anonymous|0 $function-expression/testLocal~anonymous|0~anonymous|0 $function-expression/testLocal~anonymous|0 $function-expression/testField~anonymous|0~anonymous|0 $function-expression/testField~anonymous|0 $function-expression/semanticallyAnonymous~fnDecl $function-expression/semanticallyAnonymous~fnDecl|0) (global $function-expression/f1 (mut i32) (i32.const 32)) (global $~argumentsLength (mut i32) (i32.const 0)) (global $function-expression/f2 (mut i32) (i32.const 128)) @@ -57,10 +60,11 @@ (global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0)) (global $~lib/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0)) (global $~lib/ASC_SHRINK_LEVEL i32 (i32.const 0)) - (global $~lib/rt/__rtti_base i32 (i32.const 1024)) - (global $~lib/memory/__data_end i32 (i32.const 1100)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17484)) - (global $~lib/memory/__heap_base i32 (i32.const 17484)) + (global $~lib/rt/__rtti_base i32 (i32.const 1088)) + (global $~lib/memory/__data_end i32 (i32.const 1172)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17556)) + (global $~lib/memory/__heap_base i32 (i32.const 17556)) + (export "semanticallyAnonymous" (func $function-expression/semanticallyAnonymous)) (export "memory" (memory $0)) (start $~start) (func $start:function-expression~anonymous|0 (param $0 i32) (result i32) @@ -69,13 +73,13 @@ (func $start:function-expression~anonymous|1 (param $0 i32) (result i32) local.get $0 ) - (func $start:function-expression~someName + (func $start:function-expression~someName|2 nop ) - (func $start:function-expression~anonymous|2 (result i32) + (func $start:function-expression~anonymous|3 (result i32) i32.const 1 ) - (func $start:function-expression~anonymous|3 (param $0 i32) (param $1 i32) (result i32) + (func $start:function-expression~anonymous|4 (param $0 i32) (param $1 i32) (result i32) local.get $0 local.get $1 i32.add @@ -89,10 +93,10 @@ i32.load call_indirect (type $i32_i32_=>_i32) ) - (func $start:function-expression~anonymous|4 (param $0 i32) (param $1 i32) (result i32) + (func $start:function-expression~anonymous|5 (param $0 i32) (param $1 i32) (result i32) local.get $0 ) - (func $start:function-expression~anonymous|5 (param $0 i32) (param $1 i32) (result i32) + (func $start:function-expression~anonymous|6 (param $0 i32) (param $1 i32) (result i32) i32.const 42 ) (func $function-expression/testOmittedReturn1~anonymous|0 (param $0 i32) (param $1 i32) (result i32) @@ -2621,6 +2625,45 @@ local.get $0 i32.add ) + (func $function-expression/semanticallyAnonymous~fnDecl (param $0 i32) (result i32) + local.get $0 + ) + (func $function-expression/semanticallyAnonymous~fnDecl|0 (param $0 i64) (result i64) + local.get $0 + ) + (func $function-expression/semanticallyAnonymous + (local $0 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 + global.get $~lib/memory/__stack_pointer + i32.const 1040 + local.tee $0 + i32.store + local.get $0 + drop + local.get $0 + i32.const 1072 + i32.ne + i32.eqz + if + i32.const 0 + i32.const 64 + i32.const 93 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) (func $~lib/rt/__visit_globals (param $0 i32) (local $1 i32) i32.const 768 @@ -2707,60 +2750,77 @@ call $~lib/rt/itcms/__visit end ) + (func $~lib/function/Function<%28i64%29=>i64>#__visit (param $0 i32) (param $1 i32) + local.get $0 + i32.load offset=4 + local.get $1 + call $~lib/rt/itcms/__visit + ) + (func $~lib/function/Function<%28i64%29=>i64>~visit (param $0 i32) (param $1 i32) + local.get $0 + local.get $1 + call $~lib/function/Function<%28i64%29=>i64>#__visit + ) (func $~lib/rt/__visit_members (param $0 i32) (param $1 i32) block $invalid - block $function-expression/FieldClass - block $~lib/function/Function<%28%29=>%28i32%29=>i32> - block $~lib/function/Function<%28i32%2Ci32%29=>i32> - block $~lib/function/Function<%28%29=>i32> - block $~lib/function/Function<%28%29=>void> - block $~lib/function/Function<%28i32%29=>i32> - 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 $~lib/function/Function<%28i32%29=>i32> $~lib/function/Function<%28%29=>void> $~lib/function/Function<%28%29=>i32> $~lib/function/Function<%28i32%2Ci32%29=>i32> $~lib/function/Function<%28%29=>%28i32%29=>i32> $function-expression/FieldClass $invalid + block $~lib/function/Function<%28i64%29=>i64> + block $function-expression/FieldClass + block $~lib/function/Function<%28%29=>%28i32%29=>i32> + block $~lib/function/Function<%28i32%2Ci32%29=>i32> + block $~lib/function/Function<%28%29=>i32> + block $~lib/function/Function<%28%29=>void> + block $~lib/function/Function<%28i32%29=>i32> + 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 $~lib/function/Function<%28i32%29=>i32> $~lib/function/Function<%28%29=>void> $~lib/function/Function<%28%29=>i32> $~lib/function/Function<%28i32%2Ci32%29=>i32> $~lib/function/Function<%28%29=>%28i32%29=>i32> $function-expression/FieldClass $~lib/function/Function<%28i64%29=>i64> $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 + call $~lib/function/Function<%28i32%29=>i32>~visit return end local.get $0 local.get $1 - call $~lib/function/Function<%28i32%29=>i32>~visit + call $~lib/function/Function<%28%29=>void>~visit return end local.get $0 local.get $1 - call $~lib/function/Function<%28%29=>void>~visit + call $~lib/function/Function<%28%29=>i32>~visit return end local.get $0 local.get $1 - call $~lib/function/Function<%28%29=>i32>~visit + call $~lib/function/Function<%28i32%2Ci32%29=>i32>~visit return end local.get $0 local.get $1 - call $~lib/function/Function<%28i32%2Ci32%29=>i32>~visit + call $~lib/function/Function<%28%29=>%28i32%29=>i32>~visit return end local.get $0 local.get $1 - call $~lib/function/Function<%28%29=>%28i32%29=>i32>~visit + call $function-expression/FieldClass~visit return end local.get $0 local.get $1 - call $function-expression/FieldClass~visit + call $~lib/function/Function<%28i64%29=>i64>~visit return end unreachable @@ -2773,8 +2833,8 @@ global.get $~lib/memory/__data_end i32.lt_s if - i32.const 17504 - i32.const 17552 + i32.const 17584 + i32.const 17632 i32.const 1 i32.const 1 call $~lib/builtins/abort @@ -3036,6 +3096,7 @@ call $~lib/rt/itcms/initLazy global.set $~lib/rt/itcms/fromSpace call $function-expression/testField + call $function-expression/semanticallyAnonymous global.get $~lib/memory/__stack_pointer i32.const 4 i32.add From cb0337b2f9e4b9bf0989263e8ef88dc2a8ef9fb7 Mon Sep 17 00:00:00 2001 From: dcode Date: Thu, 4 Feb 2021 00:04:27 +0100 Subject: [PATCH 2/3] make the local constant --- src/compiler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler.ts b/src/compiler.ts index a7f1dde511..a8d3adeb2b 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7449,7 +7449,7 @@ export class Compiler extends DiagnosticEmitter { ? module.i64(i64_low(offset), i64_high(offset)) : module.i32(i64_low(offset)); - // add a local referring to the function if applicable + // add a constant local referring to the function if applicable if (!isSemanticallyAnonymous) { let fname = instance.name; if (flow.lookupLocal(fname)) { @@ -7460,6 +7460,7 @@ export class Compiler extends DiagnosticEmitter { } else { let ftype = instance.type; let local = flow.addScopedLocal(instance.name, ftype); + flow.setLocalFlag(local.index, LocalFlags.CONSTANT); expr = module.local_tee(local.index, expr, ftype.isManaged); } } From 2d64b4d4d416e5d9fda2451a1cd3672c4f67ef22 Mon Sep 17 00:00:00 2001 From: dcode Date: Thu, 4 Feb 2021 00:13:41 +0100 Subject: [PATCH 3/3] allow shadowing, diagnose related declaration --- src/compiler.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index a8d3adeb2b..d7364db52f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7452,11 +7452,21 @@ export class Compiler extends DiagnosticEmitter { // add a constant local referring to the function if applicable if (!isSemanticallyAnonymous) { let fname = instance.name; - if (flow.lookupLocal(fname)) { - this.error( - DiagnosticCode.Duplicate_identifier_0, - declaration.name.range, fname - ); + let existingLocal = flow.getScopedLocal(fname); + if (existingLocal) { + if (!existingLocal.declaration.range.source.isNative) { + this.errorRelated( + DiagnosticCode.Duplicate_identifier_0, + declaration.name.range, + existingLocal.declaration.name.range, + fname + ); + } else { // scoped locals are shared temps that don't track declarations + this.error( + DiagnosticCode.Duplicate_identifier_0, + declaration.name.range, fname + ); + } } else { let ftype = instance.type; let local = flow.addScopedLocal(instance.name, ftype);