Skip to content

Commit

Permalink
Make sure finalisers are called for embedded fields (#1586)
Browse files Browse the repository at this point in the history
Previously, finalisers of embedded fields would never be called due to
embedded fields being transparent to the runtime. This change adds
calls to the adequate finalisers in parent objects' finalisers when
said parents contain embedded fields.

Closes #1551.
  • Loading branch information
Benoit Vey authored and sylvanc committed Feb 15, 2017
1 parent e042856 commit 087ebc7
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 0 deletions.
6 changes: 6 additions & 0 deletions minimal-cases/finalisers/finalisers.pony
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ primitive PrimitiveInitFinal
fun _final() =>
@printf[I32]("primitive final\n".cstring())

class EmbedFinal
fun _final() =>
@printf[I32]("embed final\n".cstring())

class ClassFinal
embed f: EmbedFinal = EmbedFinal

fun _final() =>
@printf[I32]("class final\n".cstring())

Expand Down
30 changes: 30 additions & 0 deletions src/libponyc/codegen/genfun.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "gentrace.h"
#include "gencontrol.h"
#include "genexpr.h"
#include "genreference.h"
#include "../pass/names.h"
#include "../type/assemble.h"
#include "../type/subtype.h"
Expand Down Expand Up @@ -216,6 +217,7 @@ static void make_prototype(compile_t* c, reach_type_t* t,
{
// Store the finaliser and use the C calling convention and an external
// linkage.
assert(t->final_fn == NULL);
t->final_fn = m->func;
LLVMSetFunctionCallConv(m->func, LLVMCCallConv);
LLVMSetLinkage(m->func, LLVMExternalLinkage);
Expand Down Expand Up @@ -285,6 +287,31 @@ static void add_dispatch_case(compile_t* c, reach_type_t* t, ast_t* params,
ponyint_pool_free_size(buf_size, args);
}

static void call_embed_finalisers(compile_t* c, reach_type_t* t,
LLVMValueRef obj)
{
uint32_t base = 0;
if(t->underlying != TK_STRUCT)
base++;

if(t->underlying == TK_ACTOR)
base++;

for(uint32_t i = 0; i < t->field_count; i++)
{
reach_field_t* field = &t->fields[i];
if(!field->embed)
continue;

LLVMValueRef final_fn = field->type->final_fn;
if(final_fn == NULL)
continue;

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

static bool genfun_fun(compile_t* c, reach_type_t* t, reach_method_t* m)
{
assert(m->func != NULL);
Expand All @@ -295,6 +322,9 @@ static bool genfun_fun(compile_t* c, reach_type_t* t, reach_method_t* m)
codegen_startfun(c, m->func, m->di_file, m->di_method);
name_params(c, t, m, params, m->func);

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

LLVMValueRef value = gen_expr(c, body);

if(value == NULL)
Expand Down
8 changes: 8 additions & 0 deletions src/libponyc/reach/reach.c
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,14 @@ static void reachable_method(reach_t* r, ast_t* type, const char* name,

if((n->id == TK_FUN) && ((n->cap == TK_BOX) || (n->cap == TK_TAG)))
{
if(name == stringtab("_final"))
{
// If the method is a finaliser, don't mark the ref and val versions as
// reachable.
assert(n->cap == TK_BOX);
return;
}

// TODO: if it doesn't use this-> in a constructor, we could reuse the
// function, which means always reuse in a fun tag
bool subordinate = (n->cap == TK_TAG);
Expand Down

0 comments on commit 087ebc7

Please sign in to comment.