https://gcc.gnu.org/g:c45c5ee3f5c51c00153173f7416946c05ce1b172
commit r17-2424-gc45c5ee3f5c51c00153173f7416946c05ce1b172 Author: Thomas Koenig <[email protected]> Date: Tue Jul 14 18:58:36 2026 +0200 Fix some more issues with -Wunused-but-set and -Wundefined-vars. The root cause for PR 126058 that was that sym->extra_loc was not being set for possible allocations via allocatablearguments. Looking at the code, a few more problems became clear: Not only INTENT(OUT) arguments could allocate allocatables, all other INTENTS could do so as well, but only if the formal arguments are allocatable as well. Also, curent intrinsics do not have allocatable components. During testing, there were also a few cases where, if one warning with -Wall and one with -Wextra applied, the one with -Wextra was issued peferably. This is slightly confusing, and also fixed with this patch. gcc/fortran/ChangeLog: PR fortran/126058 * gfortran.h (enum var_allocated): New enum. (symbol_attribute): Change member allocated to new enum. (gfc_used_in_allocate_expr): Add argument how. * interface.cc (gfc_compare_actual_formal): Mark variables as allocated only when the formal arg is allocatable. * intrinsic.cc (mark_args_as_used): Intrinsics do not allocate their arguments. * resolve.cc (find_unused_vs_set): Move warnings enabled with -Wextra behind those enabled with -Wall. If a variable is maybe allocated as an argument, mark it as such. * symbol.cc (mark_vars_as_used): Always set location. (gfc_used_in_allocate_expr): Add argument how. Use it. (gfc_lvalue_allocated_at): Set sym->attr.allocated to ALLOCATED_ASSIGNMENT. gcc/testsuite/ChangeLog: PR fortran/126058 * gfortran.dg/warn_undefined_vars_7.f90: New test. * gfortran.dg/warn_unused_but_set_variable_4.f90: New test. Diff: --- gcc/fortran/gfortran.h | 13 ++++- gcc/fortran/interface.cc | 11 ++-- gcc/fortran/intrinsic.cc | 7 +-- gcc/fortran/resolve.cc | 67 ++++++++++++++-------- gcc/fortran/symbol.cc | 15 +++-- .../gfortran.dg/warn_undefined_vars_7.f90 | 30 ++++++++++ .../gfortran.dg/warn_unused_but_set_variable_4.f90 | 21 +++++++ 7 files changed, 121 insertions(+), 43 deletions(-) diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index 062e9b0c9d84..df987576fffb 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -421,6 +421,15 @@ enum value_used VALUE_USED }; +/* How a variable is allocated. */ +enum var_allocated +{ + ALLOCATED_NEVER = 0, + ALLOCATED_ARG, + ALLOCATED_ALLOCATE_STMT, + ALLOCATED_ASSIGNMENT +}; + /* Strings for all symbol attributes. We use these for dumping the parse tree, in error messages, and also when reading and writing modules. In symbol.cc. */ @@ -1039,7 +1048,7 @@ typedef struct ENUM_BITFIELD (value_used) value_used:3; /* Set if the symbol has been allocated in the current procedure. */ - unsigned allocated:1; + ENUM_BITFIELD (var_allocated) allocated:2; /* Set if we already emitted a warning for this symbol and the middle-end should not add additional ones. */ @@ -4013,7 +4022,7 @@ void gfc_mark_lhs_as_used (gfc_expr *, locus *); void gfc_value_used_expr (gfc_expr *, enum value_used); void gfc_value_set_and_used (gfc_expr *, locus *loc, enum value_set, enum value_used); -void gfc_used_in_allocate_expr (gfc_expr *, locus *loc); +void gfc_used_in_allocate_expr (gfc_expr *, locus *loc, enum var_allocated); void gfc_expr_set_at (gfc_expr *, locus *loc, enum value_set); diff --git a/gcc/fortran/interface.cc b/gcc/fortran/interface.cc index 3e4ce70eb8b8..ccff784a2a5a 100644 --- a/gcc/fortran/interface.cc +++ b/gcc/fortran/interface.cc @@ -4269,21 +4269,24 @@ gfc_compare_actual_formal (gfc_actual_arglist **ap, gfc_formal_arglist *formal, { case INTENT_OUT: { - gfc_symbol *s = e->symtree->n.sym; gfc_expr_set_at (e, &e->where, VALUE_INTENT_OUT); + if (f->sym->attr.allocatable) + gfc_used_in_allocate_expr (e, &e->where, ALLOCATED_ARG); - /* INTENT(OUT) allocates variables as far as we know. */ - if (s->attr.allocatable) - s->attr.allocated = 1; } break; + case INTENT_IN: gfc_value_used_expr (e, VALUE_INTENT_IN); break; + case INTENT_INOUT: case INTENT_UNKNOWN: gfc_value_set_and_used (e, &e->where, VALUE_ARG, VALUE_MAYBE_USED); + + if (f->sym->attr.allocatable) + gfc_used_in_allocate_expr (e, &e->where, ALLOCATED_ARG); break; } } diff --git a/gcc/fortran/intrinsic.cc b/gcc/fortran/intrinsic.cc index 12b6d30c71f5..0d25de4d9d27 100644 --- a/gcc/fortran/intrinsic.cc +++ b/gcc/fortran/intrinsic.cc @@ -5029,12 +5029,7 @@ mark_args_as_used (gfc_intrinsic_arg *f, gfc_actual_arglist *a) case INTENT_OUT: if (a->expr->expr_type == EXPR_VARIABLE) - { - gfc_symbol *s = a->expr->symtree->n.sym; - gfc_expr_set_at (a->expr, &a->expr->where, VALUE_INTENT_OUT); - if (s->attr.allocatable) - s->attr.allocated = 1; - } + gfc_expr_set_at (a->expr, &a->expr->where, VALUE_INTENT_OUT); break; } } diff --git a/gcc/fortran/resolve.cc b/gcc/fortran/resolve.cc index 1b461f67afd2..6dc1b8e315d5 100644 --- a/gcc/fortran/resolve.cc +++ b/gcc/fortran/resolve.cc @@ -9755,7 +9755,7 @@ check_symbols: } success: - gfc_used_in_allocate_expr (e, &e->where); + gfc_used_in_allocate_expr (e, &e->where, ALLOCATED_ALLOCATE_STMT); if (code->expr3) gfc_value_set_at (e->symtree->n.sym, &code->expr3->where, VALUE_VARDEF); @@ -20840,24 +20840,6 @@ find_unused_vs_set (gfc_symbol *sym) || attr->volatile_ || attr->asynchronous || !attr->referenced) return; - if (warn_unused_intent_out && attr->value_set == VALUE_INTENT_OUT - && !var_value_is_used (sym)) - { - gfc_warning (OPT_Wunused_intent_out, "Variable %qs passed to " - "INTENT(OUT) argument at %L but value never used", - sym->name, &sym->other_loc); - attr->warning_emitted = 1; - return; - } - - if (warn_unused_read && attr->value_set == VALUE_READ && !var_value_is_used (sym)) - { - gfc_warning (OPT_Wunused_read, "Variable %qs read at %L but never " - "used", sym->name, &sym->other_loc); - attr->warning_emitted = 1; - return; - } - /* There is no allocation in sight, but the variable is used anyway. This might be hidden behind PRESENT, but issue a warning nonetheless. If people complain, we might want to make this to an extra option to be @@ -20970,15 +20952,50 @@ find_unused_vs_set (gfc_symbol *sym) attr->warning_emitted = 1; return; } - if (attr->allocatable && attr->allocated && !var_value_is_used (sym)) + if (attr->allocatable && !var_value_is_used (sym)) { - gfc_warning (OPT_Wunused_but_set_variable_, "Variable %qs " - "allocated at %L but never used", sym->name, - &sym->extra_loc); - attr->warning_emitted = 1; - return; + if (attr->allocated == ALLOCATED_ALLOCATE_STMT) + { + gfc_warning (OPT_Wunused_but_set_variable_, "Variable %qs " + "allocated at %L but never used", sym->name, + &sym->extra_loc); + attr->warning_emitted = 1; + return; + } + else if (attr->allocated == ALLOCATED_ARG) + { + gfc_warning (OPT_Wunused_but_set_variable_, "Variable %qs maybe " + "allocated as argument at %L but never used", + sym->name, &sym->extra_loc); + attr->warning_emitted = 1; + return; + } } } + + /* -Wunused-intent-out and -Wunused-read are enabled with -Wextra, so + check for these conditions at the end. If one of the warnings + with -Wall triggered, we do not want to issue a different warrning + for the same variable if the user supplies -Wall -Wextra instead + of only -Wall. */ + + if (warn_unused_intent_out && attr->value_set == VALUE_INTENT_OUT + && !var_value_is_used (sym)) + { + gfc_warning (OPT_Wunused_intent_out, "Variable %qs passed to " + "INTENT(OUT) argument at %L but value never used", + sym->name, &sym->other_loc); + attr->warning_emitted = 1; + return; + } + + if (warn_unused_read && attr->value_set == VALUE_READ && !var_value_is_used (sym)) + { + gfc_warning (OPT_Wunused_read, "Variable %qs read at %L but never " + "used", sym->name, &sym->other_loc); + attr->warning_emitted = 1; + return; + } } /* Run warn_unused_vs_set over a namespace recursively. */ diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc index 72ffa1919549..1349fe3a2fb3 100644 --- a/gcc/fortran/symbol.cc +++ b/gcc/fortran/symbol.cc @@ -5857,8 +5857,7 @@ mark_vars_as_used (gfc_expr **e, int *walk_subtrees, void *data) return 0; sym->attr.value_used = how_used; - if (sym->other_loc.nextc == NULL) - sym->other_loc = expr->where; + sym->other_loc = expr->where; return 0; } @@ -5894,7 +5893,7 @@ gfc_value_set_and_used (gfc_expr *expr, locus *loc, enum value_set how_set, /* ALLOCATE (A(N)) means that N is used, but A is not marked as such. */ void -gfc_used_in_allocate_expr (gfc_expr *expr, locus *loc) +gfc_used_in_allocate_expr (gfc_expr *expr, locus *loc, enum var_allocated how) { gfc_symbol *sym; enum value_used prev_used; @@ -5909,13 +5908,17 @@ gfc_used_in_allocate_expr (gfc_expr *expr, locus *loc) gfc_value_used_expr (expr, VALUE_USED); sym->attr.value_used = prev_used; sym->other_loc = prev_loc; - sym->attr.allocated = 1; + + if (how <= sym->attr.allocated) + return; + + sym->attr.allocated = how; if (sym->extra_loc.nextc == NULL) sym->extra_loc = *loc; } -/* Mark a symbol to allocated. */ +/* Mark a symbol as allocated. */ bool gfc_lvalue_allocated_at (gfc_symbol *sym, locus *loc) @@ -5923,7 +5926,7 @@ gfc_lvalue_allocated_at (gfc_symbol *sym, locus *loc) if (sym->other_loc.nextc == 0) sym->other_loc = *loc; - sym->attr.allocated = 1; + sym->attr.allocated = ALLOCATED_ASSIGNMENT; return true; } diff --git a/gcc/testsuite/gfortran.dg/warn_undefined_vars_7.f90 b/gcc/testsuite/gfortran.dg/warn_undefined_vars_7.f90 new file mode 100644 index 000000000000..7d83cc68543c --- /dev/null +++ b/gcc/testsuite/gfortran.dg/warn_undefined_vars_7.f90 @@ -0,0 +1,30 @@ +! { dg-do compile } +! { dg-additional-options "-Wundefined-vars" } + +! Allocatable variables passed to INTENT(OUT) formal arguments can +! only be considered allocated if the formal argument is allocatable. + +module m + implicit none + interface + subroutine no_allocation(x) + real, dimension(:), intent(out) :: x + end subroutine no_allocation + end interface + interface + subroutine allocation(x) + real, dimension(:), intent(out), allocatable :: x + end subroutine allocation + end interface +contains + subroutine foo + real, allocatable :: x(:) + call no_allocation(x) + print *,x ! { dg-warning "Unallocated variable.*referenced" } + end subroutine foo + subroutine bar + real, allocatable :: x(:) + call allocation(x) + print *,x + end subroutine bar +end module m diff --git a/gcc/testsuite/gfortran.dg/warn_unused_but_set_variable_4.f90 b/gcc/testsuite/gfortran.dg/warn_unused_but_set_variable_4.f90 new file mode 100644 index 000000000000..c36a73899936 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/warn_unused_but_set_variable_4.f90 @@ -0,0 +1,21 @@ +! { dg-do compile } +! { dg-additional-options "-Wall" } +! PR 126058 - this used to give an ICE. +PROGRAM p + IMPLICIT NONE + TYPE t1 + INTEGER, ALLOCATABLE :: i(:) + END TYPE t1 + call leak +CONTAINS + SUBROUTINE s1(e) + TYPE(t1), ALLOCATABLE, INTENT(OUT) :: e(:) + ALLOCATE( e(1) ) + ALLOCATE( e(1)%i(2) ) + END SUBROUTINE s1 + SUBROUTINE leak + TYPE(t1), ALLOCATABLE :: e(:) + CALL s1(e) ! { dg-warning "maybe allocated" } + CALL s1(e) + END SUBROUTINE leak +END PROGRAM p
