Skip to content

Commit 34a6175

Browse files
committed
Fix code generation failure for iftype with union return type
gen_iftype() returned the raw branch value without casting it to the overall expression type. When the iftype's type was a union (e.g. (U8 | None)) but the branch produced a primitive (e.g. U8), gen_box() received the union type and couldn't find a matching primitive representation, causing an internal compiler error. Apply the same pattern gen_if() uses: cast the selected branch's value from the branch type to the iftype's overall type via gen_assign_cast(). Closes #3770
1 parent 4e18e41 commit 34a6175

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## Fix code generation failure for iftype with union return type
2+
3+
Previously, using `iftype` in a function that returns a union type caused an internal compiler error during code generation:
4+
5+
```pony
6+
actor Main
7+
new create(env: Env) =>
8+
test[U8]()
9+
10+
fun test[T: U8](): (U8 | None) =>
11+
iftype T <: U8 then
12+
0
13+
end
14+
```
15+
16+
```
17+
Error:
18+
main.pony:5:3: internal failure: code generation failed for method Main_ref_test_U8_val_o
19+
```
20+
21+
This has been fixed. Functions using `iftype` with union return types now compile and run correctly.

src/libponyc/codegen/gencontrol.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ LLVMValueRef gen_if(compile_t* c, ast_t* ast)
177177

178178
LLVMValueRef gen_iftype(compile_t* c, ast_t* ast)
179179
{
180+
bool needed = is_result_needed(ast);
180181
AST_GET_CHILDREN(ast, left, right);
181182
AST_GET_CHILDREN(left, subtype, supertype, body);
182183

@@ -190,10 +191,28 @@ LLVMValueRef gen_iftype(compile_t* c, ast_t* ast)
190191
ast_free_unattached(r_sub);
191192
ast_free_unattached(r_super);
192193

193-
if(is_sub)
194-
return gen_expr(c, body);
194+
ast_t* branch = is_sub ? body : right;
195+
LLVMValueRef value = gen_expr(c, branch);
196+
197+
if(value == NULL || value == GEN_NOVALUE || value == GEN_NOTNEEDED)
198+
return value;
195199

196-
return gen_expr(c, right);
200+
// Cast the branch value to the iftype's overall type. Without this,
201+
// returning a primitive (e.g. U8) from an iftype with a union result type
202+
// (e.g. (U8 | None)) fails in gen_box because it receives the union type
203+
// instead of the primitive's own type for boxing.
204+
if(needed && !ast_checkflag(ast, AST_FLAG_JUMPS_AWAY))
205+
{
206+
ast_t* type = deferred_reify(reify, ast_type(ast), c->opt);
207+
compile_type_t* c_t = (compile_type_t*)reach_type(c->reach, type)->c_type;
208+
209+
ast_t* branch_type = deferred_reify(reify, ast_type(branch), c->opt);
210+
value = gen_assign_cast(c, c_t->use_type, value, branch_type);
211+
ast_free_unattached(branch_type);
212+
ast_free_unattached(type);
213+
}
214+
215+
return value;
197216
}
198217

199218
LLVMValueRef gen_while(compile_t* c, ast_t* ast)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
actor Main
2+
new create(env: Env) =>
3+
let r1 = test_then[U8]()
4+
let r2 = test_else[None]()
5+
6+
match (r1, r2)
7+
| (U8(0), None) => None
8+
else
9+
env.exitcode(1)
10+
end
11+
12+
fun test_then[T: U8](): (U8 | None) =>
13+
iftype T <: U8 then
14+
0
15+
end
16+
17+
fun test_else[T: None](): (U8 | None) =>
18+
iftype T <: U8 then
19+
0
20+
else
21+
None
22+
end

0 commit comments

Comments
 (0)