[v3] c++/reflection: P4101, Consteval-only values [PR125820]

Message ID apdkKMN7YKVkIlu2@redhat.com
State New
Headers
Series [v3] c++/reflection: P4101, Consteval-only values [PR125820] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap success Build passed
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap success Build passed

Commit Message

Marek Polacek Sept. 1, 2026, 11:47 p.m. UTC
  On Thu, Aug 27, 2026 at 02:39:30PM -0700, Jason Merrill wrote:
> On 8/27/26 4:45 PM, Marek Polacek wrote:
> > On Tue, Aug 25, 2026 at 03:11:21PM -0700, Jason Merrill wrote:
> > > On 8/25/26 12:56 PM, Marek Polacek wrote:
> > > > On Mon, Aug 24, 2026 at 03:20:33PM -0700, Jason Merrill wrote:
> > > > > On 8/23/26 6:08 PM, Marek Polacek wrote:
> > > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > > > > > 
> > > > > > -- >8 --
> > > > > > <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p4101r1.html>
> > > > > > changes the consteval-only type model to a consteval-only values model.
> > > > > > A null reflection is no longer consteval-only.
> > > > > > 
> > > > > > So, for instance, this is now OK:
> > > > > > 
> > > > > >      std::meta::info i;
> > > > > >      auto foo (std::meta::info i) { return i; } // not consteval
> > > > > > 
> > > > > > but
> > > > > > 
> > > > > >      std::meta::info type = ^^int;
> > > > > > 
> > > > > > is still ill-formed, provided it's not in an immediate function context
> > > > > > 
> > > > > > I suppose for users the change is not that dramatic, but our
> > > > > > implementation had to change quite a bit.
> > > > > > 
> > > > > > For one thing, we now need to be able to handle null reflections in the
> > > > > > ME.  This is done by removing META_TYPE and using LANG_TYPE instead.
> > > > > > In dwarf2out, LANG_TYPE already maps to DW_TAG_unspecified_type
> > > > > > so we should get "decltype(^^int)" in debug info.
> > > > > > 
> > > > > > To be able to detect invalid code like the bare:
> > > > > > 
> > > > > >      ^^int;
> > > > > > 
> > > > > > in cp_fold_*, and to allow code like:
> > > > > > 
> > > > > >      int i = (^^int, 42); // namespace scope
> > > > > > 
> > > > > > convert_to_void no longer throws away discarded-value expressions.  But
> > > > > > the modules and analyzer code sort of depended on these being
> > > > > > discarded.  Obviously, first we have to check for consteval-only values
> > > > > > and only then can we discard them.  But we need to do the discarding
> > > > > > before maybe_save_constexpr_fundef; otherwise we'd have to add a separate
> > > > > > walk for the copy.  I moved all of this into cp_fold_r.
> > > > > > 
> > > > > > As mentioned above, a null reflection can get into the ME, but REFLECT_EXPR
> > > > > > still can't.  rewrite_null_reflection rewrites REFLECT_EXPRs representing
> > > > > > a null reflection to zero.
> > > > > > 
> > > > > > Taking the address of a consteval function is no longer prohibited outside
> > > > > > an immediate function context, provided the result initializes a constexpr
> > > > > > variable.  I added a FIXME for this; I didn't want to inflate the patch
> > > > > > with testsuite changes for all the consteval tests that will need to be
> > > > > > updated once the FIXME is resolved.
> > > > > 
> > > > > What's your plan for that?  It seems wrong to bump the __cpp_consteval value
> > > > > until then.
> > > > 
> > > > The plan was to work on this once this patch has been pushed.  I hope
> > > > it's going to be a small change in constexpr.cc.
> > > > 
> > > > You're right about the __cpp_consteval change; I've dropped it for now.
> > > > 
> > > > > > +/* True if T is a consteval-only value as per [expr.const.const]/1:
> > > > > > +   A consteval-only value is either
> > > > > > +   -- a reflection value that is not the null reflection value or
> > > > > > +   -- a pointer or pointer-to-member that points to an immediate function
> > > > > > +      or to or past the end of an immediate object.
> > > > > > +   This function doesn't look for an immediate function; for that, see
> > > > > > +   find_immediate_fndecl.  */
> > > > > 
> > > > > ...in that followup, I'd expect this to change.
> > > > 
> > > > I wasn't going to mesh them together, actually.  find_immediate_fndecl is
> > > > called only in _eval_outermost_, needs a tree walk, and finds a tree instead of
> > > > being a yes/no predicate.  But this function is called from many other
> > > > places where we no longer care about immediate functions and so checking
> > > > for them would slow us down.
> > > 
> > > Why would it slow us down?  It seems like a cheap additional check.
> 
> Note that I wasn't thinking to call find_immediate_fndecl here, just return
> true for a pointer to consteval function.

Aha.  That could work, I'll have to see what it does.

> > Even if it didn't, I'm still unsure if it's OK to emit the consteval-only
> > errors in _eval_outermost_.  I guess it needs experimenting.
> > 
> > > > > > +/* Return true if T is an immediate object as per [expr.const.const]/2:
> > > > > > +   An object is an immediate object if its complete object has
> > > > > > +   -- a constituent value that is consteval-only or
> > > > > > +   -- a constituent reference that refers to an immediate object or
> > > > > > +   immediate function.
> > > > > > +
> > > > > > +   This checks the first bullet.  */
> > > > > > +
> > > > > > +static bool
> > > > > > +consteval_only_p (tree t, hash_set<tree> &seen)
> > > > > 
> > > > > The comment seems somewhat misleading, as the function also returns true for
> > > > > a consteval-only value.  And it ought to check the second bullet; I would
> > > > > expect that to already be checked along with pointers in the previous
> > > > > function.
> > > > 
> > > > True, I changed it to "This doesn't check for immediate functions".
> 
> Thanks, but it still doesn't mention that the function returns true for
> consteval-only values.

Adjusted again.

> > > > > > @@ -1136,6 +1136,41 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
> > > > > >       /* Handle aggregate NSDMI in non-constant initializers, too.  */
> > > > > >       value = replace_placeholders (value, decl);
> > > > > > +  /* Detect stuff like 'info r = ^^int;' outside a manifestly
> > > > > > +     constant-evaluated context.  */
> > > > > > +  if (flag_reflection
> > > > > > +      && !processing_template_decl
> > > > > > +      && !DECL_DECLARED_CONSTEXPR_P (decl))
> > > > > > +    {
> > > > > > +      bool bad = check_out_of_consteval_use (value, /*complain=*/false);
> > > > > > +      /* A non-constexpr variable at namespace scope with a constant
> > > > > > +	 initializer has constant initialization, so we check the folded
> > > > > > +	 value not to wrongly reject "int e = (^^int, 42);".  For non-static
> > > > > > +	 local variables, there is no such rule, so we check the unfolded
> > > > > > +	 initializer.  But we should also reject
> > > > > > +
> > > > > > +	   consteval auto fn () { return ^^int; }
> > > > > > +	   void g() { auto r = fn (); }
> > > > > > +
> > > > > > +	 so we may have to check both.  Note that the first call could
> > > > > > +	 have escalated and so we may find ourselves in an immediate
> > > > > > +	 context now.  */
> > > > > 
> > > > > I don't think we need to check both; we should check one or the other
> > > > > depending on DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P.
> > > > 
> > > > I don't know if I could do away with the second call.  I want to give
> > > > an error for `r` in the comment where it is DECL_INITIALIZED_BY_C_E_P
> > > > and I have to check value to see the REFLECT_EXPR.  But in
> > > > 
> > > >     void g() { int e = (^^int, 42); }
> > > > 
> > > > `e` is also DECL_INITIALIZED_BY_C_E_P but I have to check init
> > > > to find that ^^int because value is just 42.
> > > 
> > > Hmm, true, I was thinking it was only set just above for MCE initializer.
> > > 
> > > And looking at https://eel.is/c++draft/expr.const#init-4.1 I see that it's
> > > even considered constant-initialized.  And that the first note there lies
> > > about is_constant_evaluated; I'll email core about that.
> > > 
> > > So I think what we want is "usable in constant expressions", i.e.
> > > decl_constant_var_p.
> > 
> > I don't really understand.  Even if I keep those two out_of_consteval
> > calls, replacing DECL_INITIALIZED_BY_C_E_P with decl_constant_var_p
> > doesn't work for e.g.
> > 
> >    int i = (^^int, 42);
> > 
> > at namespace scope, where i is DECL_INITIALIZED_BY_C_E_P but not
> > decl_constant_var_p.  We can't check the unfolded value here.
> 
> Ah, right, we need "usable in constant expressions or has constant
> initialization" (https://eel.is/c++draft/expr.const#defns-1.6).
> 
> So decl_constant_var_p or static && BY_C_E_P.

OK, that does look like the right condition, changed.

...except it still doesn't avoid the need to possibly call
out_of_consteval the second time too for the consteval example:

  consteval auto fn () { return ^^int; }
  void g() { auto r = fn (); }

`r` isn't decl_constant_var_p and not static so no MCE.  But
the unfolded value is a CALL_EXPR so we wouldn't detect the
"not associated with a constexpr var" problem.  I suppose I could
detect it later in cp_fold_* where we'll see this DECL_EXPR:

  r = REFLECT_EXPR<int>;

but that would mean duplicating the diagnostic and I didn't want to
do that.  And another twist is that if `g` is constexpr, we don't
eval the fn() call here, and only detect the problem in
cp_fold_immediate_r ("If we called a consteval function...") which
gets

  r = fn();


...and another problem is that decl_constant_var_p instantiates
and as a result I'm getting errors like:

In file included from /home/polacek/x/trunk/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu/bits/stdc++.h:66,
                 from <command-line>:
/home/polacek/x/trunk/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/ratio: In instantiation of 'const intmax_t std::__ratio_multiply<std::ratio<604800>, std::ratio<1, 86400> >::__gcd1':
[...]
/home/polacek/x/trunk/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/bits/chrono.h:849:   in 'constexpr' expansion of 'std::chrono::duration<long int, std::ratio<86400> >((* & __lhs))'
/home/polacek/x/trunk/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/ratio:335: fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum)
compilation terminated.

so changed to _maybe_ like elsewhere in the function.

> > Deciding whether to call out_of_consteval based solely on
> > decl_constant_var_p also doesn't work due to the issue described
> > above.  So I kept this as it was.
> > 
> > > > > > @@ -2662,10 +2662,6 @@ maybe_make_one_only (tree decl)
> > > > > >      if (! flag_weak)
> > > > > >        return;
> > > > > > -  /* These are not to be output.  */
> > > > > > -  if (consteval_only_p (decl))
> > > > > > -    return;
> > > > > > -
> > > > > >      /* We can't set DECL_COMDAT on functions, or cp_finish_file will think
> > > > > >         we can get away with not emitting them if they aren't used.  We need
> > > > > >         to for variables so that cp_finish_decl will update their linkage,
> > > > > > @@ -2822,10 +2818,6 @@ var_finalized_p (tree var)
> > > > > >    void
> > > > > >    mark_needed (tree decl)
> > > > > >    {
> > > > > > -  /* These are not to be output.  */
> > > > > > -  if (consteval_only_p (decl))
> > > > > > -    return;
> > > > > 
> > > > > Why are these no longer necessary for immediate objects?  Maybe they should
> > > > > be checking_asserts?
> > > > 
> > > > Turns out they have no effect even without this patch.  In fact I went back
> > > > to r16-6808 and even there they seem to be meaningless.  I've replaced them
> > > > with checking_asserts and reflect/ still passes.
> > > Let's keep those asserts, then.
> > 
> > Done.
> > 
> > > New comments:
> > > 
> > > > @@ -12393,6 +12363,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
> > > >                  return false;
> > > >                }
> > > >            }
> > > > +       /* convert_to_void used to fold these away to void_node, because they
> > > > +          are a discarded-value expression.  We don't have vc_discard here
> > > > +          so do not recurse.  */
> > > > +       if (TREE_CODE (t) == CONVERT_EXPR
> > > > +           && VOID_TYPE_P (TREE_TYPE (t))
> > > > +           && !TREE_SIDE_EFFECTS (from))
> > > > +         return true;
> > > >           return (RECUR (from, TREE_CODE (t) != VIEW_CONVERT_EXPR));
> > > >         }
> > > 
> > > This seems wrong; we don't have vc_discard but we can pass false to
> > > want_rval for the same effect, which we also want for the case where from
> > > does have side-effects.
> > > 
> > > Making that change seems to produce errors in concepts-uneval3.C
> > > (potential_... and cxx_eval... don't agree about P2280, and this isn't
> > > actually allowed by P2280 anyway) and reflect/parm4.C (correct error
> > > regardless of -fimplicit-constexpr)
> > 
> > Ah, I had a RECUR with want_rval=false there but changed it to
> > return true because of concepts-uneval3.C.  But if the error is
> > right, great.
> > 
> > So changed to RECUR and adjusted the tests.
> > 
> > > > @@ -1786,6 +1756,49 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
> > > > +    case CONVERT_EXPR:
> > > > +      /* convert_to_void used to fold these away to void_node.  Do it now;
> > > > +        other code (e.g., trees_out) depends on these being expunged.  We
> > > > +        do it here before maybe_save_constexpr_fundef copies function
> > > > +        bodies.  */
> > > > +      if ((data->flags & (ff_only_non_odr | ff_genericize))
> > > > +         && VOID_TYPE_P (TREE_TYPE (stmt))
> > > > +         && !TREE_SIDE_EFFECTS (stmt))
> > > > +       {
> > > > +        *stmt_p = void_node;
> > > > +        *walk_subtrees = 0;
> > > > +        return NULL_TREE;
> > > > +       }
> > > > +      break;
> > > 
> > > Thinking about ff_only_non_odr:
> > > 
> > > As I understand it, the point of that folding is to remove constant
> > > variables used for their values under
> > > https://eel.is/c++draft/basic.link#14.4 and
> > > https://eel.is/c++draft/basic.def.odr#5.2.2 .
> > > 
> > > Notably the exposure wording only applies to variables usable in constant
> > > expressions, not variables in general.
> > > 
> > > But the wording in https://eel.is/c++draft/basic.def.odr#5.2.1 says that
> > > discarded-value naming of any variable is not an odr-use, so the name
> > > "ff_only_non_odr" suggests they should be folded away.
> > > 
> > > Also, under https://eel.is/c++draft/expr#prim.lambda.capture-11
> > > discarded-value naming of a local variable is not transformed into an access
> > > to the capture, so we should still optimize away the capture of a variable
> > > that is discarded.  mark_discarded_use seems to handle this by replacing it
> > > with its value like an rvalue conversion.  But that doesn't handle
> > > non-constant variables, so e.g. this fails:
> > > 
> > > void f()
> > > {
> > >    int i;
> > >    // i is implicitly captured, but we can optimize that away since it's
> > >    // not odr-used.
> > >    auto l = [=]{ return (i,42); };
> > >    static_assert (sizeof (l) == 1); // fails currently
> > > }
> > 
> > Ah, another problem with the comma op.  Note that my patch has an XFAIL
> > for
> > 
> >    const int &ref = (r, 42);
> > 
> > where the initializer is folded to 42 and we never detect the wrong
> > use of r.  Maybe grok_reference_init needs to also call
> > out_of_consteval, but that might result in duplicated errors.  I left
> > this unresolved.
> > 
> > > ...so I guess leave this hunk as is for now.
> > 
> > OK, kept as-is.  But we should have a PR with the test
> > and reasoning above.
> > 
> > I don't think I had a lot of options wrt where to place the
> > discarding and the out-of-consteval detection.
> > 
> > 
> > I've added a few more tests to value1.C in this version.
> > 
> > 
> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > index 967ba3fd542..c125c3c6776 100644
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > @@ -6160,6 +6160,10 @@ cp_parser_splice_specifier (cp_parser *parser, bool template_p = false,
> >         return error_mark_node;
> >       }
> > +  /* Force immediate context so that consteval-only values are OK.  */
> > +  in_consteval_if_p_temp_override icip;
> > +  in_consteval_if_p = true;
> 
> Is this still needed?  I don't see failures if I remove it.

Huh, doesn't look it it's needed anymore.  Nice.

