Skip to content

Commit

Permalink
Really always call finalisers for embedded fields
Browse files Browse the repository at this point in the history
This fixes a case overlooked in 087ebc7. In that commit, the finaliser
of an embedded field was only called if the parent object (or actor)
had an explicit finaliser. This change adds implicit finalisers to
objects that must finalise an embedded field, including recursively.
  • Loading branch information
Benoit Vey committed Mar 3, 2017
1 parent b7299f6 commit 501b27f
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 26 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ endif
# target specific build options
libponyrt.buildoptions = -DPONY_NO_ASSERT

libponyrt.tests.linkoptions += -rdynamic

libponyc.buildoptions = -D__STDC_CONSTANT_MACROS
libponyc.buildoptions += -D__STDC_FORMAT_MACROS
libponyc.buildoptions += -D__STDC_LIMIT_MACROS
Expand Down
6 changes: 4 additions & 2 deletions src/libponyc/codegen/genfun.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ static void add_dispatch_case(compile_t* c, reach_type_t* t, ast_t* params,
}

static void call_embed_finalisers(compile_t* c, reach_type_t* t,
LLVMValueRef obj)
ast_t* method_body, LLVMValueRef obj)
{
uint32_t base = 0;
if(t->underlying != TK_STRUCT)
Expand All @@ -314,7 +314,9 @@ static void call_embed_finalisers(compile_t* c, reach_type_t* t,
continue;

LLVMValueRef field_ref = LLVMBuildStructGEP(c->builder, obj, base + i, "");
codegen_debugloc(c, method_body);
LLVMBuildCall(c->builder, final_fn, &field_ref, 1, "");
codegen_debugloc(c, NULL);
}
}

Expand All @@ -329,7 +331,7 @@ static bool genfun_fun(compile_t* c, reach_type_t* t, reach_method_t* m)
name_params(c, t, m, params, m->func);

if(m->func == t->final_fn)
call_embed_finalisers(c, t, gen_this(c, NULL));
call_embed_finalisers(c, t, body, gen_this(c, NULL));

LLVMValueRef value = gen_expr(c, body);

Expand Down
111 changes: 87 additions & 24 deletions src/libponyc/reach/reach.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "reach.h"
#include "../ast/astbuild.h"
#include "../codegen/genname.h"
#include "../pass/expr.h"
#include "../type/assemble.h"
Expand Down Expand Up @@ -455,6 +456,82 @@ static void add_traits_to_type(reach_t* r, reach_type_t* t,
}
}

static void add_special(reach_t* r, reach_type_t* t, ast_t* type,
const char* special, pass_opt_t* opt)
{
special = stringtab(special);
ast_t* find = lookup_try(NULL, NULL, type, special);

if(find != NULL)
{
switch(ast_id(find))
{
case TK_NEW:
case TK_FUN:
case TK_BE:
{
reachable_method(r, t->ast, special, NULL, opt);
ast_free_unattached(find);
break;
}

default: {}
}
}
}

static void add_final(reach_t* r, reach_type_t* t, pass_opt_t* opt)
{
ast_t* def = (ast_t*)ast_data(t->ast);

BUILD(final_ast, def,
NODE(TK_FUN, AST_SCOPE
NODE(TK_BOX)
ID("_final")
NONE
NONE
NONE
NONE
NODE(TK_SEQ, NODE(TK_TRUE))
NONE
NONE));

ast_append(ast_childidx(def, 4), final_ast);
ast_set(def, stringtab("_final"), final_ast, SYM_NONE, false);
bool pop = frame_push(&opt->check, def);
bool ok = ast_passes_subtree(&final_ast, opt, PASS_FINALISER);
pony_assert(ok);
(void)ok;

if(pop)
frame_pop(&opt->check);

add_special(r, t, t->ast, "_final", opt);
}

static bool embed_has_finaliser(ast_t* ast, const char* str_final)
{
ast_t* def = (ast_t*)ast_data(ast);
if(ast_get(def, str_final, NULL) != NULL)
return true;

ast_t* members = ast_childidx(def, 4);
ast_t* member = ast_child(members);

while(member != NULL)
{
if(ast_id(member) == TK_EMBED)
{
if(embed_has_finaliser(member, str_final))
return true;
}

member = ast_sibling(member);
}

return false;
}

