On Wed, Jun 24, 2026 at 10:41:54AM -0400, Jason Merrill wrote:
> On 6/23/26 6:53 PM, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/16?
> > 
> > -- >8 --
> > 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.
> 
> tsubst uses tf_ignore_bad_quals to avoid this problem with template argument
> substitution.  Maybe build_stub_type should use it as well?

Sounds good too.  I would like to keep the new helper to hide all those
cp_type_quals (t) | TYPE_QUAL_CONST.

dg.exp passed, full testing running.  OK for trunk/16 once it passes?

-- >8 --
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.
---
 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(-)
 create mode 100644 gcc/testsuite/g++.dg/reflect/type_trait15.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 132139f9d52..76ed1e59dec 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 c511ea9a064..4c432efb56a 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 bf1c41772d3..3d6d24eb23a 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 d9a0583b8b8..dc885e47c01 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 00000000000..f0ce2e41e0b
--- /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 &>);

base-commit: 6ac3a6c8aa22dc78f68eb3c99e2c600ca3e57904
-- 
2.54.0

Reply via email to