On Wed, 6 May 2020, Patrick Palka wrote:

> On Tue, 5 May 2020, Patrick Palka wrote:
> 
> > On Tue, 5 May 2020, Patrick Palka wrote:
> > 
> > > Unfortunately, the previous fix to PR94038 is fragile.  When the
> > > argument to fold_for_warn is a bare CALL_EXPR, then all is well: the
> > > result of maybe_constant_value from fold_for_warn (with
> > > uid_sensitive=true) is reused via the cv_cache in the subsequent call to
> > > maybe_constant_value from cp_fold (with uid_sensitive=false), so we
> > > avoid instantiating bar<int>.
> > > 
> > > But when the argument to fold_for_warn is more complex, e.g. an
> > > INDIRECT_REF of a CALL_EXPR, as in the testcase below (due to bar<int>()
> > > returning const int& which we need to decay to int) then from
> > > fold_for_warn we call maybe_constant_value on the INDIRECT_REF, and from
> > > cp_fold we call it on the CALL_EXPR, so there is no reuse via the
> > > cv_cache and we therefore end up instantiating bar<int>.
> > > 
> > > So for a more robust solution to this general issue of warning flags
> > > affecting code generation, it seems that we need a way to globally avoid
> > > template instantiation during constexpr evaluation whenever we're
> > > performing warning-dependent folding.
> > > 
> > > To that end, this patch replaces the flag constexpr_ctx::uid_sensitive
> > > with a global flag uid_sensitive_constexpr_evaluation_p, and enables it
> > > during fold_for_warn using an RAII helper.
> > > 
> > > The patch also adds a counter that keeps track of the number of times
> > > uid_sensitive_constexpr_evaluation_p is called, and we use this to
> > > determine whether the result of constexpr evaluation depended on the
> > > flag's value.  This lets us safely update the cv_cache and fold_cache
> > > from fold_for_warn in the most common case where the flag's value was
> > > irrelevant during constexpr evaluation.
> > 
> > Whoops, shortly after I sent the patch it occurred to me that we only
> > want to keep track of the number of times
> > uid_sensitive_constexpr_evaluation_p was called _and returned true_.
> > If the predicate was called and it returned false, then constexpr
> > evaluation was not restricted, so we could safely cache the result.
> > 
> > Please consider this patch instead, which also defines the various
> > helper classse out-of-line in constexpr.c for improved encapsulation:
> 
> Here's another version of the patch which just polishes comments, marks
> the 'evaluation_restricted_p' method const, renames
> 'uid_sensitive_constexpr_evaluation' to
> 'uid_sensitive_constexpr_evaluation_sentinel', and renames
> 'uid_sensitive_constexpr_evaluation_marker' to
> 'uid_sensitive_constexpr_evaluation_checker' to better convey their
> purpose.
> 
> The previous version passed bootstrap/regtest on x86_64-pc-linux-gnu,
> and bootstrap/regtest for this version is in progress.
> 
> -- >8 --
> 
> gcc/cp/ChangeLog:
> 
>       PR c++/94038
>       * constexpr.c (constexpr_ctx::uid_sensitive): Remove field.
>       (uid_sensitive_constexpr_evaluation_value): Define.
>       (uid_sensitive_constexpr_evaluation_true_counter): Define.
>       (uid_sensitive_constexpr_evaluation_p): Define.
>       (uid_sensitive_constexpr_evaluation_sentinel): Define its
>       constructor and destructor.
>       (uid_sensitive_constexpr_evaluation_checker): Define its
>       constructor and its evaluation_restricted_p method.
>       (get_fundef_copy): Remove 'ctx' parameter.  Use u_s_c_e_p
>       instead of constexpr_ctx::uid_sensitive.
>       (cxx_eval_call_expression): Use u_s_c_e_p instead, and test it
>       last.  Adjust call to get_fundef_copy.
>       (instantiate_cx_fn_r): Test u_s_c_e_p so that we increment the
>       counter if necessary.
>       (cxx_eval_outermost_constant_expr): Remove 'uid_sensitive'
>       parameter.  Adjust function body accordingly.  Use
>       uid_sensitive_constexpr_evaluation_value.
>       (maybe_constant_value): Remove 'uid_sensitive' parameter and
>       adjust function body accordingly.  Set up a
>       uid_sensitive_constexpr_evaluation_checker, and use it to
>       conditionally update the cv_cache.
>       * cp-gimplify.h (cp_fold): Set up a
>       uid_sensitive_constexpr_evaluation_checker, and use it to
>       conditionally update the fold_cache.
>       * cp-tree.h (maybe_constant_value): Update declaration.
>       (struct uid_sensitive_constexpr_evaluation_sentinel): Define.
>       (struct sensitive_constexpr_evaluation_checker): Define.
>       * expr.c (fold_for_warn): Set up a
>       uid_sensitive_constexpr_evaluation_sentinel before calling
>       the folding subroutines.  Drop all but the first argument to
>       maybe_constant_value.
> 
> gcc/testsuite/ChangeLog:
> 
>       PR c++/94038
>       * g++.dg/warn/pr94038-2.C: New test.
> ---
>  gcc/cp/constexpr.c                    | 96 ++++++++++++++++++++-------
>  gcc/cp/cp-gimplify.c                  | 13 ++--
>  gcc/cp/cp-tree.h                      | 24 ++++++-
>  gcc/cp/expr.c                         |  4 +-
>  gcc/testsuite/g++.dg/warn/pr94038-2.C | 28 ++++++++
>  5 files changed, 136 insertions(+), 29 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/warn/pr94038-2.C
> 
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 637cb746576..fb67d218ed8 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -1089,11 +1089,64 @@ struct constexpr_ctx {
>    bool strict;
>    /* Whether __builtin_is_constant_evaluated () should be true.  */
>    bool manifestly_const_eval;
> -  /* Whether we want to avoid doing anything that will cause extra DECL_UID
> -     generation.  */
> -  bool uid_sensitive;
>  };
>  
> +/* This internal flag controls whether we should avoid doing anything during
> +   constexpr evaluation that would cause extra DECL_UID generation, such as
> +   template instantiation and function copying.  */
> +
> +static bool uid_sensitive_constexpr_evaluation_value;
> +
> +/* An internal counter that keeps track of the number of times
> +   uid_sensitive_constexpr_evaluation_p returned true.  */
> +
> +static unsigned uid_sensitive_constexpr_evaluation_true_counter;
> +
> +/* The accessor for uid_sensitive_constexpr_evaluation_value which also
> +   increments the corresponding counter.  */
> +
> +static bool
> +uid_sensitive_constexpr_evaluation_p ()
> +{
> +  if (uid_sensitive_constexpr_evaluation_value)
> +    {
> +      ++uid_sensitive_constexpr_evaluation_true_counter;
> +      return true;
> +    }
> +  else
> +    return false;
> +}
> +
> +uid_sensitive_constexpr_evaluation_sentinel
> +::uid_sensitive_constexpr_evaluation_sentinel ()
> +  : saved_value (uid_sensitive_constexpr_evaluation_value)
> +{
> +  uid_sensitive_constexpr_evaluation_value = true;
> +}
> +
> +uid_sensitive_constexpr_evaluation_sentinel
> +::~uid_sensitive_constexpr_evaluation_sentinel ()
> +{
> +  uid_sensitive_constexpr_evaluation_value = saved_value;
> +}
> +
> +uid_sensitive_constexpr_evaluation_checker
> +::uid_sensitive_constexpr_evaluation_checker ()
> +  : saved_counter (uid_sensitive_constexpr_evaluation_true_counter)
> +{
> +}
> +
> +/* Returns true iff some constexpr evaluation was restricted due to
> +   uid_sensitive_constexpr_evaluation_p being called and returning true
> +   after this marker object was constructed.  */
> +
> +bool
> +uid_sensitive_constexpr_evaluation_checker::evaluation_restricted_p () const
> +{
> +  return saved_counter != uid_sensitive_constexpr_evaluation_true_counter;
> +}
> +
> +
>  /* A table of all constexpr calls that have been evaluated by the
>     compiler in this translation unit.  */
>  
> @@ -1156,7 +1209,7 @@ static GTY(()) hash_map<tree, tree> 
> *fundef_copies_table;
>     is parms, TYPE is result.  */
>  
>  static tree
> -get_fundef_copy (const constexpr_ctx *ctx, constexpr_fundef *fundef)
> +get_fundef_copy (constexpr_fundef *fundef)
>  {
>    tree copy;
>    bool existed;
> @@ -1173,7 +1226,7 @@ get_fundef_copy (const constexpr_ctx *ctx, 
> constexpr_fundef *fundef)
>      }
>    else if (*slot == NULL_TREE)
>      {
> -      if (ctx->uid_sensitive)
> +      if (uid_sensitive_constexpr_evaluation_p ())
>       return NULL_TREE;
>  
>        /* We've already used the function itself, so make a copy.  */
> @@ -2292,8 +2345,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, 
> tree t,
>  
>    /* We can't defer instantiating the function any longer.  */
>    if (!DECL_INITIAL (fun)
> -      && !ctx->uid_sensitive
> -      && DECL_TEMPLOID_INSTANTIATION (fun))
> +      && DECL_TEMPLOID_INSTANTIATION (fun)
> +      && !uid_sensitive_constexpr_evaluation_p ())
>      {
>        location_t save_loc = input_location;
>        input_location = loc;
> @@ -2454,7 +2507,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, 
> tree t,
>         gcc_assert (at_eof >= 2 && ctx->quiet);
>         *non_constant_p = true;
>       }
> -      else if (tree copy = get_fundef_copy (ctx, new_call.fundef))
> +      else if (tree copy = get_fundef_copy (new_call.fundef))
>       {
>         tree body, parms, res;
>         releasing_vec ctors;
> @@ -6485,7 +6538,8 @@ instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void 
> */*data*/)
>        && DECL_DECLARED_CONSTEXPR_P (*tp)
>        && !DECL_INITIAL (*tp)
>        && !trivial_fn_p (*tp)
> -      && DECL_TEMPLOID_INSTANTIATION (*tp))
> +      && DECL_TEMPLOID_INSTANTIATION (*tp)
> +      && !uid_sensitive_constexpr_evaluation_p ())
>      {
>        ++function_depth;
>        instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false);
> @@ -6552,8 +6606,7 @@ cxx_eval_outermost_constant_expr (tree t, bool 
> allow_non_constant,
>                                 bool strict = true,
>                                 bool manifestly_const_eval = false,
>                                 bool constexpr_dtor = false,
> -                               tree object = NULL_TREE,
> -                               bool uid_sensitive = false)
> +                               tree object = NULL_TREE)
>  {
>    auto_timevar time (TV_CONSTEXPR);
>  
> @@ -6569,8 +6622,7 @@ cxx_eval_outermost_constant_expr (tree t, bool 
> allow_non_constant,
>    constexpr_global_ctx global_ctx;
>    constexpr_ctx ctx = { &global_ctx, NULL, NULL, NULL, NULL, NULL, NULL,
>                       allow_non_constant, strict,
> -                     manifestly_const_eval || !allow_non_constant,
> -                     uid_sensitive };
> +                     manifestly_const_eval || !allow_non_constant };
>  
>    tree type = initialized_type (t);
>    tree r = t;
> @@ -6660,7 +6712,7 @@ cxx_eval_outermost_constant_expr (tree t, bool 
> allow_non_constant,
>    auto_vec<tree, 16> cleanups;
>    global_ctx.cleanups = &cleanups;
>  
> -  if (!uid_sensitive)
> +  if (!uid_sensitive_constexpr_evaluation_value)
>      instantiate_constexpr_fns (r);

On second thought, we should call instantiate_constexpr_fns
unconditionally here.  I thought guarding the call with
uid_sensitive_constexpr_evaluation_value would act as an optimization to
avoid walking the expression when we are uid-sensitive, but we should
call instantiate_constexpr_fns anyway (which calls instantiate_cx_fn_p,
which checks uid_sensitive_constexpr_evaluation_p) so that
evaluation_inhibited_p will later detect when we avoided instantiating a
constexpr fn due to being uid-sensitive.

Reply via email to