From bb9ce86e52f1a0c209b87301ee388e401ac2533f Mon Sep 17 00:00:00 2001 From: Benoit Vey Date: Tue, 14 Feb 2017 23:12:29 +0100 Subject: [PATCH] Make sure finalisers are called for embedded fields 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. --- minimal-cases/finalisers/finalisers.pony | 6 +++++ src/libponyc/codegen/genfun.c | 30 ++++++++++++++++++++++++ src/libponyc/reach/reach.c | 8 +++++++ 3 files changed, 44 insertions(+) diff --git a/minimal-cases/finalisers/finalisers.pony b/minimal-cases/finalisers/finalisers.pony index d3c5075f0a..0d13feaa60 100644 --- a/minimal-cases/finalisers/finalisers.pony +++ b/minimal-cases/finalisers/finalisers.pony @@ -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()) diff --git a/src/libponyc/codegen/genfun.c b/src/libponyc/codegen/genfun.c index a66ec3132a..6e8e4201ba 100644 --- a/src/libponyc/codegen/genfun.c +++ b/src/libponyc/codegen/genfun.c @@ -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" @@ -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); @@ -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); @@ -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) diff --git a/src/libponyc/reach/reach.c b/src/libponyc/reach/reach.c index ab695d98fa..b4d34c11c4 100644 --- a/src/libponyc/reach/reach.c +++ b/src/libponyc/reach/reach.c @@ -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);