> > --- a/gcc/testsuite/g++.dg/cpp2a/concepts-uneval3.C
> > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-uneval3.C
> > @@ -3,7 +3,7 @@
> >   template <class T>
> >   struct A {
> > -  void f() requires (this, true) { }
> > +  void f() requires (this, true) { }	// { dg-error "satisfaction value of atomic constraint" }
> 
> So yes, this is ill-formed, but the actual diagnostic is wrong; to get a
> better diagnostic we also need:
> 
> > @@ -2550,7 +2550,10 @@ satisfy_atom (tree t, tree args, sat_info info)
> >    if (info.noisy ())
> >      {
> >        iloc_sentinel ils (EXPR_LOCATION (result));
> > -      result = cxx_constant_value (result);
> > +      if (require_constant_expression (result))
> > +       result = cxx_constant_value (result);
> > +      else
> > +       result = error_mark_node;
> >      }
> >    else
> >      {
> 
> so we go through potential_constant_expression on both noisy/quiet paths.

Added.

> > @@ -1898,6 +1898,10 @@ wide_int_to_tree_1 (tree type, const wide_int_ref &pcst)
> >   	case ENUMERAL_TYPE:
> >   	  break;
> > +	case LANG_TYPE:
> > +	  /* This represents the C++ std::meta::info type.  */
> 
> Let's add this case next to NULLPTR_TYPE, and say that it /could/ be info,
> not that it necessarily is.

Done.

> > +	  break;
> > +
> >   	default:
> >   	  gcc_unreachable ();
> >   	}
> > @@ -2104,6 +2108,10 @@ cache_integer_cst (tree t, bool might_duplicate ATTRIBUTE_UNUSED)
> >   	 members.  */
> >         break;
> > +    case LANG_TYPE:
> > +      /* This represents the C++ std::meta::info type.  */
> 
> Likewise.

Done.

> > @@ -7141,6 +7141,8 @@ count_type_elements (const_tree type, bool for_ctor_p)
> >       case NULLPTR_TYPE:
> >       case OPAQUE_TYPE:
> >       case BITINT_TYPE:
> > +    /* This represents the C++ std::meta::info type.  */
> > +    case LANG_TYPE:
> >         return 1;
> 
> And here.

And done.

The new test mangle10.C was failing when I rebased my patch.
Fixed by making null_reflection_p return true for the rewritten
0 form too.

To fix a crash in reflect/init11.C on armv8, cp_fold_r/STATEMENT_LIST
will no longer get rid of the whole BIND_EXPR; it only wipes it.
Verified the fix works with a cross.

Bootstrapped/regtested on x86_64-pc-linux-gnu and ppc64-linux-gnu,
ok for trunk?

-- >8 --
<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p4101r1.html>
changes the consteval-only type model to a consteval-only values model.
A null reflection is no longer consteval-only.

So, for instance, this is now OK:

  std::meta::info i;
  auto foo (std::meta::info i) { return i; } // not consteval

but

  std::meta::info type = ^^int;

is still ill-formed, provided it's not in an immediate function context

I suppose for users the change is not that dramatic, but our
implementation had to change quite a bit.

For one thing, we now need to be able to handle null reflections in the
ME.  This is done by removing META_TYPE and using LANG_TYPE instead.
In dwarf2out, LANG_TYPE already maps to DW_TAG_unspecified_type
so we should get "decltype(^^int)" in debug info.

To be able to detect invalid code like the bare:

  ^^int;

in cp_fold_*, and to allow code like:

  int i = (^^int, 42); // namespace scope

convert_to_void no longer throws away discarded-value expressions.  But
the modules and analyzer code sort of depended on these being
discarded.  Obviously, first we have to check for consteval-only values
and only then can we discard them.  But we need to do the discarding
before maybe_save_constexpr_fundef; otherwise we'd have to add a separate
walk for the copy.  I moved all of this into cp_fold_r.

As mentioned above, a null reflection can get into the ME, but REFLECT_EXPR
still can't.  rewrite_null_reflection rewrites REFLECT_EXPRs representing
a null reflection to zero.

Taking the address of a consteval function is no longer prohibited outside
an immediate function context, provided the result initializes a constexpr
variable.  I didn't want to inflate the patch with testsuite changes for all
the consteval tests that will need to be updated once this is resolved.

	PR c++/125820

gcc/cp/ChangeLog:

	* constexpr.cc (cxx_eval_outermost_constant_expr): Delete
	consteval-only smuggling detection code.
	(potential_constant_expression_1) <case CONVERT_EXPR>: Recurse
	with want_rval=false for discarded-value expressions.
	* constraint.cc (satisfy_atom): Check require_constant_expression
	before calling cxx_constant_value.  Set result to error_mark_node
	otherwise.
	* cp-gimplify.cc (cp_gimplify_expr) <case REFLECT_EXPR>: New,
	call rewrite_null_reflection on it.
	<case CALL_EXPR>: Don't call consteval_only_p.
	(wipe_consteval_only_r): Remove.
	(cp_fold_immediate_r): Don't detect invalid uses of consteval-only
	types here.
	<case IF_STMT>: Don't call wipe_consteval_only_r.
	(cp_fold_r) <case RETURN_EXPR, EXPR_STMT, STATEMENT_LIST>: New,
	detect invalid uses of consteval-only values here.
	<case CONVERT_EXPR>: New, remove discarded-value expressions.
	(cp_genericize_r) <case BIND_EXPR>: Call rewrite_null_reflection.
	Don't walk BLOCK_VARS here.
	* cp-objcp-common.cc (cp_common_init_ts): Remove the META_TYPE
	marking.
	* cp-tree.def (META_TYPE): Remove.
	* cp-tree.h (REFLECTION_TYPE_P): Adjust to check if TYPE is
	meta_info_type_node.
	(rewrite_null_reflection): Declare.
	(consteval_only_p): No longer pure.
	(check_consteval_only_fn): Remove.
	* cvt.cc (convert_to_void): Don't call check_out_of_consteval_use
	here.  Don't remove discarded-value expressions here.
	* cxx-pretty-print.cc (cxx_pretty_printer::simple_type_specifier)
	<case META_TYPE>: Remove.
	<case LANG_TYPE>: New.
	(cxx_pretty_printer::type_id) <case META_TYPE>: Remove.
	<case LANG_TYPE>: New.
	* decl.cc (wrapup_namespace_globals): Call
	rewrite_null_reflection on each element of statics.
	(cp_finish_decl): Don't call check_out_of_consteval_use.  Don't
	check consteval_only_p.
	(grokfndecl): Don't call check_consteval_only_fn.
	* decl2.cc (maybe_make_one_only): Don't return early for
	consteval_only_p.
	(mark_needed): Likewise.
	(prune_vars_needing_no_initialization): Don't prune
	consteval_only_p variables.
	(c_parse_final_cleanups): Call rewrite_null_reflection.
	* error.cc (dump_type) <case LANG_TYPE>: Handle
	REFLECTION_TYPE_P.
	<case META_TYPE>: Remove.
	(dump_type_prefix) <case META_TYPE>: Remove.
	(dump_type_suffix) <case META_TYPE>: Remove.
	(dump_expr): Use REFLECTION_TYPE_P.
	* init.cc (perform_member_init): Don't call
	check_out_of_consteval_use.
	* mangle.cc (write_type) <case META_TYPE>: Remove.
	<case LANG_TYPE>: Handle REFLECTION_TYPE_P.
	* module.cc (trees_out::type_node) <case META_TYPE>: Remove.
	<case LANG_TYPE>: New.
	(trees_in::tree_node) <case META_TYPE>: Remove.
	<case LANG_TYPE>: New.
	* name-lookup.cc (name_lookup::adl_type): Move the
	REFLECTION_TYPE_P handling to case LANG_TYPE.
	* pt.cc (tsubst) <case META_TYPE>: Remove.
	(unify) <case META_TYPE>: Remove.
	<case LANG_TYPE>: New.
	(instantiate_body): Don't call check_consteval_only_fn.
	* reflect.cc (init_reflection): Use LANG_TYPE instead of
	META_TYPE for meta_info_type_node.
	(null_reflection_p): Also accept the rewritten form.
	(rewrite_null_reflection): New.
	(consteval_only_p): Rewrite for P4101.
	(struct consteval_only_p_walker): Remove.
	(consteval_only_value_p): New.
	(consteval_only_p_walker::walk): Remove.
	(check_out_of_consteval_use_r): Walk INIT_EXPR.  Handle EXPR_STMT
	specially.
	(check_out_of_consteval_use): Remove the special VAR_P handling.
	(check_consteval_only_fn): Remove.
	* search.cc (check_final_overrider): Don't check
	consteval_only_p.
	* semantics.cc (cp_build_bit_cast): Likewise.
	* tree.cc (type_has_unique_obj_representations) <case LANG_TYPE>:
	New, handle REFLECTION_TYPE_P.
	* typeck.cc (cp_build_binary_op): Use REFLECTION_TYPE_P.
	(check_return_expr): Don't call check_out_of_consteval_use.
	* typeck2.cc (store_init_value): Check that an immediate object
	is associated with a constexpr variable.

gcc/ChangeLog:

	* expr.cc (count_type_elements) <case LANG_TYPE>: Return 1.
	* tree.cc (wide_int_to_tree_1) <case LANG_TYPE>: New.
	(cache_integer_cst) <case LANG_TYPE>: New.

libstdc++-v3/ChangeLog:

	* include/std/meta (std::meta::exception::what): Change it to
	constexpr.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-uneval3.C: Add a dg-error.
	* g++.dg/reflect/bit_cast.C: Remove dg-error.  Extend.
	* g++.dg/reflect/crash12.C: Remove dg-error.
	* g++.dg/reflect/crash18.C: Likewise.
	* g++.dg/reflect/diag3.C: Adjust expected output.
	* g++.dg/reflect/expr11.C: Likewise.
	* g++.dg/reflect/expr12.C: Likewise.
	* g++.dg/reflect/init10.C: Likewise.
	* g++.dg/reflect/init11.C: Likewise.
	* g++.dg/reflect/init12.C: Remove dg-error.
	* g++.dg/reflect/init16.C: Adjust expected output.
	* g++.dg/reflect/init19.C: Remove dg-error.
	* g++.dg/reflect/init4.C: Add an XFAIL.
	* g++.dg/reflect/init5.C: Remove dg-error.
	* g++.dg/reflect/init6.C: Adjust expected output.
	* g++.dg/reflect/init7.C: Likewise.
	* g++.dg/reflect/init9.C: Likewise.
	* g++.dg/reflect/override1.C: Likewise.
	* g++.dg/reflect/p2996-15.C: Remove dg-error.
	* g++.dg/reflect/parm3.C: Adjust expected output.
	* g++.dg/reflect/parm4.C: Likewise.
	* g++.dg/reflect/pr124012.C: Remove dg-error.
	* g++.dg/reflect/type12.C: Likewise.
	* g++.dg/reflect/type2.C: Adjust expected output.
	* g++.dg/reflect/bit_cast2.C: New test.
	* g++.dg/reflect/expr19.C: New test.
	* g++.dg/reflect/type_trait19.C: New test.
	* g++.dg/reflect/typeinfo1.C: New test.
	* g++.dg/reflect/value1.C: New test.
	* g++.dg/reflect/value2.C: New test.
	* g++.dg/reflect/value3.C: New test.
	* g++.dg/reflect/value4.C: New test.
	* g++.dg/reflect/value5.C: New test.
	* g++.dg/reflect/value6.C: New test.
	* g++.dg/reflect/value7.C: New test.
	* g++.dg/reflect/vector2.C: New test.
---
 gcc/cp/constexpr.cc                           |  37 +-
 gcc/cp/constraint.cc                          |   5 +-
 gcc/cp/cp-gimplify.cc                         | 111 ++--
 gcc/cp/cp-objcp-common.cc                     |   1 -
 gcc/cp/cp-tree.def                            |   3 -
 gcc/cp/cp-tree.h                              |   7 +-
 gcc/cp/cvt.cc                                 |  10 -
 gcc/cp/cxx-pretty-print.cc                    |  19 +-
 gcc/cp/decl.cc                                |  23 +-
 gcc/cp/decl2.cc                               |  20 +-
 gcc/cp/error.cc                               |  14 +-
 gcc/cp/init.cc                                |   3 -
 gcc/cp/mangle.cc                              |  11 +-
 gcc/cp/module.cc                              |   8 +-
 gcc/cp/name-lookup.cc                         |  14 +-
 gcc/cp/pt.cc                                  |   5 +-
 gcc/cp/reflect.cc                             | 283 +++++----
 gcc/cp/search.cc                              |  10 +-
 gcc/cp/semantics.cc                           |   6 -
 gcc/cp/tree.cc                                |  10 +
 gcc/cp/typeck.cc                              |   8 +-
 gcc/cp/typeck2.cc                             |  36 ++
 gcc/expr.cc                                   |   3 +-
 gcc/testsuite/g++.dg/cpp2a/concepts-uneval3.C |   2 +-
 gcc/testsuite/g++.dg/reflect/bit_cast.C       |  20 +-
 gcc/testsuite/g++.dg/reflect/bit_cast2.C      |  12 +
 gcc/testsuite/g++.dg/reflect/crash12.C        |   2 +-
 gcc/testsuite/g++.dg/reflect/crash18.C        |   2 +-
 gcc/testsuite/g++.dg/reflect/diag3.C          |  18 +-
 gcc/testsuite/g++.dg/reflect/expr11.C         |  20 +-
 gcc/testsuite/g++.dg/reflect/expr12.C         |  14 +-
 gcc/testsuite/g++.dg/reflect/expr19.C         |   8 +
 gcc/testsuite/g++.dg/reflect/init10.C         |   8 +-
 gcc/testsuite/g++.dg/reflect/init11.C         |   5 +-
 gcc/testsuite/g++.dg/reflect/init12.C         |   2 +-
 gcc/testsuite/g++.dg/reflect/init16.C         |   4 +-
 gcc/testsuite/g++.dg/reflect/init19.C         |   2 +-
 gcc/testsuite/g++.dg/reflect/init4.C          |   2 +-
 gcc/testsuite/g++.dg/reflect/init5.C          |   4 +-
 gcc/testsuite/g++.dg/reflect/init6.C          |  12 +-
 gcc/testsuite/g++.dg/reflect/init7.C          |  22 +-
 gcc/testsuite/g++.dg/reflect/init9.C          |  22 +-
 gcc/testsuite/g++.dg/reflect/override1.C      |   8 +-
 gcc/testsuite/g++.dg/reflect/p2996-15.C       |   4 +-
 gcc/testsuite/g++.dg/reflect/parm3.C          |  12 +-
 gcc/testsuite/g++.dg/reflect/parm4.C          |  22 +-
 gcc/testsuite/g++.dg/reflect/pr124012.C       |   8 +-
 gcc/testsuite/g++.dg/reflect/type12.C         |   2 +-
 gcc/testsuite/g++.dg/reflect/type2.C          |  14 +-
 gcc/testsuite/g++.dg/reflect/type_trait19.C   |  15 +
 gcc/testsuite/g++.dg/reflect/typeinfo1.C      |   7 +
 gcc/testsuite/g++.dg/reflect/value1.C         | 543 ++++++++++++++++++
 gcc/testsuite/g++.dg/reflect/value2.C         |  32 ++
 gcc/testsuite/g++.dg/reflect/value3.C         |  63 ++
 gcc/testsuite/g++.dg/reflect/value4.C         |  24 +
 gcc/testsuite/g++.dg/reflect/value5.C         |  40 ++
 gcc/testsuite/g++.dg/reflect/value6.C         |  41 ++
 gcc/testsuite/g++.dg/reflect/value7.C         |  52 ++
 gcc/testsuite/g++.dg/reflect/vector2.C        |  26 +
 gcc/tree.cc                                   |   4 +
 libstdc++-v3/include/std/meta                 |   2 +-
 61 files changed, 1306 insertions(+), 441 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/reflect/bit_cast2.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/expr19.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/type_trait19.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/typeinfo1.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/value1.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/value2.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/value3.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/value4.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/value5.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/value6.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/value7.C
 create mode 100644 gcc/testsuite/g++.dg/reflect/vector2.C


base-commit: fc54ab94ad257f9ef43a7287a60c21734a7a288f
  

Comments

Jason Merrill Sept. 2, 2026, 3:20 a.m. UTC | #1
On 9/1/26 7:47 PM, Marek Polacek wrote:
> On Thu, Aug 27, 2026 at 02:39:30PM -0700, Jason Merrill wrote:
>> On 8/27/26 4:45 PM, Marek Polacek wrote:
>>> On Tue, Aug 25, 2026 at 03:11:21PM -0700, Jason Merrill wrote:
>>>> On 8/25/26 12:56 PM, Marek Polacek wrote:
>>>>> On Mon, Aug 24, 2026 at 03:20:33PM -0700, Jason Merrill wrote:
>>>>>> On 8/23/26 6:08 PM, Marek Polacek wrote:
>>>>>>> @@ -1136,6 +1136,41 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
>>>>>>>        /* Handle aggregate NSDMI in non-constant initializers, too.  */
>>>>>>>        value = replace_placeholders (value, decl);
>>>>>>> +  /* Detect stuff like 'info r = ^^int;' outside a manifestly
>>>>>>> +     constant-evaluated context.  */
>>>>>>> +  if (flag_reflection
>>>>>>> +      && !processing_template_decl
>>>>>>> +      && !DECL_DECLARED_CONSTEXPR_P (decl))
>>>>>>> +    {
>>>>>>> +      bool bad = check_out_of_consteval_use (value, /*complain=*/false);
>>>>>>> +      /* A non-constexpr variable at namespace scope with a constant
>>>>>>> +	 initializer has constant initialization, so we check the folded
>>>>>>> +	 value not to wrongly reject "int e = (^^int, 42);".  For non-static
>>>>>>> +	 local variables, there is no such rule, so we check the unfolded
>>>>>>> +	 initializer.  But we should also reject
>>>>>>> +
>>>>>>> +	   consteval auto fn () { return ^^int; }
>>>>>>> +	   void g() { auto r = fn (); }
>>>>>>> +
>>>>>>> +	 so we may have to check both.  Note that the first call could
>>>>>>> +	 have escalated and so we may find ourselves in an immediate
>>>>>>> +	 context now.  */
>>>>>>
>>>>>> I don't think we need to check both; we should check one or the other
>>>>>> depending on DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P.
>>>>>
>>>>> I don't know if I could do away with the second call.  I want to give
>>>>> an error for `r` in the comment where it is DECL_INITIALIZED_BY_C_E_P
>>>>> and I have to check value to see the REFLECT_EXPR.  But in
>>>>>
>>>>>      void g() { int e = (^^int, 42); }
>>>>>
>>>>> `e` is also DECL_INITIALIZED_BY_C_E_P but I have to check init
>>>>> to find that ^^int because value is just 42.
>>>>
>>>> Hmm, true, I was thinking it was only set just above for MCE initializer.
>>>>
>>>> And looking at https://eel.is/c++draft/expr.const#init-4.1 I see that it's
>>>> even considered constant-initialized.  And that the first note there lies
>>>> about is_constant_evaluated; I'll email core about that.
>>>>
>>>> So I think what we want is "usable in constant expressions", i.e.
>>>> decl_constant_var_p.
>>>
>>> I don't really understand.  Even if I keep those two out_of_consteval
>>> calls, replacing DECL_INITIALIZED_BY_C_E_P with decl_constant_var_p
>>> doesn't work for e.g.
>>>
>>>     int i = (^^int, 42);
>>>
>>> at namespace scope, where i is DECL_INITIALIZED_BY_C_E_P but not
>>> decl_constant_var_p.  We can't check the unfolded value here.
>>
>> Ah, right, we need "usable in constant expressions or has constant
>> initialization" (https://eel.is/c++draft/expr.const#defns-1.6).
>>
>> So decl_constant_var_p or static && BY_C_E_P.
> 
> OK, that does look like the right condition, changed.
> 
> ...except it still doesn't avoid the need to possibly call
> out_of_consteval the second time too for the consteval example:
> 
>    consteval auto fn () { return ^^int; }
>    void g() { auto r = fn (); }
> 
> `r` isn't decl_constant_var_p and not static so no MCE.  But
> the unfolded value is a CALL_EXPR so we wouldn't detect the
> "not associated with a constexpr var" problem.  I suppose I could
> detect it later in cp_fold_* where we'll see this DECL_EXPR:
> 
>    r = REFLECT_EXPR<int>;
> 
> but that would mean duplicating the diagnostic and I didn't want to
> do that.  And another twist is that if `g` is constexpr, we don't
> eval the fn() call here, and only detect the problem in
> cp_fold_immediate_r ("If we called a consteval function...") which
> gets
> 
>    r = fn();

Maybe we want to delay this diagnostic until cp_fold_immediate (rather 
than duplicate it there)?
> diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
> index f5d476a4a99..8f555dd4eab 100644
> --- a/gcc/cp/cxx-pretty-print.cc
> +++ b/gcc/cp/cxx-pretty-print.cc
> @@ -1442,9 +1442,13 @@ cxx_pretty_printer::simple_type_specifier (tree t)
>         pp_cxx_trait (this, t);
>         break;
>   
> -    case META_TYPE:
> -      pp_cxx_ws_string (this, "std::meta::info");
> -      break;
> +    case LANG_TYPE:
> +      if (REFLECTION_TYPE_P (t))
> +	{
> +	  pp_cxx_ws_string (this, "std::meta::info");
> +	  break;
> +	}
> +      gcc_fallthrough ();
>   
>       default:
>         c_pretty_printer::simple_type_specifier (t);
> @@ -1925,6 +1929,13 @@ cxx_pretty_printer::type_id (tree t)
>   
>     switch (TREE_CODE (t))
>       {
> +    case LANG_TYPE:
> +      if (REFLECTION_TYPE_P (t))
> +	{
> +	  pp_cxx_type_specifier_seq (this, t);
> +	  break;
> +	}
> +      goto default_;

Why not put this just above default: like you did in simple_type_specifier?

>       case TYPE_DECL:
>       case UNION_TYPE:
>       case RECORD_TYPE:
> @@ -1942,7 +1953,6 @@ cxx_pretty_printer::type_id (tree t)
>       case NULLPTR_TYPE:
>       case TEMPLATE_ID_EXPR:
>       case OFFSET_TYPE:
> -    case META_TYPE:
>         pp_cxx_type_specifier_seq (this, t);
>         if (TYPE_PTRMEM_P (t))
>   	abstract_declarator (t);
> @@ -1976,6 +1986,7 @@ cxx_pretty_printer::type_id (tree t)
>         break;
>   
>       default:
> +    default_:
>         c_pretty_printer::type_id (t);
>         break;
>       }
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 55badc9eed9..6d08e25245d 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -1008,6 +1008,12 @@ wrapup_namespace_globals ()
>       {
>         for (tree decl : *statics)
>   	{
> +	  /* Do this here rather than e.g. in make_rtl_for_nonlocal_decl
> +	     because that would be too early; we need to keep a null
> +	     reflection as a REFLECT_EXPR for comparisons etc.  */
> +	  if (flag_reflection && DECL_INITIAL (decl))
> +	    rewrite_null_reflection (DECL_INITIAL (decl));

Don't you need to be able to compare a zero to a null reflection to 
handle zero-initialization anyway?

Jason
  

Patch

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index dbfc460d04f..9e89b709fa8 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -11277,37 +11277,6 @@  cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
       non_constant_p = true;
     }
 
-  /* Detect consteval-only smuggling: turning a consteval-only object
-     into one that is not.  For instance, in
-       struct B { };
-       struct D : B { info r; };
-       constexpr D d{^^::};
-       constexpr const B &b = d; // #1
-     #1 is wrong because D is a consteval-only type but B is not.  */
-  if (flag_reflection
-      && !non_constant_p
-      && object
-      && POINTER_TYPE_P (TREE_TYPE (object))
-      && !consteval_only_p (object)
-      && check_out_of_consteval_use (r, /*complain=*/false))
-    {
-      if (!allow_non_constant)
-	{
-	  if (TYPE_REF_P (TREE_TYPE (object)))
-	    error_at (cp_expr_loc_or_input_loc (t),
-		      "reference into an object of consteval-only type is "
-		      "not a constant expression unless it also has "
-		      "consteval-only type");
-	  else
-	    error_at (cp_expr_loc_or_input_loc (t),
-		      "pointer into an object of consteval-only type is "
-		      "not a constant expression unless it also has "
-		      "consteval-only type");
-	}
-      r = t;
-      non_constant_p = true;
-    }
-
   if (!non_constant_p && !constexpr_dtor)
     verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
 
@@ -12397,7 +12366,11 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
 		return false;
 	      }
 	  }
-        return (RECUR (from, TREE_CODE (t) != VIEW_CONVERT_EXPR));
+	/* convert_to_void used to fold these away to void_node, because they
+	   are a discarded-value expression.  */
+	if (TREE_CODE (t) == CONVERT_EXPR && VOID_TYPE_P (TREE_TYPE (t)))
+	  return RECUR (from, /*want_rval=*/false);
+	return RECUR (from, TREE_CODE (t) != VIEW_CONVERT_EXPR);
       }
 
     case ADDRESSOF_EXPR:
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index fa271a96282..180f123a16d 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2550,7 +2550,10 @@  satisfy_atom (tree t, tree args, sat_info info)
   if (info.noisy ())
     {
       iloc_sentinel ils (EXPR_LOCATION (result));
-      result = cxx_constant_value (result);
+      if (require_constant_expression (result))
+	result = cxx_constant_value (result);
+      else
+	result = error_mark_node;
     }
   else
     {
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 9dec376bec4..6333a452f04 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -798,6 +798,13 @@  cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       ret = GS_OK;
       break;
 
+    case REFLECT_EXPR:
+      /* Only the null reflection may reach the ME.  */
+      gcc_checking_assert (null_reflection_p (*expr_p));
+      rewrite_null_reflection (*expr_p);
+      ret = GS_OK;
+      break;
+
     case BASELINK:
       *expr_p = BASELINK_FUNCTIONS (*expr_p);
       ret = GS_OK;
@@ -861,8 +868,6 @@  cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
       for (int i = 0; i < call_expr_nargs (*expr_p); ++i)
 	if (check_out_of_consteval_use (CALL_EXPR_ARG (*expr_p, i)))
 	  ret = GS_ERROR;
-      if (consteval_only_p (TREE_TYPE (*expr_p)))
-	ret = GS_ERROR;
       if (flag_strong_eval_order == 2
 	  && CALL_EXPR_FN (*expr_p)
 	  && !CALL_EXPR_OPERATOR_SYNTAX (*expr_p)
@@ -1366,23 +1371,6 @@  cp_build_init_expr_for_ctor (tree call, tree init)
   return init;
 }
 
-/* For every DECL_EXPR check if it declares a consteval-only variable and
-   if so, overwrite it with a no-op.  The point here is not to leak
-   consteval-only variables into the middle end.  */
-
-static tree
-wipe_consteval_only_r (tree *stmt_p, int *, void *)
-{
-  if (TREE_CODE (*stmt_p) == DECL_EXPR)
-    {
-      tree d = DECL_EXPR_DECL (*stmt_p);
-      if (VAR_P (d) && consteval_only_p (d))
-	/* Wipe the DECL_EXPR so that it doesn't get into gimple.  */
-	*stmt_p = void_node;
-    }
-  return NULL_TREE;
-}
-
 /* A walk_tree callback for cp_fold_function and cp_fully_fold_init to handle
    immediate functions.  */
 
@@ -1410,17 +1398,6 @@  cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
       return NULL_TREE;
     }
 
