On Sat, Jan 17, 2026 at 09:36:23PM +0800, Jason Merrill wrote:
> On 1/17/26 8:49 PM, Nathaniel Shead wrote:
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> >
> > I'm not sure if this is really the best approach in the end but should
> > fix the errors at least.
> >
> > -- >8 --
> >
> > The failure in the given PR occurs because when setting up an
> > imported vague-linkage variable, we currently call 'maybe_commonize_var'
> > which for -freflection checks 'consteval_only_p'. Unfortunately this
> > latter function needs to call 'complete_type_p' which can perform
> > recursive loading of the (possibly yet-to-be-streamed) class type,
> > breaking modules assumptions.
>
> Can we just drop the consteval_only_p check in maybe_commonize_var?
>
I'd tried that initially but that crashes in e.g. reflect/expr10.C:
#0 fancy_abort (file=0x43b6f87 "../../gcc/gcc/cp/mangle.cc", line=2316,
function=0x43b7428 "discriminator_for_local_entity") at
../../gcc/gcc/diagnostics/context.cc:1772
#1 0x00000000011dcdb7 in discriminator_for_local_entity (entity=<var_decl
0x7ffff75c2558 gl>) at ../../gcc/gcc/cp/mangle.cc:2316
#2 0x00000000011dd744 in write_local_name (function=<function_decl
0x7ffff75b6900 operator()>, local_entity=<var_decl 0x7ffff75c2558 gl>,
entity=<var_decl 0x7ffff75c2558 gl>)
at ../../gcc/gcc/cp/mangle.cc:2415
#3 0x00000000011d59d0 in write_name (decl=<var_decl 0x7ffff75c2558 gl>,
ignore_local_scope=0) at ../../gcc/gcc/cp/mangle.cc:1166
#4 0x00000000011d426a in write_encoding (decl=<var_decl 0x7ffff75c2558 gl>) at
../../gcc/gcc/cp/mangle.cc:939
#5 0x00000000011d3783 in write_mangled_name (decl=<var_decl 0x7ffff75c2558
gl>, top_level=true) at ../../gcc/gcc/cp/mangle.cc:821
#6 0x00000000011ea556 in mangle_decl_string (decl=<var_decl 0x7ffff75c2558
gl>) at ../../gcc/gcc/cp/mangle.cc:4716
#7 0x00000000011ea5b3 in get_mangled_id (decl=<var_decl 0x7ffff75c2558 gl>) at
../../gcc/gcc/cp/mangle.cc:4732
#8 0x00000000011ea904 in mangle_decl (decl=<var_decl 0x7ffff75c2558 gl>) at
../../gcc/gcc/cp/mangle.cc:4770
#9 0x00000000023a6fe4 in decl_assembler_name (decl=<var_decl 0x7ffff75c2558
gl>) at ../../gcc/gcc/tree.cc:856
#10 0x0000000001711d88 in symtab_node::get_comdat_group_id (this=<symtab_node *
const 0x7ffff7421400 "gl"/3>) at ../../gcc/gcc/cgraph.h:289
#11 0x00000000017361f2 in analyze_functions (first_time=true) at
../../gcc/gcc/cgraphunit.cc:1219
#12 0x0000000001739c11 in symbol_table::finalize_compilation_unit
(this=0x7ffff7406000) at ../../gcc/gcc/cgraphunit.cc:2590
#13 0x0000000001f1e5a8 in compile_file () at ../../gcc/gcc/toplev.cc:482
#14 0x0000000001f21c85 in do_compile () at ../../gcc/gcc/toplev.cc:2225
#15 0x0000000001f22135 in toplev::main (this=0x7fffffffd612, argc=26,
argv=0x7fffffffd758) at ../../gcc/gcc/toplev.cc:2390
#16 0x0000000003ecf9d9 in main (argc=26, argv=0x7fffffffd758) at
../../gcc/gcc/main.cc:39
The issue appears to be that because we give the variable a
comdat group we then attempt to mangle it at finalisation time, which
crashes. Perhaps we could move the check out to all callers of
'maybe_commonize_var' instead but I don't think that's a great choice
either.
> > It doesn't seem particularly easy to guarantee that the class type for a
> > variable will always have its definition be fully streamed before the
> > variable definition is, so as a workaround this patch introduces two new
> > flags on lang_type that can be used to skip the 'complete_type_p' call
> > and subsequent tree walk on the type definition if it has previously
> > been done.
> >
> > This solves the issue because such a vague linkage variable in an
> > imported module will necessarily have had 'consteval_only_p' called on
> > it (for the same reason I haven't been able to remove it here), and so
> > we stream the cached result and can skip the recursive load opportunity.
> >
> > One catch is that we also need to clear any existing cached results in
> > finish_struct, as it appears to be valid to call 'consteval_only_p' on
> > an incomplete type, and so caching this call would lead to incorrect
> > results after the type is completed.
> >
> > PR c++/122785
> >
> > gcc/cp/ChangeLog:
> >
> > * class.cc (finish_struct): Clear cached consteval_only_p flag.
> > * cp-tree.h (struct lang_type): New flags consteval_only and
> > consteval_only_set.
> > (CLASSTYPE_CONSTEVAL_ONLY_P): New accessor.
> > (CLASSTYPE_CONSTEVAL_ONLY_P_SET): New accessor.
> > * module.cc (trees_out::lang_type_bools): Stream the new flags.
> > (trees_in::lang_type_bools): Likewise.
> > (module_state_config::get_dialect): Note -freflection.
> > * reflect.cc (consteval_only_p): Cache the result of this call,
> > skip complete_type_p and the tree walk if a cached result is
> > already available.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * g++.dg/modules/reflect-1_a.H: New test.
> > * g++.dg/modules/reflect-1_b.C: New test.
> >
> > Signed-off-by: Nathaniel Shead <[email protected]>
> > ---
> > gcc/cp/class.cc | 3 +++
> > gcc/cp/cp-tree.h | 13 ++++++++++++-
> > gcc/cp/module.cc | 5 +++++
> > gcc/cp/reflect.cc | 16 +++++++++++++++-
> > gcc/testsuite/g++.dg/modules/reflect-1_a.H | 11 +++++++++++
> > gcc/testsuite/g++.dg/modules/reflect-1_b.C | 6 ++++++
> > 6 files changed, 52 insertions(+), 2 deletions(-)
> > create mode 100644 gcc/testsuite/g++.dg/modules/reflect-1_a.H
> > create mode 100644 gcc/testsuite/g++.dg/modules/reflect-1_b.C
> >
> > diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
> > index faf42c7979d..409e18d8310 100644
> > --- a/gcc/cp/class.cc
> > +++ b/gcc/cp/class.cc
> > @@ -8330,6 +8330,9 @@ finish_struct (tree t, tree attributes)
> > TYPE_BEING_DEFINED (t) = 0;
> > + /* Clear our cached understanding of if this type was consteval-only. */
> > + CLASSTYPE_CONSTEVAL_ONLY_P_SET (t) = 0;
> > +
> > if (current_class_type)
> > popclass ();
> > else
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index d7acfc8b7a3..88aceb8cf40 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -2633,6 +2633,8 @@ struct GTY(()) lang_type {
> > unsigned has_constexpr_ctor : 1;
> > unsigned unique_obj_representations : 1;
> > unsigned unique_obj_representations_set : 1;
> > + unsigned consteval_only : 1;
> > + unsigned consteval_only_set : 1;
> > bool erroneous : 1;
> > bool non_pod_aggregate : 1;
> > bool non_aggregate_pod : 1;
> > @@ -2646,7 +2648,7 @@ struct GTY(()) lang_type {
> > /* There are some bits left to fill out a 32-bit word. Keep track
> > of this by updating the size of this bitfield whenever you add or
> > remove a flag. */
> > - unsigned dummy : 2;
> > + /* unsigned dummy : 0; */
> > tree primary_base;
> > vec<tree_pair_s, va_gc> *vcall_indices;
> > @@ -2891,6 +2893,15 @@ struct GTY(()) lang_type {
> > #define CLASSTYPE_UNIQUE_OBJ_REPRESENTATIONS_SET(NODE) \
> > (LANG_TYPE_CLASS_CHECK (NODE)->unique_obj_representations_set)
> > +/* Nonzero means that this class type is consteval-only. */
> > +#define CLASSTYPE_CONSTEVAL_ONLY_P(NODE) \
> > + (LANG_TYPE_CLASS_CHECK (NODE)->consteval_only)
> > +
> > +/* Nonzero means that this class type has
> > + CLASSTYPE_CONSTEVAL_ONLY_P computed. */
> > +#define CLASSTYPE_CONSTEVAL_ONLY_P_SET(NODE) \
> > + (LANG_TYPE_CLASS_CHECK (NODE)->consteval_only_set)
> > +
> > /* Nonzero means that this class contains pod types whose default
> > initialization is not a zero initialization (namely, pointers to
> > data members). */
> > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > index c786519c1c0..8eb83b121fd 100644
> > --- a/gcc/cp/module.cc
> > +++ b/gcc/cp/module.cc
> > @@ -6314,6 +6314,8 @@ trees_out::lang_type_bools (tree t, bits_out& bits)
> > WB (lang->has_constexpr_ctor);
> > WB (lang->unique_obj_representations);
> > WB (lang->unique_obj_representations_set);
> > + WB (lang->consteval_only);
> > + WB (lang->consteval_only_set);
> > gcc_checking_assert (!lang->erroneous);
> > WB (lang->non_pod_aggregate);
> > WB (lang->non_aggregate_pod);
> > @@ -6387,6 +6389,8 @@ trees_in::lang_type_bools (tree t, bits_in& bits)
> > RB (lang->has_constexpr_ctor);
> > RB (lang->unique_obj_representations);
> > RB (lang->unique_obj_representations_set);
> > + RB (lang->consteval_only);
> > + RB (lang->consteval_only_set);
> > gcc_checking_assert (!lang->erroneous);
> > RB (lang->non_pod_aggregate);
> > RB (lang->non_aggregate_pod);
> > @@ -17136,6 +17140,7 @@ module_state_config::get_dialect ()
> > (cxx_dialect < cxx20 && flag_coroutines
> > ? "/coroutines" : ""),
> > flag_module_implicit_inline ? "/implicit-inline" : "",
> > + flag_reflection ? "/reflection" : "",
> > flag_contracts ? "/contracts" : "",
> > NULL);
> > diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> > index 8f85a6bab5d..a31675f9f68 100644
> > --- a/gcc/cp/reflect.cc
> > +++ b/gcc/cp/reflect.cc
> > @@ -8071,6 +8071,13 @@ consteval_only_p (tree t)
> > if (!t)
> > return false;
> > + if (REFLECTION_TYPE_P (t))
> > + return true;
> > +
> > + /* If we've got a cached result, we have nothing further to do. */
> > + if (CLASS_TYPE_P (t) && CLASSTYPE_CONSTEVAL_ONLY_P_SET (t))
> > + return CLASSTYPE_CONSTEVAL_ONLY_P (t);
> > +
> > /* We need the complete type otherwise we'd have no fields for class
> > templates and thus come up with zilch for things like
> > template<typename T>
> > @@ -8080,7 +8087,14 @@ consteval_only_p (tree t)
> > /* Classes with std::meta::info members are also consteval-only. */
> > hash_set<tree> visited;
> > - return !!cp_walk_tree (&t, consteval_only_type_r, &visited, &visited);
> > + bool result = cp_walk_tree (&t, consteval_only_type_r, &visited,
> > &visited);
> > +
> > + if (CLASS_TYPE_P (t))
> > + {
> > + CLASSTYPE_CONSTEVAL_ONLY_P_SET (t) = true;
> > + CLASSTYPE_CONSTEVAL_ONLY_P (t) = result;
> > + }
> > + return result;
> > }
> > /* Detect if a consteval-only expression EXPR or a consteval-only
> > diff --git a/gcc/testsuite/g++.dg/modules/reflect-1_a.H
> > b/gcc/testsuite/g++.dg/modules/reflect-1_a.H
> > new file mode 100644
> > index 00000000000..025f2e27ea1
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/reflect-1_a.H
> > @@ -0,0 +1,11 @@
> > +// PR c++/122785
> > +// { dg-do compile { target c++26 } }
> > +// { dg-additional-options "-fmodule-header -freflection" }
> > +// { dg-module-cmi {} }
> > +
> > +struct S {
> > + friend S foo();
> > + S bar(int);
> > +};
> > +inline S s;
> > +template <typename T> decltype(s.bar(T{})) foo(T);
> > diff --git a/gcc/testsuite/g++.dg/modules/reflect-1_b.C
> > b/gcc/testsuite/g++.dg/modules/reflect-1_b.C
> > new file mode 100644
> > index 00000000000..a3c5b67d09a
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/modules/reflect-1_b.C
> > @@ -0,0 +1,6 @@
> > +// PR c++/122785
> > +// { dg-do compile { target c++26 } }
> > +// { dg-additional-options "-fmodules -freflection" }
> > +
> > +import "reflect-1_a.H";
> > +using ::S;
>