mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-15 02:40:29 +08:00
re PR fortran/85599 (warn about short-circuiting of logical expressions for non-pure functions)
2018-07-18 Janus Weil <janus@gcc.gnu.org> Thomas Koenig <tkoenig@gcc.gnu.org> PR fortran/85599 * dump-parse-tree.c (show_attr): Add handling of implicit_pure. * frontend-passes.c (do_warn_function_elimination): Do not warn for pure functions. * gfortran.h: Add prototypes for gfc_pure_function and gfc_implicit_pure_function. * gfortran.texi: Add chapter on evaluation of logical expressions. * invoke.texi: Mention that -Wfunction-elimination is implied by -Wextra. * lang.opt: Make -Wextra imply -Wfunction-elimination. * resolve.c (pure_function): Rename to gfc_pure_function. (gfc_implicit_pure_function): New function. (check_pure_function): Use it here. (impure_function_callback): New function. (resolve_operator): Call it via gfc_expr_walker. 2018-07-18 Janus Weil <janus@gcc.gnu.org> PR fortran/85599 * gfortran.dg/function_optimize_5.f90: Add option '-faggressive-function-elimination' and update dg-warning clauses. * gfortran.dg/short_circuiting.f90: New test. Co-Authored-By: Thomas Koenig <tkoenig@gcc.gnu.org> From-SVN: r262860
This commit is contained in:
parent
c56e97274f
commit
6457b1f096
@ -1,3 +1,22 @@
|
||||
2018-07-18 Janus Weil <janus@gcc.gnu.org>
|
||||
Thomas Koenig <tkoenig@gcc.gnu.org>
|
||||
|
||||
PR fortran/85599
|
||||
* dump-parse-tree.c (show_attr): Add handling of implicit_pure.
|
||||
* frontend-passes.c (do_warn_function_elimination): Do not warn for
|
||||
pure functions.
|
||||
* gfortran.h: Add prototypes for gfc_pure_function and
|
||||
gfc_implicit_pure_function.
|
||||
* gfortran.texi: Add chapter on evaluation of logical expressions.
|
||||
* invoke.texi: Mention that -Wfunction-elimination is implied
|
||||
by -Wextra.
|
||||
* lang.opt: Make -Wextra imply -Wfunction-elimination.
|
||||
* resolve.c (pure_function): Rename to gfc_pure_function.
|
||||
(gfc_implicit_pure_function): New function.
|
||||
(check_pure_function): Use it here.
|
||||
(impure_function_callback): New function.
|
||||
(resolve_operator): Call it via gfc_expr_walker.
|
||||
|
||||
2018-07-16 Fritz Reese <fritzoreese@gmail.com>
|
||||
|
||||
PR fortran/83184
|
||||
|
@ -716,6 +716,8 @@ show_attr (symbol_attribute *attr, const char * module)
|
||||
fputs (" ELEMENTAL", dumpfile);
|
||||
if (attr->pure)
|
||||
fputs (" PURE", dumpfile);
|
||||
if (attr->implicit_pure)
|
||||
fputs (" IMPLICIT_PURE", dumpfile);
|
||||
if (attr->recursive)
|
||||
fputs (" RECURSIVE", dumpfile);
|
||||
|
||||
|
@ -840,17 +840,22 @@ create_var (gfc_expr * e, const char *vname)
|
||||
static void
|
||||
do_warn_function_elimination (gfc_expr *e)
|
||||
{
|
||||
if (e->expr_type != EXPR_FUNCTION)
|
||||
return;
|
||||
if (e->value.function.esym)
|
||||
gfc_warning (OPT_Wfunction_elimination,
|
||||
"Removing call to function %qs at %L",
|
||||
e->value.function.esym->name, &(e->where));
|
||||
else if (e->value.function.isym)
|
||||
gfc_warning (OPT_Wfunction_elimination,
|
||||
"Removing call to function %qs at %L",
|
||||
e->value.function.isym->name, &(e->where));
|
||||
const char *name;
|
||||
if (e->expr_type == EXPR_FUNCTION
|
||||
&& !gfc_pure_function (e, &name) && !gfc_implicit_pure_function (e))
|
||||
{
|
||||
if (name)
|
||||
gfc_warning (OPT_Wfunction_elimination,
|
||||
"Removing call to impure function %qs at %L", name,
|
||||
&(e->where));
|
||||
else
|
||||
gfc_warning (OPT_Wfunction_elimination,
|
||||
"Removing call to impure function at %L",
|
||||
&(e->where));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Callback function for the code walker for doing common function
|
||||
elimination. This builds up the list of functions in the expression
|
||||
and goes through them to detect duplicates, which it then replaces
|
||||
|
@ -3275,6 +3275,8 @@ bool gfc_resolve_intrinsic (gfc_symbol *, locus *);
|
||||
bool gfc_explicit_interface_required (gfc_symbol *, char *, int);
|
||||
extern int gfc_do_concurrent_flag;
|
||||
const char* gfc_lookup_function_fuzzy (const char *, gfc_symtree *);
|
||||
int gfc_pure_function (gfc_expr *e, const char **name);
|
||||
int gfc_implicit_pure_function (gfc_expr *e);
|
||||
|
||||
|
||||
/* array.c */
|
||||
|
@ -1177,6 +1177,7 @@ might in some way or another become visible to the programmer.
|
||||
@menu
|
||||
* KIND Type Parameters::
|
||||
* Internal representation of LOGICAL variables::
|
||||
* Evaluation of logical expressions::
|
||||
* Thread-safety of the runtime library::
|
||||
* Data consistency and durability::
|
||||
* Files opened without an explicit ACTION= specifier::
|
||||
@ -1251,6 +1252,19 @@ values: @code{1} for @code{.TRUE.} and @code{0} for
|
||||
See also @ref{Argument passing conventions} and @ref{Interoperability with C}.
|
||||
|
||||
|
||||
@node Evaluation of logical expressions
|
||||
@section Evaluation of logical expressions
|
||||
|
||||
The Fortran standard does not require the compiler to evaluate all parts of an
|
||||
expression, if they do not contribute to the final result. For logical
|
||||
expressions with @code{.AND.} or @code{.OR.} operators, in particular, GNU
|
||||
Fortran will optimize out function calls (even to impure functions) if the
|
||||
result of the expression can be established without them. However, since not
|
||||
all compilers do that, and such an optimization can potentially modify the
|
||||
program flow and subsequent results, GNU Fortran throws warnings for such
|
||||
situations with the @option{-Wfunction-elimination} flag.
|
||||
|
||||
|
||||
@node Thread-safety of the runtime library
|
||||
@section Thread-safety of the runtime library
|
||||
@cindex thread-safety, threads
|
||||
|
@ -1056,8 +1056,9 @@ off via @option{-Wno-align-commons}. See also @option{-falign-commons}.
|
||||
@opindex @code{Wfunction-elimination}
|
||||
@cindex function elimination
|
||||
@cindex warnings, function elimination
|
||||
Warn if any calls to functions are eliminated by the optimizations
|
||||
Warn if any calls to impure functions are eliminated by the optimizations
|
||||
enabled by the @option{-ffrontend-optimize} option.
|
||||
This option is implied by @option{-Wextra}.
|
||||
|
||||
@item -Wrealloc-lhs
|
||||
@opindex @code{Wrealloc-lhs}
|
||||
|
@ -250,7 +250,7 @@ Fortran Var(flag_warn_frontend_loop_interchange)
|
||||
Warn if loops have been interchanged.
|
||||
|
||||
Wfunction-elimination
|
||||
Fortran Warning Var(warn_function_elimination)
|
||||
Fortran Warning Var(warn_function_elimination) LangEnabledBy(Fortran,Wextra)
|
||||
Warn about function call elimination.
|
||||
|
||||
Wimplicit-interface
|
||||
|
@ -2941,8 +2941,8 @@ is_external_proc (gfc_symbol *sym)
|
||||
static int
|
||||
pure_stmt_function (gfc_expr *, gfc_symbol *);
|
||||
|
||||
static int
|
||||
pure_function (gfc_expr *e, const char **name)
|
||||
int
|
||||
gfc_pure_function (gfc_expr *e, const char **name)
|
||||
{
|
||||
int pure;
|
||||
gfc_component *comp;
|
||||
@ -2982,6 +2982,21 @@ pure_function (gfc_expr *e, const char **name)
|
||||
}
|
||||
|
||||
|
||||
/* Check if the expression is a reference to an implicitly pure function. */
|
||||
|
||||
int
|
||||
gfc_implicit_pure_function (gfc_expr *e)
|
||||
{
|
||||
gfc_component *comp = gfc_get_proc_ptr_comp (e);
|
||||
if (comp)
|
||||
return gfc_implicit_pure (comp->ts.interface);
|
||||
else if (e->value.function.esym)
|
||||
return gfc_implicit_pure (e->value.function.esym);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
impure_stmt_fcn (gfc_expr *e, gfc_symbol *sym,
|
||||
int *f ATTRIBUTE_UNUSED)
|
||||
@ -2996,7 +3011,7 @@ impure_stmt_fcn (gfc_expr *e, gfc_symbol *sym,
|
||||
|| e->symtree->n.sym->attr.proc == PROC_ST_FUNCTION)
|
||||
return false;
|
||||
|
||||
return pure_function (e, &name) ? false : true;
|
||||
return gfc_pure_function (e, &name) ? false : true;
|
||||
}
|
||||
|
||||
|
||||
@ -3012,7 +3027,7 @@ pure_stmt_function (gfc_expr *e, gfc_symbol *sym)
|
||||
static bool check_pure_function (gfc_expr *e)
|
||||
{
|
||||
const char *name = NULL;
|
||||
if (!pure_function (e, &name) && name)
|
||||
if (!gfc_pure_function (e, &name) && name)
|
||||
{
|
||||
if (forall_flag)
|
||||
{
|
||||
@ -3034,7 +3049,8 @@ static bool check_pure_function (gfc_expr *e)
|
||||
"within a PURE procedure", name, &e->where);
|
||||
return false;
|
||||
}
|
||||
gfc_unset_implicit_pure (NULL);
|
||||
if (!gfc_implicit_pure_function (e))
|
||||
gfc_unset_implicit_pure (NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -3822,6 +3838,41 @@ lookup_uop_fuzzy (const char *op, gfc_symtree *uop)
|
||||
}
|
||||
|
||||
|
||||
/* Callback finding an impure function as an operand to an .and. or
|
||||
.or. expression. Remember the last function warned about to
|
||||
avoid double warnings when recursing. */
|
||||
|
||||
static int
|
||||
impure_function_callback (gfc_expr **e, int *walk_subtrees ATTRIBUTE_UNUSED,
|
||||
void *data)
|
||||
{
|
||||
gfc_expr *f = *e;
|
||||
const char *name;
|
||||
static gfc_expr *last = NULL;
|
||||
bool *found = (bool *) data;
|
||||
|
||||
if (f->expr_type == EXPR_FUNCTION)
|
||||
{
|
||||
*found = 1;
|
||||
if (f != last && !gfc_pure_function (f, &name)
|
||||
&& !gfc_implicit_pure_function (f))
|
||||
{
|
||||
if (name)
|
||||
gfc_warning (OPT_Wfunction_elimination,
|
||||
"Impure function %qs at %L might not be evaluated",
|
||||
name, &f->where);
|
||||
else
|
||||
gfc_warning (OPT_Wfunction_elimination,
|
||||
"Impure function at %L might not be evaluated",
|
||||
&f->where);
|
||||
}
|
||||
last = f;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Resolve an operator expression node. This can involve replacing the
|
||||
operation with a user defined function call. */
|
||||
|
||||
@ -3930,6 +3981,14 @@ resolve_operator (gfc_expr *e)
|
||||
gfc_convert_type (op1, &e->ts, 2);
|
||||
else if (op2->ts.kind < e->ts.kind)
|
||||
gfc_convert_type (op2, &e->ts, 2);
|
||||
|
||||
if (e->value.op.op == INTRINSIC_AND || e->value.op.op == INTRINSIC_OR)
|
||||
{
|
||||
/* Warn about short-circuiting
|
||||
with impure function as second operand. */
|
||||
bool op2_f = false;
|
||||
gfc_expr_walker (&op2, impure_function_callback, &op2_f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,10 @@
|
||||
2018-07-18 Janus Weil <janus@gcc.gnu.org>
|
||||
|
||||
PR fortran/85599
|
||||
* gfortran.dg/function_optimize_5.f90: Add option
|
||||
'-faggressive-function-elimination' and update dg-warning clauses.
|
||||
* gfortran.dg/short_circuiting.f90: New test.
|
||||
|
||||
2018-07-18 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
PR c++/86190 - bogus -Wsign-conversion warning
|
||||
|
@ -1,5 +1,5 @@
|
||||
! { dg-do compile }
|
||||
! { dg-options "-ffrontend-optimize -finline-matmul-limit=0 -Wfunction-elimination" }
|
||||
! { dg-options "-ffrontend-optimize -faggressive-function-elimination -finline-matmul-limit=0 -Wfunction-elimination" }
|
||||
! Check the -ffrontend-optimize (in the absence of -O) and
|
||||
! -Wfunction-elimination options.
|
||||
program main
|
||||
@ -26,16 +26,16 @@ program main
|
||||
|
||||
data a /2., 3., 5., 7./
|
||||
data b /11., 13., 17., 23./
|
||||
write (unit=line, fmt='(4F7.2)') matmul(a,b) & ! { dg-warning "Removing call to function 'matmul'" }
|
||||
write (unit=line, fmt='(4F7.2)') matmul(a,b) &
|
||||
& + matmul(a,b)
|
||||
z = sin(x) + 2.0 + sin(x) ! { dg-warning "Removing call to function 'sin'" }
|
||||
z = sin(x) + 2.0 + sin(x)
|
||||
print *,z
|
||||
x = ext_func(a) + 23 + ext_func(a)
|
||||
x = ext_func(a) + 23 + ext_func(a) ! { dg-warning "Removing call to impure function" }
|
||||
print *,d,x
|
||||
z = element(x) + element(x) ! { dg-warning "Removing call to function 'element'" }
|
||||
z = element(x) + element(x)
|
||||
print *,z
|
||||
i = mypure(x) - mypure(x) ! { dg-warning "Removing call to function 'mypure'" }
|
||||
i = mypure(x) - mypure(x)
|
||||
print *,i
|
||||
z = elem_impure(x) - elem_impure(x)
|
||||
z = elem_impure(x) - elem_impure(x) ! { dg-warning "Removing call to impure function" }
|
||||
print *,z
|
||||
end program main
|
||||
|
59
gcc/testsuite/gfortran.dg/short_circuiting.f90
Normal file
59
gcc/testsuite/gfortran.dg/short_circuiting.f90
Normal file
@ -0,0 +1,59 @@
|
||||
! { dg-do compile }
|
||||
! { dg-additional-options "-Wextra" }
|
||||
!
|
||||
! PR 85599: warn about short-circuiting of logical expressions for non-pure functions
|
||||
!
|
||||
! Contributed by Janus Weil <janus@gcc.gnu.org>
|
||||
|
||||
module a
|
||||
|
||||
interface impl_pure_a
|
||||
module procedure impl_pure_a1
|
||||
end interface
|
||||
|
||||
contains
|
||||
|
||||
logical function impl_pure_a1()
|
||||
impl_pure_a1 = .true.
|
||||
end function
|
||||
|
||||
end module
|
||||
|
||||
|
||||
program short_circuit
|
||||
|
||||
use a
|
||||
|
||||
logical :: flag
|
||||
flag = .false.
|
||||
flag = check() .and. flag
|
||||
flag = flag .and. check() ! { dg-warning "might not be evaluated" }
|
||||
flag = flag .and. pure_check()
|
||||
flag = flag .and. impl_pure_1()
|
||||
flag = flag .and. impl_pure_2()
|
||||
flag = flag .and. impl_pure_a1()
|
||||
flag = flag .and. impl_pure_a()
|
||||
|
||||
contains
|
||||
|
||||
logical function check()
|
||||
integer, save :: i = 1
|
||||
print *, "check", i
|
||||
i = i + 1
|
||||
check = .true.
|
||||
end function
|
||||
|
||||
logical pure function pure_check()
|
||||
pure_check = .true.
|
||||
end function
|
||||
|
||||
logical function impl_pure_1()
|
||||
impl_pure_1 = .true.
|
||||
end function
|
||||
|
||||
logical function impl_pure_2()
|
||||
impl_pure_2 = impl_pure_1()
|
||||
end function
|
||||
|
||||
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user