Skip to content

Commit 63c1e85

Browse files
authored
feat: Allow returning void from void functions (#2063)
1 parent ab1f0e9 commit 63c1e85

File tree

5 files changed

+225
-14
lines changed

5 files changed

+225
-14
lines changed

src/compiler.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2825,14 +2825,6 @@ export class Compiler extends DiagnosticEmitter {
28252825

28262826
var valueExpression = statement.value;
28272827
if (valueExpression) {
2828-
if (returnType == Type.void) {
2829-
this.error(
2830-
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
2831-
valueExpression.range, this.currentType.toString(), returnType.toString()
2832-
);
2833-
this.currentType = Type.void;
2834-
return module.unreachable();
2835-
}
28362828
let constraints = Constraints.CONV_IMPLICIT;
28372829
if (flow.actualFunction.is(CommonFlags.MODULE_EXPORT)) constraints |= Constraints.MUST_WRAP;
28382830

@@ -2857,15 +2849,27 @@ export class Compiler extends DiagnosticEmitter {
28572849

28582850
// Handle inline return
28592851
if (flow.isInline) {
2860-
return isLastInBody && expr != 0
2861-
? expr
2862-
: module.br(assert(flow.inlineReturnLabel), 0, expr);
2852+
return !expr
2853+
? isLastInBody
2854+
? module.nop()
2855+
: module.br(assert(flow.inlineReturnLabel))
2856+
: isLastInBody
2857+
? expr
2858+
: this.currentType == Type.void
2859+
? module.block(null, [ expr, module.br(assert(flow.inlineReturnLabel)) ])
2860+
: module.br(assert(flow.inlineReturnLabel), 0, expr);
28632861
}
28642862

28652863
// Otherwise emit a normal return
2866-
return isLastInBody && expr != 0
2867-
? expr
2868-
: module.return(expr);
2864+
return !expr
2865+
? isLastInBody
2866+
? module.nop()
2867+
: module.return()
2868+
: isLastInBody
2869+
? expr
2870+
: this.currentType == Type.void
2871+
? module.block(null, [ expr, module.return() ])
2872+
: module.return(expr);
28692873
}
28702874

28712875
private compileSwitchStatement(

tests/compiler/return.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"asc_flags": [
3+
]
4+
}

tests/compiler/return.optimized.wat

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
(module
2+
(type $none_=>_none (func))
3+
(type $i32_=>_none (func (param i32)))
4+
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
5+
(type $i32_i32_=>_none (func (param i32 i32)))
6+
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
7+
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 17452))
8+
(memory $0 1)
9+
(data (i32.const 1036) "\1c")
10+
(data (i32.const 1048) "\03\00\00\00\08\00\00\00\01")
11+
(table $0 2 funcref)
12+
(elem $0 (i32.const 1) $start:return~anonymous|0)
13+
(export "testVoidReturn" (func $return/testVoidReturn))
14+
(export "memory" (memory $0))
15+
(export "testVoidReturnFunction" (func $export:return/testVoidReturnFunction))
16+
(start $~start)
17+
(func $start:return~anonymous|0
18+
nop
19+
)
20+
(func $return/testVoidReturn (param $0 i32)
21+
nop
22+
)
23+
(func $~start
24+
(local $0 i32)
25+
global.get $~lib/memory/__stack_pointer
26+
i32.const 4
27+
i32.sub
28+
global.set $~lib/memory/__stack_pointer
29+
global.get $~lib/memory/__stack_pointer
30+
i32.const 1068
31+
i32.lt_s
32+
if
33+
i32.const 17472
34+
i32.const 17520
35+
i32.const 1
36+
i32.const 1
37+
call $~lib/builtins/abort
38+
unreachable
39+
end
40+
global.get $~lib/memory/__stack_pointer
41+
local.tee $0
42+
i32.const 0
43+
i32.store
44+
local.get $0
45+
i32.const 1056
46+
i32.store
47+
i32.const 1056
48+
i32.load
49+
call_indirect $0 (type $none_=>_none)
50+
global.get $~lib/memory/__stack_pointer
51+
i32.const 4
52+
i32.add
53+
global.set $~lib/memory/__stack_pointer
54+
)
55+
(func $export:return/testVoidReturnFunction (param $0 i32) (param $1 i32)
56+
global.get $~lib/memory/__stack_pointer
57+
i32.const 4
58+
i32.sub
59+
global.set $~lib/memory/__stack_pointer
60+
global.get $~lib/memory/__stack_pointer
61+
i32.const 1068
62+
i32.lt_s
63+
if
64+
i32.const 17472
65+
i32.const 17520
66+
i32.const 1
67+
i32.const 1
68+
call $~lib/builtins/abort
69+
unreachable
70+
end
71+
global.get $~lib/memory/__stack_pointer
72+
local.get $1
73+
i32.store
74+
local.get $1
75+
i32.load
76+
call_indirect $0 (type $none_=>_none)
77+
global.get $~lib/memory/__stack_pointer
78+
i32.const 4
79+
i32.add
80+
global.set $~lib/memory/__stack_pointer
81+
)
82+
)

tests/compiler/return.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
function nop(): void {}
2+
3+
export function testVoidReturn(cond: bool): void {
4+
if (cond) {
5+
return nop();
6+
}
7+
return nop();
8+
}
9+
10+
export function testVoidReturnFunction(cond: bool, fn: () => void): void {
11+
if (cond) {
12+
return fn();
13+
}
14+
return fn();
15+
}
16+
testVoidReturnFunction(true, () => nop());

tests/compiler/return.untouched.wat

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
(module
2+
(type $none_=>_none (func))
3+
(type $i32_i32_=>_none (func (param i32 i32)))
4+
(type $i32_=>_none (func (param i32)))
5+
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
6+
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
7+
(global $~argumentsLength (mut i32) (i32.const 0))
8+
(global $~lib/memory/__data_end i32 (i32.const 44))
9+
(global $~lib/memory/__stack_pointer (mut i32) (i32.const 16428))
10+
(global $~lib/memory/__heap_base i32 (i32.const 16428))
11+
(memory $0 1)
12+
(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")
13+
(table $0 2 funcref)
14+
(elem $0 (i32.const 1) $start:return~anonymous|0)
15+
(export "testVoidReturn" (func $return/testVoidReturn))
16+
(export "memory" (memory $0))
17+
(export "testVoidReturnFunction" (func $export:return/testVoidReturnFunction))
18+
(start $~start)
19+
(func $return/nop
20+
nop
21+
)
22+
(func $start:return~anonymous|0
23+
call $return/nop
24+
)
25+
(func $return/testVoidReturnFunction (param $0 i32) (param $1 i32)
26+
local.get $0
27+
if
28+
i32.const 0
29+
global.set $~argumentsLength
30+
local.get $1
31+
i32.load
32+
call_indirect $0 (type $none_=>_none)
33+
return
34+
end
35+
i32.const 0
36+
global.set $~argumentsLength
37+
local.get $1
38+
i32.load
39+
call_indirect $0 (type $none_=>_none)
40+
)
41+
(func $return/testVoidReturn (param $0 i32)
42+
local.get $0
43+
if
44+
call $return/nop
45+
return
46+
end
47+
call $return/nop
48+
)
49+
(func $~start
50+
call $start:return
51+
)
52+
(func $~stack_check
53+
global.get $~lib/memory/__stack_pointer
54+
global.get $~lib/memory/__data_end
55+
i32.lt_s
56+
if
57+
i32.const 16448
58+
i32.const 16496
59+
i32.const 1
60+
i32.const 1
61+
call $~lib/builtins/abort
62+
unreachable
63+
end
64+
)
65+
(func $start:return
66+
(local $0 i32)
67+
global.get $~lib/memory/__stack_pointer
68+
i32.const 4
69+
i32.sub
70+
global.set $~lib/memory/__stack_pointer
71+
call $~stack_check
72+
global.get $~lib/memory/__stack_pointer
73+
i32.const 0
74+
i32.store
75+
i32.const 1
76+
i32.const 32
77+
local.set $0
78+
global.get $~lib/memory/__stack_pointer
79+
local.get $0
80+
i32.store
81+
local.get $0
82+
call $return/testVoidReturnFunction
83+
global.get $~lib/memory/__stack_pointer
84+
i32.const 4
85+
i32.add
86+
global.set $~lib/memory/__stack_pointer
87+
)
88+
(func $export:return/testVoidReturnFunction (param $0 i32) (param $1 i32)
89+
global.get $~lib/memory/__stack_pointer
90+
i32.const 4
91+
i32.sub
92+
global.set $~lib/memory/__stack_pointer
93+
call $~stack_check
94+
global.get $~lib/memory/__stack_pointer
95+
local.get $1
96+
i32.store
97+
local.get $0
98+
local.get $1
99+
call $return/testVoidReturnFunction
100+
global.get $~lib/memory/__stack_pointer
101+
i32.const 4
102+
i32.add
103+
global.set $~lib/memory/__stack_pointer
104+
)
105+
)

0 commit comments

Comments
 (0)