diff --git a/src/builtins.ts b/src/builtins.ts index 7bd978ce..efc8345c 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -5,7 +5,7 @@ import { Compiler, - ContextualFlags, + Constraints, RuntimeFeatures, flatten } from "./compiler"; @@ -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); } @@ -782,8 +782,8 @@ export function compileCall( checkArgsRequired(operands, 1, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP) - : compiler.compileExpression(operands[0], Type.i32, ContextualFlags.WRAP); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP) + : compiler.compileExpression(operands[0], Type.i32, Constraints.MUST_WRAP); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -873,8 +873,8 @@ export function compileCall( checkArgsRequired(operands, 2, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP) - : compiler.compileExpression(operands[0], Type.i32, ContextualFlags.WRAP); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP) + : compiler.compileExpression(operands[0], Type.i32, Constraints.MUST_WRAP); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -883,7 +883,7 @@ export function compileCall( ); return module.unreachable(); } - let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], type, Constraints.CONV_IMPLICIT); let expr: ExpressionRef; switch (type.kind) { case TypeKind.I8: @@ -934,8 +934,8 @@ export function compileCall( checkArgsRequired(operands, 2, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP) - : compiler.compileExpression(operands[0], Type.i32, ContextualFlags.WRAP); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP) + : compiler.compileExpression(operands[0], Type.i32, Constraints.MUST_WRAP); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -944,7 +944,7 @@ export function compileCall( ); return module.unreachable(); } - let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], type, Constraints.CONV_IMPLICIT); let expr: ExpressionRef; switch (type.kind) { case TypeKind.I8: @@ -995,8 +995,8 @@ export function compileCall( checkArgsRequired(operands, 1, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.WRAP); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP) + : compiler.compileExpression(operands[0], Type.f64, Constraints.MUST_WRAP); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -1120,8 +1120,8 @@ export function compileCall( checkArgsRequired(operands, 2, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.WRAP); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP) + : compiler.compileExpression(operands[0], Type.f64, Constraints.MUST_WRAP); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -1130,7 +1130,7 @@ export function compileCall( ); return module.unreachable(); } - let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT | ContextualFlags.WRAP); + let arg1 = compiler.compileExpression(operands[1], type, Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP); let op: BinaryOp; switch (type.kind) { case TypeKind.I8: @@ -1190,8 +1190,8 @@ export function compileCall( checkArgsRequired(operands, 2, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT | ContextualFlags.WRAP) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.WRAP); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP) + : compiler.compileExpression(operands[0], Type.f64, Constraints.MUST_WRAP); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -1200,7 +1200,7 @@ export function compileCall( ); return module.unreachable(); } - let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT | ContextualFlags.WRAP); + let arg1 = compiler.compileExpression(operands[1], type, Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP); let op: BinaryOp; switch (type.kind) { case TypeKind.I8: @@ -1261,8 +1261,8 @@ export function compileCall( checkArgsRequired(operands, 1, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.NONE); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT) + : compiler.compileExpression(operands[0], Type.f64, Constraints.NONE); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -1312,8 +1312,8 @@ export function compileCall( checkArgsRequired(operands, 2, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.NONE); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT) + : compiler.compileExpression(operands[0], Type.f64, Constraints.NONE); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -1322,7 +1322,7 @@ export function compileCall( ); return module.unreachable(); } - let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], type, Constraints.CONV_IMPLICIT); let op: BinaryOp; switch (type.kind) { // TODO: does an integer version make sense? @@ -1344,8 +1344,8 @@ export function compileCall( checkArgsRequired(operands, 1, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.NONE); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT) + : compiler.compileExpression(operands[0], Type.f64, Constraints.NONE); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -1407,13 +1407,13 @@ export function compileCall( switch (type.kind) { case TypeKind.I32: case TypeKind.U32: { - let arg0 = compiler.compileExpression(operands[0], Type.f32, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.f32, Constraints.CONV_IMPLICIT); expr = module.unary(UnaryOp.ReinterpretF32, arg0); break; } case TypeKind.I64: case TypeKind.U64: { - let arg0 = compiler.compileExpression(operands[0], Type.f64, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.f64, Constraints.CONV_IMPLICIT); expr = module.unary(UnaryOp.ReinterpretF64, arg0); break; } @@ -1423,7 +1423,7 @@ export function compileCall( compiler.options.isWasm64 ? Type.f64 : Type.f32, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); expr = module.unary( compiler.options.isWasm64 @@ -1434,12 +1434,12 @@ export function compileCall( break; } case TypeKind.F32: { - let arg0 = compiler.compileExpression(operands[0], Type.i32, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.i32, Constraints.CONV_IMPLICIT); expr = module.unary(UnaryOp.ReinterpretI32, arg0); break; } case TypeKind.F64: { - let arg0 = compiler.compileExpression(operands[0], Type.i64, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.i64, Constraints.CONV_IMPLICIT); expr = module.unary(UnaryOp.ReinterpretI64, arg0); break; } @@ -1461,8 +1461,8 @@ export function compileCall( checkArgsRequired(operands, 1, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.NONE); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT) + : compiler.compileExpression(operands[0], Type.f64, Constraints.NONE); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.currentType = type; @@ -1499,8 +1499,8 @@ export function compileCall( checkArgsRequired(operands, 1, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT) - : compiler.compileExpression(operands[0], Type.f64, ContextualFlags.NONE); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT) + : compiler.compileExpression(operands[0], Type.f64, Constraints.NONE); let type = compiler.currentType; if (type.is(TypeFlags.REFERENCE)) { compiler.error( @@ -1555,13 +1555,14 @@ 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 ) ? contextualType : type; let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let numOperands = operands.length; let immOffset = numOperands >= 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports @@ -1615,19 +1616,19 @@ export function compileCall( let type = typeArguments![0]; let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let arg1 = isAsm ? compiler.compileExpression(operands[1], contextualType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) : compiler.compileExpression( operands[1], type, type.is(TypeFlags.INTEGER) - ? ContextualFlags.NONE // no need to convert to small int (but now might result in a float) - : ContextualFlags.IMPLICIT + ? Constraints.NONE // no need to convert to small int (but now might result in a float) + : Constraints.CONV_IMPLICIT ); let inType = compiler.currentType; if ( @@ -1704,7 +1705,7 @@ export function compileCall( } let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let immOffset = operands.length == 2 ? evaluateImmediateOffset(operands[1], compiler) : 0; // reports if (immOffset < 0) { @@ -1736,20 +1737,20 @@ export function compileCall( } let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let arg1 = isAsm ? compiler.compileExpression( operands[1], contextualType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) : compiler.compileExpression( operands[1], type, type.is(TypeFlags.INTEGER) - ? ContextualFlags.NONE // no need to convert to small int (but now might result in a float) - : ContextualFlags.IMPLICIT + ? Constraints.NONE // no need to convert to small int (but now might result in a float) + : Constraints.CONV_IMPLICIT ); let inType = compiler.currentType; if ( @@ -1795,19 +1796,19 @@ export function compileCall( } let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let arg1 = isAsm ? compiler.compileExpression(operands[1], contextualType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) : compiler.compileExpression( operands[1], type, type.is(TypeFlags.INTEGER) - ? ContextualFlags.NONE // no need to convert to small int (but now might result in a float) - : ContextualFlags.IMPLICIT + ? Constraints.NONE // no need to convert to small int (but now might result in a float) + : Constraints.CONV_IMPLICIT ); let inType = compiler.currentType; if ( @@ -1860,24 +1861,24 @@ export function compileCall( } let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let arg1 = isAsm ? compiler.compileExpression(operands[1], contextualType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) : compiler.compileExpression( operands[1], type, type.is(TypeFlags.INTEGER) - ? ContextualFlags.NONE // no need to convert to small int (but now might result in a float) - : ContextualFlags.IMPLICIT + ? Constraints.NONE // no need to convert to small int (but now might result in a float) + : Constraints.CONV_IMPLICIT ); let inType = compiler.currentType; let arg2 = compiler.compileExpression(operands[2], inType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); if ( type.is(TypeFlags.INTEGER) && @@ -1925,14 +1926,14 @@ export function compileCall( } let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let arg1 = compiler.compileExpression(operands[1], type, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let arg2 = compiler.compileExpression(operands[2], Type.i64, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); compiler.currentType = Type.i32; return module.atomic_wait(arg0, arg1, arg2, type.toNativeType()); @@ -1946,11 +1947,11 @@ export function compileCall( ) return module.unreachable(); let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let arg1 = compiler.compileExpression(operands[1], Type.i32, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); compiler.currentType = Type.i32; return module.atomic_notify(arg0, arg1); @@ -1964,8 +1965,8 @@ export function compileCall( checkArgsRequired(operands, 3, reportNode, compiler) ) return module.unreachable(); let arg0 = typeArguments - ? compiler.compileExpression(operands[0], typeArguments[0], ContextualFlags.IMPLICIT) - : compiler.compileExpressionRetainType(operands[0], Type.i32); + ? compiler.compileExpression(operands[0], typeArguments[0], Constraints.CONV_IMPLICIT) + : compiler.compileExpression(operands[0], Type.auto); let type = compiler.currentType; if (!type.isAny(TypeFlags.VALUE | TypeFlags.REFERENCE)) { compiler.error( @@ -1974,9 +1975,9 @@ export function compileCall( ); return module.unreachable(); } - let arg1 = compiler.compileExpression(operands[1], type, ContextualFlags.IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], type, Constraints.CONV_IMPLICIT); let arg2 = compiler.makeIsTrueish( - compiler.compileExpressionRetainType(operands[2], Type.bool), + compiler.compileExpression(operands[2], Type.bool), compiler.currentType // ^ ); compiler.currentType = type; @@ -2010,7 +2011,7 @@ export function compileCall( checkArgsRequired(operands, 1, reportNode, compiler) ) return module.unreachable(); return module.host(HostOp.MemoryGrow, null, [ - compiler.compileExpression(operands[0], Type.i32, ContextualFlags.IMPLICIT) + compiler.compileExpression(operands[0], Type.i32, Constraints.CONV_IMPLICIT) ]); } case BuiltinSymbols.memory_copy: { // memory.copy(dest: usize, src: usize: n: usize) -> void @@ -2026,9 +2027,9 @@ export function compileCall( return compiler.compileCallDirect(instance, operands, reportNode); } let usizeType = compiler.options.usizeType; - let arg0 = compiler.compileExpression(operands[0], usizeType, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], usizeType, ContextualFlags.IMPLICIT); - let arg2 = compiler.compileExpression(operands[2], usizeType, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], usizeType, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], usizeType, Constraints.CONV_IMPLICIT); + let arg2 = compiler.compileExpression(operands[2], usizeType, Constraints.CONV_IMPLICIT); compiler.currentType = Type.void; return module.memory_copy(arg0, arg1, arg2); } @@ -2045,9 +2046,9 @@ export function compileCall( return compiler.compileCallDirect(instance, operands, reportNode); } let usizeType = compiler.options.usizeType; - let arg0 = compiler.compileExpression(operands[0], usizeType, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], Type.u8, ContextualFlags.IMPLICIT); - let arg2 = compiler.compileExpression(operands[2], usizeType, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], usizeType, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], Type.u8, Constraints.CONV_IMPLICIT); + let arg2 = compiler.compileExpression(operands[2], usizeType, Constraints.CONV_IMPLICIT); compiler.currentType = Type.void; return module.memory_fill(arg0, arg1, arg2); } @@ -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) { @@ -2084,8 +2085,8 @@ export function compileCall( return module.unreachable(); } 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], typeArguments[0], Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP) + : compiler.compileExpression(operands[0], Type.bool, Constraints.MUST_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(numOperands); let nativeParamTypes = new Array(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(); @@ -2332,7 +2333,7 @@ export function compileCall( return module.unreachable(); } compiler.currentType = classInstance.type; - return compiler.compileInstantiate(classInstance, operands, ContextualFlags.NONE, reportNode); + return compiler.compileInstantiate(classInstance, operands, Constraints.NONE, reportNode); } // === User-defined diagnostics =============================================================== @@ -2372,7 +2373,7 @@ export function compileCall( compiler.currentType = Type.i8; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.i8, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.i8, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.i16: { if ( @@ -2382,7 +2383,7 @@ export function compileCall( compiler.currentType = Type.i16; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.i16, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.i16, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.i32: { if ( @@ -2392,7 +2393,7 @@ export function compileCall( compiler.currentType = Type.i32; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.i32, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.i32, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.i64: { if ( @@ -2402,7 +2403,7 @@ export function compileCall( compiler.currentType = Type.i64; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.i64, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.i64, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.isize: { let isizeType = compiler.options.isizeType; @@ -2413,7 +2414,7 @@ export function compileCall( compiler.currentType = isizeType; return module.unreachable(); } - return compiler.compileExpression(operands[0], isizeType, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], isizeType, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.u8: { if ( @@ -2423,7 +2424,7 @@ export function compileCall( compiler.currentType = Type.u8; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.u8, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.u8, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.u16: { if ( @@ -2433,7 +2434,7 @@ export function compileCall( compiler.currentType = Type.u16; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.u16, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.u16, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.u32: { if ( @@ -2443,7 +2444,7 @@ export function compileCall( compiler.currentType = Type.u32; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.u32, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.u32, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.u64: { if ( @@ -2453,7 +2454,7 @@ export function compileCall( compiler.currentType = Type.u64; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.u64, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.u64, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.usize: { let usizeType = compiler.options.usizeType; @@ -2464,7 +2465,7 @@ export function compileCall( compiler.currentType = usizeType; return module.unreachable(); } - return compiler.compileExpression(operands[0], usizeType, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], usizeType, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.bool: { if ( @@ -2474,7 +2475,7 @@ export function compileCall( compiler.currentType = Type.bool; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.bool, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.bool, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.f32: { if ( @@ -2484,7 +2485,7 @@ export function compileCall( compiler.currentType = Type.f32; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.f32, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.f32, Constraints.CONV_EXPLICIT); } case BuiltinSymbols.f64: { if ( @@ -2494,7 +2495,7 @@ export function compileCall( compiler.currentType = Type.f64; return module.unreachable(); } - return compiler.compileExpression(operands[0], Type.f64, ContextualFlags.EXPLICIT); + return compiler.compileExpression(operands[0], Type.f64, Constraints.CONV_EXPLICIT); } // === SIMD =================================================================================== @@ -2513,7 +2514,7 @@ export function compileCall( for (let i = 0; i < 16; ++i) { let value = operands[i]; if (value) { - let expr = compiler.precomputeExpression(value, Type.i8, ContextualFlags.IMPLICIT); + let expr = compiler.precomputeExpression(value, Type.i8, Constraints.CONV_IMPLICIT); if (getExpressionId(expr) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -2542,7 +2543,7 @@ export function compileCall( for (let i = 0; i < 8; ++i) { let value = operands[i]; if (value) { - let expr = compiler.precomputeExpression(value, Type.i16, ContextualFlags.IMPLICIT); + let expr = compiler.precomputeExpression(value, Type.i16, Constraints.CONV_IMPLICIT); if (getExpressionId(expr) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -2571,7 +2572,7 @@ export function compileCall( for (let i = 0; i < 4; ++i) { let value = operands[i]; if (value) { - let expr = compiler.precomputeExpression(value, Type.i32, ContextualFlags.IMPLICIT); + let expr = compiler.precomputeExpression(value, Type.i32, Constraints.CONV_IMPLICIT); if (getExpressionId(expr) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -2600,7 +2601,7 @@ export function compileCall( for (let i = 0; i < 2; ++i) { let value = operands[i]; if (value) { - let expr = compiler.precomputeExpression(value, Type.i64, ContextualFlags.IMPLICIT); + let expr = compiler.precomputeExpression(value, Type.i64, Constraints.CONV_IMPLICIT); if (getExpressionId(expr) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -2631,7 +2632,7 @@ export function compileCall( for (let i = 0; i < 4; ++i) { let value = operands[i]; if (value) { - let expr = compiler.precomputeExpression(value, Type.f32, ContextualFlags.IMPLICIT); + let expr = compiler.precomputeExpression(value, Type.f32, Constraints.CONV_IMPLICIT); if (getExpressionId(expr) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -2660,7 +2661,7 @@ export function compileCall( for (let i = 0; i < 2; ++i) { let value = operands[i]; if (value) { - let expr = compiler.precomputeExpression(value, Type.f64, ContextualFlags.IMPLICIT); + let expr = compiler.precomputeExpression(value, Type.f64, Constraints.CONV_IMPLICIT); if (getExpressionId(expr) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -2722,7 +2723,7 @@ export function compileCall( return module.unreachable(); } } - let arg0 = compiler.compileExpression(operands[0], type, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], type, Constraints.CONV_IMPLICIT); compiler.currentType = Type.v128; return module.unary(op, arg0); } @@ -2769,8 +2770,8 @@ export function compileCall( return module.unreachable(); } } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); - let arg1 = compiler.precomputeExpression(operands[1], Type.u8, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); + let arg1 = compiler.precomputeExpression(operands[1], Type.u8, Constraints.CONV_IMPLICIT); compiler.currentType = type; if (getExpressionId(arg1) != ExpressionId.Const) { compiler.error( @@ -2837,8 +2838,8 @@ export function compileCall( return module.unreachable(); } } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); - let arg1 = compiler.precomputeExpression(operands[1], Type.u8, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); + let arg1 = compiler.precomputeExpression(operands[1], Type.u8, Constraints.CONV_IMPLICIT); if (getExpressionId(arg1) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -2857,7 +2858,7 @@ export function compileCall( ); return module.unreachable(); } - let arg2 = compiler.compileExpression(operands[2], type, ContextualFlags.IMPLICIT); + let arg2 = compiler.compileExpression(operands[2], type, Constraints.CONV_IMPLICIT); compiler.currentType = Type.v128; return module.simd_replace(op, arg0, idx, arg2); } @@ -2909,13 +2910,13 @@ export function compileCall( return module.unreachable(); } } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], Type.v128, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], Type.v128, Constraints.CONV_IMPLICIT); let mask = new Uint8Array(16); let maxIdx = (laneCount << 1) - 1; for (let i = 0; i < laneCount; ++i) { let operand = operands[2 + i]; - let argN = compiler.precomputeExpression(operand, Type.u8, ContextualFlags.IMPLICIT); + let argN = compiler.precomputeExpression(operand, Type.u8, Constraints.CONV_IMPLICIT); if (getExpressionId(argN) != ExpressionId.Const) { compiler.error( DiagnosticCode.Expression_must_be_a_compile_time_constant, @@ -3241,8 +3242,8 @@ export function compileCall( compiler.currentType = Type.v128; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], Type.v128, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], Type.v128, Constraints.CONV_IMPLICIT); compiler.currentType = Type.v128; return module.binary(op, arg0, arg1); } @@ -3333,7 +3334,7 @@ export function compileCall( compiler.currentType = Type.v128; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); compiler.currentType = Type.v128; return module.unary(op, arg0); } @@ -3412,8 +3413,8 @@ export function compileCall( compiler.currentType = Type.v128; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], Type.i32, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], Type.i32, Constraints.CONV_IMPLICIT); compiler.currentType = Type.v128; return module.simd_shift(op, arg0, arg1); } @@ -3435,8 +3436,8 @@ export function compileCall( case BuiltinSymbols.v128_or: { op = BinaryOp.OrVec128; break; } case BuiltinSymbols.v128_xor: { op = BinaryOp.XorVec128; break; } } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], Type.v128, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], Type.v128, Constraints.CONV_IMPLICIT); return module.binary(op, arg0, arg1); } case BuiltinSymbols.v128_not: { // any_bitwise_unary(a: v128) -> v128 @@ -3448,7 +3449,7 @@ export function compileCall( compiler.currentType = Type.v128; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); return module.unary(UnaryOp.NotVec128, arg0); } case BuiltinSymbols.v128_bitselect: { // bitselect(v1: v128, v2: v128, c: v128) -> v128 @@ -3460,9 +3461,9 @@ export function compileCall( compiler.currentType = Type.v128; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], Type.v128, ContextualFlags.IMPLICIT); - let arg2 = compiler.compileExpression(operands[2], Type.v128, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], Type.v128, Constraints.CONV_IMPLICIT); + let arg2 = compiler.compileExpression(operands[2], Type.v128, Constraints.CONV_IMPLICIT); return module.simd_bitselect(arg0, arg1, arg2); } case BuiltinSymbols.v128_any_true: // any_test(a: v128) -> bool @@ -3535,7 +3536,7 @@ export function compileCall( compiler.currentType = Type.bool; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], Type.v128, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.v128, Constraints.CONV_IMPLICIT); compiler.currentType = Type.bool; return module.unary(op, arg0); } @@ -3564,7 +3565,7 @@ export function compileCall( compiler.currentType = Type.void; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], Type.u32, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], Type.u32, Constraints.CONV_IMPLICIT); compiler.runtimeFeatures |= RuntimeFeatures.visitGlobals; compiler.currentType = Type.void; return module.call(BuiltinSymbols.visit_globals, [ arg0 ], NativeType.None); @@ -3577,8 +3578,8 @@ export function compileCall( compiler.currentType = Type.void; return module.unreachable(); } - let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, ContextualFlags.IMPLICIT); - let arg1 = compiler.compileExpression(operands[1], Type.u32, ContextualFlags.IMPLICIT); + let arg0 = compiler.compileExpression(operands[0], compiler.options.usizeType, Constraints.CONV_IMPLICIT); + let arg1 = compiler.compileExpression(operands[1], Type.u32, Constraints.CONV_IMPLICIT); compiler.runtimeFeatures |= RuntimeFeatures.visitMembers; compiler.currentType = Type.void; return module.call(BuiltinSymbols.visit_members, [ arg0, arg1 ], NativeType.None); @@ -3949,7 +3950,7 @@ export function compileAbort( // assertion succeeds respectively is only being computed if the program actually crashes. // Hence, let's make it so that the autorelease is skipped at the end of the current block, // essentially ignoring the message GC-wise. Doesn't matter anyway on a crash. - messageArg = compiler.compileExpression(message, stringInstance.type, ContextualFlags.IMPLICIT | ContextualFlags.SKIP_AUTORELEASE); + messageArg = compiler.compileExpression(message, stringInstance.type, Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN); } else { messageArg = stringInstance.type.toNativeZero(module); } @@ -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], Constraints.CONV_IMPLICIT); } else { - compiler.compileExpressionRetainType(operands[0], Type.i32); + compiler.compileExpression(operands[0], Type.auto); } return compiler.currentType; } @@ -4268,7 +4265,7 @@ function evaluateImmediateOffset(expression: Expression, compiler: Compiler): i3 var expr: ExpressionRef; var value: i32; if (compiler.options.isWasm64) { - expr = compiler.precomputeExpression(expression, Type.usize64, ContextualFlags.IMPLICIT); + expr = compiler.precomputeExpression(expression, Type.usize64, Constraints.CONV_IMPLICIT); if ( getExpressionId(expr) != ExpressionId.Const || getExpressionType(expr) != NativeType.I64 || @@ -4282,7 +4279,7 @@ function evaluateImmediateOffset(expression: Expression, compiler: Compiler): i3 value = -1; } } else { - expr = compiler.precomputeExpression(expression, Type.usize32, ContextualFlags.IMPLICIT); + expr = compiler.precomputeExpression(expression, Type.usize32, Constraints.CONV_IMPLICIT); if ( getExpressionId(expr) != ExpressionId.Const || getExpressionType(expr) != NativeType.I32 || diff --git a/src/compiler.ts b/src/compiler.ts index c55b6cf4..d9261a5e 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -233,25 +233,23 @@ export class Options { } } -/** Requests or indicates compilation conditions of statements and expressions. */ -export const enum ContextualFlags { +/** Various constraints in expression compilation. */ +export const enum Constraints { NONE = 0, - /** Implicit conversion required. */ - IMPLICIT = 1 << 0, - /** Explicit conversion required. */ - EXPLICIT = 1 << 1, - /** Small integer wrap required. */ - WRAP = 1 << 2, + /** Must implicitly convert to the target type. */ + CONV_IMPLICIT = 1 << 0, + /** Must explicitly convert to the target type. */ + CONV_EXPLICIT = 1 << 1, + /** Must wrap small integer values to match the target type. */ + MUST_WRAP = 1 << 2, - /** Value is known to be immediately dropped. */ + /** Indicates that the value will be dropped immediately. */ 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 + /** Indicates that the value will be retained immediately. */ + WILL_RETAIN = 1 << 4, + /** Indicates that static data is preferred. */ + PREFER_STATIC = 1 << 5 } /** Runtime features to be activated by the compiler. */ @@ -814,8 +812,8 @@ export class Compiler extends DiagnosticEmitter { if (global.hasDecorator(DecoratorFlags.LAZY)) { this.currentFlow = global.file.startFunction.flow; } - initExpr = this.compileExpression(initializerNode, Type.i32, // reports - ContextualFlags.WRAP | ContextualFlags.SKIP_AUTORELEASE + initExpr = this.compileExpression(initializerNode, Type.auto, // reports + Constraints.MUST_WRAP | Constraints.WILL_RETAIN ); if (this.skippedAutoreleases.has(initExpr)) initAutoreleaseSkipped = true; this.currentFlow = previousFlow; @@ -888,7 +886,7 @@ export class Compiler extends DiagnosticEmitter { this.currentFlow = global.file.startFunction.flow; } initExpr = this.compileExpression(initializerNode, type, - ContextualFlags.IMPLICIT | ContextualFlags.WRAP | ContextualFlags.SKIP_AUTORELEASE | ContextualFlags.STATIC_CAPABLE + Constraints.CONV_IMPLICIT | Constraints.MUST_WRAP | Constraints.WILL_RETAIN | Constraints.PREFER_STATIC ); if (this.skippedAutoreleases.has(initExpr)) initAutoreleaseSkipped = true; this.currentFlow = previousFlow; @@ -997,7 +995,7 @@ export class Compiler extends DiagnosticEmitter { let initExpr: ExpressionRef; if (valueNode) { initExpr = this.compileExpression(valueNode, Type.i32, - ContextualFlags.IMPLICIT // autorelease is not applicable in i32 context + Constraints.CONV_IMPLICIT // autorelease is not applicable in i32 context ); if (getExpressionId(initExpr) != ExpressionId.Const) { initExpr = module.precomputeExpression(initExpr); @@ -1140,7 +1138,7 @@ export class Compiler extends DiagnosticEmitter { assert(!instance.isAny(CommonFlags.CONSTRUCTOR | CommonFlags.GET | CommonFlags.SET | CommonFlags.MAIN)); let expr = this.compileExpression((bodyNode).expression, returnType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); if (!stmts) stmts = [ expr ]; else stmts.push(expr); @@ -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(statement, contextualFlags); + stmt = this.compileBlockStatement(statement); break; } case NodeKind.BREAK: { - stmt = this.compileBreakStatement(statement, contextualFlags); + stmt = this.compileBreakStatement(statement); break; } case NodeKind.CONTINUE: { - stmt = this.compileContinueStatement(statement, contextualFlags); + stmt = this.compileContinueStatement(statement); break; } case NodeKind.DO: { - stmt = this.compileDoStatement(statement, contextualFlags); + stmt = this.compileDoStatement(statement); break; } case NodeKind.EMPTY: { - stmt = this.compileEmptyStatement(statement, contextualFlags); + stmt = this.compileEmptyStatement(statement); break; } case NodeKind.EXPRESSION: { - stmt = this.compileExpressionStatement(statement, contextualFlags); + stmt = this.compileExpressionStatement(statement); break; } case NodeKind.FOR: { - stmt = this.compileForStatement(statement, contextualFlags); + stmt = this.compileForStatement(statement); break; } case NodeKind.IF: { - stmt = this.compileIfStatement(statement, contextualFlags); + stmt = this.compileIfStatement(statement); break; } case NodeKind.RETURN: { - stmt = this.compileReturnStatement(statement, contextualFlags); + stmt = this.compileReturnStatement(statement, isLastInBody); break; } case NodeKind.SWITCH: { - stmt = this.compileSwitchStatement(statement, contextualFlags); + stmt = this.compileSwitchStatement(statement); break; } case NodeKind.THROW: { - stmt = this.compileThrowStatement(statement, contextualFlags); + stmt = this.compileThrowStatement(statement); break; } case NodeKind.TRY: { - stmt = this.compileTryStatement(statement, contextualFlags); + stmt = this.compileTryStatement(statement); break; } case NodeKind.VARIABLE: { - stmt = this.compileVariableStatement(statement, contextualFlags); + stmt = this.compileVariableStatement(statement); if (!stmt) stmt = module.nop(); break; } case NodeKind.VOID: { - stmt = this.compileVoidStatement(statement, contextualFlags); + stmt = this.compileVoidStatement(statement); break; } case NodeKind.WHILE: { - stmt = this.compileWhileStatement(statement, contextualFlags); + stmt = this.compileWhileStatement(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, Constraints.CONV_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(statement.condition, Type.bool), + this.compileExpression(statement.condition, Type.bool), this.currentType ) ); @@ -2032,7 +2017,7 @@ export class Compiler extends DiagnosticEmitter { // Compile incrementor var incrementor = statement.incrementor; var incrExpr: ExpressionRef = 0; - if (incrementor) incrExpr = this.compileExpression(incrementor, Type.void, ContextualFlags.IMPLICIT | ContextualFlags.WILL_DROP); + if (incrementor) incrExpr = this.compileExpression(incrementor, Type.void, Constraints.CONV_IMPLICIT | Constraints.WILL_DROP); // Compile body (break: drop out, continue: fall through to incrementor, + loop) var breakLabel = innerFlow.breakLabel = "break|" + label; innerFlow.breakLabel = breakLabel; @@ -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; @@ -2209,9 +2193,9 @@ export class Compiler extends DiagnosticEmitter { this.currentType = Type.void; return module.unreachable(); } - let contextualFlags = ContextualFlags.IMPLICIT; - if (flow.actualFunction.is(CommonFlags.MODULE_EXPORT)) contextualFlags |= ContextualFlags.WRAP; - expr = this.compileExpression(valueExpression, returnType, contextualFlags | ContextualFlags.SKIP_AUTORELEASE); + let constraints = Constraints.CONV_IMPLICIT; + if (flow.actualFunction.is(CommonFlags.MODULE_EXPORT)) constraints |= Constraints.MUST_WRAP; + expr = this.compileExpression(valueExpression, returnType, constraints | Constraints.WILL_RETAIN); // when returning a local, and it is already retained, skip the final set // of retaining it as the return value and releasing it as a variable @@ -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,14 +2260,16 @@ 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; var numCases = cases.length; if (!numCases) { return this.compileExpression(statement.condition, Type.void, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); } @@ -2300,7 +2286,7 @@ export class Compiler extends DiagnosticEmitter { breaks[0] = module.local_set( // initializer tempLocalIndex, this.compileExpression(statement.condition, Type.u32, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) ); @@ -2315,7 +2301,7 @@ export class Compiler extends DiagnosticEmitter { module.binary(BinaryOp.EqI32, module.local_get(tempLocalIndex, NativeType.I32), this.compileExpression(label, Type.u32, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) ) ); @@ -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; @@ -2451,15 +2437,15 @@ export class Compiler extends DiagnosticEmitter { if (!type) continue; if (declaration.initializer) { initExpr = this.compileExpression(declaration.initializer, type, // reports - ContextualFlags.IMPLICIT | ContextualFlags.SKIP_AUTORELEASE + Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN ); initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr); } // Otherwise infer type from initializer } else if (declaration.initializer) { - initExpr = this.compileExpressionRetainType(declaration.initializer, Type.void, - ContextualFlags.SKIP_AUTORELEASE + initExpr = this.compileExpression(declaration.initializer, Type.auto, + Constraints.WILL_RETAIN ); // reports initAutoreleaseSkipped = this.skippedAutoreleases.has(initExpr); if (this.currentType == Type.void) { @@ -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 + Constraints.CONV_EXPLICIT | Constraints.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 ) ); @@ -2705,12 +2695,12 @@ export class Compiler extends DiagnosticEmitter { compileInlineConstant( element: VariableLikeElement, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { assert(element.is(CommonFlags.INLINED)); var type = element.type; switch ( - !(contextualFlags & (ContextualFlags.IMPLICIT | ContextualFlags.EXPLICIT)) && + !(constraints & (Constraints.CONV_IMPLICIT | Constraints.CONV_EXPLICIT)) && type.is(TypeFlags.INTEGER) && contextualType.is(TypeFlags.INTEGER) && type.size < contextualType.size @@ -2785,34 +2775,34 @@ export class Compiler extends DiagnosticEmitter { compileExpression( expression: Expression, contextualType: Type, - contextualFlags: ContextualFlags = ContextualFlags.NONE + constraints: Constraints = Constraints.NONE ): ExpressionRef { this.currentType = contextualType; var expr: ExpressionRef; - if (contextualType == Type.void) contextualFlags |= ContextualFlags.WILL_DROP; + if (contextualType == Type.void) constraints |= Constraints.WILL_DROP; switch (expression.kind) { case NodeKind.ASSERTION: { - expr = this.compileAssertionExpression(expression, contextualType, contextualFlags); + expr = this.compileAssertionExpression(expression, contextualType, constraints); break; } case NodeKind.BINARY: { - expr = this.compileBinaryExpression(expression, contextualType, contextualFlags); + expr = this.compileBinaryExpression(expression, contextualType, constraints); break; } case NodeKind.CALL: { - expr = this.compileCallExpression(expression, contextualType, contextualFlags); + expr = this.compileCallExpression(expression, contextualType, constraints); break; } case NodeKind.COMMA: { - expr = this.compileCommaExpression(expression, contextualType, contextualFlags); + expr = this.compileCommaExpression(expression, contextualType, constraints); break; } case NodeKind.ELEMENTACCESS: { - expr = this.compileElementAccessExpression(expression, contextualType, contextualFlags); + expr = this.compileElementAccessExpression(expression, contextualType, constraints); break; } case NodeKind.FUNCTION: { - expr = this.compileFunctionExpression(expression, contextualType.signatureReference, contextualFlags); + expr = this.compileFunctionExpression(expression, contextualType.signatureReference, constraints); break; } case NodeKind.IDENTIFIER: @@ -2821,39 +2811,39 @@ export class Compiler extends DiagnosticEmitter { case NodeKind.THIS: case NodeKind.SUPER: case NodeKind.TRUE: { - expr = this.compileIdentifierExpression(expression, contextualType, contextualFlags); + expr = this.compileIdentifierExpression(expression, contextualType, constraints); break; } case NodeKind.INSTANCEOF: { - expr = this.compileInstanceOfExpression(expression, contextualType, contextualFlags); + expr = this.compileInstanceOfExpression(expression, contextualType, constraints); break; } case NodeKind.LITERAL: { - expr = this.compileLiteralExpression(expression, contextualType, contextualFlags); + expr = this.compileLiteralExpression(expression, contextualType, constraints); break; } case NodeKind.NEW: { - expr = this.compileNewExpression(expression, contextualType, contextualFlags); + expr = this.compileNewExpression(expression, contextualType, constraints); break; } case NodeKind.PARENTHESIZED: { - expr = this.compileExpression((expression).expression, contextualType, contextualFlags); + expr = this.compileExpression((expression).expression, contextualType, constraints); break; } case NodeKind.PROPERTYACCESS: { - expr = this.compilePropertyAccessExpression(expression, contextualType, contextualFlags); + expr = this.compilePropertyAccessExpression(expression, contextualType, constraints); break; } case NodeKind.TERNARY: { - expr = this.compileTernaryExpression(expression, contextualType, contextualFlags); + expr = this.compileTernaryExpression(expression, contextualType, constraints); break; } case NodeKind.UNARYPOSTFIX: { - expr = this.compileUnaryPostfixExpression(expression, contextualType, contextualFlags); + expr = this.compileUnaryPostfixExpression(expression, contextualType, constraints); break; } case NodeKind.UNARYPREFIX: { - expr = this.compileUnaryPrefixExpression(expression, contextualType, contextualFlags); + expr = this.compileUnaryPrefixExpression(expression, contextualType, constraints); break; } default: { @@ -2866,13 +2856,13 @@ export class Compiler extends DiagnosticEmitter { } // ensure conversion and wrapping in case the respective function doesn't on its own var currentType = this.currentType; - var wrap = (contextualFlags & ContextualFlags.WRAP) != 0; + var wrap = (constraints & Constraints.MUST_WRAP) != 0; if (currentType != contextualType) { - if (contextualFlags & ContextualFlags.EXPLICIT) { + if (constraints & Constraints.CONV_EXPLICIT) { expr = this.convertExpression(expr, currentType, contextualType, true, wrap, expression); wrap = false; this.currentType = contextualType; - } else if (contextualFlags & ContextualFlags.IMPLICIT) { + } else if (constraints & Constraints.CONV_IMPLICIT) { expr = this.convertExpression(expr, currentType, contextualType, false, wrap, expression); wrap = false; this.currentType = contextualType; @@ -2885,28 +2875,14 @@ 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, contextualType: Type, - contextualFlags: ContextualFlags = ContextualFlags.NONE + constraints: Constraints = Constraints.NONE ): ExpressionRef { return this.module.precomputeExpression( - this.compileExpression(expression, contextualType, contextualFlags) + this.compileExpression(expression, contextualType, constraints) ); } @@ -3106,8 +3082,9 @@ export class Compiler extends DiagnosticEmitter { compileAssertionExpression( expression: AssertionExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { + var inheritedConstraints = constraints & ~(Constraints.CONV_IMPLICIT | Constraints.CONV_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, inheritedConstraints | Constraints.CONV_EXPLICIT); } case AssertionKind.NONNULL: { assert(!expression.toType); - let expr = this.compileExpressionRetainType(expression.expression, contextualType); + let expr = this.compileExpression(expression.expression, contextualType.exceptVoid, inheritedConstraints); let type = this.currentType; if (this.currentFlow.isNonnull(expr, type)) { this.info( @@ -3159,7 +3134,7 @@ export class Compiler extends DiagnosticEmitter { compileBinaryExpression( expression: BinaryExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var module = this.module; var left = expression.left; @@ -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 @@ -3784,9 +3759,9 @@ export class Compiler extends DiagnosticEmitter { } if (compound) { - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 @@ -3873,10 +3848,10 @@ export class Compiler extends DiagnosticEmitter { } if (compound) { - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 @@ -3964,9 +3939,9 @@ export class Compiler extends DiagnosticEmitter { if (compound) { leftExpr = this.ensureSmallIntegerWrap(leftExpr, leftType); - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 @@ -4056,7 +4031,7 @@ export class Compiler extends DiagnosticEmitter { // Mathf.pow if lhs is f32 (result is f32) if (this.currentType.kind == TypeKind.F32) { - rightExpr = this.compileExpression(right, Type.f32, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, Type.f32, Constraints.CONV_IMPLICIT); rightType = this.currentType; if (!(instance = this.f32PowInstance)) { let namespace = this.program.lookupGlobal(CommonSymbols.Mathf); @@ -4090,7 +4065,7 @@ export class Compiler extends DiagnosticEmitter { left ); leftType = this.currentType; - rightExpr = this.compileExpression(right, Type.f64, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, Type.f64, Constraints.CONV_IMPLICIT); rightType = this.currentType; if (!(instance = this.f64PowInstance)) { let namespace = this.program.lookupGlobal(CommonSymbols.Math); @@ -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 @@ -4146,10 +4121,10 @@ export class Compiler extends DiagnosticEmitter { if (compound) { leftExpr = this.ensureSmallIntegerWrap(leftExpr, leftType); - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 @@ -4255,10 +4230,10 @@ export class Compiler extends DiagnosticEmitter { if (compound) { leftExpr = this.ensureSmallIntegerWrap(leftExpr, leftType); - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 @@ -4419,7 +4394,7 @@ export class Compiler extends DiagnosticEmitter { return this.module.unreachable(); } - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_IMPLICIT); rightType = this.currentType; switch (this.currentType.kind) { case TypeKind.I8: @@ -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 @@ -4486,7 +4461,7 @@ export class Compiler extends DiagnosticEmitter { } leftExpr = this.ensureSmallIntegerWrap(leftExpr, leftType); // must clear garbage bits - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_IMPLICIT); rightType = this.currentType; switch (this.currentType.kind) { case TypeKind.I8: @@ -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 @@ -4574,7 +4549,7 @@ export class Compiler extends DiagnosticEmitter { } leftExpr = this.ensureSmallIntegerWrap(leftExpr, leftType); // must clear garbage bits - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_IMPLICIT); rightType = this.currentType; switch (this.currentType.kind) { case TypeKind.U8: @@ -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 @@ -4643,10 +4618,10 @@ export class Compiler extends DiagnosticEmitter { } if (compound) { - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 @@ -4733,10 +4708,10 @@ export class Compiler extends DiagnosticEmitter { } if (compound) { - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 @@ -4826,10 +4801,10 @@ export class Compiler extends DiagnosticEmitter { } if (compound) { - rightExpr = this.compileExpression(right, leftType, ContextualFlags.IMPLICIT); + rightExpr = this.compileExpression(right, leftType, Constraints.CONV_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 inheritedConstraints = constraints & (Constraints.WILL_RETAIN | Constraints.MUST_WRAP); + leftExpr = this.compileExpression(left, contextualType.exceptVoid, inheritedConstraints); 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, inheritedConstraints | Constraints.CONV_IMPLICIT); rightType = leftType; // simplify if only interested in true or false @@ -4939,7 +4915,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.makeRetain(rightExpr); rightAutoreleaseSkipped = true; } - } else if (!(contextualFlags & ContextualFlags.SKIP_AUTORELEASE)) { // otherwise keep right alive a little longer + } else if (!(constraints & Constraints.WILL_RETAIN)) { // otherwise keep right alive a little longer rightExpr = this.moveAutorelease(rightExpr, rightFlow, flow); } @@ -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 inheritedConstraints = constraints & (Constraints.WILL_RETAIN | Constraints.MUST_WRAP); + leftExpr = this.compileExpression(left, contextualType.exceptVoid, inheritedConstraints); 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, inheritedConstraints | Constraints.CONV_IMPLICIT); rightType = leftType; // simplify if only interested in true or false @@ -5036,7 +5013,7 @@ export class Compiler extends DiagnosticEmitter { rightExpr = this.makeRetain(rightExpr); rightAutoreleaseSkipped = true; } - } else if (!(contextualFlags & ContextualFlags.SKIP_AUTORELEASE)) { // otherwise keep right alive a little longer + } else if (!(constraints & Constraints.WILL_RETAIN)) { // otherwise keep right alive a little longer rightExpr = this.moveAutorelease(rightExpr, rightFlow, flow); } @@ -5147,7 +5124,7 @@ export class Compiler extends DiagnosticEmitter { // it can actually resolve every kind of expression without ever having to recompile. rightType = operatorInstance.signature.parameterTypes[1]; } - var rightExpr = this.compileExpression(right, rightType, ContextualFlags.IMPLICIT); + var rightExpr = this.compileExpression(right, rightType, Constraints.CONV_IMPLICIT); return this.makeCallDirect(operatorInstance, [ leftExpr, rightExpr ], reportNode); } @@ -5255,7 +5232,7 @@ export class Compiler extends DiagnosticEmitter { assert(targetType != Type.void); return this.makeAssignment( target, - this.compileExpression(valueExpression, targetType, ContextualFlags.IMPLICIT | ContextualFlags.SKIP_AUTORELEASE), + this.compileExpression(valueExpression, targetType, Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN), expression, thisExpression, elementExpression, @@ -5326,11 +5303,8 @@ export class Compiler extends DiagnosticEmitter { } return this.makeFieldAssignment(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((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,11 +5387,8 @@ export class Compiler extends DiagnosticEmitter { return module.unreachable(); } let targetType = (target).type; - let thisExpr = this.compileExpressionRetainType( - assert(thisExpression), - this.options.usizeType - ); - let elementExpr = this.compileExpression(indexExpression, Type.i32, ContextualFlags.IMPLICIT); + let thisExpr = this.compileExpression(assert(thisExpression), this.options.usizeType); + let elementExpr = this.compileExpression(indexExpression, Type.i32, Constraints.CONV_IMPLICIT); if (tee) { let tempLocalTarget = flow.getTempLocal(targetType); let tempLocalElement = flow.getAndFreeTempLocal(this.currentType); @@ -5735,8 +5700,8 @@ export class Compiler extends DiagnosticEmitter { expression: CallExpression, /** Contextual type indicating the return type the caller expects, if any. */ contextualType: Type, - /** Contextual flags indicating contextual conditions. */ - contextualFlags: ContextualFlags + /** Constraints indicating contextual conditions. */ + constraints: Constraints ): ExpressionRef { var module = this.module; @@ -5775,7 +5740,7 @@ export class Compiler extends DiagnosticEmitter { this.makeAllocation(classInstance) ) ), - ContextualFlags.SKIP_AUTORELEASE + Constraints.WILL_RETAIN ); assert(this.skippedAutoreleases.has(theCall)); // guaranteed let stmts: ExpressionRef[] = [ @@ -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; } @@ -5893,7 +5858,7 @@ export class Compiler extends DiagnosticEmitter { flow.contextualTypeArguments ); if (!concreteType) return module.unreachable(); - argumentExprs[i] = this.compileExpression(argumentExpression, concreteType, ContextualFlags.IMPLICIT); + argumentExprs[i] = this.compileExpression(argumentExpression, concreteType, Constraints.CONV_IMPLICIT); } } let resolvedTypeArguments = new Array(numTypeParameters); @@ -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( @@ -5932,7 +5894,7 @@ export class Compiler extends DiagnosticEmitter { expression.arguments, expression, thisExpr, - contextualFlags + constraints ); } @@ -5969,10 +5931,7 @@ export class Compiler extends DiagnosticEmitter { let type = (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, @@ -5991,7 +5950,7 @@ export class Compiler extends DiagnosticEmitter { } case ElementKind.FUNCTION_TARGET: { signature = (target).signature; - indexArg = this.compileExpression(expression.expression, (target).type, ContextualFlags.IMPLICIT); + indexArg = this.compileExpression(expression.expression, (target).type, Constraints.CONV_IMPLICIT); break; } @@ -6013,10 +5972,7 @@ export class Compiler extends DiagnosticEmitter { case ElementKind.PROPERTY: { // instance property let getterInstance = assert((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) { @@ -6157,7 +6113,7 @@ export class Compiler extends DiagnosticEmitter { argumentExpressions: Expression[], reportNode: Node, thisArg: ExpressionRef = 0, - contextualFlags: ContextualFlags = ContextualFlags.NONE + constraints: Constraints = Constraints.NONE ): ExpressionRef { var numArguments = argumentExpressions.length; var signature = instance.signature; @@ -6187,13 +6143,13 @@ export class Compiler extends DiagnosticEmitter { let args = new Array(numArguments); for (let i = 0; i < numArguments; ++i) { args[i] = this.compileExpression(argumentExpressions[i], parameterTypes[i], - ContextualFlags.IMPLICIT | ContextualFlags.SKIP_AUTORELEASE + Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN ); } // make the inlined call (is aware of autoreleases) - let expr = this.makeCallInline(instance, args, thisArg, (contextualFlags & ContextualFlags.WILL_DROP) != 0); + let expr = this.makeCallInline(instance, args, thisArg, (constraints & Constraints.WILL_DROP) != 0); if (this.currentType.isManaged) { - if (!(contextualFlags & ContextualFlags.SKIP_AUTORELEASE)) { + if (!(constraints & Constraints.WILL_RETAIN)) { expr = this.makeAutorelease(expr, this.currentFlow); } else { this.skippedAutoreleases.add(expr); @@ -6215,15 +6171,15 @@ export class Compiler extends DiagnosticEmitter { var parameterTypes = signature.parameterTypes; for (let i = 0; i < numArguments; ++i, ++index) { operands[index] = this.compileExpression(argumentExpressions[i], parameterTypes[i], - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); } assert(index == numArgumentsInclThis); if (signature.returnType.isManaged) { - if (contextualFlags & ContextualFlags.WILL_DROP) { + if (constraints & Constraints.WILL_DROP) { // Skip autorelease and simply release return this.makeCallDirect(instance, operands, reportNode, true); - } else if (contextualFlags & ContextualFlags.SKIP_AUTORELEASE) { + } else if (constraints & Constraints.WILL_RETAIN) { // Skip autorelease and remember let expr = this.makeCallDirect(instance, operands, reportNode, false, true); this.skippedAutoreleases.add(expr); @@ -6233,7 +6189,7 @@ export class Compiler extends DiagnosticEmitter { } } return this.makeCallDirect(instance, operands, reportNode, - (contextualFlags & ContextualFlags.WILL_DROP) != 0 + (constraints & Constraints.WILL_DROP) != 0 ); } @@ -6300,7 +6256,7 @@ export class Compiler extends DiagnosticEmitter { let initExpr = this.compileExpression( assert(instance.prototype.signatureNode.parameters[i].initializer), initType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); let argumentLocal = flow.addScopedLocal(signature.getParameterName(i), initType); if (!flow.canOverflow(initExpr, initType)) flow.setLocalFlag(argumentLocal.index, LocalFlags.WRAPPED); @@ -6442,7 +6398,7 @@ export class Compiler extends DiagnosticEmitter { this.compileExpression( initializer, type, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) ); } else { @@ -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); } @@ -6758,7 +6715,7 @@ export class Compiler extends DiagnosticEmitter { operands.push(this.compileExpression( parameterNodes[i].initializer, parameterTypes[i], - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT )); continue; } @@ -6769,7 +6726,7 @@ export class Compiler extends DiagnosticEmitter { if (this.compileGlobal(global)) { if (global.is(CommonFlags.INLINED)) { operands.push( - this.compileInlineConstant(global, parameterTypes[i], ContextualFlags.IMPLICIT) + this.compileInlineConstant(global, parameterTypes[i], Constraints.CONV_IMPLICIT) ); } else { operands.push( @@ -6860,7 +6817,7 @@ export class Compiler extends DiagnosticEmitter { var parameterTypes = signature.parameterTypes; for (let i = 0; i < numArguments; ++i, ++index) { operands[index] = this.compileExpression(argumentExpressions[i], parameterTypes[i], - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); } assert(index == numArgumentsInclThis); @@ -6924,24 +6881,24 @@ export class Compiler extends DiagnosticEmitter { compileCommaExpression( expression: CommaExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var expressions = expression.expressions; var numExpressions = expressions.length; var exprs = new Array(numExpressions--); for (let i = 0; i < numExpressions; ++i) { exprs[i] = this.compileExpression(expressions[i], Type.void, // drop all except last - ContextualFlags.IMPLICIT | ContextualFlags.WILL_DROP + Constraints.CONV_IMPLICIT | Constraints.WILL_DROP ); } - exprs[numExpressions] = this.compileExpression(expressions[numExpressions], contextualType, contextualFlags); + exprs[numExpressions] = this.compileExpression(expressions[numExpressions], contextualType, constraints); return this.module.block(null, exprs, this.currentType.toNativeType()); } compileElementAccessExpression( expression: ElementAccessExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var target = this.resolver.resolveElementAccessExpression( expression, @@ -6960,11 +6917,11 @@ export class Compiler extends DiagnosticEmitter { return this.module.unreachable(); } let thisArg = this.compileExpression(expression.expression, (target).type, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ); return this.compileCallDirect(indexedGet, [ expression.elementExpression - ], expression, thisArg, contextualFlags & (ContextualFlags.WILL_DROP | ContextualFlags.SKIP_AUTORELEASE)); + ], expression, thisArg, constraints); } } this.error( @@ -6977,7 +6934,7 @@ export class Compiler extends DiagnosticEmitter { compileFunctionExpression( expression: FunctionExpression, contextualSignature: Signature | null, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var declaration = expression.declaration.clone(); // generic contexts can have multiple assert(!declaration.typeParameters); // function expression cannot be generic @@ -7131,7 +7088,7 @@ export class Compiler extends DiagnosticEmitter { compileIdentifierExpression( expression: IdentifierExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var module = this.module; var flow = this.currentFlow; @@ -7258,7 +7215,7 @@ export class Compiler extends DiagnosticEmitter { let type = (target).type; assert(type != Type.void); if ((target).is(CommonFlags.INLINED)) { - return this.compileInlineConstant(target, contextualType, contextualFlags); + return this.compileInlineConstant(target, contextualType, constraints); } let localIndex = (target).index; assert(localIndex >= 0); @@ -7275,7 +7232,7 @@ export class Compiler extends DiagnosticEmitter { let type = (target).type; assert(type != Type.void); if ((target).is(CommonFlags.INLINED)) { - return this.compileInlineConstant(target, contextualType, contextualFlags); + return this.compileInlineConstant(target, contextualType, constraints); } this.currentType = type; return this.module.global_get((target).internalName, type.toNativeType()); @@ -7318,14 +7275,14 @@ export class Compiler extends DiagnosticEmitter { compileInstanceOfExpression( expression: InstanceOfExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var module = this.module; // NOTE that this differs from TypeScript in that the rhs is a type, not an expression. at the // 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; @@ -7334,7 +7291,7 @@ export class Compiler extends DiagnosticEmitter { // instanceof - must be exact if (!expectedType.is(TypeFlags.REFERENCE)) { return module.block(null, [ - this.convertExpression(expr, actualType, Type.void, (contextualFlags & ContextualFlags.EXPLICIT) != 0, false, expression.expression), + module.drop(expr), module.i32(actualType == expectedType ? 1 : 0) ], NativeType.I32); } @@ -7342,7 +7299,7 @@ export class Compiler extends DiagnosticEmitter { // instanceof - always false if (!actualType.is(TypeFlags.REFERENCE)) { return module.block(null, [ - this.convertExpression(expr, actualType, Type.void, (contextualFlags & ContextualFlags.EXPLICIT) != 0,false, expression.expression), + module.drop(expr), module.i32(0) ], NativeType.I32); } @@ -7399,7 +7356,7 @@ export class Compiler extends DiagnosticEmitter { // downcast - check statically if (actualType.isAssignableTo(expectedType)) { return module.block(null, [ - this.convertExpression(expr, actualType, Type.void, (contextualFlags & ContextualFlags.EXPLICIT) != 0, false, expression.expression), + this.convertExpression(expr, actualType, Type.void, false, false, expression.expression), module.i32(1) ], NativeType.I32); @@ -7438,7 +7395,7 @@ export class Compiler extends DiagnosticEmitter { // false return module.block(null, [ - this.convertExpression(expr, actualType, Type.void, (contextualFlags & ContextualFlags.EXPLICIT) != 0, false, expression.expression), + module.drop(expr), module.i32(0) ], NativeType.I32); } @@ -7446,7 +7403,7 @@ export class Compiler extends DiagnosticEmitter { compileLiteralExpression( expression: LiteralExpression, contextualType: Type, - contextualFlags: ContextualFlags, + constraints: Constraints, implicitlyNegate: bool = false ): ExpressionRef { var module = this.module; @@ -7459,8 +7416,7 @@ export class Compiler extends DiagnosticEmitter { return this.compileArrayLiteral( assert(classType.typeArguments)[0], (expression).elementExpressions, - false, // TODO: isConst? - contextualFlags, + constraints, expression ); } @@ -7527,8 +7483,7 @@ export class Compiler extends DiagnosticEmitter { compileArrayLiteral( elementType: Type, expressions: (Expression | null)[], - isConst: bool, - contextualFlags: ContextualFlags, + constraints: Constraints, reportNode: Node ): ExpressionRef { var module = this.module; @@ -7553,7 +7508,7 @@ export class Compiler extends DiagnosticEmitter { let expr = expression ? module.precomputeExpression( this.compileExpression(expression, elementType, - ContextualFlags.IMPLICIT + Constraints.CONV_IMPLICIT ) ) : elementType.toNativeZero(module); @@ -7576,7 +7531,7 @@ export class Compiler extends DiagnosticEmitter { // make both the buffer and array header static if assigned to a global. this can't be done // if inside of a function because each invocation must create a new array reference then. - if (contextualFlags & ContextualFlags.STATIC_CAPABLE) { + if (constraints & Constraints.PREFER_STATIC) { let arraySegment = this.ensureStaticArrayHeader(elementType, bufferSegment); let arrayAddress = i64_add(arraySegment.offset, i64_new(runtimeHeaderSize)); this.currentType = arrayType; @@ -7736,7 +7691,7 @@ export class Compiler extends DiagnosticEmitter { exprs[i + 1] = this.module.store( // TODO: handle setters as well type.byteSize, this.module.local_get(tempLocal.index, this.options.nativeSizeType), - this.compileExpression(values[i], (member).type, ContextualFlags.IMPLICIT), + this.compileExpression(values[i], (member).type, Constraints.CONV_IMPLICIT), type.toNativeType(), (member).memoryOffset ); @@ -7762,7 +7717,7 @@ export class Compiler extends DiagnosticEmitter { compileNewExpression( expression: NewExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var module = this.module; var flow = this.currentFlow; @@ -7804,8 +7759,8 @@ export class Compiler extends DiagnosticEmitter { ); } if (!classInstance) return module.unreachable(); - if (contextualType == Type.void) contextualFlags |= ContextualFlags.WILL_DROP; - return this.compileInstantiate(classInstance, expression.arguments, contextualFlags, expression); + if (contextualType == Type.void) constraints |= Constraints.WILL_DROP; + return this.compileInstantiate(classInstance, expression.arguments, constraints, expression); } /** Gets the compiled constructor of the specified class or generates one if none is present. */ @@ -7922,7 +7877,7 @@ export class Compiler extends DiagnosticEmitter { /** Constructor arguments. */ argumentExpressions: Expression[], /** Contextual flags. */ - contextualFlags: ContextualFlags, + constraints: Constraints, /** Node to report on. */ reportNode: Node ): ExpressionRef { @@ -7932,7 +7887,7 @@ export class Compiler extends DiagnosticEmitter { argumentExpressions, reportNode, this.options.usizeType.toNativeZero(this.module), - contextualFlags + constraints ); if (getExpressionType(expr) != NativeType.None) { // possibly IMM_DROPPED this.currentType = classInstance.type; // important because a super ctor could be called @@ -7948,7 +7903,7 @@ export class Compiler extends DiagnosticEmitter { compilePropertyAccessExpression( propertyAccess: PropertyAccessExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var module = this.module; var flow = this.currentFlow; @@ -7966,7 +7921,7 @@ export class Compiler extends DiagnosticEmitter { let globalType = (target).type; assert(globalType != Type.void); if ((target).is(CommonFlags.INLINED)) { - return this.compileInlineConstant(target, contextualType, contextualFlags); + return this.compileInlineConstant(target, contextualType, constraints); } this.currentType = globalType; return module.global_get((target).internalName, globalType.toNativeType()); @@ -7986,10 +7941,7 @@ export class Compiler extends DiagnosticEmitter { } case ElementKind.FIELD: { // instance field assert((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 = (target).type; return module.load( (target).type.byteSize, @@ -8010,10 +7962,7 @@ export class Compiler extends DiagnosticEmitter { case ElementKind.PROPERTY: { // instance property let getterInstance = assert((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: { @@ -8034,7 +7983,7 @@ export class Compiler extends DiagnosticEmitter { compileTernaryExpression( expression: TernaryExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var ifThen = expression.ifThen; var ifElse = expression.ifElse; @@ -8042,7 +7991,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 +8002,21 @@ 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 inheritedConstraints = constraints & Constraints.WILL_RETAIN; + var ifThenFlow = outerFlow.fork(); this.currentFlow = ifThenFlow; - var ifThenExpr = this.compileExpressionRetainType(ifThen, contextualType, contextualFlags & ContextualFlags.SKIP_AUTORELEASE); + var ifThenExpr = this.compileExpression(ifThen, contextualType, inheritedConstraints); 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, inheritedConstraints); var ifElseType = this.currentType; var ifElseAutoreleaseSkipped = this.skippedAutoreleases.has(ifElseExpr); @@ -8101,7 +8052,7 @@ export class Compiler extends DiagnosticEmitter { ifElseAutoreleaseSkipped = true; } } else if (!IfThenAutoreleaseSkipped && commonType.isManaged) { // keep alive a little longer - // if (!(contextualFlags & ContextualFlags.SKIP_AUTORELEASE)) { + // if (!(constraints & Constraints.WILL_RETAIN)) { ifThenExpr = this.moveAutorelease(ifThenExpr, ifThenFlow, outerFlow); ifElseExpr = this.moveAutorelease(ifElseExpr, ifElseFlow, outerFlow); // } @@ -8125,7 +8076,7 @@ export class Compiler extends DiagnosticEmitter { compileUnaryPostfixExpression( expression: UnaryPostfixExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var module = this.module; var flow = this.currentFlow; @@ -8133,10 +8084,8 @@ 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, - ContextualFlags.NONE + contextualType.exceptVoid, + Constraints.NONE ); // shortcut if compiling the getter already failed @@ -8357,7 +8306,7 @@ export class Compiler extends DiagnosticEmitter { compileUnaryPrefixExpression( expression: UnaryPrefixExpression, contextualType: Type, - contextualFlags: ContextualFlags + constraints: Constraints ): ExpressionRef { var module = this.module; var compound = false; @@ -8367,10 +8316,8 @@ export class Compiler extends DiagnosticEmitter { case Token.PLUS: { expr = this.compileExpression( expression.operand, - contextualType == Type.void - ? Type.i32 - : contextualType, - ContextualFlags.NONE + contextualType.exceptVoid, + Constraints.NONE ); // check operator overload @@ -8399,7 +8346,7 @@ export class Compiler extends DiagnosticEmitter { (expression.operand).literalKind == LiteralKind.FLOAT )) { // implicitly negate integer and float literals. also enables proper checking of literal ranges. - expr = this.compileLiteralExpression(expression.operand, contextualType, ContextualFlags.NONE, true); + expr = this.compileLiteralExpression(expression.operand, contextualType, Constraints.NONE, true); // compileExpression normally does this: if (this.options.sourceMap) this.addDebugLocation(expr, expression.range); break; @@ -8407,10 +8354,8 @@ export class Compiler extends DiagnosticEmitter { expr = this.compileExpression( expression.operand, - contextualType == Type.void - ? Type.i32 - : contextualType, - ContextualFlags.NONE + contextualType.exceptVoid, + Constraints.NONE ); // check operator overload @@ -8476,10 +8421,8 @@ export class Compiler extends DiagnosticEmitter { compound = true; expr = this.compileExpression( expression.operand, - contextualType == Type.void - ? Type.i32 - : contextualType, - ContextualFlags.NONE + contextualType.exceptVoid, + Constraints.NONE ); // check operator overload @@ -8545,10 +8488,8 @@ export class Compiler extends DiagnosticEmitter { compound = true; expr = this.compileExpression( expression.operand, - contextualType == Type.void - ? Type.i32 - : contextualType, - ContextualFlags.NONE + contextualType.exceptVoid, + Constraints.NONE ); // check operator overload @@ -8613,10 +8554,8 @@ export class Compiler extends DiagnosticEmitter { case Token.EXCLAMATION: { expr = this.compileExpression( expression.operand, - contextualType == Type.void - ? Type.i32 - : contextualType, - ContextualFlags.NONE + contextualType.exceptVoid, + Constraints.NONE ); // check operator overload @@ -8644,7 +8583,7 @@ export class Compiler extends DiagnosticEmitter { : contextualType.is(TypeFlags.FLOAT) ? Type.i64 : contextualType, - ContextualFlags.NONE + Constraints.NONE ); // check operator overload @@ -8934,7 +8873,7 @@ export class Compiler extends DiagnosticEmitter { let initializerNode = field.prototype.initializerNode; if (initializerNode) { // use initializer let initExpr = this.compileExpression(initializerNode, fieldType, // reports - ContextualFlags.IMPLICIT | ContextualFlags.SKIP_AUTORELEASE + Constraints.CONV_IMPLICIT | Constraints.WILL_RETAIN ); if (fieldType.isManaged && !this.skippedAutoreleases.has(initExpr)) { initExpr = this.makeRetain(initExpr); diff --git a/src/types.ts b/src/types.ts index 166584c3..37a4ff9b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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(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. */ diff --git a/tests/compiler/assert-nonnull.optimized.wat b/tests/compiler/assert-nonnull.optimized.wat index 34da49a3..e9f76b90 100644 --- a/tests/compiler/assert-nonnull.optimized.wat +++ b/tests/compiler/assert-nonnull.optimized.wat @@ -116,6 +116,12 @@ (func $assert-nonnull/testElem (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 call $~lib/array/Array#__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 diff --git a/tests/compiler/assert-nonnull.untouched.wat b/tests/compiler/assert-nonnull.untouched.wat index 99aed7ec..ac6d6e1f 100644 --- a/tests/compiler/assert-nonnull.untouched.wat +++ b/tests/compiler/assert-nonnull.untouched.wat @@ -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#__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) diff --git a/tests/compiler/retain-i32.optimized.wat b/tests/compiler/retain-i32.optimized.wat index 6cfabbee..431e76c8 100644 --- a/tests/compiler/retain-i32.optimized.wat +++ b/tests/compiler/retain-i32.optimized.wat @@ -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 diff --git a/tests/compiler/retain-i32.ts b/tests/compiler/retain-i32.ts index 769b684f..5484c06c 100644 --- a/tests/compiler/retain-i32.ts +++ b/tests/compiler/retain-i32.ts @@ -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(0); +assert(ri instanceof i8); + +// inferring local type from load should still retain T +function testLocalRetain(): void { + var ri = load(0); + assert(ri instanceof i8); +} +testLocalRetain(); diff --git a/tests/compiler/retain-i32.untouched.wat b/tests/compiler/retain-i32.untouched.wat index fdad18d4..fa6fcdc4 100644 --- a/tests/compiler/retain-i32.untouched.wat +++ b/tests/compiler/retain-i32.untouched.wat @@ -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) ) ) diff --git a/tests/compiler/std/symbol.optimized.wat b/tests/compiler/std/symbol.optimized.wat index 4f375618..837accea 100644 --- a/tests/compiler/std/symbol.optimized.wat +++ b/tests/compiler/std/symbol.optimized.wat @@ -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 diff --git a/tests/compiler/std/symbol.untouched.wat b/tests/compiler/std/symbol.untouched.wat index d5956daa..42fda050 100644 --- a/tests/compiler/std/symbol.untouched.wat +++ b/tests/compiler/std/symbol.untouched.wat @@ -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