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

View File

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

View File

@ -280,8 +280,8 @@ export abstract class DiagnosticEmitter {
var message = DiagnosticMessage.create(code, category, arg0, arg1, arg2).withRange(range);
if (relatedRange) message.relatedRange = relatedRange;
this.diagnostics.push(message);
// console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
// console.log(<string>new Error("stack").stack);
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
console.log(<string>new Error("stack").stack);
}
/** 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. */
get alignLog2(): i32 {
return 31 - clz<i32>(this.byteSize);
@ -526,6 +532,9 @@ export class Type {
/** No return type. */
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. */

View File

@ -116,6 +116,12 @@
(func $assert-nonnull/testElem (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
local.get $0
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)
local.get $0

View File

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

View File

@ -129,3 +129,14 @@ assert(ui == 1);
ui = 255 % 255;
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 $retain-i32/si (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))
(start $start)
(func $retain-i32/test (; 1 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
@ -330,7 +331,25 @@
unreachable
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)
i32.const 0
global.get $~lib/builtins/i8.MAX_VALUE
@ -786,10 +805,26 @@
call $~lib/builtins/abort
unreachable
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
)
(func $null (; 4 ;) (type $FUNCSIG$v)
(func $null (; 5 ;) (type $FUNCSIG$v)
)
)

View File

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

View File

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