diff --git a/src/compiler.ts b/src/compiler.ts index 940cf2d6..a1654463 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -4755,8 +4755,14 @@ export class Compiler extends DiagnosticEmitter { case Token.AMPERSAND_AMPERSAND: { // left && right leftExpr = this.compileExpressionRetainType(left, contextualType, WrapMode.NONE); leftType = this.currentType; + + let previousFlow = this.currentFlow; + let rightFlow = previousFlow.fork(); + this.currentFlow = rightFlow; + rightFlow.inheritNonnullIf(leftExpr); rightExpr = this.compileExpression(right, leftType, ConversionKind.IMPLICIT, WrapMode.NONE); rightType = leftType; + this.currentFlow = previousFlow; // simplify if only interested in true or false if (contextualType == Type.bool || contextualType == Type.void) { @@ -4799,8 +4805,14 @@ export class Compiler extends DiagnosticEmitter { case Token.BAR_BAR: { // left || right leftExpr = this.compileExpressionRetainType(left, contextualType, WrapMode.NONE); leftType = this.currentType; + + let previousFlow = this.currentFlow; + let rightFlow = previousFlow.fork(); + this.currentFlow = rightFlow; + rightFlow.inheritNonnullIfNot(leftExpr); rightExpr = this.compileExpression(right, leftType, ConversionKind.IMPLICIT, WrapMode.NONE); rightType = leftType; + this.currentFlow = previousFlow; // simplify if only interested in true or false if (contextualType == Type.bool || contextualType == Type.void) { diff --git a/tests/compiler/possibly-null.optimized.wat b/tests/compiler/possibly-null.optimized.wat index 634a749a..c83e8f1a 100644 --- a/tests/compiler/possibly-null.optimized.wat +++ b/tests/compiler/possibly-null.optimized.wat @@ -16,6 +16,10 @@ (export "testWhile" (func $possibly-null/testWhile)) (export "testWhile2" (func $possibly-null/testWhile2)) (export "testWhile3" (func $possibly-null/testWhile3)) + (export "testLogicalAnd" (func $possibly-null/testTrue)) + (export "testLogicalOr" (func $possibly-null/testTrue)) + (export "testLogicalAndMulti" (func $possibly-null/testLogicalAndMulti)) + (export "testLogicalOrMulti" (func $possibly-null/testLogicalAndMulti)) (func $possibly-null/testTrue (; 0 ;) (type $FUNCSIG$vi) (param $0 i32) nop ) @@ -52,7 +56,10 @@ end end ) - (func $null (; 4 ;) (type $FUNCSIG$v) + (func $possibly-null/testLogicalAndMulti (; 4 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + nop + ) + (func $null (; 5 ;) (type $FUNCSIG$v) nop ) ) diff --git a/tests/compiler/possibly-null.ts b/tests/compiler/possibly-null.ts index 86185671..01af0a4a 100644 --- a/tests/compiler/possibly-null.ts +++ b/tests/compiler/possibly-null.ts @@ -83,16 +83,30 @@ export function testWhile3(a: Ref | null, b: Ref | null): void { } } -// TODO: +function requireNonNull(a: Ref): Ref { + return a; +} -// function requireNonNull(a: Ref): Ref { -// return a; -// } +export function testLogicalAnd(a: Ref | null): void { + a && requireNonNull(a); +} -// export function testLogicalAnd(a: Ref | null): void { -// a && requireNonNull(a); -// } +export function testLogicalOr(a: Ref | null): void { + !a || requireNonNull(a) != null; +} -// export function testLogicalOr(a: Ref | null): void { -// !a || requireNonNull(a); -// } +export function testLogicalAndMulti(a: Ref | null, b: Ref | null): void { + if (a && b) { + if (isNullable(a)) ERROR("should be non-nullable"); + if (isNullable(b)) ERROR("should be non-nullable"); + } +} + +export function testLogicalOrMulti(a: Ref | null, b: Ref | null): void { + if (!a || !b) { + // something + } else { + if (isNullable(a)) ERROR("should be non-nullable"); + if (isNullable(b)) ERROR("should be non-nullable"); + } +} diff --git a/tests/compiler/possibly-null.untouched.wat b/tests/compiler/possibly-null.untouched.wat index 04f6b77d..5ad30504 100644 --- a/tests/compiler/possibly-null.untouched.wat +++ b/tests/compiler/possibly-null.untouched.wat @@ -1,6 +1,7 @@ (module (type $FUNCSIG$vi (func (param i32))) (type $FUNCSIG$vii (func (param i32 i32))) + (type $FUNCSIG$ii (func (param i32) (result i32))) (type $FUNCSIG$v (func)) (memory $0 0) (table $0 1 funcref) @@ -18,6 +19,10 @@ (export "testWhile" (func $possibly-null/testWhile)) (export "testWhile2" (func $possibly-null/testWhile2)) (export "testWhile3" (func $possibly-null/testWhile3)) + (export "testLogicalAnd" (func $possibly-null/testLogicalAnd)) + (export "testLogicalOr" (func $possibly-null/testLogicalOr)) + (export "testLogicalAndMulti" (func $possibly-null/testLogicalAndMulti)) + (export "testLogicalOrMulti" (func $possibly-null/testLogicalOrMulti)) (func $possibly-null/testTrue (; 0 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 if @@ -128,6 +133,58 @@ end end ) - (func $null (; 12 ;) (type $FUNCSIG$v) + (func $possibly-null/requireNonNull (; 12 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + ) + (func $possibly-null/testLogicalAnd (; 13 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + if (result i32) + local.get $0 + call $possibly-null/requireNonNull + else + i32.const 0 + end + drop + ) + (func $possibly-null/testLogicalOr (; 14 ;) (type $FUNCSIG$vi) (param $0 i32) + local.get $0 + i32.eqz + if (result i32) + i32.const 1 + else + local.get $0 + call $possibly-null/requireNonNull + i32.const 0 + i32.ne + end + drop + ) + (func $possibly-null/testLogicalAndMulti (; 15 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + local.get $0 + if (result i32) + local.get $1 + else + i32.const 0 + end + if + nop + end + ) + (func $possibly-null/testLogicalOrMulti (; 16 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + local.get $0 + i32.eqz + if (result i32) + i32.const 1 + else + local.get $1 + i32.eqz + end + if + nop + else + nop + end + ) + (func $null (; 17 ;) (type $FUNCSIG$v) ) )