static void add_fields(reach_t* r, reach_type_t* t, pass_opt_t* opt)
{
ast_t* def = (ast_t*)ast_data(t->ast);
Expand Down Expand Up @@ -488,6 +565,10 @@ static void add_fields(reach_t* r, reach_type_t* t, pass_opt_t* opt)
member = ast_child(members);
size_t index = 0;

const char* str_final = stringtab("_final");
bool has_finaliser = ast_get(def, str_final, NULL) != NULL;
bool needs_finaliser = false;

while(member != NULL)
{
switch(ast_id(member))
Expand All @@ -502,12 +583,15 @@ static void add_fields(reach_t* r, reach_type_t* t, pass_opt_t* opt)

AST_GET_CHILDREN(r_member, name, type, init);

t->fields[index].embed = ast_id(member) == TK_EMBED;
bool embed = t->fields[index].embed = ast_id(member) == TK_EMBED;
t->fields[index].ast = reify(ast_type(member), typeparams, typeargs,
opt, true);
ast_setpos(t->fields[index].ast, NULL, ast_line(name), ast_pos(name));
t->fields[index].type = add_type(r, type, opt);

if(embed && !has_finaliser && !needs_finaliser)
needs_finaliser = embed_has_finaliser(type, str_final);

if(r_member != member)
ast_free_unattached(r_member);

Expand All @@ -520,30 +604,9 @@ static void add_fields(reach_t* r, reach_type_t* t, pass_opt_t* opt)

member = ast_sibling(member);
}
}

static void add_special(reach_t* r, reach_type_t* t, ast_t* type,
const char* special, pass_opt_t* opt)
{
special = stringtab(special);
ast_t* find = lookup_try(NULL, NULL, type, special);

if(find != NULL)
{
switch(ast_id(find))
{
case TK_NEW:
case TK_FUN:
case TK_BE:
{
reachable_method(r, t->ast, special, NULL, opt);
ast_free_unattached(find);
break;
}

default: {}
}
}
if(!has_finaliser && needs_finaliser)
add_final(r, t, opt);
}

static reach_type_t* add_reach_type(reach_t* r, ast_t* type)
Expand Down
106 changes: 106 additions & 0 deletions test/libponyc/codegen_final.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <gtest/gtest.h>
#include <platform.h>

#include "util.h"

#define TEST_COMPILE(src) DO(test_compile(src, "ir"))


class CodegenFinalTest : public PassTest
{};


TEST_F(CodegenFinalTest, PrimitiveInit)
{
const char* src =
"primitive PrimitiveInit\n"
" fun _init() =>\n"
" @pony_exitcode[None](I32(1))\n"

"actor Main\n"
" new create(env: Env) =>\n"
" None";

TEST_COMPILE(src);

int exit_code = 0;
ASSERT_TRUE(run_program(&exit_code));
ASSERT_EQ(exit_code, 1);
}


TEST_F(CodegenFinalTest, PrimitiveFinal)
{
const char* src =
"primitive PrimitiveFinal\n"
" fun _final() =>\n"
" @pony_exitcode[None](I32(1))\n"

"actor Main\n"
" new create(env: Env) =>\n"
" None";

TEST_COMPILE(src);

int exit_code = 0;
ASSERT_TRUE(run_program(&exit_code));
ASSERT_EQ(exit_code, 1);
}


TEST_F(CodegenFinalTest, ClassFinal)
{
const char* src =
"class ClassFinal\n"
" fun _final() =>\n"
" @pony_exitcode[None](I32(1))\n"

"actor Main\n"
" new create(env: Env) =>\n"
" ClassFinal";

TEST_COMPILE(src);

int exit_code = 0;
ASSERT_TRUE(run_program(&exit_code));
ASSERT_EQ(exit_code, 1);
}


TEST_F(CodegenFinalTest, EmbedFinal)
{
const char* src =
"class EmbedFinal\n"
" fun _final() =>\n"
" @pony_exitcode[None](I32(1))\n"

"actor Main\n"
" embed c: EmbedFinal = EmbedFinal\n"

" new create(env: Env) =>\n"
" None";

TEST_COMPILE(src);

int exit_code = 0;
ASSERT_TRUE(run_program(&exit_code));
ASSERT_EQ(exit_code, 1);
}


TEST_F(CodegenFinalTest, ActorFinal)
{
const char* src =
"actor Main\n"
" new create(env: Env) =>\n"
" None\n"

" fun _final() =>\n"
" @pony_exitcode[None](I32(1))";

TEST_COMPILE(src);

int exit_code = 0;
ASSERT_TRUE(run_program(&exit_code));
ASSERT_EQ(exit_code, 1);
}

0 comments on commit 501b27f

Please sign in to comment.