[gcc r16-9246] c++/reflection: check type traits preconditions [PR125901]

Marek Polacek via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:46f8550df01af14d2b60321ab11b855284310a1b

commit r16-9246-g46f8550df01af14d2b60321ab11b855284310a1b
Author: Marek Polacek <[email protected]>
Date:   Fri Jun 26 11:32:47 2026 -0400

    c++/reflection: check type traits preconditions [PR125901]
    
    This PR laments that our meta::is_trivially_copyable_type gives the wrong
    result when its argument is a reflection of an uninstantiated template,
    because its TYPE_HAS_* flags haven't been properly set.  They are set in
    finish_struct_1, called while instantiating the template.
    
    This led to a much broader problem: we're not checking Preconditions in
    [meta.unary.prop] and Comments in [meta.rel].  Jason pointed out to me
    that check_trait_type and its KIND parameter already does this kind of
    checking; our Reflection code just wasn't using it fully.  With this patch
    we give an error when finish_trait_expr returns an error node.
    finish_trait_expr and check_trait_type need a complain parameter.  Many
    metafuncions weren't using the finish_trait_expr mechanism as they
    should.  The rest of the patch is adding the extra arguments.
    
    I found PR126073, XFAILd for now.
    
            PR c++/125901
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (finish_trait_expr): Adjust declaration.
            * reflect.cc (eval_constant_of): Adjust the call to
            eval_is_array_type.
            (eval_reflect_object): Adjust the call to eval_is_object_type.
            (eval_type_trait): Emit a diagnostic when finish_trait_expr
            returns error_mark_node and set *non_constant_p.  Adjust the
            call to eval_type_trait.
            (eval_is_array_type): Adjust the call to eval_type_trait.
            (eval_is_pointer_type): Likewise.
            (eval_is_member_object_pointer_type): Likewise.
            (eval_is_member_function_pointer_type): Likewise.
            (eval_is_enum_type): Likewise.
            (eval_is_union_type): Likewise.
            (eval_is_class_type): Likewise.
            (eval_is_reference_type): Likewise.
            (eval_is_member_pointer_type): Likewise.
            (eval_is_object_type): Likewise.
            (eval_is_trivially_copyable_type): Use eval_type_trait.
            (eval_is_standard_layout_type): Likewise.
            (eval_is_empty_type): Adjust the call to eval_type_trait.
            (eval_is_polymorphic_type): Likewise.
            (eval_is_abstract_type): Use eval_type_trait.
            (eval_is_final_type): Adjust the call to eval_type_trait.
            (eval_is_aggregate_type): Use eval_type_trait.
            (eval_is_structural_type): Adjust the call to eval_type_trait.
            (eval_is_bounded_array_type): Likewise.
            (eval_is_constructible_type): Use eval_type_trait.
            (eval_is_default_constructible_type): Likewise.
            (eval_is_copy_constructible_type): Likewise.
            (eval_is_move_constructible_type): Likewise.
            (eval_is_assignable_type): Adjust the call to eval_type_trait.
            (eval_is_copy_assignable_type): Use eval_type_trait.
            (eval_is_move_assignable_type): Use eval_type_trait.
            (eval_is_destructible_type): Adjust the call to eval_type_trait.
            (eval_is_trivially_constructible_type): Use eval_type_trait.
            (eval_is_trivially_default_constructible_type): Likewise.
            (eval_is_trivially_copy_constructible_type): Likewise.
            (eval_is_trivially_move_constructible_type): Likewise.
            (eval_is_trivially_assignable_type): Adjust the call to
            eval_type_trait.
            (eval_is_trivially_copy_assignable_type): Use eval_type_trait.
            (eval_is_trivially_move_assignable_type): Likewise.
            (eval_is_trivially_destructible_type): Adjust the call to
            eval_type_trait.
            (eval_is_nothrow_constructible_type): Use eval_type_trait.
            (eval_is_nothrow_default_constructible_type): Likewise.
            (eval_is_nothrow_copy_constructible_type): Likewise.
            (eval_is_nothrow_move_constructible_type): Likewise.
            (eval_is_nothrow_assignable_type): Adjust the call to
            eval_type_trait.
            (eval_is_nothrow_copy_assignable_type): Use eval_type_trait.
            (eval_is_nothrow_move_assignable_type): Likewise.
            (eval_is_nothrow_destructible_type): Adjust the call to
            eval_type_trait.
            (eval_is_implicit_lifetime_type): Use eval_type_trait.
            (eval_has_virtual_destructor): Likewise.
            (eval_has_unique_object_representations): Likewise.
            (eval_reference_constructs_from_temporary): Adjust the call to
            eval_type_trait.
            (eval_reference_converts_from_temporary): Likewise.
            (eval_extent): Adjust the call to eval_is_bounded_array_type.
            Check != boolean_true_node rather than == boolean_false_node when
            checking eval_is_bounded_array_type.
            (eval_is_same_type): Adjust the call to eval_type_trait.
            (eval_is_base_of_type): Likewise.
            (eval_is_virtual_base_of_type): Likewise.
            (eval_is_convertible_type): Likewise.
            (eval_is_nothrow_convertible_type): Likewise.
            (eval_is_layout_compatible_type): Likewise.
            (eval_is_pointer_interconvertible_base_of_type): Likewise.
            (eval_is_invocable_type): Use eval_type_trait.
            (eval_is_nothrow_invocable_type): Likewise.
            (eval_data_member_spec): Adjust the calls to eval_is_array_type
            and eval_is_object_type.  Check != boolean_true_node rather than
            == boolean_false_node when checking eval_is_object_type.
            (eval_extract): Adjust the call to eval_is_reference_type.
            (process_metafunction): Adjust the calls to various
            metafunctions.
            * semantics.cc (check_trait_type): Remove a default argument.
            Add a complain parameter.  If not emitting error messages, always
            return false for incomplete types.
            (finish_trait_expr): Add a complain parameter.  Use it.  Call
            complete_type_or_maybe_complain instead of
            complete_type_or_else.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/reflect/type_trait16.C: New test.
            * g++.dg/reflect/type_trait17.C: New test.
            * g++.dg/reflect/type_trait18.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>
    (cherry picked from commit 0114024e4f9c167083607bb23e25aefa6ee14411)

Diff:
---
 gcc/cp/cp-tree.h                            |   4 +-
 gcc/cp/reflect.cc                           | 695 +++++++++++++++++-----------
 gcc/cp/semantics.cc                         |  43 +-
 gcc/testsuite/g++.dg/reflect/type_trait16.C |  37 ++
 gcc/testsuite/g++.dg/reflect/type_trait17.C | 315 +++++++++++++
 gcc/testsuite/g++.dg/reflect/type_trait18.C | 126 +++++
 6 files changed, 933 insertions(+), 287 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d0372884f6e5..fb0965872661 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8662,7 +8662,9 @@ extern bool pointer_interconvertible_base_of_p	(tree, tree, bool = false);
 extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, int, tree *);
 extern tree fold_builtin_is_string_literal	(location_t, int, tree *);
 extern tree finish_structured_binding_size	(location_t, tree, tsubst_flags_t);
-extern tree finish_trait_expr			(location_t, enum cp_trait_kind, tree, tree);
+extern tree finish_trait_expr			(location_t, enum cp_trait_kind,
+						 tree, tree,
+						 tsubst_flags_t = tf_warning_or_error);
 extern tree finish_trait_type			(enum cp_trait_kind, tree, tree, tsubst_flags_t);
 extern tree build_lambda_expr                   (void);
 extern tree build_lambda_object			(tree);
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 74b3a8e26d47..cc26ede2e443 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -33,10 +33,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "metafns.h"
 
 static tree eval_is_function_type (tree);
-static tree eval_is_object_type (location_t, tree);
+static tree eval_is_object_type (location_t, const constexpr_ctx *, tree,
+				 bool *, tree);
 static tree eval_reflect_constant (location_t, const constexpr_ctx *, tree,
 				   tree, bool *, tree *, tree);
-static tree eval_is_array_type (location_t, tree);
+static tree eval_is_array_type (location_t, const constexpr_ctx *, tree,
+				bool *, tree);
 static tree eval_reflect_constant_array (location_t, const constexpr_ctx *,
 					 tree, bool *, bool *, tree *, tree);
 static tree eval_reflect_function (location_t, const constexpr_ctx *, tree,
@@ -2794,7 +2796,8 @@ eval_constant_of (location_t loc, const constexpr_ctx *ctx, tree r,
 
   if (eval_is_annotation (r, kind) == boolean_true_node)
     r = tree_strip_any_location_wrapper (TREE_VALUE (TREE_VALUE (r)));
-  else if (eval_is_array_type (loc, type) == boolean_true_node)
+  else if (eval_is_array_type (loc, ctx, type, non_constant_p, fun)
+	   == boolean_true_node)
     {
       const tsubst_flags_t complain = complain_flags (ctx);
       /* Create a call to reflect_constant_array so that we can simply
@@ -4071,7 +4074,8 @@ eval_reflect_object (location_t loc, const constexpr_ctx *ctx, tree type,
 		     tree expr, bool *non_constant_p, tree *jump_target,
 		     tree fun)
 {
-  if (eval_is_object_type (loc, type) != boolean_true_node)
+  if (eval_is_object_type (loc, ctx, type, non_constant_p, fun)
+      != boolean_true_node)
     {
       error_at (loc, "%qT must be an object type", TREE_TYPE (type));
       return error_mark_node;
@@ -4127,10 +4131,47 @@ eval_reflect_function (location_t loc, const constexpr_ctx *ctx, tree type,
    arguments to the trait.  */
 
 static tree
-eval_type_trait (location_t loc, tree type1, tree type2, cp_trait_kind kind)
+eval_type_trait (location_t loc, const constexpr_ctx *ctx, tree type1,
+		 tree type2, cp_trait_kind kind, bool *non_constant_p,
+		 tree fun)
 {
-  tree r = finish_trait_expr (loc, kind, type1, type2);
-  gcc_checking_assert (r != error_mark_node);
+  tree r = finish_trait_expr (loc, kind, type1, type2, tf_none);
+  if (r == error_mark_node)
+    {
+      /* [meta.reflection.traits]/3.2: Otherwise, if the instantiation of S
+	 would result in undefined behavior due to dependence on an incomplete
+	 type, then the call is not a constant subexpression.  */
+      if (!cxx_constexpr_quiet_p (ctx))
+	{
+	  auto_diagnostic_group d;
+	  error_at (loc, "type trait %qE preconditions not satisfied", fun);
+	  /* See if we can figure out which argument was incomplete.  */
+	  tree inc = NULL_TREE;
+	  if (!COMPLETE_OR_VOID_TYPE_P (type1)
+	      && !array_of_unknown_bound_p (type1))
+	    inc = type1;
+	  else if (type2)
+	    {
+	      if (TYPE_P (type2)
+		  && !COMPLETE_OR_VOID_TYPE_P (type2)
+		  && !array_of_unknown_bound_p (type2))
+		inc = type2;
+	      else if (TREE_CODE (type2) == TREE_VEC)
+		for (tree t : tree_vec_range (type2))
+		  if (!COMPLETE_OR_VOID_TYPE_P (t)
+		      && !array_of_unknown_bound_p (t))
+		    {
+		      inc = t;
+		      break;
+		    }
+	    }
+	  if (inc)
+	    cxx_incomplete_type_diagnostic (loc, NULL_TREE, inc,
+					    diagnostics::kind::note);
+	}
+      *non_constant_p = true;
+      return NULL_TREE;
+    }
   STRIP_ANY_LOCATION_WRAPPER (r);
   return r;
 }
@@ -4138,9 +4179,11 @@ eval_type_trait (location_t loc, tree type1, tree type2, cp_trait_kind kind)
 /* Like above, but for type traits that take only one type.  */
 
 static tree
-eval_type_trait (location_t loc, tree type, cp_trait_kind kind)
+eval_type_trait (location_t loc, const constexpr_ctx *ctx, tree type,
+		 cp_trait_kind kind, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, NULL_TREE, kind);
+  return eval_type_trait (loc, ctx, type, NULL_TREE, kind, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_function_type.  */
@@ -4201,17 +4244,21 @@ eval_is_floating_point_type (tree type)
 /* Process std::meta::is_array_type.  */
 
 static tree
-eval_is_array_type (location_t loc, tree type)
+eval_is_array_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		    bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_ARRAY);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_ARRAY, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_pointer_type.  */
 
 static tree
-eval_is_pointer_type (location_t loc, tree type)
+eval_is_pointer_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		      bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_POINTER);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_POINTER, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_lvalue_reference_type.  */
@@ -4239,41 +4286,51 @@ eval_is_rvalue_reference_type (tree type)
 /* Process std::meta::is_member_object_pointer_type.  */
 
 static tree
-eval_is_member_object_pointer_type (location_t loc, tree type)
+eval_is_member_object_pointer_type (location_t loc, const constexpr_ctx *ctx,
+				    tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_MEMBER_OBJECT_POINTER);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_MEMBER_OBJECT_POINTER,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_member_function_pointer_type.  */
 
 static tree
-eval_is_member_function_pointer_type (location_t loc, tree type)
+eval_is_member_function_pointer_type (location_t loc, const constexpr_ctx *ctx,
+				      tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_MEMBER_FUNCTION_POINTER);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_MEMBER_FUNCTION_POINTER,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_enum_type.  */
 
 static tree
-eval_is_enum_type (location_t loc, tree type)
+eval_is_enum_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		   bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_ENUM);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_ENUM, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_union_type.  */
 
 static tree
-eval_is_union_type (location_t loc, tree type)
+eval_is_union_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		    bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_UNION);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_UNION, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_class_type.  */
 
 static tree