-  /* Most invalid uses of consteval-only types should have been already
-     detected at this point.  And the valid ones won't be needed
-     anymore.  */
-  if (flag_reflection
-      && complain
-      && (data->flags & ff_genericize)
-      && TREE_CODE (stmt) == STATEMENT_LIST)
-    for (tree s : tsi_range (stmt))
-      if (check_out_of_consteval_use (s))
-	*stmt_p = void_node;
-
   tree decl = NULL_TREE;
   bool call_p = false;
 
@@ -1454,15 +1431,8 @@  cp_fold_immediate_r (tree *stmt_p, int *walk_subtrees, void *data_)
       if (IF_STMT_CONSTEVAL_P (stmt))
 	{
 	  if (!data->pset.add (stmt))
-	    {
-	      cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_immediate_r, data_,
-			    nullptr);
-	      if (flag_reflection)
-		/* Check & clear consteval-only DECL_EXPRs even here,
-		   because we wouldn't be walking this subtree otherwise.  */
-		cp_walk_tree (&THEN_CLAUSE (stmt), wipe_consteval_only_r,
-			      data_, nullptr);
-	    }
+	    cp_walk_tree (&ELSE_CLAUSE (stmt), cp_fold_immediate_r, data_,
+			  nullptr);
 	  *walk_subtrees = 0;
 	  return NULL_TREE;
 	}
@@ -1786,6 +1756,56 @@  cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
 	}
       break;
 
+    /* Detect consteval-only values used outside an MCE context.  */
+
+    /* A STATEMENT_LIST can be collapsed into one EXPR_STMT.  These should be
+       visited before the CONVERT_EXPR below which removes discarded-value
+       expressions.  */
+    case RETURN_EXPR:
+    case EXPR_STMT:
+      if (flag_reflection
+	  && !cp_unevaluated_operand
+	  && (data->flags & (ff_only_non_odr | ff_genericize))
+	  && check_out_of_consteval_use (stmt))
+	{
+	  if (code == RETURN_EXPR)
+	    TREE_OPERAND (stmt, 0) = error_mark_node;
+	  else
+	    *stmt_p = void_node;
+	 }
+      break;
+
+    case STATEMENT_LIST:
+      if (flag_reflection
+	  && !cp_unevaluated_operand
+	  && (data->flags & (ff_only_non_odr | ff_genericize)))
+       for (tree &s : tsi_range (stmt))
+	 if (check_out_of_consteval_use (s))
+	   {
+	     /* Don't remove the whole BIND_EXPR, just wipe it, for e.g.
+		build_constexpr_constructor_member_initializers's sake.  */
+	     if (TREE_CODE (s) == BIND_EXPR)
+	       BIND_EXPR_BODY (s) = void_node;
+	     else
+	       s = void_node;
+	   }
+      break;
+
+    case CONVERT_EXPR:
+      /* convert_to_void used to fold these away to void_node.  Do it now;
+	 other code (e.g., trees_out) depends on these being expunged.  We
+	 do it here before maybe_save_constexpr_fundef copies function
+	 bodies.  */
+      if ((data->flags & (ff_only_non_odr | ff_genericize))
+	  && VOID_TYPE_P (TREE_TYPE (stmt))
+	  && !TREE_SIDE_EFFECTS (stmt))
+       {
+	 *stmt_p = void_node;
+	 *walk_subtrees = 0;
+	 return NULL_TREE;
+       }
+      break;
+
     default:
       break;
     }
@@ -2187,17 +2207,12 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	  wtd->no_sanitize_p = no_sanitize_p;
 	}
       if (flag_reflection)
-	/* Wipe consteval-only vars from BIND_EXPR_VARS and BLOCK_VARS.  */
+	/* Adjust consteval-only vars in BIND_EXPR_VARS so that REFLECT_EXPR
+	   doesn't leak into the ME.  */
 	for (tree *p = &BIND_EXPR_VARS (stmt); *p; )
 	  {
-	    if (VAR_P (*p) && consteval_only_p (*p))
-	      {
-		if (BIND_EXPR_BLOCK (stmt)
-		    && *p == BLOCK_VARS (BIND_EXPR_BLOCK (stmt)))
-		  BLOCK_VARS (BIND_EXPR_BLOCK (stmt)) = DECL_CHAIN (*p);
-		*p = DECL_CHAIN (*p);
-		continue;
-	      }
+	    if (VAR_P (*p) && DECL_INITIAL (*p))
+	      rewrite_null_reflection (DECL_INITIAL (*p));
 	    p = &DECL_CHAIN (*p);
 	  }
       wtd->bind_expr_stack.safe_push (stmt);
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 8c9930bb2f3..36b9d743afd 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -646,7 +646,6 @@  cp_common_init_ts (void)
   MARK_TS_TYPE_NON_COMMON (TEMPLATE_TYPE_PARM);
   MARK_TS_TYPE_NON_COMMON (TYPE_PACK_EXPANSION);
   MARK_TS_TYPE_NON_COMMON (PACK_INDEX_TYPE);
-  MARK_TS_TYPE_NON_COMMON (META_TYPE);
   MARK_TS_TYPE_NON_COMMON (SPLICE_SCOPE);
 
   /* Statements.  */
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 894ba61b6dc..ea1e8241975 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -588,9 +588,6 @@  DEFTREECODE (TU_LOCAL_ENTITY, "tu_local_entity", tcc_exceptional, 0)
 /* C++26 reflection expression.  */
 DEFTREECODE (REFLECT_EXPR, "reflect_expr", tcc_expression, 1)
 
-/* Represents the std::meta::info type.  */
-DEFTREECODE (META_TYPE, "meta_type", tcc_type, 0)
-
 /* Represents a dependent splice expression.  If SPLICE_EXPR_EXPRESSION_P
    is set, this tree represents a splice-expression (as opposed to
    a splice-specifier).  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 75c00f88a08..61e44db38be 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1909,7 +1909,8 @@  struct GTY(()) tree_requires_expr {
   (((struct tree_requires_expr *) REQUIRES_EXPR_CHECK (NODE))->loc)
 
 /* True iff TYPE is cv decltype(^^int).  */
-#define REFLECTION_TYPE_P(TYPE) (TREE_CODE (TYPE) == META_TYPE)
+#define REFLECTION_TYPE_P(TYPE) \
+  (TYPE_P (TYPE) && TYPE_MAIN_VARIANT (TYPE) == meta_info_type_node)
 
 /* True if NODE is a REFLECT_EXPR.  */
 #define REFLECT_EXPR_P(NODE) (TREE_CODE (NODE) == REFLECT_EXPR)
@@ -9504,9 +9505,10 @@  extern tree process_metafunction (const constexpr_ctx *, tree, tree,
 extern tree get_reflection (location_t, tree, reflect_kind = REFLECT_UNDEF);
 extern tree get_null_reflection () ATTRIBUTE_PURE;
 extern bool null_reflection_p (const_tree) ATTRIBUTE_PURE;
+extern void rewrite_null_reflection (tree &);
 extern tree splice (tree);
 extern bool check_out_of_consteval_use (tree, bool = true);
-extern bool consteval_only_p (tree) ATTRIBUTE_PURE;
+extern bool consteval_only_p (tree);
 extern bool compare_reflections (tree, tree) ATTRIBUTE_PURE;
 extern bool valid_splice_type_p (const_tree) ATTRIBUTE_PURE;
 extern bool valid_splice_scope_p (const_tree) ATTRIBUTE_PURE;
@@ -9517,7 +9519,6 @@  extern bool check_splice_expr (location_t, location_t, tree, bool, bool, bool,
 extern tree make_splice_scope (tree, bool);
 extern bool dependent_splice_p (const_tree) ATTRIBUTE_PURE;
 extern tree reflection_mangle_prefix (tree, char [3]);
-extern void check_consteval_only_fn (tree);
 extern bool reflection_function_template_p (const_tree) ATTRIBUTE_PURE;
 extern void dump_data_member_spec (pretty_printer *, tree);
 
diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index 33d528b3c32..92e360f52ad 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -1234,14 +1234,6 @@  convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
    if (concept_check_p (expr) && !cp_unevaluated_operand)
      expr = evaluate_concept_check (expr);
 
-  /* Detect using expressions of consteval-only types outside manifestly
-     constant-evaluated contexts.  We are going to discard this expression,
-     so we can't wait till cp_fold_immediate_r.  FIXME This is too early;
-     code like "int i = (^^i, 42);" is OK.  We should stop discarding
-     expressions here (PR124249).  */
-  if (stmts_are_full_exprs_p () && check_out_of_consteval_use (expr))
-    return error_mark_node;
-
   if (VOID_TYPE_P (TREE_TYPE (expr)))
     return expr;
 
@@ -1741,8 +1733,6 @@  convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
 	}
       expr = build1 (CONVERT_EXPR, void_type_node, expr);
     }
-  if (! TREE_SIDE_EFFECTS (expr))
-    expr = void_node;
   return expr;
 }
 
diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc
index f5d476a4a99..8f555dd4eab 100644
--- a/gcc/cp/cxx-pretty-print.cc
+++ b/gcc/cp/cxx-pretty-print.cc
@@ -1442,9 +1442,13 @@  cxx_pretty_printer::simple_type_specifier (tree t)
       pp_cxx_trait (this, t);
       break;
 
-    case META_TYPE:
-      pp_cxx_ws_string (this, "std::meta::info");
-      break;
+    case LANG_TYPE:
+      if (REFLECTION_TYPE_P (t))
+	{
+	  pp_cxx_ws_string (this, "std::meta::info");
+	  break;
+	}
+      gcc_fallthrough ();
 
     default:
       c_pretty_printer::simple_type_specifier (t);
