Dear All, This has proven to be a rather vexatious bug to fix. On the face of it, using the indirect reference to the passed string length for deferred character length functions should have worked at all levels of optimization. However, setting the string length within a do loop resulted in the change not being visible within the rest of the function scope, even though the correct result was returned. This was, on the face of it, the same mechanism used for both dummies and declared results, which works fine at all levels of optimization.
In order to be as conservative as possible at this stage in the release cycle, I have resorted to the belt and braces approach of using a local variable '..result', which is nulled and returned, as appropriate, in a new helper function. So that the compiled code is consistent, I have done the same for functions with and without explicitly declared result variables. There is some dead code in 'gfc_get_symbol_decl', which could, with advantage, be replaced by a gcc_assert. In addition, gfc_trans_deferred_vars could do with some further tidying up to ensure that the logic is clear. These steps can easily be done now if other maintainers think that it is timely. Bootstraps and regtests on FC21/x86_64 - OK for trunk? Paul 2016-02-19 Paul Thomas <pa...@gcc.gnu.org> PR fortran/69423 * trans-decl.c (create_function_arglist): Deferred character length functions, with and without declared results, address the passed reference type as '.result' and the local string length as '..result'. (gfc_null_and_pass_deferred_len): Helper function to null and return deferred string lengths, as needed. (gfc_trans_deferred_vars): Call it, thereby reducing repeated code, add call for deferred arrays and reroute pointer function results. Avoid using 'tmp' for anything other that a temporary tree by introducing 'type_of_array' for the arrayspec type. 2016-02-19 Paul Thomas <pa...@gcc.gnu.org> PR fortran/69423 * gfortran.dg/deferred_character_15.f90 : New test.
Index: gcc/fortran/trans-decl.c =================================================================== *** gcc/fortran/trans-decl.c (revision 233507) --- gcc/fortran/trans-decl.c (working copy) *************** create_function_arglist (gfc_symbol * sy *** 2234,2240 **** PARM_DECL, get_identifier (".__result"), len_type); ! if (!sym->ts.u.cl->length) { sym->ts.u.cl->backend_decl = length; TREE_USED (length) = 1; --- 2234,2245 ---- PARM_DECL, get_identifier (".__result"), len_type); ! if (POINTER_TYPE_P (len_type)) ! { ! sym->ts.u.cl->passed_length = length; ! TREE_USED (length) = 1; ! } ! else if (!sym->ts.u.cl->length) { sym->ts.u.cl->backend_decl = length; TREE_USED (length) = 1; *************** create_function_arglist (gfc_symbol * sy *** 2271,2283 **** type = gfc_sym_type (arg); arg->backend_decl = backend_decl; type = build_reference_type (type); - - if (POINTER_TYPE_P (len_type)) - { - sym->ts.u.cl->passed_length = length; - sym->ts.u.cl->backend_decl = - build_fold_indirect_ref_loc (input_location, length); - } } } --- 2276,2281 ---- *************** init_intent_out_dt (gfc_symbol * proc_sy *** 3917,3922 **** --- 3915,3976 ---- } + /* Helper function to manage deferred string lengths. */ + + static tree + gfc_null_and_pass_deferred_len (gfc_symbol *sym, stmtblock_t *init, + locus *loc) + { + tree tmp; + + /* Character length passed by reference. */ + tmp = sym->ts.u.cl->passed_length; + tmp = build_fold_indirect_ref_loc (input_location, tmp); + tmp = fold_convert (gfc_charlen_type_node, tmp); + + if (!sym->attr.dummy || sym->attr.intent == INTENT_OUT) + /* Zero the string length when entering the scope. */ + gfc_add_modify (init, sym->ts.u.cl->backend_decl, + build_int_cst (gfc_charlen_type_node, 0)); + else + { + tree tmp2; + + tmp2 = fold_build2_loc (input_location, MODIFY_EXPR, + gfc_charlen_type_node, + sym->ts.u.cl->backend_decl, tmp); + if (sym->attr.optional) + { + tree present = gfc_conv_expr_present (sym); + tmp2 = build3_loc (input_location, COND_EXPR, + void_type_node, present, tmp2, + build_empty_stmt (input_location)); + } + gfc_add_expr_to_block (init, tmp2); + } + + gfc_restore_backend_locus (loc); + + /* Pass the final character length back. */ + if (sym->attr.intent != INTENT_IN) + { + tmp = fold_build2_loc (input_location, MODIFY_EXPR, + gfc_charlen_type_node, tmp, + sym->ts.u.cl->backend_decl); + if (sym->attr.optional) + { + tree present = gfc_conv_expr_present (sym); + tmp = build3_loc (input_location, COND_EXPR, + void_type_node, present, tmp, + build_empty_stmt (input_location)); + } + } + else + tmp = NULL_TREE; + + return tmp; + } + /* Generate function entry and exit code, and add it to the function body. This includes: Allocation and initialization of array variables. *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 3966,3972 **** /* An automatic character length, pointer array result. */ if (proc_sym->ts.type == BT_CHARACTER && TREE_CODE (proc_sym->ts.u.cl->backend_decl) == VAR_DECL) ! gfc_trans_dummy_character (proc_sym, proc_sym->ts.u.cl, block); } else if (proc_sym->ts.type == BT_CHARACTER) { --- 4020,4037 ---- /* An automatic character length, pointer array result. */ if (proc_sym->ts.type == BT_CHARACTER && TREE_CODE (proc_sym->ts.u.cl->backend_decl) == VAR_DECL) ! { ! tmp = NULL; ! if (proc_sym->ts.deferred) ! { ! gfc_save_backend_locus (&loc); ! gfc_set_backend_locus (&proc_sym->declared_at); ! tmp = gfc_null_and_pass_deferred_len (proc_sym, &init, &loc); ! gfc_add_init_cleanup (block, gfc_finish_block (&init), tmp); ! } ! else ! gfc_trans_dummy_character (proc_sym, proc_sym->ts.u.cl, block); ! } } else if (proc_sym->ts.type == BT_CHARACTER) { *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 3993,3999 **** /* Pass back the string length on exit. */ tmp = proc_sym->ts.u.cl->backend_decl; ! if (TREE_CODE (tmp) != INDIRECT_REF) { tmp = proc_sym->ts.u.cl->passed_length; tmp = build_fold_indirect_ref_loc (input_location, tmp); --- 4058,4065 ---- /* Pass back the string length on exit. */ tmp = proc_sym->ts.u.cl->backend_decl; ! if (TREE_CODE (tmp) != INDIRECT_REF ! && proc_sym->ts.u.cl->passed_length) { tmp = proc_sym->ts.u.cl->passed_length; tmp = build_fold_indirect_ref_loc (input_location, tmp); *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4072,4092 **** = gfc_class_set_static_fields (sym->backend_decl, vptr, tmp); TREE_CONSTANT (DECL_INITIAL (sym->backend_decl)) = 1; } ! else if (sym->attr.dimension || sym->attr.codimension ! || (IS_CLASS_ARRAY (sym) && !CLASS_DATA (sym)->attr.allocatable)) { bool is_classarray = IS_CLASS_ARRAY (sym); symbol_attribute *array_attr; gfc_array_spec *as; ! array_type tmp; array_attr = is_classarray ? &CLASS_DATA (sym)->attr : &sym->attr; as = is_classarray ? CLASS_DATA (sym)->as : sym->as; /* Assumed-size Cray pointees need to be treated as AS_EXPLICIT. */ ! tmp = as->type; ! if (tmp == AS_ASSUMED_SIZE && as->cp_was_assumed) ! tmp = AS_EXPLICIT; ! switch (tmp) { case AS_EXPLICIT: if (sym->attr.dummy || sym->attr.result) --- 4138,4158 ---- = gfc_class_set_static_fields (sym->backend_decl, vptr, tmp); TREE_CONSTANT (DECL_INITIAL (sym->backend_decl)) = 1; } ! else if ((sym->attr.dimension || sym->attr.codimension ! || (IS_CLASS_ARRAY (sym) && !CLASS_DATA (sym)->attr.allocatable))) { bool is_classarray = IS_CLASS_ARRAY (sym); symbol_attribute *array_attr; gfc_array_spec *as; ! array_type type_of_array; array_attr = is_classarray ? &CLASS_DATA (sym)->attr : &sym->attr; as = is_classarray ? CLASS_DATA (sym)->as : sym->as; /* Assumed-size Cray pointees need to be treated as AS_EXPLICIT. */ ! type_of_array = as->type; ! if (type_of_array == AS_ASSUMED_SIZE && as->cp_was_assumed) ! type_of_array = AS_EXPLICIT; ! switch (type_of_array) { case AS_EXPLICIT: if (sym->attr.dummy || sym->attr.result) *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4169,4174 **** --- 4235,4249 ---- case AS_DEFERRED: seen_trans_deferred_array = true; gfc_trans_deferred_array (sym, block); + if (sym->ts.type == BT_CHARACTER && sym->ts.deferred + && sym->attr.result) + { + tree tmp; + gfc_save_backend_locus (&loc); + gfc_set_backend_locus (&sym->declared_at); + tmp = gfc_null_and_pass_deferred_len (sym, &init, &loc); + gfc_add_init_cleanup (block, gfc_finish_block (&init), tmp); + } break; default: *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4183,4188 **** --- 4258,4264 ---- continue; else if ((!sym->attr.dummy || sym->ts.deferred) && (sym->attr.allocatable + || (sym->attr.pointer && sym->attr.result) || (sym->ts.type == BT_CLASS && CLASS_DATA (sym)->attr.allocatable))) { *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4190,4285 **** { tree descriptor = NULL_TREE; - /* Nullify and automatic deallocation of allocatable - scalars. */ - e = gfc_lval_expr_from_sym (sym); - if (sym->ts.type == BT_CLASS) - gfc_add_data_component (e); - - gfc_init_se (&se, NULL); - if (sym->ts.type != BT_CLASS - || sym->ts.u.derived->attr.dimension - || sym->ts.u.derived->attr.codimension) - { - se.want_pointer = 1; - gfc_conv_expr (&se, e); - } - else if (sym->ts.type == BT_CLASS - && !CLASS_DATA (sym)->attr.dimension - && !CLASS_DATA (sym)->attr.codimension) - { - se.want_pointer = 1; - gfc_conv_expr (&se, e); - } - else - { - se.descriptor_only = 1; - gfc_conv_expr (&se, e); - descriptor = se.expr; - se.expr = gfc_conv_descriptor_data_addr (se.expr); - se.expr = build_fold_indirect_ref_loc (input_location, se.expr); - } - gfc_free_expr (e); - gfc_save_backend_locus (&loc); gfc_set_backend_locus (&sym->declared_at); gfc_start_block (&init); ! if (!sym->attr.dummy || sym->attr.intent == INTENT_OUT) { ! /* Nullify when entering the scope. */ ! tmp = fold_build2_loc (input_location, MODIFY_EXPR, ! TREE_TYPE (se.expr), se.expr, ! fold_convert (TREE_TYPE (se.expr), ! null_pointer_node)); ! if (sym->attr.optional) { ! tree present = gfc_conv_expr_present (sym); ! tmp = build3_loc (input_location, COND_EXPR, ! void_type_node, present, tmp, ! build_empty_stmt (input_location)); } - gfc_add_expr_to_block (&init, tmp); - } - - if ((sym->attr.dummy || sym->attr.result) - && sym->ts.type == BT_CHARACTER - && sym->ts.deferred) - { - /* Character length passed by reference. */ - tmp = sym->ts.u.cl->passed_length; - tmp = build_fold_indirect_ref_loc (input_location, tmp); - tmp = fold_convert (gfc_charlen_type_node, tmp); - - if (!sym->attr.dummy || sym->attr.intent == INTENT_OUT) - /* Zero the string length when entering the scope. */ - gfc_add_modify (&init, sym->ts.u.cl->backend_decl, - build_int_cst (gfc_charlen_type_node, 0)); else { ! tree tmp2; ! ! tmp2 = fold_build2_loc (input_location, MODIFY_EXPR, ! gfc_charlen_type_node, ! sym->ts.u.cl->backend_decl, tmp); ! if (sym->attr.optional) ! { ! tree present = gfc_conv_expr_present (sym); ! tmp2 = build3_loc (input_location, COND_EXPR, ! void_type_node, present, tmp2, ! build_empty_stmt (input_location)); ! } ! gfc_add_expr_to_block (&init, tmp2); } ! gfc_restore_backend_locus (&loc); ! ! /* Pass the final character length back. */ ! if (sym->attr.intent != INTENT_IN) { tmp = fold_build2_loc (input_location, MODIFY_EXPR, ! gfc_charlen_type_node, tmp, ! sym->ts.u.cl->backend_decl); if (sym->attr.optional) { tree present = gfc_conv_expr_present (sym); --- 4266,4315 ---- { tree descriptor = NULL_TREE; gfc_save_backend_locus (&loc); gfc_set_backend_locus (&sym->declared_at); gfc_start_block (&init); ! if (!sym->attr.pointer) { ! /* Nullify and automatic deallocation of allocatable ! scalars. */ ! e = gfc_lval_expr_from_sym (sym); ! if (sym->ts.type == BT_CLASS) ! gfc_add_data_component (e); ! ! gfc_init_se (&se, NULL); ! if (sym->ts.type != BT_CLASS ! || sym->ts.u.derived->attr.dimension ! || sym->ts.u.derived->attr.codimension) { ! se.want_pointer = 1; ! gfc_conv_expr (&se, e); ! } ! else if (sym->ts.type == BT_CLASS ! && !CLASS_DATA (sym)->attr.dimension ! && !CLASS_DATA (sym)->attr.codimension) ! { ! se.want_pointer = 1; ! gfc_conv_expr (&se, e); } else { ! se.descriptor_only = 1; ! gfc_conv_expr (&se, e); ! descriptor = se.expr; ! se.expr = gfc_conv_descriptor_data_addr (se.expr); ! se.expr = build_fold_indirect_ref_loc (input_location, se.expr); } + gfc_free_expr (e); ! if (!sym->attr.dummy || sym->attr.intent == INTENT_OUT) { + /* Nullify when entering the scope. */ tmp = fold_build2_loc (input_location, MODIFY_EXPR, ! TREE_TYPE (se.expr), se.expr, ! fold_convert (TREE_TYPE (se.expr), ! null_pointer_node)); if (sym->attr.optional) { tree present = gfc_conv_expr_present (sym); *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4287,4302 **** void_type_node, present, tmp, build_empty_stmt (input_location)); } } - else - tmp = NULL_TREE; } else gfc_restore_backend_locus (&loc); /* Deallocate when leaving the scope. Nullifying is not needed. */ ! if (!sym->attr.result && !sym->attr.dummy && !sym->ns->proc_name->attr.is_main_program) { if (sym->ts.type == BT_CLASS --- 4317,4337 ---- void_type_node, present, tmp, build_empty_stmt (input_location)); } + gfc_add_expr_to_block (&init, tmp); } } + + if ((sym->attr.dummy || sym->attr.result) + && sym->ts.type == BT_CHARACTER + && sym->ts.deferred + && sym->ts.u.cl->passed_length) + tmp = gfc_null_and_pass_deferred_len (sym, &init, &loc); else gfc_restore_backend_locus (&loc); /* Deallocate when leaving the scope. Nullifying is not needed. */ ! if (!sym->attr.result && !sym->attr.dummy && !sym->attr.pointer && !sym->ns->proc_name->attr.is_main_program) { if (sym->ts.type == BT_CLASS *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4313,4318 **** --- 4348,4354 ---- gfc_free_expr (expr); } } + if (sym->ts.type == BT_CLASS) { /* Initialize _vptr to declared type. */ *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4353,4371 **** if (sym->attr.dummy) { gfc_start_block (&init); ! ! /* Character length passed by reference. */ ! tmp = sym->ts.u.cl->passed_length; ! tmp = build_fold_indirect_ref_loc (input_location, tmp); ! tmp = fold_convert (gfc_charlen_type_node, tmp); ! gfc_add_modify (&init, sym->ts.u.cl->backend_decl, tmp); ! /* Pass the final character length back. */ ! if (sym->attr.intent != INTENT_IN) ! tmp = fold_build2_loc (input_location, MODIFY_EXPR, ! gfc_charlen_type_node, tmp, ! sym->ts.u.cl->backend_decl); ! else ! tmp = NULL_TREE; gfc_add_init_cleanup (block, gfc_finish_block (&init), tmp); } } --- 4389,4397 ---- if (sym->attr.dummy) { gfc_start_block (&init); ! gfc_save_backend_locus (&loc); ! gfc_set_backend_locus (&sym->declared_at); ! tmp = gfc_null_and_pass_deferred_len (sym, &init, &loc); gfc_add_init_cleanup (block, gfc_finish_block (&init), tmp); } } *************** gfc_trans_deferred_vars (gfc_symbol * pr *** 4427,4432 **** --- 4453,4459 ---- gfc_add_init_cleanup (block, gfc_finish_block (&tmpblock), NULL_TREE); } + struct module_hasher : ggc_ptr_hash<module_htab_entry> { typedef const char *compare_type; Index: gcc/testsuite/gfortran.dg/deferred_character_15.f90 =================================================================== *** gcc/testsuite/gfortran.dg/deferred_character_15.f90 (revision 0) --- gcc/testsuite/gfortran.dg/deferred_character_15.f90 (working copy) *************** *** 0 **** --- 1,44 ---- + ! { dg-do run } + ! + ! Test the fix for PR69423. + ! + ! Contributed by Antony Lewis <ant...@cosmologist.info> + ! + program tester + character(LEN=:), allocatable :: S + S= test(2) + if (len(S) .ne. 4) call abort + if (S .ne. "test") call abort + if (allocated (S)) deallocate (S) + + S= test2(2) + if (len(S) .ne. 4) call abort + if (S .ne. "test") call abort + if (allocated (S)) deallocate (S) + contains + function test(alen) + character(LEN=:), allocatable :: test + integer alen, i + do i = alen, 1, -1 + test = 'test' + exit + end do + ! This line would print nothing when compiled with -O1 and higher. + ! print *, len(test),test + if (len(test) .ne. 4) call abort + if (test .ne. "test") call abort + end function test + + function test2(alen) result (test) + character(LEN=:), allocatable :: test + integer alen, i + do i = alen, 1, -1 + test = 'test' + exit + end do + ! This worked before the fix. + ! print *, len(test),test + if (len(test) .ne. 4) call abort + if (test .ne. "test") call abort + end function test2 + end program tester