From 9405e7e33d29a37ad866cd29bc193129a97bc400 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Wed, 22 Nov 2023 12:16:36 +0800 Subject: [PATCH] feat: implement scoped type alias --- src/compiler.ts | 52 ++-- src/flow.ts | 37 ++- src/program.ts | 6 +- src/resolver.ts | 76 ++++-- tests/compiler/typealias-errors.json | 7 + tests/compiler/typealias-errors.ts | 4 + tests/compiler/typealias.debug.wat | 370 ++++++++++++++++++++++++++- tests/compiler/typealias.release.wat | 282 +++++++++++++++++++- tests/compiler/typealias.ts | 26 ++ 9 files changed, 802 insertions(+), 58 deletions(-) create mode 100644 tests/compiler/typealias-errors.json create mode 100644 tests/compiler/typealias-errors.ts diff --git a/src/compiler.ts b/src/compiler.ts index 6508090626..3e721dd4b2 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -93,7 +93,8 @@ import { PropertyPrototype, IndexSignature, File, - mangleInternalName + mangleInternalName, + TypeDefinition } from "./program"; import { @@ -180,7 +181,8 @@ import { findDecorator, isTypeOmitted, - Source + Source, + TypeDeclaration } from "./ast"; import { @@ -1156,7 +1158,7 @@ export class Compiler extends DiagnosticEmitter { // Resolve type if annotated if (typeNode) { - let resolvedType = this.resolver.resolveType(typeNode, global.parent); // reports + let resolvedType = this.resolver.resolveType(typeNode, null, global.parent); // reports if (!resolvedType) { global.set(CommonFlags.Errored); pendingElements.delete(global); @@ -2238,13 +2240,7 @@ export class Compiler extends DiagnosticEmitter { break; } case NodeKind.TypeDeclaration: { - // TODO: integrate inner type declaration into flow - this.error( - DiagnosticCode.Not_implemented_0, - statement.range, - "Inner type alias" - ); - stmt = module.unreachable(); + stmt = this.compileTypeDeclaration(statement); break; } case NodeKind.Module: { @@ -2305,6 +2301,24 @@ export class Compiler extends DiagnosticEmitter { return this.module.flatten(stmts); } + private compileTypeDeclaration(statement: TypeDeclaration): ExpressionRef { + let flow = this.currentFlow; + let name = statement.name.text; + let existedTypeAlias = flow.lookupScopedTypeAlias(name); + if (existedTypeAlias) { + this.errorRelated( + DiagnosticCode.Duplicate_identifier_0, + statement.range, + existedTypeAlias.declaration.range, + name + ); + return this.module.unreachable(); + } + let element = new TypeDefinition(name, flow.sourceFunction, statement, DecoratorFlags.None); + flow.addScopedTypeAlias(name, element); + return this.module.nop(); + } + private compileBreakStatement( statement: BreakStatement ): ExpressionRef { @@ -2962,7 +2976,7 @@ export class Compiler extends DiagnosticEmitter { let initializerNode = declaration.initializer; if (typeNode) { type = resolver.resolveType( // reports - typeNode, + typeNode, flow, flow.sourceFunction, cloneMap(flow.contextualTypeArguments) ); @@ -3729,7 +3743,7 @@ export class Compiler extends DiagnosticEmitter { case AssertionKind.As: { let flow = this.currentFlow; let toType = this.resolver.resolveType( // reports - assert(expression.toType), + assert(expression.toType), flow, flow.sourceFunction, cloneMap(flow.contextualTypeArguments) ); @@ -6162,6 +6176,7 @@ export class Compiler extends DiagnosticEmitter { typeArguments = this.resolver.resolveTypeArguments( assert(typeParameterNodes), typeArgumentNodes, + this.currentFlow, this.currentFlow.sourceFunction.parent, cloneMap(this.currentFlow.contextualTypeArguments), // don't update expression @@ -7085,7 +7100,7 @@ export class Compiler extends DiagnosticEmitter { let parameterNode = parameterNodes[i]; if (!isTypeOmitted(parameterNode.type)) { let resolvedType = this.resolver.resolveType( - parameterNode.type, + parameterNode.type, flow, sourceFunction.parent, contextualTypeArguments ); @@ -7105,7 +7120,7 @@ export class Compiler extends DiagnosticEmitter { let returnType = contextualSignature.returnType; if (!isTypeOmitted(signatureNode.returnType)) { let resolvedType = this.resolver.resolveType( - signatureNode.returnType, + signatureNode.returnType, flow, sourceFunction.parent, contextualTypeArguments ); @@ -7135,7 +7150,7 @@ export class Compiler extends DiagnosticEmitter { return module.unreachable(); } let resolvedType = this.resolver.resolveType( - thisTypeNode, + thisTypeNode, flow, sourceFunction.parent, contextualTypeArguments ); @@ -7522,7 +7537,7 @@ export class Compiler extends DiagnosticEmitter { if (isType.kind == NodeKind.NamedType) { let namedType = isType; if (!(namedType.isNullable || namedType.hasTypeArguments)) { - let element = this.resolver.resolveTypeName(namedType.name, flow.sourceFunction, ReportMode.Swallow); + let element = this.resolver.resolveTypeName(namedType.name, flow, flow.sourceFunction, ReportMode.Swallow); if (element && element.kind == ElementKind.ClassPrototype) { let prototype = element; if (prototype.is(CommonFlags.Generic)) { @@ -7534,7 +7549,7 @@ export class Compiler extends DiagnosticEmitter { // Fall back to `instanceof TYPE` let expectedType = this.resolver.resolveType( - expression.isType, + expression.isType, flow, flow.sourceFunction, cloneMap(flow.contextualTypeArguments) ); @@ -8686,7 +8701,7 @@ export class Compiler extends DiagnosticEmitter { let flow = this.currentFlow; // obtain the class being instantiated - let target = this.resolver.resolveTypeName(expression.typeName, flow.sourceFunction); + let target = this.resolver.resolveTypeName(expression.typeName, flow, flow.sourceFunction); if (!target) return module.unreachable(); if (target.kind != ElementKind.ClassPrototype) { this.error( @@ -8722,6 +8737,7 @@ export class Compiler extends DiagnosticEmitter { classInstance = this.resolver.resolveClassInclTypeArguments( classPrototype, typeArguments, + flow, flow.sourceFunction.parent, // relative to caller cloneMap(flow.contextualTypeArguments), expression diff --git a/src/flow.ts b/src/flow.ts index 3f4838ab3d..d481a61fc9 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -31,7 +31,8 @@ import { TypedElement, mangleInternalName, Property, - PropertyPrototype + PropertyPrototype, + TypeDefinition } from "./program"; import { @@ -250,6 +251,8 @@ export class Flow { breakLabel: string | null = null; /** Scoped local variables. */ scopedLocals: Map | null = null; + /** Scoped type alias. */ + scopedTypeAlias: Map | null = null; /** Local flags. */ localFlags: LocalFlags[] = []; /** Field flags on `this`. Constructors only. */ @@ -405,6 +408,38 @@ export class Flow { falseFlows.set(condExpr, falseFlow); } + addScopedTypeAlias(name: string, definition: TypeDefinition): void { + let scopedTypeAlias = this.scopedTypeAlias; + if (!scopedTypeAlias) this.scopedTypeAlias = scopedTypeAlias = new Map(); + scopedTypeAlias.set(name, definition); + } + + lookupScopedTypeAlias(name: string): TypeDefinition | null { + let current: Flow | null = this; + do { + let scopedTypeAlias = current.scopedTypeAlias; + if (scopedTypeAlias && scopedTypeAlias.has(name)) { + return assert(scopedTypeAlias.get(name)); + } + current = current.parent; + } while (current); + return null; + } + + lookupTypeAlias(name: string): TypeDefinition | null { + let definition: TypeDefinition | null = null; + if (definition = this.lookupScopedTypeAlias(name)) return definition; + + let sourceParent = this.sourceFunction.parent; + if (sourceParent.kind == ElementKind.Function) { + // lookup parent function. + let parentFunction = sourceParent; + return parentFunction.flow.lookupTypeAlias(name); + } + + return null; + } + /** Gets a free temporary local of the specified type. */ getTempLocal(type: Type): Local { let local = this.targetFunction.addLocal(type); diff --git a/src/program.ts b/src/program.ts index febeb1227c..3f9044e2d9 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1308,7 +1308,7 @@ export class Program extends DiagnosticEmitter { for (let i = 0, k = queuedExtends.length; i < k; ++i) { let thisPrototype = queuedExtends[i]; let extendsNode = assert(thisPrototype.extendsNode); // must be present if in queuedExtends - let baseElement = resolver.resolveTypeName(extendsNode.name, thisPrototype.parent); + let baseElement = resolver.resolveTypeName(extendsNode.name, null, thisPrototype.parent); if (!baseElement) continue; if (thisPrototype.kind == ElementKind.ClassPrototype) { if (baseElement.kind == ElementKind.ClassPrototype) { @@ -1405,7 +1405,7 @@ export class Program extends DiagnosticEmitter { let implementsNodes = assert(thisPrototype.implementsNodes); // must be present if in queuedImplements for (let j = 0, l = implementsNodes.length; j < l; ++j) { let implementsNode = implementsNodes[j]; - let interfaceElement = resolver.resolveTypeName(implementsNode.name, thisPrototype.parent); + let interfaceElement = resolver.resolveTypeName(implementsNode.name, null, thisPrototype.parent); if (!interfaceElement) continue; if (interfaceElement.kind == ElementKind.InterfacePrototype) { let interfacePrototype = interfaceElement; @@ -3383,7 +3383,7 @@ export class TypeDefinition extends TypedElement { constructor( /** Simple name. */ name: string, - /** Parent element, usually a file or namespace. */ + /** Parent element. */ parent: Element, /** Declaration reference. */ declaration: TypeDeclaration, diff --git a/src/resolver.ts b/src/resolver.ts index 349cd47d5e..d0837282e9 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -142,6 +142,8 @@ export class Resolver extends DiagnosticEmitter { resolveType( /** The type to resolve. */ node: TypeNode, + /** The flow */ + flow: Flow | null, /** Contextual element. */ ctxElement: Element, /** Contextual types, i.e. `T`. */ @@ -160,21 +162,11 @@ export class Resolver extends DiagnosticEmitter { let resolved: Type | null = null; switch (node.kind) { case NodeKind.NamedType: { - resolved = this.resolveNamedType( - node, - ctxElement, - ctxTypes, - reportMode - ); + resolved = this.resolveNamedType(node, flow, ctxElement, ctxTypes, reportMode); break; } case NodeKind.FunctionType: { - resolved = this.resolveFunctionType( - node, - ctxElement, - ctxTypes, - reportMode - ); + resolved = this.resolveFunctionType(node, flow, ctxElement, ctxTypes, reportMode); break; } default: assert(false); @@ -187,6 +179,8 @@ export class Resolver extends DiagnosticEmitter { private resolveNamedType( /** The type to resolve. */ node: NamedTypeNode, + /** The flow */ + flow: Flow | null, /** Contextual element. */ ctxElement: Element, /** Contextual types, i.e. `T`. */ @@ -225,7 +219,7 @@ export class Resolver extends DiagnosticEmitter { } // Look up in context - let element = this.resolveTypeName(nameNode, ctxElement, reportMode); + let element = this.resolveTypeName(nameNode, flow, ctxElement, reportMode); if (!element) return null; // Use shadow type if present (i.e. namespace sharing a type) @@ -264,6 +258,7 @@ export class Resolver extends DiagnosticEmitter { let instance = this.resolveClassInclTypeArguments( element, typeArgumentNodes, + flow, ctxElement, cloneMap(ctxTypes), // don't inherit node, @@ -318,6 +313,7 @@ export class Resolver extends DiagnosticEmitter { typeArguments = this.resolveTypeArguments( typeParameterNodes, typeArgumentNodes, + flow, ctxElement, ctxTypes = cloneMap(ctxTypes), // update node, @@ -332,6 +328,7 @@ export class Resolver extends DiagnosticEmitter { } let type = this.resolveType( typeDefinition.typeNode, + flow, element, ctxTypes, reportMode @@ -361,6 +358,8 @@ export class Resolver extends DiagnosticEmitter { private resolveFunctionType( /** The type to resolve. */ node: FunctionTypeNode, + /** The flow */ + flow: Flow | null, /** Contextual element. */ ctxElement: Element, /** Contextual types, i.e. `T`. */ @@ -373,6 +372,7 @@ export class Resolver extends DiagnosticEmitter { if (explicitThisType) { thisType = this.resolveType( explicitThisType, + flow, ctxElement, ctxTypes, reportMode @@ -409,6 +409,7 @@ export class Resolver extends DiagnosticEmitter { } let parameterType = this.resolveType( parameterTypeNode, + flow, ctxElement, ctxTypes, reportMode @@ -429,6 +430,7 @@ export class Resolver extends DiagnosticEmitter { } else { returnType = this.resolveType( returnTypeNode, + flow, ctxElement, ctxTypes, reportMode @@ -451,7 +453,7 @@ export class Resolver extends DiagnosticEmitter { ): Type | null { const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode); + let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); if (!typeArgument) return null; switch (typeArgument.kind) { case TypeKind.I8: @@ -486,7 +488,7 @@ export class Resolver extends DiagnosticEmitter { ): Type | null { const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode); + let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); if (!typeArgument) return null; let classReference = typeArgument.classReference; if (!classReference) { @@ -530,7 +532,7 @@ export class Resolver extends DiagnosticEmitter { ): Type | null { const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode); + let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); if (!typeArgument) return null; let classReference = typeArgument.getClassOrWrapper(this.program); if (classReference) { @@ -558,7 +560,7 @@ export class Resolver extends DiagnosticEmitter { ): Type | null { const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode); + let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); if (!typeArgument) return null; let signatureReference = typeArgument.getSignature(); if (signatureReference) return signatureReference.returnType; @@ -583,7 +585,7 @@ export class Resolver extends DiagnosticEmitter { ): Type | null { const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode); if (!typeArgumentNode) return null; - let typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode); + let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode); if (!typeArgument) return null; if (!typeArgument.isNullableReference) return typeArgument; return typeArgument.nonNullableType; @@ -593,12 +595,16 @@ export class Resolver extends DiagnosticEmitter { resolveTypeName( /** The type name to resolve. */ node: TypeName, + /** The flow */ + flow: Flow | null, /** Contextual element. */ ctxElement: Element, /** How to proceed with eventual diagnostics. */ reportMode: ReportMode = ReportMode.Report ): Element | null { - let element = ctxElement.lookup(node.identifier.text, true); + let element: Element | null = null; + if (flow) element = flow.lookupTypeAlias(node.identifier.text); + if (!element) element = ctxElement.lookup(node.identifier.text, true); if (!element) { if (reportMode == ReportMode.Report) { this.error( @@ -632,6 +638,8 @@ export class Resolver extends DiagnosticEmitter { typeParameters: TypeParameterNode[], /** Type argument nodes provided. */ typeArgumentNodes: TypeNode[] | null, + /** Flow */ + flow: Flow | null, /** Contextual element. */ ctxElement: Element, /** Contextual types, i.e. `T`. Updated in place with the new set of contextual types. */ @@ -641,9 +649,8 @@ export class Resolver extends DiagnosticEmitter { /** How to proceed with eventual diagnostics. */ reportMode: ReportMode = ReportMode.Report ): Type[] | null { - var - minParameterCount = 0, - maxParameterCount = 0; + let minParameterCount = 0; + let maxParameterCount = 0; for (let i = 0, k = typeParameters.length; i < k; ++i) { if (!typeParameters[i].defaultType) ++minParameterCount; ++maxParameterCount; @@ -672,12 +679,14 @@ export class Resolver extends DiagnosticEmitter { let type = i < argumentCount ? this.resolveType( // reports typeArgumentNodes![i], + flow, ctxElement, oldCtxTypes, // update reportMode ) : this.resolveType( // reports assert(typeParameters[i].defaultType), + flow, ctxElement, cloneMap(ctxTypes), // don't update reportMode @@ -796,6 +805,7 @@ export class Resolver extends DiagnosticEmitter { } let resolvedDefaultType = this.resolveType( defaultType, + null, prototype, defaultTypeContextualTypeArguments, reportMode @@ -846,7 +856,7 @@ export class Resolver extends DiagnosticEmitter { if (typeArgumentNodes && typeArgumentNodes.length > 0) { // foo(bar: Array) let classReference = type.classReference; if (classReference) { - let classPrototype = this.resolveTypeName(namedTypeNode.name, ctxElement); + let classPrototype = this.resolveTypeName(namedTypeNode.name, null, ctxElement); if (!classPrototype || classPrototype.kind != ElementKind.ClassPrototype) return; if (classReference.prototype == classPrototype) { let typeArguments = classReference.typeArguments; @@ -1306,7 +1316,7 @@ export class Resolver extends DiagnosticEmitter { if (global.is(CommonFlags.Resolved)) return true; let typeNode = global.typeNode; let type = typeNode - ? this.resolveType(typeNode, global.parent, null, reportMode) + ? this.resolveType(typeNode, null, global.parent, null, reportMode) : this.resolveExpression( assert(global.initializerNode), global.file.startFunction.flow, @@ -1424,7 +1434,7 @@ export class Resolver extends DiagnosticEmitter { let shadowType = target.shadowType; if (shadowType) { if (!shadowType.is(CommonFlags.Resolved)) { - let resolvedType = this.resolveType(shadowType.typeNode, shadowType.parent, null, reportMode); + let resolvedType = this.resolveType(shadowType.typeNode, null, shadowType.parent, null, reportMode); if (resolvedType) shadowType.setType(resolvedType); } let classReference = shadowType.type.classReference; @@ -1708,6 +1718,7 @@ export class Resolver extends DiagnosticEmitter { case AssertionKind.Prefix: { let type = this.resolveType( assert(node.toType), // must be set if not NONNULL + null, ctxFlow.sourceFunction, ctxFlow.contextualTypeArguments, reportMode @@ -1764,6 +1775,7 @@ export class Resolver extends DiagnosticEmitter { case AssertionKind.Prefix: { return this.resolveType( assert(node.toType), + null, ctxFlow.sourceFunction, ctxFlow.contextualTypeArguments, reportMode @@ -2659,12 +2671,13 @@ export class Resolver extends DiagnosticEmitter { /** How to proceed with eventual diagnostics. */ reportMode: ReportMode = ReportMode.Report ): Element | null { - let element = this.resolveTypeName(node.typeName, ctxFlow.sourceFunction, reportMode); + let element = this.resolveTypeName(node.typeName, ctxFlow, ctxFlow.sourceFunction, reportMode); if (!element) return null; if (element.kind == ElementKind.ClassPrototype) { return this.resolveClassInclTypeArguments( element, node.typeArguments, + ctxFlow, ctxFlow.sourceFunction, cloneMap(ctxFlow.contextualTypeArguments), node, @@ -2744,7 +2757,7 @@ export class Resolver extends DiagnosticEmitter { const declaration = node.declaration; const signature = declaration.signature; const body = declaration.body; - let functionType = this.resolveType(signature, ctxFlow.sourceFunction, ctxFlow.contextualTypeArguments, reportMode); + let functionType = this.resolveType(signature, null, ctxFlow.sourceFunction, ctxFlow.contextualTypeArguments, reportMode); if ( functionType && declaration.arrowKind != ArrowKind.None && @@ -2843,6 +2856,7 @@ export class Resolver extends DiagnosticEmitter { if (explicitThisType) { thisType = this.resolveType( explicitThisType, + null, prototype.parent, // relative to function ctxTypes, reportMode @@ -2876,6 +2890,7 @@ export class Resolver extends DiagnosticEmitter { } let parameterType = this.resolveType( typeNode, + null, prototype.parent, // relative to function ctxTypes, reportMode @@ -2912,6 +2927,7 @@ export class Resolver extends DiagnosticEmitter { } let type = this.resolveType( typeNode, + null, prototype.parent, // relative to function ctxTypes, reportMode @@ -3026,6 +3042,7 @@ export class Resolver extends DiagnosticEmitter { resolvedTypeArguments = this.resolveTypeArguments( // reports assert(prototype.typeParameterNodes), typeArgumentNodes, + null, ctxElement, ctxTypes, // update reportNode, @@ -3174,6 +3191,7 @@ export class Resolver extends DiagnosticEmitter { let base = this.resolveClassInclTypeArguments( basePrototype, extendsNode.typeArguments, + null, prototype.parent, // relative to derived class cloneMap(ctxTypes), // don't inherit extendsNode, @@ -3214,6 +3232,7 @@ export class Resolver extends DiagnosticEmitter { let iface = this.resolveClassInclTypeArguments( interfacePrototype, implementsNode.typeArguments, + null, prototype.parent, cloneMap(ctxTypes), implementsNode, @@ -3610,6 +3629,8 @@ export class Resolver extends DiagnosticEmitter { prototype: ClassPrototype, /** Type arguments provided to be resolved. */ typeArgumentNodes: TypeNode[] | null, + /** Flow of {@link typeArgumentNodes} */ + flow: Flow | null, /** Contextual element. */ ctxElement: Element, /** Contextual types, i.e. `T`. */ @@ -3626,6 +3647,7 @@ export class Resolver extends DiagnosticEmitter { resolvedTypeArguments = this.resolveTypeArguments( // reports assert(prototype.typeParameterNodes), // must be present if generic typeArgumentNodes, + flow, ctxElement, ctxTypes, // update reportNode, diff --git a/tests/compiler/typealias-errors.json b/tests/compiler/typealias-errors.json new file mode 100644 index 0000000000..91a3ef17ac --- /dev/null +++ b/tests/compiler/typealias-errors.json @@ -0,0 +1,7 @@ +{ + "asc_flags": [ + ], + "stderr": [ + "TS2300: Duplicate identifier 'A'." + ] +} diff --git a/tests/compiler/typealias-errors.ts b/tests/compiler/typealias-errors.ts new file mode 100644 index 0000000000..988e058572 --- /dev/null +++ b/tests/compiler/typealias-errors.ts @@ -0,0 +1,4 @@ +export function DuplicateTypeAlias(): void { + type A = i32; + type A = i32; +} diff --git a/tests/compiler/typealias.debug.wat b/tests/compiler/typealias.debug.wat index 0e5a48657b..9c6d493f68 100644 --- a/tests/compiler/typealias.debug.wat +++ b/tests/compiler/typealias.debug.wat @@ -1,15 +1,371 @@ (module - (type $0 (func (param i32) (result i32))) - (global $~lib/memory/__data_end i32 (i32.const 8)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 32776)) - (global $~lib/memory/__heap_base i32 (i32.const 32776)) - (memory $0 0) - (table $0 1 1 funcref) - (elem $0 (i32.const 1)) + (type $0 (func)) + (type $1 (func (param i32) (result i32))) + (type $2 (func (param i32 i32 i32 i32 i32) (result i32))) + (type $3 (func (param i32 i32 i32 i32))) + (type $4 (func (param i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) + (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) + (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) + (global $~lib/native/ASC_SHRINK_LEVEL i32 (i32.const 0)) + (global $~argumentsLength (mut i32) (i32.const 0)) + (global $~lib/memory/__data_end i32 (i32.const 156)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 32924)) + (global $~lib/memory/__heap_base i32 (i32.const 32924)) + (memory $0 1) + (data $0 (i32.const 12) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\06\00\00\00i\006\004\00\00\00\00\00\00\00") + (data $1 (i32.const 44) ",\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\18\00\00\00t\00y\00p\00e\00a\00l\00i\00a\00s\00.\00t\00s\00\00\00\00\00") + (data $2 (i32.const 92) "\1c\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\06\00\00\00f\006\004\00\00\00\00\00\00\00") + (data $3 (i32.const 124) "\1c\00\00\00\00\00\00\00\00\00\00\00\04\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") + (table $0 2 2 funcref) + (elem $0 (i32.const 1) $typealias/outer_function~inner_function) (export "alias" (func $typealias/alias)) (export "memory" (memory $0)) + (start $~start) + (func $~lib/rt/common/OBJECT#get:rtSize (param $this i32) (result i32) + local.get $this + i32.load offset=16 + ) + (func $~lib/string/String#get:length (param $this i32) (result i32) + local.get $this + i32.const 20 + i32.sub + call $~lib/rt/common/OBJECT#get:rtSize + i32.const 1 + i32.shr_u + return + ) + (func $~lib/util/string/compareImpl (param $str1 i32) (param $index1 i32) (param $str2 i32) (param $index2 i32) (param $len i32) (result i32) + (local $ptr1 i32) + (local $ptr2 i32) + (local $7 i32) + (local $a i32) + (local $b i32) + local.get $str1 + local.get $index1 + i32.const 1 + i32.shl + i32.add + local.set $ptr1 + local.get $str2 + local.get $index2 + i32.const 1 + i32.shl + i32.add + local.set $ptr2 + i32.const 0 + i32.const 2 + i32.lt_s + drop + local.get $len + i32.const 4 + i32.ge_u + if (result i32) + local.get $ptr1 + i32.const 7 + i32.and + local.get $ptr2 + i32.const 7 + i32.and + i32.or + i32.eqz + else + i32.const 0 + end + if + block $do-break|0 + loop $do-loop|0 + local.get $ptr1 + i64.load + local.get $ptr2 + i64.load + i64.ne + if + br $do-break|0 + end + local.get $ptr1 + i32.const 8 + i32.add + local.set $ptr1 + local.get $ptr2 + i32.const 8 + i32.add + local.set $ptr2 + local.get $len + i32.const 4 + i32.sub + local.set $len + local.get $len + i32.const 4 + i32.ge_u + br_if $do-loop|0 + end + end + end + loop $while-continue|1 + local.get $len + local.tee $7 + i32.const 1 + i32.sub + local.set $len + local.get $7 + if + local.get $ptr1 + i32.load16_u + local.set $a + local.get $ptr2 + i32.load16_u + local.set $b + local.get $a + local.get $b + i32.ne + if + local.get $a + local.get $b + i32.sub + return + end + local.get $ptr1 + i32.const 2 + i32.add + local.set $ptr1 + local.get $ptr2 + i32.const 2 + i32.add + local.set $ptr2 + br $while-continue|1 + end + end + i32.const 0 + return + ) + (func $typealias/generic_type_alias + i32.const 5 + i32.const 5 + i32.eq + drop + ) + (func $typealias/alias_in_type_arguments + i32.const 6 + i32.const 6 + i32.eq + drop + ) + (func $start:typealias + call $typealias/outer_function + call $typealias/generic_type_alias + call $typealias/alias_in_type_arguments + ) (func $typealias/alias (param $a i32) (result i32) local.get $a return ) + (func $~start + call $start:typealias + ) + (func $~stack_check + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__data_end + i32.lt_s + if + i32.const 32944 + i32.const 32992 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $~lib/string/String.__eq (param $left i32) (param $right i32) (result i32) + (local $leftLength i32) + (local $3 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + local.get $left + local.get $right + i32.eq + if + i32.const 1 + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + end + local.get $left + i32.const 0 + i32.eq + if (result i32) + i32.const 1 + else + local.get $right + i32.const 0 + i32.eq + end + if + i32.const 0 + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + end + local.get $left + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store + local.get $3 + call $~lib/string/String#get:length + local.set $leftLength + local.get $leftLength + local.get $right + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store + local.get $3 + call $~lib/string/String#get:length + i32.ne + if + i32.const 0 + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + end + local.get $left + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store + local.get $3 + i32.const 0 + local.get $right + local.set $3 + global.get $~lib/memory/__stack_pointer + local.get $3 + i32.store offset=4 + local.get $3 + i32.const 0 + local.get $leftLength + call $~lib/util/string/compareImpl + i32.eqz + local.set $3 + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + return + ) + (func $typealias/outer_function~inner_function + (local $alias f64) + (local $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + f64.const 1 + local.set $alias + i32.const 112 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + i32.const 112 + local.set $1 + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store offset=4 + local.get $1 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 64 + i32.const 17 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $typealias/outer_function + (local $alias i64) + (local $inner_function i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 12 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i64.const 0 + i64.store + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store offset=8 + i64.const 1 + local.set $alias + i32.const 32 + local.set $2 + global.get $~lib/memory/__stack_pointer + local.get $2 + i32.store + local.get $2 + i32.const 32 + local.set $2 + global.get $~lib/memory/__stack_pointer + local.get $2 + i32.store offset=4 + local.get $2 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 64 + i32.const 13 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 144 + local.tee $inner_function + i32.store offset=8 + local.get $inner_function + drop + i32.const 0 + global.set $~argumentsLength + local.get $inner_function + i32.load + call_indirect (type $0) + global.get $~lib/memory/__stack_pointer + i32.const 12 + i32.add + global.set $~lib/memory/__stack_pointer + ) ) diff --git a/tests/compiler/typealias.release.wat b/tests/compiler/typealias.release.wat index 1056cb7250..3b7cdfe076 100644 --- a/tests/compiler/typealias.release.wat +++ b/tests/compiler/typealias.release.wat @@ -1,9 +1,287 @@ (module - (type $0 (func (param i32) (result i32))) - (memory $0 0) + (type $0 (func)) + (type $1 (func (param i32 i32 i32 i32))) + (type $2 (func (param i32) (result i32))) + (type $3 (func (param i32 i32) (result i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 33948)) + (memory $0 1) + (data $0 (i32.const 1036) "\1c") + (data $0.1 (i32.const 1048) "\02\00\00\00\06\00\00\00i\006\004") + (data $1 (i32.const 1068) ",") + (data $1.1 (i32.const 1080) "\02\00\00\00\18\00\00\00t\00y\00p\00e\00a\00l\00i\00a\00s\00.\00t\00s") + (data $2 (i32.const 1116) "\1c") + (data $2.1 (i32.const 1128) "\02\00\00\00\06\00\00\00f\006\004") + (data $3 (i32.const 1148) "\1c") + (data $3.1 (i32.const 1160) "\04\00\00\00\08\00\00\00\01") + (table $0 2 2 funcref) + (elem $0 (i32.const 1) $typealias/outer_function~inner_function) (export "alias" (func $typealias/alias)) (export "memory" (memory $0)) + (start $~start) (func $typealias/alias (param $0 i32) (result i32) local.get $0 ) + (func $~start + (local $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 12 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1180 + i32.lt_s + if + i32.const 33968 + i32.const 34016 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.tee $0 + i64.const 0 + i64.store + local.get $0 + i32.const 0 + i32.store offset=8 + local.get $0 + i32.const 1056 + i32.store + local.get $0 + i32.const 1056 + i32.store offset=4 + i32.const 1056 + i32.const 1056 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 13 + i32.const 3 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 1168 + i32.store offset=8 + i32.const 1168 + i32.load + call_indirect (type $0) + global.get $~lib/memory/__stack_pointer + i32.const 12 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $~lib/string/String.__eq (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1180 + i32.lt_s + if + i32.const 33968 + i32.const 34016 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.tee $2 + i64.const 0 + i64.store + local.get $0 + local.get $1 + i32.eq + if + local.get $2 + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + i32.const 1 + return + end + block $folding-inner0 + local.get $1 + i32.eqz + local.get $0 + i32.eqz + i32.or + br_if $folding-inner0 + global.get $~lib/memory/__stack_pointer + local.tee $4 + local.tee $2 + local.get $0 + i32.store + local.get $0 + i32.const 20 + i32.sub + i32.load offset=16 + i32.const 1 + i32.shr_u + local.set $3 + local.get $2 + local.get $1 + i32.store + local.get $3 + local.get $1 + i32.const 20 + i32.sub + i32.load offset=16 + i32.const 1 + i32.shr_u + i32.ne + br_if $folding-inner0 + local.get $4 + local.get $0 + i32.store + local.get $0 + local.set $2 + local.get $4 + local.get $1 + i32.store offset=4 + local.get $3 + local.tee $0 + i32.const 4 + i32.ge_u + if (result i32) + local.get $2 + i32.const 7 + i32.and + local.get $1 + i32.const 7 + i32.and + i32.or + else + i32.const 1 + end + i32.eqz + if + loop $do-loop|0 + local.get $2 + i64.load + local.get $1 + i64.load + i64.eq + if + local.get $2 + i32.const 8 + i32.add + local.set $2 + local.get $1 + i32.const 8 + i32.add + local.set $1 + local.get $0 + i32.const 4 + i32.sub + local.tee $0 + i32.const 4 + i32.ge_u + br_if $do-loop|0 + end + end + end + block $__inlined_func$~lib/util/string/compareImpl$5 + loop $while-continue|1 + local.get $0 + local.tee $3 + i32.const 1 + i32.sub + local.set $0 + local.get $3 + if + local.get $2 + i32.load16_u + local.tee $5 + local.get $1 + i32.load16_u + local.tee $4 + i32.sub + local.set $3 + local.get $4 + local.get $5 + i32.ne + br_if $__inlined_func$~lib/util/string/compareImpl$5 + local.get $2 + i32.const 2 + i32.add + local.set $2 + local.get $1 + i32.const 2 + i32.add + local.set $1 + br $while-continue|1 + end + end + i32.const 0 + local.set $3 + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + local.get $3 + i32.eqz + return + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + i32.const 0 + ) + (func $typealias/outer_function~inner_function + (local $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1180 + i32.lt_s + if + i32.const 33968 + i32.const 34016 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.tee $0 + i64.const 0 + i64.store + local.get $0 + i32.const 1136 + i32.store + local.get $0 + i32.const 1136 + i32.store offset=4 + i32.const 1136 + i32.const 1136 + call $~lib/string/String.__eq + i32.eqz + if + i32.const 0 + i32.const 1088 + i32.const 17 + i32.const 5 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + i32.const 8 + i32.add + global.set $~lib/memory/__stack_pointer + ) ) diff --git a/tests/compiler/typealias.ts b/tests/compiler/typealias.ts index b6b1449047..a28013ffa6 100644 --- a/tests/compiler/typealias.ts +++ b/tests/compiler/typealias.ts @@ -6,3 +6,29 @@ export type alias = i32; export function alias(a: alias): alias { return a; } + +function outer_function(): void { + type alias = i64; + let alias: alias = 1; + assert(nameof(alias) == nameof()); + function inner_function(): void { + type alias = f64; + let alias: alias = 1; + assert(nameof(alias) == nameof()); + } + inner_function(); +} +outer_function(); + +function generic_type_alias(): void { + type V = Array; + assert(idof>() == idof>()); +} +generic_type_alias(); + + +function alias_in_type_arguments(): void { + type alias = i64; + assert(idof>() == idof>()); +} +alias_in_type_arguments();