diff --git a/src/compiler.ts b/src/compiler.ts index 58a02ac35e..31f84f065a 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2825,14 +2825,6 @@ export class Compiler extends DiagnosticEmitter { var valueExpression = statement.value; if (valueExpression) { - if (returnType == Type.void) { - this.error( - DiagnosticCode.Type_0_is_not_assignable_to_type_1, - valueExpression.range, this.currentType.toString(), returnType.toString() - ); - this.currentType = Type.void; - return module.unreachable(); - } let constraints = Constraints.CONV_IMPLICIT; if (flow.actualFunction.is(CommonFlags.MODULE_EXPORT)) constraints |= Constraints.MUST_WRAP; @@ -2857,15 +2849,27 @@ export class Compiler extends DiagnosticEmitter { // Handle inline return if (flow.isInline) { - return isLastInBody && expr != 0 - ? expr - : module.br(assert(flow.inlineReturnLabel), 0, expr); + return !expr + ? isLastInBody + ? module.nop() + : module.br(assert(flow.inlineReturnLabel)) + : isLastInBody + ? expr + : this.currentType == Type.void + ? module.block(null, [ expr, module.br(assert(flow.inlineReturnLabel)) ]) + : module.br(assert(flow.inlineReturnLabel), 0, expr); } // Otherwise emit a normal return - return isLastInBody && expr != 0 - ? expr - : module.return(expr); + return !expr + ? isLastInBody + ? module.nop() + : module.return() + : isLastInBody + ? expr + : this.currentType == Type.void + ? module.block(null, [ expr, module.return() ]) + : module.return(expr); } private compileSwitchStatement( diff --git a/tests/compiler/return.json b/tests/compiler/return.json new file mode 100644 index 0000000000..1bdd02b1be --- /dev/null +++ b/tests/compiler/return.json @@ -0,0 +1,4 @@ +{ + "asc_flags": [ + ] +} diff --git a/tests/compiler/return.optimized.wat b/tests/compiler/return.optimized.wat new file mode 100644 index 0000000000..64eb14b70d --- /dev/null +++ b/tests/compiler/return.optimized.wat @@ -0,0 +1,82 @@ +(module + (type $none_=>_none (func)) + (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) "\03\00\00\00\08\00\00\00\01") + (table $0 2 funcref) + (elem $0 (i32.const 1) $start:return~anonymous|0) + (export "testVoidReturn" (func $return/testVoidReturn)) + (export "memory" (memory $0)) + (export "testVoidReturnFunction" (func $export:return/testVoidReturnFunction)) + (start $~start) + (func $start:return~anonymous|0 + nop + ) + (func $return/testVoidReturn (param $0 i32) + nop + ) + (func $~start + (local $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 + i32.const 1056 + i32.load + call_indirect $0 (type $none_=>_none) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $export:return/testVoidReturnFunction (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) + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) +) diff --git a/tests/compiler/return.ts b/tests/compiler/return.ts new file mode 100644 index 0000000000..3d1818e903 --- /dev/null +++ b/tests/compiler/return.ts @@ -0,0 +1,16 @@ +function nop(): void {} + +export function testVoidReturn(cond: bool): void { + if (cond) { + return nop(); + } + return nop(); +} + +export function testVoidReturnFunction(cond: bool, fn: () => void): void { + if (cond) { + return fn(); + } + return fn(); +} +testVoidReturnFunction(true, () => nop()); diff --git a/tests/compiler/return.untouched.wat b/tests/compiler/return.untouched.wat new file mode 100644 index 0000000000..74d0fb1a0b --- /dev/null +++ b/tests/compiler/return.untouched.wat @@ -0,0 +1,105 @@ +(module + (type $none_=>_none (func)) + (type $i32_i32_=>_none (func (param i32 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 $~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\03\00\00\00\08\00\00\00\01\00\00\00\00\00\00\00\00\00\00\00") + (table $0 2 funcref) + (elem $0 (i32.const 1) $start:return~anonymous|0) + (export "testVoidReturn" (func $return/testVoidReturn)) + (export "memory" (memory $0)) + (export "testVoidReturnFunction" (func $export:return/testVoidReturnFunction)) + (start $~start) + (func $return/nop + nop + ) + (func $start:return~anonymous|0 + call $return/nop + ) + (func $return/testVoidReturnFunction (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) + return + end + i32.const 0 + global.set $~argumentsLength + local.get $1 + i32.load + call_indirect $0 (type $none_=>_none) + ) + (func $return/testVoidReturn (param $0 i32) + local.get $0 + if + call $return/nop + return + end + call $return/nop + ) + (func $~start + call $start:return + ) + (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 $start:return + (local $0 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 + i32.const 32 + local.set $0 + global.get $~lib/memory/__stack_pointer + local.get $0 + i32.store + local.get $0 + call $return/testVoidReturnFunction + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) + (func $export:return/testVoidReturnFunction (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 $return/testVoidReturnFunction + global.get $~lib/memory/__stack_pointer + i32.const 4 + i32.add + global.set $~lib/memory/__stack_pointer + ) +)