On Sun, Nov 09, 2025 at 03:53:51PM +1100, Nathaniel Shead wrote:
> On Tue, Oct 28, 2025 at 04:57:01PM +1100, Nathaniel Shead wrote:
> > On Fri, Oct 03, 2025 at 05:28:59PM +0100, Jason Merrill wrote:
> > > On 10/2/25 2:13 PM, Nathaniel Shead wrote:
> > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> > > > 
> > > > This approach does have some downsides, in that this early folding does
> > > > potentially impact later-running diagnostic messages and overall could
> > > > make things more difficult to understand; I'm not sure what a good
> > > > alternative approach would be though.  Any thoughts welcome.
> > > 
> > > For those reasons, as much as possible we've tried to defer folding until
> > > cp_fold_function, called from finish_function.  It seems the problem here 
> > > is
> > > that folding happens after the call to maybe_save_constexpr_fundef, so
> > > perhaps we need an earlier pass to do only non-ODR-use folding?
> > > 
> > 
> > Right.  I'd previously dismissed this approach as it appeared
> > fundamentally incompatible with supporting this in templates, but as you
> > point out below that's impossible in general so this does some more
> > reasonable.
> > 
> > It's just not constexpr functions, it turns out even regular inline
> > functions we don't fold away all non-ODR-used constants in some cases
> > (this is PR c++/199097), but I guess we're just missing some folding
> > that we could be doing; I'll need to look further.
> > 
> 
> Here's a patch that implements the folding in cp-gimplify.  I'm not a
> huge fan of the way I've done the OpenMP changes but I'm not sure if
> there's a better approach; thoughts welcome.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> 

Ping for this patch.