@@ -1925,6 +1929,13 @@  cxx_pretty_printer::type_id (tree t)
 
   switch (TREE_CODE (t))
     {
+    case LANG_TYPE:
+      if (REFLECTION_TYPE_P (t))
+	{
+	  pp_cxx_type_specifier_seq (this, t);
+	  break;
+	}
+      goto default_;
     case TYPE_DECL:
     case UNION_TYPE:
     case RECORD_TYPE:
@@ -1942,7 +1953,6 @@  cxx_pretty_printer::type_id (tree t)
     case NULLPTR_TYPE:
     case TEMPLATE_ID_EXPR:
     case OFFSET_TYPE:
-    case META_TYPE:
       pp_cxx_type_specifier_seq (this, t);
       if (TYPE_PTRMEM_P (t))
 	abstract_declarator (t);
@@ -1976,6 +1986,7 @@  cxx_pretty_printer::type_id (tree t)
       break;
 
     default:
+    default_:
       c_pretty_printer::type_id (t);
       break;
     }
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 55badc9eed9..6d08e25245d 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -1008,6 +1008,12 @@  wrapup_namespace_globals ()
     {
       for (tree decl : *statics)
 	{
+	  /* Do this here rather than e.g. in make_rtl_for_nonlocal_decl
+	     because that would be too early; we need to keep a null
+	     reflection as a REFLECT_EXPR for comparisons etc.  */
+	  if (flag_reflection && DECL_INITIAL (decl))
+	    rewrite_null_reflection (DECL_INITIAL (decl));
+
 	  if (warn_unused_function
 	      && TREE_CODE (decl) == FUNCTION_DECL
 	      && DECL_INITIAL (decl) == 0
@@ -9994,10 +10000,6 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	    }
 	}
 
-      /* Detect stuff like 'info r = ^^int;' outside a manifestly
-	 constant-evaluated context.  */
-      check_out_of_consteval_use (decl);
-
       /* If this is a local variable that will need a mangled name,
 	 register it now.  We must do this before processing the
 	 initializer for the variable, since the initialization might
@@ -10022,10 +10024,9 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 	      walk_tree (&init, notice_forced_label_r, NULL, NULL);
 	      add_local_decl (cfun, decl);
 	    }
-	  if (!consteval_only_p (decl))
-	    /* And make sure it's in the symbol table for
-	       c_parse_final_cleanups to find.  */
-	    varpool_node::get_create (decl);
+	  /* And make sure it's in the symbol table for
+	     c_parse_final_cleanups to find.  */
+	  varpool_node::get_create (decl);
 	}
 
       if (flag_openmp
@@ -12887,12 +12888,6 @@  grokfndecl (tree ctype,
   if (DECL_CONSTRUCTOR_P (decl) && !grok_ctor_properties (ctype, decl))
     return NULL_TREE;
 
-  /* Don't call check_consteval_only_fn for defaulted functions.  Those are
-     immediate-escalating functions but at this point DECL_DEFAULTED_P has
-     not been set.  */
-  if (initialized != SD_DEFAULTED)
-    check_consteval_only_fn (decl);
-
   if (ctype == NULL_TREE || check)
     return decl;
 
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index f3ca6cb7bdb..7fe567f98db 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -2662,9 +2662,7 @@  maybe_make_one_only (tree decl)
   if (! flag_weak)
     return;
 
-  /* These are not to be output.  */
-  if (consteval_only_p (decl))
-    return;
+  gcc_checking_assert (!consteval_only_p (decl));
 
   /* We can't set DECL_COMDAT on functions, or cp_finish_file will think
      we can get away with not emitting them if they aren't used.  We need
@@ -2822,9 +2820,7 @@  var_finalized_p (tree var)
 void
 mark_needed (tree decl)
 {
-  /* These are not to be output.  */
-  if (consteval_only_p (decl))
-    return;
+  gcc_checking_assert (!consteval_only_p (decl));
 
   TREE_USED (decl) = 1;
   if (TREE_CODE (decl) == FUNCTION_DECL)
@@ -5050,14 +5046,6 @@  prune_vars_needing_no_initialization (tree *vars)
 	  continue;
 	}
 
-      /* Reflections are consteval-only types and we don't want them
-	 to survive until gimplification.  */
-      if (consteval_only_p (decl))
-	{
-	  var = &TREE_CHAIN (t);
-	  continue;
-	}
-
       /* This variable is going to need initialization and/or
 	 finalization, so we add it to the list.  */
       *var = TREE_CHAIN (t);
@@ -6098,6 +6086,10 @@  c_parse_final_cleanups (void)
       /* Static data members are just like namespace-scope globals.  */
       FOR_EACH_VEC_SAFE_ELT (pending_statics, i, decl)
 	{
+	  /* Rewrite the REFLECT_EXPR with 0 so that the ME can process it.  */
+	  if (flag_reflection && DECL_INITIAL (decl))
+	    rewrite_null_reflection (DECL_INITIAL (decl));
+
 	  if (consteval_only_p (decl)
 	      || var_finalized_p (decl)
 	      || DECL_REALLY_EXTERN (decl)
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index a5332864bc8..3d5a194b9bc 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -697,6 +697,11 @@  dump_type (cxx_pretty_printer *pp, tree t, int flags)
 	pp_string (pp, M_("<brace-enclosed initializer list>"));
       else if (t == unknown_type_node)
 	pp_string (pp, M_("<unresolved overloaded function type>"));
+      else if (REFLECTION_TYPE_P (t))
+	{
+	  pp_cxx_ws_string (pp, "std::meta::info");
+	  pp_c_type_qualifier_list (pp, t);
+	}
       else
 	{
 	  pp_cxx_cv_qualifier_seq (pp, t);
@@ -879,11 +884,6 @@  dump_type (cxx_pretty_printer *pp, tree t, int flags)
       pp_c_type_qualifier_list (pp, t);
       break;
 
-    case META_TYPE:
-      pp_cxx_ws_string (pp, "std::meta::info");
-      pp_c_type_qualifier_list (pp, t);
-      break;
-
     case SPLICE_SCOPE:
       dump_expr (pp, SPLICE_SCOPE_EXPR (t), flags & ~TFF_EXPR_IN_PARENS);
       break;
@@ -1147,7 +1147,6 @@  dump_type_prefix (cxx_pretty_printer *pp, tree t, int flags)
     case FIXED_POINT_TYPE:
     case NULLPTR_TYPE:
     case PACK_INDEX_TYPE:
-    case META_TYPE:
     case SPLICE_SCOPE:
       dump_type (pp, t, flags);
       pp->set_padding (pp_before);
@@ -1282,7 +1281,6 @@  dump_type_suffix (cxx_pretty_printer *pp, tree t, int flags)
     case FIXED_POINT_TYPE:
     case NULLPTR_TYPE:
     case PACK_INDEX_TYPE:
-    case META_TYPE:
     case SPLICE_SCOPE:
       break;
 
@@ -3487,7 +3485,7 @@  dump_expr (cxx_pretty_printer *pp, tree t, int flags)
 		/* For reflection we care about the difference
 		   between std::meta::info/std::nullptr_t and
 		   decltype(^^int)/decltype(nullptr).  */
-		if (TREE_CODE (h) == META_TYPE && !typedef_variant_p (h))
+		if (REFLECTION_TYPE_P (h) && !typedef_variant_p (h))
 		  {
 		    pp_cxx_ws_string (pp, "decltype(^^int)");
 		    pp_c_type_qualifier_list (pp, h);
diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index 7c0c0fef55f..6bdc5fbbeb0 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -1008,9 +1008,6 @@  perform_member_init (tree member, tree init, hash_set<tree> &uninitialized)
   if (init == error_mark_node)
     return;
 
-  if (check_out_of_consteval_use (init))
-    return;
-
   /* Effective C++ rule 12 requires that all data members be
      initialized.  */
   if (warn_ecpp && init == NULL_TREE && TREE_CODE (type) != ARRAY_TYPE)
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index b2697f27c32..1f60cf45286 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -2739,11 +2739,6 @@  write_type (tree type)
 		++is_builtin_type;
 	      break;
 
-	    case META_TYPE:
-	      write_string ("Dm");
-	      ++is_builtin_type;
-	      break;
-
 	    case SPLICE_SCOPE:
 	      write_splice (type);
 	      break;
@@ -2776,6 +2771,12 @@  write_type (tree type)
 	      break;
 
 	    case LANG_TYPE:
+	      if (REFLECTION_TYPE_P (type))
+		{
+		  write_string ("Dm");
+		  ++is_builtin_type;
+		  break;
+		}
 	      /* fall through.  */
 
 	    default:
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index e61cd700d8f..c7f287bf287 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -9889,8 +9889,9 @@  trees_out::type_node (tree type)
 	}
       break;
 
-    case META_TYPE:
+    case LANG_TYPE:
       /* No additional data.  */
+      gcc_checking_assert (REFLECTION_TYPE_P (type));
       break;
 
     case SPLICE_SCOPE:
@@ -10744,7 +10745,10 @@  trees_in::tree_node (bool is_use)
 	    }
 	    break;
 
-	  case META_TYPE:
+	  /* LANG_TYPE can be more things, but here we assume it represents
+	     std::meta::info.  Unfortunately here it's not possible to check
+	     REFLECTION_TYPE_P.  */
+	  case LANG_TYPE:
 	    if (!get_overrun ())
 	      res = meta_info_type_node;
 	    break;
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 60a6e6f4430..1351f77ff0f 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -1570,13 +1570,6 @@  name_lookup::adl_type (tree type)
       adl_type (TYPE_PTRMEM_POINTED_TO_TYPE (type));
       return;
     }
-  else if (REFLECTION_TYPE_P (type))
-    {
-      /* The namespace std::meta is an associated namespace of
-	 std::meta::info.  */
-      adl_namespace (std_meta_node);
-      return;
-    }
 
   switch (TREE_CODE (type))
     {
@@ -1611,6 +1604,13 @@  name_lookup::adl_type (tree type)
       return;
 
     case LANG_TYPE:
+      if (REFLECTION_TYPE_P (type))
+	{
+	  /* The namespace std::meta is an associated namespace of
+	     std::meta::info.  */
+	  adl_namespace (std_meta_node);
+	  return;
+	}
       gcc_assert (type == unknown_type_node
 		  || type == init_list_type_node);
       return;
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index ef912d1a7a7..00fabe82e05 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17390,7 +17390,6 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case VECTOR_TYPE:
     case BOOLEAN_TYPE:
     case NULLPTR_TYPE:
-    case META_TYPE:
     case LANG_TYPE:
       return t;
 
@@ -26860,7 +26859,7 @@  unify (tree tparms, tree targs, tree parm, tree arg, int strict,
     case VOID_TYPE:
     case OPAQUE_TYPE:
     case NULLPTR_TYPE:
-    case META_TYPE:
+    case LANG_TYPE:
       if (TREE_CODE (arg) != TREE_CODE (parm))
 	return unify_type_mismatch (explain_p, parm, arg);
 
@@ -29006,8 +29005,6 @@  instantiate_body (tree pattern, tree args, tree d, bool nested_p)
       if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
 	cp_check_omp_declare_reduction (d);
 
-      check_consteval_only_fn (d);
-
       if (int errs = errorcount + sorrycount)
 	if (errs > current_tinst_level->errors)
 	  if (function *f = DECL_STRUCT_FUNCTION (d))
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 7e962fdc19e..3b38161ad7b 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -61,7 +61,7 @@  init_reflection ()
   /* The type std::meta::info is a scalar type for which equality and
      inequality are meaningful, but for which no ordering relation is
      defined.  */
-  meta_info_type_node = make_node (META_TYPE);
+  meta_info_type_node = make_node (LANG_TYPE);
   /* Make it a complete type.  */
   TYPE_SIZE (meta_info_type_node) = bitsize_int (GET_MODE_BITSIZE (ptr_mode));
   TYPE_SIZE_UNIT (meta_info_type_node) = size_int (GET_MODE_SIZE (ptr_mode));
@@ -291,8 +291,36 @@  get_null_reflection ()
 bool
 null_reflection_p (const_tree t)
 {
-  return (t && TREE_CODE (t) == REFLECT_EXPR
-	  && REFLECT_EXPR_HANDLE (t) == unknown_type_node);
+  if (!t)
+    return false;
+
+  if (REFLECT_EXPR_P (t) && REFLECT_EXPR_HANDLE (t) == unknown_type_node)
+    return true;
+
+  /* This is the rewritten form.  */
+  return integer_zerop (t) && REFLECTION_TYPE_P (TREE_TYPE (t));
+}
+
+/* If R represents a null reflection, rewrite it with something the ME
+   can process; that is, something that doesn't use REFLECT_EXPR.  */
+
+void
+rewrite_null_reflection (tree &r)
+{
+  /* A tree walk mostly only so that we recurse through CONSTRUCTORs.  */
+  auto walker = [](tree *tp, int *walk_subtrees, void *) -> tree
+    {
+      if (TYPE_P (*tp))
+	*walk_subtrees = 0;
+      else if (null_reflection_p (*tp))
+	{
+	  *tp = build_int_cst (meta_info_type_node, 0);
+	  *walk_subtrees = 0;
+	}
+      return NULL_TREE;
+    };
+
+  cp_walk_tree (&r, walker, nullptr, nullptr);
 }
 
 /* Do strip_typedefs on T, but only for types.  */
@@ -8803,153 +8831,116 @@  splice (tree refl)
   return refl;
 }
 
-/* A cache of the known boolean result of consteval_only_p_walker::walk
-   for class types.  */
+static bool consteval_only_p (tree, hash_set<tree> &);
+
+/* True if T is a consteval-only value as per [expr.const.const]/1:
+   A consteval-only value is either
+   -- a reflection value that is not the null reflection value or
+   -- a pointer or pointer-to-member that points to an immediate function
+      or to or past the end of an immediate object.
 
-static GTY((cache)) type_tree_cache_map *consteval_only_class_cache;
+   This function doesn't look for an immediate function; for that, see
+   find_immediate_fndecl.  */
 
-struct consteval_only_p_walker
+static bool
+consteval_only_value_p (tree t, hash_set<tree> &seen)
 {
-  /* The set of class types we've seen.  */
-  hash_set<tree> class_seen;
-  /* The number of class types we're recursively inside.  */
-  int class_depth = 0;
-  /* True if we've optimistically assumed an already-seen
-     consteval-unknown class type is not consteval.  */
-  bool optimistic_p = false;
+  STRIP_NOPS (t);
 
-  tristate walk (tree);
-};
+  if (REFLECT_EXPR_P (t) && !null_reflection_p (t))
+    return true;
 
-/* True if T is a consteval-only type as per [basic.types.general]/12,
-   or is a declaration with such a type, or a TREE_VEC thereof.  */
+  switch (TREE_CODE (t))
+    {
+    case ADDR_EXPR:
+    case POINTER_PLUS_EXPR:
+    case ARRAY_REF:
+    case COMPONENT_REF:
+      return consteval_only_p (TREE_OPERAND (t, 0), seen);
+    default:
+      break;
+    }
+  return false;
+}
 
-bool
-consteval_only_p (tree t)
+/* Return true if T is an immediate object as per [expr.const.const]/2:
+   An object is an immediate object if its complete object has
+   -- a constituent value that is consteval-only or
+   -- a constituent reference that refers to an immediate object or
+   immediate function.
+
+   Also return true for consteval-only values.  This doesn't check for
+   immediate functions.  */
+
+static bool
+consteval_only_p (tree t, hash_set<tree> &seen)
 {
-  if (!flag_reflection)
+  if (!flag_reflection || !t || t == error_mark_node)
     return false;
 
-  if (!TYPE_P (t))
-    t = TREE_TYPE (t);
+  /* Say that the wrapper itself is consteval-only so that
+     check_out_of_consteval_use_r returns the tree carrying the location.  */
+  STRIP_ANY_LOCATION_WRAPPER (t);
 
-  if (!t || t == error_mark_node)
+  /* Walking COMPONENT_REFs can walk back into a CONSTRUCTOR that's still
+     under construction and we'd loop.  */
+  if (seen.add (t))
     return false;
 
   if (TREE_CODE (t) == TREE_VEC)
     {
       for (tree arg : tree_vec_range (t))
-	if (arg && consteval_only_p (arg))
+	if (arg && consteval_only_p (arg, seen))
 	  return true;
       return false;
     }
 
-  /* For dependent types we can't be sure if this type is consteval-only.  */
-  if (dependent_type_p (t))
-    return false;
-
-  consteval_only_p_walker walker;
-  return walker.walk (t).is_true ();
-}
-
-/* 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.  */
-
-tristate
-consteval_only_p_walker::walk (tree t)
-{
-  if (t == error_mark_node)
-    return false;
+  /* Pull out the initializer.  Don't call fold_non_dependent_expr and
+     similar here because that could prematurely instantiate things.  */
+  if (VAR_P (t))
+    t = DECL_INITIAL (t);
 
-  t = TYPE_MAIN_VARIANT (t);
+  if (t && TREE_CODE (t) == TARGET_EXPR)
+    t = TARGET_EXPR_INITIAL (t);
 
-  if (REFLECTION_TYPE_P (t))
-    return true;
-  else if (INDIRECT_TYPE_P (t))
-    return walk (TREE_TYPE (t));
-  else if (TREE_CODE (t) == ARRAY_TYPE)
-    return walk (TREE_TYPE (t));
-  else if (FUNC_OR_METHOD_TYPE_P (t))
-    {
-      tristate r = walk (TREE_TYPE (t));
-      for (tree parm = TYPE_ARG_TYPES (t);
-	   parm != NULL_TREE && parm != void_list_node;
-	   parm = TREE_CHAIN (parm))
-	{
-	  if (r.is_true ())
-	    break;
-	  r = r || walk (TREE_VALUE (parm));
-	}
-      return r;
-    }
-  else if (RECORD_OR_UNION_TYPE_P (t))
+  if (t && TREE_CODE (t) == CONSTRUCTOR)
     {
-      if (tree *slot = hash_map_safe_get (consteval_only_class_cache, t))
-	return *slot == boolean_true_node;
-
-      if (!COMPLETE_TYPE_P (t) && LAMBDA_TYPE_P (t))
-	/* Defer until we've definitely gone through prune_lambda_captures.  */
-	return tristate::unknown ();
+      for (constructor_elt &elt : CONSTRUCTOR_ELTS (t))
+	if (consteval_only_p (elt.value, seen))
+	  return true;
+      return false;
+    }
 
-      if (class_seen.add (t))
-	{
-	  /* Optimistically assume this already seen consteval-unknown class is
-	     not consteval-only, for sake of mutually recursive classes.  */
-	  optimistic_p = true;
-	  return false;
-	}
-      ++class_depth;
+  return t && consteval_only_value_p (t, seen);
+}
 
-      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 || walk (TREE_TYPE (member));
-	    if (r.is_true ())
-	      break;
-	  }
+/* Wrapper for consteval_only_p that sets up a hash set.  */
 
-      if (r.is_true ())
-	hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
-				   t, boolean_true_node);
-      else if (r.is_false ()
-	       /* The optimistic assumption above is at odds with caching
-		  'false' results for a nested class type.  */
-	       && (class_depth == 1 || !optimistic_p))
-	hash_map_safe_put<hm_ggc> (consteval_only_class_cache,
-				   t, boolean_false_node);
-
-      --class_depth;
-      return r;
-    }
-  else if (TYPE_PTRMEM_P (t))
-    return (walk (TYPE_PTRMEM_CLASS_TYPE (t))
-	    || walk (TYPE_PTRMEM_POINTED_TO_TYPE (t)));
-  else
-    return false;
+bool
+consteval_only_p (tree t)
+{
+  hash_set<tree> seen;
+  return consteval_only_p (t, seen);
 }
 
 /* A walker for check_out_of_consteval_use_r.  It cannot be a lambda, because
    we have to call this recursively.  */
 
 static tree
-check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset)
+check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset_)
 {
   tree t = *tp;
+  auto pset = static_cast<hash_set<tree> *>(pset_);
 
   /* No need to look into types or unevaluated operands.  */
   if (TYPE_P (t)
       || (unevaluated_p (TREE_CODE (t)) && !REFLECT_EXPR_P (t))
-      /* Don't walk INIT_EXPRs, because we'd emit bogus errors about
-	 member initializers.  */
-      || TREE_CODE (t) == INIT_EXPR
-      /* And don't recurse on DECL_EXPRs.  */
+      /* Don't recurse on DECL_EXPRs.  */
       || TREE_CODE (t) == DECL_EXPR
       /* Neither into USING_STMT.  */
       || TREE_CODE (t) == USING_STMT
       /* The operand of a splice is a constant-expression, thus
-	 manifestly constant-evaluated, so consteval-only types are permitted
+	 manifestly constant-evaluated, so consteval-only values are permitted
 	 here.  */
       || TREE_CODE (t) == SPLICE_EXPR
       /* Blocks can appear in the TREE_VEC operand of OpenMP
@@ -8974,19 +8965,33 @@  check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset)
 	return NULL_TREE;
       }
 
+  if (TREE_CODE (t) == EXPR_STMT)
+    {
+      if (tree r = cp_walk_tree (&EXPR_STMT_EXPR (t),
+				 check_out_of_consteval_use_r, pset, pset))
+	{
+	  /* If we can't give a precise location of the expression,
+	     use the location of the whole statement.  */
+	  if (cp_expr_location (r) == UNKNOWN_LOCATION)
+	    return t;
+	  return r;
+	}
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
   if (VAR_P (t) && DECL_HAS_VALUE_EXPR_P (t))
     {
       tree vexpr = DECL_VALUE_EXPR (t);
       if (tree ret = cp_walk_tree (&vexpr, check_out_of_consteval_use_r, pset,
-				   (hash_set<tree> *) pset))
+				   pset))
 	return ret;
     }
 
   if (TREE_CODE (t) == BIND_EXPR)
     {
       if (tree r = cp_walk_tree (&BIND_EXPR_BODY (t),
-				 check_out_of_consteval_use_r, pset,
-				 static_cast<hash_set<tree> *>(pset)))
+				 check_out_of_consteval_use_r, pset, pset))
 	return r;
       /* Don't walk BIND_EXPR_VARS.  */
       *walk_subtrees = false;
