Skip to content

Commit

Permalink
feat: implement scoped type alias (#2806)
Browse files Browse the repository at this point in the history
  • Loading branch information
HerrCai0907 authored Nov 23, 2023
1 parent 153def6 commit 1e0466e
Show file tree
Hide file tree
Showing 9 changed files with 802 additions and 58 deletions.
52 changes: 34 additions & 18 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ import {
PropertyPrototype,
IndexSignature,
File,
mangleInternalName
mangleInternalName,
TypeDefinition
} from "./program";

import {
Expand Down Expand Up @@ -180,7 +181,8 @@ import {

findDecorator,
isTypeOmitted,
Source
Source,
TypeDeclaration
} from "./ast";

import {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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(<TypeDeclaration>statement);
break;
}
case NodeKind.Module: {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
);
Expand Down Expand Up @@ -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)
);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
);
Expand All @@ -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
);
Expand Down Expand Up @@ -7135,7 +7150,7 @@ export class Compiler extends DiagnosticEmitter {
return module.unreachable();
}
let resolvedType = this.resolver.resolveType(
thisTypeNode,
thisTypeNode, flow,
sourceFunction.parent,
contextualTypeArguments
);
Expand Down Expand Up @@ -7522,7 +7537,7 @@ export class Compiler extends DiagnosticEmitter {
if (isType.kind == NodeKind.NamedType) {
let namedType = <NamedTypeNode>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 = <ClassPrototype>element;
if (prototype.is(CommonFlags.Generic)) {
Expand All @@ -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)
);
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
37 changes: 36 additions & 1 deletion src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ import {
TypedElement,
mangleInternalName,
Property,
PropertyPrototype
PropertyPrototype,
TypeDefinition
} from "./program";

import {
Expand Down Expand Up @@ -250,6 +251,8 @@ export class Flow {
breakLabel: string | null = null;
/** Scoped local variables. */
scopedLocals: Map<string,Local> | null = null;
/** Scoped type alias. */
scopedTypeAlias: Map<string,TypeDefinition> | null = null;
/** Local flags. */
localFlags: LocalFlags[] = [];
/** Field flags on `this`. Constructors only. */
Expand Down Expand Up @@ -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 = <Function>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);
Expand Down
6 changes: 3 additions & 3 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 = <InterfacePrototype>interfaceElement;
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 1e0466e

Please sign in to comment.