Use a special 'auto' type in type inference

This commit is contained in:
dcode 2019-06-12 08:34:39 +02:00
parent 40dac8269d
commit acda2d05c8
11 changed files with 239 additions and 216 deletions

View File

@ -648,7 +648,7 @@ export function compileCall(
checkTypeAbsent(typeArguments, reportNode, prototype) | checkTypeAbsent(typeArguments, reportNode, prototype) |
checkArgsRequired(operands, 1, reportNode, compiler) checkArgsRequired(operands, 1, reportNode, compiler)
) return module.unreachable(); ) return module.unreachable();
let expr = compiler.compileExpressionRetainType(operands[0], Type.i32); let expr = compiler.compileExpression(operands[0], Type.auto);
compiler.currentType = Type.bool; compiler.currentType = Type.bool;
return module.i32(getExpressionId(expr) == ExpressionId.Const ? 1 : 0); return module.i32(getExpressionId(expr) == ExpressionId.Const ? 1 : 0);
} }
@ -1555,6 +1555,7 @@ export function compileCall(
) return module.unreachable(); ) return module.unreachable();
let type = typeArguments![0]; let type = typeArguments![0];
let outType = ( let outType = (
contextualType != Type.auto &&
type.is(TypeFlags.INTEGER) && type.is(TypeFlags.INTEGER) &&
contextualType.is(TypeFlags.INTEGER) && contextualType.is(TypeFlags.INTEGER) &&
contextualType.size > type.size contextualType.size > type.size
@ -1965,7 +1966,7 @@ export function compileCall(
) return module.unreachable(); ) return module.unreachable();
let arg0 = typeArguments let arg0 = typeArguments
? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT) ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT)
: compiler.compileExpressionRetainType(operands[0], Type.i32); : compiler.compileExpression(operands[0], Type.auto);
let type = compiler.currentType; let type = compiler.currentType;
if (!type.isAny(TypeFlags.VALUE | TypeFlags.REFERENCE)) { if (!type.isAny(TypeFlags.VALUE | TypeFlags.REFERENCE)) {
compiler.error( compiler.error(
@ -1976,7 +1977,7 @@ export function compileCall(
} }
let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT); let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT);
let arg2 = compiler.makeIsTrueish( let arg2 = compiler.makeIsTrueish(
compiler.compileExpressionRetainType(operands[2], Type.bool), compiler.compileExpression(operands[2], Type.bool),
compiler.currentType // ^ compiler.currentType // ^
); );
compiler.currentType = type; compiler.currentType = type;
@ -2060,7 +2061,7 @@ export function compileCall(
checkArgsRequired(operands, 1, reportNode, compiler) checkArgsRequired(operands, 1, reportNode, compiler)
) return module.unreachable(); ) return module.unreachable();
let toType = typeArguments![0]; let toType = typeArguments![0];
let arg0 = compiler.compileExpressionRetainType(operands[0], toType); let arg0 = compiler.compileExpression(operands[0], toType);
let fromType = compiler.currentType; let fromType = compiler.currentType;
compiler.currentType = toType; compiler.currentType = toType;
if (fromType.size != toType.size) { if (fromType.size != toType.size) {
@ -2085,7 +2086,7 @@ export function compileCall(
} }
let arg0 = typeArguments let arg0 = typeArguments
? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP) ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP)
: compiler.compileExpressionRetainType(operands[0], Type.bool, ContextualFlags.WRAP); : compiler.compileExpression(operands[0], Type.bool, ContextualFlags.WRAP);
let type = compiler.currentType; let type = compiler.currentType;
compiler.currentType = type.nonNullableType; compiler.currentType = type.nonNullableType;
@ -2272,7 +2273,7 @@ export function compileCall(
checkArgsOptional(operands, 1, i32.MAX_VALUE, reportNode, compiler) checkArgsOptional(operands, 1, i32.MAX_VALUE, reportNode, compiler)
) return module.unreachable(); ) return module.unreachable();
let returnType = typeArguments ? typeArguments[0] : contextualType; let returnType = typeArguments ? typeArguments[0] : contextualType;
let arg0 = compiler.compileExpressionRetainType(operands[0], Type.u32); let arg0 = compiler.compileExpression(operands[0], Type.u32);
let arg0Type = compiler.currentType; let arg0Type = compiler.currentType;
if (!( if (!(
arg0Type == Type.u32 || // either plain index arg0Type == Type.u32 || // either plain index
@ -2290,7 +2291,7 @@ export function compileCall(
let parameterTypes = new Array<Type>(numOperands); let parameterTypes = new Array<Type>(numOperands);
let nativeParamTypes = new Array<NativeType>(numOperands); let nativeParamTypes = new Array<NativeType>(numOperands);
for (let i = 0; i < numOperands; ++i) { for (let i = 0; i < numOperands; ++i) {
operandExprs[i] = compiler.compileExpressionRetainType(operands[1 + i], Type.i32); operandExprs[i] = compiler.compileExpression(operands[1 + i], Type.i32);
let operandType = compiler.currentType; let operandType = compiler.currentType;
parameterTypes[i] = operandType; parameterTypes[i] = operandType;
nativeParamTypes[i] = operandType.toNativeType(); nativeParamTypes[i] = operandType.toNativeType();
@ -4232,21 +4233,17 @@ function evaluateConstantType(
return typeArguments[0]; return typeArguments[0];
} }
if (operands.length == 1) { // optional type argument if (operands.length == 1) { // optional type argument
if (typeArguments) { if (typeArguments !== null && typeArguments.length) {
if (typeArguments.length == 1) { if (typeArguments.length > 1) {
compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT); compiler.error(
} else { DiagnosticCode.Expected_0_type_arguments_but_got_1,
if (typeArguments.length) { reportNode.typeArgumentsRange, "1", typeArguments.length.toString(10)
compiler.error( );
DiagnosticCode.Expected_0_type_arguments_but_got_1, return null;
reportNode.typeArgumentsRange, "1", typeArguments.length.toString(10)
);
return null;
}
compiler.compileExpressionRetainType(operands[0], Type.i32);
} }
compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT);
} else { } else {
compiler.compileExpressionRetainType(operands[0], Type.i32); compiler.compileExpression(operands[0], Type.auto);
} }
return compiler.currentType; return compiler.currentType;
} }

View File

@ -248,10 +248,8 @@ export const enum ContextualFlags {
WILL_DROP = 1 << 3, WILL_DROP = 1 << 3,
/** Value is known to be immediately assigned to a retaining target. */ /** Value is known to be immediately assigned to a retaining target. */
SKIP_AUTORELEASE = 1 << 4, SKIP_AUTORELEASE = 1 << 4,
/** Is the last statement in a function body. */
LAST_IN_BODY = 1 << 5,
/** Data can be compiled statically. */ /** Data can be compiled statically. */
STATIC_CAPABLE = 1 << 6 STATIC_CAPABLE = 1 << 5
} }
/** Runtime features to be activated by the compiler. */ /** Runtime features to be activated by the compiler. */
@ -814,7 +812,7 @@ export class Compiler extends DiagnosticEmitter {
if (global.hasDecorator(DecoratorFlags.LAZY)) { if (global.hasDecorator(DecoratorFlags.LAZY)) {
this.currentFlow = global.file.startFunction.flow; this.currentFlow = global.file.startFunction.flow;
} }
initExpr = this.compileExpression(initializerNode, Type.i32, // reports initExpr = this.compileExpression(initializerNode, Type.auto, // reports
ContextualFlags.WRAP | ContextualFlags.SKIP_AUTORELEASE ContextualFlags.WRAP | ContextualFlags.SKIP_AUTORELEASE
); );
if (this.skippedAutoreleases.has(initExpr)) initAutoreleaseSkipped = true; if (this.skippedAutoreleases.has(initExpr)) initAutoreleaseSkipped = true;
@ -1686,70 +1684,70 @@ export class Compiler extends DiagnosticEmitter {
compileStatement( compileStatement(
statement: Statement, statement: Statement,
contextualFlags: ContextualFlags = ContextualFlags.NONE isLastInBody: bool = false
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
var stmt: ExpressionRef; var stmt: ExpressionRef;
switch (statement.kind) { switch (statement.kind) {
case NodeKind.BLOCK: { case NodeKind.BLOCK: {
stmt = this.compileBlockStatement(<BlockStatement>statement, contextualFlags); stmt = this.compileBlockStatement(<BlockStatement>statement);
break; break;
} }
case NodeKind.BREAK: { case NodeKind.BREAK: {
stmt = this.compileBreakStatement(<BreakStatement>statement, contextualFlags); stmt = this.compileBreakStatement(<BreakStatement>statement);
break; break;
} }
case NodeKind.CONTINUE: { case NodeKind.CONTINUE: {
stmt = this.compileContinueStatement(<ContinueStatement>statement, contextualFlags); stmt = this.compileContinueStatement(<ContinueStatement>statement);
break; break;
} }
case NodeKind.DO: { case NodeKind.DO: {
stmt = this.compileDoStatement(<DoStatement>statement, contextualFlags); stmt = this.compileDoStatement(<DoStatement>statement);
break; break;
} }
case NodeKind.EMPTY: { case NodeKind.EMPTY: {
stmt = this.compileEmptyStatement(<EmptyStatement>statement, contextualFlags); stmt = this.compileEmptyStatement(<EmptyStatement>statement);
break; break;
} }
case NodeKind.EXPRESSION: { case NodeKind.EXPRESSION: {
stmt = this.compileExpressionStatement(<ExpressionStatement>statement, contextualFlags); stmt = this.compileExpressionStatement(<ExpressionStatement>statement);
break; break;
} }
case NodeKind.FOR: { case NodeKind.FOR: {
stmt = this.compileForStatement(<ForStatement>statement, contextualFlags); stmt = this.compileForStatement(<ForStatement>statement);
break; break;
} }
case NodeKind.IF: { case NodeKind.IF: {
stmt = this.compileIfStatement(<IfStatement>statement, contextualFlags); stmt = this.compileIfStatement(<IfStatement>statement);
break; break;
} }
case NodeKind.RETURN: { case NodeKind.RETURN: {
stmt = this.compileReturnStatement(<ReturnStatement>statement, contextualFlags); stmt = this.compileReturnStatement(<ReturnStatement>statement, isLastInBody);
break; break;
} }
case NodeKind.SWITCH: { case NodeKind.SWITCH: {
stmt = this.compileSwitchStatement(<SwitchStatement>statement, contextualFlags); stmt = this.compileSwitchStatement(<SwitchStatement>statement);
break; break;
} }
case NodeKind.THROW: { case NodeKind.THROW: {
stmt = this.compileThrowStatement(<ThrowStatement>statement, contextualFlags); stmt = this.compileThrowStatement(<ThrowStatement>statement);
break; break;
} }
case NodeKind.TRY: { case NodeKind.TRY: {
stmt = this.compileTryStatement(<TryStatement>statement, contextualFlags); stmt = this.compileTryStatement(<TryStatement>statement);
break; break;
} }
case NodeKind.VARIABLE: { case NodeKind.VARIABLE: {
stmt = this.compileVariableStatement(<VariableStatement>statement, contextualFlags); stmt = this.compileVariableStatement(<VariableStatement>statement);
if (!stmt) stmt = module.nop(); if (!stmt) stmt = module.nop();
break; break;
} }
case NodeKind.VOID: { case NodeKind.VOID: {
stmt = this.compileVoidStatement(<VoidStatement>statement, contextualFlags); stmt = this.compileVoidStatement(<VoidStatement>statement);
break; break;
} }
case NodeKind.WHILE: { case NodeKind.WHILE: {
stmt = this.compileWhileStatement(<WhileStatement>statement, contextualFlags); stmt = this.compileWhileStatement(<WhileStatement>statement);
break; break;
} }
case NodeKind.TYPEDECLARATION: { case NodeKind.TYPEDECLARATION: {
@ -1783,11 +1781,7 @@ export class Compiler extends DiagnosticEmitter {
var module = this.module; var module = this.module;
var flow = this.currentFlow; var flow = this.currentFlow;
for (let i = 0; i < numStatements; ++i) { for (let i = 0; i < numStatements; ++i) {
let stmt = this.compileStatement(statements[i], let stmt = this.compileStatement(statements[i], isBody && i == numStatements - 1);
isBody && i == numStatements - 1
? ContextualFlags.LAST_IN_BODY
: ContextualFlags.NONE
);
switch (getExpressionId(stmt)) { switch (getExpressionId(stmt)) {
case ExpressionId.Block: { case ExpressionId.Block: {
if (!getBlockName(stmt)) { if (!getBlockName(stmt)) {
@ -1808,8 +1802,7 @@ export class Compiler extends DiagnosticEmitter {
} }
compileBlockStatement( compileBlockStatement(
statement: BlockStatement, statement: BlockStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var statements = statement.statements; var statements = statement.statements;
var outerFlow = this.currentFlow; var outerFlow = this.currentFlow;
@ -1825,8 +1818,7 @@ export class Compiler extends DiagnosticEmitter {
} }
compileBreakStatement( compileBreakStatement(
statement: BreakStatement, statement: BreakStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
if (statement.label) { if (statement.label) {
@ -1859,8 +1851,7 @@ export class Compiler extends DiagnosticEmitter {
} }
compileContinueStatement( compileContinueStatement(
statement: ContinueStatement, statement: ContinueStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
var label = statement.label; var label = statement.label;
@ -1895,8 +1886,7 @@ export class Compiler extends DiagnosticEmitter {
} }
compileDoStatement( compileDoStatement(
statement: DoStatement, statement: DoStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
@ -1967,24 +1957,19 @@ export class Compiler extends DiagnosticEmitter {
} }
compileEmptyStatement( compileEmptyStatement(
statement: EmptyStatement, statement: EmptyStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
return this.module.nop(); return this.module.nop();
} }
compileExpressionStatement( compileExpressionStatement(
statement: ExpressionStatement, statement: ExpressionStatement
contextualFlags: ContextualFlags,
): ExpressionRef { ): ExpressionRef {
return this.compileExpression(statement.expression, Type.void, return this.compileExpression(statement.expression, Type.void, ContextualFlags.IMPLICIT);
contextualFlags | ContextualFlags.EXPLICIT | ContextualFlags.WILL_DROP
);
} }
compileForStatement( compileForStatement(
statement: ForStatement, statement: ForStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
@ -2005,7 +1990,7 @@ export class Compiler extends DiagnosticEmitter {
if (statement.condition) { if (statement.condition) {
condExpr = module.precomputeExpression( condExpr = module.precomputeExpression(
this.makeIsTrueish( this.makeIsTrueish(
this.compileExpressionRetainType(<Expression>statement.condition, Type.bool), this.compileExpression(<Expression>statement.condition, Type.bool),
this.currentType this.currentType
) )
); );
@ -2113,8 +2098,7 @@ export class Compiler extends DiagnosticEmitter {
} }
compileIfStatement( compileIfStatement(
statement: IfStatement, statement: IfStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
var ifTrue = statement.ifTrue; var ifTrue = statement.ifTrue;
@ -2124,7 +2108,7 @@ export class Compiler extends DiagnosticEmitter {
// The condition doesn't initiate a branch yet // The condition doesn't initiate a branch yet
var condExpr = module.precomputeExpression( var condExpr = module.precomputeExpression(
this.makeIsTrueish( this.makeIsTrueish(
this.compileExpressionRetainType(statement.condition, Type.bool), this.compileExpression(statement.condition, Type.bool),
this.currentType this.currentType
) )
); );
@ -2189,7 +2173,7 @@ export class Compiler extends DiagnosticEmitter {
compileReturnStatement( compileReturnStatement(
statement: ReturnStatement, statement: ReturnStatement,
contextualFlags: ContextualFlags isLastInBody: bool
): ExpressionRef { ): ExpressionRef {
var module = this.module; var module = this.module;
var expr: ExpressionRef = 0; var expr: ExpressionRef = 0;
@ -2256,7 +2240,7 @@ export class Compiler extends DiagnosticEmitter {
flow.freeScopedLocals(); flow.freeScopedLocals();
// If the last statement anyway, make it the block's return value // If the last statement anyway, make it the block's return value
if ((contextualFlags & ContextualFlags.LAST_IN_BODY) != 0 && expr && returnType != Type.void) { if (isLastInBody && expr && returnType != Type.void) {
if (!stmts.length) return expr; if (!stmts.length) return expr;
stmts.push(expr); stmts.push(expr);
return module.block(null, stmts, returnType.toNativeType()); return module.block(null, stmts, returnType.toNativeType());
@ -2276,7 +2260,9 @@ export class Compiler extends DiagnosticEmitter {
return module.block(null, stmts); return module.block(null, stmts);
} }
compileSwitchStatement(statement: SwitchStatement, contextualFlags: ContextualFlags): ExpressionRef { compileSwitchStatement(
statement: SwitchStatement
): ExpressionRef {
var module = this.module; var module = this.module;
var cases = statement.cases; var cases = statement.cases;
@ -2389,8 +2375,7 @@ export class Compiler extends DiagnosticEmitter {
} }
compileThrowStatement( compileThrowStatement(
statement: ThrowStatement, statement: ThrowStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var flow = this.currentFlow; var flow = this.currentFlow;
@ -2413,8 +2398,7 @@ export class Compiler extends DiagnosticEmitter {
} }
compileTryStatement( compileTryStatement(
statement: TryStatement, statement: TryStatement
contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
// TODO: can't yet support something like: try { return ... } finally { ... } // TODO: can't yet support something like: try { return ... } finally { ... }
// worthwhile to investigate lowering returns to block results (here)? // worthwhile to investigate lowering returns to block results (here)?
@ -2426,7 +2410,9 @@ export class Compiler extends DiagnosticEmitter {
} }
/** Compiles a variable statement. Returns `0` if an initializer is not necessary. */ /** Compiles a variable statement. Returns `0` if an initializer is not necessary. */
compileVariableStatement(statement: VariableStatement, contextualFlags: ContextualFlags): ExpressionRef { compileVariableStatement(
statement: VariableStatement
): ExpressionRef {
var module = this.module; var module = this.module;
var declarations = statement.declarations; var declarations = statement.declarations;
var numDeclarations = declarations.length; var numDeclarations = declarations.length;
@ -2458,7 +2444,7 @@ export class Compiler extends DiagnosticEmitter {
// Otherwise infer type from initializer // Otherwise infer type from initializer
} else if (declaration.initializer) { } else if (declaration.initializer) {
initExpr = this.compileExpressionRetainType(declaration.initializer, Type.void, initExpr = this.compileExpression(declaration.initializer, Type.auto,
ContextualFlags.SKIP_AUTORELEASE ContextualFlags.SKIP_AUTORELEASE
); // reports ); // reports
initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr); initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr);
@ -2617,20 +2603,24 @@ export class Compiler extends DiagnosticEmitter {
: flatten(module, initializers, NativeType.None); : flatten(module, initializers, NativeType.None);
} }
compileVoidStatement(statement: VoidStatement, contextualFlags: ContextualFlags): ExpressionRef { compileVoidStatement(
statement: VoidStatement
): ExpressionRef {
return this.compileExpression(statement.expression, Type.void, return this.compileExpression(statement.expression, Type.void,
ContextualFlags.EXPLICIT | ContextualFlags.WILL_DROP ContextualFlags.EXPLICIT | ContextualFlags.WILL_DROP
); );
} }
compileWhileStatement(statement: WhileStatement, contextualFlags: ContextualFlags): ExpressionRef { compileWhileStatement(
statement: WhileStatement
): ExpressionRef {
var module = this.module; var module = this.module;
var outerFlow = this.currentFlow; var outerFlow = this.currentFlow;
// Compile condition // Compile condition
var condExpr = module.precomputeExpression( var condExpr = module.precomputeExpression(
this.makeIsTrueish( this.makeIsTrueish(
this.compileExpressionRetainType(statement.condition, Type.bool), this.compileExpression(statement.condition, Type.bool),
this.currentType this.currentType
) )
); );
@ -2885,20 +2875,6 @@ export class Compiler extends DiagnosticEmitter {
return expr; return expr;
} }
/** Compiles an expression while retaining the type, that is not void, it ultimately compiles to. */
compileExpressionRetainType(
expression: Expression,
contextualType: Type,
contextualFlags: ContextualFlags = ContextualFlags.NONE
): ExpressionRef {
return this.compileExpression(expression,
contextualType == Type.void
? Type.i32 // default to i32
: contextualType,
(contextualFlags & ~(ContextualFlags.IMPLICIT | ContextualFlags.EXPLICIT))
);
}
/** Compiles and precomputes an expression, possibly yielding a costant value. */ /** Compiles and precomputes an expression, possibly yielding a costant value. */
precomputeExpression( precomputeExpression(
expression: Expression, expression: Expression,
@ -3108,6 +3084,7 @@ export class Compiler extends DiagnosticEmitter {
contextualType: Type, contextualType: Type,
contextualFlags: ContextualFlags contextualFlags: ContextualFlags
): ExpressionRef { ): ExpressionRef {
var inheritedFlags = contextualFlags & ~(ContextualFlags.IMPLICIT | ContextualFlags.EXPLICIT);
switch (expression.assertionKind) { switch (expression.assertionKind) {
case AssertionKind.PREFIX: case AssertionKind.PREFIX:
case AssertionKind.AS: { case AssertionKind.AS: {
@ -3118,13 +3095,11 @@ export class Compiler extends DiagnosticEmitter {
flow.contextualTypeArguments flow.contextualTypeArguments
); );
if (!toType) return this.module.unreachable(); if (!toType) return this.module.unreachable();
return this.compileExpression(expression.expression, toType, return this.compileExpression(expression.expression, toType, inheritedFlags | ContextualFlags.EXPLICIT);
contextualFlags | ContextualFlags.EXPLICIT
);
} }
case AssertionKind.NONNULL: { case AssertionKind.NONNULL: {
assert(!expression.toType); assert(!expression.toType);
let expr = this.compileExpressionRetainType(expression.expression, contextualType); let expr = this.compileExpression(expression.expression, contextualType.exceptVoid, inheritedFlags);
let type = this.currentType; let type = this.currentType;
if (this.currentFlow.isNonnull(expr, type)) { if (this.currentFlow.isNonnull(expr, type)) {
this.info( this.info(
@ -3177,7 +3152,7 @@ export class Compiler extends DiagnosticEmitter {
var operator = expression.operator; var operator = expression.operator;
switch (operator) { switch (operator) {
case Token.LESSTHAN: { case Token.LESSTHAN: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3197,7 +3172,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.unreachable(); return this.module.unreachable();
} }
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, true)) { if (commonType = Type.commonDenominator(leftType, rightType, true)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3277,7 +3252,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case Token.GREATERTHAN: { case Token.GREATERTHAN: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3297,7 +3272,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.unreachable(); return this.module.unreachable();
} }
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, true)) { if (commonType = Type.commonDenominator(leftType, rightType, true)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3377,7 +3352,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case Token.LESSTHAN_EQUALS: { case Token.LESSTHAN_EQUALS: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3397,7 +3372,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.unreachable(); return this.module.unreachable();
} }
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, true)) { if (commonType = Type.commonDenominator(leftType, rightType, true)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3477,7 +3452,7 @@ export class Compiler extends DiagnosticEmitter {
break; break;
} }
case Token.GREATERTHAN_EQUALS: { case Token.GREATERTHAN_EQUALS: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3497,7 +3472,7 @@ export class Compiler extends DiagnosticEmitter {
return this.module.unreachable(); return this.module.unreachable();
} }
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, true)) { if (commonType = Type.commonDenominator(leftType, rightType, true)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3584,7 +3559,7 @@ export class Compiler extends DiagnosticEmitter {
// checking for a possible use of unary EQZ. while the most classic of all optimizations, // 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. // that's not what the source told us to do. for reference, `!left` emits unary EQZ.
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3600,7 +3575,7 @@ export class Compiler extends DiagnosticEmitter {
// still allow '==' with references // still allow '==' with references
} }
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3672,7 +3647,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.EXCLAMATION_EQUALS_EQUALS: case Token.EXCLAMATION_EQUALS_EQUALS:
case Token.EXCLAMATION_EQUALS: { case Token.EXCLAMATION_EQUALS: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3688,7 +3663,7 @@ export class Compiler extends DiagnosticEmitter {
// still allow '!=' with references // still allow '!=' with references
} }
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3763,7 +3738,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.PLUS_EQUALS: compound = true; case Token.PLUS_EQUALS: compound = true;
case Token.PLUS: { case Token.PLUS: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3786,7 +3761,7 @@ export class Compiler extends DiagnosticEmitter {
if (compound) { if (compound) {
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3852,7 +3827,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.MINUS_EQUALS: compound = true; case Token.MINUS_EQUALS: compound = true;
case Token.MINUS: { case Token.MINUS: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3876,7 +3851,7 @@ export class Compiler extends DiagnosticEmitter {
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
rightType = this.currentType; rightType = this.currentType;
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -3942,7 +3917,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.ASTERISK_EQUALS: compound = true; case Token.ASTERISK_EQUALS: compound = true;
case Token.ASTERISK: { case Token.ASTERISK: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -3966,7 +3941,7 @@ export class Compiler extends DiagnosticEmitter {
leftExpr = this.ensureSmallIntegerWrap(leftExpr, leftType); leftExpr = this.ensureSmallIntegerWrap(leftExpr, leftType);
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -4032,7 +4007,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.ASTERISK_ASTERISK_EQUALS: compound = true; case Token.ASTERISK_ASTERISK_EQUALS: compound = true;
case Token.ASTERISK_ASTERISK: { case Token.ASTERISK_ASTERISK: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4124,7 +4099,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.SLASH_EQUALS: compound = true; case Token.SLASH_EQUALS: compound = true;
case Token.SLASH: { case Token.SLASH: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4149,7 +4124,7 @@ export class Compiler extends DiagnosticEmitter {
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
rightType = this.currentType; rightType = this.currentType;
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -4233,7 +4208,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.PERCENT_EQUALS: compound = true; case Token.PERCENT_EQUALS: compound = true;
case Token.PERCENT: { case Token.PERCENT: {
leftExpr = this.compileExpressionRetainType(left, contextualType); leftExpr = this.compileExpression(left, contextualType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4258,7 +4233,7 @@ export class Compiler extends DiagnosticEmitter {
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
rightType = this.currentType; rightType = this.currentType;
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -4399,7 +4374,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.LESSTHAN_LESSTHAN_EQUALS: compound = true; case Token.LESSTHAN_LESSTHAN_EQUALS: compound = true;
case Token.LESSTHAN_LESSTHAN: { case Token.LESSTHAN_LESSTHAN: {
leftExpr = this.compileExpressionRetainType(left, contextualType.intType); leftExpr = this.compileExpression(left, contextualType.intType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4465,7 +4440,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.GREATERTHAN_GREATERTHAN_EQUALS: compound = true; case Token.GREATERTHAN_GREATERTHAN_EQUALS: compound = true;
case Token.GREATERTHAN_GREATERTHAN: { case Token.GREATERTHAN_GREATERTHAN: {
leftExpr = this.compileExpressionRetainType(left, contextualType.intType); leftExpr = this.compileExpression(left, contextualType.intType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4553,7 +4528,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: compound = true; case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: compound = true;
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: { case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: {
leftExpr = this.compileExpressionRetainType(left, contextualType.intType); leftExpr = this.compileExpression(left, contextualType.intType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4622,7 +4597,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.AMPERSAND_EQUALS: compound = true; case Token.AMPERSAND_EQUALS: compound = true;
case Token.AMPERSAND: { case Token.AMPERSAND: {
leftExpr = this.compileExpressionRetainType(left, contextualType.intType); leftExpr = this.compileExpression(left, contextualType.intType);
leftType = this.currentType; leftType = this.currentType;
// check operator overloadd // check operator overloadd
@ -4646,7 +4621,7 @@ export class Compiler extends DiagnosticEmitter {
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
rightType = this.currentType; rightType = this.currentType;
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -4712,7 +4687,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.BAR_EQUALS: compound = true; case Token.BAR_EQUALS: compound = true;
case Token.BAR: { case Token.BAR: {
leftExpr = this.compileExpressionRetainType(left, contextualType.intType); leftExpr = this.compileExpression(left, contextualType.intType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4736,7 +4711,7 @@ export class Compiler extends DiagnosticEmitter {
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
rightType = this.currentType; rightType = this.currentType;
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -4805,7 +4780,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.CARET_EQUALS: compound = true; case Token.CARET_EQUALS: compound = true;
case Token.CARET: { case Token.CARET: {
leftExpr = this.compileExpressionRetainType(left, contextualType.intType); leftExpr = this.compileExpression(left, contextualType.intType);
leftType = this.currentType; leftType = this.currentType;
// check operator overload // check operator overload
@ -4829,7 +4804,7 @@ export class Compiler extends DiagnosticEmitter {
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT);
rightType = this.currentType; rightType = this.currentType;
} else { } else {
rightExpr = this.compileExpressionRetainType(right, leftType); rightExpr = this.compileExpression(right, leftType);
rightType = this.currentType; rightType = this.currentType;
if (commonType = Type.commonDenominator(leftType, rightType, false)) { if (commonType = Type.commonDenominator(leftType, rightType, false)) {
leftExpr = this.convertExpression(leftExpr, leftExpr = this.convertExpression(leftExpr,
@ -4901,13 +4876,14 @@ export class Compiler extends DiagnosticEmitter {
case Token.AMPERSAND_AMPERSAND: { // left && right -> (t = left) ? right : t case Token.AMPERSAND_AMPERSAND: { // left && right -> (t = left) ? right : t
let flow = this.currentFlow; let flow = this.currentFlow;
leftExpr = this.compileExpressionRetainType(left, contextualType, contextualFlags); let inheritedFlags = contextualFlags & (ContextualFlags.SKIP_AUTORELEASE | ContextualFlags.WRAP);
leftExpr = this.compileExpression(left, contextualType.exceptVoid, inheritedFlags);
leftType = this.currentType; leftType = this.currentType;
let rightFlow = flow.fork(); let rightFlow = flow.fork();
this.currentFlow = rightFlow; this.currentFlow = rightFlow;
rightFlow.inheritNonnullIfTrue(leftExpr); rightFlow.inheritNonnullIfTrue(leftExpr);
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT | (contextualFlags & ~ContextualFlags.WILL_DROP)); rightExpr = this.compileExpression(right, leftType, inheritedFlags | ContextualFlags.IMPLICIT);
rightType = leftType; rightType = leftType;
// simplify if only interested in true or false // simplify if only interested in true or false
@ -4998,13 +4974,14 @@ export class Compiler extends DiagnosticEmitter {
} }
case Token.BAR_BAR: { // left || right -> ((t = left) ? t : right) case Token.BAR_BAR: { // left || right -> ((t = left) ? t : right)
let flow = this.currentFlow; let flow = this.currentFlow;
leftExpr = this.compileExpressionRetainType(left, contextualType, contextualFlags); let inheritedFlags = contextualFlags & (ContextualFlags.SKIP_AUTORELEASE | ContextualFlags.WRAP);
leftExpr = this.compileExpression(left, contextualType.exceptVoid, inheritedFlags);
leftType = this.currentType; leftType = this.currentType;
let rightFlow = flow.fork(); let rightFlow = flow.fork();
this.currentFlow = rightFlow; this.currentFlow = rightFlow;
rightFlow.inheritNonnullIfFalse(leftExpr); rightFlow.inheritNonnullIfFalse(leftExpr);
rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT | contextualFlags); rightExpr = this.compileExpression(right, leftType, inheritedFlags | ContextualFlags.IMPLICIT);
rightType = leftType; rightType = leftType;
// simplify if only interested in true or false // simplify if only interested in true or false
@ -5326,11 +5303,8 @@ export class Compiler extends DiagnosticEmitter {
} }
return this.makeFieldAssignment(<Field>target, return this.makeFieldAssignment(<Field>target,
valueExpr, valueExpr,
this.compileExpressionRetainType( // FIXME: explicit type (currently fails due to missing null checking)
assert(thisExpression), this.compileExpression(assert(thisExpression), this.options.usizeType),
// FIXME: explicit type (currently fails due to missing null checking)
this.options.usizeType
),
tee tee
); );
} }
@ -5371,20 +5345,14 @@ export class Compiler extends DiagnosticEmitter {
} }
// call just the setter if the return value isn't of interest // call just the setter if the return value isn't of interest
if (!tee) { if (!tee) {
let thisExpr = this.compileExpressionRetainType( let thisExpr = this.compileExpression(assert(thisExpression), this.options.usizeType);
assert(thisExpression),
this.options.usizeType
);
return this.makeCallDirect(setterInstance, [ thisExpr, valueExpr ], valueExpression); return this.makeCallDirect(setterInstance, [ thisExpr, valueExpr ], valueExpression);
} }
// otherwise call the setter first, then the getter // otherwise call the setter first, then the getter
let getterInstance = assert((<Property>target).getterInstance); // must be present let getterInstance = assert((<Property>target).getterInstance); // must be present
let returnType = getterInstance.signature.returnType; let returnType = getterInstance.signature.returnType;
let nativeReturnType = returnType.toNativeType(); let nativeReturnType = returnType.toNativeType();
let thisExpr = this.compileExpressionRetainType( let thisExpr = this.compileExpression(assert(thisExpression), this.options.usizeType);
assert(thisExpression),
this.options.usizeType
);
let tempLocal = flow.getAndFreeTempLocal(returnType); let tempLocal = flow.getAndFreeTempLocal(returnType);
let tempLocalIndex = tempLocal.index; let tempLocalIndex = tempLocal.index;
return module.block(null, [ return module.block(null, [
@ -5419,10 +5387,7 @@ export class Compiler extends DiagnosticEmitter {
return module.unreachable(); return module.unreachable();
} }
let targetType = (<Class>target).type; let targetType = (<Class>target).type;
let thisExpr = this.compileExpressionRetainType( let thisExpr = this.compileExpression(assert(thisExpression), this.options.usizeType);
assert(thisExpression),
this.options.usizeType
);
let elementExpr = this.compileExpression(indexExpression, Type.i32, ContextualFlags.IMPLICIT); let elementExpr = this.compileExpression(indexExpression, Type.i32, ContextualFlags.IMPLICIT);
if (tee) { if (tee) {
let tempLocalTarget = flow.getTempLocal(targetType); let tempLocalTarget = flow.getTempLocal(targetType);
@ -5868,7 +5833,7 @@ export class Compiler extends DiagnosticEmitter {
if (templateName !== null && inferredTypes.has(templateName)) { if (templateName !== null && inferredTypes.has(templateName)) {
let inferredType = inferredTypes.get(templateName); let inferredType = inferredTypes.get(templateName);
if (inferredType) { if (inferredType) {
argumentExprs[i] = this.compileExpressionRetainType(argumentExpression, inferredType); argumentExprs[i] = this.compileExpression(argumentExpression, inferredType);
let commonType: Type | null; let commonType: Type | null;
if (!(commonType = Type.commonDenominator(inferredType, this.currentType, true))) { if (!(commonType = Type.commonDenominator(inferredType, this.currentType, true))) {
if (!(commonType = Type.commonDenominator(inferredType, this.currentType, false))) { if (!(commonType = Type.commonDenominator(inferredType, this.currentType, false))) {
@ -5881,7 +5846,7 @@ export class Compiler extends DiagnosticEmitter {
} }
inferredType = commonType; inferredType = commonType;
} else { } else {
argumentExprs[i] = this.compileExpressionRetainType(argumentExpression, Type.i32); argumentExprs[i] = this.compileExpression(argumentExpression, Type.auto);
inferredType = this.currentType; inferredType = this.currentType;
// ++numInferred; // ++numInferred;
} }
@ -5921,10 +5886,7 @@ export class Compiler extends DiagnosticEmitter {
// compile 'this' expression if an instance method // compile 'this' expression if an instance method
let thisExpr: ExpressionRef = 0; let thisExpr: ExpressionRef = 0;
if (instance.is(CommonFlags.INSTANCE)) { if (instance.is(CommonFlags.INSTANCE)) {
thisExpr = this.compileExpressionRetainType( thisExpr = this.compileExpression(assert(this.resolver.currentThisExpression), this.options.usizeType);
assert(this.resolver.currentThisExpression),
this.options.usizeType
);
} }
return this.compileCallDirect( return this.compileCallDirect(
@ -5969,10 +5931,7 @@ export class Compiler extends DiagnosticEmitter {
let type = (<Field>target).type; let type = (<Field>target).type;
if (signature = type.signatureReference) { if (signature = type.signatureReference) {
let thisExpression = assert(this.resolver.currentThisExpression); let thisExpression = assert(this.resolver.currentThisExpression);
let thisExpr = this.compileExpressionRetainType( let thisExpr = this.compileExpression(thisExpression, this.options.usizeType);
thisExpression,
this.options.usizeType
);
indexArg = module.load( indexArg = module.load(
4, 4,
false, false,
@ -6013,10 +5972,7 @@ export class Compiler extends DiagnosticEmitter {
case ElementKind.PROPERTY: { // instance property case ElementKind.PROPERTY: { // instance property
let getterInstance = assert((<Property>target).getterInstance); let getterInstance = assert((<Property>target).getterInstance);
indexArg = this.compileCallDirect(getterInstance, [], expression.expression, indexArg = this.compileCallDirect(getterInstance, [], expression.expression,
this.compileExpressionRetainType( this.compileExpression(assert(this.resolver.currentThisExpression), this.options.usizeType)
assert(this.resolver.currentThisExpression),
this.options.usizeType
)
); );
signature = this.currentType.signatureReference; signature = this.currentType.signatureReference;
if (!signature) { if (!signature) {
@ -6579,6 +6535,7 @@ export class Compiler extends DiagnosticEmitter {
/** Makes an automatic release call at the end of the current flow. */ /** Makes an automatic release call at the end of the current flow. */
makeAutorelease(expr: ExpressionRef, flow: Flow = this.currentFlow): ExpressionRef { makeAutorelease(expr: ExpressionRef, flow: Flow = this.currentFlow): ExpressionRef {
// FIXME: loses track of nonNull state?
return this.module.local_tee(flow.getAutoreleaseLocal(this.options.usizeType).index, expr); return this.module.local_tee(flow.getAutoreleaseLocal(this.options.usizeType).index, expr);
} }
@ -7325,7 +7282,7 @@ export class Compiler extends DiagnosticEmitter {
// time of implementation, this seemed more useful because dynamic rhs expressions are not // time of implementation, this seemed more useful because dynamic rhs expressions are not
// possible in AS anyway. also note that the code generated below must preserve side-effects of // possible in AS anyway. also note that the code generated below must preserve side-effects of
// the LHS expression even when the result is a constant, i.e. return a block dropping `expr`. // the LHS expression even when the result is a constant, i.e. return a block dropping `expr`.
var expr = this.compileExpressionRetainType(expression.expression, this.options.usizeType); var expr = this.compileExpression(expression.expression, this.options.usizeType);
var actualType = this.currentType; var actualType = this.currentType;
var expectedType = this.resolver.resolveType(expression.isType, this.currentFlow.actualFunction); var expectedType = this.resolver.resolveType(expression.isType, this.currentFlow.actualFunction);
this.currentType = Type.bool; this.currentType = Type.bool;
@ -7986,10 +7943,7 @@ export class Compiler extends DiagnosticEmitter {
} }
case ElementKind.FIELD: { // instance field case ElementKind.FIELD: { // instance field
assert((<Field>target).memoryOffset >= 0); assert((<Field>target).memoryOffset >= 0);
let thisExpr = this.compileExpressionRetainType( let thisExpr = this.compileExpression(assert(this.resolver.currentThisExpression), this.options.usizeType);
assert(this.resolver.currentThisExpression),
this.options.usizeType
);
this.currentType = (<Field>target).type; this.currentType = (<Field>target).type;
return module.load( return module.load(
(<Field>target).type.byteSize, (<Field>target).type.byteSize,
@ -8010,10 +7964,7 @@ export class Compiler extends DiagnosticEmitter {
case ElementKind.PROPERTY: { // instance property case ElementKind.PROPERTY: { // instance property
let getterInstance = assert((<Property>target).getterInstance); let getterInstance = assert((<Property>target).getterInstance);
return this.compileCallDirect(getterInstance, [], propertyAccess, return this.compileCallDirect(getterInstance, [], propertyAccess,
this.compileExpressionRetainType( this.compileExpression(assert(this.resolver.currentThisExpression), this.options.usizeType)
assert(this.resolver.currentThisExpression),
this.options.usizeType
)
); );
} }
case ElementKind.FUNCTION_PROTOTYPE: { case ElementKind.FUNCTION_PROTOTYPE: {
@ -8042,7 +7993,7 @@ export class Compiler extends DiagnosticEmitter {
var condExpr = this.module.precomputeExpression( var condExpr = this.module.precomputeExpression(
this.makeIsTrueish( this.makeIsTrueish(
this.compileExpressionRetainType(expression.condition, Type.bool), this.compileExpression(expression.condition, Type.bool),
this.currentType this.currentType
) )
); );
@ -8053,19 +8004,19 @@ export class Compiler extends DiagnosticEmitter {
getExpressionType(condExpr) == NativeType.I32 getExpressionType(condExpr) == NativeType.I32
) { ) {
return getConstValueI32(condExpr) return getConstValueI32(condExpr)
? this.compileExpressionRetainType(ifThen, contextualType) ? this.compileExpression(ifThen, contextualType)
: this.compileExpressionRetainType(ifElse, contextualType); : this.compileExpression(ifElse, contextualType);
} }
var ifThenFlow = outerFlow.fork(); var ifThenFlow = outerFlow.fork();
this.currentFlow = ifThenFlow; this.currentFlow = ifThenFlow;
var ifThenExpr = this.compileExpressionRetainType(ifThen, contextualType, contextualFlags & ContextualFlags.SKIP_AUTORELEASE); var ifThenExpr = this.compileExpression(ifThen, contextualType, contextualFlags & ContextualFlags.SKIP_AUTORELEASE);
var ifThenType = this.currentType; var ifThenType = this.currentType;
var IfThenAutoreleaseSkipped = this.skippedAutoreleases.has(ifThenExpr); var IfThenAutoreleaseSkipped = this.skippedAutoreleases.has(ifThenExpr);
var ifElseFlow = outerFlow.fork(); var ifElseFlow = outerFlow.fork();
this.currentFlow = ifElseFlow; this.currentFlow = ifElseFlow;
var ifElseExpr = this.compileExpressionRetainType(ifElse, contextualType, contextualFlags & ContextualFlags.SKIP_AUTORELEASE); var ifElseExpr = this.compileExpression(ifElse, contextualType, contextualFlags & ContextualFlags.SKIP_AUTORELEASE);
var ifElseType = this.currentType; var ifElseType = this.currentType;
var ifElseAutoreleaseSkipped = this.skippedAutoreleases.has(ifElseExpr); var ifElseAutoreleaseSkipped = this.skippedAutoreleases.has(ifElseExpr);
@ -8133,9 +8084,7 @@ export class Compiler extends DiagnosticEmitter {
// make a getter for the expression (also obtains the type) // make a getter for the expression (also obtains the type)
var getValue = this.compileExpression( // reports var getValue = this.compileExpression( // reports
expression.operand, expression.operand,
contextualType == Type.void contextualType.exceptVoid,
? Type.i32
: contextualType,
ContextualFlags.NONE ContextualFlags.NONE
); );
@ -8367,9 +8316,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.PLUS: { case Token.PLUS: {
expr = this.compileExpression( expr = this.compileExpression(
expression.operand, expression.operand,
contextualType == Type.void contextualType.exceptVoid,
? Type.i32
: contextualType,
ContextualFlags.NONE ContextualFlags.NONE
); );
@ -8407,9 +8354,7 @@ export class Compiler extends DiagnosticEmitter {
expr = this.compileExpression( expr = this.compileExpression(
expression.operand, expression.operand,
contextualType == Type.void contextualType.exceptVoid,
? Type.i32
: contextualType,
ContextualFlags.NONE ContextualFlags.NONE
); );
@ -8476,9 +8421,7 @@ export class Compiler extends DiagnosticEmitter {
compound = true; compound = true;
expr = this.compileExpression( expr = this.compileExpression(
expression.operand, expression.operand,
contextualType == Type.void contextualType.exceptVoid,
? Type.i32
: contextualType,
ContextualFlags.NONE ContextualFlags.NONE
); );
@ -8545,9 +8488,7 @@ export class Compiler extends DiagnosticEmitter {
compound = true; compound = true;
expr = this.compileExpression( expr = this.compileExpression(
expression.operand, expression.operand,
contextualType == Type.void contextualType.exceptVoid,
? Type.i32
: contextualType,
ContextualFlags.NONE ContextualFlags.NONE
); );
@ -8613,9 +8554,7 @@ export class Compiler extends DiagnosticEmitter {
case Token.EXCLAMATION: { case Token.EXCLAMATION: {
expr = this.compileExpression( expr = this.compileExpression(
expression.operand, expression.operand,
contextualType == Type.void contextualType.exceptVoid,
? Type.i32
: contextualType,
ContextualFlags.NONE ContextualFlags.NONE
); );

View File

@ -280,8 +280,8 @@ export abstract class DiagnosticEmitter {
var message = DiagnosticMessage.create(code, category, arg0, arg1, arg2).withRange(range); var message = DiagnosticMessage.create(code, category, arg0, arg1, arg2).withRange(range);
if (relatedRange) message.relatedRange = relatedRange; if (relatedRange) message.relatedRange = relatedRange;
this.diagnostics.push(message); this.diagnostics.push(message);
// console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
// console.log(<string>new Error("stack").stack); console.log(<string>new Error("stack").stack);
} }
/** Emits an informatory diagnostic message. */ /** Emits an informatory diagnostic message. */

View File

@ -145,6 +145,12 @@ export class Type {
} }
} }
/** Substitutes this type with the auto type if this type is void. */
get exceptVoid(): Type {
if (this.kind == TypeKind.VOID) return Type.auto;
return this;
}
/** Gets this type's logarithmic alignment in memory. */ /** Gets this type's logarithmic alignment in memory. */
get alignLog2(): i32 { get alignLog2(): i32 {
return 31 - clz<i32>(this.byteSize); return 31 - clz<i32>(this.byteSize);
@ -526,6 +532,9 @@ export class Type {
/** No return type. */ /** No return type. */
static readonly void: Type = new Type(TypeKind.VOID, TypeFlags.NONE, 0); static readonly void: Type = new Type(TypeKind.VOID, TypeFlags.NONE, 0);
/** Alias of i32 indicating type inference of locals and globals with just an initializer. */
static readonly auto: Type = new Type(Type.i32.kind, Type.i32.flags, Type.i32.size);
} }
/** Converts an array of types to an array of native types. */ /** Converts an array of types to an array of native types. */

View File

@ -116,6 +116,12 @@
(func $assert-nonnull/testElem (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $assert-nonnull/testElem (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
local.get $0 local.get $0
call $~lib/array/Array<assert-nonnull/Foo | null>#__get call $~lib/array/Array<assert-nonnull/Foo | null>#__get
local.tee $0
i32.eqz
if
unreachable
end
local.get $0
) )
(func $assert-nonnull/testAll (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $assert-nonnull/testAll (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
local.get $0 local.get $0

View File

@ -179,7 +179,6 @@
) )
(func $assert-nonnull/testElem (; 11 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $assert-nonnull/testElem (; 11 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)
(local $2 i32)
local.get $0 local.get $0
call $~lib/rt/stub/__retain call $~lib/rt/stub/__retain
drop drop
@ -187,13 +186,16 @@
i32.const 0 i32.const 0
call $~lib/array/Array<assert-nonnull/Foo | null>#__get call $~lib/array/Array<assert-nonnull/Foo | null>#__get
local.tee $1 local.tee $1
if (result i32)
local.get $1
else
unreachable
end
call $~lib/rt/stub/__retain call $~lib/rt/stub/__retain
local.set $2 local.set $1
local.get $1
call $~lib/rt/stub/__release
local.get $0 local.get $0
call $~lib/rt/stub/__release call $~lib/rt/stub/__release
local.get $2 local.get $1
) )
(func $assert-nonnull/testAll (; 12 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (func $assert-nonnull/testAll (; 12 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
(local $1 i32) (local $1 i32)

View File

@ -6,6 +6,7 @@
(data (i32.const 8) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00r\00e\00t\00a\00i\00n\00-\00i\003\002\00.\00t\00s") (data (i32.const 8) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00r\00e\00t\00a\00i\00n\00-\00i\003\002\00.\00t\00s")
(global $retain-i32/si (mut i32) (i32.const 0)) (global $retain-i32/si (mut i32) (i32.const 0))
(global $retain-i32/ui (mut i32) (i32.const 0)) (global $retain-i32/ui (mut i32) (i32.const 0))
(global $retain-i32/ri (mut i32) (i32.const 0))
(export "memory" (memory $0)) (export "memory" (memory $0))
(start $start) (start $start)
(func $start:retain-i32 (; 1 ;) (type $FUNCSIG$v) (func $start:retain-i32 (; 1 ;) (type $FUNCSIG$v)
@ -254,6 +255,12 @@
call $~lib/builtins/abort call $~lib/builtins/abort
unreachable unreachable
end end
i32.const 0
i32.load8_s
global.set $retain-i32/ri
i32.const 0
i32.load8_s
drop
) )
(func $start (; 2 ;) (type $FUNCSIG$v) (func $start (; 2 ;) (type $FUNCSIG$v)
call $start:retain-i32 call $start:retain-i32

View File

@ -129,3 +129,14 @@ assert(ui == 1);
ui = 255 % 255; ui = 255 % 255;
assert(ui == 0); assert(ui == 0);
// inferring global type from load should still retain T
var ri = load<i8>(0);
assert(ri instanceof i8);
// inferring local type from load should still retain T
function testLocalRetain(): void {
var ri = load<i8>(0);
assert(ri instanceof i8);
}
testLocalRetain();

View File

@ -18,6 +18,7 @@
(global $~lib/builtins/u32.MAX_VALUE i32 (i32.const -1)) (global $~lib/builtins/u32.MAX_VALUE i32 (i32.const -1))
(global $retain-i32/si (mut i32) (i32.const 0)) (global $retain-i32/si (mut i32) (i32.const 0))
(global $retain-i32/ui (mut i32) (i32.const 0)) (global $retain-i32/ui (mut i32) (i32.const 0))
(global $retain-i32/ri (mut i32) (i32.const 0))
(export "memory" (memory $0)) (export "memory" (memory $0))
(start $start) (start $start)
(func $retain-i32/test (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (func $retain-i32/test (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
@ -330,7 +331,25 @@
unreachable unreachable
end end
) )
(func $start:retain-i32 (; 2 ;) (type $FUNCSIG$v) (func $retain-i32/testLocalRetain (; 2 ;) (type $FUNCSIG$v)
(local $0 i32)
i32.const 0
i32.load8_s
local.set $0
local.get $0
drop
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 140
i32.const 2
call $~lib/builtins/abort
unreachable
end
)
(func $start:retain-i32 (; 3 ;) (type $FUNCSIG$v)
(local $0 i32) (local $0 i32)
i32.const 0 i32.const 0
global.get $~lib/builtins/i8.MAX_VALUE global.get $~lib/builtins/i8.MAX_VALUE
@ -786,10 +805,26 @@
call $~lib/builtins/abort call $~lib/builtins/abort
unreachable unreachable
end end
i32.const 0
i32.load8_s
global.set $retain-i32/ri
global.get $retain-i32/ri
drop
i32.const 1
i32.eqz
if
i32.const 0
i32.const 24
i32.const 135
i32.const 0
call $~lib/builtins/abort
unreachable
end
call $retain-i32/testLocalRetain
) )
(func $start (; 3 ;) (type $FUNCSIG$v) (func $start (; 4 ;) (type $FUNCSIG$v)
call $start:retain-i32 call $start:retain-i32
) )
(func $null (; 4 ;) (type $FUNCSIG$v) (func $null (; 5 ;) (type $FUNCSIG$v)
) )
) )

View File

@ -1475,6 +1475,7 @@
call $~lib/string/String.__concat call $~lib/string/String.__concat
) )
(func $start:std/symbol (; 27 ;) (type $FUNCSIG$v) (func $start:std/symbol (; 27 ;) (type $FUNCSIG$v)
(local $0 i32)
i32.const 24 i32.const 24
call $~lib/symbol/Symbol call $~lib/symbol/Symbol
global.set $std/symbol/sym1 global.set $std/symbol/sym1
@ -1537,9 +1538,21 @@
end end
global.get $std/symbol/sym3 global.get $std/symbol/sym3
call $~lib/symbol/_Symbol.keyFor call $~lib/symbol/_Symbol.keyFor
local.tee $0
i32.eqz
if
unreachable
end
local.get $0
global.set $std/symbol/key3 global.set $std/symbol/key3
global.get $std/symbol/sym4 global.get $std/symbol/sym4
call $~lib/symbol/_Symbol.keyFor call $~lib/symbol/_Symbol.keyFor
local.tee $0
i32.eqz
if
unreachable
end
local.get $0
global.set $std/symbol/key4 global.set $std/symbol/key4
global.get $std/symbol/key3 global.get $std/symbol/key3
i32.const 24 i32.const 24

View File

@ -3342,8 +3342,6 @@
(local $1 i32) (local $1 i32)
(local $2 i32) (local $2 i32)
(local $3 i32) (local $3 i32)
(local $4 i32)
(local $5 i32)
i32.const 24 i32.const 24
call $~lib/symbol/Symbol call $~lib/symbol/Symbol
global.set $std/symbol/sym1 global.set $std/symbol/sym1
@ -3423,11 +3421,21 @@
global.get $std/symbol/sym3 global.get $std/symbol/sym3
call $~lib/symbol/_Symbol.keyFor call $~lib/symbol/_Symbol.keyFor
local.tee $0 local.tee $0
if (result i32)
local.get $0
else
unreachable
end
call $~lib/rt/stub/__retain call $~lib/rt/stub/__retain
global.set $std/symbol/key3 global.set $std/symbol/key3
global.get $std/symbol/sym4 global.get $std/symbol/sym4
call $~lib/symbol/_Symbol.keyFor call $~lib/symbol/_Symbol.keyFor
local.tee $1 local.tee $0
if (result i32)
local.get $0
else
unreachable
end
call $~lib/rt/stub/__retain call $~lib/rt/stub/__retain
global.set $std/symbol/key4 global.set $std/symbol/key4
global.get $std/symbol/key3 global.get $std/symbol/key3
@ -3457,7 +3465,7 @@
i32.const 0 i32.const 0
call $~lib/symbol/Symbol call $~lib/symbol/Symbol
call $~lib/symbol/_Symbol#toString call $~lib/symbol/_Symbol#toString
local.tee $2 local.tee $0
i32.const 704 i32.const 704
call $~lib/string/String.__eq call $~lib/string/String.__eq
i32.eqz i32.eqz
@ -3471,7 +3479,7 @@
end end
global.get $std/symbol/sym3 global.get $std/symbol/sym3
call $~lib/symbol/_Symbol#toString call $~lib/symbol/_Symbol#toString
local.tee $3 local.tee $1
i32.const 736 i32.const 736
call $~lib/string/String.__eq call $~lib/string/String.__eq
i32.eqz i32.eqz
@ -3489,7 +3497,7 @@
global.set $std/symbol/isConcatSpreadable global.set $std/symbol/isConcatSpreadable
global.get $std/symbol/hasInstance global.get $std/symbol/hasInstance
call $~lib/symbol/_Symbol#toString call $~lib/symbol/_Symbol#toString
local.tee $4 local.tee $2
i32.const 776 i32.const 776
call $~lib/string/String.__eq call $~lib/string/String.__eq
i32.eqz i32.eqz
@ -3503,7 +3511,7 @@
end end
global.get $std/symbol/isConcatSpreadable global.get $std/symbol/isConcatSpreadable
call $~lib/symbol/_Symbol#toString call $~lib/symbol/_Symbol#toString
local.tee $5 local.tee $3
i32.const 832 i32.const 832
call $~lib/string/String.__eq call $~lib/string/String.__eq
i32.eqz i32.eqz
@ -3527,10 +3535,6 @@
call $~lib/rt/stub/__release call $~lib/rt/stub/__release
local.get $3 local.get $3
call $~lib/rt/stub/__release call $~lib/rt/stub/__release
local.get $4
call $~lib/rt/stub/__release
local.get $5
call $~lib/rt/stub/__release
) )
(func $start (; 34 ;) (type $FUNCSIG$v) (func $start (; 34 ;) (type $FUNCSIG$v)
call $start:std/symbol call $start:std/symbol