diff mbox series

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

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

Commit Message

Marek Polacek Sept. 2, 2026, 4:57 p.m. UTC
On Tue, Sep 01, 2026 at 11:20:30PM -0400, Jason Merrill wrote:
> 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)?

I'd thought about it but cp_fold_immediate_r only sees the folded
init so we would not give errors for block-scope

  int i = (^^int, 42);

since the initializer then is just 42.

> > 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?

Ah sure.  Initially I didn't want to repeat the pp_cxx_type_specifier_seq
call so I just had it fall through to it, but that was too ugly so I just
duplicated it, but didn't move the LANG_TYPE case.  Done now.

> >       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?

I don't believe so because build_zero_init_1 has:

  else if (REFLECTION_TYPE_P (type))
    /* [dcl.init.general]: "if T is std::meta::info, the object is initialized
       to a null reflection value".  */
    init = get_null_reflection ();

I've added null7.C to exercise this a bit more and found no issues.

Re-ran only dg.exp since there were no significant changes.

-- >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/null7.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/null7.C          |  31 +
 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 +-
 62 files changed, 1337 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/null7.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: 0acb69bf9bb0db14b7e230fb5370257c530fb6e8

Comments

Jason Merrill Sept. 2, 2026, 7:44 p.m. UTC | #1
On 9/2/26 12:57 PM, Marek Polacek wrote:
> On Tue, Sep 01, 2026 at 11:20:30PM -0400, Jason Merrill wrote:
>> 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)?
> 
> I'd thought about it but cp_fold_immediate_r only sees the folded
> init so we would not give errors for block-scope
> 
>    int i = (^^int, 42);
> 
> since the initializer then is just 42.
> 
>>> 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?
> 
> Ah sure.  Initially I didn't want to repeat the pp_cxx_type_specifier_seq
> call so I just had it fall through to it, but that was too ugly so I just
> duplicated it, but didn't move the LANG_TYPE case.  Done now.
> 
>>>        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?
> 
> I don't believe so because build_zero_init_1 has:
> 
>    else if (REFLECTION_TYPE_P (type))
>      /* [dcl.init.general]: "if T is std::meta::info, the object is initialized
>         to a null reflection value".  */
>      init = get_null_reflection ();
> 
> I've added null7.C to exercise this a bit more and found no issues.

That test could also use a default-initialized (namespace-scope) info 
variable; I suspect that doesn't go through build_zero_init_1, 
especially since you don't change zero_init_p.

And generally it seems more robust to implement this comparison in case 
we do find ourselves comparing to things that have already been lowered, 
e.g. in late instantiation or mangling.

Jason
diff mbox series

Patch

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index a78150786c9..ed8b8034537 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -11534,37 +11534,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);
 
@@ -12654,7 +12623,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 6b9c8c1ba3f..020de12a6b4 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)
@@ -9505,9 +9506,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;
@@ -9518,7 +9520,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..f844039eb99 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);
@@ -1942,7 +1946,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);
@@ -1975,6 +1978,14 @@  cxx_pretty_printer::type_id (tree t)
       }
       break;
 
+    case LANG_TYPE:
+      if (REFLECTION_TYPE_P (t))
+	{
+	  pp_cxx_type_specifier_seq (this, t);
+	  break;
+	}
+      gcc_fallthrough ();
+
     default:
       c_pretty_printer::type_id (t);
       break;
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 27183cd9885..662cf7f2589 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
@@ -10000,10 +10006,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
@@ -10028,10 +10030,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
@@ -12893,12 +12894,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 dd0efa54112..1c57535aab6 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -17395,7 +17395,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;
 
@@ -26865,7 +26864,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);
 
@@ -29011,8 +29010,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 469f71dcab6..f1dff113e8a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -15007,12 +15007,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 1d88d6c574b..79651f3e15d 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -5174,6 +5174,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/null7.C b/gcc/testsuite/g++.dg/reflect/null7.C
new file mode 100644
index 00000000000..1e4bd03d67f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/null7.C
@@ -0,0 +1,31 @@ 
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection" }
+
+using info = decltype(^^::);
+
+info g = info ();
+
+struct S { info a, b; };
+constexpr S s = { ^^::S };
+static_assert (s.b == info ());
+
+constexpr S s2[2] = { { ^^::S, ^^::S } };
+static_assert (s2[1].a == info ());
+
+S gs = {};
+info garr[3] = { };
+
+constexpr info arr[3] = { ^^::S };
+static_assert (arr[1] == arr[2]);
+static_assert (arr[2] == info ());
+
+struct T { info r; T () = default; };
+constexpr T t {};
+static_assert (t.r == info ());
+
+bool h (S s) { return s.b == info (); }
+
+constexpr info r = info ();
+static_assert (r == info ());
+
+info *k () { static info lg = info (); return &lg; }
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