diff --git a/src/compiler.ts b/src/compiler.ts index d95f7f05..2357a3ca 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -6138,24 +6138,32 @@ export class Compiler extends DiagnosticEmitter { // time of implementation, this seemed more useful because dynamic rhs expressions are not // possible in AS anyway. var expr = this.compileExpressionRetainType(expression.expression, this.options.usizeType, WrapMode.NONE); - var type = this.currentType; - var isType = this.resolver.resolveType( + var actualType = this.currentType; + var expectedType = this.resolver.resolveType( expression.isType, this.currentFlow.actualFunction ); this.currentType = Type.bool; - if (!isType) return module.createUnreachable(); - return type.is(TypeFlags.NULLABLE) && !isType.is(TypeFlags.NULLABLE) - ? type.nonNullableType.isAssignableTo(isType) - ? module.createBinary( // not precomputeable - type.is(TypeFlags.LONG) - ? BinaryOp.NeI64 - : BinaryOp.NeI32, - expr, - type.toNativeZero(module) - ) - : module.createI32(0) - : module.createI32(type.isAssignableTo(isType, true) ? 1 : 0); + if (!expectedType) return module.createUnreachable(); + + // instanceof must be exact + if (!expectedType.is(TypeFlags.REFERENCE)) { + return module.createI32(actualType == expectedType ? 1 : 0); + } + // instanceof must be != 0 + if ( + actualType.is(TypeFlags.NULLABLE) && !expectedType.is(TypeFlags.NULLABLE) && + actualType.nonNullableType.isAssignableTo(expectedType) + ) { + return module.createBinary( + actualType.is(TypeFlags.LONG) + ? BinaryOp.NeI64 + : BinaryOp.NeI32, + expr, + actualType.toNativeZero(module) + ); + } + return module.createI32(actualType.isAssignableTo(expectedType) ? 1 : 0); } compileLiteralExpression( diff --git a/tests/compiler/instanceof.optimized.wat b/tests/compiler/instanceof.optimized.wat index 69798ebe..064e9a42 100644 --- a/tests/compiler/instanceof.optimized.wat +++ b/tests/compiler/instanceof.optimized.wat @@ -15,7 +15,7 @@ if i32.const 0 i32.const 8 - i32.const 43 + i32.const 68 i32.const 0 call $~lib/env/abort unreachable @@ -27,7 +27,7 @@ if i32.const 0 i32.const 8 - i32.const 46 + i32.const 71 i32.const 0 call $~lib/env/abort unreachable diff --git a/tests/compiler/instanceof.ts b/tests/compiler/instanceof.ts index 5a0d036d..64d96b03 100644 --- a/tests/compiler/instanceof.ts +++ b/tests/compiler/instanceof.ts @@ -4,27 +4,51 @@ class B extends A {} var a: A; var b: B; var i: i32; +var I: i64; var f: f32; +var F: f64; assert( a instanceof A ); assert( b instanceof A ); assert(!(i instanceof A)); +assert(!(I instanceof A)); assert(!(f instanceof A)); +assert(!(F instanceof A)); assert(!(a instanceof B)); assert( b instanceof B ); assert(!(i instanceof B)); +assert(!(I instanceof B)); assert(!(f instanceof B)); +assert(!(F instanceof B)); assert(!(a instanceof i32)); assert(!(b instanceof i32)); assert( i instanceof i32 ); +assert(!(I instanceof i32)); assert(!(f instanceof i32)); +assert(!(F instanceof i32)); + +assert(!(a instanceof i64)); +assert(!(b instanceof i64)); +assert(!(i instanceof i64)); +assert( I instanceof i64 ); +assert(!(f instanceof i64)); +assert(!(F instanceof i64)); assert(!(a instanceof f32)); assert(!(b instanceof f32)); assert(!(i instanceof f32)); +assert(!(I instanceof f32)); assert( f instanceof f32 ); +assert(!(F instanceof f32)); + +assert(!(a instanceof f64)); +assert(!(b instanceof f64)); +assert(!(i instanceof f64)); +assert(!(I instanceof f64)); +assert(!(f instanceof f64)); +assert( F instanceof f64 ); function isI32(v: T): bool { // should eliminate non-applicable branches (see fixture) @@ -38,13 +62,14 @@ function isI32(v: T): bool { assert( isI32(0)); assert(!isI32(0.0)); assert(!isI32(0)); // signedness is relevant +assert(!isI32(0)); // byte size is relevant var an: A | null = null; -assert(!(an instanceof A)); // TS: null is not an instance of A -assert( an instanceof A | null); // AS: null is an instance of A | null +assert(!(an instanceof A)); // TS: ==null is not an instance of A +assert( an instanceof A | null); // AS: ==null is an instance of A | null an = changetype(1); -assert( an instanceof A); // TS: non-null is an instance of A -assert( an instanceof A | null); // AS: non-null is an instance of A | null +assert( an instanceof A); // TS: !=null is an instance of A +assert( an instanceof A | null); // AS: !=null is an instance of A | null // TODO: keep track of nullability during flows, so this becomes precomputable: // assert(an !== null && an instanceof A); diff --git a/tests/compiler/instanceof.untouched.wat b/tests/compiler/instanceof.untouched.wat index 65d8c99c..b79a62e5 100644 --- a/tests/compiler/instanceof.untouched.wat +++ b/tests/compiler/instanceof.untouched.wat @@ -11,7 +11,9 @@ (global $instanceof/a (mut i32) (i32.const 0)) (global $instanceof/b (mut i32) (i32.const 0)) (global $instanceof/i (mut i32) (i32.const 0)) + (global $instanceof/I (mut i64) (i64.const 0)) (global $instanceof/f (mut f32) (f32.const 0)) + (global $instanceof/F (mut f64) (f64.const 0)) (global $instanceof/an (mut i32) (i32.const 0)) (global $~lib/memory/HEAP_BASE i32 (i32.const 40)) (export "memory" (memory $0)) @@ -29,29 +31,12 @@ i32.const 0 return ) - (func $start:instanceof (; 4 ;) (type $_) - i32.const 1 - i32.eqz - if - i32.const 0 - i32.const 8 - i32.const 9 - i32.const 0 - call $~lib/env/abort - unreachable - end - i32.const 1 - i32.eqz - if - i32.const 0 - i32.const 8 - i32.const 10 - i32.const 0 - call $~lib/env/abort - unreachable - end + (func $instanceof/isI32 (; 4 ;) (type $ii) (param $0 i32) (result i32) i32.const 0 - i32.eqz + return + ) + (func $start:instanceof (; 5 ;) (type $_) + i32.const 1 i32.eqz if i32.const 0 @@ -61,8 +46,7 @@ call $~lib/env/abort unreachable end - i32.const 0 - i32.eqz + i32.const 1 i32.eqz if i32.const 0 @@ -75,6 +59,17 @@ i32.const 0 i32.eqz i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 13 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz if i32.const 0 i32.const 8 @@ -83,7 +78,8 @@ call $~lib/env/abort unreachable end - i32.const 1 + i32.const 0 + i32.eqz i32.eqz if i32.const 0 @@ -110,13 +106,12 @@ if i32.const 0 i32.const 8 - i32.const 17 + i32.const 18 i32.const 0 call $~lib/env/abort unreachable end - i32.const 0 - i32.eqz + i32.const 1 i32.eqz if i32.const 0 @@ -137,7 +132,8 @@ call $~lib/env/abort unreachable end - i32.const 1 + i32.const 0 + i32.eqz i32.eqz if i32.const 0 @@ -164,7 +160,7 @@ if i32.const 0 i32.const 8 - i32.const 24 + i32.const 23 i32.const 0 call $~lib/env/abort unreachable @@ -202,18 +198,104 @@ unreachable end i32.const 0 - call $instanceof/isI32 + i32.eqz i32.eqz if i32.const 0 i32.const 8 - i32.const 38 + i32.const 28 i32.const 0 call $~lib/env/abort unreachable end - f64.const 0 - call $instanceof/isI32 + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 29 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 30 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 32 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 33 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 34 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 1 + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 35 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 36 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 37 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 i32.eqz i32.eqz if @@ -225,7 +307,6 @@ unreachable end i32.const 0 - call $instanceof/isI32 i32.eqz i32.eqz if @@ -236,6 +317,161 @@ call $~lib/env/abort unreachable end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 41 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 42 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 1 + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 43 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 44 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 46 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 47 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 48 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 49 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 50 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 1 + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 51 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + call $instanceof/isI32 + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 62 + i32.const 0 + call $~lib/env/abort + unreachable + end + f64.const 0 + call $instanceof/isI32 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 63 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + call $instanceof/isI32 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 64 + i32.const 0 + call $~lib/env/abort + unreachable + end + i32.const 0 + call $instanceof/isI32 + i32.eqz + i32.eqz + if + i32.const 0 + i32.const 8 + i32.const 65 + i32.const 0 + call $~lib/env/abort + unreachable + end global.get $instanceof/an i32.const 0 i32.ne @@ -244,7 +480,7 @@ if i32.const 0 i32.const 8 - i32.const 43 + i32.const 68 i32.const 0 call $~lib/env/abort unreachable @@ -254,7 +490,7 @@ if i32.const 0 i32.const 8 - i32.const 44 + i32.const 69 i32.const 0 call $~lib/env/abort unreachable @@ -268,7 +504,7 @@ if i32.const 0 i32.const 8 - i32.const 46 + i32.const 71 i32.const 0 call $~lib/env/abort unreachable @@ -278,15 +514,15 @@ if i32.const 0 i32.const 8 - i32.const 47 + i32.const 72 i32.const 0 call $~lib/env/abort unreachable end ) - (func $start (; 5 ;) (type $_) + (func $start (; 6 ;) (type $_) call $start:instanceof ) - (func $null (; 6 ;) (type $_) + (func $null (; 7 ;) (type $_) ) )