From patchwork Wed Sep 2 20:37:30 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 2297028 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ig6AAzFf; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=38.145.34.32; helo=vm01.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4hZvhN0Bwbz1xwZ for ; Thu, 03 Sep 2026 06:38:42 +1000 (AEST) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9B9844B9DB60 for ; Wed, 2 Sep 2026 20:38:40 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9B9844B9DB60 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ig6AAzFf X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 3F9494BA900E for ; Wed, 2 Sep 2026 20:37:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3F9494BA900E Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 3F9494BA900E Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788381469; cv=none; b=S8Rj6Hk6SGsiqhZvNueKK2p9W9munItg9Y7hvsnf9k06dCETad9zXG4a8IB7ER7iy7pflSZ2pcSbiiZWh8E2l9VFkjqoO6AJjGwJmuJc7N4W8irDUadtuHbBXp82yQ6Rz28W7sbEHbRHrHS93qGhda1ZuNLoBTM5k6UwaWa/H7o= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1788381469; c=relaxed/simple; bh=+jxahTaINhOFLzjJjQNLxG2NyUB99usqjeT0Crhfhdc=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=wsX+rJN/BjDZ105ZrOFI7UnQmLRRnaClwVTDukZCqoA2QfGwyXRRpvWI57W+j4/ncY+yZJB5UX3FZLAHxg6Rb7cFOm5pEqguaEmw9ZlSgUxg0nl4FJNX2T5WBQt9YLq3uiCb+VEUiSQDjNgjt256B9uGnTQcPNVJssxPqO75nRw= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ig6AAzFf DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3F9494BA900E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1788381468; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=6NFuOudhxrmRzaqy7Qp58v7WYz6bl4lnacNKLQ1+2DQ=; b=Ig6AAzFf8M/mKSEyGo1U136oL4eWnLNfohhCwfzQHLRgClOxW7A+/6vYtgL3wmySXjHj6P UpF3abZ4iGj56xYMl9eeuC2ta7mM5fhcMTidV/GMSJY4F0tgVZ7Z26IrNJ2ZLxT69eZ9lY HZAm6rQrwPs4WdjfOEkAAWFts0BBI3c= Received: from mail-qv1-f72.google.com (mail-qv1-f72.google.com [209.85.219.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-627-2EcJER4tPIi5HNmzOlj41g-1; Wed, 02 Sep 2026 16:37:38 -0400 X-MC-Unique: 2EcJER4tPIi5HNmzOlj41g-1 X-Mimecast-MFC-AGG-ID: 2EcJER4tPIi5HNmzOlj41g_1788381458 Received: by mail-qv1-f72.google.com with SMTP id 6a1803df08f44-90c8da50f8dso36997006d6.2 for ; Wed, 02 Sep 2026 13:37:38 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788381458; x=1788986258; h=user-agent:in-reply-to:content-disposition:content-type :mime-version:references:message-id:subject:cc:to:from:date:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=6NFuOudhxrmRzaqy7Qp58v7WYz6bl4lnacNKLQ1+2DQ=; b=Unm5y6+Iw2evJ/pgf9Ps8b2vICSK7IrYLsF1ZZh6Hqn3iUh95j5n8nYlGnSzfxzRut W5GZnF4TBjtbT/cSn48pEwD41B0kfGZXhspwrRXp2jEp3dJbzpWsWn46kbAw468lmVRv 2vAgmSNUQzKdVrLh8c6YBGpA7z4LtbafMHOaMQX6V2bdXdYv9xGCWAYDjH+phmJCScB8 Ek96cJ0y+JXOwRwYoIZUGQArZUF0ZqHoov11uPjBL3geyL1BybH/FkXPBSZmjifjcsO7 OZOhNqPt/jiIK0sCmwSraA0Yb1mzFt1S2kXS3ZxQnlZaARuapGxz77hBnSEZcLmpv461 vdHw== X-Gm-Message-State: AFuF++kKS/UThDhpN1b4RqWGXRH6tsuTVnuDTUCuOYi/AtFBzMQ7Kn7N z2sH7m64JULJr6ZZBqAHacFoYpqOxKE38EPOYdYKt5pyClZsyzhpq2Dsjak1xM/LPLQSwuYl7uM qXOhNt0Wc8OH/mccyETcUHMhEqLjjeahi0dIiL5CC+1LPOFUGVsLvmw3Kx3k4KxKK19c= X-Gm-Gg: AYBFou2xOOn9bojen6L81kxYDa50h/rQFodI1sikB/d/Hjv++RPIVoJpNi53sZt31pt g2XTzw5g15kw0yYV1TFp6L4GIWib4T34XK2wxPAoSySqxRYfE0cTSaKVAZ90Avb4W9/PjsdH93L kRzeMiCTq+4yFu7l3W6nKyBMJssnHOEE4EzzMBoFOYjiXLJsecby+zBYk9r1SNigBGktB/9ooWj de6vh2OwzrsKeG2GRLoE/zH+aauTeuYLeLKIAs11tskN89yS/r/nu2pdWeYffkR9CLo3Y+gkhgq 7scsYhIg9p9fIz/v2AfYpTH1aCSijSM5tQVEZz8bB3WjmUKMSXsyGjx0tCcZgIn2ezrKSbjZzsd HVg== X-Received: by 2002:a05:622a:388:b0:51b:60f1:689a with SMTP id d75a77b69052e-53036b90446mr90222351cf.7.1788381455345; Wed, 02 Sep 2026 13:37:35 -0700 (PDT) X-Received: by 2002:a05:622a:388:b0:51b:60f1:689a with SMTP id d75a77b69052e-53036b90446mr90220411cf.7.1788381453269; Wed, 02 Sep 2026 13:37:33 -0700 (PDT) Received: from redhat.com ([2603:7002:1f00:31d2:1607:6e59:bac6:6d08]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-5303306cedfsm26683251cf.13.2026.09.02.13.37.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Sep 2026 13:37:32 -0700 (PDT) Date: Wed, 2 Sep 2026 16:37:30 -0400 From: Marek Polacek To: Jason Merrill Cc: GCC Patches Subject: [PATCH v5] c++/reflection: P4101, Consteval-only values [PR125820] Message-ID: References: <20260823220801.1575358-1-polacek@redhat.com> <9b620bbc-21a7-41d9-94de-f2ff88633ef1@redhat.com> <9d798dfc-5b6c-4e2c-a566-0967763e0d99@redhat.com> <851915fe-14c2-4462-93b2-227d51f3bcdf@redhat.com> <0630a587-99c2-413c-87ad-d2ceaa0dc82c@redhat.com> <774901cd-4d0e-405f-ac0d-e9f6b90b8b77@redhat.com> MIME-Version: 1.0 In-Reply-To: <774901cd-4d0e-405f-ac0d-e9f6b90b8b77@redhat.com> User-Agent: Mutt/2.4.0 (2026-06-19) X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: wIVul5R0_wqSFySPwzz93qXRNisgT6nKYlPGB4jIMPA_1788381458 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org On Wed, Sep 02, 2026 at 03:44:30PM -0400, Jason Merrill wrote: > 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** 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; > > > > > > > > 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. Added, though at least value1.C also tests it. We still go through build_zero_init_1, but... > 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. ...this is easy to implement. It's the change in cxx_eval_binary_expression and compare_reflections. dg.exp passed. -- >8 -- 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_binary_expression): Check REFLECTION_TYPE_P instead of REFLECT_EXPR_P. (cxx_eval_outermost_constant_expr): Delete consteval-only smuggling detection code. (potential_constant_expression_1) : 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) : New, call rewrite_null_reflection on it. : 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. : Don't call wipe_consteval_only_r. (cp_fold_r) : New, detect invalid uses of consteval-only values here. : New, remove discarded-value expressions. (cp_genericize_r) : 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) : Remove. : New. (cxx_pretty_printer::type_id) : Remove. : 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) : Handle REFLECTION_TYPE_P. : Remove. (dump_type_prefix) : Remove. (dump_type_suffix) : Remove. (dump_expr): Use REFLECTION_TYPE_P. * init.cc (perform_member_init): Don't call check_out_of_consteval_use. * mangle.cc (write_type) : Remove. : Handle REFLECTION_TYPE_P. * module.cc (trees_out::type_node) : Remove. : New. (trees_in::tree_node) : Remove. : New. * name-lookup.cc (name_lookup::adl_type): Move the REFLECTION_TYPE_P handling to case LANG_TYPE. * pt.cc (tsubst) : Remove. (unify) : Remove. : 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. (compare_reflections): Deal with null reflections first. (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) : 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) : Return 1. * tree.cc (wide_int_to_tree_1) : New. (cache_integer_cst) : 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 | 40 +- 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 | 286 +++++---- 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 | 32 ++ 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, 1343 insertions(+), 442 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: 00dafc5b715e0ba6adb42356addd6859e72b094b diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index a78150786c9..7c4e1c2da66 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -5592,7 +5592,8 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, lhs = cplus_expand_constant (lhs); else if (TREE_CODE (rhs) == PTRMEM_CST) rhs = cplus_expand_constant (rhs); - else if (REFLECT_EXPR_P (lhs) && REFLECT_EXPR_P (rhs)) + else if (REFLECTION_TYPE_P (TREE_TYPE (lhs)) + && REFLECTION_TYPE_P (TREE_TYPE (rhs))) { const bool eq = compare_reflections (lhs, rhs); r = constant_boolean_node (eq == is_code_eq, type); @@ -11534,37 +11535,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 +12624,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_("")); else if (t == unknown_type_node) pp_string (pp, M_("")); + 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 &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..2def4ffb8ae 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 &); + +/* 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 &seen) { - /* The set of class types we've seen. */ - hash_set 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; + + 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; +} -/* 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. */ +/* 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. -bool -consteval_only_p (tree t) + Also return true for consteval-only values. This doesn't check for + immediate functions. */ + +static bool +consteval_only_p (tree t, hash_set &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; + /* 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); - 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; - - 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; + for (constructor_elt &elt : CONSTRUCTOR_ELTS (t)) + if (consteval_only_p (elt.value, seen)) + return true; + return false; + } - if (!COMPLETE_TYPE_P (t) && LAMBDA_TYPE_P (t)) - /* Defer until we've definitely gone through prune_lambda_captures. */ - return tristate::unknown (); + return t && consteval_only_value_p (t, seen); +} - 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; +/* Wrapper for consteval_only_p that sets up a hash set. */ - 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; - } - - if (r.is_true ()) - hash_map_safe_put (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 (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 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 *>(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 *) 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 *>(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 *>(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 *>(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 *>(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 % " - "used outside a constant-evaluated context", t); - inform (DECL_SOURCE_LOCATION (t), "add %"); - } - 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; } @@ -9117,6 +9115,9 @@ check_out_of_consteval_use (tree expr, bool complain/*=true*/) bool compare_reflections (tree lhs, tree rhs) { + if (null_reflection_p (lhs) || null_reflection_p (rhs)) + return null_reflection_p (lhs) == null_reflection_p (rhs); + reflect_kind lkind; do { @@ -9238,21 +9239,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, "% has padding bits and no value bits"); return false; + case LANG_TYPE: + if (REFLECTION_TYPE_P (t)) + { + if (explain) + inform (loc, "% 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** 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 %", decl); + inform (DECL_SOURCE_LOCATION (decl), "add %"); + 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; +template struct A; // { 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(b); // { dg-message "from here" } - (void) std::bit_cast(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(b); + (void) std::bit_cast(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(b); // { dg-error "consteval-only value outside an immediate function context" } + (void) std::bit_cast(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 -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(&r); // { dg-error "pointer into an object of consteval-only type" } + constexpr auto x = foo(&r); constexpr auto y = bar(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(); 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 struct X : T { }; -auto a = X{}; // { dg-error "outside a constant-evaluated context" } +auto a = X{}; auto b = X{}; 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 @@ -24,8 +24,8 @@ struct X { T t; }; -X x1; // { dg-error "consteval-only variable" } -constinit X x2{}; // { dg-error "consteval-only variable" } +X x1; +constinit X x2{}; constexpr X 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 -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..96bdc999c53 --- /dev/null +++ b/gcc/testsuite/g++.dg/reflect/null7.C @@ -0,0 +1,32 @@ +// { dg-do compile { target c++26 } } +// { dg-additional-options "-freflection" } + +using info = decltype(^^::); + +info g = info (); +info g2; + +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 ≶ } 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 -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 T fn2 () { return ^^void; } // { dg-error "consteval-only" } -const info i = fn2(); // { dg-error "consteval-only variable" } +const info i = fn2(); 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 mData; }; -const Array A::mData{}; // { dg-error "outside a constant-evaluated context" } +const Array A::mData{}; struct B { static constexpr Array 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 +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 + +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 +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 struct E; +void foo (E *); + +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 +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 +constexpr void +ctfn () +{ + info rn = B ? info{} : ^^int; + info ri = B ? ^^int : info{}; +} + +template +void +uninst () +{ + info rn = B ? info{} : ^^int; + info ri = B ? ^^int : info{}; +} + +template +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 struct TC { }; +auto e7 = TC<^^int>(); +auto e7n = TC(); + +template struct D { }; +auto e8 = D(); +auto e8n = D(); + +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(); + ctfn(); + esc(); +} + +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 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(); // 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 +constexpr void ex() { + auto x = F(); +} +auto p = &ex; // { dg-error "taking address of an immediate function" } + +consteval int id(int x) { return x; } +template +constexpr auto apply_to(int i) { return F(i); } +auto q = &apply_to; // { 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 +#include + +using VVI = std::vector>; +using JV = decltype (std::views::join (std::declval ())); + +static_assert (std::ranges::input_range); +static_assert (std::ranges::forward_range); + +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