From 7096c5b59ace9b0346a68eab430f315254e0a7b5 Mon Sep 17 00:00:00 2001 From: dcode Date: Thu, 19 Aug 2021 16:34:35 +0200 Subject: [PATCH 1/6] Compile virtual dependencies before finalizing stubs --- src/compiler.ts | 183 ++++++++---------- src/program.ts | 15 ++ src/resolver.ts | 53 +++++ .../class-overloading-cast.untouched.wat | 6 +- .../compiler/class-overloading.untouched.wat | 98 +++++----- 5 files changed, 201 insertions(+), 154 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 0e0e06d405..1114343bcf 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -532,25 +532,30 @@ export class Compiler extends DiagnosticEmitter { // set up virtual lookup tables var functionTable = this.functionTable; + var virtualCalls = this.virtualCalls; for (let i = 0, k = functionTable.length; i < k; ++i) { let instance = functionTable[i]; if (instance.is(CommonFlags.VIRTUAL)) { assert(instance.is(CommonFlags.INSTANCE)); functionTable[i] = this.ensureVirtualStub(instance); // incl. varargs - this.finalizeVirtualStub(instance); + virtualCalls.add(instance); } else if (instance.signature.requiredParameters < instance.signature.parameterTypes.length) { functionTable[i] = this.ensureVarargsStub(instance); } } - var virtualCalls = this.virtualCalls; + var virtualCallsToFinalize = new Set(); while (virtualCalls.size) { - // finalizing a stub may discover more virtual calls, so do this in a loop + // compiling a stub's dependencies may find more dependencies, so do this in a loop for (let _values = Set_values(virtualCalls), i = 0, k = _values.length; i < k; ++i) { let instance = unchecked(_values[i]); - this.finalizeVirtualStub(instance); + this.compileVirtualDependencies(instance); virtualCalls.delete(instance); + virtualCallsToFinalize.add(instance); } } + for (let _values = Set_values(virtualCallsToFinalize), i = 0, k = _values.length; i < k; ++i) { + this.finalizeVirtualStub(_values[i]); + } // finalize runtime features module.removeGlobal(BuiltinNames.rtti_base); @@ -6953,16 +6958,23 @@ export class Compiler extends DiagnosticEmitter { return stub; } + /** Compiles the specified function's virtual dependencies. */ + private compileVirtualDependencies(instance: Function): void { + var overloadInstances = this.resolver.resolveOverloads(instance); + if (!overloadInstances) return; + for (let i = 0, k = overloadInstances.length; i < k; ++i) { + this.compileFunction(overloadInstances[i]); + } + } + /** Finalizes the virtual stub of the specified function. */ private finalizeVirtualStub(instance: Function): void { var stub = this.ensureVirtualStub(instance); if (stub.is(CommonFlags.COMPILED)) return; - // Wouldn't be here if there wasn't at least one overload - var overloadPrototypes = assert(instance.prototype.overloads); + var overloadInstances = assert(this.resolver.resolveOverloads(instance)); assert(instance.parent.kind == ElementKind.CLASS || instance.parent.kind == ElementKind.INTERFACE); - var parentClassInstance = instance.parent; var module = this.module; var usizeType = this.options.usizeType; var sizeTypeRef = usizeType.toRef(); @@ -6991,102 +7003,69 @@ export class Compiler extends DiagnosticEmitter { // so we first have to find the concrete classes it became bound to, obtain // their bound prototypes and make sure these are resolved and compiled as // we are going to call them conditionally based on this's class id. - for (let _values = Set_values(overloadPrototypes), i = 0, k = _values.length; i < k; ++i) { - let unboundOverloadPrototype = _values[i]; - assert(!unboundOverloadPrototype.isBound); - let unboundOverloadParent = unboundOverloadPrototype.parent; - let isProperty = unboundOverloadParent.kind == ElementKind.PROPERTY_PROTOTYPE; - let classInstances: Map | null; - if (isProperty) { - let propertyParent = (unboundOverloadParent).parent; - assert(propertyParent.kind == ElementKind.CLASS_PROTOTYPE); - classInstances = (propertyParent).instances; + for (let i = 0, k = overloadInstances.length; i < k; ++i) { + let overloadInstance = overloadInstances[i]; + if (!overloadInstance.is(CommonFlags.COMPILED)) continue; // errored + let overloadType = overloadInstance.type; + let originalType = instance.type; + if (!overloadType.isAssignableTo(originalType)) { + this.error( + DiagnosticCode.Type_0_is_not_assignable_to_type_1, + overloadInstance.identifierNode.range, overloadType.toString(), originalType.toString() + ); + continue; + } + // TODO: additional optional parameters are not permitted by `isAssignableTo` yet + let overloadSignature = overloadInstance.signature; + let overloadParameterTypes = overloadSignature.parameterTypes; + let overloadNumParameters = overloadParameterTypes.length; + let paramExprs = new Array(1 + overloadNumParameters); + paramExprs[0] = module.local_get(0, sizeTypeRef); // this + for (let n = 1; n <= numParameters; ++n) { + paramExprs[n] = module.local_get(n, parameterTypes[n - 1].toRef()); + } + let needsVarargsStub = false; + for (let n = numParameters; n < overloadNumParameters; ++n) { + // TODO: inline constant initializers and skip varargs stub + paramExprs[1 + n] = this.makeZero(overloadParameterTypes[n], overloadInstance.declaration); + needsVarargsStub = true; + } + let calledName = needsVarargsStub + ? this.ensureVarargsStub(overloadInstance).internalName + : overloadInstance.internalName; + let returnTypeRef = overloadSignature.returnType.toRef(); + let stmts = new Array(); + if (needsVarargsStub) { + // Safe to prepend since paramExprs are local.get's + stmts.push(module.global_set(this.ensureArgumentsLength(), module.i32(numParameters))); + } + if (returnType == Type.void) { + stmts.push( + module.call(calledName, paramExprs, returnTypeRef) + ); + stmts.push( + module.return() + ); } else { - assert(unboundOverloadParent.kind == ElementKind.CLASS_PROTOTYPE); - classInstances = (unboundOverloadParent).instances; - } - if (classInstances) { - for (let _values = Map_values(classInstances), j = 0, l = _values.length; j < l; ++j) { - let classInstance = _values[j]; - // Chcek if the parent class is a subtype of instance's class - if (!classInstance.isAssignableTo(parentClassInstance)) continue; - let overloadInstance: Function | null; - if (isProperty) { - let boundProperty = assert(classInstance.members!.get(unboundOverloadParent.name)); - assert(boundProperty.kind == ElementKind.PROPERTY_PROTOTYPE); - let boundPropertyInstance = this.resolver.resolveProperty(boundProperty); - if (!boundPropertyInstance) continue; - if (instance.is(CommonFlags.GET)) { - overloadInstance = boundPropertyInstance.getterInstance; - } else { - assert(instance.is(CommonFlags.SET)); - overloadInstance = boundPropertyInstance.setterInstance; - } - } else { - let boundPrototype = assert(classInstance.members!.get(unboundOverloadPrototype.name)); - assert(boundPrototype.kind == ElementKind.FUNCTION_PROTOTYPE); - overloadInstance = this.resolver.resolveFunction(boundPrototype, instance.typeArguments); - } - if (!overloadInstance || !this.compileFunction(overloadInstance)) continue; - let overloadType = overloadInstance.type; - let originalType = instance.type; - if (!overloadType.isAssignableTo(originalType)) { - this.error( - DiagnosticCode.Type_0_is_not_assignable_to_type_1, - overloadInstance.identifierNode.range, overloadType.toString(), originalType.toString() - ); - continue; - } - // TODO: additional optional parameters are not permitted by `isAssignableTo` yet - let overloadSignature = overloadInstance.signature; - let overloadParameterTypes = overloadSignature.parameterTypes; - let overloadNumParameters = overloadParameterTypes.length; - let paramExprs = new Array(1 + overloadNumParameters); - paramExprs[0] = module.local_get(0, sizeTypeRef); // this - for (let n = 1; n <= numParameters; ++n) { - paramExprs[n] = module.local_get(n, parameterTypes[n - 1].toRef()); - } - let needsVarargsStub = false; - for (let n = numParameters; n < overloadNumParameters; ++n) { - // TODO: inline constant initializers and skip varargs stub - paramExprs[1 + n] = this.makeZero(overloadParameterTypes[n], overloadInstance.declaration); - needsVarargsStub = true; - } - let calledName = needsVarargsStub - ? this.ensureVarargsStub(overloadInstance).internalName - : overloadInstance.internalName; - let returnTypeRef = overloadSignature.returnType.toRef(); - let stmts = new Array(); - if (needsVarargsStub) { - // Safe to prepend since paramExprs are local.get's - stmts.push(module.global_set(this.ensureArgumentsLength(), module.i32(numParameters))); - } - if (returnType == Type.void) { - stmts.push( - module.call(calledName, paramExprs, returnTypeRef) - ); - stmts.push( - module.return() - ); - } else { - stmts.push( - module.return( - module.call(calledName, paramExprs, returnTypeRef) - ) - ); - } - builder.addCase(classInstance.id, stmts); - // Also alias each extendee inheriting this exact overload - let extendees = classInstance.getAllExtendees( - isProperty - ? unboundOverloadParent.name - : instance.prototype.name - ); - for (let _values = Set_values(extendees), a = 0, b = _values.length; a < b; ++a) { - let extendee = _values[a]; - builder.addCase(extendee.id, stmts); - } - } + stmts.push( + module.return( + module.call(calledName, paramExprs, returnTypeRef) + ) + ); + } + let classInstance = assert(overloadInstance.getClassOrInterface()); + builder.addCase(classInstance.id, stmts); + // Also alias each extendee inheriting this exact overload + let extendees = classInstance.getAllExtendees( + overloadInstance.isAccessor + // FIXME: Parent is not the property, but should be, so we don't need + // to use a substring here but can trivially use the property name. + ? instance.prototype.name.substring(GETTER_PREFIX.length) + : instance.prototype.name + ); + for (let _values = Set_values(extendees), a = 0, b = _values.length; a < b; ++a) { + let extendee = _values[a]; + builder.addCase(extendee.id, stmts); } } diff --git a/src/program.ts b/src/program.ts index e614a0da5f..226502a4fb 100644 --- a/src/program.ts +++ b/src/program.ts @@ -3653,6 +3653,11 @@ export class Function extends TypedElement { registerConcreteElement(program, this); } + /** Gets whether this is an accessor method of a property. */ + get isAccessor(): bool { + return this.isAny(CommonFlags.GET | CommonFlags.SET); + } + /** Gets the name of the parameter at the specified index. */ getParameterName(index: i32): string { var parameters = (this.declaration).signature.parameters; @@ -3661,6 +3666,16 @@ export class Function extends TypedElement { : getDefaultParameterName(index); } + /** Gets the class or interface this function belongs to, if an instance method. */ + getClassOrInterface(): Class | null { + var parent = this.parent; + if (parent.kind == ElementKind.PROPERTY) parent = parent.parent; + if (parent.kind == ElementKind.CLASS || parent.kind == ElementKind.INTERFACE) { + return parent; + } + return null; + } + /** Creates a stub for use with this function, i.e. for varargs or virtual calls. */ newStub(postfix: string): Function { var stub = new Function( diff --git a/src/resolver.ts b/src/resolver.ts index b1f7b41dc9..b727ff7833 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -2833,6 +2833,59 @@ export class Resolver extends DiagnosticEmitter { ); } + /** Resolves reachable overloads of the given instance method. */ + resolveOverloads(instance: Function): Function[] | null { + var overloadPrototypes = instance.prototype.overloads; + if (!overloadPrototypes) return null; + + var parentClassInstance = assert(instance.getClassOrInterface()); + var overloads = new Set(); + + // A method's `overloads` property contains its unbound overload prototypes + // so we first have to find the concrete classes it became bound to, obtain + // their bound prototypes and make sure these are resolved. + for (let _values = Set_values(overloadPrototypes), i = 0, k = _values.length; i < k; ++i) { + let unboundOverloadPrototype = _values[i]; + assert(!unboundOverloadPrototype.isBound); + let unboundOverloadParent = unboundOverloadPrototype.parent; + let isProperty = unboundOverloadParent.kind == ElementKind.PROPERTY_PROTOTYPE; + let classInstances: Map | null; + if (isProperty) { + let propertyParent = (unboundOverloadParent).parent; + assert(propertyParent.kind == ElementKind.CLASS_PROTOTYPE); + classInstances = (propertyParent).instances; + } else { + assert(unboundOverloadParent.kind == ElementKind.CLASS_PROTOTYPE); + classInstances = (unboundOverloadParent).instances; + } + if (!classInstances) continue; + for (let _values = Map_values(classInstances), j = 0, l = _values.length; j < l; ++j) { + let classInstance = _values[j]; + // Check if the parent class is a subtype of instance's class + if (!classInstance.isAssignableTo(parentClassInstance)) continue; + let overloadInstance: Function | null; + if (isProperty) { + let boundProperty = assert(classInstance.members!.get(unboundOverloadParent.name)); + assert(boundProperty.kind == ElementKind.PROPERTY_PROTOTYPE); + let boundPropertyInstance = this.resolveProperty(boundProperty); + if (!boundPropertyInstance) continue; + if (instance.is(CommonFlags.GET)) { + overloadInstance = boundPropertyInstance.getterInstance; + } else { + assert(instance.is(CommonFlags.SET)); + overloadInstance = boundPropertyInstance.setterInstance; + } + } else { + let boundPrototype = assert(classInstance.members!.get(unboundOverloadPrototype.name)); + assert(boundPrototype.kind == ElementKind.FUNCTION_PROTOTYPE); + overloadInstance = this.resolveFunction(boundPrototype, instance.typeArguments); + } + if (overloadInstance) overloads.add(overloadInstance); + } + } + return Set_values(overloads); + } + /** Currently resolving classes. */ private resolveClassPending: Set = new Set(); diff --git a/tests/compiler/class-overloading-cast.untouched.wat b/tests/compiler/class-overloading-cast.untouched.wat index 76de41f7d4..c6ef378905 100644 --- a/tests/compiler/class-overloading-cast.untouched.wat +++ b/tests/compiler/class-overloading-cast.untouched.wat @@ -2527,6 +2527,9 @@ (func $class-overloading-cast/B#foo (param $0 i32) (param $1 i32) (result i32) i32.const 464 ) + (func $class-overloading-cast/B#foo (param $0 i32) (param $1 f64) (result i32) + i32.const 464 + ) (func $class-overloading-cast/A#foo@virtual (param $0 i32) (param $1 i32) (result i32) (local $2 i32) block $default @@ -2561,9 +2564,6 @@ local.get $1 call $class-overloading-cast/A#foo ) - (func $class-overloading-cast/B#foo (param $0 i32) (param $1 f64) (result i32) - i32.const 464 - ) (func $class-overloading-cast/A#foo@virtual (param $0 i32) (param $1 f64) (result i32) (local $2 i32) block $default diff --git a/tests/compiler/class-overloading.untouched.wat b/tests/compiler/class-overloading.untouched.wat index 7030572e97..5942c725e6 100644 --- a/tests/compiler/class-overloading.untouched.wat +++ b/tests/compiler/class-overloading.untouched.wat @@ -2567,6 +2567,55 @@ i32.const 624 global.set $class-overloading/which ) + (func $class-overloading/B#b (param $0 i32) (param $1 i32) + i32.const 496 + global.set $class-overloading/which + ) + (func $class-overloading/F#b (param $0 i32) (param $1 i32) + i32.const 624 + global.set $class-overloading/which + ) + (func $class-overloading/B#get:c (param $0 i32) (result i32) + i32.const 496 + global.set $class-overloading/which + i32.const 0 + ) + (func $class-overloading/F#get:c (param $0 i32) (result i32) + i32.const 624 + global.set $class-overloading/which + i32.const 0 + ) + (func $class-overloading/B#set:c (param $0 i32) (param $1 i32) + i32.const 496 + global.set $class-overloading/which + ) + (func $class-overloading/F#set:c (param $0 i32) (param $1 i32) + i32.const 624 + global.set $class-overloading/which + ) + (func $class-overloading/CA#foo (param $0 i32) + i32.const 656 + global.set $class-overloading/which + ) + (func $class-overloading/CC#foo (param $0 i32) + i32.const 688 + global.set $class-overloading/which + ) + (func $class-overloading/A1#baz (param $0 i32) (result i32) + i32.const 720 + i32.const 528 + i32.const 186 + i32.const 5 + call $~lib/builtins/abort + unreachable + ) + (func $class-overloading/A1#bar (param $0 i32) (result i32) + local.get $0 + call $class-overloading/A1#baz@virtual + ) + (func $class-overloading/B1#baz (param $0 i32) (result i32) + i32.const 3 + ) (func $class-overloading/A#a@virtual (param $0 i32) (param $1 i32) (local $2 i32) block $default @@ -2619,14 +2668,6 @@ local.get $1 call $class-overloading/A#a ) - (func $class-overloading/B#b (param $0 i32) (param $1 i32) - i32.const 496 - global.set $class-overloading/which - ) - (func $class-overloading/F#b (param $0 i32) (param $1 i32) - i32.const 624 - global.set $class-overloading/which - ) (func $class-overloading/A#b@virtual (param $0 i32) (param $1 i32) (local $2 i32) block $default @@ -2679,16 +2720,6 @@ local.get $1 call $class-overloading/A#b ) - (func $class-overloading/B#get:c (param $0 i32) (result i32) - i32.const 496 - global.set $class-overloading/which - i32.const 0 - ) - (func $class-overloading/F#get:c (param $0 i32) (result i32) - i32.const 624 - global.set $class-overloading/which - i32.const 0 - ) (func $class-overloading/A#get:c@virtual (param $0 i32) (result i32) (local $1 i32) block $default @@ -2737,14 +2768,6 @@ local.get $0 call $class-overloading/A#get:c ) - (func $class-overloading/B#set:c (param $0 i32) (param $1 i32) - i32.const 496 - global.set $class-overloading/which - ) - (func $class-overloading/F#set:c (param $0 i32) (param $1 i32) - i32.const 624 - global.set $class-overloading/which - ) (func $class-overloading/A#set:c@virtual (param $0 i32) (param $1 i32) (local $2 i32) block $default @@ -2797,14 +2820,6 @@ local.get $1 call $class-overloading/A#set:c ) - (func $class-overloading/CA#foo (param $0 i32) - i32.const 656 - global.set $class-overloading/which - ) - (func $class-overloading/CC#foo (param $0 i32) - i32.const 688 - global.set $class-overloading/which - ) (func $class-overloading/IA#foo@virtual (param $0 i32) (local $1 i32) block $default @@ -2835,18 +2850,6 @@ end unreachable ) - (func $class-overloading/A1#baz (param $0 i32) (result i32) - i32.const 720 - i32.const 528 - i32.const 186 - i32.const 5 - call $~lib/builtins/abort - unreachable - ) - (func $class-overloading/A1#bar (param $0 i32) (result i32) - local.get $0 - call $class-overloading/A1#baz@virtual - ) (func $class-overloading/A2#foo@virtual (param $0 i32) (result i32) (local $1 i32) block $default @@ -2869,9 +2872,6 @@ local.get $0 call $class-overloading/A2#foo ) - (func $class-overloading/B1#baz (param $0 i32) (result i32) - i32.const 3 - ) (func $class-overloading/A1#baz@virtual (param $0 i32) (result i32) (local $1 i32) block $default From 78f338c26018c92c851a6a1653970da3436b9b3d Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 20 Aug 2021 19:07:31 +0200 Subject: [PATCH 2/6] look at declaration --- src/compiler.ts | 8 +------- src/program.ts | 10 ++++------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 1114343bcf..8c1dc8dce4 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7056,13 +7056,7 @@ export class Compiler extends DiagnosticEmitter { let classInstance = assert(overloadInstance.getClassOrInterface()); builder.addCase(classInstance.id, stmts); // Also alias each extendee inheriting this exact overload - let extendees = classInstance.getAllExtendees( - overloadInstance.isAccessor - // FIXME: Parent is not the property, but should be, so we don't need - // to use a substring here but can trivially use the property name. - ? instance.prototype.name.substring(GETTER_PREFIX.length) - : instance.prototype.name - ); + let extendees = classInstance.getAllExtendees(instance.declaration.name.text); // without get:/set: for (let _values = Set_values(extendees), a = 0, b = _values.length; a < b; ++a) { let extendee = _values[a]; builder.addCase(extendee.id, stmts); diff --git a/src/program.ts b/src/program.ts index 226502a4fb..b21f6ecab5 100644 --- a/src/program.ts +++ b/src/program.ts @@ -3456,7 +3456,7 @@ export class FunctionPrototype extends DeclaredElement { /** Methods overloading this one, if any. These are unbound. */ overloads: Set | null = null; - /** Clones of this prototype that are bounds to specific classes. */ + /** Clones of this prototype that are bound to specific classes. */ private boundPrototypes: Map | null = null; /** Constructs a new function prototype. */ @@ -3504,11 +3504,9 @@ export class FunctionPrototype extends DeclaredElement { /** Tests if this prototype is bound to a class. */ get isBound(): bool { var parent = this.parent; - return parent.kind == ElementKind.CLASS || - parent.kind == ElementKind.PROPERTY_PROTOTYPE && ( - parent.parent.kind == ElementKind.CLASS || - parent.parent.kind == ElementKind.INTERFACE - ); + var parentKind = parent.kind; + if (parentKind == ElementKind.PROPERTY_PROTOTYPE) parentKind = parent.parent.kind; + return parentKind == ElementKind.CLASS || parentKind == ElementKind.INTERFACE; } /** Creates a clone of this prototype that is bound to a concrete class instead. */ From a8ad41edd63e651ce88801fa41a5351aaead1634 Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 3 Sep 2021 17:34:50 +0200 Subject: [PATCH 3/6] handle new overloads being discovered --- src/compiler.ts | 35 +++++++++++++++++------------------ src/resolver.ts | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 8c1dc8dce4..1ce5a462ae 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -450,6 +450,7 @@ export class Compiler extends DiagnosticEmitter { var options = this.options; var module = this.module; var program = this.program; + var resolver = this.resolver; var hasShadowStack = options.stackSize > 0; // implies runtime=incremental // initialize lookup maps, built-ins, imports, exports, etc. @@ -530,7 +531,7 @@ export class Compiler extends DiagnosticEmitter { compileClassInstanceOf(this, prototype); } - // set up virtual lookup tables + // set up virtual stubs var functionTable = this.functionTable; var virtualCalls = this.virtualCalls; for (let i = 0, k = functionTable.length; i < k; ++i) { @@ -543,17 +544,24 @@ export class Compiler extends DiagnosticEmitter { functionTable[i] = this.ensureVarargsStub(instance); } } - var virtualCallsToFinalize = new Set(); - while (virtualCalls.size) { - // compiling a stub's dependencies may find more dependencies, so do this in a loop + var virtualCallsSeen = new Set(); + do { + // virtual stubs and overloads have cross-dependencies on each other, in that compiling + // either may discover the respective other. do this in a loop until no more are found. + resolver.discoveredOverload = false; for (let _values = Set_values(virtualCalls), i = 0, k = _values.length; i < k; ++i) { let instance = unchecked(_values[i]); - this.compileVirtualDependencies(instance); - virtualCalls.delete(instance); - virtualCallsToFinalize.add(instance); + let overloadInstances = resolver.resolveOverloads(instance); + if (overloadInstances) { + for (let i = 0, k = overloadInstances.length; i < k; ++i) { + this.compileFunction(overloadInstances[i]); + } + } + virtualCallsSeen.add(instance); } - } - for (let _values = Set_values(virtualCallsToFinalize), i = 0, k = _values.length; i < k; ++i) { + } while (virtualCalls.size > virtualCallsSeen.size || resolver.discoveredOverload); + virtualCallsSeen.clear(); + for (let _values = Set_values(virtualCalls), i = 0, k = _values.length; i < k; ++i) { this.finalizeVirtualStub(_values[i]); } @@ -6958,15 +6966,6 @@ export class Compiler extends DiagnosticEmitter { return stub; } - /** Compiles the specified function's virtual dependencies. */ - private compileVirtualDependencies(instance: Function): void { - var overloadInstances = this.resolver.resolveOverloads(instance); - if (!overloadInstances) return; - for (let i = 0, k = overloadInstances.length; i < k; ++i) { - this.compileFunction(overloadInstances[i]); - } - } - /** Finalizes the virtual stub of the specified function. */ private finalizeVirtualStub(instance: Function): void { var stub = this.ensureVirtualStub(instance); diff --git a/src/resolver.ts b/src/resolver.ts index b727ff7833..42c9a8cbdd 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -126,6 +126,8 @@ export class Resolver extends DiagnosticEmitter { currentThisExpression: Expression | null = null; /** Element expression of the previously resolved element access. */ currentElementExpression : Expression | null = null; + /** Whether a new overload has been discovered. */ + discoveredOverload: bool = false; /** Constructs the resolver for the specified program. */ constructor( @@ -2757,6 +2759,20 @@ export class Resolver extends DiagnosticEmitter { ctxTypes ); prototype.setResolvedInstance(instanceKey, instance); + + // remember discovered overloads for virtual stub finalization + if (classInstance) { + let methodOrPropertyName = instance.declaration.name.text; + let baseClass = classInstance.base; + while (baseClass) { + let baseMembers = baseClass.members; + if (baseMembers && baseMembers.has(methodOrPropertyName)) { + this.discoveredOverload = true; + break; + } + baseClass = baseClass.base; + } + } return instance; } From 84960f8b73f0b976ff59ee98864e69e863fb87a8 Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 3 Sep 2021 20:51:44 +0200 Subject: [PATCH 4/6] clean --- src/compiler.ts | 144 +++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 74 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index 1ce5a462ae..ce61b3c6a1 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -389,8 +389,8 @@ export class Compiler extends DiagnosticEmitter { lazyFunctions: Set = new Set(); /** Pending class-specific instanceof helpers. */ pendingClassInstanceOf: Set = new Set(); - /** Functions potentially involving a virtual call. */ - virtualCalls: Set = new Set(); + /** Virtually called stubs that may have overloads. */ + virtualStubs: Set = new Set(); /** Elements currently undergoing compilation. */ pendingElements: Set = new Set(); /** Elements, that are module exports, already processed */ @@ -533,23 +533,23 @@ export class Compiler extends DiagnosticEmitter { // set up virtual stubs var functionTable = this.functionTable; - var virtualCalls = this.virtualCalls; + var virtualStubs = this.virtualStubs; for (let i = 0, k = functionTable.length; i < k; ++i) { let instance = functionTable[i]; if (instance.is(CommonFlags.VIRTUAL)) { assert(instance.is(CommonFlags.INSTANCE)); functionTable[i] = this.ensureVirtualStub(instance); // incl. varargs - virtualCalls.add(instance); + virtualStubs.add(instance); } else if (instance.signature.requiredParameters < instance.signature.parameterTypes.length) { functionTable[i] = this.ensureVarargsStub(instance); } } - var virtualCallsSeen = new Set(); + var virtualStubsSeen = new Set(); do { // virtual stubs and overloads have cross-dependencies on each other, in that compiling // either may discover the respective other. do this in a loop until no more are found. resolver.discoveredOverload = false; - for (let _values = Set_values(virtualCalls), i = 0, k = _values.length; i < k; ++i) { + for (let _values = Set_values(virtualStubs), i = 0, k = _values.length; i < k; ++i) { let instance = unchecked(_values[i]); let overloadInstances = resolver.resolveOverloads(instance); if (overloadInstances) { @@ -557,11 +557,11 @@ export class Compiler extends DiagnosticEmitter { this.compileFunction(overloadInstances[i]); } } - virtualCallsSeen.add(instance); + virtualStubsSeen.add(instance); } - } while (virtualCalls.size > virtualCallsSeen.size || resolver.discoveredOverload); - virtualCallsSeen.clear(); - for (let _values = Set_values(virtualCalls), i = 0, k = _values.length; i < k; ++i) { + } while (virtualStubs.size > virtualStubsSeen.size || resolver.discoveredOverload); + virtualStubsSeen.clear(); + for (let _values = Set_values(virtualStubs), i = 0, k = _values.length; i < k; ++i) { this.finalizeVirtualStub(_values[i]); } @@ -6962,7 +6962,7 @@ export class Compiler extends DiagnosticEmitter { null, module.unreachable() ); - this.virtualCalls.add(original); + this.virtualStubs.add(original); return stub; } @@ -6971,8 +6971,6 @@ export class Compiler extends DiagnosticEmitter { var stub = this.ensureVirtualStub(instance); if (stub.is(CommonFlags.COMPILED)) return; - var overloadInstances = assert(this.resolver.resolveOverloads(instance)); - assert(instance.parent.kind == ElementKind.CLASS || instance.parent.kind == ElementKind.INTERFACE); var module = this.module; var usizeType = this.options.usizeType; @@ -6997,68 +6995,66 @@ export class Compiler extends DiagnosticEmitter { TypeRef.I32 ) ); - - // A method's `overloads` property contains its unbound overload prototypes - // so we first have to find the concrete classes it became bound to, obtain - // their bound prototypes and make sure these are resolved and compiled as - // we are going to call them conditionally based on this's class id. - for (let i = 0, k = overloadInstances.length; i < k; ++i) { - let overloadInstance = overloadInstances[i]; - if (!overloadInstance.is(CommonFlags.COMPILED)) continue; // errored - let overloadType = overloadInstance.type; - let originalType = instance.type; - if (!overloadType.isAssignableTo(originalType)) { - this.error( - DiagnosticCode.Type_0_is_not_assignable_to_type_1, - overloadInstance.identifierNode.range, overloadType.toString(), originalType.toString() - ); - continue; - } - // TODO: additional optional parameters are not permitted by `isAssignableTo` yet - let overloadSignature = overloadInstance.signature; - let overloadParameterTypes = overloadSignature.parameterTypes; - let overloadNumParameters = overloadParameterTypes.length; - let paramExprs = new Array(1 + overloadNumParameters); - paramExprs[0] = module.local_get(0, sizeTypeRef); // this - for (let n = 1; n <= numParameters; ++n) { - paramExprs[n] = module.local_get(n, parameterTypes[n - 1].toRef()); - } - let needsVarargsStub = false; - for (let n = numParameters; n < overloadNumParameters; ++n) { - // TODO: inline constant initializers and skip varargs stub - paramExprs[1 + n] = this.makeZero(overloadParameterTypes[n], overloadInstance.declaration); - needsVarargsStub = true; - } - let calledName = needsVarargsStub - ? this.ensureVarargsStub(overloadInstance).internalName - : overloadInstance.internalName; - let returnTypeRef = overloadSignature.returnType.toRef(); - let stmts = new Array(); - if (needsVarargsStub) { - // Safe to prepend since paramExprs are local.get's - stmts.push(module.global_set(this.ensureArgumentsLength(), module.i32(numParameters))); - } - if (returnType == Type.void) { - stmts.push( - module.call(calledName, paramExprs, returnTypeRef) - ); - stmts.push( - module.return() - ); - } else { - stmts.push( - module.return( + var overloadInstances = this.resolver.resolveOverloads(instance); + if (overloadInstances) { + for (let i = 0, k = overloadInstances.length; i < k; ++i) { + let overloadInstance = overloadInstances[i]; + if (!overloadInstance.is(CommonFlags.COMPILED)) continue; // errored + let overloadType = overloadInstance.type; + let originalType = instance.type; + if (!overloadType.isAssignableTo(originalType)) { + this.error( + DiagnosticCode.Type_0_is_not_assignable_to_type_1, + overloadInstance.identifierNode.range, overloadType.toString(), originalType.toString() + ); + continue; + } + // TODO: additional optional parameters are not permitted by `isAssignableTo` yet + let overloadSignature = overloadInstance.signature; + let overloadParameterTypes = overloadSignature.parameterTypes; + let overloadNumParameters = overloadParameterTypes.length; + let paramExprs = new Array(1 + overloadNumParameters); + paramExprs[0] = module.local_get(0, sizeTypeRef); // this + for (let n = 1; n <= numParameters; ++n) { + paramExprs[n] = module.local_get(n, parameterTypes[n - 1].toRef()); + } + let needsVarargsStub = false; + for (let n = numParameters; n < overloadNumParameters; ++n) { + // TODO: inline constant initializers and skip varargs stub + paramExprs[1 + n] = this.makeZero(overloadParameterTypes[n], overloadInstance.declaration); + needsVarargsStub = true; + } + let calledName = needsVarargsStub + ? this.ensureVarargsStub(overloadInstance).internalName + : overloadInstance.internalName; + let returnTypeRef = overloadSignature.returnType.toRef(); + let stmts = new Array(); + if (needsVarargsStub) { + // Safe to prepend since paramExprs are local.get's + stmts.push(module.global_set(this.ensureArgumentsLength(), module.i32(numParameters))); + } + if (returnType == Type.void) { + stmts.push( module.call(calledName, paramExprs, returnTypeRef) - ) - ); - } - let classInstance = assert(overloadInstance.getClassOrInterface()); - builder.addCase(classInstance.id, stmts); - // Also alias each extendee inheriting this exact overload - let extendees = classInstance.getAllExtendees(instance.declaration.name.text); // without get:/set: - for (let _values = Set_values(extendees), a = 0, b = _values.length; a < b; ++a) { - let extendee = _values[a]; - builder.addCase(extendee.id, stmts); + ); + stmts.push( + module.return() + ); + } else { + stmts.push( + module.return( + module.call(calledName, paramExprs, returnTypeRef) + ) + ); + } + let classInstance = assert(overloadInstance.getClassOrInterface()); + builder.addCase(classInstance.id, stmts); + // Also alias each extendee inheriting this exact overload + let extendees = classInstance.getAllExtendees(instance.declaration.name.text); // without get:/set: + for (let _values = Set_values(extendees), a = 0, b = _values.length; a < b; ++a) { + let extendee = _values[a]; + builder.addCase(extendee.id, stmts); + } } } From d2e6bc587ee1eb8fbbaa46aaee5316d21a6cf7d6 Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 3 Sep 2021 20:53:13 +0200 Subject: [PATCH 5/6] more --- src/compiler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler.ts b/src/compiler.ts index ce61b3c6a1..b6bf25da19 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -538,8 +538,7 @@ export class Compiler extends DiagnosticEmitter { let instance = functionTable[i]; if (instance.is(CommonFlags.VIRTUAL)) { assert(instance.is(CommonFlags.INSTANCE)); - functionTable[i] = this.ensureVirtualStub(instance); // incl. varargs - virtualStubs.add(instance); + functionTable[i] = this.ensureVirtualStub(instance); // includes varargs stub } else if (instance.signature.requiredParameters < instance.signature.parameterTypes.length) { functionTable[i] = this.ensureVarargsStub(instance); } From ac8f105e9e6eff779a745ea547e271fd8ea3dd5e Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 3 Sep 2021 20:54:49 +0200 Subject: [PATCH 6/6] more --- src/program.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/program.ts b/src/program.ts index b21f6ecab5..8ae21bafd2 100644 --- a/src/program.ts +++ b/src/program.ts @@ -3651,11 +3651,6 @@ export class Function extends TypedElement { registerConcreteElement(program, this); } - /** Gets whether this is an accessor method of a property. */ - get isAccessor(): bool { - return this.isAny(CommonFlags.GET | CommonFlags.SET); - } - /** Gets the name of the parameter at the specified index. */ getParameterName(index: i32): string { var parameters = (this.declaration).signature.parameters;