-eval_is_class_type (location_t loc, tree type)
+eval_is_class_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		    bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_CLASS);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_CLASS, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_reflection_type.  */
@@ -4290,9 +4347,11 @@ eval_is_reflection_type (tree type)
 /* Process std::meta::is_reference_type.  */
 
 static tree
-eval_is_reference_type (location_t loc, tree type)
+eval_is_reference_type (location_t loc, const constexpr_ctx *ctx, tree type,
+			bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_REFERENCE);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_REFERENCE, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_arithmetic_type.  */
@@ -4309,9 +4368,11 @@ eval_is_arithmetic_type (tree type)
 /* Process std::meta::is_object_type.  */
 
 static tree
-eval_is_object_type (location_t loc, tree type)
+eval_is_object_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		     bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_OBJECT);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_OBJECT, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_scalar_type.  */
@@ -4353,9 +4414,11 @@ eval_is_compound_type (tree type)
 /* Process std::meta::is_member_pointer_type.  */
 
 static tree
-eval_is_member_pointer_type (location_t loc, tree type)
+eval_is_member_pointer_type (location_t loc, const constexpr_ctx *ctx,
+			     tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_MEMBER_POINTER);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_MEMBER_POINTER,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_const_type.  */
@@ -4383,58 +4446,60 @@ eval_is_volatile_type (tree type)
 /* Process std::meta::is_trivially_copyable_type.  */
 
 static tree
-eval_is_trivially_copyable_type (tree type)
+eval_is_trivially_copyable_type (location_t loc, const constexpr_ctx *ctx,
+				 tree type, bool *non_constant_p, tree fun)
 {
-  if (trivially_copyable_p (type))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, CPTK_IS_TRIVIALLY_COPYABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_standard_layout_type.  */
 
 static tree
-eval_is_standard_layout_type (tree type)
+eval_is_standard_layout_type (location_t loc, const constexpr_ctx *ctx,
+			      tree type, bool *non_constant_p, tree fun)
 {
-  if (std_layout_type_p (type))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, CPTK_IS_STD_LAYOUT, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_empty_type.  */
 
 static tree
-eval_is_empty_type (location_t loc, tree type)
+eval_is_empty_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		    bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_EMPTY);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_EMPTY, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_polymorphic_type.  */
 
 static tree
-eval_is_polymorphic_type (location_t loc, tree type)
+eval_is_polymorphic_type (location_t loc, const constexpr_ctx *ctx,
+			  tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_POLYMORPHIC);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_POLYMORPHIC, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_abstract_type.  */
 
 static tree
-eval_is_abstract_type (tree type)
+eval_is_abstract_type (location_t loc, const constexpr_ctx *ctx,
+		       tree type, bool *non_constant_p, tree fun)
 {
-  if (ABSTRACT_CLASS_TYPE_P (type))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, CPTK_IS_ABSTRACT, non_constant_p,
+			  fun);
 }
 
 /* Process std::meta::is_final_type.  */
 
 static tree
-eval_is_final_type (location_t loc, tree type)
+eval_is_final_type (location_t loc, const constexpr_ctx *ctx, tree type,
+		    bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_FINAL);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_FINAL, non_constant_p, fun);
 }
 
 /* Process std::meta::is_final.
@@ -4464,20 +4529,21 @@ eval_is_final (tree r)
 /* Process std::meta::is_aggregate_type.  */
 
 static tree
