diff --git a/src/builtins.ts b/src/builtins.ts index 7bc25737e0..87069da616 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -9540,7 +9540,7 @@ function ensureVisitMembersOf(compiler: Compiler, instance: Class): void { // for example to visit all members of a collection, e.g. arrays and maps. var hasVisitImpl = false; if (instance.isDeclaredInLibrary) { - let visitPrototype = instance.lookupInSelf("__visit"); + let visitPrototype = instance.getMember("__visit"); if (visitPrototype) { assert(visitPrototype.kind == ElementKind.FUNCTION_PROTOTYPE); let visitInstance = program.resolver.resolveFunction(visitPrototype, null); diff --git a/src/compiler.ts b/src/compiler.ts index 82be831671..de05434a1b 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -88,8 +88,7 @@ import { PropertyPrototype, IndexSignature, File, - mangleInternalName, - DeclaredElement + mangleInternalName } from "./program"; import { @@ -8428,7 +8427,7 @@ export class Compiler extends DiagnosticEmitter { ) ); // tempData = tempThis.dataStart - var dataStartMember = assert(arrayInstance.lookupInSelf("dataStart")); + var dataStartMember = assert(arrayInstance.getMember("dataStart")); assert(dataStartMember.kind == ElementKind.FIELD); stmts.push( module.local_set(tempDataStart.index, @@ -8677,8 +8676,8 @@ export class Compiler extends DiagnosticEmitter { // Iterate through the members defined in our expression for (let i = 0; i < numNames; ++i) { let memberName = names[i].text; - let member: DeclaredElement; - if (!members || !members.has(memberName) || (member = assert(members.get(memberName))).kind != ElementKind.FIELD) { + let member = classReference.getMember(memberName); + if (!member || member.kind != ElementKind.FIELD) { this.error( DiagnosticCode.Property_0_does_not_exist_on_type_1, names[i].range, memberName, classType.toString() diff --git a/src/program.ts b/src/program.ts index 8300f27947..7fa8f417d0 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1223,7 +1223,7 @@ export class Program extends DiagnosticEmitter { ); } } else { // i.e. export { foo [as bar] } - let element = file.lookupInSelf(localName); + let element = file.getMember(localName); if (element) { file.ensureExport(exportName, element); } else { @@ -1751,7 +1751,7 @@ export class Program extends DiagnosticEmitter { } // exported from this file - element = foreignFile.lookupInSelf(queuedExport.localIdentifier.text); + element = foreignFile.getMember(queuedExport.localIdentifier.text); if (element) return element; } } @@ -2200,7 +2200,7 @@ export class Program extends DiagnosticEmitter { if (foreignPath === null) { // resolve right away if the local element already exists - if (element = localFile.lookupInSelf(localName)) { + if (element = localFile.getMember(localName)) { localFile.ensureExport(foreignName, element); // otherwise queue it @@ -2514,7 +2514,7 @@ export class Program extends DiagnosticEmitter { this.checkDecorators(declaration.decorators, DecoratorFlags.GLOBAL) ); if (!parent.add(name, original)) return null; - var element = assert(parent.lookupInSelf(name)); // possibly merged + var element = assert(parent.getMember(name)); // possibly merged var members = declaration.members; for (let i = 0, k = members.length; i < k; ++i) { let member = members[i]; @@ -2775,15 +2775,17 @@ export abstract class Element { /** Tests if this element has a specific decorator flag or flags. */ hasDecorator(flag: DecoratorFlags): bool { return (this.decoratorFlags & flag) == flag; } - /** Looks up the element with the specified name within this element. */ - lookupInSelf(name: string): DeclaredElement | null { + /** Get the member with the specified name, if any. */ + getMember(name: string): DeclaredElement | null { var members = this.members; - if (members !== null && members.has(name)) return assert(members.get(name)); + if (members && members.has(name)) return assert(members.get(name)); return null; } - /** Looks up the element with the specified name relative to this element, like in JS. */ - abstract lookup(name: string): Element | null; + /** Looks up the element with the specified name relative to this element. */ + lookup(name: string, isType: bool = false): Element | null { + return this.parent.lookup(name, isType); + } /** Adds an element as a member of this one. Reports and returns `false` if a duplicate. */ add(name: string, element: DeclaredElement, localIdentifierIfImport: IdentifierExpression | null = null): bool { @@ -3038,7 +3040,7 @@ export class File extends Element { element = this.program.ensureGlobal(name, element); // possibly merged globally } if (!super.add(name, element, localIdentifierIfImport)) return false; - element = assert(this.lookupInSelf(name)); // possibly merged locally + element = assert(this.getMember(name)); // possibly merged locally if (element.is(CommonFlags.EXPORT) && !localIdentifierIfImport) { this.ensureExport( element.name, @@ -3049,23 +3051,23 @@ export class File extends Element { } /* @override */ - lookupInSelf(name: string): DeclaredElement | null { - var element = super.lookupInSelf(name); + getMember(name: string): DeclaredElement | null { + var element = super.getMember(name); if (element) return element; var exportsStar = this.exportsStar; if (exportsStar) { for (let i = 0, k = exportsStar.length; i < k; ++i) { - if (element = exportsStar[i].lookupInSelf(name)) return element; + if (element = exportsStar[i].getMember(name)) return element; } } return null; } /* @override */ - lookup(name: string): Element | null { - var element = this.lookupInSelf(name); + lookup(name: string, isType: bool = false): Element | null { + var element = this.getMember(name); if (element) return element; - return this.program.lookup(name); + return this.program.lookup(name); // has no meaningful parent } /** Ensures that an element is an export of this file. */ @@ -3176,11 +3178,6 @@ export class TypeDefinition extends TypedElement { get typeNode(): TypeNode { return (this.declaration).type; } - - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** A namespace that differs from a file in being user-declared with a name. */ @@ -3209,10 +3206,10 @@ export class Namespace extends DeclaredElement { } /* @override */ - lookup(name: string): Element | null { - var inSelf = this.lookupInSelf(name); - if (inSelf) return inSelf; - return this.parent.lookup(name); + lookup(name: string, isType: bool = false): Element | null { + var member = this.getMember(name); + if (member) return member; + return super.lookup(name, isType); } } @@ -3243,10 +3240,10 @@ export class Enum extends TypedElement { } /* @override */ - lookup(name: string): Element | null { - var inSelf = this.lookupInSelf(name); - if (inSelf) return inSelf; - return this.parent.lookup(name); + lookup(name: string, isType: bool = false): Element | null { + var member = this.getMember(name); + if (member) return member; + return super.lookup(name, isType); } } @@ -3319,11 +3316,6 @@ export abstract class VariableLikeElement extends TypedElement { this.constantFloatValue = value; this.set(CommonFlags.CONST | CommonFlags.INLINED | CommonFlags.RESOLVED); } - - /** @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** An enum value. */ @@ -3357,11 +3349,6 @@ export class EnumValue extends VariableLikeElement { get valueNode(): Expression | null { return (this.declaration).initializer; } - - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** A global variable. */ @@ -3546,11 +3533,6 @@ export class FunctionPrototype extends DeclaredElement { else assert(!instances.has(instanceKey)); instances.set(instanceKey, instance); } - - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** A resolved function. */ @@ -3709,10 +3691,12 @@ export class Function extends TypedElement { } /* @override */ - lookup(name: string): Element | null { - var locals = this.localsByName; - if (locals.has(name)) return assert(locals.get(name)); - return this.parent.lookup(name); + lookup(name: string, isType: bool = false): Element | null { + if (!isType) { + let locals = this.localsByName; + if (locals.has(name)) return assert(locals.get(name)); + } + return super.lookup(name, isType); } // used by flows to keep track of temporary locals @@ -3797,11 +3781,6 @@ export class FieldPrototype extends DeclaredElement { get parameterIndex(): i32 { return (this.declaration).parameterIndex; } - - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** A resolved instance field. */ @@ -3912,11 +3891,6 @@ export class PropertyPrototype extends DeclaredElement { this.flags &= ~(CommonFlags.GET | CommonFlags.SET); } - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } - /** Tests if this prototype is bound to a class. */ get isBound(): bool { switch (this.parent.kind) { @@ -3992,11 +3966,6 @@ export class Property extends VariableLikeElement { registerConcreteElement(this.program, this); } } - - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** A resolved index signature. */ @@ -4026,11 +3995,6 @@ export class IndexSignature extends TypedElement { getSetterInstance(isUnchecked: bool): Function | null { return (this.parent).lookupOverload(OperatorKind.INDEXED_SET, isUnchecked); } - - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** A yet unresolved class prototype. */ @@ -4155,11 +4119,6 @@ export class ClassPrototype extends DeclaredElement { else assert(!instances.has(instanceKey)); instances.set(instanceKey, instance); } - - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } } /** A resolved class. */ @@ -4211,7 +4170,7 @@ export class Class extends TypedElement { /** Tests if this class is array-like. */ get isArrayLike(): bool { if (this.isBuiltinArray) return true; - var lengthField = this.lookupInSelf("length"); + var lengthField = this.getMember("length"); return lengthField !== null && ( lengthField.kind == ElementKind.FIELD || ( @@ -4361,30 +4320,20 @@ export class Class extends TypedElement { return null; } - /* @override */ - lookup(name: string): Element | null { - return this.parent.lookup(name); - } - /** Gets the method of the specified name, resolved with the given type arguments. */ getMethod(name: string, typeArguments: Type[] | null = null): Function | null { - var members = this.members; - if (members !== null && members.has(name)) { - let bound = changetype(members.get(name)); - if (bound.kind == ElementKind.FUNCTION_PROTOTYPE) { - return this.program.resolver.resolveFunction(bound, typeArguments); - } + var member = this.getMember(name); + if (member && member.kind == ElementKind.FUNCTION_PROTOTYPE) { + return this.program.resolver.resolveFunction(member, typeArguments); } return null; } /** Calculates the memory offset of the specified field. */ offsetof(fieldName: string): u32 { - var members = assert(this.members); - assert(members.has(fieldName)); - var field = members.get(fieldName); - assert(field.kind == ElementKind.FIELD); - return (field).memoryOffset; + var member = assert(this.getMember(fieldName)); + assert(member.kind == ElementKind.FIELD); + return (member).memoryOffset; } /** Creates a buffer suitable to hold a runtime instance of this class. */ @@ -4404,9 +4353,9 @@ export class Class extends TypedElement { /** Writes a field value to a buffer and returns the number of bytes written. */ writeField(name: string, value: T, buffer: Uint8Array, baseOffset: i32 = this.program.totalOverhead): i32 { - var element = this.lookupInSelf(name); - if (element !== null && element.kind == ElementKind.FIELD) { - let fieldInstance = element; + var member = this.getMember(name); + if (member !== null && member.kind == ElementKind.FIELD) { + let fieldInstance = member; let offset = baseOffset + fieldInstance.memoryOffset; let typeKind = fieldInstance.type.kind; switch (typeKind) { diff --git a/src/resolver.ts b/src/resolver.ts index 42c9a8cbdd..fa83a4a0d3 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -589,7 +589,7 @@ export class Resolver extends DiagnosticEmitter { /** How to proceed with eventual diagnostics. */ reportMode: ReportMode = ReportMode.REPORT ): Element | null { - var element = ctxElement.lookup(node.identifier.text); + var element = ctxElement.lookup(node.identifier.text, true); if (!element) { if (reportMode == ReportMode.REPORT) { this.error( @@ -602,7 +602,7 @@ export class Resolver extends DiagnosticEmitter { var prev = node; var next = node.next; while (next) { - if (!(element = element.lookupInSelf(next.identifier.text))) { + if (!(element = element.getMember(next.identifier.text))) { if (reportMode == ReportMode.REPORT) { this.error( DiagnosticCode.Property_0_does_not_exist_on_type_1, @@ -1337,8 +1337,8 @@ export class Resolver extends DiagnosticEmitter { break; } else if (!target.is(CommonFlags.GENERIC)) { // Inherit from 'Function' if not overridden, i.e. fn.call - let members = target.members; - if (!members || !members.has(propertyName)) { + let ownMember = target.getMember(propertyName); + if (!ownMember) { let functionInstance = this.resolveFunction(target, null, uniqueMap(), ReportMode.SWALLOW); if (functionInstance) { let wrapper = functionInstance.type.getClassOrWrapper(this.program); @@ -1357,9 +1357,8 @@ export class Resolver extends DiagnosticEmitter { case ElementKind.CLASS: case ElementKind.INTERFACE: { do { - let members = target.members; - if (members !== null && members.has(propertyName)) { - let member = assert(members.get(propertyName)); + let member = target.getMember(propertyName); + if (member) { if (member.kind == ElementKind.PROPERTY_PROTOTYPE) { let propertyInstance = this.resolveProperty(member, reportMode); if (!propertyInstance) return null; @@ -1406,11 +1405,11 @@ export class Resolver extends DiagnosticEmitter { break; } default: { // enums or other namespace-like elements - let members = target.members; - if (members !== null && members.has(propertyName)) { + let member = target.getMember(propertyName); + if (member) { this.currentThisExpression = targetNode; this.currentElementExpression = null; - return assert(members.get(propertyName)); // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE... + return member; // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE... } break; } @@ -3221,7 +3220,7 @@ export class Resolver extends DiagnosticEmitter { // Link _own_ constructor if present { - let ctorPrototype = instance.lookupInSelf(CommonNames.constructor); + let ctorPrototype = instance.getMember(CommonNames.constructor); if (ctorPrototype !== null && ctorPrototype.parent === instance) { assert(ctorPrototype.kind == ElementKind.FUNCTION_PROTOTYPE); let ctorInstance = this.resolveFunction( diff --git a/tests/compiler/resolve-localortype.json b/tests/compiler/resolve-localortype.json new file mode 100644 index 0000000000..1bdd02b1be --- /dev/null +++ b/tests/compiler/resolve-localortype.json @@ -0,0 +1,4 @@ +{ + "asc_flags": [ + ] +} diff --git a/tests/compiler/resolve-localortype.optimized.wat b/tests/compiler/resolve-localortype.optimized.wat new file mode 100644 index 0000000000..80df492dec --- /dev/null +++ b/tests/compiler/resolve-localortype.optimized.wat @@ -0,0 +1,36 @@ +(module + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17408)) + (memory $0 0) + (export "memory" (memory $0)) + (export "test" (func $export:resolve-localortype/test)) + (func $export:resolve-localortype/test (param $0 i32) (result i32) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1024 + i32.lt_s + if + i32.const 17440 + i32.const 17488 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.tee $1 + local.get $0 + i32.store + local.get $1 + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $0 + ) +) diff --git a/tests/compiler/resolve-localortype.ts b/tests/compiler/resolve-localortype.ts new file mode 100644 index 0000000000..abc2794454 --- /dev/null +++ b/tests/compiler/resolve-localortype.ts @@ -0,0 +1,6 @@ +// see: https://github.com/AssemblyScript/assemblyscript/issues/1684 + +function foo(s: T): T { return s; } +export function test(string: string): string { + return foo(string); // must not conflict +} diff --git a/tests/compiler/resolve-localortype.untouched.wat b/tests/compiler/resolve-localortype.untouched.wat new file mode 100644 index 0000000000..533c8dcac3 --- /dev/null +++ b/tests/compiler/resolve-localortype.untouched.wat @@ -0,0 +1,53 @@ +(module + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $none_=>_none (func)) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/memory/__data_end i32 (i32.const 8)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16392)) + (global $~lib/memory/__heap_base i32 (i32.const 16392)) + (memory $0 0) + (table $0 1 funcref) + (elem $0 (i32.const 1)) + (export "memory" (memory $0)) + (export "test" (func $export:resolve-localortype/test)) + (func $resolve-localortype/foo<~lib/string/String> (param $0 i32) (result i32) + local.get $0 + ) + (func $resolve-localortype/test (param $0 i32) (result i32) + local.get $0 + call $resolve-localortype/foo<~lib/string/String> + ) + (func $~stack_check + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__data_end + i32.lt_s + if + i32.const 16416 + i32.const 16464 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $export:resolve-localortype/test (param $0 i32) (result i32) + (local $1 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 + local.get $0 + i32.store + local.get $0 + call $resolve-localortype/test + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $1 + ) +)