From e7ee27ede3d71a4f8d206848fc042d1e6f7222e4 Mon Sep 17 00:00:00 2001 From: Benoit Vey Date: Tue, 10 Apr 2018 20:02:55 +0200 Subject: [PATCH] Fix code generation failure with tuples in recover blocks This fixes a bug where code generation would fail when trying to convert a tuple from a recover block to an abstract type. This was occurring because the `gen_assign_cast` function was expecting the tuple to have the LLVM type corresponding to the Pony type outside of the recover block, but it was encountering the type from inside of the recover block. This was failing because we use different LLVM types for tuples with differing capabilities. Objects from recover blocks are now casted to the correct expected type before being handled to their user. Closes #2639. --- src/libponyc/codegen/gencontrol.c | 21 ++++++++ src/libponyc/codegen/gencontrol.h | 2 + src/libponyc/codegen/genexpr.c | 2 +- src/libponyc/codegen/genfun.c | 89 +++++++++++++++++++------------ src/libponyc/reach/reach.c | 1 + test/libponyc/codegen.cc | 22 +++++++- 6 files changed, 100 insertions(+), 37 deletions(-) diff --git a/src/libponyc/codegen/gencontrol.c b/src/libponyc/codegen/gencontrol.c index 69c54763d9..d2cc44137d 100644 --- a/src/libponyc/codegen/gencontrol.c +++ b/src/libponyc/codegen/gencontrol.c @@ -459,6 +459,27 @@ LLVMValueRef gen_repeat(compile_t* c, ast_t* ast) return GEN_NOTNEEDED; } +LLVMValueRef gen_recover(compile_t* c, ast_t* ast) +{ + ast_t* body = ast_childidx(ast, 1); + LLVMValueRef ret = gen_expr(c, body); + + if(is_result_needed(ast)) + { + deferred_reification_t* reify = c->frame->reify; + + ast_t* type = deferred_reify(reify, ast_type(ast), c->opt); + compile_type_t* c_t = (compile_type_t*)reach_type(c->reach, type)->c_type; + ast_free_unattached(type); + + type = deferred_reify(reify, ast_type(body), c->opt); + ret = gen_assign_cast(c, c_t->use_type, ret, type); + ast_free_unattached(type); + } + + return ret; +} + LLVMValueRef gen_break(compile_t* c, ast_t* ast) { ast_t* body = ast_child(ast); diff --git a/src/libponyc/codegen/gencontrol.h b/src/libponyc/codegen/gencontrol.h index 6b4ec15c9e..d25ed2bc01 100644 --- a/src/libponyc/codegen/gencontrol.h +++ b/src/libponyc/codegen/gencontrol.h @@ -16,6 +16,8 @@ LLVMValueRef gen_while(compile_t* c, ast_t* ast); LLVMValueRef gen_repeat(compile_t* c, ast_t* ast); +LLVMValueRef gen_recover(compile_t* c, ast_t* ast); + LLVMValueRef gen_break(compile_t* c, ast_t* ast); LLVMValueRef gen_continue(compile_t* c, ast_t* ast); diff --git a/src/libponyc/codegen/genexpr.c b/src/libponyc/codegen/genexpr.c index 543295035e..565fd44b6b 100644 --- a/src/libponyc/codegen/genexpr.c +++ b/src/libponyc/codegen/genexpr.c @@ -116,7 +116,7 @@ LLVMValueRef gen_expr(compile_t* c, ast_t* ast) } case TK_RECOVER: - ret = gen_expr(c, ast_childidx(ast, 1)); + ret = gen_recover(c, ast); break; case TK_BREAK: diff --git a/src/libponyc/codegen/genfun.c b/src/libponyc/codegen/genfun.c index 0fca757578..c3096ef1e0 100644 --- a/src/libponyc/codegen/genfun.c +++ b/src/libponyc/codegen/genfun.c @@ -723,7 +723,7 @@ static bool genfun_allocator(compile_t* c, reach_type_t* t) } static bool genfun_forward(compile_t* c, reach_type_t* t, - reach_method_name_t* n, reach_method_t* m) + reach_method_name_t* n, reach_method_t* m) { compile_method_t* c_m = (compile_method_t*)m->c_method; pony_assert(c_m->func != NULL); @@ -761,6 +761,52 @@ static bool genfun_forward(compile_t* c, reach_type_t* t, return true; } +static bool genfun_method(compile_t* c, reach_type_t* t, + reach_method_name_t* n, reach_method_t* m) +{ + if(m->intrinsic) + { + if(m->internal && (n->name == c->str__final)) + { + if(!genfun_implicit_final(c, t, m)) + return false; + } + } else if(m->forwarding) { + if(!genfun_forward(c, t, n, m)) + return false; + } else { + switch(ast_id(m->fun->ast)) + { + case TK_NEW: + if(t->underlying == TK_ACTOR) + { + if(!genfun_newbe(c, t, m)) + return false; + } else { + if(!genfun_new(c, t, m)) + return false; + } + break; + + case TK_BE: + if(!genfun_be(c, t, m)) + return false; + break; + + case TK_FUN: + if(!genfun_fun(c, t, m)) + return false; + break; + + default: + pony_assert(0); + return false; + } + } + + return true; +} + void genfun_param_attrs(compile_t* c, reach_type_t* t, reach_method_t* m, LLVMValueRef fun) { @@ -900,44 +946,17 @@ bool genfun_method_bodies(compile_t* c, reach_type_t* t) while((m = reach_mangled_next(&n->r_mangled, &j)) != NULL) { - if(m->intrinsic) + if(!genfun_method(c, t, n, m)) { - if(m->internal && (n->name == c->str__final)) + if(errors_get_count(c->opt->check.errors) == 0) { - if(!genfun_implicit_final(c, t, m)) - return false; + pony_assert(m->fun != NULL); + ast_error(c->opt->check.errors, m->fun->ast, + "internal failure: code generation failed for method %s", + m->full_name); } - } else if(m->forwarding) { - if(!genfun_forward(c, t, n, m)) - return false; - } else { - switch(ast_id(m->fun->ast)) - { - case TK_NEW: - if(t->underlying == TK_ACTOR) - { - if(!genfun_newbe(c, t, m)) - return false; - } else { - if(!genfun_new(c, t, m)) - return false; - } - break; - - case TK_BE: - if(!genfun_be(c, t, m)) - return false; - break; - - case TK_FUN: - if(!genfun_fun(c, t, m)) - return false; - break; - default: - pony_assert(0); - return false; - } + return false; } } } diff --git a/src/libponyc/reach/reach.c b/src/libponyc/reach/reach.c index f05a4bb8ca..044520b4ed 100644 --- a/src/libponyc/reach/reach.c +++ b/src/libponyc/reach/reach.c @@ -1433,6 +1433,7 @@ static void reachable_expr(reach_t* r, deferred_reification_t* reify, case TK_WHILE: case TK_REPEAT: case TK_TRY: + case TK_RECOVER: { if(is_result_needed(ast) && !ast_checkflag(ast, AST_FLAG_JUMPS_AWAY)) { diff --git a/test/libponyc/codegen.cc b/test/libponyc/codegen.cc index 27c43f8d3b..d837e5b8e3 100644 --- a/test/libponyc/codegen.cc +++ b/test/libponyc/codegen.cc @@ -368,7 +368,7 @@ TEST_F(CodegenTest, ViewpointAdaptedFieldReach) TEST_F(CodegenTest, StringSerialization) { - // From issue 2245 + // From issue #2245 const char* src = "use \"serialise\"\n" @@ -623,3 +623,23 @@ TEST_F(CodegenTest, DescTable) ASSERT_EQ(type_id->getZExtValue(), i); } } + + +TEST_F(CodegenTest, RecoverCast) +{ + // From issue #2639 + const char* src = + "class A\n" + " new create() => None\n" + + "actor Main\n" + " new create(env: Env) =>\n" + " let x: ((None, None) | (A iso, None)) =\n" + " if false then\n" + " (None, None)\n" + " else\n" + " recover (A, None) end\n" + " end"; + + TEST_COMPILE(src); +}