Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call finalisers for embedded fields when parent type has no finalizer. #1629

Merged
merged 1 commit into from
Mar 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,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
118 changes: 94 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,89 @@ 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)
{
switch(ast_id(ast))
{
case TK_NOMINAL:
break;

default:
return false;
}

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) &&
embed_has_finaliser(ast_type(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 +572,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 +590,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 +611,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);
}