https://gcc.gnu.org/g:c2ea08d913db442995864b37da9900f00aa38188
commit r17-1813-gc2ea08d913db442995864b37da9900f00aa38188 Author: Marek Polacek <[email protected]> Date: Tue Jun 23 17:39:29 2026 -0400 c++/reflection: type traits and reference collapsing [PR125939] This PR shows that some of our meta type traits don't work with references: we emit bogus error: 'const' qualifiers cannot be applied to 'int&' errors. The problem is that build_stub_type is trying to add const to a reference, which you can only do through a typedef, but here we don't have a typedef. Resolved by passing tf_ignore_bad_quals to cp_build_qualified_type. The new build_const_lref helper is to spruce up the code a bit. PR c++/125939 gcc/cp/ChangeLog: * cp-tree.h (build_const_lref): Declare. * method.cc (build_stub_type): Pass tf_ignore_bad_quals to cp_build_qualified_type. * reflect.cc (build_const_lref): New. (get_range_elts): Use it. (eval_is_copy_constructible_type): Likewise. (eval_is_copy_assignable_type): Likewise. (eval_is_trivially_copy_assignable_type): Likewise. (eval_is_nothrow_copy_constructible_type): Likewise. (eval_is_nothrow_copy_assignable_type): Likewise. * tree.cc (trivially_copy_constructible_p): Likewise. (handle_annotation_attribute): Likewise. gcc/testsuite/ChangeLog: * g++.dg/reflect/type_trait15.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/cp-tree.h | 1 + gcc/cp/method.cc | 4 +- gcc/cp/reflect.cc | 29 +++--- gcc/cp/tree.cc | 7 +- gcc/testsuite/g++.dg/reflect/type_trait15.C | 133 ++++++++++++++++++++++++++++ 5 files changed, 154 insertions(+), 20 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 132139f9d520..76ed1e59dec3 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -9530,6 +9530,7 @@ extern void coro_set_ramp_function (tree, tree); /* In reflect.cc */ extern void init_reflection (); +WARN_UNUSED_RESULT extern tree build_const_lref (tree); extern tree maybe_update_function_parm (tree); extern bool metafunction_p (tree) ATTRIBUTE_PURE; extern tree direct_base_derived (tree) ATTRIBUTE_PURE; diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index c511ea9a0642..4c432efb56ae 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -1902,7 +1902,9 @@ maybe_synthesize_method (tree fndecl) tree build_stub_type (tree type, int quals, bool rvalue) { - tree argtype = cp_build_qualified_type (type, quals); + tree argtype + = cp_build_qualified_type (type, quals, + tf_warning_or_error | tf_ignore_bad_quals); return cp_build_reference_type (argtype, rvalue); } diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index bf1c41772d34..3d6d24eb23ac 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -324,6 +324,15 @@ maybe_update_function_parm (tree parm) return ret; } +/* Build up const T &. */ + +tree +build_const_lref (tree t) +{ + return build_stub_type (t, cp_type_quals (t) | TYPE_QUAL_CONST, + /*rval=*/false); +} + /* Return true if DECL comes from std::meta. */ static bool @@ -587,10 +596,7 @@ get_range_elts (location_t loc, const constexpr_ctx *ctx, tree call, int n, *non_constant_p = true; return NULL_TREE; } - TREE_VEC_ELT (args, 0) - = build_stub_type (valuete, - cp_type_quals (valuete) | TYPE_QUAL_CONST, - false); + TREE_VEC_ELT (args, 0) = build_const_lref (valuete); if (!is_xible (INIT_EXPR, valuete, args)) { if (!cxx_constexpr_quiet_p (ctx)) @@ -4570,8 +4576,7 @@ static tree eval_is_copy_constructible_type (tree type) { tree arg = make_tree_vec (1); - TREE_VEC_ELT (arg, 0) - = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false); + TREE_VEC_ELT (arg, 0) = build_const_lref (type); if (is_xible (INIT_EXPR, type, arg)) return boolean_true_node; else @@ -4605,8 +4610,7 @@ static tree eval_is_copy_assignable_type (tree type) { tree type1 = cp_build_reference_type (type, /*rval=*/false); - tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, - false); + tree type2 = build_const_lref (type); if (is_xible (MODIFY_EXPR, type1, type2)) return boolean_true_node; else @@ -4694,8 +4698,7 @@ static tree eval_is_trivially_copy_assignable_type (tree type) { tree type1 = cp_build_reference_type (type, /*rval=*/false); - tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, - false); + tree type2 = build_const_lref (type); if (is_trivially_xible (MODIFY_EXPR, type1, type2)) return boolean_true_node; else @@ -4751,8 +4754,7 @@ static tree eval_is_nothrow_copy_constructible_type (tree type) { tree arg = make_tree_vec (1); - TREE_VEC_ELT (arg, 0) - = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false); + TREE_VEC_ELT (arg, 0) = build_const_lref (type); if (is_nothrow_xible (INIT_EXPR, type, arg)) return boolean_true_node; else @@ -4786,8 +4788,7 @@ static tree eval_is_nothrow_copy_assignable_type (tree type) { tree type1 = cp_build_reference_type (type, /*rval=*/false); - tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, - false); + tree type2 = build_const_lref (type); if (is_nothrow_xible (MODIFY_EXPR, type1, type2)) return boolean_true_node; else diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index d9a0583b8b83..dc885e47c01e 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -4915,8 +4915,7 @@ bool trivially_copy_constructible_p (tree t) { tree arg = make_tree_vec (1); - TREE_VEC_ELT (arg, 0) - = build_stub_type (t, cp_type_quals (t) | TYPE_QUAL_CONST, false); + TREE_VEC_ELT (arg, 0) = build_const_lref (t); return is_trivially_xible (INIT_EXPR, t, arg); } @@ -5982,9 +5981,7 @@ handle_annotation_attribute (tree *node, tree ARG_UNUSED (name), { tree arg = make_tree_vec (1); tree type = TREE_TYPE (TREE_VALUE (args)); - TREE_VEC_ELT (arg, 0) - = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, - /*rvalue=*/false); + TREE_VEC_ELT (arg, 0) = build_const_lref (type); if (!is_xible (INIT_EXPR, type, arg)) { auto_diagnostic_group d; diff --git a/gcc/testsuite/g++.dg/reflect/type_trait15.C b/gcc/testsuite/g++.dg/reflect/type_trait15.C new file mode 100644 index 000000000000..f0ce2e41e0b8 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/type_trait15.C @@ -0,0 +1,133 @@ +// PR c++/125939 +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +#include <meta> + +template<typename T> +constexpr bool cct = std::meta::is_copy_constructible_type(^^T); + +int main() { + using T = int&; + static_assert(std::meta::is_copy_constructible_type(^^T)); + static_assert(cct<int&>); +} + +static_assert (is_copy_constructible_type (^^int)); +static_assert (std::is_copy_constructible_v<int>); +static_assert (is_copy_constructible_type (^^const int)); +static_assert (std::is_copy_constructible_v<const int>); +static_assert (is_copy_constructible_type (^^int &)); +static_assert (std::is_copy_constructible_v<int &>); +static_assert (is_copy_constructible_type (^^const int &)); +static_assert (std::is_copy_constructible_v<const int &>); +static_assert (!is_copy_constructible_type (^^int &&)); +static_assert (!std::is_copy_constructible_v<int &&>); +static_assert (!is_copy_constructible_type (^^const int &&)); +static_assert (!std::is_copy_constructible_v<const int &&>); +static_assert (!is_copy_constructible_type (^^void)); +static_assert (!std::is_copy_constructible_v<void>); +static_assert (!is_copy_constructible_type (^^int() const &)); +static_assert (!std::is_copy_constructible_v<int() const &>); + +static_assert (is_trivially_copy_constructible_type (^^int)); +static_assert (std::is_trivially_copy_constructible_v<int>); +static_assert (is_trivially_copy_constructible_type (^^const int)); +static_assert (std::is_trivially_copy_constructible_v<const int>); +static_assert (is_trivially_copy_constructible_type (^^int &)); +static_assert (std::is_trivially_copy_constructible_v<int &>); +static_assert (is_trivially_copy_constructible_type (^^const int &)); +static_assert (std::is_trivially_copy_constructible_v<const int &>); +static_assert (!is_trivially_copy_constructible_type (^^int &&)); +static_assert (!std::is_trivially_copy_constructible_v<int &&>); +static_assert (!is_trivially_copy_constructible_type (^^const int &&)); +static_assert (!std::is_trivially_copy_constructible_v<const int &&>); +static_assert (!is_trivially_copy_constructible_type (^^void)); +static_assert (!std::is_trivially_copy_constructible_v<void>); +static_assert (!is_trivially_copy_constructible_type (^^int() const &)); +static_assert (!std::is_trivially_copy_constructible_v<int() const &>); + +static_assert (is_nothrow_copy_constructible_type (^^int)); +static_assert (std::is_nothrow_copy_constructible_v<int>); +static_assert (is_nothrow_copy_constructible_type (^^const int)); +static_assert (std::is_nothrow_copy_constructible_v<const int>); +static_assert (is_nothrow_copy_constructible_type (^^int &)); +static_assert (std::is_nothrow_copy_constructible_v<int &>); +static_assert (is_nothrow_copy_constructible_type (^^const int &)); +static_assert (std::is_nothrow_copy_constructible_v<const int &>); +static_assert (!is_nothrow_copy_constructible_type (^^int &&)); +static_assert (!std::is_nothrow_copy_constructible_v<int &&>); +static_assert (!is_nothrow_copy_constructible_type (^^const int &&)); +static_assert (!std::is_nothrow_copy_constructible_v<const int &&>); +static_assert (!is_nothrow_copy_constructible_type (^^void)); +static_assert (!std::is_nothrow_copy_constructible_v<void>); +static_assert (!is_nothrow_copy_constructible_type (^^int() const &)); +static_assert (!std::is_nothrow_copy_constructible_v<int() const &>); + +static_assert (!is_assignable_type (^^int, ^^int)); +static_assert (!std::is_assignable_v<int, int>); +static_assert (!is_assignable_type (^^const int, ^^int)); +static_assert (!std::is_assignable_v<const int, int>); +static_assert (is_assignable_type (^^int &, ^^int)); +static_assert (std::is_assignable_v<int &, int>); +static_assert (!is_assignable_type (^^const int &, ^^int)); +static_assert (!std::is_assignable_v<const int &, int>); +static_assert (!is_assignable_type (^^int &&, ^^int)); +static_assert (!std::is_assignable_v<int &&, int>); +static_assert (!is_assignable_type (^^const int &&, ^^int)); +static_assert (!std::is_assignable_v<const int &&, int>); +static_assert (!is_assignable_type (^^void, ^^int)); +static_assert (!std::is_assignable_v<void, int>); +static_assert (!is_assignable_type (^^int() const &, ^^int)); +static_assert (!std::is_assignable_v<int() const &, int>); + +static_assert (is_copy_assignable_type (^^int)); +static_assert (std::is_copy_assignable_v<int>); +static_assert (!is_copy_assignable_type (^^const int)); +static_assert (!std::is_copy_assignable_v<const int>); +static_assert (is_copy_assignable_type (^^int &)); +static_assert (std::is_copy_assignable_v<int &>); +static_assert (!is_copy_assignable_type (^^const int &)); +static_assert (!std::is_copy_assignable_v<const int &>); +static_assert (is_copy_assignable_type (^^int &&)); +static_assert (std::is_copy_assignable_v<int &&>); +static_assert (!is_copy_assignable_type (^^const int &&)); +static_assert (!std::is_copy_assignable_v<const int &&>); +static_assert (!is_copy_assignable_type (^^void)); +static_assert (!std::is_copy_assignable_v<void>); +static_assert (!is_copy_assignable_type (^^int() const &)); +static_assert (!std::is_copy_assignable_v<int() const &>); + +static_assert (is_trivially_copy_assignable_type (^^int)); +static_assert (std::is_trivially_copy_assignable_v<int>); +static_assert (!is_trivially_copy_assignable_type (^^const int)); +static_assert (!std::is_trivially_copy_assignable_v<const int>); +static_assert (is_trivially_copy_assignable_type (^^int &)); +static_assert (std::is_trivially_copy_assignable_v<int &>); +static_assert (!is_trivially_copy_assignable_type (^^const int &)); +static_assert (!std::is_trivially_copy_assignable_v<const int &>); +static_assert (is_trivially_copy_assignable_type (^^int &&)); +static_assert (std::is_trivially_copy_assignable_v<int &&>); +static_assert (!is_trivially_copy_assignable_type (^^const int &&)); +static_assert (!std::is_trivially_copy_assignable_v<const int &&>); +static_assert (!is_trivially_copy_assignable_type (^^void)); +static_assert (!std::is_trivially_copy_assignable_v<void>); +static_assert (!is_trivially_copy_assignable_type (^^int() const &)); +static_assert (!std::is_trivially_copy_assignable_v<int() const &>); + +static_assert (is_nothrow_copy_assignable_type (^^int)); +static_assert (std::is_nothrow_copy_assignable_v<int>); +static_assert (!is_nothrow_copy_assignable_type (^^const int)); +static_assert (!std::is_nothrow_copy_assignable_v<const int>); +static_assert (is_nothrow_copy_assignable_type (^^int &)); +static_assert (std::is_nothrow_copy_assignable_v<int &>); +static_assert (!is_nothrow_copy_assignable_type (^^const int &)); +static_assert (!std::is_nothrow_copy_assignable_v<const int &>); +static_assert (is_nothrow_copy_assignable_type (^^int &&)); +static_assert (std::is_nothrow_copy_assignable_v<int &&>); +static_assert (!is_nothrow_copy_assignable_type (^^const int &&)); +static_assert (!std::is_nothrow_copy_assignable_v<const int &&>); +static_assert (!is_nothrow_copy_assignable_type (^^void)); +static_assert (!std::is_nothrow_copy_assignable_v<void>); +static_assert (!is_nothrow_copy_assignable_type (^^int() const &)); +static_assert (!std::is_nothrow_copy_assignable_v<int() const &>);
