Created attachment 41268 [details] Fortran code with a finalize subroutine When I compile and run the attached code, the subroutine 't_final' gets called once. > gfortran prog.f90 && ./a.out In SUBROUTINE t_final The PGI compiler gives the same behaviour. However, 't_final' is called three times when the Intel (v17.0.1) compiler is used: > ifort prog.f90 && ./a.out In SUBROUTINE t_final In SUBROUTINE t_final In SUBROUTINE t_final I initially assumed that Intel was wrong and sent them a bug report. (It's out-voted and the behaviour is unintuitive.) However, Intel claim that their behaviour is correct. I shall post Intel's response in a follow-up message.
Intel's response: Hello Andrew, Our development team investigated the issue and determined the following: --------------- Finalization is a tricky thing - there are many different events that trigger a call to a finalizer. Although it may not seem so at first, this program *should* call the finalizer 3 times, not once. The first finalization occurs as a result of this line of code: u = new_t() Here, 'u' is finalized as a result of the following text from the F2008 standard: "When an intrinsic assignment statement is executed, the variable is finalized after evaluation of expr and before the definition of the variable." 'Variable' in this line of code is 'u'. Since this is an intrinsic assignment statement, 'u' is the first object to be finalized. The second finalization occurs because of the same line of user code, but this time it's the result of 'new_t' that is finalized according to the following text: " If an executable construct references a function, the result is finalized after execution of the innermost executable construct containing the reference." Because of this rule, the result of the call to 'new_t' is finalized next. The third finalization occurs at the end of routine 's'. The standard says: "A nonpointer, nonallocatable object that is not a dummy argument or function result is finalized immediately before it would become undefined due to execution of a RETURN or END statement" The variable 'u' is a nonpointer, nonallocatable object that is not a dummy argument or function result, so it is finalized at the end of the routine 's'. So the three calls to the finalizer are correct and the results from PGI Fortran are incorrect. ------------------- Since this is not a Intel Fortran Compiler defect, I am closing this case. Do not hesitate to contact me if you have any other questions or need further assistance with Intel® Software Development Products. Thank you for contacting Intel® Online Service Center, Devorah Intel® Developer Support
Confirmed from 4.9 up to trunk (8.0).
I think this depends a lot on the compiler implementation. Declaring a type does not define an instance of it. I can see that since new_t is returning a function result which could be a pointer, the compiler could end up only creating one instance.
(In reply to Jerry DeLisle from comment #3) > I think this depends a lot on the compiler implementation. I don't actually think the calling of finalization routines is supposed to depend on the compiler implementation. F08 is rather clear on where it should happen, I guess. For the missing cases, see also: * PR 64290 (No finalization at deallocation of LHS) * PR 65347 (Final subroutine not called for function result) and PR 37336 comment 27 which includes both of them.
(In reply to janus from comment #4) > (In reply to Jerry DeLisle from comment #3) > > I think this depends a lot on the compiler implementation. > > I don't actually think the calling of finalization routines is supposed to > depend on the compiler implementation. F08 is rather clear on where it > should happen, I guess. > > For the missing cases, see also: > * PR 64290 (No finalization at deallocation of LHS) > * PR 65347 (Final subroutine not called for function result) > > and PR 37336 comment 27 which includes both of them. My only thought here is that those conditions may be A or B or C vs A and B and C If the finalization is to for example deallocate something, would we end up trying to deallocate more than once? It seems to me that it depends on how many actual instances of the object were created.
For info, the actual code I was developing did use the finalization subroutine to deallocate pointers, which resulted in seg faults when compiled with ifort. This was how I discovered the problem. I also reported the problem to PGI. The latest version of pgfortran (17.7) now produces the same behaviour as ifort: > pgfortran -V pgfortran 17.5-0 64-bit target on x86-64 Linux -tp haswell PGI Compilers and Tools Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. > pgfortran prog.f90 && ./a.out In SUBROUTINE t_final > pgfortran -V pgfortran 17.7-0 64-bit target on x86-64 Linux -tp haswell PGI Compilers and Tools Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. > pgfortran prog.f90 && ./a.out In SUBROUTINE t_final In SUBROUTINE t_final In SUBROUTINE t_final
This is still present in the actual trunk.
(In reply to Jürgen Reuter from comment #7) > This is still present in the actual trunk. I can confirm that Intel's interpretation is completely correct. I have a patch in progress that issues the correct number of final calls but which is ordering the evaluation of the function result and the finalization of the lhs in the wrong order. Paul
The master branch has been updated by Paul Thomas <pault@gcc.gnu.org>: https://gcc.gnu.org/g:d7caf313525a46f200d7f5db1ba893f853774aee commit r13-6747-gd7caf313525a46f200d7f5db1ba893f853774aee Author: Paul Thomas <pault@gcc.gnu.org> Date: Sat Mar 18 07:56:23 2023 +0000 Fortran: Fix bugs and missing features in finalization [PR37336] 2023-03-18 Paul Thomas <pault@gcc.gnu.org> gcc/fortran PR fortran/103854 PR fortran/96122 PR fortran/37336 * class.cc (finalize_component): Include the missing arguments in the call to the component's finalizer wrapper. (has_finalizer_component): Do not return true for procedure pointer components. (finalizer_insert_packed_call): Remove the redundant argument in the call to the final subroutine. (generate_finalization_wrapper): Add support for assumed rank finalizers. (gfc_may_be_finalized): New helper function. * dump-parse-tree.cc (write_proc): Whitespace. * gfortran.h : Add prototype for gfc_may_be_finalized. * resolve.cc (resolve_function): Correct derived types that have an incomplete namespace. (resolve_where, gfc_resolve_where_code_in_forall, gfc_resolve_forall_body, gfc_resolve_code): Check that the op code is still EXEC_ASSIGN. If it is set lhs to must finalize. (is_finalizable_type): New function. (generate_component_assignments): Set must_finalize if needed. (gfc_resolve_finalizers): Error if assumed rank finalizer is not the only one. Warning on lack of scalar finalizer modified to account for assumed rank finalizers. (generate_final_call): New function. (generate_component_assignments): Enclose the outermost call in a block to capture automatic deallocation and final calls. Set must_finalize as required to satisfy the standards. Use an explicit pointer assignment for pointer components to capture finalization of the target. Likewise use explicit assignment for allocatable components. Do not use the temporary copy of the lhs in defined assignment if the component is allocatable. Put the temporary in the same namespace as the lhs symbol if the component may be finalized. Remove the leading assignment from the expansion of assignment of components that have their own defined assignment components. Suppress finalization of assignment of temporary components to the lhs. Make an explicit final call for the rhs function temporary if it exists. (gfc_resolve_code): Set must_finalize for assignments with an array constructor on the rhs. (gfc_resolve_finalizers): Ensure that an assumed rank finalizer is the only finalizer for that type and correct the surprising warning for the lack of a scalar finalizer. (check_defined_assignments): Handle allocatable components. (resolve_fl_derived): Set referenced the vtab for use associated symbols. (resolve_symbol): Set referenced an unreferenced symbol that will be finalized. * trans-array.cc (gfc_trans_array_constructor_value): Add code to finalize the constructor result. Warn that this feature was removed in F2018 and that it is suppressed by -std=2018. (trans_array_constructor): Add finalblock, pass to previous and apply to loop->post if filled. (gfc_add_loop_ss_code): Add se finalblock to outer loop post. (gfc_trans_array_cobounds, gfc_trans_array_bounds): Add any generated finalization code to the main block. (structure_alloc_comps): Add boolean argument to suppress finalization and use it for calls from gfc_deallocate_alloc_comp_no_caf. Otherwise it defaults to false. (gfc_copy_alloc_comp_no_fini): New wrapper for structure_alloc_comps. (gfc_alloc_allocatable_for_assignment): Suppress finalization by setting new arg in call to gfc_deallocate_alloc_comp_no_caf. (gfc_trans_deferred_array): Use gfc_may_be_finalized and do not deallocate the components of entities with a leading '_' in the name that are also marked as artificial. * trans-array.h : Add the new boolean argument to the prototype of gfc_deallocate_alloc_comp_no_caf with a default of false. Add prototype for gfc_copy_alloc_comp_no_fini. * trans-decl.cc(init_intent_out_dt): Tidy up the code. * trans-expr.cc (gfc_init_se): Initialize finalblock. (gfc_conv_procedure_call): Use gfc_finalize_tree_expr to finalize function results. Replace in-line block for class results with call to new function. (gfc_conv_expr): Finalize structure constructors for F2003 and F2008. Warn that this feature was deleted in F2018 and, unlike array constructors, is not default. Add array constructor finalblock to the post block. (gfc_trans_scalar_assign): Suppress finalization by setting new argument in call to gfc_deallocate_alloc_comp_no_caf. Add the finalization blocks to the main block. (gfc_trans_arrayfunc_assign): Use gfc_assignment_finalizer_call and ensure that finalization occurs after the evaluation of the rhs but using the initial value for the lhs. Finalize rhs function results using gfc_finalize_tree_expr. (trans_class_assignment, gfc_trans_assignment_1): As previous function, taking care to order evaluation, assignment and finalization correctly. * trans-io.cc (gfc_trans_transfer): Add the final block. * trans-stmt.cc (gfc_trans_call, gfc_trans_allocate): likewise. (trans_associate_var): Nullify derived allocatable components and finalize function targets with defined assignment components on leaving the block scope. (trans_allocate): Finalize source expressions, if required, and set init_expr artificial temporarily to suppress the finalization in gfc_trans_assignment. * trans.cc (gfc_add_finalizer_call): Do not finalize the temporaries generated in type assignment with defined assignment components. (gfc_assignment_finalizer_call): New function. (gfc_finalize_tree_expr): New function. * trans.h: Add finalblock to gfc_se. Add the prototypes for gfc_finalize_tree_expr and gfc_assignment_finalizer_call. gcc/testsuite/ PR fortran/64290 * gfortran.dg/finalize_38.f90 : New test. * gfortran.dg/finalize_38a.f90 : New test. * gfortran.dg/allocate_with_source_25.f90 : The number of final calls goes down from 6 to 4. * gfortran.dg/associate_25.f90 : Remove the incorrect comment. * gfortran.dg/auto_dealloc_2.f90 : Change the tree dump expr but the final count remains the same. * gfortran.dg/unlimited_polymorphic_8.f90 : Tree dump reveals foo.1.x rather than foo.0.x PR fortran/67444 * gfortran.dg/finalize_39.f90 : New test. PR fortran/67471 * gfortran.dg/finalize_40.f90 : New test. PR fortran/69298 PR fortran/70863 * gfortran.dg/finalize_41.f90 : New test. PR fortran/71798 * gfortran.dg/finalize_42.f90 : New test. PR fortran/80524 * gfortran.dg/finalize_43.f90 : New test. PR fortran/82996 * gfortran.dg/finalize_44.f90 : New test. PR fortran/84472 * gfortran.dg/finalize_45.f90 : New test. PR fortran/88735 PR fortran/93691 * gfortran.dg/finalize_46.f90 : New test. PR fortran/91316 * gfortran.dg/finalize_47.f90 : New test. PR fortran/106576 * gfortran.dg/finalize_48.f90 : New test. PR fortran/37336 * gfortran.dg/finalize_49.f90 : New test. * gfortran.dg/finalize_50.f90 : New test. * gfortran.dg/finalize_51.f90 : New test.
Mainline now behaves in the same way as nagfor and ifort with this testcase. I'll ask to backport to 12-branch in a few weeks. Cheers Paul