Skip to content
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
161 changes: 158 additions & 3 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ import {
NodeKind,
LiteralExpression,
ArrayLiteralExpression,
IdentifierExpression
IdentifierExpression,
NamedTypeNode
} from "./ast";

import {
Expand Down Expand Up @@ -85,7 +86,9 @@ import {
DecoratorFlags,
Class,
PropertyPrototype,
VariableLikeElement
VariableLikeElement,
Element,
OperatorKind
} from "./program";

import {
Expand All @@ -94,11 +97,13 @@ import {
} from "./flow";

import {
ReportMode
ReportMode,
Resolver
} from "./resolver";

import {
CommonFlags,
CommonNames,
Feature,
featureToString,
TypeinfoFlags
Expand Down Expand Up @@ -768,6 +773,17 @@ export namespace BuiltinNames {
export const Object = "~lib/object/Object";
}

/** Builtin types context. */
export class BuiltinTypesContext {
constructor(
public resolver: Resolver,
public node: NamedTypeNode,
public ctxElement: Element,
public ctxTypes: Map<string, Type> | null,
public reportMode: ReportMode
) {}
}

/** Builtin variable compilation context. */
export class BuiltinVariableContext {
constructor(
Expand Down Expand Up @@ -804,13 +820,152 @@ export class BuiltinFunctionContext {
) {}
}

/** Builtin types map. */
export const builtinTypes = new Map<string, (ctx: BuiltinTypesContext) => Type | null>();

/** Builtin functions map. */
export const builtinFunctions = new Map<string, (ctx: BuiltinFunctionContext) => ExpressionRef>();

/** Builtin variables map. */
export const builtinVariables_onCompile = new Map<string, (ctx: BuiltinVariableContext) => void>();
export const builtinVariables_onAccess = new Map<string, (ctx: BuiltinVariableContext) => ExpressionRef>();

// === Builtin Types ==========================================================================
function builtin_resolveNativeType(ctx: BuiltinTypesContext): Type | null {
let resolver = ctx.resolver;
let node = ctx.node;
let ctxElement = ctx.ctxElement;
let ctxTypes = ctx.ctxTypes;
let reportMode = ctx.reportMode;
const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
switch (typeArgument.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32: return Type.i32;
case TypeKind.Isize: if (!resolver.program.options.isWasm64) return Type.i32;
case TypeKind.I64: return Type.i64;
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.Bool: return Type.u32;
case TypeKind.Usize: if (!resolver.program.options.isWasm64) return Type.u32;
case TypeKind.U64: return Type.u64;
case TypeKind.F32: return Type.f32;
case TypeKind.F64: return Type.f64;
case TypeKind.V128: return Type.v128;
case TypeKind.Void: return Type.void;
default: assert(false);
}
return null;
}
builtinTypes.set(CommonNames.native, builtin_resolveNativeType);

function builtin_resolveIndexOfType(ctx: BuiltinTypesContext): Type | null {
let resolver = ctx.resolver;
let node = ctx.node;
let ctxElement = ctx.ctxElement;
let ctxTypes = ctx.ctxTypes;
let reportMode = ctx.reportMode;
const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
let classReference = typeArgument.classReference;
if (!classReference) {
if (reportMode == ReportMode.Report) {
resolver.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}
let overload = classReference.lookupOverload(OperatorKind.IndexedGet);
if (overload) {
let parameterTypes = overload.signature.parameterTypes;
if (overload.is(CommonFlags.Static)) {
assert(parameterTypes.length == 2);
return parameterTypes[1];
} else {
assert(parameterTypes.length == 1);
return parameterTypes[0];
}
}
if (reportMode == ReportMode.Report) {
resolver.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}
builtinTypes.set(CommonNames.indexof, builtin_resolveIndexOfType);

function builtin_resolveValueOfType(ctx: BuiltinTypesContext): Type | null {
let resolver = ctx.resolver;
let node = ctx.node;
let ctxElement = ctx.ctxElement;
let ctxTypes = ctx.ctxTypes;
let reportMode = ctx.reportMode;
const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
let classReference = typeArgument.getClassOrWrapper(resolver.program);
if (classReference) {
let overload = classReference.lookupOverload(OperatorKind.IndexedGet);
if (overload) return overload.signature.returnType;
}
if (reportMode == ReportMode.Report) {
resolver.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}
builtinTypes.set(CommonNames.valueof, builtin_resolveValueOfType);

function builtin_resolveReturnOfType(ctx: BuiltinTypesContext): Type | null {
let resolver = ctx.resolver;
let node = ctx.node;
let ctxElement = ctx.ctxElement;
let ctxTypes = ctx.ctxTypes;
let reportMode = ctx.reportMode;
const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
let signatureReference = typeArgument.getSignature();
if (signatureReference) return signatureReference.returnType;
if (reportMode == ReportMode.Report) {
resolver.error(
DiagnosticCode.Type_0_has_no_call_signatures,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}
builtinTypes.set(CommonNames.returnof, builtin_resolveReturnOfType);

function builtin_resolveNonnullableType(ctx: BuiltinTypesContext): Type | null {
let resolver = ctx.resolver;
let node = ctx.node;
let ctxElement = ctx.ctxElement;
let ctxTypes = ctx.ctxTypes;
let reportMode = ctx.reportMode;
const typeArgumentNode = resolver.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = resolver.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
if (!typeArgument.isNullableReference) return typeArgument;
return typeArgument.nonNullableType;
}
builtinTypes.set(CommonNames.nonnull, builtin_resolveNonnullableType);

// === Static type evaluation =================================================================

// helper global used by checkConstantType
Expand Down
148 changes: 9 additions & 139 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ import {
} from "./tokenizer";

import {
BuiltinNames
BuiltinNames,
builtinTypes,
BuiltinTypesContext
} from "./builtins";

/** Indicates whether errors are reported or not. */
Expand Down Expand Up @@ -299,11 +301,11 @@ export class Resolver extends DiagnosticEmitter {
// Handle special built-in types
if (isSimpleType) {
let text = nameNode.identifier.text;
if (text == CommonNames.native) return this.resolveBuiltinNativeType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.indexof) return this.resolveBuiltinIndexofType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.valueof) return this.resolveBuiltinValueofType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.returnof) return this.resolveBuiltinReturnTypeType(node, ctxElement, ctxTypes, reportMode);
if (text == CommonNames.nonnull) return this.resolveBuiltinNotNullableType(node, ctxElement, ctxTypes, reportMode);
if (builtinTypes.has(text)) {
let fn = assert(builtinTypes.get(text));
let ctx = new BuiltinTypesContext(this, node, ctxElement, ctxTypes, reportMode);
return fn(ctx);
}
}

// Resolve normally
Expand Down Expand Up @@ -441,138 +443,6 @@ export class Resolver extends DiagnosticEmitter {
return node.isNullable ? signature.type.asNullable() : signature.type;
}

private resolveBuiltinNativeType(
/** The type to resolve. */
node: NamedTypeNode,
/** Contextual element. */
ctxElement: Element,
/** Contextual types, i.e. `T`. */
ctxTypes: Map<string,Type> | null = null,
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.Report
): Type | null {
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
switch (typeArgument.kind) {
case TypeKind.I8:
case TypeKind.I16:
case TypeKind.I32: return Type.i32;
case TypeKind.Isize: if (!this.program.options.isWasm64) return Type.i32;
case TypeKind.I64: return Type.i64;
case TypeKind.U8:
case TypeKind.U16:
case TypeKind.U32:
case TypeKind.Bool: return Type.u32;
case TypeKind.Usize: if (!this.program.options.isWasm64) return Type.u32;
case TypeKind.U64: return Type.u64;
case TypeKind.F32: return Type.f32;
case TypeKind.F64: return Type.f64;
case TypeKind.V128: return Type.v128;
case TypeKind.Void: return Type.void;
default: assert(false);
}
return null;
}

private resolveBuiltinIndexofType(
/** The type to resolve. */
node: NamedTypeNode,
/** Contextual element. */
ctxElement: Element,
/** Contextual types, i.e. `T`. */
ctxTypes: Map<string,Type> | null = null,
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.Report
): Type | null {
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
let classReference = typeArgument.classReference;
if (!classReference) {
if (reportMode == ReportMode.Report) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}
let overload = classReference.lookupOverload(OperatorKind.IndexedGet);
if (overload) {
let parameterTypes = overload.signature.parameterTypes;
if (overload.is(CommonFlags.Static)) {
assert(parameterTypes.length == 2);
return parameterTypes[1];
} else {
assert(parameterTypes.length == 1);
return parameterTypes[0];
}
}
if (reportMode == ReportMode.Report) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}

private resolveBuiltinValueofType(
/** The type to resolve. */
node: NamedTypeNode,
/** Contextual element. */
ctxElement: Element,
/** Contextual types, i.e. `T`. */
ctxTypes: Map<string,Type> | null = null,
/** How to proceed with eventual diagnostics. */
reportMode: ReportMode = ReportMode.Report
): Type | null {
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
let classReference = typeArgument.getClassOrWrapper(this.program);
if (classReference) {
let overload = classReference.lookupOverload(OperatorKind.IndexedGet);
if (overload) return overload.signature.returnType;
}
if (reportMode == ReportMode.Report) {
this.error(
DiagnosticCode.Index_signature_is_missing_in_type_0,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}

private resolveBuiltinReturnTypeType(
/** The type to resolve. */
node: NamedTypeNode,
/** Contextual element. */
ctxElement: Element,
/** Contextual types, i.e. `T`. */
ctxTypes: Map<string,Type> | null = null,
/** How to proceed with eventualy diagnostics. */
reportMode: ReportMode = ReportMode.Report
): Type | null {
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
if (!typeArgumentNode) return null;
let typeArgument = this.resolveType(typeArgumentNode, null, ctxElement, ctxTypes, reportMode);
if (!typeArgument) return null;
let signatureReference = typeArgument.getSignature();
if (signatureReference) return signatureReference.returnType;
if (reportMode == ReportMode.Report) {
this.error(
DiagnosticCode.Type_0_has_no_call_signatures,
typeArgumentNode.range, typeArgument.toString()
);
}
return null;
}

private resolveBuiltinNotNullableType(
/** The type to resolve. */
node: NamedTypeNode,
Expand Down Expand Up @@ -3814,7 +3684,7 @@ export class Resolver extends DiagnosticEmitter {
return instance;
}

private ensureOneTypeArgument(
ensureOneTypeArgument(
/** The type to resolve. */
node: NamedTypeNode,
/** How to proceed with eventual diagnostics. */
Expand Down
Loading