diff --git a/src/ast.ts b/src/ast.ts index e4439e6b2a..57ded93d56 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1087,6 +1087,37 @@ export abstract class TypeNode extends Node { /** Whether nullable or not. */ isNullable: bool; + + /** Tests if this type has a generic component matching one of the given type parameters. */ + hasGenericComponent(typeParameterNodes: TypeParameterNode[]): bool { + var self = this; // TS otherwise complains + if (this.kind == NodeKind.NAMEDTYPE) { + if (!(self).name.next) { + let typeArgumentNodes = (self).typeArguments; + if (typeArgumentNodes !== null && typeArgumentNodes.length) { + for (let i = 0, k = typeArgumentNodes.length; i < k; ++i) { + if (typeArgumentNodes[i].hasGenericComponent(typeParameterNodes)) return true; + } + } else { + let name = (self).name.identifier.text; + for (let i = 0, k = typeParameterNodes.length; i < k; ++i) { + if (typeParameterNodes[i].name.text == name) return true; + } + } + } + } else if (this.kind == NodeKind.FUNCTIONTYPE) { + let parameterNodes = (self).parameters; + for (let i = 0, k = parameterNodes.length; i < k; ++i) { + if (parameterNodes[i].type.hasGenericComponent(typeParameterNodes)) return true; + } + if ((self).returnType.hasGenericComponent(typeParameterNodes)) return true; + let explicitThisType = (self).explicitThisType; + if (explicitThisType !== null && explicitThisType.hasGenericComponent(typeParameterNodes)) return true; + } else { + assert(false); + } + return false; + } } /** Represents a type name. */ diff --git a/src/compiler.ts b/src/compiler.ts index 4c69cfd112..48f85a18b0 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5843,26 +5843,26 @@ export class Compiler extends DiagnosticEmitter { // infer generic call if type arguments have been omitted } else if (prototype.is(CommonFlags.GENERIC)) { - let inferredTypes = new Map(); + let contextualTypeArguments = makeMap(flow.contextualTypeArguments); + + // fill up contextual types with auto for each generic component let typeParameterNodes = assert(prototype.typeParameterNodes); let numTypeParameters = typeParameterNodes.length; + let typeParameterNames = new Set(); for (let i = 0; i < numTypeParameters; ++i) { - inferredTypes.set(typeParameterNodes[i].name.text, null); + let name = typeParameterNodes[i].name.text; + contextualTypeArguments.set(name, Type.auto); + typeParameterNames.add(name); } - // let numInferred = 0; + let parameterNodes = prototype.functionTypeNode.parameters; let numParameters = parameterNodes.length; let argumentNodes = expression.arguments; let numArguments = argumentNodes.length; - let argumentExprs = new Array(numArguments); + + // infer types with generic components while updating contextual types for (let i = 0; i < numParameters; ++i) { - let typeNode = parameterNodes[i].type; - let templateName = typeNode.kind == NodeKind.NAMEDTYPE && !(typeNode).name.next - ? (typeNode).name.identifier.text - : null; - let argumentExpression = i < numArguments - ? argumentNodes[i] - : parameterNodes[i].initializer; + let argumentExpression = i < numArguments ? argumentNodes[i] : parameterNodes[i].initializer; if (!argumentExpression) { // missing initializer -> too few arguments this.error( DiagnosticCode.Expected_0_arguments_but_got_1, @@ -5870,43 +5870,19 @@ export class Compiler extends DiagnosticEmitter { ); return module.unreachable(); } - if (templateName !== null && inferredTypes.has(templateName)) { - let inferredType = inferredTypes.get(templateName); - if (inferredType) { - argumentExprs[i] = this.compileExpression(argumentExpression, inferredType); - let commonType: Type | null; - if (!(commonType = Type.commonDenominator(inferredType, this.currentType, true))) { - if (!(commonType = Type.commonDenominator(inferredType, this.currentType, false))) { - this.error( - DiagnosticCode.Type_0_is_not_assignable_to_type_1, - parameterNodes[i].type.range, this.currentType.toString(), inferredType.toString() - ); - return module.unreachable(); - } - } - inferredType = commonType; - } else { - argumentExprs[i] = this.compileExpression(argumentExpression, Type.auto); - inferredType = this.currentType; - // ++numInferred; - } - inferredTypes.set(templateName, inferredType); - } else { - let concreteType = this.resolver.resolveType( - parameterNodes[i].type, - flow.actualFunction, - flow.contextualTypeArguments - ); - if (!concreteType) return module.unreachable(); - argumentExprs[i] = this.compileExpression(argumentExpression, concreteType, Constraints.CONV_IMPLICIT); + let typeNode = parameterNodes[i].type; + if (typeNode.hasGenericComponent(typeParameterNodes)) { + this.resolver.inferGenericType(typeNode, argumentExpression, flow, contextualTypeArguments, typeParameterNames); } } - let resolvedTypeArguments = new Array(numTypeParameters); + + // apply concrete types to the generic function signature + let resolvedTypeArguments = Array.create(numTypeParameters); for (let i = 0; i < numTypeParameters; ++i) { let name = typeParameterNodes[i].name.text; - if (inferredTypes.has(name)) { - let inferredType = inferredTypes.get(name); - if (inferredType) { + if (contextualTypeArguments.has(name)) { + let inferredType = contextualTypeArguments.get(name)!; + if (inferredType != Type.auto) { resolvedTypeArguments[i] = inferredType; continue; } @@ -5924,12 +5900,6 @@ export class Compiler extends DiagnosticEmitter { resolvedTypeArguments, makeMap(flow.contextualTypeArguments) ); - if (!instance) return this.module.unreachable(); - if (prototype.hasDecorator(DecoratorFlags.UNSAFE)) this.checkUnsafe(expression); - return this.makeCallDirect(instance, argumentExprs, expression, contextualType == Type.void); - // TODO: this skips inlining because inlining requires compiling its temporary locals in - // the scope of the inlined flow. might need another mechanism to lock temp. locals early, - // so inlining can be performed in `makeCallDirect` instead? // otherwise resolve the non-generic call as usual } else { diff --git a/src/resolver.ts b/src/resolver.ts index d0bf3a3d4a..def92e880c 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -65,7 +65,8 @@ import { TernaryExpression, isTypeOmitted, FunctionExpression, - NewExpression + NewExpression, + ParameterNode } from "./ast"; import { @@ -691,6 +692,82 @@ export class Resolver extends DiagnosticEmitter { return typeArguments; } + /** Infers the generic type(s) of an argument expression and updates `ctxTypes`. */ + inferGenericType( + /** The generic type being inferred. */ + typeNode: TypeNode, + /** The respective argument expression. */ + exprNode: Expression, + /** Contextual flow. */ + ctxFlow: Flow, + /** Contextual types, i.e. `T`, with unknown types initialized to `auto`. */ + ctxTypes: Map, + /** The names of the type parameters being inferred. */ + typeParameterNames: Set + ): void { + var type = this.resolveExpression(exprNode, ctxFlow, Type.auto, ReportMode.SWALLOW); + if (type) this.propagateInferredGenericTypes(typeNode, type, ctxFlow, ctxTypes, typeParameterNames); + } + + /** Updates contextual types with a possibly encapsulated inferred type. */ + private propagateInferredGenericTypes( + /** The inferred type node. */ + node: TypeNode, + /** The inferred type. */ + type: Type, + /** Contextual flow. */ + ctxFlow: Flow, + /** Contextual types, i.e. `T`, with unknown types initialized to `auto`. */ + ctxTypes: Map, + /** The names of the type parameters being inferred. */ + typeParameterNames: Set + ): void { + if (node.kind == NodeKind.NAMEDTYPE) { + let typeArgumentNodes = (node).typeArguments; + if (typeArgumentNodes !== null && typeArgumentNodes.length) { // foo(bar: Array) + let classReference = type.classReference; + if (classReference) { + let classPrototype = this.resolveTypeName((node).name, ctxFlow.actualFunction); + if (!classPrototype || classPrototype.kind != ElementKind.CLASS_PROTOTYPE) return; + if (classReference.prototype == classPrototype) { + let typeArguments = classReference.typeArguments; + if (typeArguments !== null && typeArguments.length == typeArgumentNodes.length) { + for (let i = 0, k = typeArguments.length; i < k; ++i) { + this.propagateInferredGenericTypes(typeArgumentNodes[i], typeArguments[i], ctxFlow, ctxTypes, typeParameterNames); + } + return; + } + } + } + } else { // foo(bar: T) + let name = (node).name.identifier.text; + if (ctxTypes.has(name)) { + let currentType = ctxTypes.get(name)!; + if (currentType == Type.auto || (typeParameterNames.has(name) && currentType.isAssignableTo(type))) { + ctxTypes.set(name, type); + } + } + } + } else if (node.kind == NodeKind.FUNCTIONTYPE) { // foo(bar: (baz: T) => i32)) + let parameterNodes = (node).parameters; + if (parameterNodes !== null && parameterNodes.length) { + let signatureReference = type.signatureReference; + if (signatureReference) { + let parameterTypes = signatureReference.parameterTypes; + let thisType = signatureReference.thisType; + if (parameterTypes.length == parameterNodes.length && !thisType == !(node).explicitThisType) { + for (let i = 0, k = parameterTypes.length; i < k; ++i) { + this.propagateInferredGenericTypes(parameterNodes[i].type, parameterTypes[i], ctxFlow, ctxTypes, typeParameterNames); + } + this.propagateInferredGenericTypes((node).returnType, signatureReference.returnType, ctxFlow, ctxTypes, typeParameterNames); + if (thisType) this.propagateInferredGenericTypes((node).explicitThisType!, thisType, ctxFlow, ctxTypes, typeParameterNames); + return; + } + } + } + } + } + /** Gets the concrete type of an element. */ getTypeOfElement(element: Element): Type | null { var kind = element.kind; @@ -908,7 +985,7 @@ export class Resolver extends DiagnosticEmitter { case NodeKind.TRUE: { return this.resolveIdentifierExpression( node, - ctxFlow, ctxFlow.actualFunction, reportMode + ctxFlow, ctxType, ctxFlow.actualFunction, reportMode ); } case NodeKind.THIS: { @@ -1018,11 +1095,23 @@ export class Resolver extends DiagnosticEmitter { node: IdentifierExpression, /** Flow to search for scoped locals. */ ctxFlow: Flow, + /** Contextual type. */ + ctxType: Type = Type.auto, /** Element to search. */ ctxElement: Element = ctxFlow.actualFunction, // differs for enums and namespaces /** How to proceed with eventual diagnostics. */ reportMode: ReportMode = ReportMode.REPORT ): Type | null { + switch (node.kind) { + case NodeKind.TRUE: + case NodeKind.FALSE: return Type.bool; + case NodeKind.NULL: { + let classReference = ctxType.classReference; + return ctxType.is(TypeFlags.REFERENCE) && classReference !== null + ? classReference.type.asNullable() + : this.program.options.usizeType; // TODO: anyref context? + } + } var element = this.lookupIdentifierExpression(node, ctxFlow, ctxElement, reportMode); if (!element) return null; if (element.kind == ElementKind.FUNCTION_PROTOTYPE) { diff --git a/tests/compiler/call-inferred.optimized.wat b/tests/compiler/call-inferred.optimized.wat index a9c60e8c7f..15efb2b570 100644 --- a/tests/compiler/call-inferred.optimized.wat +++ b/tests/compiler/call-inferred.optimized.wat @@ -1,9 +1,43 @@ (module + (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) (type $FUNCSIG$v (func)) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (memory $0 1) (data (i32.const 8) " \00\00\00\01\00\00\00\01\00\00\00 \00\00\00c\00a\00l\00l\00-\00i\00n\00f\00e\00r\00r\00e\00d\00.\00t\00s") + (global $~lib/argc (mut i32) (i32.const 0)) (export "memory" (memory $0)) - (func $start (; 0 ;) (type $FUNCSIG$v) + (start $start) + (func $start:call-inferred (; 1 ;) (type $FUNCSIG$v) + (local $0 f32) + i32.const 0 + global.set $~lib/argc + block $1of1 + block $0of1 + block $outOfRange + global.get $~lib/argc + br_table $0of1 $1of1 $outOfRange + end + unreachable + end + f32.const 42 + local.set $0 + end + local.get $0 + f32.const 42 + f32.ne + if + i32.const 0 + i32.const 24 + i32.const 13 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + ) + (func $start (; 2 ;) (type $FUNCSIG$v) + call $start:call-inferred + ) + (func $null (; 3 ;) (type $FUNCSIG$v) nop ) ) diff --git a/tests/compiler/call-inferred.untouched.wat b/tests/compiler/call-inferred.untouched.wat index 4d8f89f101..97e21b27ca 100644 --- a/tests/compiler/call-inferred.untouched.wat +++ b/tests/compiler/call-inferred.untouched.wat @@ -9,6 +9,7 @@ (data (i32.const 8) " \00\00\00\01\00\00\00\01\00\00\00 \00\00\00c\00a\00l\00l\00-\00i\00n\00f\00e\00r\00r\00e\00d\00.\00t\00s\00") (table $0 1 funcref) (elem (i32.const 0) $null) + (global $~lib/argc (mut i32) (i32.const 0)) (export "memory" (memory $0)) (start $start) (func $call-inferred/foo (; 1 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) @@ -23,7 +24,22 @@ (func $call-inferred/bar (; 4 ;) (type $FUNCSIG$ff) (param $0 f32) (result f32) local.get $0 ) - (func $start:call-inferred (; 5 ;) (type $FUNCSIG$v) + (func $call-inferred/bar|trampoline (; 5 ;) (type $FUNCSIG$ff) (param $0 f32) (result f32) + block $1of1 + block $0of1 + block $outOfRange + global.get $~lib/argc + br_table $0of1 $1of1 $outOfRange + end + unreachable + end + f32.const 42 + local.set $0 + end + local.get $0 + call $call-inferred/bar + ) + (func $start:call-inferred (; 6 ;) (type $FUNCSIG$v) i32.const 42 call $call-inferred/foo i32.const 42 @@ -63,8 +79,10 @@ call $~lib/builtins/abort unreachable end - f32.const 42 - call $call-inferred/bar + i32.const 0 + global.set $~lib/argc + f32.const 0 + call $call-inferred/bar|trampoline f32.const 42 f32.eq i32.eqz @@ -77,9 +95,9 @@ unreachable end ) - (func $start (; 6 ;) (type $FUNCSIG$v) + (func $start (; 7 ;) (type $FUNCSIG$v) call $start:call-inferred ) - (func $null (; 7 ;) (type $FUNCSIG$v) + (func $null (; 8 ;) (type $FUNCSIG$v) ) ) diff --git a/tests/compiler/infer-generic.json b/tests/compiler/infer-generic.json new file mode 100644 index 0000000000..b1da366ff4 --- /dev/null +++ b/tests/compiler/infer-generic.json @@ -0,0 +1,5 @@ +{ + "asc_flags": [ + "--runtime none" + ] +} \ No newline at end of file diff --git a/tests/compiler/infer-generic.optimized.wat b/tests/compiler/infer-generic.optimized.wat new file mode 100644 index 0000000000..8da77f7349 --- /dev/null +++ b/tests/compiler/infer-generic.optimized.wat @@ -0,0 +1,81 @@ +(module + (type $FUNCSIG$iifii (func (param i32 f32 i32 i32) (result i32))) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$v (func)) + (type $FUNCSIG$ff (func (param f32) (result f32))) + (memory $0 1) + (data (i32.const 8) " \00\00\00\01\00\00\00\01\00\00\00 \00\00\00i\00n\00f\00e\00r\00-\00g\00e\00n\00e\00r\00i\00c\00.\00t\00s") + (data (i32.const 56) "\0c\00\00\00\01\00\00\00\00\00\00\00\0c\00\00\00\00\00\80?\00\00\00@\00\00@@") + (data (i32.const 88) "\10\00\00\00\01\00\00\00\03\00\00\00\10\00\00\00H\00\00\00H\00\00\00\0c\00\00\00\03") + (table $0 2 funcref) + (elem (i32.const 0) $null $start:infer-generic~anonymous|0) + (global $~lib/argc (mut i32) (i32.const 0)) + (export "memory" (memory $0)) + (export "test1" (func $infer-generic/test1)) + (export "test2" (func $infer-generic/test2)) + (export "test3" (func $infer-generic/test2)) + (export "test4" (func $infer-generic/test2)) + (start $start) + (func $start:infer-generic~anonymous|0 (; 0 ;) (type $FUNCSIG$iifii) (param $0 i32) (param $1 f32) (param $2 i32) (param $3 i32) (result i32) + local.get $1 + f32.const 0 + f32.ne + i32.const 0 + local.get $0 + select + ) + (func $~lib/array/Array#reduce (; 1 ;) (type $FUNCSIG$v) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + i32.const 116 + i32.load + local.set $1 + loop $loop|0 + local.get $0 + local.get $1 + i32.const 116 + i32.load + local.tee $2 + local.get $1 + local.get $2 + i32.lt_s + select + i32.lt_s + if + i32.const 4 + global.set $~lib/argc + local.get $3 + i32.const 108 + i32.load + local.get $0 + i32.const 2 + i32.shl + i32.add + f32.load + local.get $0 + i32.const 104 + call $start:infer-generic~anonymous|0 + local.set $3 + local.get $0 + i32.const 1 + i32.add + local.set $0 + br $loop|0 + end + end + ) + (func $infer-generic/test1 (; 2 ;) (type $FUNCSIG$ff) (param $0 f32) (result f32) + local.get $0 + ) + (func $infer-generic/test2 (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + ) + (func $start (; 4 ;) (type $FUNCSIG$v) + call $~lib/array/Array#reduce + ) + (func $null (; 5 ;) (type $FUNCSIG$v) + nop + ) +) diff --git a/tests/compiler/infer-generic.ts b/tests/compiler/infer-generic.ts new file mode 100644 index 0000000000..fc8e5aedc7 --- /dev/null +++ b/tests/compiler/infer-generic.ts @@ -0,0 +1,52 @@ +// T should infer T + +function inferPlain(arr: T): T { + return arr; +} + +export function test1(arr: f32): f32 { + return inferPlain(arr); +} + +// Array should infer T + +function inferEncapsulatedClass(arr: T[]): T[] { + return arr; +} + +export function test2(arr: f32[]): f32[] { + return inferEncapsulatedClass(arr); +} + +// (a: T) => R should infer T,R + +function inferEncapsulatedFunction(fn: (a: T) => R): (a: T) => R { + return fn; +} + +export function test3(fn: (a: f32) => f64): (a: f32) => f64 { + return inferEncapsulatedFunction(fn); +} + +// (a: T, b: i32) => R should not bail out on non-inferred i32 + +function inferEncapsulatedFunctionMixed(fn: (a: T, b: i32) => R): (a: T, b: i32) => R { + return fn; +} + +export function test4(fn: (a: f32, b: i32) => f64): (a: f32, b: i32) => f64 { + return inferEncapsulatedFunctionMixed(fn); +} + +// should pick common compatible type f64 + +function inferCompatible(a: T, b: T): bool { + return a == b; +} +assert(inferCompatible(1, 1.0)); + +// should work with function expressions with omitted types +// if all types can be inferred from other arguments + +const arr: f32[] = [1.0, 2.0, 3.0]; +arr.reduce(((acc, cur) => acc && cur != 0), false); diff --git a/tests/compiler/infer-generic.untouched.wat b/tests/compiler/infer-generic.untouched.wat new file mode 100644 index 0000000000..296df99d33 --- /dev/null +++ b/tests/compiler/infer-generic.untouched.wat @@ -0,0 +1,172 @@ +(module + (type $FUNCSIG$idd (func (param f64 f64) (result i32))) + (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$iifii (func (param i32 f32 i32 i32) (result i32))) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$iiii (func (param i32 i32 i32) (result i32))) + (type $FUNCSIG$v (func)) + (type $FUNCSIG$ff (func (param f32) (result f32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (memory $0 1) + (data (i32.const 8) " \00\00\00\01\00\00\00\01\00\00\00 \00\00\00i\00n\00f\00e\00r\00-\00g\00e\00n\00e\00r\00i\00c\00.\00t\00s\00") + (data (i32.const 56) "\0c\00\00\00\01\00\00\00\00\00\00\00\0c\00\00\00\00\00\80?\00\00\00@\00\00@@") + (data (i32.const 88) "\10\00\00\00\01\00\00\00\03\00\00\00\10\00\00\00H\00\00\00H\00\00\00\0c\00\00\00\03\00\00\00") + (table $0 2 funcref) + (elem (i32.const 0) $null $start:infer-generic~anonymous|0) + (global $infer-generic/arr i32 (i32.const 104)) + (global $~lib/argc (mut i32) (i32.const 0)) + (export "memory" (memory $0)) + (export "test1" (func $infer-generic/test1)) + (export "test2" (func $infer-generic/test2)) + (export "test3" (func $infer-generic/test3)) + (export "test4" (func $infer-generic/test4)) + (start $start) + (func $infer-generic/inferCompatible (; 1 ;) (type $FUNCSIG$idd) (param $0 f64) (param $1 f64) (result i32) + local.get $0 + local.get $1 + f64.eq + ) + (func $~lib/rt/stub/__retain (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + ) + (func $~lib/rt/stub/__release (; 3 ;) (type $FUNCSIG$vi) (param $0 i32) + nop + ) + (func $start:infer-generic~anonymous|0 (; 4 ;) (type $FUNCSIG$iifii) (param $0 i32) (param $1 f32) (param $2 i32) (param $3 i32) (result i32) + (local $4 i32) + local.get $3 + call $~lib/rt/stub/__retain + drop + local.get $0 + if (result i32) + local.get $1 + f32.const 0 + f32.ne + else + i32.const 0 + end + local.set $4 + local.get $3 + call $~lib/rt/stub/__release + local.get $4 + ) + (func $~lib/array/Array#reduce (; 5 ;) (type $FUNCSIG$iiii) (param $0 i32) (param $1 i32) (param $2 i32) (result i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 i32) + local.get $2 + local.set $3 + block $break|0 + i32.const 0 + local.set $4 + local.get $0 + i32.load offset=12 + local.set $5 + loop $loop|0 + local.get $4 + local.get $5 + local.tee $6 + local.get $0 + i32.load offset=12 + local.tee $7 + local.get $6 + local.get $7 + i32.lt_s + select + i32.lt_s + i32.eqz + br_if $break|0 + i32.const 4 + global.set $~lib/argc + local.get $3 + local.get $0 + i32.load offset=4 + local.get $4 + i32.const 2 + i32.shl + i32.add + f32.load + local.get $4 + local.get $0 + local.get $1 + call_indirect (type $FUNCSIG$iifii) + local.set $3 + local.get $4 + i32.const 1 + i32.add + local.set $4 + br $loop|0 + end + unreachable + end + local.get $3 + ) + (func $start:infer-generic (; 6 ;) (type $FUNCSIG$v) + (local $0 i32) + (local $1 i32) + f64.const 1 + f64.const 1 + call $infer-generic/inferCompatible + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 46 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + global.get $infer-generic/arr + i32.const 1 + i32.const 0 + call $~lib/array/Array#reduce + drop + ) + (func $infer-generic/inferPlain (; 7 ;) (type $FUNCSIG$ff) (param $0 f32) (result f32) + local.get $0 + ) + (func $infer-generic/test1 (; 8 ;) (type $FUNCSIG$ff) (param $0 f32) (result f32) + local.get $0 + call $infer-generic/inferPlain + ) + (func $infer-generic/inferEncapsulatedClass (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/rt/stub/__retain + drop + local.get $0 + ) + (func $infer-generic/test2 (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + call $~lib/rt/stub/__retain + drop + local.get $0 + call $infer-generic/inferEncapsulatedClass + local.set $1 + local.get $0 + call $~lib/rt/stub/__release + local.get $1 + ) + (func $infer-generic/inferEncapsulatedFunction (; 11 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + ) + (func $infer-generic/test3 (; 12 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $infer-generic/inferEncapsulatedFunction + ) + (func $infer-generic/inferEncapsulatedFunctionMixed (; 13 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + ) + (func $infer-generic/test4 (; 14 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $infer-generic/inferEncapsulatedFunctionMixed + ) + (func $start (; 15 ;) (type $FUNCSIG$v) + call $start:infer-generic + ) + (func $null (; 16 ;) (type $FUNCSIG$v) + ) +) diff --git a/tests/compiler/number.untouched.wat b/tests/compiler/number.untouched.wat index 123054e703..826eba3516 100644 --- a/tests/compiler/number.untouched.wat +++ b/tests/compiler/number.untouched.wat @@ -440,20 +440,12 @@ call $~lib/util/number/itoa32 return ) - (func $~lib/rt/stub/__release (; 8 ;) (type $FUNCSIG$vi) (param $0 i32) - nop - ) - (func $~lib/number/I32#toString (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) + (func $~lib/number/I32#toString (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 call $~lib/util/number/itoa - local.tee $1 - call $~lib/rt/stub/__retain - local.set $2 - local.get $1 - call $~lib/rt/stub/__release - local.get $2 + ) + (func $~lib/rt/stub/__release (; 9 ;) (type $FUNCSIG$vi) (param $0 i32) + nop ) (func $~lib/string/String#get:length (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 diff --git a/tests/compiler/resolve-access.untouched.wat b/tests/compiler/resolve-access.untouched.wat index 9b5ee73455..bb0f2a3084 100644 --- a/tests/compiler/resolve-access.untouched.wat +++ b/tests/compiler/resolve-access.untouched.wat @@ -1937,20 +1937,12 @@ call $~lib/util/number/utoa64 return ) - (func $~lib/rt/stub/__release (; 15 ;) (type $FUNCSIG$vi) (param $0 i32) - nop - ) - (func $~lib/number/U64#toString (; 16 ;) (type $FUNCSIG$ij) (param $0 i64) (result i32) - (local $1 i32) - (local $2 i32) + (func $~lib/number/U64#toString (; 15 ;) (type $FUNCSIG$ij) (param $0 i64) (result i32) local.get $0 call $~lib/util/number/itoa - local.tee $1 - call $~lib/rt/stub/__retain - local.set $2 - local.get $1 - call $~lib/rt/stub/__release - local.get $2 + ) + (func $~lib/rt/stub/__release (; 16 ;) (type $FUNCSIG$vi) (param $0 i32) + nop ) (func $resolve-access/arrayAccess (; 17 ;) (type $FUNCSIG$i) (result i32) (local $0 i32) @@ -2054,16 +2046,8 @@ return ) (func $~lib/number/U32#toString (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) local.get $0 call $~lib/util/number/itoa - local.tee $1 - call $~lib/rt/stub/__retain - local.set $2 - local.get $1 - call $~lib/rt/stub/__release - local.get $2 ) (func $resolve-access/propertyAccess (; 24 ;) (type $FUNCSIG$i) (result i32) (local $0 i32) diff --git a/tests/compiler/resolve-binary.untouched.wat b/tests/compiler/resolve-binary.untouched.wat index b758892828..b3292d57da 100644 --- a/tests/compiler/resolve-binary.untouched.wat +++ b/tests/compiler/resolve-binary.untouched.wat @@ -615,16 +615,8 @@ return ) (func $~lib/number/I32#toString (; 13 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) local.get $0 call $~lib/util/number/itoa - local.tee $1 - call $~lib/rt/stub/__retain - local.set $2 - local.get $1 - call $~lib/rt/stub/__release - local.get $2 ) (func $~lib/math/NativeMath.scalbn (; 14 ;) (type $FUNCSIG$ddi) (param $0 f64) (param $1 i32) (result f64) (local $2 f64) diff --git a/tests/compiler/resolve-function-expression.untouched.wat b/tests/compiler/resolve-function-expression.untouched.wat index 4b897ebc8a..975bb7a130 100644 --- a/tests/compiler/resolve-function-expression.untouched.wat +++ b/tests/compiler/resolve-function-expression.untouched.wat @@ -415,20 +415,12 @@ call $~lib/util/number/itoa32 return ) - (func $~lib/rt/stub/__release (; 11 ;) (type $FUNCSIG$vi) (param $0 i32) - nop - ) - (func $~lib/number/I32#toString (; 12 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) + (func $~lib/number/I32#toString (; 11 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 call $~lib/util/number/itoa - local.tee $1 - call $~lib/rt/stub/__retain - local.set $2 - local.get $1 - call $~lib/rt/stub/__release - local.get $2 + ) + (func $~lib/rt/stub/__release (; 12 ;) (type $FUNCSIG$vi) (param $0 i32) + nop ) (func $~lib/string/String#get:length (; 13 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 diff --git a/tests/compiler/resolve-ternary.optimized.wat b/tests/compiler/resolve-ternary.optimized.wat index 5d995368d7..9b474e835c 100644 --- a/tests/compiler/resolve-ternary.optimized.wat +++ b/tests/compiler/resolve-ternary.optimized.wat @@ -3082,11 +3082,7 @@ i32.const 1 call $~lib/util/number/itoa32 local.tee $0 - call $~lib/rt/pure/__retain - local.set $1 local.get $0 - call $~lib/rt/pure/__release - local.get $1 i32.const 296 call $~lib/string/String.__eq i32.eqz @@ -3100,8 +3096,9 @@ end f64.const 1 call $~lib/util/number/dtoa - local.tee $2 - local.get $2 + local.tee $0 + local.set $2 + local.get $0 i32.const 1568 call $~lib/string/String.__eq i32.eqz @@ -3155,8 +3152,8 @@ call $~lib/builtins/abort unreachable end - local.get $1 call $~lib/rt/pure/__release + local.get $2 call $~lib/rt/pure/__release ) (func $start (; 42 ;) (type $FUNCSIG$v) diff --git a/tests/compiler/resolve-ternary.untouched.wat b/tests/compiler/resolve-ternary.untouched.wat index 49bd9cf119..ceb48b8707 100644 --- a/tests/compiler/resolve-ternary.untouched.wat +++ b/tests/compiler/resolve-ternary.untouched.wat @@ -3503,16 +3503,8 @@ return ) (func $~lib/number/I32#toString (; 31 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) local.get $0 call $~lib/util/number/itoa - local.tee $1 - call $~lib/rt/pure/__retain - local.set $2 - local.get $1 - call $~lib/rt/pure/__release - local.get $2 ) (func $~lib/string/String#get:length (; 32 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 diff --git a/tests/compiler/resolve-unary.untouched.wat b/tests/compiler/resolve-unary.untouched.wat index d50da7ed2b..189238ada3 100644 --- a/tests/compiler/resolve-unary.untouched.wat +++ b/tests/compiler/resolve-unary.untouched.wat @@ -416,20 +416,12 @@ call $~lib/util/number/itoa32 return ) - (func $~lib/rt/stub/__release (; 8 ;) (type $FUNCSIG$vi) (param $0 i32) - nop - ) - (func $~lib/number/I32#toString (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) - (local $1 i32) - (local $2 i32) + (func $~lib/number/I32#toString (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 call $~lib/util/number/itoa - local.tee $1 - call $~lib/rt/stub/__retain - local.set $2 - local.get $1 - call $~lib/rt/stub/__release - local.get $2 + ) + (func $~lib/rt/stub/__release (; 9 ;) (type $FUNCSIG$vi) (param $0 i32) + nop ) (func $~lib/string/String#get:length (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0