https://gcc.gnu.org/g:d91e9e992cf8edf7dfcbe5a2d1f4cb05a66d7640
commit r17-5510-gd91e9e992cf8edf7dfcbe5a2d1f4cb05a66d7640 Author: Marek Polacek <[email protected]> Date: Tue Oct 13 14:54:57 2026 -0400 c++/reflection: use OPAQUE_TYPE for std::meta::info Currently, we use the LANG_TYPE node to represent the C++ Reflection type std::meta::info. Unfortunately that doesn't work too well with streaming for LTO, see 126017. Another problem was that the std::meta::info LANG_TYPE wasn't marked TYPE_CONTAINS_PLACEHOLDER_INTERNAL (127856). Jakub suggested changing LANG_TYPE to OPAQUE_TYPE. OPAQUE_TYPE was introduced in GCC 11 for PowerPC MMA __vector_pair/__vector_quad types. Its description reads as pretty much what we want for std::meta::info. One difference between the PowerPC MMA types and std::meta::info is that the latter has a scalar integer mode instead of MODE_OPAQUE, so it's possible to distinguish between them. I've checked rs6000_mangle_type and I don't think it needs any changes. Using OPAQUE_TYPE fixes both the LTO crashes and 127856. PR c++/126017 PR c++/127856 gcc/cp/ChangeLog: * cxx-pretty-print.cc (cxx_pretty_printer::simple_type_specifier) <case LANG_TYPE>: Rename to OPAQUE_TYPE. (cxx_pretty_printer::type_id) <case LANG_TYPE>: Likewise. * mangle.cc (write_type) <case LANG_TYPE>: Likewise. * module.cc (trees_out::type_node) <case LANG_TYPE>: Likewise. (trees_in::tree_node) <case LANG_TYPE>: Likewise. * name-lookup.cc (name_lookup::adl_type) <case LANG_TYPE>: Don't check REFLECTION_TYPE_P here. <case OPAQUE_TYPE>: New. * reflect.cc (init_reflection): Create an OPAQUE_TYPE node instead of a LANG_TYPE node. * tree.cc (type_has_unique_obj_representations) <case LANG_TYPE>: Rename to OPAQUE_TYPE. gcc/ChangeLog: * doc/generic.texi: Update OPAQUE_TYPE description. * dwarf2out.cc (gen_type_die_with_usage) <case OPAQUE_TYPE>: If SCALAR_INT_MODE_P, goto unspecified_type. * tree.cc (wide_int_to_tree_1) <case LANG_TYPE>: Rename to OPAQUE_TYPE. Assert SCALAR_INT_MODE_P. (cache_integer_cst) <case LANG_TYPE>: Likewise. (verify_opaque_type): Allow SCALAR_INT_MODE_P mode. * tree.def (OPAQUE_TYPE): Update description. gcc/testsuite/ChangeLog: * g++.dg/reflect/diag6.C: Adjust expected diagnostic. * g++.dg/reflect/init2.C: Likewise. * g++.dg/lto/pr126017_0.C: New test. * g++.dg/lto/pr126017_1.C: New test. * g++.dg/reflect/pr127856.C: New test. * g++.target/powerpc/reflection1.C: New test. Co-authored-by: Jakub Jelinek <[email protected]> Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/cxx-pretty-print.cc | 4 ++-- gcc/cp/mangle.cc | 2 +- gcc/cp/module.cc | 10 ++++++---- gcc/cp/name-lookup.cc | 14 +++++++------- gcc/cp/reflect.cc | 2 +- gcc/cp/tree.cc | 2 +- gcc/doc/generic.texi | 8 +++++--- gcc/dwarf2out.cc | 6 +++++- gcc/testsuite/g++.dg/lto/pr126017_0.C | 14 ++++++++++++++ gcc/testsuite/g++.dg/lto/pr126017_1.C | 22 ++++++++++++++++++++++ gcc/testsuite/g++.dg/reflect/diag6.C | 4 ++-- gcc/testsuite/g++.dg/reflect/init2.C | 2 +- gcc/testsuite/g++.dg/reflect/pr127856.C | 11 +++++++++++ gcc/testsuite/g++.target/powerpc/reflection1.C | 19 +++++++++++++++++++ gcc/tree.cc | 13 ++++++++++--- gcc/tree.def | 3 ++- 16 files changed, 109 insertions(+), 27 deletions(-) diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc index f844039eb994..acf0e2b2da12 100644 --- a/gcc/cp/cxx-pretty-print.cc +++ b/gcc/cp/cxx-pretty-print.cc @@ -1442,7 +1442,7 @@ cxx_pretty_printer::simple_type_specifier (tree t) pp_cxx_trait (this, t); break; - case LANG_TYPE: + case OPAQUE_TYPE: if (REFLECTION_TYPE_P (t)) { pp_cxx_ws_string (this, "std::meta::info"); @@ -1978,7 +1978,7 @@ cxx_pretty_printer::type_id (tree t) } break; - case LANG_TYPE: + case OPAQUE_TYPE: if (REFLECTION_TYPE_P (t)) { pp_cxx_type_specifier_seq (this, t); diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 50af008c73b6..db194b8b3977 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -2770,7 +2770,7 @@ write_type (tree type) write_expression (PACK_INDEX_INDEX (type)); break; - case LANG_TYPE: + case OPAQUE_TYPE: if (REFLECTION_TYPE_P (type)) { write_string ("Dm"); diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 1fb2eab99f65..c07deff284e3 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -9890,8 +9890,10 @@ trees_out::type_node (tree type) } break; - case LANG_TYPE: - /* No additional data. */ + case OPAQUE_TYPE: + /* No additional data. OPAQUE_TYPEs created by the rs6000 backend + are registered as built-in types and are recorded in fixed_trees + so they will not get here. */ gcc_checking_assert (REFLECTION_TYPE_P (type)); break; @@ -10746,10 +10748,10 @@ trees_in::tree_node (bool is_use) } break; - /* LANG_TYPE can be more things, but here we assume it represents + /* OPAQUE_TYPE can be more things, but here we assume it represents std::meta::info. Unfortunately here it's not possible to check REFLECTION_TYPE_P. */ - case LANG_TYPE: + case OPAQUE_TYPE: if (!get_overrun ()) res = meta_info_type_node; break; diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index f9a5fda7e1fc..9fd20c0cfd7e 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -1604,13 +1604,6 @@ name_lookup::adl_type (tree type) return; case LANG_TYPE: - if (REFLECTION_TYPE_P (type)) - { - /* The namespace std::meta is an associated namespace of - std::meta::info. */ - adl_namespace (std_meta_node); - return; - } gcc_assert (type == unknown_type_node || type == init_list_type_node); return; @@ -1619,6 +1612,13 @@ name_lookup::adl_type (tree type) adl_type (PACK_EXPANSION_PATTERN (type)); return; + case OPAQUE_TYPE: + if (REFLECTION_TYPE_P (type)) + /* The namespace std::meta is an associated namespace of + std::meta::info. */ + adl_namespace (std_meta_node); + return; + default: break; } diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index 8bf518df64fa..fd430342de93 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -61,7 +61,7 @@ init_reflection () /* The type std::meta::info is a scalar type for which equality and inequality are meaningful, but for which no ordering relation is defined. */ - meta_info_type_node = make_node (LANG_TYPE); + meta_info_type_node = make_node (OPAQUE_TYPE); /* Make it a complete type. */ TYPE_SIZE (meta_info_type_node) = bitsize_int (GET_MODE_BITSIZE (ptr_mode)); TYPE_SIZE_UNIT (meta_info_type_node) = size_int (GET_MODE_SIZE (ptr_mode)); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 930694759656..ca77c611c1d7 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -5175,7 +5175,7 @@ type_has_unique_obj_representations (const_tree t, bool explain/*=false*/) inform (loc, "%<std::nullptr_t%> has padding bits and no value bits"); return false; - case LANG_TYPE: + case OPAQUE_TYPE: if (REFLECTION_TYPE_P (t)) { if (explain) diff --git a/gcc/doc/generic.texi b/gcc/doc/generic.texi index e61b151cd863..cc42ba85ac75 100644 --- a/gcc/doc/generic.texi +++ b/gcc/doc/generic.texi @@ -498,10 +498,12 @@ constants will be the type of enumeration type itself. @item OPAQUE_TYPE Used for things that have a @code{MODE_OPAQUE} mode class in the -backend. Opaque types have a size and precision, and can be held in -memory or registers. They are used when we do not want the compiler to +backend. Opaque types have a size and precision, and can be held in +memory or registers. They are used when we do not want the compiler to make assumptions about the availability of other operations as would -happen with integer types. +happen with integer types. This type can also have a scalar integer +mode; this is how the C++ front end represents the @code{std::meta::info} +type. The only @code{INTEGER_CST} of such a type is zero. @item BOOLEAN_TYPE Used to represent the @code{bool} type. diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc index 5348f0aa7a0c..e8a6019f72e0 100644 --- a/gcc/dwarf2out.cc +++ b/gcc/dwarf2out.cc @@ -26992,8 +26992,12 @@ gen_type_die_with_usage (tree type, dw_die_ref context_die, gen_tagged_type_die (type, context_die, usage, reverse); return; - case VOID_TYPE: case OPAQUE_TYPE: + if (SCALAR_INT_MODE_P (TYPE_MODE (type))) + /* C++ std::meta::info type. */ + goto unspecified_type; + gcc_fallthrough (); + case VOID_TYPE: case INTEGER_TYPE: case REAL_TYPE: case FIXED_POINT_TYPE: diff --git a/gcc/testsuite/g++.dg/lto/pr126017_0.C b/gcc/testsuite/g++.dg/lto/pr126017_0.C new file mode 100644 index 000000000000..97a8e862ff90 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr126017_0.C @@ -0,0 +1,14 @@ +// PR c++/126017 +// { dg-lto-do run } +// { dg-lto-options { { -std=c++26 -O2 -flto -freflection -g } } } + +using info = decltype (^^::); +info a {}; +extern info foo (info); + +int +main () +{ + if (a != info{} || foo (a) != a) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/lto/pr126017_1.C b/gcc/testsuite/g++.dg/lto/pr126017_1.C new file mode 100644 index 000000000000..4a63fa2cbf45 --- /dev/null +++ b/gcc/testsuite/g++.dg/lto/pr126017_1.C @@ -0,0 +1,22 @@ +// PR c++/126017 + +#include <meta> + +extern std::meta::info a; + +consteval std::meta::info +bar () +{ + struct S; + consteval { define_aggregate (^^S, { data_member_spec (^^int, { .name = "abc" }) }); } + return ^^S; +} + +struct T : [: bar () :] {}; + +std::meta::info +foo (std::meta::info x) +{ + T a; + return x; +} diff --git a/gcc/testsuite/g++.dg/reflect/diag6.C b/gcc/testsuite/g++.dg/reflect/diag6.C index 2eed4d0b6336..14cd5f4709f2 100644 --- a/gcc/testsuite/g++.dg/reflect/diag6.C +++ b/gcc/testsuite/g++.dg/reflect/diag6.C @@ -5,6 +5,6 @@ void g () { - constexpr decltype(^^::) dm = ^^int; // { dg-message ".constexpr std::meta::info const dm. previously declared here" } - constexpr decltype(^^::) dm = ^^int; // { dg-error "redeclaration of .constexpr std::meta::info const dm." } + constexpr decltype(^^::) dm = ^^int; // { dg-message ".constexpr const std::meta::info dm. previously declared here" } + constexpr decltype(^^::) dm = ^^int; // { dg-error "redeclaration of .constexpr const std::meta::info dm." } } diff --git a/gcc/testsuite/g++.dg/reflect/init2.C b/gcc/testsuite/g++.dg/reflect/init2.C index c4f2292f21d5..c95570068bcd 100644 --- a/gcc/testsuite/g++.dg/reflect/init2.C +++ b/gcc/testsuite/g++.dg/reflect/init2.C @@ -8,7 +8,7 @@ f () constexpr static auto srefl = ^^int; constexpr auto *p = &srefl; constexpr auto **q = &p; // { dg-error "unable to deduce" } - // { dg-message "types .auto\\*. and .std::meta::info const\\* const." "" { target *-*-* } .-1 } + // { dg-message "types .auto\\*. and .const std::meta::info\\* const." "" { target *-*-* } .-1 } } void diff --git a/gcc/testsuite/g++.dg/reflect/pr127856.C b/gcc/testsuite/g++.dg/reflect/pr127856.C new file mode 100644 index 000000000000..0faebce184f3 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/pr127856.C @@ -0,0 +1,11 @@ +// PR c++/127856 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-O2 -freflection" } + +struct A { decltype (^^int) a; bool b = true; }; + +void +foo () +{ + A a; +} diff --git a/gcc/testsuite/g++.target/powerpc/reflection1.C b/gcc/testsuite/g++.target/powerpc/reflection1.C new file mode 100644 index 000000000000..815c030e61bd --- /dev/null +++ b/gcc/testsuite/g++.target/powerpc/reflection1.C @@ -0,0 +1,19 @@ +// { dg-do compile { target c++26 } } +// { dg-require-effective-target power10_ok } +// { dg-additional-options "-freflection -mcpu=power10" } + +using info = decltype (^^::); + +template <typename T> struct W {}; + +void f (W<info>) {} +void f (W<__vector_quad>) {} +void f (W<__vector_pair>) {} + +static_assert (^^info != ^^__vector_quad); +static_assert (__is_scalar (info)); +static_assert (!__is_same (info, __vector_quad)); + +// { dg-final { scan-assembler "_Z1f1WIDmE" } } +// { dg-final { scan-assembler "_Z1f1WIu13__vector_quadE" } } +// { dg-final { scan-assembler "_Z1f1WIu13__vector_pairE" } } diff --git a/gcc/tree.cc b/gcc/tree.cc index e43a2b78c61c..7e5a53234bae 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -1862,7 +1862,9 @@ wide_int_to_tree_1 (tree type, const wide_int_ref &pcst) switch (code) { /* This could represent the C++ std::meta::info type. */ - case LANG_TYPE: + case OPAQUE_TYPE: + gcc_assert (SCALAR_INT_MODE_P (TYPE_MODE (type))); + /* Fallthru. */ case NULLPTR_TYPE: gcc_assert (hwi == 0); /* Fallthru. */ @@ -2039,7 +2041,9 @@ cache_integer_cst (tree t, bool might_duplicate ATTRIBUTE_UNUSED) switch (TREE_CODE (type)) { /* This could represent the C++ std::meta::info type. */ - case LANG_TYPE: + case OPAQUE_TYPE: + gcc_assert (SCALAR_INT_MODE_P (TYPE_MODE (type))); + /* Fallthru. */ case NULLPTR_TYPE: gcc_checking_assert (integer_zerop (t)); /* Fallthru. */ @@ -14540,7 +14544,10 @@ verify_opaque_type (const_tree t, tree tv, tree tc) debug_tree (t2); return; } - if (!OPAQUE_MODE_P (TYPE_MODE (t2))) + /* For C++ Reflection, we use the opaque type with a scalar integer + mode. */ + if (!OPAQUE_MODE_P (TYPE_MODE (t2)) + && !SCALAR_INT_MODE_P (TYPE_MODE (t2))) { error ("type %s is not with opaque mode", kind_msg); debug_tree (t2); diff --git a/gcc/tree.def b/gcc/tree.def index 2e767dbe0453..c11ad1abf24b 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -265,7 +265,8 @@ DEFTREECODE (LANG_TYPE, "lang_type", tcc_type, 0) /* This is for types that will use MODE_OPAQUE in the back end. They are meant to be able to go in a register of some sort but are explicitly not to be converted or operated on like INTEGER_TYPE. They will have size and - alignment information only. */ + alignment information only. The C++ FE uses this to represent the + std::meta::info type; in that case, it has a scalar integer mode. */ DEFTREECODE (OPAQUE_TYPE, "opaque_type", tcc_type, 0) /* Expressions */
