F2003 Finalization -- progress and problems
Tobias Burnus
burnus@net-b.de
Tue Jun 10 10:02:00 GMT 2008
Hi Daniel,
Daniel Kraft wrote in an earlier email:
> After finishing the code, I want to write some little documentation
> about how finalization works and is implemented if you want so in the
> future everyone has it easier to work on it (now that I still know how
> it works...), if you point me where to write this.
GREAT idea! I think gfc-internals.texi is the best place, see:
http://gcc.gnu.org/onlinedocs/gfc-internals/
(An alternative would be the Wiki, but I think gfc-internals.texi is
better.)
> c) what exactly does that "specification expression in scoping unit"
> mean? But otherwise, this sounds similar to b).
"If a specification expression in a scoping unit references a function,
the result is finalized before execution of the first executable
statement in the scoping unit."
Well, a specification expression is for instance
subroutine bar(x)
integer :: x
character(len=func(x)) :: str
which could be translated into the following tree-like pseudo code:
{
type(func_type) *tmp
tmp = func(x)
malloc(str, tmp->len)
finalize(tmp)
}
(Assuming that func returns an a derived type and "=" is overloaded to
return the "len" component.)
If there are several specification expressions, one could finalize them
either directly after using them (as we will probably do) or one could
defer it and finalize them one after another just before first
executable statement comes.
Actually, there might be more restrictions:
"7.1.6 Specification expression"
"R729 specification-expr is scalar-int-expr"
"C710 (R729) The scalar-int-expr shall be a restricted expression."
"A *restricted expression* is an expression [...], and where any final
subroutine that is invoked is *pure*."
> b) results of functions and structure-constructors after they have been
> used: I'm not sure how this could be done in resolution-phase (without
> "rewriting" the code and introducing temporaries), but maybe in trans
> this will be easier. Any ideas and tipps?
I don't quite understand what with "after they have been used" is meant
- nor can I find it in the standard. (Currently, I have no opinion
whether on should use trans*.c or resolve*.c.)
> e) intrinsic assignment: for ordinary assignments this is handled
What do you mean by ordinary assignments? If you mean a simple
type(t) :: x, y
y = x
then this is an intrinsic assignment unless you have "assignment(=)"
anywhere. Using
interface assignment(=)
subroutine assign_t(x,y)
the finalization happens automatically as the LHS argument matches a
INTENT(OUT) in assign_t.
> OTOH, for assignments inside WHERE/FORALL I still have to look into
> it; maybe this
> will be easy, too, but there's at least something I'm thinking about:
>
> WHERE (arr == 2)
> arr = 5
> END WHERE
>
> Should this finalize the whole masked arr at once or each element before
> it is overwritten? Can there be a similar unclarity for FORALL?
I really dislike WHERE as it is (at least to me) a bit intransparent
what happens there. Maybe something to ask at lang.comp.fortran.
From the standard:
"If a nonelemental function reference occurs in the expr or variable of
a where-assignment-stmt or in a mask-expr, the function is evaluated
without any masked control; that is, all of its argument expressions are
fully evaluated and the function is fully evaluated. If the result is an
array and the reference is not within the argument list of a
nonelemental function, elements corresponding to true values in the
control mask are selected for use in evaluating the expr, variable or
mask-expr.
If an elemental operation or function reference occurs in the expr or
variable of a where-assignment-stmt or in a mask-expr, and is not within
the argument list of a nonelemental function reference, the operation is
performed or the function is evaluated only for the elements
corresponding to true values of the control mask."
I read this as follows, but now guarantees that I interpret the standard
correctly.
{
tmp = function(array)
finalize(array(mask))
array(mask) = tmp(mask)
}
or
{
finalize(array(mask))
array(mask) = array2(mask)
}
or
{
finalize(array(mask))
array(mask) = elementalfunc(array2(mask))
}
FORALL: I have not yet fully understand how this should be handled, but
note:
"C738 (R756) Any procedure referenced in a forall-body-construct,
including one referenced by a defined operation, assignment, or
*finalization*, shall be a *pure* procedure."
And for PURE procedures:
"C1273 Any procedure referenced *in a pure subprogram*, including one
referenced via a defined operation, assignment, or *finalization*, shall
be *pure*."
See also examples "C.1.8 Final subroutines", though they do not apply
yet (parameterized derived types and type extension is not yet implemented).
Daniel Kraft wrote:
> Ok, so in this case I'll assume INTENT(INOUT) and don't finalize the
> arguments.
Yes. Only if the interface is known (explicit interface) and the intent
== INTENT_OUT.
> I was more thinking about the expression "x + foobar (x)" in itself
> rather than the I/O; that's because foobar (x) changes the value of x
> and I wanted to know whether it was defined or not if the first x had
> the earlier or later value.
Found it. "7.1.8 Evaluation of operations":
"[...] The evaluation of a function reference shall neither affect nor
be affected by the evaluation of any other entity within the statement.
If a function reference causes definition or undefinition of an actual
argument of the function, that argument or any associated entities shall
not appear elsewhere in the same statement. However, execution of a
function reference in the logical expression in an IF statement
(8.1.2.4), the mask expression in a WHERE statement (7.4.3.1), or the
subscripts and strides in a FORALL statement (7.4.4) is permitted to
define variables in the statement that is conditionally executed."
In other words: The program is invalid and the compiler can do anything.
(The saying goes this includes starting WW3.)
> I was unsure about whether to call the array-finalizer routine or the
> scalar one for each element. As in theory they could be totally
> different and I think the standard should somehow define which one
> ought to be called. But maybe this isn't the case and we "are free to
> choose"? I'd find it more intuitive if we finalized the whole array
> as for the user an ELEMENTAL-procedure is just like one taking a whole
> array. So try to finalize the array on a whole before the call and
> any scalarization of it.
I think the standard is rather clear about it:
"When an entity is finalized, the following steps are carried out in
sequence: (1) If the dynamic type of the entity has a final subroutine
whose dummy argument has the same kind type parameters and rank as the
entity being finalized, it is called with the entity as an actual
argument. Otherwise, if there is an elemental final subroutine whose
dummy argument has the same kind type parameters as the entity being
finalized, it is called with the entity as an actual argument.
Otherwise, no subroutine is called at this point."
And: "When a procedure is invoked, a nonpointer, nonallocatable object
that is an actual argument associated with an INTENT(OUT) dummy argument
is finalized."
The actual argument is an array and thus one causes the array version of
the finalization subroutine (if present).
>> + TYPE(yes_t), ALLOCATABLE :: alloc_vector(:)
>> + ! alloc_vector is deallocated automatically here
>> + ! XXX: Or not?
>
> BTW, I was a bit surprised when I first read about this nice feature
> of automatic deallocation (and automatic allocation the like)--Fortran
> isn't C++ :D
That is one of the nice features of Fortran: With allocatable, one
cannot have loose memory. If one tries to allocate an already allocated
variable, one gets an error; and when the variable goes out of scope, it
is automatically deallocated.
Additionally, all variables are by default the only reference to a chunk
of memory (C: "restricted"), which helps with the alias analysis. Only
if a variable has the attribute TARGET (or when it is a POINTER), there
might be some (other) pointer pointing to the same memory. This helps
with the compiler alias analysis and makes the program potentially faster.
Tobias
More information about the Fortran
mailing list