@@ -8998,8 +9003,7 @@  check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset)
       if (IF_STMT_CONSTEVAL_P (t))
 	{
 	  if (tree r = cp_walk_tree (&ELSE_CLAUSE (t),
-				     check_out_of_consteval_use_r, pset,
-				     static_cast<hash_set<tree> *>(pset)))
+				     check_out_of_consteval_use_r, pset, pset))
 	    return r;
 	  /* Don't walk the consteval branch.  */
 	  *walk_subtrees = false;
@@ -9008,12 +9012,10 @@  check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset)
       else if (IF_STMT_CONSTEXPR_P (t))
 	{
 	  if (tree r = cp_walk_tree (&THEN_CLAUSE (t),
-				     check_out_of_consteval_use_r, pset,
-				     static_cast<hash_set<tree> *>(pset)))
+				     check_out_of_consteval_use_r, pset, pset))
 	    return r;
 	  if (tree r = cp_walk_tree (&ELSE_CLAUSE (t),
-				     check_out_of_consteval_use_r, pset,
-				     static_cast<hash_set<tree> *>(pset)))
+				     check_out_of_consteval_use_r, pset, pset))
 	    return r;
 	  /* Don't walk the condition -- it's a manifestly constant-evaluated
 	     context.  */
@@ -9033,8 +9035,7 @@  check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset)
       return NULL_TREE;
     }
 
-  /* Now check the type to see if we are dealing with a consteval-only
-     expression.  */
+  /* If we don't find an immediate object, there's nothing to do.  */
   if (!consteval_only_p (t))
     return NULL_TREE;
 
@@ -9061,7 +9062,15 @@  check_out_of_consteval_use_r (tree *tp, int *walk_subtrees, void *pset)
 
 /* Detect if a consteval-only expression EXPR or a consteval-only
    variable EXPR not declared constexpr is used outside
-   a manifestly constant-evaluated context.  E.g.:
+   a manifestly constant-evaluated context.
+
+   [expr.const.const]/3: Every immediate object shall be
+   -- the object associated with a constexpr variable or a subobject thereof,
+   -- a template parameter object ([temp.param]) or a subject thereof, or
+   -- an object whose lifetime begins and ends during the evaluation of
+      a core constant expression.
+
+   E.g.:
 
      void f() {
        constexpr auto r = ^^int;  // OK
@@ -9092,20 +9101,9 @@  check_out_of_consteval_use (tree expr, bool complain/*=true*/)
   if (tree t = cp_walk_tree (&expr, check_out_of_consteval_use_r, &pset, &pset))
     {
       if (complain)
-	{
-	  if (VAR_P (t) && !DECL_DECLARED_CONSTEXPR_P (t))
-	    {
-	      auto_diagnostic_group d;
-	      error_at (cp_expr_loc_or_input_loc (t),
-			"consteval-only variable %qD not declared %<constexpr%> "
-			"used outside a constant-evaluated context", t);
-	      inform (DECL_SOURCE_LOCATION (t), "add %<constexpr%>");
-	    }
-	  else
-	    error_at (cp_expr_loc_or_input_loc (t),
-		      "consteval-only expressions are only allowed in "
-		      "a constant-evaluated context");
-	}
+	error_at (cp_expr_loc_or_input_loc (t),
+		  "consteval-only value outside an immediate function "
+		  "context");
       return true;
     }
 
@@ -9238,21 +9236,6 @@  valid_splice_for_member_access_p (const_tree t, bool decls_only_p/*=true*/)
 	  || TREE_CODE (t) == TREE_BINFO);
 }
 
-/* Check a function DECL for CWG 3115: Every function of consteval-only
-   type shall be an immediate function.  */
-
-void
-check_consteval_only_fn (tree decl)
-{
-  if (!DECL_IMMEDIATE_FUNCTION_P (decl)
-      && consteval_only_p (decl)
-      /* But if the function can be escalated, merrily we roll along.  */
-      && !immediate_escalating_function_p (decl))
-    error_at (DECL_SOURCE_LOCATION (decl),
-	      "function of consteval-only type must be declared %qs",
-	      "consteval");
-}
-
 /* Check if T is a valid result of splice-expression.  ADDRESS_P is true if
    we are taking the address of the splice.  MEMBER_ACCESS_P is true if this
    splice is used in foo.[: bar :] or foo->[: bar :] context.  TEMPLATE_P is
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index 9845032d28d..a415b423ef0 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -2131,15 +2131,13 @@  check_final_overrider (tree overrider, tree basefn)
       return 0;
     }
 
-  /* A class with a consteval virtual function that overrides a virtual
-     function that is not consteval shall have consteval-only type (CWG 3117).
-     A consteval virtual function shall not be overridden by a virtual
-     function that is not consteval.  */
+  /* [class.virtual]/18: A non-immediate virtual function shall not be
+     overridden by an immediate virtual function.  An immediate virtual
+     function shall not be overridden by a non-immediate virtual function.  */
   if ((DECL_IMMEDIATE_FUNCTION_P (basefn)
        && !DECL_IMMEDIATE_FUNCTION_P (overrider))
       || (!DECL_IMMEDIATE_FUNCTION_P (basefn)
-	  && DECL_IMMEDIATE_FUNCTION_P (overrider)
-	  && !consteval_only_p (overrider)))
+	  && DECL_IMMEDIATE_FUNCTION_P (overrider)))
     {
       auto_diagnostic_group d;
       if (DECL_IMMEDIATE_FUNCTION_P (overrider))
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 7907668da36..a77d7701e97 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -14981,12 +14981,6 @@  cp_build_bit_cast (location_t loc, tree type, tree arg,
 			 "is not trivially copyable", type);
 	  return error_mark_node;
 	}
-      if (consteval_only_p (type) || consteval_only_p (arg))
-	{
-	  error_at (loc, "%<__builtin_bit_cast%> cannot be used with "
-			 "consteval-only types");
-	  return error_mark_node;
-	}
     }
 
   if (error_operand_p (arg))
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index a4aec230a34..173c8539e54 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -5173,6 +5173,16 @@  type_has_unique_obj_representations (const_tree t, bool explain/*=false*/)
 	inform (loc, "%<std::nullptr_t%> has padding bits and no value bits");
       return false;
 
+    case LANG_TYPE:
+      if (REFLECTION_TYPE_P (t))
+	{
+	  if (explain)
+	    inform (loc, "%<std::meta::info%> has an unspecified object "
+		    "representation");
+	  return false;
+	}
+      gcc_fallthrough ();
+
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 712b95ab245..0b200804fe9 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -6389,7 +6389,7 @@  cp_build_binary_op (const op_location_t &location,
 	}
       /* [expr.eq]: "If both operands are of type std::meta::info,
 	 comparison is defined as follows..."  */
-      else if (code0 == META_TYPE && code1 == META_TYPE)
+      else if (REFLECTION_TYPE_P (type0) && REFLECTION_TYPE_P (type1))
 	result_type = type0;
       else
 	{
@@ -11901,12 +11901,6 @@  check_return_expr (tree retval, bool *no_warning, bool *dangling)
 	*dangling = true;
     }
 
-  if (check_out_of_consteval_use (retval))
-    {
-      current_function_return_value = error_mark_node;
-      return error_mark_node;
-    }
-
   /* A naive attempt to reduce the number of -Wdangling-reference false
      positives: if we know that this function can return a variable with
      static storage duration rather than one of its parameters, suppress
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 62f583dcf40..1aea0fde549 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -1136,6 +1136,42 @@  store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
   /* Handle aggregate NSDMI in non-constant initializers, too.  */
   value = replace_placeholders (value, decl);
 
+  /* Detect stuff like 'info r = ^^int;' outside a manifestly
+     constant-evaluated context.  */
+  if (flag_reflection
+      && !processing_template_decl
+      && !DECL_DECLARED_CONSTEXPR_P (decl))
+    {
+      const bool mce_p
+	= (decl_maybe_constant_var_p (decl)
+	   || (TREE_STATIC (decl)
+	       && DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)));
+      bool bad = check_out_of_consteval_use (value, /*complain=*/false);
+      /* A non-constexpr variable at namespace scope with a constant
+	 initializer has constant initialization, so we check the folded
+	 value not to wrongly reject "int e = (^^int, 42);".  For non-static
+	 local variables, there is no such rule, so we check the unfolded
+	 initializer.  But we should also reject
+
+	   consteval auto fn () { return ^^int; }
+	   void g() { auto r = fn (); }
+
+	 so we may have to check both.  Note that the first call could
+	 have escalated and so we may find ourselves in an immediate
+	 context now.  */
+      if (!bad && !mce_p && value != init)
+	bad = check_out_of_consteval_use (init, /*complain=*/false);
+      if (bad)
+	{
+	  auto_diagnostic_group d;
+	  error_at (DECL_SOURCE_LOCATION (decl),
+		    "%qD is initialized with a consteval-only value but is "
+		    "not declared %<constexpr%>", decl);
+	  inform (DECL_SOURCE_LOCATION (decl), "add %<constexpr%>");
+	  value = error_mark_node;
+	}
+    }
+
   /* A COMPOUND_LITERAL_P CONSTRUCTOR is the syntactic form; by the time we get
      here it should have been digested into an actual value for the type.  */
   gcc_checking_assert (TREE_CODE (value) != CONSTRUCTOR
diff --git a/gcc/expr.cc b/gcc/expr.cc
index 3d99be8472f..1e57a565770 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -7138,6 +7138,8 @@  count_type_elements (const_tree type, bool for_ctor_p)
     case POINTER_TYPE:
     case OFFSET_TYPE:
     case REFERENCE_TYPE:
+    /* This could represent the C++ std::meta::info type.  */
+    case LANG_TYPE:
     case NULLPTR_TYPE:
     case OPAQUE_TYPE:
     case BITINT_TYPE:
@@ -7149,7 +7151,6 @@  count_type_elements (const_tree type, bool for_ctor_p)
     case VOID_TYPE:
     case METHOD_TYPE:
     case FUNCTION_TYPE:
-    case LANG_TYPE:
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-uneval3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-uneval3.C
index 7fc4065730b..f08b9a04da9 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-uneval3.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-uneval3.C
@@ -6,4 +6,4 @@  struct A {
   void f() requires (this, true) { }
 };
 
-template struct A<int>;
+template struct A<int>;	// { dg-error ".this. is not a constant expression" }
diff --git a/gcc/testsuite/g++.dg/reflect/bit_cast.C b/gcc/testsuite/g++.dg/reflect/bit_cast.C
index bab62717f7e..d305c54f19a 100644
--- a/gcc/testsuite/g++.dg/reflect/bit_cast.C
+++ b/gcc/testsuite/g++.dg/reflect/bit_cast.C
@@ -10,10 +10,20 @@  f ()
   auto a = ^^int;
   auto *b = &a;
   void *c = nullptr;
-  (void) std::bit_cast<void *>(b);  // { dg-message "from here" }
-  (void) std::bit_cast<decltype(^^int) *>(c); // { dg-message "from here" }
-  __builtin_bit_cast (void *, b); // { dg-error ".__builtin_bit_cast. cannot be used with consteval-only types" }
-  __builtin_bit_cast (decltype(^^int) *, c);  // { dg-error ".__builtin_bit_cast. cannot be used with consteval-only types" }
+  (void) std::bit_cast<void *>(b);
+  (void) std::bit_cast<decltype(^^int) *>(c);
+  __builtin_bit_cast (void *, b);
+  __builtin_bit_cast (decltype(^^int) *, c);
 }
 
-// { dg-error ".__builtin_bit_cast. cannot be used with consteval-only types" "" { target *-*-* } 0 }
+void
+g ()
+{
+  constexpr static auto a = ^^int;
+  constexpr auto *b = &a;
+  void *c = nullptr;
+  (void) std::bit_cast<void *>(b);		// { dg-error "consteval-only value outside an immediate function context" }
+  (void) std::bit_cast<decltype(^^int) *>(c);
+  __builtin_bit_cast (void *, b);		// { dg-error "consteval-only value outside an immediate function context" }
+  __builtin_bit_cast (decltype(^^int) *, c);
+}
diff --git a/gcc/testsuite/g++.dg/reflect/bit_cast2.C b/gcc/testsuite/g++.dg/reflect/bit_cast2.C
new file mode 100644
index 00000000000..b6be007bcb8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/bit_cast2.C
@@ -0,0 +1,12 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+using info = decltype(^^::);
+
+/* I don't know what these should actually do.  At least make sure we don't
+   ICE.  */
+constexpr auto x = __builtin_bit_cast (unsigned long, ^^int);   // { dg-message "sorry, unimplemented: .__builtin_bit_cast. cannot be constant evaluated because the argument cannot be encoded" }
+constexpr auto y = __builtin_bit_cast (decltype(^^int), 0UL);   // { dg-message "sorry, unimplemented: .__builtin_bit_cast. cannot be constant evaluated because the argument cannot be interpreted" }
+info z = __builtin_bit_cast (decltype(^^int), 0xdeadbeefUL);
diff --git a/gcc/testsuite/g++.dg/reflect/crash12.C b/gcc/testsuite/g++.dg/reflect/crash12.C
index a234c95be55..e289a689111 100644
--- a/gcc/testsuite/g++.dg/reflect/crash12.C
+++ b/gcc/testsuite/g++.dg/reflect/crash12.C
@@ -3,6 +3,6 @@ 
 
 #include <meta>
 
-struct X { constexpr operator std::meta::info(); }; // { dg-error "function of consteval-only type" }
+struct X { constexpr operator std::meta::info(); };
 // { dg-warning "used but never defined" "" { target *-*-* } .-1 }
 constexpr auto r = std::meta::type_of (X{}); // { dg-error "used before its definition" }
diff --git a/gcc/testsuite/g++.dg/reflect/crash18.C b/gcc/testsuite/g++.dg/reflect/crash18.C
index 3fe08734f56..51ecd4ad30e 100644
--- a/gcc/testsuite/g++.dg/reflect/crash18.C
+++ b/gcc/testsuite/g++.dg/reflect/crash18.C
@@ -3,4 +3,4 @@ 
 
 // We also test this elsewhere but we only crashed when there were no other
 // errors.
-void fn (decltype(^^::)) {} // { dg-error "function of consteval-only type must be declared .consteval." }
+void fn (decltype(^^::)) {}
diff --git a/gcc/testsuite/g++.dg/reflect/diag3.C b/gcc/testsuite/g++.dg/reflect/diag3.C
index 49233ee354c..2be45cb962f 100644
--- a/gcc/testsuite/g++.dg/reflect/diag3.C
+++ b/gcc/testsuite/g++.dg/reflect/diag3.C
@@ -2,31 +2,31 @@ 
 // { dg-additional-options "-freflection" }
 // Test that we suggest adding "constexpr" (where allowed).
 
-auto foo = ^^int;  // { dg-error "consteval-only variable .foo." }
+auto foo = ^^int;  // { dg-error ".foo. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
-constinit auto foo_ = ^^int; // { dg-error "consteval-only variable .foo_." }
+constinit auto foo_ = ^^int; // { dg-error ".foo_. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
 constexpr auto foo__ = ^^int;
-thread_local auto tfoo = ^^int;  // { dg-error "consteval-only variable .tfoo." }
+thread_local auto tfoo = ^^int;  // { dg-error ".tfoo. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
-thread_local constinit auto tfoo_ = ^^int; // { dg-error "consteval-only variable .tfoo_." }
+thread_local constinit auto tfoo_ = ^^int; // { dg-error ".tfoo_. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
 thread_local constexpr auto tfoo__ = ^^int;
 
 void
 f ()
 {
-  auto ref = ^^int;  // { dg-error "consteval-only variable .ref." }
+  auto ref = ^^int;  // { dg-error ".ref. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
   constexpr auto ref_ = ^^int;
-  static auto sref = ^^int;  // { dg-error "consteval-only variable .sref." }
+  static auto sref = ^^int;  // { dg-error ".sref. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
-  static auto constinit sref_ = ^^int; // { dg-error "consteval-only variable .sref_." }
+  static auto constinit sref_ = ^^int; // { dg-error ".sref_. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
   static auto constexpr sref__ = ^^int;
-  thread_local auto tref = ^^int; // { dg-error "consteval-only variable .tref." }
+  thread_local auto tref = ^^int; // { dg-error ".tref. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
-  thread_local constinit auto tref_ = ^^int; // { dg-error "consteval-only variable .tref_." }
+  thread_local constinit auto tref_ = ^^int; // { dg-error ".tref_. is initialized with a consteval-only value" }
 // { dg-message "add .constexpr." "" { target *-*-* } .-1 }
   thread_local constexpr auto tref__ = ^^int;
 }
diff --git a/gcc/testsuite/g++.dg/reflect/expr11.C b/gcc/testsuite/g++.dg/reflect/expr11.C
index 44d66947d9d..61f934f8bab 100644
--- a/gcc/testsuite/g++.dg/reflect/expr11.C
+++ b/gcc/testsuite/g++.dg/reflect/expr11.C
@@ -11,18 +11,18 @@  f ()
   constexpr auto q = ^^float;
   if constexpr (foo (^^::) == ^^::)
     {
-      auto r = ^^int; // { dg-error "consteval-only variable .r." }
+      auto r = ^^int; // { dg-error "consteval-only value" }
       constexpr auto cr = ^^int;
     }
-  if constexpr (auto r = ^^int;  // { dg-error "consteval-only variable .r." }
+  if constexpr (auto r = ^^int;  // { dg-error "consteval-only value" }
 		r == ^^int);	 // { dg-error "the value of .r. is not usable" }
   if constexpr (constexpr auto r = ^^int; r == ^^int);
   if constexpr (q != ^^char);
   if constexpr (^^int != ^^char);
-  if (q != ^^char);  // { dg-error "consteval-only expressions" }
-  if (^^char == ^^char);  // { dg-error "consteval-only expressions" }
-  while (^^char == ^^char);  // { dg-error "consteval-only expressions" }
-  do {} while (^^char == ^^char);  // { dg-error "consteval-only expressions" }
+  if (q != ^^char);  // { dg-error "consteval-only value" }
+  if (^^char == ^^char);  // { dg-error "consteval-only value" }
+  while (^^char == ^^char);  // { dg-error "consteval-only value" }
+  do {} while (^^char == ^^char);  // { dg-error "consteval-only value" }
   consteval {
     if (q != ^^char);
     if (^^char == ^^char);
@@ -32,18 +32,18 @@  f ()
 
   if constexpr (true)
     {
-      auto r = ^^int; // { dg-error "consteval-only variable .r." }
+      auto r = ^^int; // { dg-error "consteval-only value" }
     }
   else
     {
-      auto r = ^^int; // { dg-error "consteval-only variable .r." }
+      auto r = ^^int; // { dg-error "consteval-only value" }
     }
   if constexpr (false)
     {
-      auto r = ^^int; // { dg-error "consteval-only variable .r." }
+      auto r = ^^int; // { dg-error "consteval-only value" }
     }
   else
     {
-      auto r = ^^int; // { dg-error "consteval-only variable .r." }
+      auto r = ^^int; // { dg-error "consteval-only value" }
     }
 }
diff --git a/gcc/testsuite/g++.dg/reflect/expr12.C b/gcc/testsuite/g++.dg/reflect/expr12.C
index 34aed536429..6120793a06b 100644
--- a/gcc/testsuite/g++.dg/reflect/expr12.C
+++ b/gcc/testsuite/g++.dg/reflect/expr12.C
@@ -19,30 +19,30 @@  f ()
 
   if not consteval
     {
-      ^^void;  // { dg-error "consteval-only expressions" }
+      ^^void;  // { dg-error "consteval-only value" }
     }
   if not consteval
     {
-      q;  // { dg-error "consteval-only expressions" }
+      q;  // { dg-error "consteval-only value" }
     }
   if not consteval
     {
-      auto r = ^^int;  // { dg-error "consteval-only variable" }
+      auto r = ^^int;  // { dg-error "consteval-only value" }
     }
   if not consteval
     {
-      if (q != ^^char);  // { dg-error "consteval-only expressions" }
+      if (q != ^^char);  // { dg-error "consteval-only value" }
     }
   if not consteval
     {
-      if (^^char == ^^char);  // { dg-error "consteval-only expressions" }
+      if (^^char == ^^char);  // { dg-error "consteval-only value" }
     }
   if not consteval
     {
-      while (^^char != ^^char);  // { dg-error "consteval-only expressions" }
+      while (^^char != ^^char);  // { dg-error "consteval-only value" }
     }
   if not consteval
     {
-      do {} while (^^char != ^^char);  // { dg-error "consteval-only expressions" }
+      do {} while (^^char != ^^char);  // { dg-error "consteval-only value" }
     }
 }
