diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index b36c4be42736..10b5ed2b109f 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -8561,47 +8561,10 @@ splice (tree refl)
   return refl;
 }
 
-/* A walker for consteval_only_p.  It cannot be a lambda, because we
-   have to call this recursively, sigh.  */
+static tristate consteval_only_p_1 (tree, hash_set<tree>&);
 
-static tree
-consteval_only_type_r (tree *tp, int *walk_subtrees, void *data)
-{
-  tree t = *tp;
-  /* Types can contain themselves recursively, hence this.  */
-  auto visited = static_cast<hash_set<tree> *>(data);
-
-  if (!TYPE_P (t))
-    return NULL_TREE;
-
-  if (REFLECTION_TYPE_P (t))
-    return t;
-
-  if (typedef_variant_p (t))
-    /* Tell cp_walk_subtrees to look through typedefs.  */
-    *walk_subtrees = 2;
-
-  if (RECORD_OR_UNION_TYPE_P (t))
-    {
-      /* Don't walk template arguments; A<info>::type isn't a consteval-only
-	 type.  */
-      *walk_subtrees = 0;
-      /* So we have to walk the fields manually.  */
-      for (tree member = TYPE_FIELDS (t);
-	   member; member = DECL_CHAIN (member))
-	if (TREE_CODE (member) == FIELD_DECL)
-	  if (tree r = cp_walk_tree (&TREE_TYPE (member),
-				     consteval_only_type_r, visited, visited))
-	    return r;
-    }
-
-  return NULL_TREE;
-}
-
-/* True if T is a consteval-only type as per [basic.types.general]:
-   "A type is consteval-only if it is either std::meta::info or a type
-   compounded from a consteval-only type", or something that has
-   a consteval-only type.  */
+/* True if T is a consteval-only type as per [basic.types.general], or
+   is a declaration with such a type, or a TREE_VEC thereof.  */
 
 bool
 consteval_only_p (tree t)
@@ -8612,7 +8575,7 @@ consteval_only_p (tree t)
   if (!TYPE_P (t))
     t = TREE_TYPE (t);
 
-  if (!t)
+  if (!t || t == error_mark_node)
     return false;
 
   if (TREE_CODE (t) == TREE_VEC)
@@ -8634,9 +8597,80 @@ consteval_only_p (tree t)
      which could be consteval-only, depending on T.  */
   t = complete_type (t);
 
-  /* Classes with std::meta::info members are also consteval-only.  */
-  hash_set<tree> visited;
-  return !!cp_walk_tree (&t, consteval_only_type_r, &visited, &visited);
+  hash_set<tree> class_set;
+  return consteval_only_p_1 (t, class_set).is_true ();
+}
+
+/* A cache of the boolean result of consteval_only_p_1 for class types, when
+   the result is known.  */
+
+static GTY((cache)) type_tree_cache_map *consteval_only_class_cache;
+
+/* Recursive workhorse of consteval_only_p.  Returns true if T is definitely
+   consteval-only, false if it's definitely not, and unknown if we saw an
+   incomplete type and therefore don't know.  CLASS_SET is the set of class types
+   we're recursively inside.  */
+
+static tristate
+consteval_only_p_1 (tree t, hash_set<tree>& class_set)
+{
+  t = TYPE_MAIN_VARIANT (t);
+
+  if (REFLECTION_TYPE_P (t))
+    return true;
+  else if (INDIRECT_TYPE_P (t))
+    return consteval_only_p_1 (TREE_TYPE (t), class_set);
+  else if (TREE_CODE (t) == ARRAY_TYPE)
+    return consteval_only_p_1 (TREE_TYPE (t), class_set);
+  else if (FUNC_OR_METHOD_TYPE_P (t))
+    {
+      tristate r = consteval_only_p_1 (TREE_TYPE (t), class_set);
+      for (tree parm = TYPE_ARG_TYPES (t);
+	   parm != NULL_TREE && parm != void_list_node;
+	   parm = TREE_CHAIN (parm))
+	{
+	  r = r || consteval_only_p_1 (TREE_VALUE (parm), class_set);
+	  if (r.is_true ())
+	    break;
+	}
+      return r;
+    }
+  else if (RECORD_OR_UNION_TYPE_P (t))
+    {
+      if (tree *slot = hash_map_safe_get (consteval_only_class_cache, t))
+	return *slot == boolean_true_node;
+
+      if (class_set.add (t))
+	/* Handle struct A { A* p; } etc.  */
+	return false;
+
+      tristate r = COMPLETE_TYPE_P (t) ? false : tristate::unknown ();
+      for (tree member = TYPE_FIELDS (t); member; member = DECL_CHAIN (member))
+	if (TREE_CODE (member) == FIELD_DECL)
+	  {
+	    r = r || consteval_only_p_1 (TREE_TYPE (member), class_set);
+	    if (r.is_true ())
+	      break;
+	  }
+
+      if (COMPLETE_TYPE_P (t))
+	{
+	  if (r.is_true ())
+	    hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
+				       t, boolean_true_node);
+	  else if (r.is_false ())
+	    hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
+				       t, boolean_false_node);
+	}
+
+      class_set.remove (t);
+      return r;
+    }
+  else if (TYPE_PTRMEM_P (t))
+    return (consteval_only_p_1 (TYPE_PTRMEM_CLASS_TYPE (t), class_set)
+	    || consteval_only_p_1 (TYPE_PTRMEM_POINTED_TO_TYPE (t), class_set));
+  else
+    return false;
 }
 
 /* A walker for check_out_of_consteval_use_r.  It cannot be a lambda, because
