diff --git a/src/compiler.ts b/src/compiler.ts index 0970535974..58a02ac35e 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -9212,20 +9212,32 @@ export class Compiler extends DiagnosticEmitter { var ifElseExpr = this.compileExpression(ifElse, ctxType == Type.auto ? ifThenType : ctxType); var ifElseType = this.currentType; - var commonType = Type.commonDenominator(ifThenType, ifElseType, false); - if (!commonType) { - this.error( - DiagnosticCode.Type_0_is_not_assignable_to_type_1, - ifElse.range, ifElseType.toString(), ifThenType.toString() - ); - this.currentType = ctxType; - return module.unreachable(); + if (ctxType == Type.void) { // values, including type mismatch, are irrelevant + if (ifThenType != Type.void) { + ifThenExpr = module.drop(ifThenExpr); + ifThenType = Type.void; + } + if (ifElseType != Type.void) { + ifElseExpr = module.drop(ifElseExpr); + ifElseType = Type.void; + } + this.currentType = Type.void; + } else { + let commonType = Type.commonDenominator(ifThenType, ifElseType, false); + if (!commonType) { + this.error( + DiagnosticCode.Type_0_is_not_assignable_to_type_1, + ifElse.range, ifElseType.toString(), ifThenType.toString() + ); + this.currentType = ctxType; + return module.unreachable(); + } + ifThenExpr = this.convertExpression(ifThenExpr, ifThenType, commonType, false, ifThen); + ifThenType = commonType; + ifElseExpr = this.convertExpression(ifElseExpr, ifElseType, commonType, false, ifElse); + ifElseType = commonType; + this.currentType = commonType; } - ifThenExpr = this.convertExpression(ifThenExpr, ifThenType, commonType, false, ifThen); - ifThenType = commonType; - ifElseExpr = this.convertExpression(ifElseExpr, ifElseType, commonType, false, ifElse); - ifElseType = commonType; - this.currentType = commonType; ifThenFlow.freeScopedLocals(); ifElseFlow.freeScopedLocals(); diff --git a/tests/compiler/ternary.optimized.wat b/tests/compiler/ternary.optimized.wat index 3d16a78c19..cff99dff2f 100644 --- a/tests/compiler/ternary.optimized.wat +++ b/tests/compiler/ternary.optimized.wat @@ -1,12 +1,91 @@ (module + (type $none_=>_none (func)) (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) - (memory $0 0) + (type $i32_=>_none (func (param i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (type $i32_i32_=>_none (func (param i32 i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 17452)) + (memory $0 1) + (data (i32.const 1036) "\1c") + (data (i32.const 1048) "\01\00\00\00\02\00\00\00a") + (table $0 1 funcref) (export "test" (func $ternary/test)) + (export "testDropWithTypeMismatch" (func $ternary/testDropWithTypeMismatch)) (export "memory" (memory $0)) + (export "testVoidInclTypeMismatch" (func $export:ternary/testVoidInclTypeMismatch)) (func $ternary/test (param $0 i32) (param $1 i32) (param $2 i32) (result i32) local.get $1 local.get $2 local.get $0 select ) + (func $ternary/testDropWithTypeMismatch (param $0 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1068 + i32.lt_s + if + i32.const 17472 + i32.const 17520 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.tee $0 + i32.const 0 + i32.store + local.get $0 + i32.const 1056 + i32.store + local.get $0 + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $export:ternary/testVoidInclTypeMismatch (param $0 i32) (param $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + global.get $~lib/memory/__stack_pointer + i32.const 1068 + i32.lt_s + if + i32.const 17472 + i32.const 17520 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + local.get $0 + if + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + end + local.get $0 + i32.eqz + if + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) ) diff --git a/tests/compiler/ternary.ts b/tests/compiler/ternary.ts index c3010ead74..4f0a7fd892 100644 --- a/tests/compiler/ternary.ts +++ b/tests/compiler/ternary.ts @@ -11,3 +11,15 @@ a = (0 ? unreachable() : 1) ? 1 : unreachable(); export function test(x: i32, y: i32, z: i32): i32 { return x ? y : z; } + +export function testDropWithTypeMismatch(cond: bool): void { + var x = 1; + var y = "a"; + cond ? x : y; +} + +export function testVoidInclTypeMismatch(cond: bool, nop: () => void): void { + cond ? nop() : nop(); + cond ? nop() : true; + cond ? true : nop(); +} diff --git a/tests/compiler/ternary.untouched.wat b/tests/compiler/ternary.untouched.wat index 1a6667332f..1f54423584 100644 --- a/tests/compiler/ternary.untouched.wat +++ b/tests/compiler/ternary.untouched.wat @@ -1,15 +1,23 @@ (module (type $none_=>_none (func)) + (type $i32_i32_=>_none (func (param i32 i32))) (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32))) + (type $i32_=>_none (func (param i32))) + (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) + (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (global $ternary/a (mut i32) (i32.const 0)) - (global $~lib/memory/__data_end i32 (i32.const 8)) - (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16392)) - (global $~lib/memory/__heap_base i32 (i32.const 16392)) - (memory $0 0) + (global $~argumentsLength (mut i32) (i32.const 0)) + (global $~lib/memory/__data_end i32 (i32.const 44)) + (global $~lib/memory/__stack_pointer (mut i32) (i32.const 16428)) + (global $~lib/memory/__heap_base i32 (i32.const 16428)) + (memory $0 1) + (data (i32.const 12) "\1c\00\00\00\00\00\00\00\00\00\00\00\01\00\00\00\02\00\00\00a\00\00\00\00\00\00\00\00\00\00\00") (table $0 1 funcref) (elem $0 (i32.const 1)) (export "test" (func $ternary/test)) + (export "testDropWithTypeMismatch" (func $ternary/testDropWithTypeMismatch)) (export "memory" (memory $0)) + (export "testVoidInclTypeMismatch" (func $export:ternary/testVoidInclTypeMismatch)) (start $~start) (func $start:ternary i32.const 1 @@ -33,7 +41,105 @@ local.get $2 end ) + (func $ternary/testDropWithTypeMismatch (param $0 i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + i32.const 0 + i32.store + i32.const 1 + local.set $1 + global.get $~lib/memory/__stack_pointer + i32.const 32 + local.tee $2 + i32.store + local.get $0 + if + local.get $1 + drop + else + local.get $2 + drop + end + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $ternary/testVoidInclTypeMismatch (param $0 i32) (param $1 i32) + local.get $0 + if + i32.const 0 + global.set $~argumentsLength + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + else + i32.const 0 + global.set $~argumentsLength + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + end + local.get $0 + if + i32.const 0 + global.set $~argumentsLength + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + else + i32.const 1 + drop + end + local.get $0 + if + i32.const 1 + drop + else + i32.const 0 + global.set $~argumentsLength + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + end + ) (func $~start call $start:ternary ) + (func $~stack_check + global.get $~lib/memory/__stack_pointer + global.get $~lib/memory/__data_end + i32.lt_s + if + i32.const 16448 + i32.const 16496 + i32.const 1 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + ) + (func $export:ternary/testVoidInclTypeMismatch (param $0 i32) (param $1 i32) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.sub + global.set $~lib/memory/__stack_pointer + call $~stack_check + global.get $~lib/memory/__stack_pointer + local.get $1 + i32.store + local.get $0 + local.get $1 + call $ternary/testVoidInclTypeMismatch + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) )