> -- >8 --
> 
> Subject: [PATCH] c++: Fold non-ODR usages of potentially constant values early
>  [PR120005]
> 
> [basic.link] p14.4 says that a declaration naming a TU-local entity is
> an exposure, ignoring "any reference to a non-volatile const object or
> reference with internal or no linkage initialized with a constant
> expression that is not an odr-use".
> 
> To implement this, we cannot stream these entities but must fold them
> into their underlying values beforehand.  This was already done to a
> degree by cp_fold, but it didn't handle all cases, and notably was not
> performed on the saved body of a constexpr function, which would then
> cause errors during modules streaming.
> 
> This patch implements this by supplementing cp_fold with additional
> rules to fold non-ODR usages of contants.  We need to do this as an
> additional walk before saving the constexpr function definition, so we
> also disable as much other folding during this walk as possible to
> prevent removing any information that the constexpr interpreter
> requires to function correctly.
> 
> With this we still will error on uses in templates.  In general it's
> impossible to tell within an uninstantiated template body whether a
> reference is an ODR-use in the face of dependent expressions, so we
> don't attempt to do anything for this case.
> 
>       PR c++/119097
>       PR c++/120005
> 
> gcc/cp/ChangeLog:
> 
>       * constexpr.cc (potential_constant_expression_1): Fall back to
>       location from parent expression if needed.
>       * cp-gimplify.cc (enum fold_flags): Add ff_only_non_odr.
>       (cp_fold_data::cp_fold_data): Assert invariant for flags.
>       (cp_fold_omp_clause_refs_r): New function.
>       (cp_fold_r): Specially handle OMP_CLAUSE_DECL.
>       (cp_fold_function_non_odr_use): New function.
>       (cp_fold_non_odr_use_1): New function.
>       (cp_fold_maybe_rvalue): Fold non-ODR uses when requested.
>       (cp_fold_non_odr_use): New function.
>       (fold_caches): Increase number of caches.
>       (get_fold_cache): Use a new cache for non-ODR use walks.
>       (cp_fold): Skip most folding for non-ODR use walks; always
>       fold constant-initialized references; remove dead code to
>       fold __builtin_source_location.
>       * cp-tree.h (cp_fold_function_non_odr_use): Declare.
>       (cp_fold_non_odr_use): Declare.
>       * decl.cc (finish_function): Fold non-ODR uses before saving
>       constexpr fundef.  Invoke PLUGIN_PRE_GENERICIZE before this
>       folding.
>       * ptree.cc (cxx_print_xnode): Handle TU_LOCAL_ENTITY.
>       * tree.cc (bot_manip): Propagate TREE_CONSTANT.
>       * typeck2.cc (digest_nsdmi_init): Fold non-ODR uses in NSDMIs.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/cpp0x/constexpr-cast.C: Adjust diagnostics.
>       * g++.dg/warn/overflow-warn-1.C: Fix diagnostic checks.
>       * g++.dg/warn/overflow-warn-3.C: Likewise.
>       * g++.dg/warn/overflow-warn-4.C: Likewise.
>       * g++.dg/modules/internal-8_a.C: Remove xfails, supplement with
>       additional testcases.
>       * g++.dg/modules/internal-8_b.C: New test.
> 
> Signed-off-by: Nathaniel Shead <[email protected]>
> ---
>  gcc/cp/constexpr.cc                         |   6 +-
>  gcc/cp/cp-gimplify.cc                       | 234 ++++++++++++++++++--
>  gcc/cp/cp-tree.h                            |   2 +
>  gcc/cp/decl.cc                              |  13 +-
>  gcc/cp/ptree.cc                             |   9 +
>  gcc/cp/tree.cc                              |   1 +
>  gcc/cp/typeck2.cc                           |   5 +
>  gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C |   8 +-
>  gcc/testsuite/g++.dg/modules/internal-8_a.C |  59 +++--
>  gcc/testsuite/g++.dg/modules/internal-8_b.C |  10 +
>  gcc/testsuite/g++.dg/warn/overflow-warn-1.C |   9 +-
>  gcc/testsuite/g++.dg/warn/overflow-warn-3.C |   7 +-
>  gcc/testsuite/g++.dg/warn/overflow-warn-4.C |   7 +-
>  13 files changed, 315 insertions(+), 55 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-8_b.C
> 
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index 016e3db927c..63b1d537333 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -11411,6 +11411,7 @@ potential_constant_expression_1 (tree t, bool 
> want_rval, bool strict, bool now,
>    if (t == NULL_TREE)
>      return true;
>    location_t loc = cp_expr_loc_or_input_loc (t);
> +  iloc_sentinel ils = loc;
>  
>    if (*jump_target)
>      /* If we are jumping, ignore everything.  This is simpler than the
> @@ -11734,10 +11735,7 @@ potential_constant_expression_1 (tree t, bool 
> want_rval, bool strict, bool now,
>        {
>          tree from = TREE_OPERAND (t, 0);
>       if (location_wrapper_p (t))
> -       {
> -         iloc_sentinel ils = loc;
> -         return (RECUR (from, want_rval));
> -       }
> +       return (RECUR (from, want_rval));
>       if (INDIRECT_TYPE_P (TREE_TYPE (t)))
>         {
>           STRIP_ANY_LOCATION_WRAPPER (from);
> diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
> index 1662336e165..a5dbd225923 100644
> --- a/gcc/cp/cp-gimplify.cc
> +++ b/gcc/cp/cp-gimplify.cc
> @@ -74,6 +74,11 @@ enum fold_flags {
>       definitely not in a manifestly constant-evaluated
>       context.  */
>    ff_mce_false = 1 << 1,
> +  /* Whether we're only folding non-ODR usages of constants.
> +     This happens before saving the constexpr funcdef, so
> +     we should do as little other folding as possible.
> +     Mutually exclusive with ff_mce_false.  */
> +  ff_only_non_odr = 1 << 2,
>  };
>  
>  using fold_flags_t = int;
> @@ -82,7 +87,11 @@ struct cp_fold_data
>  {
>    hash_set<tree> pset;
>    fold_flags_t flags;
> -  cp_fold_data (fold_flags_t flags): flags (flags) {}
> +  cp_fold_data (fold_flags_t flags): flags (flags)
> +  {
> +    gcc_checking_assert (!(flags & ff_mce_false)
> +                      || !(flags & ff_only_non_odr));
> +  }
>  };
>  
>  /* Forward declarations.  */
> @@ -1442,6 +1451,38 @@ cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, 
> void *data_)
>    return NULL_TREE;
>  }
>  
> +/* A walk_tree helper to replace constant-initialized references in an
> +   OMP_CLAUSE with the the declaration that they refer to.  Such refs
> +   will have been folded out in the body by cp_fold_non_odr_use_1 and
> +   so we need to follow suit to prevent confusion.  */
> +
> +static tree
> +cp_fold_omp_clause_refs_r (tree *expr_p, int *walk_subtrees, void */*data*/)
> +{
> +  tree expr = *expr_p;
> +
> +  if (TYPE_P (expr))
> +    {
> +      *walk_subtrees = 0;
> +      return NULL_TREE;
> +    }
> +
> +  if (DECL_P (expr))
> +    {
> +      *walk_subtrees = 0;
> +
> +      if (decl_constant_var_p (expr)
> +       && TYPE_REF_P (TREE_TYPE (expr)))
> +     {
> +       tree init = maybe_constant_value (expr);
> +       if (TREE_CONSTANT (init))
> +         *expr_p = tree_strip_nop_conversions (init);
> +     }
> +    }
> +
> +  return NULL_TREE;
> +}
> +
>  /* Perform any pre-gimplification folding of C++ front end trees to
>     GENERIC.
>     Note:  The folding of non-omp cases is something to move into
> @@ -1529,6 +1570,22 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void 
> *data_)
>        *walk_subtrees = 0;
>        return NULL_TREE;
>  
> +    case OMP_CLAUSE:
> +      if ((data->flags & ff_only_non_odr)
> +       && omp_clause_num_ops[OMP_CLAUSE_CODE (stmt)] >= 1
> +       && OMP_CLAUSE_CODE (stmt) >= OMP_CLAUSE_PRIVATE
> +       && OMP_CLAUSE_CODE (stmt) <= OMP_CLAUSE__SCANTEMP_
> +       && OMP_CLAUSE_DECL (stmt))
> +     {
> +       tree *decl = &OMP_CLAUSE_DECL (stmt);
> +       cp_walk_tree (decl, cp_fold_omp_clause_refs_r, NULL, NULL);
> +       if (TREE_CODE (*decl) == ADDR_EXPR
> +           && DECL_P (TREE_OPERAND (*decl, 0)))
> +         *decl = TREE_OPERAND (*decl, 0);
> +       data->pset.add (*decl);
> +     }
> +      break;
> +
>      case IF_STMT:
>        if (IF_STMT_CONSTEVAL_P (stmt))
>       {
> @@ -1620,6 +1677,17 @@ cp_fold_function (tree fndecl)
>      DECL_ESCALATION_CHECKED_P (fndecl) = true;
>  }
>  
> +/* Fold any non-ODR usages of constant variables in FNDECL.  This occurs
> +   before saving the constexpr fundef, so do as little other folding
> +   as possible.  */
> +
> +void
> +cp_fold_function_non_odr_use (tree fndecl)
> +{
> +  cp_fold_data data (ff_only_non_odr);
> +  cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
> +}
> +
>  /* We've stashed immediate-escalating functions.  Now see if they indeed
>     ought to be promoted to consteval.  */
>  
> @@ -2921,6 +2989,61 @@ cxx_omp_disregard_value_expr (tree decl, bool shared)
>    return false;
>  }
>  
> +/* Fold any non-ODR-usages of a constant variable in expression X.  */
> +
> +static tree
> +cp_fold_non_odr_use_1 (tree x)
> +{
> +  tree var = x;
> +  bool has_indirect_ref = false;
> +  while (!VAR_P (var))
> +    switch (TREE_CODE (var))
> +      {
> +      case ARRAY_REF:
> +      case BIT_FIELD_REF:
> +      case COMPONENT_REF:
> +      case VIEW_CONVERT_EXPR:
> +      CASE_CONVERT:
> +     var = TREE_OPERAND (var, 0);
> +     break;
> +
> +      case INDIRECT_REF:
> +     if (REFERENCE_REF_P (var))
> +       {
> +         var = TREE_OPERAND (var, 0);
> +         has_indirect_ref = true;
> +       }
> +     else
> +       return x;
> +     break;
> +
> +      default:
> +     return x;
> +      }
> +
> +  if (TREE_THIS_VOLATILE (var)
> +      || !decl_constant_var_p (var))
> +    return x;
> +
> +  /* We mustn't fold std::hardware_destructive_interference_size here
> +     so that maybe_warn_about_constant_value can complain if it's used
> +     in a manifestly constant-evaluated context.  */
> +  if (decl_in_std_namespace_p (var)
> +      && DECL_NAME (var)
> +      && id_equal (DECL_NAME (var), 
> "hardware_destructive_interference_size"))
> +    return x;
> +
> +  tree t = maybe_constant_value (x);
> +  if (TREE_CONSTANT (t))
> +    {
> +      if (has_indirect_ref)
> +     t = convert_from_reference (t);
> +      return t;
> +    }
> +  else
> +    return x;
> +}
> +
>  /* Fold expression X which is used as an rvalue if RVAL is true.  */
>  
>  static tree
> @@ -2931,8 +3054,17 @@ cp_fold_maybe_rvalue (tree x, bool rval, fold_flags_t 
> flags)
>        x = cp_fold (x, flags);
>        if (rval)
>       x = mark_rvalue_use (x);
> -      if (rval && DECL_P (x)
> -       && !TYPE_REF_P (TREE_TYPE (x)))
> +      if (rval && (flags & ff_only_non_odr))
> +     {
> +       tree v = cp_fold_non_odr_use_1 (x);
> +       if (v != x)
> +         {
> +           x = v;
> +           continue;
> +         }
> +     }
> +      else if (rval && DECL_P (x)
> +            && !TYPE_REF_P (TREE_TYPE (x)))
>       {
>         tree v = decl_constant_value (x);
>         if (v != x && v != error_mark_node)
> @@ -2966,6 +3098,15 @@ cp_fold_rvalue (tree x)
>    return cp_fold_rvalue (x, ff_none);
>  }
>  
> +/* Fold any non-ODR used constants in an expression X which
> +   is used as an rvalue if RVAL is true.  */
> +
> +tree
> +cp_fold_non_odr_use (tree x, bool rval)
> +{
> +  return cp_fold_maybe_rvalue (x, rval, ff_only_non_odr);
> +}
> +
>  /* Perform folding on expression X.  */
>  
>  static tree
> @@ -3029,7 +3170,7 @@ c_fully_fold (tree x, bool /*in_init*/, bool 
> */*maybe_const*/, bool lval)
>    return cp_fold_maybe_rvalue (x, !lval);
>  }
>  
> -static GTY((deletable)) hash_map<tree, tree> *fold_caches[2];
> +static GTY((deletable)) hash_map<tree, tree> *fold_caches[3];
>  
>  /* Subroutine of cp_fold.  Returns which fold cache to use according
>     to the given flags.  We need multiple caches since the result of
> @@ -3039,6 +3180,8 @@ static hash_map<tree, tree> *&
>  get_fold_cache (fold_flags_t flags)
>  {
>    if (flags & ff_mce_false)
> +    return fold_caches[2];
> +  else if (flags & ff_only_non_odr)
>      return fold_caches[1];
>    else
>      return fold_caches[0];
> @@ -3103,7 +3246,7 @@ cp_fold (tree x, fold_flags_t flags)
>        /* Strip CLEANUP_POINT_EXPR if the expression doesn't have side
>        effects.  */
>        r = cp_fold (TREE_OPERAND (x, 0), flags);
> -      if (!TREE_SIDE_EFFECTS (r))
> +      if (!TREE_SIDE_EFFECTS (r) && !(flags & ff_only_non_odr))
>       x = r;
>        break;
>  
> @@ -3138,6 +3281,13 @@ cp_fold (tree x, fold_flags_t flags)
>  
>        if (op0 == error_mark_node)
>       x = error_mark_node;
> +      else if (flags & ff_only_non_odr)
> +     {
> +       if (op0 != TREE_OPERAND (x, 0))
> +         x = build1_loc (loc, code, TREE_TYPE (x), op0);
> +       if (code == NOP_EXPR)
> +         REINTERPRET_CAST_P (x) = REINTERPRET_CAST_P (org_x);
> +     }
>        else if (code == CONVERT_EXPR
>         && SCALAR_TYPE_P (TREE_TYPE (x))
>         && op0 != void_node)
> @@ -3160,7 +3310,15 @@ cp_fold (tree x, fold_flags_t flags)
>  
>      case EXCESS_PRECISION_EXPR:
>        op0 = cp_fold_maybe_rvalue (TREE_OPERAND (x, 0), rval_ops, flags);
> -      x = fold_convert_loc (EXPR_LOCATION (x), TREE_TYPE (x), op0);
> +      if (op0 == error_mark_node)
> +     x = error_mark_node;
> +      else if (flags & ff_only_non_odr)
> +     {
> +       if (op0 != TREE_OPERAND (x, 0))
> +         x = build1_loc (EXPR_LOCATION (x), code, TREE_TYPE (x), op0);
> +     }
> +      else
> +     x = fold_convert_loc (EXPR_LOCATION (x), TREE_TYPE (x), op0);
>        break;
>  
>      case INDIRECT_REF:
> @@ -3171,6 +3329,15 @@ cp_fold (tree x, fold_flags_t flags)
>         if (p != x)
>           return cp_fold (p, flags);
>       }
> +      /* When folding non-ODR usages of constants, we also want to
> +      remove any constant-initialized references, even when
> +      used as lvalues.  */
> +      if ((flags & ff_only_non_odr) && REFERENCE_REF_P (x))
> +     {
> +       tree r = cp_fold_non_odr_use_1 (x);
> +       if (r != x)
> +         return cp_fold (r, flags);
> +     }
>        goto unary;
>  
>      case ADDR_EXPR:
> @@ -3179,7 +3346,8 @@ cp_fold (tree x, fold_flags_t flags)
>  
>        /* Cope with user tricks that amount to offsetof.  */
>        if (op0 != error_mark_node
> -       && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (op0)))
> +       && !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (op0))
> +       && !(flags & ff_only_non_odr))
>       {
>         tree val = get_base_address (op0);
>         if (val
> @@ -3219,7 +3387,10 @@ cp_fold (tree x, fold_flags_t flags)
>       x = error_mark_node;
>        else if (op0 != TREE_OPERAND (x, 0))
>       {
> -       x = fold_build1_loc (loc, code, TREE_TYPE (x), op0);
> +       if (flags & ff_only_non_odr)
> +         x = build1_loc (loc, code, TREE_TYPE (x), op0);
> +       else
> +         x = fold_build1_loc (loc, code, TREE_TYPE (x), op0);
>         if (code == INDIRECT_REF
>             && (INDIRECT_REF_P (x) || TREE_CODE (x) == MEM_REF))
>           {
> @@ -3228,7 +3399,7 @@ cp_fold (tree x, fold_flags_t flags)
>             TREE_THIS_VOLATILE (x) = TREE_THIS_VOLATILE (org_x);
>           }
>       }
> -      else
> +      else if (!(flags & ff_only_non_odr))
>       x = fold (x);
>  
>        gcc_assert (TREE_CODE (x) != COND_EXPR
> @@ -3239,6 +3410,11 @@ cp_fold (tree x, fold_flags_t flags)
>        op0 = cp_fold_rvalue (TREE_OPERAND (x, 0), flags);
>        if (op0 == error_mark_node)
>       x = error_mark_node;
> +      else if (flags & ff_only_non_odr)
> +     {
> +       if (op0 != TREE_OPERAND (x, 0))
> +         x = build1_loc (EXPR_LOCATION (x), code, TREE_TYPE (x), op0);
> +     }
>        else
>       x = fold_convert (TREE_TYPE (x), op0);
>        break;
> @@ -3302,6 +3478,15 @@ cp_fold (tree x, fold_flags_t flags)
>        if (clear_decl_read)
>       DECL_READ_P (op0) = 0;
>  
> +      if (flags & ff_only_non_odr)
> +     {
> +       if (op0 == error_mark_node || op1 == error_mark_node)
> +         x = error_mark_node;
> +       else if (op0 != TREE_OPERAND (x, 0) || op1 != TREE_OPERAND (x, 1))
> +         x = build2_loc (loc, code, TREE_TYPE (x), op0, op1);
> +       break;
> +     }
> +
>        /* decltype(nullptr) has only one value, so optimize away all 
> comparisons
>        with that type right away, keeping them in the IL causes troubles for
>        various optimizations.  */
> @@ -3367,6 +3552,19 @@ cp_fold (tree x, fold_flags_t flags)
>        op1 = cp_fold (TREE_OPERAND (x, 1), flags);
>        op2 = cp_fold (TREE_OPERAND (x, 2), flags);
>  
> +      if (flags & ff_only_non_odr)
> +     {
> +       if (op0 == error_mark_node
> +           || op1 == error_mark_node
> +           || op2 == error_mark_node)
> +         x = error_mark_node;
> +       else if (op0 != TREE_OPERAND (x, 0)
> +                || op1 != TREE_OPERAND (x, 1)
> +                || op2 != TREE_OPERAND (x, 2))
> +         x = build3_loc (loc, code, TREE_TYPE (x), op0, op1, op2);
> +       break;
> +     }
> +
>        if (TREE_CODE (TREE_TYPE (x)) == BOOLEAN_TYPE)
>       {
>         warning_sentinel s (warn_int_in_bool_context);
> @@ -3463,7 +3661,8 @@ cp_fold (tree x, fold_flags_t flags)
>           && DECL_DECLARED_CONSTEXPR_P (current_function_decl))
>         nw = 1;
>  
> -     if (callee && fndecl_built_in_p (callee, BUILT_IN_FRONTEND))
> +     if (callee && !(flags & ff_only_non_odr)
> +         && fndecl_built_in_p (callee, BUILT_IN_FRONTEND))
>         {
>           iloc_sentinel ils (EXPR_LOCATION (x));
>           switch (DECL_FE_FUNCTION_CODE (callee))
> @@ -3494,14 +3693,6 @@ cp_fold (tree x, fold_flags_t flags)
>           break;
>         }
>  
> -     if (callee
> -         && fndecl_built_in_p (callee, CP_BUILT_IN_SOURCE_LOCATION,
> -                               BUILT_IN_FRONTEND))
> -       {
> -         x = fold_builtin_source_location (x);
> -         break;
> -       }
> -
>       bool changed = false;
>       int m = call_expr_nargs (x);
>       for (int i = 0; i < m; i++)
> @@ -3520,7 +3711,9 @@ cp_fold (tree x, fold_flags_t flags)
>               changed = true;
>             }
>         }
> -     if (x == error_mark_node)
> +     /* Don't fold away the function entirely if we're just folding
> +        non-ODR-used variables.  */
> +     if (x == error_mark_node || (flags & ff_only_non_odr))
>         break;
>  
>       optimize = nw;
> @@ -3645,7 +3838,8 @@ cp_fold (tree x, fold_flags_t flags)
>         TREE_THIS_VOLATILE (x) = TREE_THIS_VOLATILE (org_x);
>       }
>  
> -      x = fold (x);
> +      if (!(flags & ff_only_non_odr))
> +     x = fold (x);
>        break;
>  
>      case SAVE_EXPR:
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 4f077e70151..e41ff9c3b86 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -8868,8 +8868,10 @@ extern tree cxx_omp_map_array_section          
> (location_t, tree);
>  extern bool cxx_omp_privatize_by_reference   (const_tree);
>  extern bool cxx_omp_disregard_value_expr     (tree, bool);
>  extern void cp_fold_function                 (tree);
> +extern void cp_fold_function_non_odr_use     (tree);
>  extern tree cp_fold_maybe_rvalue             (tree, bool);
>  extern tree cp_fold_rvalue                   (tree);
> +extern tree cp_fold_non_odr_use                      (tree, bool);
>  extern tree cp_fully_fold                    (tree);
>  extern tree cp_fully_fold_init                       (tree);
>  extern tree predeclare_vla                   (tree);
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 751ba40fc7f..e5c2fe3ae6b 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -20418,14 +20418,19 @@ finish_function (bool inline_p)
>         || is_valid_constexpr_fn (fndecl, /*complain*/false))
>        && potential_constant_expression (DECL_SAVED_TREE (fndecl)));
>  
> -  /* Save constexpr function body before it gets munged by
> -     the NRV transformation.   */
> -  maybe_save_constexpr_fundef (fndecl);
> -
>    /* Invoke the pre-genericize plugin before we start munging things.  */
>    if (!processing_template_decl)
>      invoke_plugin_callbacks (PLUGIN_PRE_GENERICIZE, fndecl);
>  
> +  /* Fold away non-ODR usages of constants so that we don't need to
> +     try and stream them in modules if they're internal.  */
> +  if (!processing_template_decl)
> +    cp_fold_function_non_odr_use (fndecl);
> +
> +  /* Save constexpr function body before it gets munged by
> +     the NRV transformation.   */
> +  maybe_save_constexpr_fundef (fndecl);
> +
>    /* Perform delayed folding before NRV transformation.  */
>    if (!processing_template_decl
>        && !DECL_IMMEDIATE_FUNCTION_P (fndecl)
> diff --git a/gcc/cp/ptree.cc b/gcc/cp/ptree.cc
> index d074e0ea0fd..f89809b208a 100644
> --- a/gcc/cp/ptree.cc
> +++ b/gcc/cp/ptree.cc
> @@ -410,6 +410,15 @@ cxx_print_xnode (FILE *file, tree node, int indent)
>      case PTRMEM_CST:
>        print_node (file, "member", PTRMEM_CST_MEMBER (node), indent+4);
>        break;
> +    case TU_LOCAL_ENTITY:
> +      print_node (file, "name", TU_LOCAL_ENTITY_NAME (node), indent+4);
> +      if (location_t loc = TU_LOCAL_ENTITY_LOCATION (node))
> +     {
> +       expanded_location xloc = expand_location (loc);
> +       indent_to (file, indent+4);
> +       fprintf (file, "%s:%d:%d", xloc.file, xloc.line, xloc.column);
> +     }
> +      break;
>      default:
>        break;
>      }
> diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
> index 3edb74a256f..c222cf6590d 100644
> --- a/gcc/cp/tree.cc
> +++ b/gcc/cp/tree.cc
> @@ -3202,6 +3202,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_)
>        TARGET_EXPR_LIST_INIT_P (u) = TARGET_EXPR_LIST_INIT_P (t);
>        TARGET_EXPR_DIRECT_INIT_P (u) = TARGET_EXPR_DIRECT_INIT_P (t);
>        TARGET_EXPR_ELIDING_P (u) = TARGET_EXPR_ELIDING_P (t);
> +      TREE_CONSTANT (u) = TREE_CONSTANT (t);
>  
>        /* Map the old variable to the new one.  */
>        splay_tree_insert (target_remap,
> diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
> index d77de9212ed..dcef4f0b041 100644
> --- a/gcc/cp/typeck2.cc
> +++ b/gcc/cp/typeck2.cc
> @@ -1588,6 +1588,11 @@ digest_nsdmi_init (tree decl, tree init, 
> tsubst_flags_t complain)
>        && CP_AGGREGATE_TYPE_P (type))
>      init = reshape_init (type, init, complain);
>    init = digest_init_flags (type, init, flags, complain);
> +
> +  /* Fold away any non-ODR used constants so that we don't need to
> +     stream them in modules.  */
> +  init = cp_fold_non_odr_use (init, /*rval=*/!TYPE_REF_P (type));
> +
>    set_target_expr_eliding (init);
>  
>    /* We may have temporary materialization in a NSDMI, if the initializer
> diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C 
> b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
> index 909c34e8be6..485a610759f 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C
> @@ -7,18 +7,18 @@ int i;
>  // The following was accepted due to bug 49171.
>  constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error "not a 
> constant expression" }
>  
> -constexpr void *r0 = reinterpret_cast<void*>(1);    // { dg-error "not a 
> constant expression|'reinterpret_cast' from integer to pointer" }
> +constexpr void *r0 = reinterpret_cast<void*>(1);    // { dg-error 
> "'reinterpret_cast' from integer to pointer" }
>  constexpr void *r1 = reinterpret_cast<void*>(sizeof 'x');  // { dg-error 
> "'reinterpret_cast<void\\*>\\(1\[ul\]\*\\)' is not a constant expression" }
>  
>  template <class T>
>  constexpr bool f ()
>  {
>  #if __cplusplus > 201103L
> -  T *p = reinterpret_cast<T*>(sizeof (T));  // { dg-error "not a constant 
> expression" "" { target c++14 } }
> +  T *p = reinterpret_cast<T*>(sizeof (T));  // { dg-error 
> "'reinterpret_cast' from integer to pointer" "" { target c++14 } }
>    return p;
>  #else
> -  return *reinterpret_cast<T*>(sizeof (T));  // { dg-error "not a constant 
> expression" "" { target c++11_only } }
> +  return *reinterpret_cast<T*>(sizeof (T));  // { dg-error 
> "'reinterpret_cast' from integer to pointer" "" { target c++11_only } }
>  #endif
>  }
>  
> -constexpr bool b = f<int>();   // { dg-message "in .constexpr. expansion of 
> " }
> +constexpr bool b = f<int>();   // { dg-message "called in a constant 
> expression" }
> diff --git a/gcc/testsuite/g++.dg/modules/internal-8_a.C 
> b/gcc/testsuite/g++.dg/modules/internal-8_a.C
> index 46e07a23cf0..7a68ae9cecb 100644
> --- a/gcc/testsuite/g++.dg/modules/internal-8_a.C
> +++ b/gcc/testsuite/g++.dg/modules/internal-8_a.C
> @@ -1,35 +1,62 @@
> -// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
> -// Non-ODR usages of const variables currently are erroneously
> -// reported in templates and constexpr functions; this test
> -// XFAILS them until we can implement a fix.
> +// { dg-additional-options "-fmodules" }
> +// { dg-module-cmi M }
> +// Note we don't support eliding non-ODR usages within templates,
> +// as this is in general impossible to detect.
> +// FIXME we could probably at least elide cases that we can prove, though...
>  
>  export module M;
>  
> -namespace { struct internal_t {}; };
> +namespace {
> +  struct internal_t {};
> +  struct A { int x; };
> +  struct B { A a; };
> +};
>  static const int value = 123;
>  static const int& ref = 456;
>  static const internal_t internal {};
> +static constexpr B other { 789 };
> +static constexpr const B& other_ref = other;
> +static const B& other_ref_ref = (0, other_ref);
>  
>  constexpr void f(int) {}
>  
> -export constexpr
> -void no_odr_use_cexpr() {  // { dg-bogus "TU-local" "" { xfail *-*-* } }
> +constexpr int no_odr_use_cexpr() {
>    int x = value;
>    int y = ref;
>    int z = (internal, 0);
>  
>    value;
>    bool b = value < value;
> -  f(value);
> +  f(other_ref_ref.a.x);
> +  return value;
>  }
>  
> -export template <typename T>
> -void no_odr_use_templ() {  // { dg-bogus "TU-local" "" { xfail *-*-* } }
> -  int x = value;
> -  int y = ref;
> -  int z = (internal, 0);
> +struct S {};
> +static constexpr int S::* md = nullptr;
> +static constexpr void (S::* mfn)() = nullptr;
> +constexpr auto test_md() {
> +  auto pfn = md;
> +  return pfn;
> +}
> +constexpr auto test_mfn() {
> +  auto pfn = mfn;
> +  return pfn;
> +}
>  
> -  value;
> -  bool b = value < value;
> -  f(value);
> +namespace {
> +  struct Bitfield {
> +    int x : 5;
> +  };
> +  constexpr Bitfield bf{ 4 };
> +}
> +constexpr auto test_bitfield() {
> +  return bf.x;
>  }
> +
> +// PR c++/119097
> +namespace { static constexpr const int default_val { 789 }; }
> +struct A {
> +  int value { default_val };
> +  constexpr auto a() { return default_val; }
> +};
> +
> diff --git a/gcc/testsuite/g++.dg/modules/internal-8_b.C 
> b/gcc/testsuite/g++.dg/modules/internal-8_b.C
> new file mode 100644
> index 00000000000..c274b056a45
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/modules/internal-8_b.C
> @@ -0,0 +1,10 @@
> +// { dg-additional-options "-fmodules" }
> +
> +module M;
> +
> +static_assert(no_odr_use_cexpr() == 123);
> +static_assert(test_md() == nullptr);
> +static_assert(test_mfn() == nullptr);
> +static_assert(test_bitfield() == 4);
> +static_assert(A{}.value == 789);
> +static_assert(A{}.a() == 789);
> diff --git a/gcc/testsuite/g++.dg/warn/overflow-warn-1.C 
> b/gcc/testsuite/g++.dg/warn/overflow-warn-1.C
> index 9177373a939..6db1be8d0ce 100644
> --- a/gcc/testsuite/g++.dg/warn/overflow-warn-1.C
> +++ b/gcc/testsuite/g++.dg/warn/overflow-warn-1.C
> @@ -13,12 +13,15 @@ enum e {
>       in the standard).  */
>    E2 = 2 || 1 / 0, /* { dg-bogus "warning: division by zero" "" { xfail 
> *-*-* } } */
>    E3 = 1 / 0, /* { dg-warning "division by zero" } */
> -  /* { dg-error "enumerator value for 'E3' is not an integer constant|not a 
> constant.expression" "enum error" { target *-*-* } .-1 } */
> +  /* { dg-error "enumerator value for 'E3' is not an integer constant" "enum 
> error" { target *-*-* } .-1 } */
> +  /* { dg-error "' is not a constant expression" "enum error" { target c++ } 
> .-2 } */
> +  /* { dg-error "division by zero is not a constant expression" "division" { 
> target c++11 } .-3 } */
>    /* But as in DR#031, the 1/0 in an evaluated subexpression means the
>       whole expression violates the constraints.  */
>    E4 = 0 * (1 / 0), /* { dg-warning "division by zero" } */
> -  /* { dg-error "enumerator value for 'E4' is not an integer constant" "enum 
> error" { target c++ } .-1 } */
> -  /* { dg-error "division by zero is not a constant.expression" "division" { 
> target c++11 } .-2 } */
> +  /* { dg-error "enumerator value for 'E4' is not an integer constant" "enum 
> error" { target *-*-* } .-1 } */
> +  /* { dg-error "' is not a constant expression" "enum error" { target c++ } 
> .-2 } */
> +  /* { dg-error "division by zero is not a constant expression" "division" { 
> target c++11 } .-3 } */
>    E5 = INT_MAX + 1, /* { dg-warning "integer overflow in expression" } */
>    /* { dg-warning "overflow in constant expression" "constant" { target 
> *-*-* } .-1 } */
>    /* Again, overflow in evaluated subexpression.  */
> diff --git a/gcc/testsuite/g++.dg/warn/overflow-warn-3.C 
> b/gcc/testsuite/g++.dg/warn/overflow-warn-3.C
> index 91afd01e09b..4eba30ad9bd 100644
> --- a/gcc/testsuite/g++.dg/warn/overflow-warn-3.C
> +++ b/gcc/testsuite/g++.dg/warn/overflow-warn-3.C
> @@ -13,12 +13,15 @@ enum e {
>       in the standard).  */
>    E2 = 2 || 1 / 0, /* { dg-bogus "warning: division by zero" "" { xfail 
> *-*-* } } */
>    E3 = 1 / 0, /* { dg-warning "division by zero" } */
> -  /* { dg-error "enumerator value for 'E3' is not an integer constant|not a 
> constant.expression" "enum error" { target *-*-* } .-1 } */
> +  /* { dg-error "enumerator value for 'E3' is not an integer constant" "enum 
> error" { target *-*-* } .-1 } */
> +  /* { dg-error "' is not a constant expression" "enum error" { target c++ } 
> .-2 } */
> +  /* { dg-error "division by zero is not a constant expression" "division" { 
> target c++11 } .-3 } */
>    /* But as in DR#031, the 1/0 in an evaluated subexpression means the
>       whole expression violates the constraints.  */
>    E4 = 0 * (1 / 0), /* { dg-warning "division by zero" } */
>    /* { dg-error "enumerator value for 'E4' is not an integer constant" "enum 
> error" { target c++ } .-1 } */
> -  /* { dg-error "division by zero is not a constant.expression" "division" { 
> target c++11 } .-2 } */
> +  /* { dg-error "' is not a constant expression" "enum error" { target c++ } 
> .-2 } */
> +  /* { dg-error "division by zero is not a constant expression" "division" { 
> target c++11 } .-3 } */
>    E5 = INT_MAX + 1, /* { dg-warning "integer overflow in expression" } */
>    /* { dg-warning "overflow in constant expression" "constant" { target 
> *-*-* } .-1 } */
>    /* Again, overflow in evaluated subexpression.  */
> diff --git a/gcc/testsuite/g++.dg/warn/overflow-warn-4.C 
> b/gcc/testsuite/g++.dg/warn/overflow-warn-4.C
> index 3f0bf1f7240..3887356a2b7 100644
> --- a/gcc/testsuite/g++.dg/warn/overflow-warn-4.C
> +++ b/gcc/testsuite/g++.dg/warn/overflow-warn-4.C
> @@ -13,12 +13,15 @@ enum e {
>       in the standard).  */
>    E2 = 2 || 1 / 0, /* { dg-bogus "warning: division by zero" "" { xfail 
> *-*-* } } */
>    E3 = 1 / 0, /* { dg-warning "division by zero" } */
> -  /* { dg-error "enumerator value for 'E3' is not an integer constant|not a 
> constant.expression" "enum error" { target *-*-* } .-1 } */
> +  /* { dg-error "enumerator value for 'E3' is not an integer constant" "enum 
> error" { target *-*-* } .-1 } */
> +  /* { dg-error "' is not a constant expression" "enum error" { target c++ } 
> .-2 } */
> +  /* { dg-error "division by zero is not a constant expression" "division" { 
> target c++11 } .-3 } */
>    /* But as in DR#031, the 1/0 in an evaluated subexpression means the
>       whole expression violates the constraints.  */
>    E4 = 0 * (1 / 0), /* { dg-warning "division by zero" } */
>    /* { dg-error "enumerator value for 'E4' is not an integer constant" "enum 
> error" { target c++ } .-1 } */
> -  /* { dg-error "division by zero is not a constant.expression" "division" { 
> target c++11 } .-2 } */
> +  /* { dg-error "' is not a constant expression" "enum error" { target c++ } 
> .-2 } */
> +  /* { dg-error "division by zero is not a constant expression" "division" { 
> target c++11 } .-3 } */
>    E5 = INT_MAX + 1, /* { dg-warning "integer overflow in expression" } */
>    /* { dg-error "overflow in constant expression" "constant" { target *-*-* 
> } .-1 } */
>    /* { dg-error "enumerator value for 'E5' is not an integer constant" "enum 
> error" { target *-*-* } .-2 } */
> -- 
> 2.51.0
> 

Reply via email to