-eval_is_aggregate_type (tree type)
+eval_is_aggregate_type (location_t loc, const constexpr_ctx *ctx,
+			tree type, bool *non_constant_p, tree fun)
 {
-  if (CP_AGGREGATE_TYPE_P (type))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, CPTK_IS_AGGREGATE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_structural_type.  */
 
 static tree
-eval_is_structural_type (location_t loc, tree type)
+eval_is_structural_type (location_t loc, const constexpr_ctx *ctx,
+			 tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_STRUCTURAL);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_STRUCTURAL,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_signed_type.  */
@@ -4505,9 +4571,11 @@ eval_is_unsigned_type (tree type)
 /* Process std::meta::is_bounded_array_type.  */
 
 static tree
-eval_is_bounded_array_type (location_t loc, tree type)
+eval_is_bounded_array_type (location_t loc, const constexpr_ctx *ctx,
+			    tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_BOUNDED_ARRAY);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_BOUNDED_ARRAY,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_unbounded_array_type.  */
@@ -4535,328 +4603,361 @@ eval_is_scoped_enum_type (tree type)
 /* Process std::meta::is_constructible_type.  */
 
 static tree
-eval_is_constructible_type (tree type, tree tvec)
+eval_is_constructible_type (location_t loc, const constexpr_ctx *ctx,
+			    tree type, tree tvec, bool *non_constant_p,
+			    tree fun)
 {
-  if (is_xible (INIT_EXPR, type, tvec))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, tvec, CPTK_IS_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_default_constructible_type.  */
 
 static tree
-eval_is_default_constructible_type (tree type)
+eval_is_default_constructible_type (location_t loc, const constexpr_ctx *ctx,
+				    tree type, bool *non_constant_p, tree fun)
 {
-  if (is_xible (INIT_EXPR, type, make_tree_vec (0)))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, make_tree_vec (0),
+			  CPTK_IS_CONSTRUCTIBLE, non_constant_p, fun);
 }
 
 /* Process std::meta::is_copy_constructible_type.  */
 
 static tree
-eval_is_copy_constructible_type (tree type)
+eval_is_copy_constructible_type (location_t loc, const constexpr_ctx *ctx,
+				 tree type, bool *non_constant_p, tree fun)
 {
   tree arg = make_tree_vec (1);
   TREE_VEC_ELT (arg, 0)
     = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false);
-  if (is_xible (INIT_EXPR, type, arg))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, arg, CPTK_IS_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_move_constructible_type.  */
 
 static tree
-eval_is_move_constructible_type (tree type)
+eval_is_move_constructible_type (location_t loc, const constexpr_ctx *ctx,
+				 tree type, bool *non_constant_p, tree fun)
 {
   tree arg = make_tree_vec (1);
   TREE_VEC_ELT (arg, 0) = cp_build_reference_type (type, /*rval=*/true);
-  if (is_xible (INIT_EXPR, type, arg))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, arg, CPTK_IS_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_assignable_type.  */
 
 static tree
-eval_is_assignable_type (location_t loc, tree type1, tree type2)
+eval_is_assignable_type (location_t loc, const constexpr_ctx *ctx,
+			 tree type1, tree type2, bool *non_constant_p,
+			 tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_ASSIGNABLE);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_copy_assignable_type.  */
 
 static tree
-eval_is_copy_assignable_type (tree type)
+eval_is_copy_assignable_type (location_t loc, const constexpr_ctx *ctx,
+			      tree type, bool *non_constant_p, tree fun)
 {
   tree type1 = cp_build_reference_type (type, /*rval=*/false);
   tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST,
 				false);
-  if (is_xible (MODIFY_EXPR, type1, type2))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_move_assignable_type.  */
 
 static tree
-eval_is_move_assignable_type (tree type)
+eval_is_move_assignable_type (location_t loc, const constexpr_ctx *ctx,
+			      tree type, bool *non_constant_p, tree fun)
 {
   tree type1 = cp_build_reference_type (type, /*rval=*/false);
   tree type2 = cp_build_reference_type (type, /*rval=*/true);
-  if (is_xible (MODIFY_EXPR, type1, type2))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_destructible_type.  */
 
 static tree
-eval_is_destructible_type (location_t loc, tree type)
+eval_is_destructible_type (location_t loc, const constexpr_ctx *ctx,
+			   tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_DESTRUCTIBLE);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_DESTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_constructible_type.  */
 
 static tree
-eval_is_trivially_constructible_type (tree type, tree tvec)
+eval_is_trivially_constructible_type (location_t loc, const constexpr_ctx *ctx,
+				      tree type, tree tvec,
+				      bool *non_constant_p, tree fun)
 {
-  if (is_trivially_xible (INIT_EXPR, type, tvec))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, tvec,
+			  CPTK_IS_TRIVIALLY_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_default_constructible_type.  */
 
 static tree
-eval_is_trivially_default_constructible_type (tree type)
+eval_is_trivially_default_constructible_type (location_t loc,
+					      const constexpr_ctx *ctx,
+					      tree type, bool *non_constant_p,
+					      tree fun)
 {
-  if (is_trivially_xible (INIT_EXPR, type, make_tree_vec (0)))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, make_tree_vec (0),
+			  CPTK_IS_TRIVIALLY_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_copy_constructible_type.  */
 
 static tree
-eval_is_trivially_copy_constructible_type (tree type)
+eval_is_trivially_copy_constructible_type (location_t loc,
+					   const constexpr_ctx *ctx,
+					   tree type, bool *non_constant_p,
+					   tree fun)
 {
   tree arg = make_tree_vec (1);
   TREE_VEC_ELT (arg, 0)
     = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false);
-  if (is_trivially_xible (INIT_EXPR, type, arg))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, arg,
+			  CPTK_IS_TRIVIALLY_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_move_constructible_type.  */
 
 static tree
-eval_is_trivially_move_constructible_type (tree type)
+eval_is_trivially_move_constructible_type (location_t loc,
+					   const constexpr_ctx *ctx,
+					   tree type, bool *non_constant_p,
+					   tree fun)
 {
   tree arg = make_tree_vec (1);
   TREE_VEC_ELT (arg, 0) = cp_build_reference_type (type, /*rval=*/true);
-  if (is_trivially_xible (INIT_EXPR, type, arg))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, arg,
+			  CPTK_IS_TRIVIALLY_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_assignable_type.  */
 
 static tree
-eval_is_trivially_assignable_type (location_t loc, tree type1, tree type2)
+eval_is_trivially_assignable_type (location_t loc, const constexpr_ctx *ctx,
+				   tree type1, tree type2,
+				   bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_TRIVIALLY_ASSIGNABLE);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_TRIVIALLY_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_copy_assignable_type.  */
 
 static tree
-eval_is_trivially_copy_assignable_type (tree type)
+eval_is_trivially_copy_assignable_type (location_t loc,
+					const constexpr_ctx *ctx,
+					tree type, bool *non_constant_p,
+					tree fun)
 {
   tree type1 = cp_build_reference_type (type, /*rval=*/false);
   tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST,
 				false);
-  if (is_trivially_xible (MODIFY_EXPR, type1, type2))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_TRIVIALLY_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_move_assignable_type.  */
 
 static tree
-eval_is_trivially_move_assignable_type (tree type)
+eval_is_trivially_move_assignable_type (location_t loc,
+					const constexpr_ctx *ctx,
+					tree type, bool *non_constant_p,
+					tree fun)
 {
   tree type1 = cp_build_reference_type (type, /*rval=*/false);
   tree type2 = cp_build_reference_type (type, /*rval=*/true);
-  if (is_trivially_xible (MODIFY_EXPR, type1, type2))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_TRIVIALLY_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_trivially_destructible_type.  */
 
 static tree
-eval_is_trivially_destructible_type (location_t loc, tree type)
+eval_is_trivially_destructible_type (location_t loc, const constexpr_ctx *ctx,
+				     tree type, bool *non_constant_p,
+				     tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_TRIVIALLY_DESTRUCTIBLE);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_TRIVIALLY_DESTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_constructible_type.  */
 
 static tree
-eval_is_nothrow_constructible_type (tree type, tree tvec)
+eval_is_nothrow_constructible_type (location_t loc, const constexpr_ctx *ctx,
+				    tree type, tree tvec, bool *non_constant_p,
+				    tree fun)
 {
-  if (is_nothrow_xible (INIT_EXPR, type, tvec))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, tvec, CPTK_IS_NOTHROW_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_default_constructible_type.  */
 
 static tree
-eval_is_nothrow_default_constructible_type (tree type)
+eval_is_nothrow_default_constructible_type (location_t loc,
+					    const constexpr_ctx *ctx,
+					    tree type, bool *non_constant_p,
+					    tree fun)
 {
-  if (is_nothrow_xible (INIT_EXPR, type, make_tree_vec (0)))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, make_tree_vec (0),
+			  CPTK_IS_NOTHROW_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_copy_constructible_type.  */
 
 static tree
-eval_is_nothrow_copy_constructible_type (tree type)
+eval_is_nothrow_copy_constructible_type (location_t loc,
+					 const constexpr_ctx *ctx,
+					 tree type, bool *non_constant_p,
+					 tree fun)
 {
   tree arg = make_tree_vec (1);
   TREE_VEC_ELT (arg, 0)
     = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST, false);
-  if (is_nothrow_xible (INIT_EXPR, type, arg))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, arg,
+			  CPTK_IS_NOTHROW_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_move_constructible_type.  */
 
 static tree
-eval_is_nothrow_move_constructible_type (tree type)
+eval_is_nothrow_move_constructible_type (location_t loc,
+					 const constexpr_ctx *ctx,
+					 tree type, bool *non_constant_p,
+					 tree fun)
 {
   tree arg = make_tree_vec (1);
   TREE_VEC_ELT (arg, 0) = cp_build_reference_type (type, /*rval=*/true);
-  if (is_nothrow_xible (INIT_EXPR, type, arg))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, arg,
+			  CPTK_IS_NOTHROW_CONSTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_assignable_type.  */
 
 static tree
-eval_is_nothrow_assignable_type (location_t loc, tree type1, tree type2)
+eval_is_nothrow_assignable_type (location_t loc, const constexpr_ctx *ctx,
+				 tree type1, tree type2, bool *non_constant_p,
+				 tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_NOTHROW_ASSIGNABLE);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_NOTHROW_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_copy_assignable_type.  */
 
 static tree
-eval_is_nothrow_copy_assignable_type (tree type)
+eval_is_nothrow_copy_assignable_type (location_t loc, const constexpr_ctx *ctx,
+				      tree type, bool *non_constant_p, tree fun)
 {
   tree type1 = cp_build_reference_type (type, /*rval=*/false);
   tree type2 = build_stub_type (type, cp_type_quals (type) | TYPE_QUAL_CONST,
 				false);
-  if (is_nothrow_xible (MODIFY_EXPR, type1, type2))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_NOTHROW_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_move_assignable_type.  */
 
 static tree
-eval_is_nothrow_move_assignable_type (tree type)
+eval_is_nothrow_move_assignable_type (location_t loc, const constexpr_ctx *ctx,
+				      tree type, bool *non_constant_p, tree fun)
 {
   tree type1 = cp_build_reference_type (type, /*rval=*/false);
   tree type2 = cp_build_reference_type (type, /*rval=*/true);
-  if (is_nothrow_xible (MODIFY_EXPR, type1, type2))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_NOTHROW_ASSIGNABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_destructible_type.  */
 
 static tree
-eval_is_nothrow_destructible_type (location_t loc, tree type)
+eval_is_nothrow_destructible_type (location_t loc, const constexpr_ctx *ctx,
+				   tree type, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type, CPTK_IS_NOTHROW_DESTRUCTIBLE);
+  return eval_type_trait (loc, ctx, type, CPTK_IS_NOTHROW_DESTRUCTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_implicit_lifetime_type.  */
 
 static tree
-eval_is_implicit_lifetime_type (tree type)
+eval_is_implicit_lifetime_type (location_t loc, const constexpr_ctx *ctx,
+				tree type, bool *non_constant_p, tree fun)
 {
-  if (implicit_lifetime_type_p (type))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, CPTK_IS_IMPLICIT_LIFETIME,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::has_virtual_destructor.  */
 
 static tree
-eval_has_virtual_destructor (tree type)
+eval_has_virtual_destructor (location_t loc, const constexpr_ctx *ctx,
+			     tree type, bool *non_constant_p, tree fun)
 {
-  if (type_has_virtual_destructor (type))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, CPTK_HAS_VIRTUAL_DESTRUCTOR,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::has_unique_object_representations.  */
 
 static tree
-eval_has_unique_object_representations (tree type)
+eval_has_unique_object_representations (location_t loc,
+					const constexpr_ctx *ctx,
+					tree type, bool *non_constant_p,
+					tree fun)
 {
-  if (type_has_unique_obj_representations (type))
-    return boolean_true_node;
-  else
-    return boolean_false_node;
+  return eval_type_trait (loc, ctx, type, CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::reference_constructs_from_temporary.  */
 
 static tree
-eval_reference_constructs_from_temporary (location_t loc, tree type1,
-					  tree type2)
+eval_reference_constructs_from_temporary (location_t loc,
+					  const constexpr_ctx *ctx,
+					  tree type1, tree type2,
+					  bool *non_constant_p,
+					  tree fun)
 {
-  return eval_type_trait (loc, type1, type2,
-			  CPTK_REF_CONSTRUCTS_FROM_TEMPORARY);
+  return eval_type_trait (loc, ctx, type1, type2,
+			  CPTK_REF_CONSTRUCTS_FROM_TEMPORARY,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::reference_converts_from_temporary.  */
 
 static tree
-eval_reference_converts_from_temporary (location_t loc, tree type1, tree type2)
+eval_reference_converts_from_temporary (location_t loc,
+					const constexpr_ctx *ctx,
+					tree type1, tree type2,
+					bool *non_constant_p,
+					tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_REF_CONVERTS_FROM_TEMPORARY);
+  return eval_type_trait (loc, ctx, type1, type2,
+			  CPTK_REF_CONVERTS_FROM_TEMPORARY,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::rank.  */
@@ -4873,7 +4974,8 @@ eval_rank (tree type)
 /* Process std::meta::extent.  */
 
 static tree
-eval_extent (location_t loc, tree type, tree i)
+eval_extent (location_t loc, const constexpr_ctx *ctx, tree type, tree i,
+	     bool *non_constant_p, tree fun)
 {
   size_t rank = tree_to_uhwi (i);
   while (rank && TREE_CODE (type) == ARRAY_TYPE)
@@ -4884,7 +4986,8 @@ eval_extent (location_t loc, tree type, tree i)
   tree r;
   if (rank
       || TREE_CODE (type) != ARRAY_TYPE
-      || eval_is_bounded_array_type (loc, type) == boolean_false_node)
+      || eval_is_bounded_array_type (loc, ctx, type, non_constant_p,
+				     fun) != boolean_true_node)
     r = size_zero_node;
   else
     r = size_binop (PLUS_EXPR, TYPE_MAX_VALUE (TYPE_DOMAIN (type)),
@@ -4896,69 +4999,90 @@ eval_extent (location_t loc, tree type, tree i)
 /* Process std::meta::is_same_type.  */
 
 static tree
-eval_is_same_type (location_t loc, tree type1, tree type2)
+eval_is_same_type (location_t loc, const constexpr_ctx *ctx, tree type1,
+		   tree type2, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_SAME);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_SAME,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_base_of_type.  */
 
 static tree
-eval_is_base_of_type (location_t loc, tree type1, tree type2)
+eval_is_base_of_type (location_t loc, const constexpr_ctx *ctx,
+		      tree type1, tree type2, bool *non_constant_p, tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_BASE_OF);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_BASE_OF,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_virtual_base_of_type.  */
 
 static tree
-eval_is_virtual_base_of_type (location_t loc, tree type1, tree type2)
+eval_is_virtual_base_of_type (location_t loc, const constexpr_ctx *ctx,
+			      tree type1, tree type2, bool *non_constant_p,
+			      tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_VIRTUAL_BASE_OF);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_VIRTUAL_BASE_OF,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_convertible_type.  */
 
 static tree
-eval_is_convertible_type (location_t loc, tree type1, tree type2)
+eval_is_convertible_type (location_t loc, const constexpr_ctx *ctx,
+			  tree type1, tree type2, bool *non_constant_p,
+			  tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_CONVERTIBLE);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_CONVERTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_nothrow_convertible_type.  */
 
 static tree
-eval_is_nothrow_convertible_type (location_t loc, tree type1, tree type2)
+eval_is_nothrow_convertible_type (location_t loc, const constexpr_ctx *ctx,
+				  tree type1, tree type2, bool *non_constant_p,
+				  tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_NOTHROW_CONVERTIBLE);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_NOTHROW_CONVERTIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_layout_compatible_type.  */
 
 static tree
-eval_is_layout_compatible_type (location_t loc, tree type1, tree type2)
+eval_is_layout_compatible_type (location_t loc, const constexpr_ctx *ctx,
+				tree type1, tree type2, bool *non_constant_p,
+				tree fun)
 {
-  return eval_type_trait (loc, type1, type2, CPTK_IS_LAYOUT_COMPATIBLE);
+  return eval_type_trait (loc, ctx, type1, type2, CPTK_IS_LAYOUT_COMPATIBLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_pointer_interconvertible_base_of_type.  */
 
 static tree
 eval_is_pointer_interconvertible_base_of_type (location_t loc,
-					       tree type1, tree type2)
+					       const constexpr_ctx *ctx,
+					       tree type1, tree type2,
+					       bool *non_constant_p,
+					       tree fun)
 {
-  return eval_type_trait (loc, type1, type2,
-			  CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF);
+  return eval_type_trait (loc, ctx, type1, type2,
+			  CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_invocable_type.  */
 
 static tree
-eval_is_invocable_type (location_t loc, tree type, tree tvec)
+eval_is_invocable_type (location_t loc, const constexpr_ctx *ctx,
+			tree type, tree tvec, bool *non_constant_p,
+			tree fun)
 {
-  tree r = finish_trait_expr (loc, CPTK_IS_INVOCABLE, type, tvec);
-  STRIP_ANY_LOCATION_WRAPPER (r);
-  return r;
+  return eval_type_trait (loc, ctx, type, tvec, CPTK_IS_INVOCABLE,
+			  non_constant_p, fun);
 }
 
 /* Helper for various eval_* type trait functions which can't use builtin
@@ -5031,11 +5155,12 @@ eval_is_invocable_r_type (location_t loc, const constexpr_ctx *ctx,
 /* Process std::meta::is_nothrow_invocable_type.  */
 
 static tree
-eval_is_nothrow_invocable_type (location_t loc, tree type, tree tvec)
+eval_is_nothrow_invocable_type (location_t loc, const constexpr_ctx *ctx,
+				tree type, tree tvec, bool *non_constant_p,
+				tree fun)
 {
-  tree r = finish_trait_expr (loc, CPTK_IS_NOTHROW_INVOCABLE, type, tvec);
-  STRIP_ANY_LOCATION_WRAPPER (r);
-  return r;
+  return eval_type_trait (loc, ctx, type, tvec, CPTK_IS_NOTHROW_INVOCABLE,
+			  non_constant_p, fun);
 }
 
 /* Process std::meta::is_{,nothrow_}swappable_with_type.  */
@@ -6079,8 +6204,10 @@ eval_data_member_spec (location_t loc, const constexpr_ctx *ctx,
 	return throw_exception (loc, ctx, "reflection does not have a type",
 				fun, non_constant_p, jump_target);
       tree type = type_of (r, kind);
-      if (eval_is_array_type (loc, type) == boolean_true_node
-	  || eval_is_object_type (loc, type) == boolean_false_node)
+      if ((eval_is_array_type (loc, ctx, type, non_constant_p, fun)
+	   == boolean_true_node)
+	  || (eval_is_object_type (loc, ctx, type, non_constant_p, fun)
+	      != boolean_true_node))
 	return throw_exception (loc, ctx, "reflection does not have "
 					  "non-array object type",
 				fun, non_constant_p, jump_target);
@@ -7689,7 +7816,8 @@ eval_extract (location_t loc, const constexpr_ctx *ctx, tree type, tree r,
 	      reflect_kind kind, bool *non_constant_p, bool *overflow_p,
 	      tree *jump_target, tree fun)
 {
-  if (eval_is_reference_type (loc, type) == boolean_true_node)
+  if (eval_is_reference_type (loc, ctx, type, non_constant_p, fun)
+      == boolean_true_node)
     return extract_ref (loc, ctx, type, r, kind, non_constant_p, jump_target,
 			fun);
   type = cv_unqualified (type);
@@ -8263,85 +8391,90 @@ process_metafunction (const constexpr_ctx *ctx, tree fun, tree call,
     case METAFN_IS_FLOATING_POINT_TYPE:
       return eval_is_floating_point_type (h);
     case METAFN_IS_ARRAY_TYPE:
-      return eval_is_array_type (loc, h);
+      return eval_is_array_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_POINTER_TYPE:
-      return eval_is_pointer_type (loc, h);
+      return eval_is_pointer_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_LVALUE_REFERENCE_TYPE:
       return eval_is_lvalue_reference_type (h);
     case METAFN_IS_RVALUE_REFERENCE_TYPE:
       return eval_is_rvalue_reference_type (h);
     case METAFN_IS_MEMBER_OBJECT_POINTER_TYPE:
-      return eval_is_member_object_pointer_type (loc, h);
+      return eval_is_member_object_pointer_type (loc, ctx, h, non_constant_p,
+						 fun);
     case METAFN_IS_MEMBER_FUNCTION_POINTER_TYPE:
-      return eval_is_member_function_pointer_type (loc, h);
+      return eval_is_member_function_pointer_type (loc, ctx, h, non_constant_p,
+						   fun);
     case METAFN_IS_ENUM_TYPE:
-      return eval_is_enum_type (loc, h);
+      return eval_is_enum_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_UNION_TYPE:
-      return eval_is_union_type (loc, h);
+      return eval_is_union_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_CLASS_TYPE:
-      return eval_is_class_type (loc, h);
+      return eval_is_class_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_FUNCTION_TYPE:
       return eval_is_function_type (h);
     case METAFN_IS_REFLECTION_TYPE:
       return eval_is_reflection_type (h);
     case METAFN_IS_REFERENCE_TYPE:
-      return eval_is_reference_type (loc, h);
+      return eval_is_reference_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_ARITHMETIC_TYPE:
       return eval_is_arithmetic_type (h);
     case METAFN_IS_FUNDAMENTAL_TYPE:
       return eval_is_fundamental_type (h);
     case METAFN_IS_OBJECT_TYPE:
-      return eval_is_object_type (loc, h);
+      return eval_is_object_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_SCALAR_TYPE:
       return eval_is_scalar_type (h);
     case METAFN_IS_COMPOUND_TYPE:
       return eval_is_compound_type (h);
     case METAFN_IS_MEMBER_POINTER_TYPE:
-      return eval_is_member_pointer_type (loc, h);
+      return eval_is_member_pointer_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_CONST_TYPE:
       return eval_is_const_type (h);
     case METAFN_IS_VOLATILE_TYPE:
       return eval_is_volatile_type (h);
     case METAFN_IS_TRIVIALLY_COPYABLE_TYPE:
-      return eval_is_trivially_copyable_type (h);
+      return eval_is_trivially_copyable_type (loc, ctx, h, non_constant_p,
+					      fun);
     case METAFN_IS_STANDARD_LAYOUT_TYPE:
-      return eval_is_standard_layout_type (h);
+      return eval_is_standard_layout_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_EMPTY_TYPE:
-      return eval_is_empty_type (loc, h);
+      return eval_is_empty_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_POLYMORPHIC_TYPE:
-      return eval_is_polymorphic_type (loc, h);
+      return eval_is_polymorphic_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_ABSTRACT_TYPE:
-      return eval_is_abstract_type (h);
+      return eval_is_abstract_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_FINAL_TYPE:
-      return eval_is_final_type (loc, h);
+      return eval_is_final_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_AGGREGATE_TYPE:
-      return eval_is_aggregate_type (h);
+      return eval_is_aggregate_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_STRUCTURAL_TYPE:
-      return eval_is_structural_type (loc, h);
+      return eval_is_structural_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_SIGNED_TYPE:
       return eval_is_signed_type (h);
     case METAFN_IS_UNSIGNED_TYPE:
       return eval_is_unsigned_type (h);
     case METAFN_IS_BOUNDED_ARRAY_TYPE:
-      return eval_is_bounded_array_type (loc, h);
+      return eval_is_bounded_array_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_UNBOUNDED_ARRAY_TYPE:
       return eval_is_unbounded_array_type (h);
     case METAFN_IS_SCOPED_ENUM_TYPE:
       return eval_is_scoped_enum_type (h);
     case METAFN_IS_CONSTRUCTIBLE_TYPE:
-      return eval_is_constructible_type (h, hvec);
+      return eval_is_constructible_type (loc, ctx, h, hvec, non_constant_p,
+					 fun);
     case METAFN_IS_DEFAULT_CONSTRUCTIBLE_TYPE:
-      return eval_is_default_constructible_type (h);
+      return eval_is_default_constructible_type (loc, ctx, h, non_constant_p,
+						 fun);
     case METAFN_IS_COPY_CONSTRUCTIBLE_TYPE:
-      return eval_is_copy_constructible_type (h);
+      return eval_is_copy_constructible_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_MOVE_CONSTRUCTIBLE_TYPE:
-      return eval_is_move_constructible_type (h);
+      return eval_is_move_constructible_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_ASSIGNABLE_TYPE:
-      return eval_is_assignable_type (loc, h, h1);
+      return eval_is_assignable_type (loc, ctx, h, h1, non_constant_p, fun);
     case METAFN_IS_COPY_ASSIGNABLE_TYPE:
-      return eval_is_copy_assignable_type (h);
+      return eval_is_copy_assignable_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_MOVE_ASSIGNABLE_TYPE:
-      return eval_is_move_assignable_type (h);
+      return eval_is_move_assignable_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_SWAPPABLE_WITH_TYPE:
       return eval_is_swappable_with_type (loc, ctx, h, h1, call,
 					  non_constant_p, jump_target, fun,
@@ -8350,37 +8483,53 @@ process_metafunction (const constexpr_ctx *ctx, tree fun, tree call,
       return eval_is_swappable_type (loc, ctx, h, call, non_constant_p,
 				     jump_target, fun, "is_swappable");
     case METAFN_IS_DESTRUCTIBLE_TYPE:
-      return eval_is_destructible_type (loc, h);
+      return eval_is_destructible_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_IS_TRIVIALLY_CONSTRUCTIBLE_TYPE:
-      return eval_is_trivially_constructible_type (h, hvec);
+      return eval_is_trivially_constructible_type (loc, ctx, h, hvec,
+						   non_constant_p, fun);
     case METAFN_IS_TRIVIALLY_DEFAULT_CONSTRUCTIBLE_TYPE:
-      return eval_is_trivially_default_constructible_type (h);
+      return eval_is_trivially_default_constructible_type (loc, ctx, h,
+							   non_constant_p,
+							   fun);
     case METAFN_IS_TRIVIALLY_COPY_CONSTRUCTIBLE_TYPE:
-      return eval_is_trivially_copy_constructible_type (h);
+      return eval_is_trivially_copy_constructible_type (loc, ctx, h,
+							non_constant_p, fun);
     case METAFN_IS_TRIVIALLY_MOVE_CONSTRUCTIBLE_TYPE:
-      return eval_is_trivially_move_constructible_type (h);
+      return eval_is_trivially_move_constructible_type (loc, ctx, h,
+							non_constant_p, fun);
     case METAFN_IS_TRIVIALLY_ASSIGNABLE_TYPE:
-      return eval_is_trivially_assignable_type (loc, h, h1);
+      return eval_is_trivially_assignable_type (loc, ctx, h, h1,
+						non_constant_p, fun);
     case METAFN_IS_TRIVIALLY_COPY_ASSIGNABLE_TYPE:
-      return eval_is_trivially_copy_assignable_type (h);
+      return eval_is_trivially_copy_assignable_type (loc, ctx, h,
+						     non_constant_p, fun);
     case METAFN_IS_TRIVIALLY_MOVE_ASSIGNABLE_TYPE:
-      return eval_is_trivially_move_assignable_type (h);
+      return eval_is_trivially_move_assignable_type (loc, ctx, h,
+						     non_constant_p, fun);
     case METAFN_IS_TRIVIALLY_DESTRUCTIBLE_TYPE:
-      return eval_is_trivially_destructible_type (loc, h);
+      return eval_is_trivially_destructible_type (loc, ctx, h, non_constant_p,
+						  fun);
     case METAFN_IS_NOTHROW_CONSTRUCTIBLE_TYPE:
-      return eval_is_nothrow_constructible_type (h, hvec);
+      return eval_is_nothrow_constructible_type (loc, ctx, h, hvec,
+						 non_constant_p, fun);
     case METAFN_IS_NOTHROW_DEFAULT_CONSTRUCTIBLE_TYPE:
-      return eval_is_nothrow_default_constructible_type (h);
+      return eval_is_nothrow_default_constructible_type (loc, ctx, h,
+							 non_constant_p, fun);
     case METAFN_IS_NOTHROW_COPY_CONSTRUCTIBLE_TYPE:
-      return eval_is_nothrow_copy_constructible_type (h);
+      return eval_is_nothrow_copy_constructible_type (loc, ctx, h,
+						      non_constant_p, fun);
     case METAFN_IS_NOTHROW_MOVE_CONSTRUCTIBLE_TYPE:
-      return eval_is_nothrow_move_constructible_type (h);
+      return eval_is_nothrow_move_constructible_type (loc, ctx, h,
+						      non_constant_p, fun);
     case METAFN_IS_NOTHROW_ASSIGNABLE_TYPE:
-      return eval_is_nothrow_assignable_type (loc, h, h1);
+      return eval_is_nothrow_assignable_type (loc, ctx, h, h1, non_constant_p,
+					      fun);
     case METAFN_IS_NOTHROW_COPY_ASSIGNABLE_TYPE:
-      return eval_is_nothrow_copy_assignable_type (h);
+      return eval_is_nothrow_copy_assignable_type (loc, ctx, h, non_constant_p,
+						   fun);
     case METAFN_IS_NOTHROW_MOVE_ASSIGNABLE_TYPE:
-      return eval_is_nothrow_move_assignable_type (h);
+      return eval_is_nothrow_move_assignable_type (loc, ctx, h, non_constant_p,
+						   fun);
     case METAFN_IS_NOTHROW_SWAPPABLE_WITH_TYPE:
       return eval_is_swappable_with_type (loc, ctx, h, h1, call,
 					  non_constant_p, jump_target, fun,
@@ -8389,43 +8538,53 @@ process_metafunction (const constexpr_ctx *ctx, tree fun, tree call,
       return eval_is_swappable_type (loc, ctx, h, call, non_constant_p,
 				     jump_target, fun, "is_nothrow_swappable");
     case METAFN_IS_NOTHROW_DESTRUCTIBLE_TYPE:
-      return eval_is_nothrow_destructible_type (loc, h);
+      return eval_is_nothrow_destructible_type (loc, ctx, h, non_constant_p,
+						fun);
     case METAFN_IS_IMPLICIT_LIFETIME_TYPE:
-      return eval_is_implicit_lifetime_type (h);
+      return eval_is_implicit_lifetime_type (loc, ctx, h, non_constant_p, fun);
     case METAFN_HAS_VIRTUAL_DESTRUCTOR:
-      return eval_has_virtual_destructor (h);
+      return eval_has_virtual_destructor (loc, ctx, h, non_constant_p, fun);
     case METAFN_HAS_UNIQUE_OBJECT_REPRESENTATIONS:
-      return eval_has_unique_object_representations (h);
+      return eval_has_unique_object_representations (loc, ctx, h,
+						     non_constant_p, fun);
     case METAFN_REFERENCE_CONSTRUCTS_FROM_TEMPORARY:
-      return eval_reference_constructs_from_temporary (loc, h, h1);
+      return eval_reference_constructs_from_temporary (loc, ctx, h, h1,
+						       non_constant_p, fun);
     case METAFN_REFERENCE_CONVERTS_FROM_TEMPORARY:
-      return eval_reference_converts_from_temporary (loc, h, h1);
+      return eval_reference_converts_from_temporary (loc, ctx, h, h1,
+						     non_constant_p, fun);
     case METAFN_RANK:
       return eval_rank (h);
     case METAFN_EXTENT:
-      return eval_extent (loc, h, expr);
+      return eval_extent (loc, ctx, h, expr, non_constant_p, fun);
     case METAFN_IS_SAME_TYPE:
-      return eval_is_same_type (loc, h, h1);
+      return eval_is_same_type (loc, ctx, h, h1, non_constant_p, fun);
     case METAFN_IS_BASE_OF_TYPE:
-      return eval_is_base_of_type (loc, h, h1);
+      return eval_is_base_of_type (loc, ctx, h, h1, non_constant_p, fun);
     case METAFN_IS_VIRTUAL_BASE_OF_TYPE:
-      return eval_is_virtual_base_of_type (loc, h, h1);
+      return eval_is_virtual_base_of_type (loc, ctx, h, h1, non_constant_p,
+					   fun);
     case METAFN_IS_CONVERTIBLE_TYPE:
-      return eval_is_convertible_type (loc, h, h1);
+      return eval_is_convertible_type (loc, ctx, h, h1, non_constant_p, fun);
     case METAFN_IS_NOTHROW_CONVERTIBLE_TYPE:
-      return eval_is_nothrow_convertible_type (loc, h, h1);
+      return eval_is_nothrow_convertible_type (loc, ctx, h, h1, non_constant_p,
+					       fun);
     case METAFN_IS_LAYOUT_COMPATIBLE_TYPE:
-      return eval_is_layout_compatible_type (loc, h, h1);
+      return eval_is_layout_compatible_type (loc, ctx, h, h1, non_constant_p,
+					     fun);
     case METAFN_IS_POINTER_INTERCONVERTIBLE_BASE_OF_TYPE:
-      return eval_is_pointer_interconvertible_base_of_type (loc, h, h1);
+      return eval_is_pointer_interconvertible_base_of_type (loc, ctx, h, h1,
+							    non_constant_p,
+							    fun);
     case METAFN_IS_INVOCABLE_TYPE:
-      return eval_is_invocable_type (loc, h, hvec);
+      return eval_is_invocable_type (loc, ctx, h, hvec, non_constant_p, fun);
     case METAFN_IS_INVOCABLE_R_TYPE:
       return eval_is_invocable_r_type (loc, ctx, h, h1, hvec, call,
 				       non_constant_p, jump_target, fun,
 				       "is_invocable_r");
     case METAFN_IS_NOTHROW_INVOCABLE_TYPE:
-      return eval_is_nothrow_invocable_type (loc, h, hvec);
+      return eval_is_nothrow_invocable_type (loc, ctx, h, hvec, non_constant_p,
+					     fun);
     case METAFN_IS_NOTHROW_INVOCABLE_R_TYPE:
       return eval_is_invocable_r_type (loc, ctx, h, h1, hvec, call,
 				       non_constant_p, jump_target, fun,
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index e0ca8ab6a45b..18808354b6e8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -14230,10 +14230,13 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
    If TYPE is a non-union class type, it must be complete.
 
    When KIND == 4:
-   If TYPE is a class type, it must be complete.  */
+   If TYPE is a class type, it must be complete.
+
+   If COMPLAIN is not tf_none, we permerror and return false if that doesn't
+   produce a hard error.  */
 
 static bool
-check_trait_type (tree type, int kind = 1)
+check_trait_type (tree type, int kind, tsubst_flags_t complain)
 {
   if (type == NULL_TREE)
     return true;
@@ -14241,7 +14244,7 @@ check_trait_type (tree type, int kind = 1)
   if (TREE_CODE (type) == TREE_VEC)
     {
       for (tree arg : tree_vec_range (type))
-	if (!check_trait_type (arg, kind))
+	if (!check_trait_type (arg, kind, complain))
 	  return false;
       return true;
     }
@@ -14260,9 +14263,12 @@ check_trait_type (tree type, int kind = 1)
 
   type = complete_type (strip_array_types (type));
   if (!COMPLETE_TYPE_P (type)
-      && cxx_incomplete_type_diagnostic (NULL_TREE, type,
-					 diagnostics::kind::permerror)
-      && !flag_permissive)
+      /* If we're not emitting error messages, always return false for
+	 incomplete types.  */
+      && (complain == tf_none
+	  || (cxx_incomplete_type_diagnostic (NULL_TREE, type,
+					      diagnostics::kind::permerror)
+	      && !flag_permissive)))
     return false;
   return true;
 }
@@ -14332,7 +14338,8 @@ finish_structured_binding_size (location_t loc, tree type,
 /* Process a trait expression.  */
 
 tree
-finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
+finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2,
+		   tsubst_flags_t complain/*=tf_warning_or_error*/)
 {
   if (type1 == error_mark_node
       || type2 == error_mark_node)
@@ -14370,7 +14377,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_DESTRUCTIBLE:
     case CPTK_IS_NOTHROW_DESTRUCTIBLE:
     case CPTK_IS_TRIVIALLY_DESTRUCTIBLE:
-      if (!check_trait_type (type1))
+      if (!check_trait_type (type1, /*kind=*/1, complain))
 	return error_mark_node;
       break;
 
@@ -14381,7 +14388,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_TRIVIALLY_COPYABLE:
     case CPTK_IS_STRUCTURAL:
     case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
-      if (!check_trait_type (type1, /* kind = */ 2))
+      if (!check_trait_type (type1, /* kind = */ 2, complain))
 	return error_mark_node;
       break;
 
@@ -14389,7 +14396,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_EMPTY:
     case CPTK_IS_POLYMORPHIC:
     case CPTK_HAS_VIRTUAL_DESTRUCTOR:
-      if (!check_trait_type (type1, /* kind = */ 3))
+      if (!check_trait_type (type1, /* kind = */ 3, complain))
 	return error_mark_node;
       break;
 
@@ -14398,7 +14405,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_AGGREGATE:
     case CPTK_IS_FINAL:
     case CPTK_IS_IMPLICIT_LIFETIME:
-      if (!check_trait_type (type1, /* kind = */ 4))
+      if (!check_trait_type (type1, /* kind = */ 4, complain))
 	return error_mark_node;
       break;
 
@@ -14419,8 +14426,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_ASSIGNABLE:
     case CPTK_IS_NOTHROW_ASSIGNABLE:
     case CPTK_IS_TRIVIALLY_ASSIGNABLE:
-      if (!check_trait_type (type1)
-	  || !check_trait_type (type2))
+      if (!check_trait_type (type1, /*kind=*/1, complain)
+	  || !check_trait_type (type2, /*kind=*/1, complain))
 	return error_mark_node;
       break;
 
@@ -14428,14 +14435,14 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
     case CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF:
       if (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
 	  && !same_type_ignoring_top_level_qualifiers_p (type1, type2)
-	  && !complete_type_or_else (type2, NULL_TREE))
+	  && !complete_type_or_maybe_complain (type2, NULL_TREE, complain))
 	/* We already issued an error.  */
 	return error_mark_node;
       break;
 
     case CPTK_IS_VIRTUAL_BASE_OF:
       if (NON_UNION_CLASS_TYPE_P (type1) && NON_UNION_CLASS_TYPE_P (type2)
-	  && !complete_type_or_else (type2, NULL_TREE))
+	  && !complete_type_or_maybe_complain (type2, NULL_TREE, complain))
 	/* We already issued an error.  */
 	return error_mark_node;
       break;
@@ -14472,17 +14479,17 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
       return maybe_wrap_with_location (type_order_value (type1, type2), loc);
 
     case CPTK_STRUCTURED_BINDING_SIZE:
-      return finish_structured_binding_size (loc, type1, tf_warning_or_error);
+      return finish_structured_binding_size (loc, type1, complain);
 
     case CPTK_IS_LAYOUT_COMPATIBLE:
       if (!array_of_unknown_bound_p (type1)
 	  && TREE_CODE (type1) != VOID_TYPE
-	  && !complete_type_or_else (type1, NULL_TREE))
+	  && !complete_type_or_maybe_complain (type1, NULL_TREE, complain))
 	/* We already issued an error.  */
 	return error_mark_node;
       if (!array_of_unknown_bound_p (type2)
 	  && TREE_CODE (type2) != VOID_TYPE
-	  && !complete_type_or_else (type2, NULL_TREE))
+	  && !complete_type_or_maybe_complain (type2, NULL_TREE, complain))
 	/* We already issued an error.  */
 	return error_mark_node;
       break;
diff --git a/gcc/testsuite/g++.dg/reflect/type_trait16.C b/gcc/testsuite/g++.dg/reflect/type_trait16.C
new file mode 100644
index 000000000000..5fca8658f1cb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/type_trait16.C
@@ -0,0 +1,37 @@
+// PR c++/125901
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <meta>
+
+template<typename T>
+struct S {
+  T t;
+};
+
+static_assert(is_trivially_copyable_type(^^S<int>));
+static_assert(!is_trivially_copyable_type(^^std::vector<int>));
+static_assert(is_standard_layout_type(^^std::vector<int>));
+static_assert(is_default_constructible_type(^^std::vector<int>));
+static_assert(!is_trivially_default_constructible_type(^^std::vector<int>));
+static_assert(!is_trivially_copy_constructible_type(^^std::vector<int>));
+static_assert(!is_trivially_move_constructible_type(^^std::vector<int>));
+static_assert(is_copy_assignable_type(^^std::vector<int>));
+static_assert(!is_implicit_lifetime_type(^^std::vector<int>));
+static_assert(!has_virtual_destructor(^^std::vector<int>));
+static_assert(!has_unique_object_representations(^^std::vector<int>));
+
+const std::vector<int> vec{};
+const S<int> s{};
+
+static_assert(is_trivially_copyable_type(^^S<int>));
+static_assert(!is_trivially_copyable_type(^^std::vector<int>));
+static_assert(is_standard_layout_type(^^std::vector<int>));
+static_assert(is_default_constructible_type(^^std::vector<int>));
+static_assert(!is_trivially_default_constructible_type(^^std::vector<int>));
+static_assert(!is_trivially_copy_constructible_type(^^std::vector<int>));
+static_assert(!is_trivially_move_constructible_type(^^std::vector<int>));
+static_assert(is_copy_assignable_type(^^std::vector<int>));
+static_assert(!is_implicit_lifetime_type(^^std::vector<int>));
+static_assert(!has_virtual_destructor(^^std::vector<int>));
+static_assert(!has_unique_object_representations(^^std::vector<int>));
diff --git a/gcc/testsuite/g++.dg/reflect/type_trait17.C b/gcc/testsuite/g++.dg/reflect/type_trait17.C
new file mode 100644
index 000000000000..2a2c2ddeb787
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/type_trait17.C
@@ -0,0 +1,315 @@
+// PR c++/125901
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// [meta.unary.prop]
+
+#include <meta>
+
+template<typename>
+struct S;
+union U;
+
+// remove_all_extents_t<T> shall be a complete type or cv void.
+constexpr bool b0 = is_trivially_copyable_type (^^S<int>);    // { dg-error "preconditions not satisfied" }
+constexpr bool b1 = is_trivially_copyable_type (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+constexpr bool b0u = is_trivially_copyable_type (^^U);    // { dg-error "preconditions not satisfied" }
+constexpr bool b1u = is_trivially_copyable_type (^^U[1]); // { dg-error "preconditions not satisfied" }
+static_assert (!is_trivially_copyable_type (^^void));
+
+constexpr bool b2 = is_standard_layout_type (^^S<int>);	      // { dg-error "preconditions not satisfied" }
+constexpr bool b3 = is_standard_layout_type (^^S<int>[1]);    // { dg-error "preconditions not satisfied" }
+constexpr bool b2u = is_standard_layout_type (^^U);	      // { dg-error "preconditions not satisfied" }
+constexpr bool b3u = is_standard_layout_type (^^U[1]);    // { dg-error "preconditions not satisfied" }
+static_assert (!is_standard_layout_type (^^void));
+
+constexpr bool b11 = is_structural_type (^^S<int>);	      // { dg-error "preconditions not satisfied" }
+constexpr bool b12 = is_structural_type (^^S<int>[1]);    // { dg-error "preconditions not satisfied" }
+constexpr bool b11u = is_structural_type (^^U);	      // { dg-error "preconditions not satisfied" }
+constexpr bool b12u = is_structural_type (^^U[1]);    // { dg-error "preconditions not satisfied" }
+static_assert (!is_structural_type (^^void));
+
+// If T is a non-union class type, T shall be a complete type.
+constexpr bool b4 = is_empty_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_empty_type (^^S<int>[1]));
+static_assert (!is_empty_type (^^U));
+static_assert (!is_empty_type (^^U[1]));
+static_assert (!is_empty_type (^^void));
+
+constexpr bool b5 = is_polymorphic_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_polymorphic_type (^^S<int>[1]));
+static_assert (!is_polymorphic_type (^^U));
+static_assert (!is_polymorphic_type (^^U[1]));
+static_assert (!is_polymorphic_type (^^void));
+
+constexpr bool b6 = is_abstract_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_abstract_type (^^S<int>[1]));
+static_assert (!is_abstract_type (^^U));
+static_assert (!is_abstract_type (^^U[1]));
+static_assert (!is_abstract_type (^^void));
+
+// If T is a class type, T shall be a complete type.
+constexpr bool b7 = is_final_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+constexpr bool b8 = is_final_type (^^U);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_final_type (^^S<int>[1]));
+static_assert (!is_final_type (^^U[1]));
+static_assert (!is_final_type (^^void));
+
+// T shall be an array type, a complete type, or cv void.
+static_assert (is_aggregate_type (^^S<int>[1]));
+static_assert (is_aggregate_type (^^U[]));
+static_assert (is_aggregate_type (^^S<int>[]));
+static_assert (is_aggregate_type (^^U[1]));
+static_assert (!is_aggregate_type (^^void));
+constexpr bool b9 = is_aggregate_type (^^S<int>); // { dg-error "preconditions not satisfied" }
+constexpr bool b10 = is_aggregate_type (^^U);	// { dg-error "preconditions not satisfied" }
+
+// T and all types in the template parameter pack Args shall be
+// complete types, cv void, or arrays of unknown bound.
+constexpr bool b13 = is_constructible_type (^^S<int>, { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b14 = is_constructible_type (^^int, { ^^S<int> }); // { dg-error "preconditions not satisfied" }
+constexpr bool b13a = is_constructible_type (^^S<int>[1], { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b14a = is_constructible_type (^^int, { ^^S<int>[1] }); // { dg-error "preconditions not satisfied" }
+static_assert (!is_constructible_type (^^S<int>[], { ^^int }));
+static_assert (!is_constructible_type (^^int, { ^^S<int>[] }));
+constexpr bool b15 = is_constructible_type (^^U, { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b16 = is_constructible_type (^^int, { ^^U });  // { dg-error "preconditions not satisfied" }
+constexpr bool b15a = is_constructible_type (^^U[1], { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b16a = is_constructible_type (^^int, { ^^U[1] });  // { dg-error "preconditions not satisfied" }
+static_assert (!is_constructible_type (^^U[], { ^^int }));
+static_assert (!is_constructible_type (^^int, { ^^U[] }));
+static_assert (!is_constructible_type (^^int, { ^^void }));
+
+// T shall be a complete type, cv void, or an array of unknown bound.
+constexpr bool b17 = is_default_constructible_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+constexpr bool b18 = is_default_constructible_type (^^U);	// { dg-error "preconditions not satisfied" }
+constexpr bool b19 = is_default_constructible_type (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+constexpr bool b20 = is_default_constructible_type (^^U[1]);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_default_constructible_type (^^S<int>[]));
+static_assert (!is_default_constructible_type (^^U[]));
+static_assert (!is_default_constructible_type (^^void));
+
+constexpr bool b21 = is_copy_constructible_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+constexpr bool b22 = is_copy_constructible_type (^^U);	// { dg-error "preconditions not satisfied" }
+constexpr bool b23 = is_copy_constructible_type (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+constexpr bool b24 = is_copy_constructible_type (^^U[1]);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_copy_constructible_type (^^S<int>[]));
+static_assert (!is_copy_constructible_type (^^U[]));
+static_assert (!is_copy_constructible_type (^^void));
+
+constexpr bool b25 = is_move_constructible_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+constexpr bool b26 = is_move_constructible_type (^^U);	// { dg-error "preconditions not satisfied" }
+constexpr bool b27 = is_move_constructible_type (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+constexpr bool b28 = is_move_constructible_type (^^U[1]);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_move_constructible_type (^^S<int>[]));
+static_assert (!is_move_constructible_type (^^U[]));
+static_assert (!is_move_constructible_type (^^void));
+
+// T and U shall be complete types, cv void, or arrays of unknown bound.
+constexpr bool b29 = is_assignable_type (^^S<int>, ^^int);  // { dg-error "preconditions not satisfied" }
+constexpr bool b30 = is_assignable_type (^^int, ^^S<int>);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_assignable_type (^^S<int>[], ^^int));
+static_assert (!is_assignable_type (^^int, ^^S<int>[]));
+static_assert (!is_assignable_type (^^void, ^^int));
+static_assert (!is_assignable_type (^^int, ^^void));
+
+// T shall be a complete type, cv void, or an array of unknown bound.
+constexpr bool b31 = is_copy_assignable_type (^^S<int>);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+constexpr bool b31u = is_copy_assignable_type (^^U);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+static_assert (!is_copy_assignable_type (^^S<int>[]));
+static_assert (!is_copy_assignable_type (^^U[]));
+static_assert (!is_copy_assignable_type (^^void));
+
+constexpr bool b32 = is_move_assignable_type (^^S<int>);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+constexpr bool b32u = is_move_assignable_type (^^U);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+static_assert (!is_move_assignable_type (^^S<int>[]));
+static_assert (!is_move_assignable_type (^^U[]));
+static_assert (!is_move_assignable_type (^^void));
+
+// T and U shall be complete types, cv void, or arrays of unknown bound.
+constexpr bool b33 = is_swappable_with_type (^^S<int>, ^^int);	// { dg-message "required from here" }
+constexpr bool b34 = is_swappable_with_type (^^int, ^^S<int>);	// { dg-message "required from here" }
+constexpr bool b33u = is_swappable_with_type (^^U, ^^int);	// { dg-message "required from here" }
+constexpr bool b34u = is_swappable_with_type (^^int, ^^U);	// { dg-message "required from here" }
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
+static_assert (!is_swappable_with_type (^^S<int>[], ^^int));
+static_assert (!is_swappable_with_type (^^int, ^^S<int>[]));
+static_assert (!is_swappable_with_type (^^U[], ^^int));
+static_assert (!is_swappable_with_type (^^int, ^^U[]));
+static_assert (!is_swappable_with_type (^^void, ^^int));
+static_assert (!is_swappable_with_type (^^int, ^^void));
+
+constexpr bool b33n = is_nothrow_swappable_with_type (^^S<int>, ^^int);	// { dg-message "required from here" }
+constexpr bool b34n = is_nothrow_swappable_with_type (^^int, ^^S<int>);	// { dg-message "required from here" }
+static_assert (!is_nothrow_swappable_with_type (^^S<int>[], ^^int));
+static_assert (!is_nothrow_swappable_with_type (^^int, ^^S<int>[]));
+static_assert (!is_nothrow_swappable_with_type (^^void, ^^int));
+static_assert (!is_nothrow_swappable_with_type (^^int, ^^void));
+
+struct W;
+constexpr bool b35 = is_swappable_type (^^W);	// { dg-message "required from here" }
+static_assert (!is_swappable_type (^^S<int>[]));
+static_assert (!is_swappable_type (^^void));
+constexpr bool b35n = is_nothrow_swappable_type (^^W);	// { dg-message "required from here" }
+static_assert (!is_nothrow_swappable_type (^^S<int>[]));
+static_assert (!is_nothrow_swappable_type (^^void));
+// { dg-error "invalid use of incomplete type .struct W." "" { target *-*-* } 0 }
+
+//T shall be a complete type, cv void, or an array of unknown bound.
+constexpr bool b36 = is_destructible_type (^^S<int>); // { dg-error "preconditions not satisfied" }
+constexpr bool b36u = is_destructible_type (^^U); // { dg-error "preconditions not satisfied" }
+static_assert (!is_destructible_type (^^S<int>[]));
+static_assert (!is_destructible_type (^^void));
+
+constexpr bool b36n = is_nothrow_destructible_type (^^S<int>); // { dg-error "preconditions not satisfied" }
+constexpr bool b36nu = is_nothrow_destructible_type (^^U); // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_destructible_type (^^S<int>[]));
+static_assert (!is_nothrow_destructible_type (^^U[]));
+static_assert (!is_nothrow_destructible_type (^^void));
+
+// T and all types in the template parameter pack Args shall be complete
+// types, cv void, or arrays of unknown bound.
+constexpr bool b37 = is_trivially_constructible_type (^^S<int>, {});  // { dg-error "preconditions not satisfied" }
+constexpr bool b38 = is_trivially_constructible_type (^^int, { ^^S<int> });  // { dg-error "preconditions not satisfied" }
+static_assert (!is_trivially_constructible_type (^^S<int>[], {}));
+static_assert (!is_trivially_constructible_type (^^int, { ^^S<int>[] }));
+static_assert (!is_trivially_constructible_type (^^void, {}));
+static_assert (!is_trivially_constructible_type (^^int, { ^^void }));
+
+// T shall be a complete type, cv void, or an array of unknown bound.
+constexpr bool b39 = is_trivially_default_constructible_type (^^S<int>);  // { dg-error "preconditions not satisfied" }
+constexpr bool b39u = is_trivially_default_constructible_type (^^U);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_trivially_default_constructible_type (^^S<int>[]));
+static_assert (!is_trivially_default_constructible_type (^^U[]));
+static_assert (!is_trivially_default_constructible_type (^^void));
+
+constexpr bool b40 = is_trivially_copy_constructible_type (^^S<int>);  // { dg-error "preconditions not satisfied" }
+constexpr bool b40u = is_trivially_copy_constructible_type (^^U);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_trivially_copy_constructible_type (^^S<int>[]));
+static_assert (!is_trivially_copy_constructible_type (^^U[]));
+static_assert (!is_trivially_copy_constructible_type (^^void));
+
+constexpr bool b41 = is_trivially_move_constructible_type (^^S<int>);  // { dg-error "preconditions not satisfied" }
+constexpr bool b41u = is_trivially_move_constructible_type (^^U);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_trivially_move_constructible_type (^^S<int>[]));
+static_assert (!is_trivially_move_constructible_type (^^U[]));
+static_assert (!is_trivially_move_constructible_type (^^void));
+
+// T and U shall be complete types, cv void, or arrays of unknown bound.
+constexpr bool b42 = is_trivially_assignable_type (^^S<int>, ^^int);  // { dg-error "preconditions not satisfied" }
+constexpr bool b43 = is_trivially_assignable_type (^^int, ^^S<int>);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_trivially_assignable_type (^^S<int>[], ^^int));
+static_assert (!is_trivially_assignable_type (^^int, ^^S<int>[]));
+static_assert (!is_trivially_assignable_type (^^void, ^^int));
+static_assert (!is_trivially_assignable_type (^^int, ^^void));
+
+// T shall be a complete type, cv void, or an array of unknown bound.
+constexpr bool b44 = is_trivially_copy_assignable_type (^^S<int>);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+constexpr bool b44u = is_trivially_copy_assignable_type (^^U);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+static_assert (!is_trivially_copy_assignable_type (^^S<int>[]));
+static_assert (!is_trivially_copy_assignable_type (^^U[]));
+static_assert (!is_trivially_copy_assignable_type (^^void));
+
+constexpr bool b45 = is_trivially_move_assignable_type (^^S<int>);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+constexpr bool b45u = is_trivially_move_assignable_type (^^U);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+static_assert (!is_trivially_move_assignable_type (^^S<int>[]));
+static_assert (!is_trivially_move_assignable_type (^^U[]));
+static_assert (!is_trivially_move_assignable_type (^^void));
+
+constexpr bool b46 = is_trivially_destructible_type (^^S<int>);	// { dg-error "preconditions not satisfied" }
+constexpr bool b46u = is_trivially_destructible_type (^^U);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_trivially_destructible_type (^^S<int>[]));
+static_assert (!is_trivially_destructible_type (^^U[]));
+static_assert (!is_trivially_destructible_type (^^void));
+
+// T and all types in the template parameter pack Args shall be complete
+// types, cv void, or arrays of unknown bound.
+constexpr bool b47 = is_nothrow_constructible_type (^^S<int>, { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b48 = is_nothrow_constructible_type (^^int, { ^^S<int> }); // { dg-error "preconditions not satisfied" }
+constexpr bool b49 = is_nothrow_constructible_type (^^S<int>[1], { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b50 = is_nothrow_constructible_type (^^int, { ^^S<int>[1] }); // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_constructible_type (^^S<int>[], { ^^int }));
+static_assert (!is_nothrow_constructible_type (^^int, { ^^S<int>[] }));
+constexpr bool b51 = is_nothrow_constructible_type (^^U, { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b52 = is_nothrow_constructible_type (^^int, { ^^U });  // { dg-error "preconditions not satisfied" }
+constexpr bool b53 = is_nothrow_constructible_type (^^U[1], { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b54 = is_nothrow_constructible_type (^^int, { ^^U[1] });  // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_constructible_type (^^U[], { ^^int }));
+static_assert (!is_nothrow_constructible_type (^^int, { ^^U[] }));
+static_assert (!is_nothrow_constructible_type (^^int, { ^^void }));
+
+// T shall be a complete type, cv void, or an array of unknown bound.
+constexpr bool b55 = is_nothrow_default_constructible_type (^^S<int>); // { dg-error "preconditions not satisfied" }
+constexpr bool b56 = is_nothrow_default_constructible_type (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_default_constructible_type (^^S<int>[]));
+constexpr bool b57 = is_nothrow_default_constructible_type (^^U);  // { dg-error "preconditions not satisfied" }
+constexpr bool b58 = is_nothrow_default_constructible_type (^^U[1]);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_default_constructible_type (^^U[]));
+static_assert (!is_nothrow_default_constructible_type (^^void));
+
+constexpr bool b59 = is_nothrow_copy_constructible_type (^^S<int>); // { dg-error "preconditions not satisfied" }
+constexpr bool b60 = is_nothrow_copy_constructible_type (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_copy_constructible_type (^^S<int>[]));
+constexpr bool b61 = is_nothrow_copy_constructible_type (^^U);  // { dg-error "preconditions not satisfied" }
+constexpr bool b62 = is_nothrow_copy_constructible_type (^^U[1]);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_copy_constructible_type (^^U[]));
+static_assert (!is_nothrow_copy_constructible_type (^^void));
+
+constexpr bool b63 = is_nothrow_move_constructible_type (^^S<int>); // { dg-error "preconditions not satisfied" }
+constexpr bool b64 = is_nothrow_move_constructible_type (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_move_constructible_type (^^S<int>[]));
+constexpr bool b65 = is_nothrow_move_constructible_type (^^U);  // { dg-error "preconditions not satisfied" }
+constexpr bool b66 = is_nothrow_move_constructible_type (^^U[1]);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_move_constructible_type (^^U[]));
+static_assert (!is_nothrow_move_constructible_type (^^void));
+
+constexpr bool b67 = is_nothrow_assignable_type (^^S<int>, ^^int);  // { dg-error "preconditions not satisfied" }
+constexpr bool b68 = is_nothrow_assignable_type (^^int, ^^S<int>);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_assignable_type (^^S<int>[], ^^int));
+static_assert (!is_nothrow_assignable_type (^^int, ^^S<int>[]));
+static_assert (!is_nothrow_assignable_type (^^void, ^^int));
+static_assert (!is_nothrow_assignable_type (^^int, ^^void));
+
+constexpr bool b69 = is_nothrow_copy_assignable_type (^^S<int>);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+constexpr bool b70 = is_nothrow_copy_assignable_type (^^U);	  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+static_assert (!is_nothrow_copy_assignable_type (^^S<int>[]));
+static_assert (!is_nothrow_copy_assignable_type (^^void));
+
+constexpr bool b71 = is_nothrow_move_assignable_type (^^S<int>);  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+constexpr bool b72 = is_nothrow_move_assignable_type (^^U);	  // { dg-error "preconditions not satisfied" "PR126073" { xfail *-*-* } }
+static_assert (!is_nothrow_move_assignable_type (^^S<int>[]));
+static_assert (!is_nothrow_move_assignable_type (^^void));
+
+// T shall be an array type, a complete type, or cv void.
+constexpr bool b73 = is_implicit_lifetime_type (^^S<int>);  // { dg-error "preconditions not satisfied" }
+constexpr bool b74 = is_implicit_lifetime_type (^^U);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_implicit_lifetime_type (^^S<int>[1]));
+static_assert (!is_implicit_lifetime_type (^^void));
+
+// If T is a non-union class type, T shall be a complete type.
+constexpr bool b75 = has_virtual_destructor (^^S<int>);	// { dg-error "preconditions not satisfied" }
+static_assert (!has_virtual_destructor (^^S<int>[1]));
+static_assert (!has_virtual_destructor (^^U));
+static_assert (!has_virtual_destructor (^^U[1]));
+static_assert (!has_virtual_destructor (^^void));
+
+// remove_all_extents_t<T> shall be a complete type or cv void.
+constexpr bool b76 = has_unique_object_representations (^^S<int>);    // { dg-error "preconditions not satisfied" }
+constexpr bool b77 = has_unique_object_representations (^^S<int>[1]); // { dg-error "preconditions not satisfied" }
+constexpr bool b78 = has_unique_object_representations (^^U);    // { dg-error "preconditions not satisfied" }
+constexpr bool b79 = has_unique_object_representations (^^U[1]); // { dg-error "preconditions not satisfied" }
+static_assert (!has_unique_object_representations (^^void));
+
+// T and U shall be complete types, cv void, or arrays of unknown bound.
+constexpr bool b80 = reference_constructs_from_temporary (^^S<int>, ^^int);  // { dg-error "preconditions not satisfied" }
+constexpr bool b81 = reference_constructs_from_temporary (^^int, ^^S<int>);  // { dg-error "preconditions not satisfied" }
+static_assert (!reference_constructs_from_temporary (^^S<int>[], ^^int));
+static_assert (!reference_constructs_from_temporary (^^int, ^^S<int>[]));
+static_assert (!reference_constructs_from_temporary (^^void, ^^int));
+static_assert (!reference_constructs_from_temporary (^^int, ^^void));
+
+constexpr bool b82 = reference_converts_from_temporary (^^S<int>, ^^int);  // { dg-error "preconditions not satisfied" }
+constexpr bool b83 = reference_converts_from_temporary (^^int, ^^S<int>);  // { dg-error "preconditions not satisfied" }
+static_assert (!reference_converts_from_temporary (^^S<int>[], ^^int));
+static_assert (!reference_converts_from_temporary (^^int, ^^S<int>[]));
+static_assert (!reference_converts_from_temporary (^^void, ^^int));
+static_assert (!reference_converts_from_temporary (^^int, ^^void));
diff --git a/gcc/testsuite/g++.dg/reflect/type_trait18.C b/gcc/testsuite/g++.dg/reflect/type_trait18.C
new file mode 100644
index 000000000000..05976a1fcb54
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/type_trait18.C
@@ -0,0 +1,126 @@
+// PR c++/125901
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// [meta.rel]
+
+#include <meta>
+
+struct S;
+union U;
+struct B {};
+
+// If Base and Derived are non-union class types and are not (possibly
+// cv-qualified versions of) the same type, Derived shall be a complete type.
+constexpr bool b1 = is_base_of_type (^^B, ^^S);	// { dg-error "preconditions not satisfied" }
+constexpr bool b2 = is_base_of_type (^^B, ^^const S);	// { dg-error "preconditions not satisfied" }
+static_assert (is_base_of_type (^^S, ^^S));
+static_assert (is_base_of_type (^^S, ^^const S));
+static_assert (!is_base_of_type (^^B, ^^U));
+
+constexpr bool b15 = is_pointer_interconvertible_base_of_type (^^B, ^^S);	// { dg-error "preconditions not satisfied" }
+constexpr bool b16 = is_pointer_interconvertible_base_of_type (^^B, ^^const S);	// { dg-error "preconditions not satisfied" }
+static_assert (is_pointer_interconvertible_base_of_type (^^S, ^^S));
+static_assert (is_pointer_interconvertible_base_of_type (^^S, ^^const S));
+static_assert (!is_pointer_interconvertible_base_of_type (^^B, ^^U));
+
+// If Base and Derived are non-union class types, Derived shall be a complete type.
+constexpr bool b3 = is_virtual_base_of_type (^^B, ^^S);	// { dg-error "preconditions not satisfied" }
+constexpr bool b4 = is_virtual_base_of_type (^^B, ^^const S);	// { dg-error "preconditions not satisfied" }
+constexpr bool b5 = is_virtual_base_of_type (^^S, ^^S);	// { dg-error "preconditions not satisfied" }
+constexpr bool b6 = is_virtual_base_of_type (^^S, ^^const S);	// { dg-error "preconditions not satisfied" }
+static_assert (!is_virtual_base_of_type (^^B, ^^U));
+static_assert (!is_virtual_base_of_type (^^U, ^^U));
+
+// From and To shall be complete types, cv void, or arrays of unknown bound.
+constexpr bool b7 = is_convertible_type (^^S, ^^B);   // { dg-error "preconditions not satisfied" }
+constexpr bool b8 = is_convertible_type (^^B, ^^S);   // { dg-error "preconditions not satisfied" }
+constexpr bool b9 = is_convertible_type (^^S, ^^B[]);   // { dg-error "preconditions not satisfied" }
+constexpr bool b10 = is_convertible_type (^^B[], ^^S);   // { dg-error "preconditions not satisfied" }
+constexpr bool b11 = is_convertible_type (^^S, ^^void);   // { dg-error "preconditions not satisfied" }
+constexpr bool b12 = is_convertible_type (^^void, ^^S);   // { dg-error "preconditions not satisfied" }
+static_assert (!is_convertible_type (^^S[], ^^B));
+static_assert (!is_convertible_type (^^B, ^^U[]));
+
+constexpr bool b7n = is_nothrow_convertible_type (^^S, ^^B);   // { dg-error "preconditions not satisfied" }
+constexpr bool b8n = is_nothrow_convertible_type (^^B, ^^S);   // { dg-error "preconditions not satisfied" }
+constexpr bool b9n = is_nothrow_convertible_type (^^S, ^^B[]);   // { dg-error "preconditions not satisfied" }
+constexpr bool b10n = is_nothrow_convertible_type (^^B[], ^^S);   // { dg-error "preconditions not satisfied" }
+constexpr bool b11n = is_nothrow_convertible_type (^^S, ^^void);   // { dg-error "preconditions not satisfied" }
+constexpr bool b12n = is_nothrow_convertible_type (^^void, ^^S);   // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_convertible_type (^^S[], ^^B));
+static_assert (!is_nothrow_convertible_type (^^B, ^^U[]));
+
+// T and U shall be complete types, cv void, or arrays of unknown bound.
+constexpr bool b13 = is_layout_compatible_type (^^S, ^^int);  // { dg-error "preconditions not satisfied" }
+constexpr bool b14 = is_layout_compatible_type (^^int, ^^S);  // { dg-error "preconditions not satisfied" }
+static_assert (!is_layout_compatible_type (^^S[], ^^int));
+static_assert (!is_layout_compatible_type (^^int, ^^S[]));
+static_assert (!is_layout_compatible_type (^^void, ^^int));
+static_assert (!is_layout_compatible_type (^^int, ^^void));
+
+// Fn and all types in the template parameter pack ArgTypes shall be
+// complete types, cv void, or arrays of unknown bound.
+constexpr bool b17 = is_invocable_type (^^S, { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b18 = is_invocable_type (^^int, { ^^S }); // { dg-error "preconditions not satisfied" }
+constexpr bool b19 = is_invocable_type (^^S[1], { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b20 = is_invocable_type (^^int, { ^^S[1] }); // { dg-error "preconditions not satisfied" }
+static_assert (!is_invocable_type (^^S[], { ^^int }));
+static_assert (!is_invocable_type (^^int, { ^^S[] }));
+constexpr bool b21 = is_invocable_type (^^U, { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b22 = is_invocable_type (^^int, { ^^U });  // { dg-error "preconditions not satisfied" }
+constexpr bool b23 = is_invocable_type (^^U[1], { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b24 = is_invocable_type (^^int, { ^^U[1] });  // { dg-error "preconditions not satisfied" }
+static_assert (!is_invocable_type (^^U[], { ^^int }));
+static_assert (!is_invocable_type (^^int, { ^^U[] }));
+static_assert (!is_invocable_type (^^int, { ^^void }));
+
+constexpr bool b17n = is_nothrow_invocable_type (^^S, { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b18n = is_nothrow_invocable_type (^^int, { ^^S }); // { dg-error "preconditions not satisfied" }
+constexpr bool b19n = is_nothrow_invocable_type (^^S[1], { ^^int }); // { dg-error "preconditions not satisfied" }
+constexpr bool b20n = is_nothrow_invocable_type (^^int, { ^^S[1] }); // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_invocable_type (^^S[], { ^^int }));
+static_assert (!is_nothrow_invocable_type (^^int, { ^^S[] }));
+constexpr bool b21n = is_nothrow_invocable_type (^^U, { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b22n = is_nothrow_invocable_type (^^int, { ^^U });  // { dg-error "preconditions not satisfied" }
+constexpr bool b23n = is_nothrow_invocable_type (^^U[1], { ^^int });  // { dg-error "preconditions not satisfied" }
+constexpr bool b24n = is_nothrow_invocable_type (^^int, { ^^U[1] });  // { dg-error "preconditions not satisfied" }
+static_assert (!is_nothrow_invocable_type (^^U[], { ^^int }));
+static_assert (!is_nothrow_invocable_type (^^int, { ^^U[] }));
+static_assert (!is_nothrow_invocable_type (^^int, { ^^void }));
+
+// Fn, R, and all types in the template parameter pack ArgTypes shall be
+// complete types, cv void, or arrays of unknown bound.
+constexpr bool b25 = is_invocable_r_type (^^S, ^^int, { ^^int }); // { dg-message "required from here" }
+constexpr bool b26 = is_invocable_r_type (^^int, ^^S, { ^^S });	  // { dg-message "required from here" }
+constexpr bool b27 = is_invocable_r_type (^^S[1], ^^int, { ^^int }); // { dg-message "required from here" }
+constexpr bool b28 = is_invocable_r_type (^^int, ^^int, { ^^S[1] }); // { dg-message "required from here" }
+constexpr bool b29 = is_invocable_r_type (^^int, ^^S, { ^^int });   // { dg-message "required from here" }
+static_assert (!is_invocable_r_type (^^S[], ^^int, { ^^int }));
+static_assert (!is_invocable_r_type (^^int, ^^int, { ^^S[] }));
+constexpr bool b30 = is_invocable_r_type (^^U, ^^int, { ^^int });  // { dg-message "required from here" }
+constexpr bool b31 = is_invocable_r_type (^^int, ^^int, { ^^U });  // { dg-message "required from here" }
+constexpr bool b32 = is_invocable_r_type (^^U[1], ^^int, { ^^int });  // { dg-message "required from here" }
+constexpr bool b33 = is_invocable_r_type (^^int, ^^int, { ^^U[1] });  // { dg-message "required from here" }
+constexpr bool b34 = is_invocable_r_type (^^int, ^^U, { ^^int });     // { dg-message "required from here" }
+static_assert (!is_invocable_r_type (^^U[], ^^int, { ^^int }));
+static_assert (!is_invocable_r_type (^^int, ^^int, { ^^U[] }));
+static_assert (!is_invocable_r_type (^^int, ^^U[], { ^^int }));
+static_assert (!is_invocable_r_type (^^int, ^^int, { ^^void }));
+
+constexpr bool b25n = is_nothrow_invocable_r_type (^^S, ^^int, { ^^int }); // { dg-message "required from here" }
+constexpr bool b26n = is_nothrow_invocable_r_type (^^int, ^^S, { ^^S });	  // { dg-message "required from here" }
+constexpr bool b27n = is_nothrow_invocable_r_type (^^S[1], ^^int, { ^^int }); // { dg-message "required from here" }
+constexpr bool b28n = is_nothrow_invocable_r_type (^^int, ^^int, { ^^S[1] }); // { dg-message "required from here" }
+constexpr bool b29n = is_nothrow_invocable_r_type (^^int, ^^S, { ^^int });   // { dg-message "required from here" }
+static_assert (!is_nothrow_invocable_r_type (^^S[], ^^int, { ^^int }));
+static_assert (!is_nothrow_invocable_r_type (^^int, ^^int, { ^^S[] }));
+constexpr bool b30n = is_nothrow_invocable_r_type (^^U, ^^int, { ^^int });  // { dg-message "required from here" }
+constexpr bool b31n = is_nothrow_invocable_r_type (^^int, ^^int, { ^^U });  // { dg-message "required from here" }
+constexpr bool b32n = is_nothrow_invocable_r_type (^^U[1], ^^int, { ^^int });  // { dg-message "required from here" }
+constexpr bool b33n = is_nothrow_invocable_r_type (^^int, ^^int, { ^^U[1] });  // { dg-message "required from here" }
+constexpr bool b34n = is_nothrow_invocable_r_type (^^int, ^^U, { ^^int });     // { dg-message "required from here" }
+static_assert (!is_nothrow_invocable_r_type (^^U[], ^^int, { ^^int }));
+static_assert (!is_nothrow_invocable_r_type (^^int, ^^int, { ^^U[] }));
+static_assert (!is_nothrow_invocable_r_type (^^int, ^^U[], { ^^int }));
+static_assert (!is_nothrow_invocable_r_type (^^int, ^^int, { ^^void }));
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.