diff --git a/gcc/testsuite/g++.dg/reflect/expr19.C b/gcc/testsuite/g++.dg/reflect/expr19.C
new file mode 100644
index 00000000000..9beddcefd75
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/expr19.C
@@ -0,0 +1,8 @@ 
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+constexpr auto r = ^^int;
+
+void f () { (void) r; }		      // { dg-error "consteval-only" }
+void g (bool c) { if (c) (void) r; }  // { dg-error "consteval-only" }
+auto h () { return r; }		      // { dg-error "consteval-only" }
diff --git a/gcc/testsuite/g++.dg/reflect/init10.C b/gcc/testsuite/g++.dg/reflect/init10.C
index df81b890fae..02bdf61d0d5 100644
--- a/gcc/testsuite/g++.dg/reflect/init10.C
+++ b/gcc/testsuite/g++.dg/reflect/init10.C
@@ -9,8 +9,8 @@  struct A {
   consteval A() : i{^^void} {}
 };
 
-A a1;  // { dg-error "consteval-only variable .a1." }
-constinit A a2;  // { dg-error "consteval-only variable .a2." }
+A a1;  // { dg-error "consteval-only value" }
+constinit A a2;  // { dg-error "consteval-only value" }
 constexpr A a3;
 
 struct B {
@@ -19,6 +19,6 @@  struct B {
  consteval B() : i{}, j{i} {}
 };
 
-B b1;  // { dg-error "consteval-only variable .b1." }
-constinit B b2;  // { dg-error "consteval-only variable .b2." }
+B b1;
+constinit B b2;
 constexpr B b3;
diff --git a/gcc/testsuite/g++.dg/reflect/init11.C b/gcc/testsuite/g++.dg/reflect/init11.C
index 7173023e602..05e2d0cad73 100644
--- a/gcc/testsuite/g++.dg/reflect/init11.C
+++ b/gcc/testsuite/g++.dg/reflect/init11.C
@@ -1,6 +1,5 @@ 
 // { dg-do compile { target c++26 } }
 // { dg-additional-options "-freflection" }
-// Test invalid reflections in member init lists.
 
 using info = decltype(^^void);
 
@@ -16,10 +15,10 @@  struct B {
 
 struct C {
   info i;
-  constexpr C() : i{} {}  // { dg-error "function of consteval-only type must be declared .consteval." }
+  constexpr C() : i{} {}
 };
 
 struct D {
   info i;
-  D() : i{} {}  // { dg-error "function of consteval-only type must be declared .consteval." }
+  D() : i{} {}
 };
diff --git a/gcc/testsuite/g++.dg/reflect/init12.C b/gcc/testsuite/g++.dg/reflect/init12.C
index 8ec456d1cba..3d1ca6a46cb 100644
--- a/gcc/testsuite/g++.dg/reflect/init12.C
+++ b/gcc/testsuite/g++.dg/reflect/init12.C
@@ -24,6 +24,6 @@  void
 g ()
 {
   constexpr static auto r = ^^int;
-  constexpr auto x = foo<info>(&r); // { dg-error "pointer into an object of consteval-only type" }
+  constexpr auto x = foo<info>(&r);
   constexpr auto y = bar<info>(nullptr);
 }
diff --git a/gcc/testsuite/g++.dg/reflect/init16.C b/gcc/testsuite/g++.dg/reflect/init16.C
index 621b1c57d3b..d411ef7450d 100644
--- a/gcc/testsuite/g++.dg/reflect/init16.C
+++ b/gcc/testsuite/g++.dg/reflect/init16.C
@@ -41,7 +41,7 @@  void fox() {
         do_something_runtime<A>();
         int C = something_of(A);
         constexpr int E = something_of(A);
-        identity(A); // { dg-error "consteval-only expressions" }
-        identity(B); // { dg-error "consteval-only expressions" }
+        identity(A); // { dg-error "consteval-only value" }
+        identity(B); // { dg-error "consteval-only value" }
     }
 }
diff --git a/gcc/testsuite/g++.dg/reflect/init19.C b/gcc/testsuite/g++.dg/reflect/init19.C
index bfebe4cf2d7..bd0d10fa9d2 100644
--- a/gcc/testsuite/g++.dg/reflect/init19.C
+++ b/gcc/testsuite/g++.dg/reflect/init19.C
@@ -8,5 +8,5 @@  struct N { int i; };
 template<typename T>
 struct X : T { };
 
-auto a = X<C>{}; // { dg-error "outside a constant-evaluated context" }
+auto a = X<C>{};
 auto b = X<N>{};
diff --git a/gcc/testsuite/g++.dg/reflect/init4.C b/gcc/testsuite/g++.dg/reflect/init4.C
index 6d9a39b40e0..f337def032c 100644
--- a/gcc/testsuite/g++.dg/reflect/init4.C
+++ b/gcc/testsuite/g++.dg/reflect/init4.C
@@ -49,7 +49,7 @@  constexpr G g;
 struct H {
   info (*fp)();
 };
-constexpr H h{foo}; // { dg-error "address of immediate function" }
+constexpr H h{foo};	// { dg-bogus "returns address" "" { xfail *-*-* } }
 
 union U {
   int n;
diff --git a/gcc/testsuite/g++.dg/reflect/init5.C b/gcc/testsuite/g++.dg/reflect/init5.C
index dd9f07bf4d6..abefac11866 100644
--- a/gcc/testsuite/g++.dg/reflect/init5.C
+++ b/gcc/testsuite/g++.dg/reflect/init5.C
@@ -12,10 +12,10 @@  consteval const Base &fn1() {
   static constexpr Derived d;
   return d;
 }
-constexpr auto &ref = fn1(); // { dg-error "reference into an object of consteval-only type" }
+constexpr auto &ref = fn1();
 
 consteval void *fn2() {
   static constexpr auto v = ^^int;
   return (void *)&v;
 }
-constexpr const void *ptr = fn2(); // { dg-error "pointer into an object of consteval-only type" }
+constexpr const void *ptr = fn2();
diff --git a/gcc/testsuite/g++.dg/reflect/init6.C b/gcc/testsuite/g++.dg/reflect/init6.C
index 371d8cee314..36f598c1c00 100644
--- a/gcc/testsuite/g++.dg/reflect/init6.C
+++ b/gcc/testsuite/g++.dg/reflect/init6.C
@@ -11,12 +11,12 @@  struct N {
   info i = ^^void;
 };
 
-S s1;  // { dg-error "consteval-only variable" }
-constinit S s2{};  // { dg-error "consteval-only variable" }
+S s1;
+constinit S s2{};
 constexpr S s3{^^int};
 
-N n1;  // { dg-error "consteval-only variable" }
-constinit N n2;  // { dg-error "consteval-only variable" }
+N n1;  // { dg-error "consteval-only value" }
+constinit N n2;  // { dg-error "consteval-only value" }
 constexpr N n3;
 
 template<typename T>
@@ -24,8 +24,8 @@  struct X {
   T t;
 };
 
-X<info> x1;  // { dg-error "consteval-only variable" }
-constinit X<info> x2{};  // { dg-error "consteval-only variable" }
+X<info> x1;
+constinit X<info> x2{};
 constexpr X<info> x3{^^int};
 
 void
diff --git a/gcc/testsuite/g++.dg/reflect/init7.C b/gcc/testsuite/g++.dg/reflect/init7.C
index c530776d74b..c9155e2d434 100644
--- a/gcc/testsuite/g++.dg/reflect/init7.C
+++ b/gcc/testsuite/g++.dg/reflect/init7.C
@@ -5,25 +5,25 @@ 
 
 using info = decltype(^^int);
 
-info r1 = ^^int;  // { dg-error "consteval-only variable .r1. not declared .constexpr. used outside a constant-evaluated context" }
-const info r2 = ^^int;  // { dg-error "consteval-only variable .r2. not declared .constexpr. used outside a constant-evaluated context" }
+info r1 = ^^int;  // { dg-error ".r1. is initialized with a consteval-only value but is not declared .constexpr." }
+const info r2 = ^^int;  // { dg-error ".r2. is initialized with a consteval-only value but is not declared .constexpr." }
 
 constexpr info r3 = ^^int;
-constinit info r4 = ^^int;  // { dg-error "consteval-only variable .r4. not declared .constexpr. used outside a constant-evaluated context" }
-const info *const p1 = &r3;  // { dg-error "consteval-only variable .p1. not declared .constexpr. used outside a constant-evaluated context" }
-info *p2;  // { dg-error "consteval-only variable .p2. not declared .constexpr. used outside a constant-evaluated context" }
-const info &q = r3;  // { dg-error "consteval-only variable .q. not declared .constexpr. used outside a constant-evaluated context" }
+constinit info r4 = ^^int;  // { dg-error ".r4. is initialized with a consteval-only value but is not declared .constexpr." }
+const info *const p1 = &r3;  // { dg-error ".p1. is initialized with a consteval-only value but is not declared .constexpr." }
+info *p2;
+const info &q = r3;  // { dg-error ".q. is initialized with a consteval-only value but is not declared .constexpr." }
 
 void
 g ()
 {
-  info l1 = ^^int;  // { dg-error "consteval-only variable .l1. not declared .constexpr. used outside a constant-evaluated context" }
-  const info l2 = ^^int;  // { dg-error "consteval-only variable .l2. not declared .constexpr. used outside a constant-evaluated context" }
+  info l1 = ^^int;  // { dg-error ".l1. is initialized with a consteval-only value but is not declared .constexpr." }
+  const info l2 = ^^int;  // { dg-error ".l2. is initialized with a consteval-only value but is not declared .constexpr." }
   constexpr info l3 = ^^int;
-  static info l4 = ^^int;  // { dg-error "consteval-only variable .l4. not declared .constexpr. used outside a constant-evaluated context" }
-  static const info l5 = ^^int;  // { dg-error "consteval-only variable .l5. not declared .constexpr. used outside a constant-evaluated context" }
+  static info l4 = ^^int;  // { dg-error ".l4. is initialized with a consteval-only value but is not declared .constexpr." }
+  static const info l5 = ^^int;  // { dg-error ".l5. is initialized with a consteval-only value but is not declared .constexpr." }
   static constexpr info l6 = ^^int;
-  static constinit info l7 = ^^int;  // { dg-error "consteval-only variable .l7. not declared .constexpr. used outside a constant-evaluated context" }
+  static constinit info l7 = ^^int;  // { dg-error ".l7. is initialized with a consteval-only value but is not declared .constexpr." }
 }
 
 consteval void
diff --git a/gcc/testsuite/g++.dg/reflect/init9.C b/gcc/testsuite/g++.dg/reflect/init9.C
index c088a2d9a7c..a8005e04344 100644
--- a/gcc/testsuite/g++.dg/reflect/init9.C
+++ b/gcc/testsuite/g++.dg/reflect/init9.C
@@ -4,9 +4,9 @@ 
 
 using info = decltype(^^int);
 
-info foo (); // { dg-error "function of consteval-only type must be declared .consteval." }
-constexpr info bar (); // { dg-error "function of consteval-only type must be declared .consteval." }
-void baz (info); // { dg-error "function of consteval-only type must be declared .consteval." }
+info foo ();
+constexpr info bar ();
+void baz (info);
 
 consteval info
 ok1 ()
@@ -28,26 +28,26 @@  ok3 (info i)
 }
 
 constexpr info
-bad1 () // { dg-error "function of consteval-only type must be declared .consteval." }
+bad1 ()
 {
-  return ^^int;  // { dg-error "consteval-only expressions" }
+  return ^^int;  // { dg-error "consteval-only value" }
 }
 
 info
-bad2 () // { dg-error "function of consteval-only type must be declared .consteval." }
+bad2 ()
 {
-  return ^^int;  // { dg-error "consteval-only expressions" }
+  return ^^int;  // { dg-error "consteval-only value" }
 }
 
 constexpr auto
-bad3 (info i) // { dg-error "function of consteval-only type must be declared .consteval." }
+bad3 (info i)
 {
-  return i;  // { dg-error "consteval-only expressions" }
+  return i;
 }
 
 template<info R>
 info
-bad4 () // { dg-error "function of consteval-only type must be declared .consteval." }
+bad4 ()
 {
-  return R;  // { dg-error "consteval-only expressions" }
+  return R;
 }
diff --git a/gcc/testsuite/g++.dg/reflect/override1.C b/gcc/testsuite/g++.dg/reflect/override1.C
index d3124f55152..540287d6f8a 100644
--- a/gcc/testsuite/g++.dg/reflect/override1.C
+++ b/gcc/testsuite/g++.dg/reflect/override1.C
@@ -9,12 +9,12 @@  struct B {
 };
 
 struct D1 : B {
-  consteval virtual void foo() override { } // { dg-error "overriding" }
+  consteval virtual void foo() override { } // { dg-error "overriding non-.consteval. function" }
 };
 
 struct D2 : B {
   info i;
-  consteval virtual void foo() override { }
+  consteval virtual void foo() override { } // { dg-error "overriding non-.consteval. function" }
 };
 
 struct D3 : B {
@@ -30,10 +30,10 @@  struct D4 : B2 {
 };
 
 struct D5 : B2 {
-  virtual void foo() override { } // { dg-error "overriding" }
+  virtual void foo() override { } // { dg-error "overriding .consteval. function" }
 };
 
 struct D6 : B2 {
   info i;
-  virtual void foo() override { } // { dg-error "consteval-only type|overriding" }
+  virtual void foo() override { } // { dg-error "overriding .consteval. function" }
 };
diff --git a/gcc/testsuite/g++.dg/reflect/p2996-15.C b/gcc/testsuite/g++.dg/reflect/p2996-15.C
index 3ced17647ae..867db78163d 100644
--- a/gcc/testsuite/g++.dg/reflect/p2996-15.C
+++ b/gcc/testsuite/g++.dg/reflect/p2996-15.C
@@ -11,6 +11,6 @@  consteval const Base& fn(const Derived& derived) { return derived; }
 
 constexpr Derived obj{.r=^^::}; // OK
 constexpr const Derived& d = obj; // OK
-constexpr const Base& b1 = fn(obj); // { dg-error "reference into an object of consteval-only" }
-constexpr const Base& b2 = obj;	  // { dg-error "reference into an object of consteval-only" }
+constexpr const Base& b1 = fn(obj);
+constexpr const Base& b2 = obj;
 constexpr Base b3 = obj;
diff --git a/gcc/testsuite/g++.dg/reflect/parm3.C b/gcc/testsuite/g++.dg/reflect/parm3.C
index 1ff9357dc3d..4128166ac5e 100644
--- a/gcc/testsuite/g++.dg/reflect/parm3.C
+++ b/gcc/testsuite/g++.dg/reflect/parm3.C
@@ -5,23 +5,23 @@ 
 using info = decltype(^^int);
 
 consteval void foo (info) { }
-constexpr void bar (info) { } // { dg-error "function of consteval-only type must be declared .consteval." }
-void baz (info) { }  // { dg-error "function of consteval-only type must be declared .consteval." }
+constexpr void bar (info) { }
+void baz (info) { }
 
 void
 f ()
 {
   foo (^^void);
-  bar (^^void);  // { dg-error "consteval-only expressions" }
-  baz (^^void);  // { dg-error "consteval-only expressions" }
+  bar (^^void);  // { dg-error "consteval-only value" }
+  baz (^^void);  // { dg-error "consteval-only value" }
 }
 
 constexpr void
 g ()
 {
   foo (^^void);
-  bar (^^void);  // { dg-error "consteval-only expressions" }
-  baz (^^void);  // { dg-error "consteval-only expressions" }
+  bar (^^void);  // { dg-error "consteval-only value" }
+  baz (^^void);  // { dg-error "consteval-only value" }
 }
 
 consteval void
diff --git a/gcc/testsuite/g++.dg/reflect/parm4.C b/gcc/testsuite/g++.dg/reflect/parm4.C
index 8867e6adbb6..bb733f17233 100644
--- a/gcc/testsuite/g++.dg/reflect/parm4.C
+++ b/gcc/testsuite/g++.dg/reflect/parm4.C
@@ -5,28 +5,28 @@ 
 using info = decltype(^^int);
 
 struct S {
-  int mfn0 (info) { return 0; }  // { dg-error "function of consteval-only type must be declared .consteval." }
-  constexpr int mfn1 (info) { return 1; }  // { dg-error "function of consteval-only type must be declared .consteval." }
+  int mfn0 (info) { return 0; }
+  constexpr int mfn1 (info) { return 1; }
   consteval int mfn2 (info) { return 2; }
-  int mfn3 (int, info) { return 0; }  // { dg-error "function of consteval-only type must be declared .consteval." }
-  info mfn4 () { return ^^int; }  // { dg-error "consteval-only expressions|function of consteval-only type must be declared .consteval." }
+  int mfn3 (int, info) { return 0; }
+  info mfn4 () { return ^^int; }    // { dg-error "consteval-only value" }
 };
 
 void
 g (S s)
 {
-  int i0 = s.mfn0 (^^int);  // { dg-error "consteval-only expressions" "" { target { ! implicit_constexpr } } }
+  int i0 = s.mfn0 (^^int);  // { dg-error "consteval-only value" }
   constexpr int i1 = s.mfn1 (^^int);
   constexpr int i2 = s.mfn2 (^^int);
-  int i3 = s.mfn3 (42, ^^int);  // { dg-error "consteval-only expressions" "" { target { ! implicit_constexpr } } }
-  info i4 = s.mfn4 ();  // { dg-error "consteval-only variable" }
+  int i3 = s.mfn3 (42, ^^int);  // { dg-error "consteval-only value" }
+  info i4 = s.mfn4 ();
 }
 
 template<typename T>
-int fn (T) { return 4; } // { dg-error "function of consteval-only type must be declared .consteval." }
-const int a = fn (^^int); // { dg-error "consteval-only expressions" }
-int b = fn (^^int); // { dg-error "consteval-only expressions" }
+int fn (T) { return 4; }
+const int a = fn (^^int); // { dg-error "consteval-only value" }
+int b = fn (^^int); // { dg-error "consteval-only value" }
 
 template<typename T>
 T fn2 () { return ^^void; } // { dg-error "consteval-only" }
-const info i = fn2<info>(); // { dg-error "consteval-only variable" }
+const info i = fn2<info>();
diff --git a/gcc/testsuite/g++.dg/reflect/pr124012.C b/gcc/testsuite/g++.dg/reflect/pr124012.C
index 84783d1a805..55df158808d 100644
--- a/gcc/testsuite/g++.dg/reflect/pr124012.C
+++ b/gcc/testsuite/g++.dg/reflect/pr124012.C
@@ -10,28 +10,28 @@  void
 bar ()
 {
   constexpr auto [a, b] = A {};
-  foo (a);			// { dg-error "consteval-only expressions are only allowed in a constant-evaluated context" }
+  foo (a);
 }
 
 void
 baz ()
 {
   constexpr auto a = A {};
-  foo (a.a);			// { dg-error "consteval-only expressions are only allowed in a constant-evaluated context" }
+  foo (a.a);
 }
 
 void
 qux ()
 {
   constexpr auto a = A {};
-  corge (&a.a);			// { dg-error "consteval-only expressions are only allowed in a constant-evaluated context" }
+  corge (&a.a);
 }
 
 void
 garply ()
 {
   constexpr auto [a, b] = A {};
-  corge (&a);			// { dg-error "consteval-only expressions are only allowed in a constant-evaluated context" }
+  corge (&a);
 }
 
 void
diff --git a/gcc/testsuite/g++.dg/reflect/type12.C b/gcc/testsuite/g++.dg/reflect/type12.C
index c066f27fb69..31717841705 100644
--- a/gcc/testsuite/g++.dg/reflect/type12.C
+++ b/gcc/testsuite/g++.dg/reflect/type12.C
@@ -17,7 +17,7 @@  struct CE { decltype(^^::) i; };
 struct A {
   static const Array<CE, 10> mData;
 };
-const Array<CE, 10> A::mData{}; // { dg-error "outside a constant-evaluated context" }
+const Array<CE, 10> A::mData{};
 
 struct B {
   static constexpr Array<CE, 11> mData{};
diff --git a/gcc/testsuite/g++.dg/reflect/type2.C b/gcc/testsuite/g++.dg/reflect/type2.C
index 7d1f16b981d..2ca31d1a648 100644
--- a/gcc/testsuite/g++.dg/reflect/type2.C
+++ b/gcc/testsuite/g++.dg/reflect/type2.C
@@ -7,13 +7,13 @@  struct A;
 void
 f1 ()
 {
-  const auto r = ^^double; // { dg-error "consteval-only variable" }
+  const auto r = ^^double; // { dg-error "consteval-only value" }
   constexpr auto r2 = ^^int;
-  r2; // { dg-error "consteval-only expressions" }
-  ^^void; // { dg-error "consteval-only expressions" }
-  ^^int == ^^int; // { dg-error "consteval-only expressions" }
-  (void) ^^float; // { dg-error "consteval-only expressions" }
-  auto rr = r; // { dg-error "consteval-only variable" }
+  r2; // { dg-error "consteval-only value" }
+  ^^void; // { dg-error "consteval-only value" }
+  ^^int == ^^int; // { dg-error "consteval-only value" }
+  (void) ^^float; // { dg-error "consteval-only value" }
+  auto rr = r2; // { dg-error "consteval-only value" }
 
   constexpr auto x = &(^^int); // { dg-error "lvalue required" }
 
@@ -28,7 +28,7 @@  f1 ()
 constexpr void
 f2 ()
 {
-  auto r = ^^int; // { dg-error "consteval-only variable" }
+  auto r = ^^int; // { dg-error "consteval-only value" }
 }
 
 void
diff --git a/gcc/testsuite/g++.dg/reflect/type_trait19.C b/gcc/testsuite/g++.dg/reflect/type_trait19.C
new file mode 100644
index 00000000000..3a09edec7ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/type_trait19.C
@@ -0,0 +1,15 @@ 
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+// Test reflection type traits [meta.reflection.traits], type properties.
+
+#include <meta>
+using namespace std::meta;
+
+using U = std::meta::info;
+using A = U[5];
+struct S { std::meta::info i; };
+
+static_assert (!has_unique_object_representations (^^std::meta::info));
+static_assert (!has_unique_object_representations (^^U));
+static_assert (!has_unique_object_representations (^^S));
+static_assert (!has_unique_object_representations (^^A));
diff --git a/gcc/testsuite/g++.dg/reflect/typeinfo1.C b/gcc/testsuite/g++.dg/reflect/typeinfo1.C
new file mode 100644
index 00000000000..baf3025a173
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/typeinfo1.C
@@ -0,0 +1,7 @@ 
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+#include <typeinfo>
+
+const std::type_info &ti = typeid(decltype(^^int));
+int main () {}
diff --git a/gcc/testsuite/g++.dg/reflect/value1.C b/gcc/testsuite/g++.dg/reflect/value1.C
new file mode 100644
index 00000000000..36764a78384
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/value1.C
@@ -0,0 +1,543 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection -g" }
+
+using info = decltype(^^::);
+using size_t = decltype (sizeof 0);
+void *operator new (size_t, void *);
+
+consteval int imm_fn() { return 42; }
+constexpr int not_imm_fn() { return 42; }
+int fn();
+auto id (info i) { return i; }
+
+struct S { info r; };
+struct DS : S { int i; };
+constexpr DS cds{ { ^^int }, 0 };
+DS dsn{ { info{} }, 0 };
+DS ds{ { ^^int }, 0 };	// { dg-error "initialized with a consteval-only value" }
+auto basep1 = (S *) &cds; // { dg-error "initialized with a consteval-only value" }
+constexpr auto basep2 = (S *) &cds;
+
+struct O { consteval int f() const { return 1; } };
+auto pm1 = &O::f;	      // { dg-error "address of an immediate function" }
+constexpr auto pm2 = &O::f;   // { dg-bogus "returns address" "" { xfail *-*-* } }
+
+struct C {
+  info r;
+  constexpr C(info x) : r(x) {}
+};
+constexpr C c1(^^int);
+C c2(info{});
+C c3(^^int);		// { dg-error "initialized with a consteval-only value" }
+
+struct CB : C {
+  constexpr CB(info x) : C(x) {}
+};
+constexpr CB cb1(^^int);
+CB cb2(info{});
+CB cb3(^^int);		// { dg-error "initialized with a consteval-only value" }
+
+struct Deleg {
+  info r;
+  constexpr Deleg() : Deleg(info{}) {}
+  constexpr Deleg(info x) : r(x) {}
+};
+constexpr Deleg cdlg{};
+Deleg dlg{};
+
+struct N {
+  info i = ^^int;
+};
+
+struct N2 {
+  info i = ^^int;	// { dg-error "consteval-only value" }
+  constexpr N2() {}     // not immediate-escalating
+  constexpr N2(info x) : i(x) {}
+};
+constexpr N2 cn2a;  // { dg-error "constant expression" }
+
+struct N3 {
+  info i = ^^int;
+  consteval N3() {}
+  constexpr N3(info x) : i(x) {}
+};
+constexpr N3 cn3a;
+
+struct N4 {
+  info i = ^^int;
+  constexpr N4() = default;
+  constexpr N4(info x) : i(x) {}
+};
+constexpr N4 cn4a;
+N4 n4a;	    // { dg-error "initialized with a consteval-only value" }
+
+struct N5 {
+  static constexpr info si = ^^int;
+  static info si2;
+};
+info N5::si2 = ^^int;	// { dg-error "initialized with a consteval-only value" }
+
+struct L {
+  static constexpr info si = info{};
+  static info si2;
+};
+info L::si2 = info{};
+
+template<typename T, typename U>
+struct V {
+  T t;
+  U u;
+};
+auto v1 = V{info{}, 42};
+constexpr auto v2 = V{^^int, 42};
+auto v3 = V{^^int, 42};	  // { dg-error "initialized with a consteval-only value" }
+
+struct Wrapper { const info &r; };
+Wrapper w1{ ^^int };	    // { dg-error "initialized with a consteval-only value" }
+constexpr Wrapper w2{ ^^int };
+
+info a;
+info *pa;
+info **ppa;
+S b;
+S b2{};
+
+template<typename> struct E;
+void foo (E<int> *);
+
+auto normal() -> void {
+  info c;
+  S d;
+}
+
+constexpr auto r = ^^int;
+auto e1 = ^^int; // { dg-error "initialized with a consteval-only value" }
+auto en = info{};
+
+const info &ref1 = r;	    // { dg-error "initialized with a consteval-only value" }
+constexpr const info &ref2 = r;
+
+constexpr info arr[] = { ^^int, ^^char };
+constexpr info e5 = arr[0];
+info e6 = arr[0];    // { dg-error "initialized with a consteval-only value" }
+auto epast1 = arr + 2;	// { dg-error "initialized with a consteval-only value" }
+constexpr auto epast2 = arr + 2;
+info narr[] = { info{}, info{} };
+info narr2[3];
+info narr3[3]{};
+
+// See PR124249.  This is OK because the initializer is constant:
+// namespace scope, (r, 42) is a constant expression -> i2 has constant
+// initialization -> the initializer is manifestly constant-evaluated
+// -> r is in an immediate function context -> we accept.
+int i1 = (^^int, 42);
+int i2 = (r, 42);
+int i3 = (info{}, 42);
+// No constant initialization, so not MCE.
+int i4 = (r, fn());    // { dg-error "initialized with a consteval-only value" }
+
+const int &gref = (r, 42);
+
+constexpr N cng{};
+N ng;	// { dg-error "initialized with a consteval-only value" }
+static N nsg;	// { dg-error "initialized with a consteval-only value" }
+
+constexpr const info *p = &r;
+const info *q = &r;   // { dg-error "initialized with a consteval-only value" }
+
+struct Ptr {
+  const info *p;
+};
+auto ptr1 = Ptr{ .p = nullptr };
+constexpr auto ptr2 = Ptr{ .p = &r };
+auto ptr3 = Ptr{ .p = &r };    // { dg-error "initialized with a consteval-only value" }
+
+struct Pair { info r; int i; };
+constexpr Pair p1 = { ^^int, 0 };
+Pair p2 = { info{}, 0 };
+Pair p3 = { ^^int, 0 };	  // { dg-error "initialized with a consteval-only value" }
+auto [sb1, sb2] = Pair{^^int, 0}; // { dg-error "initialized with a consteval-only value" }
+constexpr auto [sb3, sb4] = Pair{^^int, 0};
+auto [sb5, sb6] = Pair{info{}, 0};
+
+struct Nested { Pair inner; int c; };
+auto ne = Nested{{^^int, 0}, 1};  // { dg-error "initialized with a consteval-only value" }
+constexpr auto cne = Nested{{^^int, 0}, 1};
+auto ne2 = Nested{{info{}, 0}, 1};
+constexpr auto cne2 = Nested{{info{}, 0}, 1};
+
+consteval auto getr () { return ^^int; }
+consteval auto getnr () { return info{}; }
+constexpr auto r1 = getr ();
+auto r2 = getnr ();
+// r3 is an object with a constituent consteval-only value
+// but is not associated with a constexpr variable.
+auto r3 = getr ();	  // { dg-error "initialized with a consteval-only value" }
+
+union U { info r; int i; };
+// Only the active member counts here.
+constexpr U u1 = { .i = 0 };
+constexpr U u2 = { .r = ^^int };
+auto u3 = U{ .i = 0 };
+auto u4 = U{ .r = ^^int };	// { dg-error "initialized with a consteval-only value" }
+auto u5 = U{ .r = info{} };
+
+template<bool B>
+void
+tfn ()
+{
+  info rn = B ? info{} : ^^int;	  // { dg-error "initialized with a consteval-only value" }
+  info ri = B ? ^^int : info{};	  // { dg-error "initialized with a consteval-only value" }
+}
+
+template<bool B>
+constexpr void
+ctfn ()
+{
+  info rn = B ? info{} : ^^int;
+  info ri = B ? ^^int : info{};
+}
+
+template<bool B>
+void
+uninst ()
+{
+  info rn = B ? info{} : ^^int;
+  info ri = B ? ^^int : info{};
+}
+
+template<typename>
+constexpr void
+esc ()
+{
+  N n;
+}
+
+void
+dflt (info x = ^^int) // { dg-error "consteval-only value outside an immediate function context" }
+{
+}
+
+void
+dflt_null (info x = info{})
+{
+}
+
+consteval void
+dflt_ok (info x = ^^int)
+{
+}
+
+template<auto V> struct TC { };
+auto e7 = TC<^^int>();
+auto e7n = TC<info{}>();
+
+template<Pair> struct D { };
+auto e8 = D<Pair{^^int, 42}>();
+auto e8n = D<Pair{info{}, 42}>();
+
+void
+g (bool b)
+{
+  info null;
+  null = info();
+
+  constexpr auto lr = ^^int;
+  auto e2 = ^^int;	  // { dg-error "initialized with a consteval-only value" }
+  auto en = info{};
+  static constexpr info sg = ^^int;
+  static info sg2 = ^^int;  // { dg-error "initialized with a consteval-only value" }
+  const info &lref1 = r;    // { dg-error "initialized with a consteval-only value" }
+  constexpr const info &lref2 = r;
+
+  // Don't accept these: the "has constant initialization" rule applies to
+  // variables at namespace scope only.
+  int e3 = (^^int, 42);	    // { dg-error "initialized with a consteval-only value" }
+  int e4 = (r, 42);	    // { dg-error "initialized with a consteval-only value" }
+  int e5 = (r, fn());	    // { dg-error "initialized with a consteval-only value" }
+  // But these are static.
+  static int e6 = (^^int, 42);
+  static int e7 = (r, 42);
+  static int e8 = (r, fn());       // { dg-error "initialized with a consteval-only value" }
+
+  // grok_reference_init turns the init into
+  // (const int &) &D.2686 and so store_init_value won't see
+  // any consteval-only value.
+  const int &ref = (r, 42);	  // { dg-error "initialized with a consteval-only value" "" { xfail *-*-* } }
+
+  info rn = b ? info{} : ^^int;	  // { dg-error "initialized with a consteval-only value" }
+  info ri = b ? ^^int : info{};	  // { dg-error "initialized with a consteval-only value" }
+
+  constexpr info larr[] = { ^^int, ^^char };
+  constexpr info le5 = arr[0];
+  info le6 = arr[0];    // { dg-error "initialized with a consteval-only value" }
+  info lnarr[] = { info{}, info{} };
+
+  constexpr const info *lp = &r;
+  const info *lq = &r;   // { dg-error "initialized with a consteval-only value" }
+
+  constexpr C lc1(^^int);
+  C lc2(info{});
+  C lc3(^^int);		// { dg-error "initialized with a consteval-only value" }
+
+  constexpr CB lcb1(^^int);
+  CB lcb2(info{});
+  CB lcb3(^^int);		// { dg-error "initialized with a consteval-only value" }
+
+  Wrapper lw1{ ^^int };	    // { dg-error "initialized with a consteval-only value" }
+  // w2 at block scope has this initializer:
+  // {.r=(<<< Unknown tree: lang_type >>> &) &_ZGRL2w2_}
+  // which is reduced_constant_expression_p but here we have
+  // {.r=(<<< Unknown tree: lang_type >>> &) &D.2718}
+  // which is not reduced_constant_expression_p.  The error
+  // also happens without reflection:
+  // struct W { const int &r; }
+  // void h () { constexpr W lw{42}; }
+  constexpr Wrapper lw2{ ^^int }; // { dg-error "not a constant expression" }
+
+  constexpr N lcng{};
+  N lng;	// { dg-error "initialized with a consteval-only value" }
+
+  constexpr Pair p4 = { ^^int, 0 };
+  Pair p5 = { info{}, 0 };
+  Pair p6 = { ^^int, 0 };	  // { dg-error "initialized with a consteval-only value" }
+  auto [lsb1, lsb2] = Pair{^^int, 0}; // { dg-error "initialized with a consteval-only value" }
+  constexpr auto [lsb3, lsb4] = Pair{^^int, 0};
+  auto [lsb5, lsb6] = Pair{info{}, 0};
+
+  auto lptr1 = Ptr{ .p = nullptr };
+  constexpr auto lptr2 = Ptr{ .p = &r };
+  auto lptr3 = Ptr{ .p = &r };    // { dg-error "initialized with a consteval-only value" }
+
+  auto lne = Nested{{^^int, 0}, 1};  // { dg-error "initialized with a consteval-only value" }
+  constexpr auto lcne = Nested{{^^int, 0}, 1};
+  auto lne2 = Nested{{info{}, 0}, 1};
+  constexpr auto lcne2 = Nested{{info{}, 0}, 1};
+
+  constexpr DS lcds{ { ^^int }, 0 };
+  DS ldsn{ { info{} }, 0 };
+  DS lds{ { ^^int }, 0 };	// { dg-error "initialized with a consteval-only value" }
+  auto lbasep1 = (S *) &cds; // { dg-error "initialized with a consteval-only value" }
+  constexpr auto lbasep2 = (S *) &cds;
+
+  constexpr U lu1 = { .i = 0 };
+  constexpr U lu2 = { .r = ^^int };
+  auto lu3 = U{ .i = 0 };
+  auto lu4 = U{ .r = ^^int };	// { dg-error "initialized with a consteval-only value" }
+  auto lu5 = U{ .r = info{} };
+
+  constexpr auto lr1 = getr ();
+  auto lr2 = getnr ();
+  auto lr3 = getr ();	  // { dg-error "initialized with a consteval-only value" }
+
+  // We think that [expr.const.imm]/2 gives us the rationale for
+  // rejecting this code, even though r isn't ODR-used here.
+  ^^int;  // { dg-error "consteval-only value outside an immediate function context" }
+  r;	  // { dg-error "consteval-only value outside an immediate function context" }
+  (^^int, true);  // { dg-error "consteval-only value outside an immediate function context" }
+  (r, true);	  // { dg-error "consteval-only value outside an immediate function context" }
+  lr;	  // { dg-error "consteval-only value outside an immediate function context" }
+
+  a = info();
+  a = ^^int; // { dg-error "consteval-only value outside an immediate function context" }
+  a = r; // { dg-error "consteval-only value outside an immediate function context" }
+
+  a == a;
+  a == ^^int; // { dg-error "consteval-only value outside an immediate function context" }
+  ^^int != info{};  // { dg-error "consteval-only value outside an immediate function context" }
+  ^^int == ^^int;   // { dg-error "consteval-only value outside an immediate function context" }
+  constexpr bool b1 = ^^int != info{};
+  constexpr bool b2 = ^^int == ^^int;
+
+  a == r; // { dg-error "consteval-only value outside an immediate function context" }
+
+  constexpr N cln;
+  N ln;	// { dg-error "initialized with a consteval-only value" }
+
+  new (&a) info();
+  new (&a) info(^^int); // { dg-error "consteval-only value outside an immediate function context" }
+  new info(info{});
+  new info(^^int);	// { dg-error "consteval-only value outside an immediate function context" }
+
+  S{};
+  S{.r={}};
+  S{.r=^^int}; // { dg-error "consteval-only value outside an immediate function context" }
+
+  auto lam = [] { info l = ^^int; };
+  lam ();
+
+  auto lam2 = [] { return ^^int; };
+  auto e9 = lam2 ();	// { dg-error "initialized with a consteval-only value" }
+  constexpr auto e10 = lam2 ();
+  constexpr auto lam3 = [i = ^^int]{ return i; };
+  auto lam4 = [i = ^^int]{ return i; };	// { dg-error "initialized with a consteval-only value" }
+  auto lam5 = [lr]{ return lr; };	// { dg-error "initialized with a consteval-only value" }
+  constexpr auto lam6 = [lr]{ return lr; };
+
+  if consteval {
+    auto ifc = ^^int;
+  }
+
+  auto x1 = imm_fn ();
+  auto x2 = []() consteval { return ^^int; }();  // { dg-error "initialized with a consteval-only value" }
+
+  id (null);
+  id (^^int);	// { dg-error "consteval-only value outside an immediate function context" }
+  dflt ();
+  dflt_null ();
+  dflt_ok ();
+  tfn<true>();
+  ctfn<true>();
+  esc<int>();
+}
+
+consteval void
+ceg (bool b)
+{
+  info null;
+  null = info();
+
+  constexpr auto lr = ^^int;
+  auto e2 = ^^int;
+  static constexpr info sg = ^^int;
+  static info sg2 = ^^int;
+  const info &lref1 = r;
+  constexpr const info &lref2 = r;
+
+  int e3 = (^^int, 42);
+  int e4 = (r, 42);
+  int e5 = (r, fn());
+  static int e6 = (^^int, 42);
+  static int e7 = (r, 42);
+  static int e8 = (r, fn());
+
+  const int &ref = (r, 42);
+
+  info rn = b ? info{} : ^^int;
+  info ri = b ? ^^int : info{};
+
+  constexpr info larr[] = { ^^int, ^^char };
+  constexpr info le5 = arr[0];
+  info le6 = arr[0];
+  info lnarr[] = { info{}, info{} };
+
+  constexpr const info *lp = &r;
+  const info *lq = &r;
+
+  constexpr C lc1(^^int);
+  C lc2(info{});
+  C lc3(^^int);
+
+  constexpr CB lcb1(^^int);
+  CB lcb2(info{});
+  CB lcb3(^^int);
+
+  Wrapper lw1{ ^^int };
+
+  constexpr N lcng{};
+  N lng;
+
+  constexpr Pair p4 = { ^^int, 0 };
+  Pair p5 = { info{}, 0 };
+  Pair p6 = { ^^int, 0 };
+  auto [lsb1, lsb2] = Pair{^^int, 0};
+  constexpr auto [lsb3, lsb4] = Pair{^^int, 0};
+  auto [lsb5, lsb6] = Pair{info{}, 0};
+
+  auto lptr1 = Ptr{ .p = nullptr };
+  constexpr auto lptr2 = Ptr{ .p = &r };
+  auto lptr3 = Ptr{ .p = &r };
+
+  auto lne = Nested{{^^int, 0}, 1};
+  constexpr auto lcne = Nested{{^^int, 0}, 1};
+  auto lne2 = Nested{{info{}, 0}, 1};
+  constexpr auto lcne2 = Nested{{info{}, 0}, 1};
+
+  constexpr DS lcds{ { ^^int }, 0 };
+  DS ldsn{ { info{} }, 0 };
+  DS lds{ { ^^int }, 0 };
+  auto lbasep1 = (S *) &cds;
+  constexpr auto lbasep2 = (S *) &cds;
+
+  constexpr U lu1 = { .i = 0 };
+  constexpr U lu2 = { .r = ^^int };
+  auto lu3 = U{ .i = 0 };
+  auto lu4 = U{ .r = ^^int };
+  auto lu5 = U{ .r = info{} };
+
+  constexpr auto lr1 = getr ();
+  auto lr2 = getnr ();
+  auto lr3 = getr ();
+
+  ^^int;
+  r;
+  lr;
+  (^^int, true);
+  (r, true);
+
+  a = info();
+  a = ^^int;
+  a = r;
+
+  a == a;
+  a == ^^int;
+  ^^int != info{};
+  ^^int == ^^int;
+  constexpr bool b1 = ^^int != info{};
+  constexpr bool b2 = ^^int == ^^int;
+
+  a == r;
+
+  constexpr N cln;
+  N ln;
+
+  new (&a) info();
+  new (&a) info(^^int);
+  new info(info{});
+  new info(^^int);
+
+  S{};
+  S{.r={}};
+  S{.r=^^int};
+
+  auto lam = [] { info l = ^^int; };
+  lam ();
+
+  auto lam2 = [] { return ^^int; };
+  auto e9 = lam2 ();
+  constexpr auto e10 = lam2 ();
+  constexpr auto lam3 = [i = ^^int]{ return i; };
+  auto lam4 = [i = ^^int]{ return i; };
+  auto lam5 = [lr]{ return lr; };
+  constexpr auto lam6 = [lr]{ return lr; };
+
+  if consteval {
+    auto ifc = ^^int;
+  }
+
+  auto x1 = imm_fn ();
+  auto x2 = []() consteval { return ^^int; }();
+
+  id (null);
+  id (^^int);
+}
+
+constexpr void
+cg (bool b)
+{
+  int e3 = (^^int, 42);	    // { dg-error "initialized with a consteval-only value" }
+  int e4 = (r, 42);	    // { dg-error "initialized with a consteval-only value" }
+  int e5 = (r, fn());	    // { dg-error "initialized with a consteval-only value" }
+  static int e6 = (^^int, 42);
+  static int e7 = (r, 42);
+  static int e8 = (r, fn());       // { dg-error "initialized with a consteval-only value" }
+
+  info rn = b ? info{} : ^^int;	// { dg-error "initialized with a consteval-only value" }
+  info ri = b ? ^^int : info{};	// { dg-error "initialized with a consteval-only value" }
+
+  constexpr auto lr1 = getr ();
+  auto lr2 = getnr ();
+  auto lr3 = getr ();	  // { dg-error "consteval-only value" }
+}
diff --git a/gcc/testsuite/g++.dg/reflect/value2.C b/gcc/testsuite/g++.dg/reflect/value2.C
new file mode 100644
index 00000000000..1c73055acf7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/value2.C
@@ -0,0 +1,32 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+using info = decltype(^^::);
+
+// [expr.const.const]
+
+consteval int plus1(int x) { return x + 1; }
+template <auto V> struct C { };
+
+auto a = plus1; // { dg-error "taking address of an immediate function" }
+constexpr auto b = plus1; // // { dg-bogus "returns address" "" { xfail *-*-* } } OK
+auto c = C<plus1>(); // OK
+
+auto d = ^^int; // { dg-error "initialized with a consteval-only value" }
+auto e = C<^^char>(); // OK
+
+// [expr.const.imm]
+
+consteval info refl() { return ^^int; }
+template <auto F>
+constexpr void ex() {
+  auto x = F();
+}
+auto p = &ex<refl>; // { dg-error "taking address of an immediate function" }
+
+consteval int id(int x) { return x; }
+template <auto F>
+constexpr auto apply_to(int i) { return F(i); }
+auto q = &apply_to<id>; // { dg-error "taking address of an immediate function" }
diff --git a/gcc/testsuite/g++.dg/reflect/value3.C b/gcc/testsuite/g++.dg/reflect/value3.C
new file mode 100644
index 00000000000..07589afa972
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/value3.C
@@ -0,0 +1,63 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection -g" }
+
+using info = decltype(^^::);
+
+struct A { info i; };
+struct B : A { };
+struct C { A a; };
+struct D { info i1, i2, i3; };
+struct E { C c; info i; };
+struct F { A a[10]; };
+struct G { info i = info{}; };
+
+A a1 = { info{} };
+A a2{ info{} };
+B b1 = { info{} };
+B b2{ info{} };
+C c1 = { { info{} } };
+C c2{ { info{} } };
+D d1 = { info{}, info{}, info{} };
+D d2{ info{}, info{}, info{} };
+E e1 = { { { info{} } }, info{} };
+E e2{ { { info{} } }, info{} };
+F f1 = { { { info{} }, { info{} } } };
+F f2{ { { info{} }, { info{} } } };
+G g1{};
+G g2;
+
+void
+fn ()
+{
+  A la1 = { info{} };
+  A la2{ info{} };
+  B lb1 = { info{} };
+  B lb2{ info{} };
+  C lc1 = { { info{} } };
+  C lc2{ { info{} } };
+  D ld1 = { info{}, info{}, info{} };
+  D ld2{ info{}, info{}, info{} };
+  E le1 = { { { info{} } }, info{} };
+  E le2{ { { info{} } }, info{} };
+  F lf1 = { { { info{} }, { info{} } } };
+  F lf2{ { { info{} }, { info{} } } };
+  G lg1{};
+  G lg2;
+
+  static A sa1 = { info{} };
+  static A sa2{ info{} };
+  static B sb1 = { info{} };
+  static B sb2{ info{} };
+  static C sc1 = { { info{} } };
+  static C sc2{ { info{} } };
+  static D sd1 = { info{}, info{}, info{} };
+  static D sd2{ info{}, info{}, info{} };
+  static E se1 = { { { info{} } }, info{} };
+  static E se2{ { { info{} } }, info{} };
+  static F sf1 = { { { info{} }, { info{} } } };
+  static F sf2{ { { info{} }, { info{} } } };
+  static G sg1{};
+  static G sg2;
+}
diff --git a/gcc/testsuite/g++.dg/reflect/value4.C b/gcc/testsuite/g++.dg/reflect/value4.C
new file mode 100644
index 00000000000..26ba9a9d371
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/value4.C
@@ -0,0 +1,24 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+using info = decltype(^^::);
+
+struct V { int a, b, c; };
+constexpr info varr[] = { ^^V::a, ^^V::b, ^^V::c };
+constexpr const info *p = varr;
+static_assert (p[0] == ^^V::a);
+static_assert (p[1] == ^^V::b);
+static_assert (p[2] == ^^V::c);
+static_assert (varr[0] == ^^V::a);
+
+constexpr info arr[] = { ^^int, ^^double };
+auto q = &arr[0]; // { dg-error ".q. is initialized with a consteval-only value but is not declared .constexpr." }
+
+void
+fn ()
+{
+  auto r = arr; // { dg-error ".r. is initialized with a consteval-only value but is not declared .constexpr." }
+  (void) r;
+}
diff --git a/gcc/testsuite/g++.dg/reflect/value5.C b/gcc/testsuite/g++.dg/reflect/value5.C
new file mode 100644
index 00000000000..9dec04e7247
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/value5.C
@@ -0,0 +1,40 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+using info = decltype(^^::);
+
+struct S
+{
+  info i;
+  int n;
+};
+
+constexpr info arr[] = { ^^int, ^^double, ^^char };
+constexpr S s = { ^^int, 42 };
+constexpr S sarr[2] = { { ^^int, 1 }, { ^^double, 2 } };
+constexpr const info *p = arr;
+
+// These check check_out_of_consteval_use which uses a tree walk
+// on its expr.  value6.C checks consteval_only_p not used in
+// a tree walk.
+const void *vp = &p;		  // { dg-error "initialized with a consteval-only value" }
+
+const auto p1 = &arr[0];	  // { dg-error "initialized with a consteval-only value" }
+const auto p2 = arr;	  // { dg-error "initialized with a consteval-only value" }
+const auto p3 = arr + 1;	  // { dg-error "initialized with a consteval-only value" }
+const auto p4 = arr + 3;	  // { dg-error "initialized with a consteval-only value" }
+const auto p5 = &s;		  // { dg-error "initialized with a consteval-only value" }
+const auto p6 = &s.i;	  // { dg-error "initialized with a consteval-only value" }
+const auto p7 = &sarr[1];	  // { dg-error "initialized with a consteval-only value" }
+const auto p8 = &sarr[1].i;	  // { dg-error "initialized with a consteval-only value" }
+
+struct Holder { const info *p; int n; };
+auto h = Holder{ &arr[0], 1 };	  // { dg-error "initialized with a consteval-only value" }
+
+constexpr bool cond = true;
+const auto p_cond = cond ? &arr[0] : &arr[1];	  // { dg-error "initialized with a consteval-only value" }
+
+// Not an error.
+auto pdm = &S::i;
diff --git a/gcc/testsuite/g++.dg/reflect/value6.C b/gcc/testsuite/g++.dg/reflect/value6.C
new file mode 100644
index 00000000000..4bff1b81179
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/value6.C
@@ -0,0 +1,41 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+using info = decltype(^^::);
+
+struct S
+{
+  info i;
+  int n;
+};
+
+constexpr info arr[] = { ^^int, ^^double, ^^char };
+constexpr S s = { ^^int, 42 };
+constexpr S sarr[2] = { { ^^int, 1 }, { ^^double, 2 } };
+constexpr const info *p = arr;
+
+constexpr const void *vp = &p;
+
+// This tests that consteval_only_value_p properly detects
+// consteval-only values when they are in ADDR_EXPR, POINTER_PLUS_EXPR,
+// ARRAY_REF, and similar.  A failure would result in a link error.
+constexpr const auto p1 = &arr[0];
+constexpr const auto p2 = arr;
+constexpr const auto p3 = arr + 1;
+constexpr const auto p4 = arr + 3;
+constexpr const auto p5 = &s;
+constexpr const auto p6 = &s.i;
+constexpr const auto p7 = &sarr[1];
+constexpr const auto p8 = &sarr[1].i;
+
+constexpr bool cond = true;
+constexpr auto p_cond = cond ? &arr[0] : &arr[1];
+
+struct Holder { const info *p; int n; };
+constexpr Holder h = { &arr[0], 1 };
+
+constexpr auto cpdm = &S::i;
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/reflect/value7.C b/gcc/testsuite/g++.dg/reflect/value7.C
new file mode 100644
index 00000000000..8b5e2434eae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/value7.C
@@ -0,0 +1,52 @@ 
+// PR c++/125820
+// P4101R1, Consteval-only Values for C++26
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+// Like value6.C, but with function-local static.
+
+using info = decltype(^^::);
+
+struct S
+{
+  info i;
+  int n;
+};
+struct Holder { const info *p; int n; };
+
+void
+g ()
+{
+  constexpr static info arr[] = { ^^int, ^^double, ^^char };
+  constexpr static S s = { ^^int, 42 };
+  constexpr static S sarr[2] = { { ^^int, 1 }, { ^^double, 2 } };
+  constexpr static const info *p = arr;
+
+  constexpr const void *vp = &p;
+  constexpr const auto p1 = &arr[0];
+  constexpr const auto p2 = arr;
+  constexpr const auto p3 = arr + 1;
+  constexpr const auto p4 = arr + 3;
+  constexpr const auto p5 = &s;
+  constexpr const auto p6 = &s.i;
+  constexpr const auto p7 = &sarr[1];
+  constexpr const auto p8 = &sarr[1].i;
+  constexpr bool cond = true;
+  constexpr auto p_cond = cond ? &arr[0] : &arr[1];
+  constexpr Holder h = { &arr[0], 1 };
+  constexpr auto cpdm = &S::i;
+
+  constexpr static const void *svp = &p;
+  constexpr static const auto sp1 = &arr[0];
+  constexpr static const auto sp2 = arr;
+  constexpr static const auto sp3 = arr + 1;
+  constexpr static const auto sp4 = arr + 3;
+  constexpr static const auto sp5 = &s;
+  constexpr static const auto sp6 = &s.i;
+  constexpr static const auto sp7 = &sarr[1];
+  constexpr static const auto sp8 = &sarr[1].i;
+  constexpr static auto sp_cond = cond ? &arr[0] : &arr[1];
+  constexpr static Holder sh = { &arr[0], 1 };
+  constexpr static auto scpdm = &S::i;
+}
+
+int main () {}
diff --git a/gcc/testsuite/g++.dg/reflect/vector2.C b/gcc/testsuite/g++.dg/reflect/vector2.C
new file mode 100644
index 00000000000..31e8e24aa65
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/vector2.C
@@ -0,0 +1,26 @@ 
+// { dg-do run { target c++26 } }
+// { dg-additional-options "-freflection" }
+// No use of Reflection in this test, but it crashed with Reflection
+// enabled.
+
+#include <vector>
+#include <ranges>
+
+using VVI = std::vector<std::vector<int>>;
+using JV = decltype (std::views::join (std::declval<VVI> ()));
+
+static_assert (std::ranges::input_range<JV>);
+static_assert (std::ranges::forward_range<JV>);
+
+int
+main ()
+{
+  VVI vv { { 1, 2 }, { 3, 4, 5 } };
+  int n = 0;
+  for (auto &elt : vv | std::views::join)
+    {
+      (void) elt;
+      ++n;
+    }
+  return n == 5 ? 0 : 1;
+}
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 69a7c7e6d34..db3713c2f1b 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -1860,6 +1860,8 @@  wide_int_to_tree_1 (tree type, const wide_int_ref &pcst)
 
       switch (code)
 	{
+	/* This could represent the C++ std::meta::info type.  */
+	case LANG_TYPE:
 	case NULLPTR_TYPE:
 	  gcc_assert (hwi == 0);
 	  /* Fallthru.  */
@@ -2035,6 +2037,8 @@  cache_integer_cst (tree t, bool might_duplicate ATTRIBUTE_UNUSED)
      wide_int_to_type_1.  */
   switch (TREE_CODE (type))
     {
+    /* This could represent the C++ std::meta::info type.  */
+    case LANG_TYPE:
     case NULLPTR_TYPE:
       gcc_checking_assert (integer_zerop (t));
       /* Fallthru.  */
diff --git a/libstdc++-v3/include/std/meta b/libstdc++-v3/include/std/meta
index bc04dddcb64..3b49a17ad22 100644
--- a/libstdc++-v3/include/std/meta
+++ b/libstdc++-v3/include/std/meta
@@ -113,7 +113,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       consteval exception& operator=(const exception&) = default;
       consteval exception& operator=(exception&&) = default;
 
-      consteval const char*
+      constexpr const char*
       what() const noexcept override
       {
 	// If u8string is not empty and string is empty, conversion