mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-12 06:21:29 +00:00
Revised implicit type conversions; Initial function expression compilation
This commit is contained in:
14
src/ast.ts
14
src/ast.ts
@ -721,7 +721,7 @@ export abstract class Node {
|
||||
|
||||
static createFunctionDeclaration(
|
||||
name: IdentifierExpression,
|
||||
typeParameters: TypeParameter[],
|
||||
typeParameters: TypeParameter[] | null,
|
||||
parameters: Parameter[],
|
||||
returnType: TypeNode | null,
|
||||
body: Statement | null,
|
||||
@ -732,7 +732,7 @@ export abstract class Node {
|
||||
var stmt = new FunctionDeclaration();
|
||||
stmt.range = range;
|
||||
stmt.name = name; name.parent = stmt;
|
||||
stmt.typeParameters = typeParameters; setParent(typeParameters, stmt);
|
||||
stmt.typeParameters = typeParameters; if (typeParameters) setParent(typeParameters, stmt);
|
||||
stmt.parameters = parameters; setParent(parameters, stmt);
|
||||
stmt.returnType = returnType; if (returnType) returnType.parent = stmt;
|
||||
stmt.body = body; if (body) body.parent = stmt;
|
||||
@ -743,7 +743,7 @@ export abstract class Node {
|
||||
|
||||
static createMethodDeclaration(
|
||||
name: IdentifierExpression,
|
||||
typeParameters: TypeParameter[],
|
||||
typeParameters: TypeParameter[] | null,
|
||||
parameters: Parameter[],
|
||||
returnType: TypeNode | null,
|
||||
body: Statement | null,
|
||||
@ -754,7 +754,7 @@ export abstract class Node {
|
||||
var stmt = new MethodDeclaration();
|
||||
stmt.range = range;
|
||||
stmt.name = name; name.parent = stmt;
|
||||
stmt.typeParameters = typeParameters; setParent(typeParameters, stmt);
|
||||
stmt.typeParameters = typeParameters; if (typeParameters) setParent(typeParameters, stmt);
|
||||
stmt.parameters = parameters; setParent(parameters, stmt);
|
||||
stmt.returnType = returnType; if (returnType) returnType.parent = stmt;
|
||||
stmt.body = body; if (body) body.parent = stmt;
|
||||
@ -1490,13 +1490,17 @@ export class FunctionDeclaration extends DeclarationStatement {
|
||||
kind = NodeKind.FUNCTIONDECLARATION;
|
||||
|
||||
/** Accepted type parameters. */
|
||||
typeParameters: TypeParameter[];
|
||||
typeParameters: TypeParameter[] | null;
|
||||
/** Accepted parameters. */
|
||||
parameters: Parameter[];
|
||||
/** Return type. */
|
||||
returnType: TypeNode | null;
|
||||
/** Body statement. Usually a block. */
|
||||
body: Statement | null;
|
||||
|
||||
get isGeneric(): bool {
|
||||
return this.typeParameters != null && this.typeParameters.length > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents an `if` statement. */
|
||||
|
@ -2303,7 +2303,7 @@ function evaluateConstantOffset(compiler: Compiler, expression: Expression): i32
|
||||
var expr: ExpressionRef;
|
||||
var value: i32;
|
||||
if (compiler.options.isWasm64) {
|
||||
expr = compiler.precomputeExpression(expression, Type.i64);
|
||||
expr = compiler.precomputeExpression(expression, Type.usize64);
|
||||
if (
|
||||
_BinaryenExpressionGetId(expr) != ExpressionId.Const ||
|
||||
_BinaryenExpressionGetType(expr) != NativeType.I64 ||
|
||||
@ -2317,7 +2317,7 @@ function evaluateConstantOffset(compiler: Compiler, expression: Expression): i32
|
||||
value = -1;
|
||||
}
|
||||
} else {
|
||||
expr = compiler.precomputeExpression(expression, Type.i32);
|
||||
expr = compiler.precomputeExpression(expression, Type.usize32);
|
||||
if (
|
||||
_BinaryenExpressionGetId(expr) != ExpressionId.Const ||
|
||||
_BinaryenExpressionGetType(expr) != NativeType.I32 ||
|
||||
|
170
src/compiler.ts
170
src/compiler.ts
@ -88,6 +88,7 @@ import {
|
||||
CommaExpression,
|
||||
ElementAccessExpression,
|
||||
FloatLiteralExpression,
|
||||
FunctionExpression,
|
||||
IdentifierExpression,
|
||||
IntegerLiteralExpression,
|
||||
LiteralExpression,
|
||||
@ -200,6 +201,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
/** Map of already compiled static string segments. */
|
||||
stringSegments: Map<string,MemorySegment> = new Map();
|
||||
|
||||
/** Function table being compiled. */
|
||||
functionTable: Function[] = new Array();
|
||||
|
||||
/** Already processed file names. */
|
||||
files: Set<string> = new Set();
|
||||
|
||||
@ -294,6 +298,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
"memory"
|
||||
);
|
||||
}
|
||||
|
||||
// set up function table
|
||||
if (k = this.functionTable.length) {
|
||||
var entries = new Array<FunctionRef>(k);
|
||||
for (i = 0; i < k; ++i) {
|
||||
entries[i] = this.functionTable[i].ref;
|
||||
}
|
||||
this.module.setFunctionTable(entries);
|
||||
}
|
||||
|
||||
return this.module;
|
||||
}
|
||||
|
||||
@ -376,7 +390,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
noTreeShaking ||
|
||||
(isEntry && hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>statement).modifiers))
|
||||
) &&
|
||||
!(<FunctionDeclaration>statement).typeParameters.length
|
||||
!(<FunctionDeclaration>statement).isGeneric
|
||||
) {
|
||||
this.compileFunctionDeclaration(<FunctionDeclaration>statement, []);
|
||||
}
|
||||
@ -868,7 +882,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
(
|
||||
noTreeShaking ||
|
||||
hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>member).modifiers)
|
||||
) && !(<FunctionDeclaration>member).typeParameters.length
|
||||
) &&
|
||||
!(<FunctionDeclaration>member).isGeneric
|
||||
) {
|
||||
this.compileFunctionDeclaration(<FunctionDeclaration>member, []);
|
||||
}
|
||||
@ -1068,7 +1083,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// memory
|
||||
|
||||
/** Adds a static memory segment with the specified data. */
|
||||
/** Adds a static memory segment with the specified data. */
|
||||
addMemorySegment(buffer: Uint8Array, alignment: i32 = 8): MemorySegment {
|
||||
var memoryOffset = i64_align(this.memoryOffset, alignment);
|
||||
var segment = MemorySegment.create(buffer, memoryOffset);
|
||||
@ -1077,6 +1092,20 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return segment;
|
||||
}
|
||||
|
||||
// function table
|
||||
|
||||
/** Adds a function table entry and returns the assigned index. */
|
||||
addFunctionTableEntry(func: Function): i32 {
|
||||
assert(func.is(ElementFlags.COMPILED));
|
||||
if (func.functionTableIndex >= 0) {
|
||||
return func.functionTableIndex;
|
||||
}
|
||||
var index = this.functionTable.length;
|
||||
this.functionTable.push(func);
|
||||
func.functionTableIndex = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
// statements
|
||||
|
||||
compileStatement(statement: Statement): ExpressionRef {
|
||||
@ -1241,7 +1270,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.currentFunction.flow.breakLabel = previousBreakLabel;
|
||||
this.currentFunction.flow.continueLabel = previousContinueLabel;
|
||||
|
||||
var condition = this.compileExpression(statement.condition, Type.i32);
|
||||
var condition = makeIsTrueish(
|
||||
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
this.module
|
||||
);
|
||||
|
||||
this.currentFunction.leaveBreakContext();
|
||||
|
||||
@ -1319,7 +1352,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
compileIfStatement(statement: IfStatement): ExpressionRef {
|
||||
|
||||
// The condition doesn't initiate a branch yet
|
||||
var condition = this.compileExpression(statement.condition, Type.i32);
|
||||
var condition = makeIsTrueish(
|
||||
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
this.module
|
||||
);
|
||||
|
||||
// Each arm initiates a branch
|
||||
this.currentFunction.flow = this.currentFunction.flow.enterBranchOrScope();
|
||||
@ -1359,14 +1396,14 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var context = this.currentFunction.enterBreakContext();
|
||||
|
||||
// introduce a local for evaluating the condition (exactly once)
|
||||
var tempLocal = this.currentFunction.getTempLocal(Type.i32);
|
||||
var tempLocal = this.currentFunction.getTempLocal(Type.u32);
|
||||
var k = statement.cases.length;
|
||||
|
||||
// Prepend initializer to inner block. Does not initiate a new branch, yet.
|
||||
var breaks = new Array<ExpressionRef>(1 + k);
|
||||
breaks[0] = this.module.createSetLocal( // initializer
|
||||
tempLocal.index,
|
||||
this.compileExpression(statement.condition, Type.i32)
|
||||
this.compileExpression(statement.condition, Type.u32)
|
||||
);
|
||||
|
||||
// make one br_if per (possibly dynamic) labeled case (binaryen optimizes to br_table where possible)
|
||||
@ -1577,7 +1614,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
compileWhileStatement(statement: WhileStatement): ExpressionRef {
|
||||
|
||||
// The condition does not yet initialize a branch
|
||||
var condition = this.compileExpression(statement.condition, Type.i32);
|
||||
var condition = makeIsTrueish(
|
||||
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
this.module
|
||||
);
|
||||
|
||||
// Statements initiate a new branch with its own break context
|
||||
var label = this.currentFunction.enterBreakContext();
|
||||
@ -1715,6 +1756,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
expr = this.compileElementAccessExpression(<ElementAccessExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.FUNCTION:
|
||||
case NodeKind.FUNCTIONARROW:
|
||||
expr = this.compileFunctionExpression(<FunctionExpression>expression, contextualType);
|
||||
break;
|
||||
|
||||
case NodeKind.IDENTIFIER:
|
||||
case NodeKind.FALSE:
|
||||
case NodeKind.NULL:
|
||||
@ -1817,6 +1863,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createDrop(expr);
|
||||
}
|
||||
|
||||
if (conversionKind == ConversionKind.IMPLICIT && !fromType.isAssignableTo(toType)) {
|
||||
this.error(
|
||||
DiagnosticCode.Conversion_from_type_0_to_1_requires_an_explicit_cast,
|
||||
reportNode.range, fromType.toString(), toType.toString()
|
||||
);
|
||||
}
|
||||
|
||||
var mod = this.module;
|
||||
var losesInformation = false;
|
||||
|
||||
@ -1974,13 +2027,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// otherwise (smaller) i32/u32 to (same size) i32/u32
|
||||
}
|
||||
|
||||
if (losesInformation && conversionKind == ConversionKind.IMPLICIT) {
|
||||
this.error(
|
||||
DiagnosticCode.Conversion_from_type_0_to_1_possibly_loses_information_and_thus_requires_an_explicit_cast,
|
||||
reportNode.range, fromType.toString(), toType.toString()
|
||||
);
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@ -3768,6 +3814,25 @@ export class Compiler extends DiagnosticEmitter {
|
||||
], expression);
|
||||
}
|
||||
|
||||
compileFunctionExpression(expression: FunctionExpression, contextualType: Type): ExpressionRef {
|
||||
var declaration = expression.declaration;
|
||||
var simpleName = (declaration.name.text.length
|
||||
? declaration.name.text
|
||||
: "anonymous") + "|" + this.functionTable.length.toString(10);
|
||||
var prototype = new FunctionPrototype(
|
||||
this.program,
|
||||
simpleName,
|
||||
this.currentFunction.internalName + "~" + simpleName,
|
||||
declaration
|
||||
);
|
||||
var instance = this.compileFunctionUsingTypeArguments(prototype, [], null, declaration);
|
||||
if (!instance) return this.module.createUnreachable();
|
||||
this.currentType = Type.u32.asFunction(instance);
|
||||
var index = this.addFunctionTableEntry(instance);
|
||||
if (index < 0) return this.module.createUnreachable();
|
||||
return this.module.createI32(index);
|
||||
}
|
||||
|
||||
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): ExpressionRef {
|
||||
// check special keywords first
|
||||
switch (expression.kind) {
|
||||
@ -4045,7 +4110,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.stringSegments.set(stringValue, stringSegment);
|
||||
}
|
||||
var stringOffset = stringSegment.offset;
|
||||
this.currentType = this.options.usizeType;
|
||||
var stringType = this.program.types.get("string");
|
||||
this.currentType = stringType ? stringType : this.options.usizeType;
|
||||
if (this.options.isWasm64) {
|
||||
return this.module.createI64(i64_low(stringOffset), i64_high(stringOffset));
|
||||
}
|
||||
@ -4244,7 +4310,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
assert((<Field>element).memoryOffset >= 0);
|
||||
targetExpr = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
this.options.usizeType
|
||||
this.options.usizeType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
this.currentType = (<Field>element).type;
|
||||
return this.module.createLoad(
|
||||
@ -4265,9 +4332,8 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (getterInstance.is(ElementFlags.INSTANCE)) {
|
||||
targetExpr = this.compileExpression(
|
||||
<Expression>resolved.targetExpression,
|
||||
this.options.isWasm64
|
||||
? Type.usize64
|
||||
: Type.usize32
|
||||
this.options.usizeType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
this.currentType = getterInstance.returnType;
|
||||
return this.makeCall(getterInstance, [ targetExpr ]);
|
||||
@ -4283,7 +4349,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileTernaryExpression(expression: TernaryExpression, contextualType: Type): ExpressionRef {
|
||||
var condition = this.compileExpression(expression.condition, Type.i32);
|
||||
var condition = makeIsTrueish(
|
||||
this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
this.module
|
||||
);
|
||||
var ifThen = this.compileExpression(expression.ifThen, contextualType);
|
||||
var ifElse = this.compileExpression(expression.ifElse, contextualType);
|
||||
return this.module.createIf(condition, ifThen, ifElse);
|
||||
@ -4308,6 +4378,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
switch (expression.operator) {
|
||||
|
||||
case Token.PLUS_PLUS:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
@ -4363,6 +4440,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.MINUS_MINUS:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
@ -4496,6 +4580,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.MINUS:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
if (expression.operand.kind == NodeKind.LITERAL && (
|
||||
(<LiteralExpression>expression.operand).literalKind == LiteralKind.INTEGER ||
|
||||
(<LiteralExpression>expression.operand).literalKind == LiteralKind.FLOAT
|
||||
@ -4559,6 +4650,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.PLUS_PLUS:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
compound = true;
|
||||
expr = this.compileExpression(
|
||||
expression.operand,
|
||||
@ -4616,6 +4714,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.MINUS_MINUS:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
compound = true;
|
||||
expr = this.compileExpression(
|
||||
expression.operand,
|
||||
@ -4687,6 +4792,13 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.TILDE:
|
||||
if (this.currentType.isReference) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
expression.range
|
||||
);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
expr = this.compileExpression(
|
||||
expression.operand,
|
||||
contextualType == Type.void
|
||||
@ -4866,13 +4978,13 @@ export function makeIsFalseish(expr: ExpressionRef, type: Type, module: Module):
|
||||
}
|
||||
|
||||
/** Creates a comparison whether an expression is 'true' in a broader sense. */
|
||||
export function makeIsTrueish(expr: ExpressionRef, type: Type, module: Module): ExpressionRef {
|
||||
export function makeIsTrueish(
|
||||
expr: ExpressionRef,
|
||||
type: Type,
|
||||
module: Module
|
||||
): ExpressionRef {
|
||||
switch (type.kind) {
|
||||
|
||||
default: // any integer up to 32 bits
|
||||
expr = module.createBinary(BinaryOp.NeI32, expr, module.createI32(0));
|
||||
break;
|
||||
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64:
|
||||
expr = module.createBinary(BinaryOp.NeI64, expr, module.createI64(0));
|
||||
@ -4881,9 +4993,9 @@ export function makeIsTrueish(expr: ExpressionRef, type: Type, module: Module):
|
||||
case TypeKind.USIZE:
|
||||
// TODO: strings
|
||||
case TypeKind.ISIZE:
|
||||
expr = type.size == 64
|
||||
? module.createBinary(BinaryOp.NeI64, expr, module.createI64(0))
|
||||
: module.createBinary(BinaryOp.NeI32, expr, module.createI32(0));
|
||||
if (type.size == 64) {
|
||||
expr = module.createBinary(BinaryOp.NeI64, expr, module.createI64(0));
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.F32:
|
||||
|
@ -4,7 +4,7 @@
|
||||
export enum DiagnosticCode {
|
||||
Operation_not_supported = 100,
|
||||
Operation_is_unsafe = 101,
|
||||
Conversion_from_type_0_to_1_possibly_loses_information_and_thus_requires_an_explicit_cast = 200,
|
||||
Conversion_from_type_0_to_1_requires_an_explicit_cast = 200,
|
||||
Conversion_from_type_0_to_1_will_require_an_explicit_cast_when_switching_between_32_64_bit = 201,
|
||||
Type_0_cannot_be_changed_to_type_1 = 202,
|
||||
Type_0_cannot_be_reinterpreted_as_type_1 = 203,
|
||||
@ -101,7 +101,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
switch (code) {
|
||||
case 100: return "Operation not supported.";
|
||||
case 101: return "Operation is unsafe.";
|
||||
case 200: return "Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast.";
|
||||
case 200: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
|
||||
case 201: return "Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.";
|
||||
case 202: return "Type '{0}' cannot be changed to type '{1}'.";
|
||||
case 203: return "Type '{0}' cannot be reinterpreted as type '{1}'.";
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"Operation not supported.": 100,
|
||||
"Operation is unsafe.": 101,
|
||||
"Conversion from type '{0}' to '{1}' possibly loses information and thus requires an explicit cast.": 200,
|
||||
"Conversion from type '{0}' to '{1}' requires an explicit cast.": 200,
|
||||
"Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.": 201,
|
||||
"Type '{0}' cannot be changed to type '{1}'.": 202,
|
||||
"Type '{0}' cannot be reinterpreted as type '{1}'.": 203,
|
||||
|
@ -876,14 +876,16 @@ export function serializeFunctionDeclaration(node: FunctionDeclaration, sb: stri
|
||||
function serializeFunctionCommon(node: FunctionDeclaration, sb: string[], isArrow: bool = false): void {
|
||||
var i: i32, k: i32;
|
||||
serializeIdentifierExpression(node.name, sb);
|
||||
if (k = node.typeParameters.length) {
|
||||
sb.push("<");
|
||||
serializeTypeParameter(node.typeParameters[0], sb);
|
||||
for (i = 1; i < k; ++i) {
|
||||
sb.push(", ");
|
||||
serializeTypeParameter(node.typeParameters[i], sb);
|
||||
if (node.typeParameters) {
|
||||
if (k = node.typeParameters.length) {
|
||||
sb.push("<");
|
||||
serializeTypeParameter(node.typeParameters[0], sb);
|
||||
for (i = 1; i < k; ++i) {
|
||||
sb.push(", ");
|
||||
serializeTypeParameter(node.typeParameters[i], sb);
|
||||
}
|
||||
sb.push(">");
|
||||
}
|
||||
sb.push(">");
|
||||
}
|
||||
sb.push("(");
|
||||
if (k = node.parameters.length) {
|
||||
|
@ -56,6 +56,7 @@ import {
|
||||
ExportStatement,
|
||||
ExpressionStatement,
|
||||
ForStatement,
|
||||
FunctionExpression,
|
||||
FunctionDeclaration,
|
||||
IfStatement,
|
||||
ImportDeclaration,
|
||||
@ -79,8 +80,7 @@ import {
|
||||
addModifier,
|
||||
getModifier,
|
||||
hasModifier,
|
||||
setReusableModifiers,
|
||||
FunctionExpression
|
||||
setReusableModifiers
|
||||
|
||||
} from "./ast";
|
||||
|
||||
@ -874,8 +874,6 @@ export class Parser extends DiagnosticEmitter {
|
||||
if (tn.skip(Token.LESSTHAN)) {
|
||||
typeParameters = this.parseTypeParameters(tn);
|
||||
if (!typeParameters) return null;
|
||||
} else {
|
||||
typeParameters = [];
|
||||
}
|
||||
|
||||
if (!tn.skip(Token.OPENPAREN)) {
|
||||
@ -1046,7 +1044,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
|
||||
var declaration = Node.createFunctionDeclaration(
|
||||
identifier,
|
||||
[],
|
||||
null,
|
||||
parameters,
|
||||
returnType,
|
||||
body,
|
||||
@ -1213,7 +1211,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
? Node.createConstructorExpression(tn.range())
|
||||
: Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
|
||||
|
||||
var typeParameters: TypeParameter[] | null;
|
||||
var typeParameters: TypeParameter[] | null = null;
|
||||
if (tn.skip(Token.LESSTHAN)) {
|
||||
if (isConstructor) {
|
||||
this.error(
|
||||
@ -1223,8 +1221,6 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
typeParameters = this.parseTypeParameters(tn);
|
||||
if (!typeParameters) return null;
|
||||
} else {
|
||||
typeParameters = [];
|
||||
}
|
||||
|
||||
// method: '(' Parameters (':' Type)? '{' Statement* '}' ';'?
|
||||
|
@ -474,12 +474,18 @@ export class Program extends DiagnosticEmitter {
|
||||
// check and possibly register string type
|
||||
if (
|
||||
prototype.is(ElementFlags.GLOBAL) &&
|
||||
declaration.name.text === "String" &&
|
||||
!this.types.has("string")
|
||||
declaration.name.text == "String"
|
||||
) {
|
||||
var instance = prototype.resolve(null);
|
||||
if (instance) {
|
||||
this.types.set("string", instance.type);
|
||||
if (!this.types.has("string")) {
|
||||
var instance = prototype.resolve(null);
|
||||
if (instance) {
|
||||
this.types.set("string", instance.type);
|
||||
}
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
declaration.name.range, declaration.programLevelInternalName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2116,7 +2122,7 @@ export class FunctionPrototype extends Element {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.declaration.typeParameters.length) {
|
||||
if (this.declaration.isGeneric) {
|
||||
this.set(ElementFlags.GENERIC);
|
||||
}
|
||||
if (this.classPrototype = classPrototype) {
|
||||
@ -2163,7 +2169,7 @@ export class FunctionPrototype extends Element {
|
||||
// override call specific contextual type arguments
|
||||
var functionTypeParameters = declaration.typeParameters;
|
||||
if (functionTypeArguments && (k = functionTypeArguments.length)) {
|
||||
if (k != functionTypeParameters.length) {
|
||||
if (!functionTypeParameters || k != functionTypeParameters.length) {
|
||||
throw new Error("type argument count mismatch");
|
||||
}
|
||||
for (i = 0; i < k; ++i) {
|
||||
@ -2229,7 +2235,7 @@ export class FunctionPrototype extends Element {
|
||||
if (this.is(ElementFlags.GENERIC)) {
|
||||
assert(typeArgumentNodes != null && typeArgumentNodes.length != 0);
|
||||
resolvedTypeArguments = this.program.resolveTypeArguments(
|
||||
this.declaration.typeParameters,
|
||||
assert(this.declaration.typeParameters),
|
||||
typeArgumentNodes,
|
||||
contextualTypeArguments,
|
||||
reportNode
|
||||
@ -2288,6 +2294,10 @@ export class Function extends Element {
|
||||
flow: Flow;
|
||||
/** Remembered debug locations. */
|
||||
debugLocations: Range[] | null = null;
|
||||
/** Function reference, if compiled. */
|
||||
ref: FunctionRef = 0;
|
||||
/** Function table index, if any. */
|
||||
functionTableIndex: i32 = -1;
|
||||
|
||||
private nextBreakId: i32 = 0;
|
||||
private breakStack: i32[] | null = null;
|
||||
@ -2444,6 +2454,7 @@ export class Function extends Element {
|
||||
|
||||
/** Finalizes the function once compiled, releasing no longer needed resources. */
|
||||
finalize(module: Module, ref: FunctionRef): void {
|
||||
this.ref = ref;
|
||||
assert(!this.breakStack || !this.breakStack.length); // internal error
|
||||
this.breakStack = null;
|
||||
this.breakContext = null;
|
||||
@ -2466,6 +2477,11 @@ export class Function extends Element {
|
||||
this.debugLocations = null;
|
||||
}
|
||||
|
||||
/** Tests if a value of this function type is assignable to a target of the specified function type. */
|
||||
isAssignableTo(target: Function): bool {
|
||||
return this == target; // TODO
|
||||
}
|
||||
|
||||
/** Returns the TypeScript representation of this function. */
|
||||
toString(): string { return this.prototype.simpleName; }
|
||||
|
||||
@ -2865,6 +2881,17 @@ export class Class extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests if a value of this class type is assignable to a target of the specified class type. */
|
||||
isAssignableTo(target: Class): bool {
|
||||
var current: Class | null = this;
|
||||
do {
|
||||
if (current == target) {
|
||||
return true;
|
||||
}
|
||||
} while (current = current.base);
|
||||
return false;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.prototype.simpleName;
|
||||
}
|
||||
|
192
src/types.ts
192
src/types.ts
@ -122,7 +122,7 @@ export class Type {
|
||||
|
||||
/** Composes a function type from this type and a function. */
|
||||
asFunction(functionType: Function): Type {
|
||||
assert(this.kind == TypeKind.USIZE && !this.isReference);
|
||||
assert(this.kind == TypeKind.U32 && !this.isReference);
|
||||
var ret = new Type(this.kind, this.flags & ~TypeFlags.VALUE | TypeFlags.REFERENCE, this.size);
|
||||
ret.functionType = functionType;
|
||||
return ret;
|
||||
@ -140,6 +140,196 @@ export class Type {
|
||||
return this.nullableType;
|
||||
}
|
||||
|
||||
/** Tests if a value of this type is assignable to a target of the specified type. */
|
||||
isAssignableTo(target: Type): bool {
|
||||
var currentClass: Class | null;
|
||||
var targetClass: Class | null;
|
||||
var currentFunction: Function | null;
|
||||
var targetFunction: Function | null;
|
||||
if (this.isReference) {
|
||||
if (target.isReference) {
|
||||
if (currentClass = this.classType) {
|
||||
if (targetClass = target.classType) {
|
||||
return currentClass.isAssignableTo(targetClass);
|
||||
}
|
||||
} else if (currentFunction = this.functionType) {
|
||||
if (targetFunction = target.functionType) {
|
||||
return currentFunction.isAssignableTo(targetFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!target.isReference) {
|
||||
switch (this.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I8: // same
|
||||
case TypeKind.I16: // larger
|
||||
case TypeKind.I32: // larger
|
||||
case TypeKind.I64: // larger
|
||||
case TypeKind.ISIZE: // larger
|
||||
case TypeKind.U8: // signed to unsigned
|
||||
case TypeKind.U16: // larger
|
||||
case TypeKind.U32: // larger
|
||||
case TypeKind.U64: // larger
|
||||
case TypeKind.USIZE: // larger
|
||||
case TypeKind.F32: // safe
|
||||
case TypeKind.F64: // safe
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.I16:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I16: // same
|
||||
case TypeKind.I32: // larger
|
||||
case TypeKind.I64: // larger
|
||||
case TypeKind.ISIZE: // larger
|
||||
case TypeKind.U16: // signed to unsigned
|
||||
case TypeKind.U32: // larger
|
||||
case TypeKind.U64: // larger
|
||||
case TypeKind.USIZE: // larger
|
||||
case TypeKind.F32: // safe
|
||||
case TypeKind.F64: // safe
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.I32:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I32: // same
|
||||
case TypeKind.I64: // larger
|
||||
case TypeKind.ISIZE: // same or larger
|
||||
case TypeKind.U32: // signed to unsigned
|
||||
case TypeKind.U64: // larger
|
||||
case TypeKind.USIZE: // signed to unsigned or larger
|
||||
case TypeKind.F64: // safe
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.I64:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I64: // same
|
||||
case TypeKind.U64: // signed to unsigned
|
||||
return true;
|
||||
case TypeKind.ISIZE: // possibly same
|
||||
case TypeKind.USIZE: // possibly signed to unsigned
|
||||
return target.size == 64;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.ISIZE:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I32: // possibly same
|
||||
case TypeKind.U32: // possibly signed to unsigned
|
||||
return this.size == 32;
|
||||
case TypeKind.I64: // same or larger
|
||||
case TypeKind.ISIZE: // same
|
||||
case TypeKind.U64: // signed to unsigned or larger
|
||||
case TypeKind.USIZE: // signed to unsigned
|
||||
return true;
|
||||
case TypeKind.F64: // possibly safe
|
||||
return target.size == 32;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.U8:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I16: // larger
|
||||
case TypeKind.I32: // larger
|
||||
case TypeKind.I64: // larger
|
||||
case TypeKind.ISIZE: // larger
|
||||
case TypeKind.U8: // same
|
||||
case TypeKind.U16: // larger
|
||||
case TypeKind.U32: // larger
|
||||
case TypeKind.U64: // larger
|
||||
case TypeKind.USIZE: // larger
|
||||
case TypeKind.F32: // safe
|
||||
case TypeKind.F64: // safe
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.U16:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I32: // larger
|
||||
case TypeKind.I64: // larger
|
||||
case TypeKind.ISIZE: // larger
|
||||
case TypeKind.U16: // same
|
||||
case TypeKind.U32: // larger
|
||||
case TypeKind.U64: // larger
|
||||
case TypeKind.USIZE: // larger
|
||||
case TypeKind.F32: // safe
|
||||
case TypeKind.F64: // safe
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.U32:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I64: // larger
|
||||
case TypeKind.U32: // same
|
||||
case TypeKind.U64: // larger
|
||||
case TypeKind.USIZE: // same or larger
|
||||
case TypeKind.F64: // safe
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.U64:
|
||||
switch (target.kind) {
|
||||
case TypeKind.U64: // same
|
||||
return true;
|
||||
case TypeKind.USIZE: // possibly same
|
||||
return target.size == 64;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.USIZE:
|
||||
switch (target.kind) {
|
||||
case TypeKind.U32: // possibly same
|
||||
return this.size == 32;
|
||||
case TypeKind.U64: // same or larger
|
||||
case TypeKind.USIZE: // same
|
||||
return true;
|
||||
case TypeKind.F64: // possibly safe
|
||||
return target.size == 32;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.BOOL:
|
||||
switch (target.kind) {
|
||||
case TypeKind.I8: // larger
|
||||
case TypeKind.I16: // larger
|
||||
case TypeKind.I32: // larger
|
||||
case TypeKind.I64: // larger
|
||||
case TypeKind.ISIZE: // larger
|
||||
case TypeKind.U8: // larger
|
||||
case TypeKind.U16: // larger
|
||||
case TypeKind.U32: // larger
|
||||
case TypeKind.U64: // larger
|
||||
case TypeKind.USIZE: // larger
|
||||
case TypeKind.BOOL: // same
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.F32:
|
||||
switch (target.kind) {
|
||||
case TypeKind.F32: // same
|
||||
case TypeKind.F64: // larger
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeKind.F64:
|
||||
return target.kind == TypeKind.F64;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Converts this type to its TypeScript representation. */
|
||||
toString(kindOnly: bool = false): string {
|
||||
switch (this.kind) {
|
||||
|
Reference in New Issue
Block a user