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?

-- >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