mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-12 06:21:29 +00:00
Update binary expression inference, see #35; Update dependencies
This commit is contained in:
@ -1563,13 +1563,14 @@ export function compileCall(
|
||||
if (offset < 0) { // reported in evaluateConstantOffset
|
||||
return module.createUnreachable();
|
||||
}
|
||||
compiler.currentType = typeArguments[0];
|
||||
return module.createLoad(
|
||||
typeArguments[0].byteSize,
|
||||
typeArguments[0].is(TypeFlags.SIGNED | TypeFlags.INTEGER),
|
||||
arg0,
|
||||
typeArguments[0].is(TypeFlags.INTEGER) &&
|
||||
contextualType.is(TypeFlags.INTEGER) &&
|
||||
contextualType.size >= typeArguments[0].size
|
||||
contextualType.size > typeArguments[0].size
|
||||
? (compiler.currentType = contextualType).toNativeType()
|
||||
: (compiler.currentType = typeArguments[0]).toNativeType(),
|
||||
offset
|
||||
@ -1896,7 +1897,7 @@ export function compileCall(
|
||||
}
|
||||
arg0 = compiler.compileExpression(operands[0], typeArguments[0]);
|
||||
} else {
|
||||
arg0 = compiler.compileExpression(operands[0], Type.i32, ConversionKind.NONE);
|
||||
arg0 = compiler.compileExpressionRetainType(operands[0], Type.i32);
|
||||
}
|
||||
|
||||
type = compiler.currentType;
|
||||
|
674
src/compiler.ts
674
src/compiler.ts
@ -529,7 +529,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var initializeInStart = false;
|
||||
|
||||
if (global.is(ElementFlags.INLINED)) {
|
||||
initExpr = this.compileInlineConstant(global, global.type);
|
||||
initExpr = this.compileInlineConstant(global, global.type, true);
|
||||
} else {
|
||||
if (declaration.initializer) {
|
||||
if (!initExpr) {
|
||||
@ -1662,11 +1662,20 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// expressions
|
||||
|
||||
/** Compiles an inlined constant value of a variable-like element. */
|
||||
compileInlineConstant(element: VariableLikeElement, contextualType: Type): ExpressionRef {
|
||||
/**
|
||||
* Compiles the value of an inlined constant element.
|
||||
* @param retainType If true, the annotated type of the constant is retained. Otherwise, the value
|
||||
* is precomputed according to context.
|
||||
*/
|
||||
compileInlineConstant(
|
||||
element: VariableLikeElement,
|
||||
contextualType: Type,
|
||||
retainType: bool
|
||||
): ExpressionRef {
|
||||
assert(element.is(ElementFlags.INLINED));
|
||||
|
||||
switch (
|
||||
!retainType &&
|
||||
element.type.is(TypeFlags.INTEGER) &&
|
||||
contextualType.is(TypeFlags.INTEGER) &&
|
||||
element.type.size < contextualType.size
|
||||
@ -1773,7 +1782,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case NodeKind.NULL:
|
||||
case NodeKind.THIS:
|
||||
case NodeKind.TRUE:
|
||||
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, contextualType);
|
||||
expr = this.compileIdentifierExpression(
|
||||
<IdentifierExpression>expression,
|
||||
contextualType,
|
||||
conversionKind == ConversionKind.NONE // retain type of inlined constants
|
||||
);
|
||||
break;
|
||||
|
||||
case NodeKind.LITERAL:
|
||||
@ -1789,7 +1802,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case NodeKind.PROPERTYACCESS:
|
||||
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, contextualType);
|
||||
expr = this.compilePropertyAccessExpression(
|
||||
<PropertyAccessExpression>expression,
|
||||
contextualType,
|
||||
conversionKind == ConversionKind.NONE // retain type of inlined constants
|
||||
);
|
||||
break;
|
||||
|
||||
case NodeKind.TERNARY:
|
||||
@ -1817,6 +1834,17 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return expr;
|
||||
}
|
||||
|
||||
compileExpressionRetainType(expression: Expression, contextualType: Type, wrapSmallIntegers: bool = true) {
|
||||
return this.compileExpression(
|
||||
expression,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
wrapSmallIntegers
|
||||
);
|
||||
}
|
||||
|
||||
precomputeExpression(
|
||||
expression: Expression,
|
||||
contextualType: Type,
|
||||
@ -1877,9 +1905,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
);
|
||||
}
|
||||
|
||||
var mod = this.module;
|
||||
var losesInformation = false;
|
||||
|
||||
if (fromType.is(TypeFlags.FLOAT)) {
|
||||
|
||||
// float to float
|
||||
@ -1888,39 +1913,37 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// f32 to f64
|
||||
if (toType.kind == TypeKind.F64) {
|
||||
expr = mod.createUnary(UnaryOp.PromoteF32, expr);
|
||||
expr = this.module.createUnary(UnaryOp.PromoteF32, expr);
|
||||
}
|
||||
|
||||
// otherwise f32 to f32
|
||||
|
||||
// f64 to f32
|
||||
} else if (toType.kind == TypeKind.F32) {
|
||||
losesInformation = true;
|
||||
expr = mod.createUnary(UnaryOp.DemoteF64, expr);
|
||||
expr = this.module.createUnary(UnaryOp.DemoteF64, expr);
|
||||
}
|
||||
|
||||
// otherwise f64 to f64
|
||||
|
||||
// float to int
|
||||
} else if (toType.is(TypeFlags.INTEGER)) {
|
||||
losesInformation = true;
|
||||
|
||||
// f32 to int
|
||||
if (fromType.kind == TypeKind.F32) {
|
||||
if (toType.is(TypeFlags.SIGNED)) {
|
||||
if (toType.is(TypeFlags.LONG)) {
|
||||
expr = mod.createUnary(UnaryOp.TruncF32ToI64, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF32ToI64, expr);
|
||||
} else {
|
||||
expr = mod.createUnary(UnaryOp.TruncF32ToI32, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF32ToI32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, this.module);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (toType.is(TypeFlags.LONG)) {
|
||||
expr = mod.createUnary(UnaryOp.TruncF32ToU64, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF32ToU64, expr);
|
||||
} else {
|
||||
expr = mod.createUnary(UnaryOp.TruncF32ToU32, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF32ToU32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, this.module);
|
||||
}
|
||||
@ -1931,18 +1954,18 @@ export class Compiler extends DiagnosticEmitter {
|
||||
} else {
|
||||
if (toType.is(TypeFlags.SIGNED)) {
|
||||
if (toType.is(TypeFlags.LONG)) {
|
||||
expr = mod.createUnary(UnaryOp.TruncF64ToI64, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF64ToI64, expr);
|
||||
} else {
|
||||
expr = mod.createUnary(UnaryOp.TruncF64ToI32, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF64ToI32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, this.module);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (toType.is(TypeFlags.LONG)) {
|
||||
expr = mod.createUnary(UnaryOp.TruncF64ToU64, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF64ToU64, expr);
|
||||
} else {
|
||||
expr = mod.createUnary(UnaryOp.TruncF64ToU32, expr);
|
||||
expr = this.module.createUnary(UnaryOp.TruncF64ToU32, expr);
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, this.module);
|
||||
}
|
||||
@ -1962,16 +1985,14 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// int to f32
|
||||
if (toType.kind == TypeKind.F32) {
|
||||
if (fromType.is(TypeFlags.LONG)) {
|
||||
losesInformation = true;
|
||||
expr = mod.createUnary(
|
||||
expr = this.module.createUnary(
|
||||
fromType.is(TypeFlags.SIGNED)
|
||||
? UnaryOp.ConvertI64ToF32
|
||||
: UnaryOp.ConvertU64ToF32,
|
||||
expr
|
||||
);
|
||||
} else {
|
||||
losesInformation = !fromType.is(TypeFlags.SMALL);
|
||||
expr = mod.createUnary(
|
||||
expr = this.module.createUnary(
|
||||
fromType.is(TypeFlags.SIGNED)
|
||||
? UnaryOp.ConvertI32ToF32
|
||||
: UnaryOp.ConvertU32ToF32,
|
||||
@ -1982,15 +2003,14 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// int to f64
|
||||
} else {
|
||||
if (fromType.is(TypeFlags.LONG)) {
|
||||
losesInformation = true;
|
||||
expr = mod.createUnary(
|
||||
expr = this.module.createUnary(
|
||||
fromType.is(TypeFlags.SIGNED)
|
||||
? UnaryOp.ConvertI64ToF64
|
||||
: UnaryOp.ConvertU64ToF64,
|
||||
expr
|
||||
);
|
||||
} else {
|
||||
expr = mod.createUnary(
|
||||
expr = this.module.createUnary(
|
||||
fromType.is(TypeFlags.SIGNED)
|
||||
? UnaryOp.ConvertI32ToF64
|
||||
: UnaryOp.ConvertU32ToF64,
|
||||
@ -2005,8 +2025,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// i64 to i32
|
||||
if (!toType.is(TypeFlags.LONG)) {
|
||||
losesInformation = true;
|
||||
expr = mod.createUnary(UnaryOp.WrapI64, expr); // discards upper bits
|
||||
expr = this.module.createUnary(UnaryOp.WrapI64, expr); // discards upper bits
|
||||
if (toType.is(TypeFlags.SMALL)) {
|
||||
expr = makeSmallIntegerWrap(expr, toType, this.module);
|
||||
}
|
||||
@ -2014,7 +2033,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// i32 to i64
|
||||
} else if (toType.is(TypeFlags.LONG)) {
|
||||
expr = mod.createUnary(toType.is(TypeFlags.SIGNED) ? UnaryOp.ExtendI32 : UnaryOp.ExtendU32, expr);
|
||||
expr = this.module.createUnary(toType.is(TypeFlags.SIGNED) ? UnaryOp.ExtendI32 : UnaryOp.ExtendU32, expr);
|
||||
|
||||
// i32 or smaller to even smaller or same size int with change of sign
|
||||
} else if (
|
||||
@ -2027,26 +2046,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
)
|
||||
)
|
||||
) {
|
||||
losesInformation = true;
|
||||
expr = makeSmallIntegerWrap(expr, toType, this.module);
|
||||
}
|
||||
|
||||
// otherwise (smaller) i32/u32 to (same size) i32/u32
|
||||
}
|
||||
|
||||
this.currentType = toType;
|
||||
return expr;
|
||||
}
|
||||
|
||||
/** Computes the common compatible type of two types. Returns `null` if incompatible. */
|
||||
computeCommonType(leftType: Type, rightType: Type): Type | null {
|
||||
if (leftType.isAssignableTo(rightType)) {
|
||||
return rightType;
|
||||
} else if (rightType.isAssignableTo(leftType)) {
|
||||
return leftType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
compileAssertionExpression(expression: AssertionExpression, contextualType: Type): ExpressionRef {
|
||||
var toType = this.program.resolveType( // reports
|
||||
expression.toType,
|
||||
@ -2061,43 +2070,15 @@ export class Compiler extends DiagnosticEmitter {
|
||||
contextualType: Type,
|
||||
wrapSmallIntegers: bool = true
|
||||
): ExpressionRef {
|
||||
|
||||
var left: ExpressionRef;
|
||||
var leftType: Type;
|
||||
var right: ExpressionRef;
|
||||
|
||||
// TODO: Currently, the common type of any binary expression is the first operand's type. This
|
||||
// differs from C and other languages where comparing an int to a long, in this order, upcasts
|
||||
// left to a long before comparison, instead of failing when trying to downcast right to an int.
|
||||
// NOTE that if we change the current behaviour, some examples, tests and wiki pages will have
|
||||
// to be updated, while compound binary operations must retain the previous behavior.
|
||||
|
||||
// var left = this.compileExpression(
|
||||
// expression.left,
|
||||
// contextualType == Type.void
|
||||
// ? Type.i32
|
||||
// : contextualType,
|
||||
// ConversionKind.NONE
|
||||
// );
|
||||
// var leftType = this.currentType;
|
||||
// var right = this.compileExpression(
|
||||
// expression.right,
|
||||
// leftType,
|
||||
// ConversionKind.NONE
|
||||
// );
|
||||
// var rightType = this.currentType;
|
||||
// var commonType = this.computeCommonType(leftType, rightType);
|
||||
// if (!commonType) {
|
||||
// this.error(
|
||||
// DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
// expression.range,
|
||||
// Token.operatorToString(expression.operator), leftType.toString(), rightType.toString()
|
||||
// );
|
||||
// this.currentType = contextualType;
|
||||
// return this.module.createUnreachable();
|
||||
// }
|
||||
var rightType: Type;
|
||||
var commonType: Type | null;
|
||||
|
||||
var condition: ExpressionRef;
|
||||
var expr: ExpressionRef;
|
||||
|
||||
var compound = false;
|
||||
var possiblyOverflows = false;
|
||||
var tempLocal: Local | null = null;
|
||||
@ -2105,16 +2086,23 @@ export class Compiler extends DiagnosticEmitter {
|
||||
switch (expression.operator) {
|
||||
|
||||
case Token.LESSTHAN:
|
||||
left = this.compileExpression(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
right = this.compileExpression(expression.right, this.currentType);
|
||||
left = this.compileExpressionRetainType(expression.left, contextualType);
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(expression.right, leftType);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, true)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "<", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
switch (commonType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
@ -2177,16 +2165,23 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.GREATERTHAN:
|
||||
left = this.compileExpression(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
right = this.compileExpression(expression.right, this.currentType);
|
||||
left = this.compileExpressionRetainType(expression.left, contextualType);
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(expression.right, leftType);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, true)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, ">", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
switch (commonType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
@ -2249,16 +2244,23 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.LESSTHAN_EQUALS:
|
||||
left = this.compileExpression(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
right = this.compileExpression(expression.right, this.currentType);
|
||||
left = this.compileExpressionRetainType(expression.left, contextualType);
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(expression.right, leftType);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, true)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "<=", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
switch (commonType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
@ -2321,16 +2323,23 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.GREATERTHAN_EQUALS:
|
||||
left = this.compileExpression(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
right = this.compileExpression(expression.right, this.currentType);
|
||||
left = this.compileExpressionRetainType(expression.left, contextualType);
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(expression.right, leftType);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, true)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, ">=", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
switch (commonType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
@ -2400,16 +2409,23 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// checking for a possible use of unary EQZ. while the most classic of all optimizations,
|
||||
// that's not what the source told us to do. for reference, `!left` emits unary EQZ.
|
||||
|
||||
left = this.compileExpression(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
right = this.compileExpression(expression.right, this.currentType);
|
||||
left = this.compileExpressionRetainType(expression.left, contextualType);
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(expression.right, leftType);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, Token.operatorToString(expression.operator), leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
switch (commonType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
@ -2459,16 +2475,23 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.EXCLAMATION_EQUALS_EQUALS:
|
||||
// TODO?
|
||||
case Token.EXCLAMATION_EQUALS:
|
||||
left = this.compileExpression(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
);
|
||||
right = this.compileExpression(expression.right, this.currentType);
|
||||
left = this.compileExpressionRetainType(expression.left, contextualType);
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(expression.right, leftType);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, Token.operatorToString(expression.operator), leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
switch (commonType.kind) {
|
||||
|
||||
case TypeKind.I8:
|
||||
case TypeKind.I16:
|
||||
@ -2521,20 +2544,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.PLUS_EQUALS:
|
||||
compound = true;
|
||||
case Token.PLUS:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
false // retains low bits of small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "+", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -2582,20 +2623,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.MINUS_EQUALS:
|
||||
compound = true;
|
||||
case Token.MINUS:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
false // retains low bits of small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "-", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -2647,20 +2706,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.ASTERISK_EQUALS:
|
||||
compound = true;
|
||||
case Token.ASTERISK:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
false // retains low bits of small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "*", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -2713,20 +2790,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.SLASH_EQUALS:
|
||||
compound = true;
|
||||
case Token.SLASH:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
true // TODO: when can division remain unwrapped? does it overflow?
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
true // ^
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "/", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -2794,20 +2889,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.PERCENT_EQUALS:
|
||||
compound = true;
|
||||
case Token.PERCENT:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
true // TODO: when can remainder remain unwrapped? may it overflow?
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
true // ^
|
||||
contextualType,
|
||||
true // TODO: when can remainder remain unwrapped? does it overflow?
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "%", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -2875,14 +2988,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.LESSTHAN_LESSTHAN_EQUALS:
|
||||
compound = true;
|
||||
case Token.LESSTHAN_LESSTHAN:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType.is(TypeFlags.FLOAT)
|
||||
? Type.i64
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
false // retains low bits of small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
@ -2933,14 +3041,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.GREATERTHAN_GREATERTHAN_EQUALS:
|
||||
compound = true;
|
||||
case Token.GREATERTHAN_GREATERTHAN:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType.is(TypeFlags.FLOAT)
|
||||
? Type.i64
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
true // must wrap small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
@ -3006,14 +3109,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS:
|
||||
compound = true;
|
||||
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType == Type.void
|
||||
? Type.u64
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
true // modifies low bits of small integers if unsigned
|
||||
);
|
||||
right = this.compileExpression(
|
||||
@ -3063,22 +3161,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.AMPERSAND_EQUALS:
|
||||
compound = true;
|
||||
case Token.AMPERSAND:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType.is(TypeFlags.FLOAT)
|
||||
? Type.i64
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
false // retains low bits of small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "&", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -3121,22 +3235,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.BAR_EQUALS:
|
||||
compound = true;
|
||||
case Token.BAR:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType.is(TypeFlags.FLOAT)
|
||||
? Type.i64
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
false // retains low bits of small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "|", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -3179,22 +3309,38 @@ export class Compiler extends DiagnosticEmitter {
|
||||
case Token.CARET_EQUALS:
|
||||
compound = true;
|
||||
case Token.CARET:
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType.is(TypeFlags.FLOAT)
|
||||
? Type.i64
|
||||
: contextualType,
|
||||
ConversionKind.NONE,
|
||||
contextualType,
|
||||
false // retains low bits of small integers
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
if (compound) {
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
this.currentType,
|
||||
ConversionKind.IMPLICIT,
|
||||
false // ^
|
||||
);
|
||||
} else {
|
||||
leftType = this.currentType;
|
||||
right = this.compileExpressionRetainType(
|
||||
expression.right,
|
||||
leftType,
|
||||
false // ^
|
||||
);
|
||||
rightType = this.currentType;
|
||||
if (commonType = Type.commonCompatible(leftType, rightType, false)) {
|
||||
left = this.convertExpression(left, leftType, commonType, ConversionKind.IMPLICIT, expression.left);
|
||||
right = this.convertExpression(right, rightType, commonType, ConversionKind.IMPLICIT, expression.right);
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode.Operator_0_cannot_be_applied_to_types_1_and_2,
|
||||
expression.range, "^", leftType.toString(), rightType.toString()
|
||||
);
|
||||
this.currentType = contextualType;
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.currentType.kind) {
|
||||
|
||||
@ -3237,12 +3383,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// logical (no overloading)
|
||||
|
||||
case Token.AMPERSAND_AMPERSAND: // left && right
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
contextualType
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
@ -3286,12 +3429,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case Token.BAR_BAR: // left || right
|
||||
left = this.compileExpression(
|
||||
left = this.compileExpressionRetainType(
|
||||
expression.left,
|
||||
contextualType == Type.void
|
||||
? Type.i32
|
||||
: contextualType,
|
||||
ConversionKind.NONE
|
||||
contextualType
|
||||
);
|
||||
right = this.compileExpression(
|
||||
expression.right,
|
||||
@ -3884,7 +4024,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.module.createI32(index);
|
||||
}
|
||||
|
||||
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): ExpressionRef {
|
||||
/**
|
||||
* Compiles an identifier in the specified context.
|
||||
* @param retainConstantType Retains the type of inlined constants if `true`, otherwise
|
||||
* precomputes them according to context.
|
||||
*/
|
||||
compileIdentifierExpression(
|
||||
expression: IdentifierExpression,
|
||||
contextualType: Type,
|
||||
retainConstantType: bool
|
||||
): ExpressionRef {
|
||||
// check special keywords first
|
||||
switch (expression.kind) {
|
||||
|
||||
@ -3948,7 +4097,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case ElementKind.LOCAL:
|
||||
if ((<Local>element).is(ElementFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Local>element, contextualType);
|
||||
return this.compileInlineConstant(<Local>element, contextualType, retainConstantType);
|
||||
}
|
||||
assert((<Local>element).index >= 0);
|
||||
this.currentType = (<Local>element).type;
|
||||
@ -3963,7 +4112,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
assert((<Global>element).type != Type.void);
|
||||
if ((<Global>element).is(ElementFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Global>element, contextualType);
|
||||
return this.compileInlineConstant(<Global>element, contextualType, retainConstantType);
|
||||
}
|
||||
this.currentType = (<Global>element).type;
|
||||
return this.module.createGetGlobal((<Global>element).internalName, this.currentType.toNativeType());
|
||||
@ -4323,7 +4472,16 @@ export class Compiler extends DiagnosticEmitter {
|
||||
return this.compileExpression(expression.expression, contextualType, ConversionKind.NONE);
|
||||
}
|
||||
|
||||
compilePropertyAccessExpression(propertyAccess: PropertyAccessExpression, contextualType: Type): ExpressionRef {
|
||||
/**
|
||||
* Compiles a property access in the specified context.
|
||||
* @param retainConstantType Retains the type of inlined constants if `true`, otherwise
|
||||
* precomputes them according to context.
|
||||
*/
|
||||
compilePropertyAccessExpression(
|
||||
propertyAccess: PropertyAccessExpression,
|
||||
contextualType: Type,
|
||||
retainConstantType: bool
|
||||
): ExpressionRef {
|
||||
var resolved = this.program.resolvePropertyAccess(propertyAccess, this.currentFunction); // reports
|
||||
if (!resolved) return this.module.createUnreachable();
|
||||
|
||||
@ -4340,7 +4498,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
assert((<Global>element).type != Type.void);
|
||||
if ((<Global>element).is(ElementFlags.INLINED)) {
|
||||
return this.compileInlineConstant(<Global>element, contextualType);
|
||||
return this.compileInlineConstant(<Global>element, contextualType, retainConstantType);
|
||||
}
|
||||
this.currentType = (<Global>element).type;
|
||||
return this.module.createGetGlobal((<Global>element).internalName, this.currentType.toNativeType());
|
||||
|
204
src/types.ts
204
src/types.ts
@ -114,7 +114,7 @@ export class Type {
|
||||
|
||||
/** Composes a class type from this type and a class. */
|
||||
asClass(classType: Class): Type {
|
||||
assert(this.kind == TypeKind.USIZE);
|
||||
assert(this.kind == TypeKind.USIZE && !this.classType);
|
||||
var ret = new Type(this.kind, this.flags & ~TypeFlags.VALUE | TypeFlags.REFERENCE, this.size);
|
||||
ret.classType = classType;
|
||||
return ret;
|
||||
@ -122,26 +122,26 @@ export class Type {
|
||||
|
||||
/** Composes a function type from this type and a function. */
|
||||
asFunction(functionType: Function): Type {
|
||||
assert(this.kind == TypeKind.U32 && !this.isReference);
|
||||
assert(this.kind == TypeKind.U32 && !this.functionType);
|
||||
var ret = new Type(this.kind, this.flags & ~TypeFlags.VALUE | TypeFlags.REFERENCE, this.size);
|
||||
ret.functionType = functionType;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Composes the respective nullable type of this type. */
|
||||
asNullable(): Type | null {
|
||||
assert(this.kind == TypeKind.USIZE);
|
||||
asNullable(): Type {
|
||||
assert(this.isReference);
|
||||
if (!this.nullableType) {
|
||||
assert(!this.is(TypeFlags.NULLABLE) && this.isReference);
|
||||
assert(!this.is(TypeFlags.NULLABLE));
|
||||
this.nullableType = new Type(this.kind, this.flags | TypeFlags.NULLABLE, this.size);
|
||||
this.nullableType.classType = this.classType;
|
||||
this.nullableType.functionType = this.functionType;
|
||||
this.nullableType.classType = this.classType; // either a class reference
|
||||
this.nullableType.functionType = this.functionType; // or a function reference
|
||||
}
|
||||
return this.nullableType;
|
||||
}
|
||||
|
||||
/** Tests if a value of this type is assignable to a target of the specified type. */
|
||||
isAssignableTo(target: Type): bool {
|
||||
isAssignableTo(target: Type, signednessIsImportant: bool = false): bool {
|
||||
var currentClass: Class | null;
|
||||
var targetClass: Class | null;
|
||||
var currentFunction: Function | null;
|
||||
@ -159,177 +159,35 @@ export class Type {
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
if (this.is(TypeFlags.INTEGER)) {
|
||||
if (target.is(TypeFlags.INTEGER)) {
|
||||
if (!signednessIsImportant || this.is(TypeFlags.SIGNED) == target.is(TypeFlags.SIGNED)) {
|
||||
return this.size <= target.size;
|
||||
}
|
||||
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;
|
||||
} else if (target.kind == TypeKind.F32) {
|
||||
return this.size <= 23; // mantissa bits
|
||||
} else if (target.kind == TypeKind.F64) {
|
||||
return this.size <= 52; // ^
|
||||
}
|
||||
} else if (this.is(TypeFlags.FLOAT)) {
|
||||
if (target.is(TypeFlags.FLOAT)) {
|
||||
return this.size <= target.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Determines the common compatible type of two types, if any. */
|
||||
static commonCompatible(left: Type, right: Type, signednessIsImportant: bool): Type | null {
|
||||
if (right.isAssignableTo(left, signednessIsImportant)) {
|
||||
return left;
|
||||
} else if (left.isAssignableTo(right, signednessIsImportant)) {
|
||||
return right;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Converts this type to its TypeScript representation. */
|
||||
toString(kindOnly: bool = false): string {
|
||||
switch (this.kind) {
|
||||
|
Reference in New Issue
Block a user