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 >
