[Fortran] PR37336 - FIINAL patch [1/n]: Implement the finalization wrapper subroutine
Tobias Burnus
burnus@net-b.de
Sat Aug 25 15:21:00 GMT 2012
Dear Mikael, dear all,
Mikael Morin wrote:
> the patch mixes deallocation and finalization, which are treated
> separately in the standard.
First, I want to remark that the standard - in many cases - does not
require memory freeing ("deallocation"), it "merely" makes it possible
that one does not leak memory with allocatables. The actually freeing of
the memory is just a matter of the qualify of the implementation.
Secondly, for a polymorphic type, one does not know at compile time
whether it has allocatable components or not - nor whether it has a
finalizer or not. Hence, I do not see another possibility to have a
common _free/_final entry point in the vtable. As there has to be a
common entry point, I think it makes sense to have a single finalization
wrapper which handles both. (I had also initially thought, that one
could handle those two cases separately, but now I don't see it anymore.)
> 1. some weird cases are not correctly covered (polymorphic components,
> multiple level of finalizable and/or non-finalizable components, of
> inheritance, ...)
I do believe that polymorphic components are correctly handled: If they
are a POINTER, they are untouched but if they are ALLOCATABLE, one calls
DEALLOCATE for the component, which should handle the
finalization/deallocation correctly. (And nonallocatble, nonpointer
components do not exist.)
> I would like to point out that forcing the wrapper's array argument to
> be contiguous will lead to poor code as repacking will be needed with
> inherited types to call the parent's wrapper (and the parent's parent's,
> etc...).
I think that's unavoidable with the current array descriptor, which
assumes that the stride is always a multiple of the size of the type. I
concur that with the new array descriptor, one restrict the
copy-in/copy-out to calling explict-shape/assumed-size finalizers, which
probably do not occur in practice.
> >+finalize_component (gfc_expr *expr, gfc_symbol *derived, gfc_component *comp,
> >+ e = gfc_copy_expr (expr);
> >+ e->ref = gfc_get_ref ();
> You should walk to the end of the reference chain. Otherwise you are
> overwriting it here.
I will do this.
>> >+ if (comp->attr.allocatable
>> >+ || (comp->ts.type == BT_CLASS && CLASS_DATA (comp)
>> >+ && CLASS_DATA (comp)->attr.allocatable))
>> >+ {
>>
>> >+ }
>> >+ else if (comp->ts.type == BT_DERIVED
>> >+ && comp->ts.u.derived->f2k_derived
>> >+ && comp->ts.u.derived->f2k_derived->finalizers)
> What about polymorphic components?
I have to admit that the code is a bit implicit: polymorphic components
are either ALLOCATABLE - and hence handled in the "if" block, or they
are pointers - in which case this function is not called at all.
> What if only comp's subcomponents are finalizable, the finalization
> wrapper should still be called, shouldn't it?
Well, that's handled in the "else" branch. There, I walk all
subcomponents. I do not need to walk them in case there is a finalizer
as the called finalization wrapper will handle them.
>> >+ else
>> >+ {
>> >+ gfc_component *c;
>> >+
>> >+ gcc_assert ((comp->attr.alloc_comp || comp->attr.final_comp)
>> >+ && comp->ts.type != BT_CLASS);
>> >+ for (c = comp->ts.u.derived->components; c; c = c->next)
>> >+ if ((comp->ts.type != BT_CLASS && !comp->attr.pointer
>> >+ && (comp->attr.alloc_comp || comp->attr.allocatable
>> >+ || comp->attr.final_comp))
>> >+ || ((comp->ts.type == BT_CLASS && CLASS_DATA (comp)
>> >+ && CLASS_DATA (comp)->attr.allocatable)))
>> >+ finalize_component (e, comp->ts.u.derived, comp, stat, code);
>> >+ }
> This doesn't work, you use comp instead of c.
I hate copy-and-paste bugs. Thanks.
> If there is a polymorphic component whose declared type is not
> finalizable, but whose actual type is, the finalization wrapper should
> still be called.
But it will, as written above, polymorphic components are allocatable
(or they are pointers and won't get finalized).
> If comp has finalizable subcomponents, it has a finalization wrapper,
> which is (or should be) caught above, so this branch is (or should be)
> unreachable.
I probably miss something, but I don't see why this branch should be
unreachable. One has:
if (component is allocatable)
call DEALLOCATE(comp) ! which might invoke finalizers
else if (component itself has a finalizer)
call FINAL_WRAPPER
else
for all nonpointer subcomponents which are allocatables, have
finalizers or have allocatable/finalizable components, call
finalize_component.
end if
>> >+ block->symtree = gfc_find_symtree (sub_ns->sym_root, "c_f_pointer");
> This is useless...
I concur.
>> >+ bool alloc_comp = false;
> This is misnamed, it should be final_comp or something.
I concur, its use became extended during the development of the patch.
>
>> >+ for (comp = vtab->ts.u.derived->components; comp; comp = comp->next)
>> >+ if (comp->name[0] == '_' && comp->name[1] == 'f')
> I have no strong opinion about it, but slightly prefer strcmp (...,
> "_final") with regard to readability, and solidity against future vtab
> extensions with methods starting with "_f".
Maybe. "_" && "f" should be faster and I don't see us adding more vtable
functions. on the other hand, strcmp is safer and clearer. I also don't
have a strong opinion about that.
>
>> >+ {
>> >+ ancestor_wrapper = comp->initializer;
>> >+ break;
>> >+ }
>> >+ }
>> >+
>> >+ /* No wrapper of the ancestor and no own FINAL subroutines and
>> >+ allocatable components: Return a NULL() expression. */
>> >+ if ((!ancestor_wrapper || ancestor_wrapper->expr_type == EXPR_NULL)
>> >+ && !derived->attr.alloc_comp
> shouldn't there be `&& !derived->attr.final_comp' also?
I concur; I forgot that line when I retrofitted the case that there is a
finalizer but no allocatable componet.
>> >+ else if (comp->ts.type == BT_CLASS && CLASS_DATA (comp)
>> >+ && CLASS_DATA (comp)->attr.allocatable)
>> >+ alloc_comp = true;
> Shouldn't one assume without condition that there are allocatable or
> finalizable subcomponents when there is a polymorphic component?
Well, we do not deallocate/finalize polymorphic POINTER components.
>> >+ if (ancestor_wrapper && ancestor_wrapper->expr_type != EXPR_NULL)
>> >+ {
>> >+ last_code->next = XCNEW (gfc_code);
>> >+ last_code = last_code->next;
>> >+ last_code->op = EXEC_CALL;
>> >+ last_code->loc = gfc_current_locus;
>> >+ last_code->symtree = ancestor_wrapper->symtree;
>> >+ last_code->resolved_sym = ancestor_wrapper->symtree->n.sym;
>> >+
>> >+ last_code->ext.actual = gfc_get_actual_arglist ();
>> >+ last_code->ext.actual->expr = gfc_lval_expr_from_sym (array);
> I think a reference to the parent component is missing.
Actually, for a scalar it does not matter and for nonscalars, I still
need to write the pack/unpack support. For the latter, I am not yet sure
how to handle it best. As the Fortran standard doesn't allow
"assumed_rank%comp", this case has to be handled in some special way.
>> >diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c
>> >index 44b1900..4cafefe 100644
>> >--- a/gcc/fortran/parse.c
>> >+++ b/gcc/fortran/parse.c
>> >@@ -2250,6 +2250,16 @@ endType:
>> > sym->attr.lock_comp = 1;
>> > }
>> >
>> >+ /* Look for finalizers. */
>> >+ if (c->attr.final_comp
> c->attr.final_comp is never set.
>
> I would like to avoid if possible yet another symbol attribute set in
> three different functions in three different files and used all over the
> place. What about using a function "calculating" the predicate this time?
Maybe, however, one has then to call the function a lot of times: In
generate_finalization_wrapper for the whole type, then for the new added
components, and then for each component in finalize_component. With the
current code, the latter has a complexity of approx. O(n lg n), but one
might be able to improve it a bit by restructuring the code. (On the
other hand, "n" is probably not excessively large.)
Tobias
More information about the Fortran
mailing list