Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement scoped type alias #2806

Merged
merged 1 commit into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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