From patchwork Mon Jul 6 16:08:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladislav Semykin X-Patchwork-Id: 138601 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 487684BA2E13 for ; Mon, 6 Jul 2026 16:09:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 487684BA2E13 Authentication-Results: sourceware.org; dkim=fail reason="signature verification failed" (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=ezR5YAxB X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-yw1-x112e.google.com (mail-yw1-x112e.google.com [IPv6:2607:f8b0:4864:20::112e]) by sourceware.org (Postfix) with ESMTPS id D3A9E4BA2E04 for ; Mon, 6 Jul 2026 16:08:34 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D3A9E4BA2E04 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D3A9E4BA2E04 Authentication-Results: sourceware.org; arc=pass smtp.remote-ip=2607:f8b0:4864:20::112e ARC-Seal: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1783354115; cv=pass; b=gdf70SdE6EAPo2XPIsq+euVSmMc+LtsWqPaLjon0k/NqcKrPwuUlAiNoXHJrb3dlWTBpgZ43egGy44zjPslfSvsCvwor1/K4thTeZeWz/Fxgn4iNjNBwlkhbxYt8vTeq7bluokGoq5AaH4tMf5XwGoeQeHIhHFc5POjSvpLzCxw= ARC-Message-Signature: i=2; a=rsa-sha256; d=sourceware.org; s=key; t=1783354115; c=relaxed/simple; bh=VjB+eYmoZZ/hzESeD4Z52bV7j3ZpvXk4ywjKsf88bMA=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=QCXWdliRjI3mzD3LIJ+RmlSt6sZcOQqyyB9aLbTXOvrrf15/F7w5rRNFdiIYK60v0CM0DqPLHkUponjsIX1r4wWTThN3uI8ikHL8dZPu6sQG3aRGoplroyjXRjDcXgQ+TBzaLJuYo+Kw2Xy+0xyDEoCXjienCvYFceE509bpS6s= ARC-Authentication-Results: i=2; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=ezR5YAxB DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D3A9E4BA2E04 Received: by mail-yw1-x112e.google.com with SMTP id 00721157ae682-80bb578d58bso26697657b3.0 for ; Mon, 06 Jul 2026 09:08:34 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1783354114; cv=none; d=google.com; s=arc-20260327; b=WmEYv/F1N0e+g2dkxpnZ+3BLGTMv/l1yqa/2asidF1UfwgHD5SmdxjRXFVgaWmI62+ CjxKid8JAUZcJP5Xhoo/WmbnG3QkhB+Rv/ZUNpLOvZadXCtSdXE+ukER5tsI4T6y6+I/ Zn572m2jpfQAErU4lbgzjlfcFpMT9uSb0Cang2aizXCdx7D2hP9+X2vDi6giX7yIYyDU XNj9gdiMsDC82YsuhQzqZxmMijBUOpgDzxrT5K0QvWh04ox2blpc2s6tY3coSOYQ6yL4 jltELyw+pLPT8eZwOrRBN2rJu6mfLSBAGHa9Ypoq1gT85gHcgyurgqrq75mauLAVxIQo 7lqw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=to:subject:message-id:date:from:mime-version:dkim-signature; bh=eiBN3LJ94XImd98DDQdiC8tOHZB9SJUEgDgvxfcRfc8=; fh=hPrbWPhweUx4V0GV9uXJqbyAzg2ABmTz7kczrAQqMmM=; b=EW/QfdXE+oZJv78nUiOCaYa13bU7AaU3Rb2NaY8kb5JpVmZ2muyq/G5Ns53E78cjaN JV37XftddgTJZg8Zgwd78B1GLYeuxO3O8WeAkgDVBlF7rBxfV71COvcvC1uAGu+0/hVA uWNuBGADvZjgyriw60OBImpVBAGVzXt5nNbibSz2o7brt+tP0GOwQ/7fNikM2EzhDDPu NMeFcPze1/8UdSCaoMje3TyZ4QZQ43G2Z5ooJI2yMCzfAiyAtvncJn6HOMWP5ma+IIEQ WG3oK0Pk5rt0weeZGbhmLxf+oDgbmTC78u8MpTNfLkEdLC8yrBa3C4RfUrLtOLra32co aeWA==; darn=gcc.gnu.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783354114; x=1783958914; darn=gcc.gnu.org; h=content-type:to:subject:message-id:date:from:mime-version:from:to :cc:subject:date:message-id:reply-to:content-type; bh=eiBN3LJ94XImd98DDQdiC8tOHZB9SJUEgDgvxfcRfc8=; b=ezR5YAxBzbPd0I248llL8BcEduyIMnzJpPdYXIZLLF387aKCdIBSSLJcYAN6d3nBhG GPBjcSK9Rdyi6wAoJD7AGYKf9P9krkZEaIZ//YE3q+Sl+WmwlvrjU5opaRVLsQwcatgO vjNcdhRRKKpjdkMZjMSDqu+48qMxOIMcFqjlm4TFZmGBu29gtAOLJAULSbTREko9/kCI 4OT4cqEuE2Kvsbv7fn3/WnWyBOZa2K/1wobudXtcE0GqCi/Ky0yaXfyZAPOzzBpK2QcS oQHick4sfTlKik+9DzbbnEfgPuORZ574XaRrEZSa4IQ+HhkDmDjO3hqNilF46bUMN2OL KiCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783354114; x=1783958914; h=content-type:to:subject:message-id:date:from:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=eiBN3LJ94XImd98DDQdiC8tOHZB9SJUEgDgvxfcRfc8=; b=QBi1lTP5vUH+aciTDlfGeul5dTp9kyYmLrILC9uUnoULSgjb5f050xQJRA9mdT+SX3 jc1DfbjYqh0SZXliVmfMXp+Wn8C7c7RbiqVZdBB2wqBiPwxG32HuEjneLGV4lTx5+HoO M8ZP3UotMP7/JJJ0qXbrzzSQ8D7TJOdxQV0SqCF6IdL/3skjghaqSfl6rijsnPbvipAe 16ts4PQf9FKbvMklwJGzvlOJ1RwQ4x+kmy0Hsh8KyTFre2JBLYvkBYCgOmjK6eg1I/Uv kBPN5dIL/zQKMellwAtjXHRXkIFUQpqbHU9mPgV8aH/y2tmSEYjmi6KN+cnqOWnei2AH ft9Q== X-Gm-Message-State: AOJu0YwV70z9hNXCktGuiI0fL8tEgdcZNl4WtHU7WO0RrosjxgzElXyY 1NwGcjkUNRRqgKSz93+qa+yiVDzJ09GrRoRcs57K4gSQhyPJJ2GrHkk5b59SWgYSj823PvkDzyI ZZrwpR3K/zPyQMiqGb6ZQeoBXsr2/RZIHm1W6wI199eKG X-Gm-Gg: AfdE7cmcIh9y8V6hk9+TlQTv1JfTv7haSDx8JzrtQYC2S9DxynQGOc0bZM+/x0oRXKS 4SU1BZP0CloE2Jlp9RBc8Am//9ZDgr94RH0n7fHD7fdTmQc05aOVNFjF2gSuvFShU+4FV3yGCXK 5BIGAhB40iyZSmeY8wDPOa85jQZ6bMj84jT9sKEGKqT1zuePeCc+4Dwj2tDv9Ta+kEe02FN+GG/ oRJ+VscQ8GKN8lieztVWe4VsFtTnQZ1+vzMvcumTDFR/K8ApNFavmU9pY16RQFRFqTY5Q== X-Received: by 2002:a05:690c:6993:b0:7bd:882a:43d9 with SMTP id 00721157ae682-81be1e3d45bmr6197207b3.27.1783354113441; Mon, 06 Jul 2026 09:08:33 -0700 (PDT) MIME-Version: 1.0 From: Vladislav Semykin Date: Mon, 6 Jul 2026 19:08:20 +0300 X-Gm-Features: AVVi8CdVqAbCmoEfpEY73Nqya-bAhX_6gHKBUf9ZCybv7a3vV0OE-iGnUg27btA Message-ID: Subject: [PATCH v5] cp: fix unevaluated operand context for typeid [PR c++/125886] To: gcc-patches@gcc.gnu.org X-Spam-Status: No, score=-6.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_IMAGE_ONLY_08, HTML_IMAGE_RATIO_04, HTML_MESSAGE, IMAGE_ATTACHED, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org 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~patchwork=sourceware.org@gcc.gnu.org [image: image.png] Also, I want to ask the team about links on standard in my comments. I don't quite understand why the gcc-style doesn't allow a space before the square bracket; I'll leave it as is for now. But what does the team prefer? How appropriate are my references to the standard? From 69f31589451dad35eb144eacdc9e2cdd1ce8b041 Mon Sep 17 00:00:00 2001 From: Vladislav Semykin Date: Mon, 6 Jul 2026 19:02:06 +0300 Subject: [PATCH v5] cp: fix unevaluated operand context for typeid [PR c++/125886] Per [expr.typeid]/4-5, a typeid operand is unevaluated by default and is evaluated only for a glvalue of polymorphic class type whose dynamic type is not known at compile time. Previously GCC always parsed the operand in an evaluated context, which broke unevaluated uses (declval, non-static data members per DR613, function parameters) and missed lambda capture diagnostics for evaluated polymorphic operands. Implement a two-pass parse: first under cp_unevaluated, then - if typeid_evaluated_p says the operand is evaluated - roll back and re-parse under cp_evaluated. Share the evaluated/unevaluated predicate via typeid_evaluated_p in rtti.cc, used from the parser, tsubst_expr, and build_typeid. Also fixes PR c++/68604 and PR c++/116385, and removes a now-stale xfail in g++.dg/coroutines/unevaluated.C. Changes since v1: - Apply the same two-pass logic to template-dependent operands in tsubst_expr (TYPEID_EXPR), as Jason requested. - Fix lambda capture: a polymorphic glvalue typeid operand inside a lambda without a capture-default is now correctly diagnosed as "not captured". Root cause was typeid_evaluated_p rejecting reference types and being gated on the "nonnull" flag, which is unrelated to the evaluated/unevaluated decision. - Factor the polymorphic-glvalue predicate into shared typeid_evaluated_p used by the parser, tsubst, and build_typeid. - Drop the cxx_dialect >= cxx11 gate: per Jason, DR613 applies in all modes, since finish_non_static_data_member doesn't check cxx_dialect either. Changes since v2: - Restored the full commit message (rationale + ChangeLog entries). - Shortened cp_unevaluated_operand comments in cp-tree.h and parser.cc (git gcc-style). - Fixed indentation in parser.cc and rtti.cc (git gcc-style). - Use cp_unevaluated RAII in cp_parser_postfix_expression instead of manual ++/--cp_unevaluated_operand, matching tsubst_expr. - Kept glvalue_p in typeid_evaluated_p: [expr.typeid]/4 applies only to a glvalue of polymorphic class type; a polymorphic class prvalue can have resolves_to_fixed_type_p false yet still be unevaluated (see "Not a glvalue" in g++.dg/cpp2a/constexpr-typeid2.C). Without glvalue_p that case would incorrectly take the evaluated re-parse path. - Investigated the g++.dg/modules/compile-std1.C failure Jason flagged as surprising (reproduced with 'make check-c++ RUNTESTFLAGS="modules.exp=compile-std1.C"'). g++.log shows cc1plus: fatal error: bits/std.cc: No such file or directory and the same for bits/std.compat.cc under --compile-std-module; import std then fails because gcm.cache/std.gcm was never built. Here the libstdc++ symlinks at .../include/bits/std.{,compat.}cc point at src/c++23/std.{,compat.}cc, but those generated files are missing (only .in templates remain) - incomplete libstdc++ modules build, not a cp/typeid regression. Unrelated to this patch. Changes since v3: - Removed cp_evaluated on the re-parse path in both cp_parser_postfix_expression and tsubst_expr. As Jason pointed out, the re-parse should not override the typeid itself being within an outer unevaluated operand (e.g. sizeof (typeid (b))); it should just drop back to whatever context enclosed it. - Added a comment on the glvalue_p check in typeid_evaluated_p noting it's only needed because resolves_to_fixed_type_p doesn't handle all prvalue cases (e.g. COMPOUND_EXPR), per Jason's observation. - Removed a stale test (lambda_sizeof_typeid) that encoded the old, incorrect behavior (requiring capture for typeid nested inside sizeof); added nested_unevaluated and nested_unevaluated_lambda to cover the corrected behavior instead. Changes since v4: - Added a !cp_unevaluated_operand check to the condition in both cp_parser_postfix_expression and tsubst_expr, as Marek pointed out. If typeid is already within an outer unevaluated operand (e.g. sizeof (typeid (b))), the evaluated re-parse can never change anything: per [basic.def.odr]/3, an unevaluated operand's subexpressions stay not potentially evaluated regardless of [expr.typeid]/4, so the second parse would just redo the same tokens under the same ambient context and risk duplicated diagnostics. Covered by the existing nested_unevaluated and nested_unevaluated_lambda tests in pr125886.C. Tested full CI on: x86_64 GNU/Linux 6.17.0-35-generic Ubuntu 24.04. The following failures are pre-existing on trunk without this patch and are unrelated to the changes here (full log via 'make -C gcc -k check-c++-all' attached): FAIL: c-c++-common/analyzer/flex-without-call-summaries.c -std=c++11 at line 885 (test for warnings, line 884) XPASS: c-c++-common/analyzer/flex-without-call-summaries.c -std=c++11 PR analyzer/103546 (test for bogus messages, line 892) FAIL: c-c++-common/analyzer/flex-without-call-summaries.c -std=c++11 (test for excess errors) FAIL: g++.dg/guality/pr55665.C -O2 -flto -fno-use-linker-plugin -flto-partition=none line 23 p == 40 FAIL: g++.dg/modules/compile-std1.C -std=c++26 -fimplicit-constexpr (test for excess errors) FAIL: g++.dg/modules/xtreme-header-8.C -std=c++26 -fimplicit-constexpr (test for excess errors) FAIL: g++.dg/modules/xtreme-header-2_a.H -std=c++26 -fimplicit-constexpr (test for excess errors) FAIL: g++.dg/modules/xtreme-header-2_c.C -std=c++26 -fimplicit-constexpr (test for excess errors) FAIL: g++.dg/modules/xtreme-header-7_a.H -std=c++26 -fimplicit-constexpr (test for excess errors) FAIL: g++.dg/modules/xtreme-header_a.H -std=c++26 -fimplicit-constexpr (test for excess errors) FAIL: g++.dg/plugin/std-module-exports-c++20.C -fplugin=./std_module_exports_plugin.so (test for excess errors) FAIL: g++.dg/plugin/std-module-exports-c++23.C -fplugin=./std_module_exports_plugin.so (test for excess errors) FAIL: g++.dg/plugin/std-module-exports-c++26.C -fplugin=./std_module_exports_plugin.so (test for excess errors) gcc/cp/ChangeLog: PR c++/125886 * parser.cc (cp_parser_postfix_expression): Two-pass typeid parse. * pt.cc (tsubst_expr): Same for TYPEID_EXPR. * rtti.cc (typeid_evaluated_p, build_typeid): Shared predicate. * cp-tree.h: Declare typeid_evaluated_p. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/pr125886.C: New test. * g++.dg/rtti/typeid14.C, g++.dg/rtti/typeid15.C: New tests. * g++.dg/coroutines/unevaluated.C: Drop stale xfail. Signed-off-by: Vladislav Semykin --- gcc/cp/cp-tree.h | 1 + gcc/cp/parser.cc | 31 ++++++- gcc/cp/pt.cc | 21 ++++- gcc/cp/rtti.cc | 48 ++++++++--- gcc/testsuite/g++.dg/coroutines/unevaluated.C | 3 +- gcc/testsuite/g++.dg/cpp0x/pr125886.C | 82 +++++++++++++++++++ gcc/testsuite/g++.dg/rtti/typeid14.C | 19 +++++ gcc/testsuite/g++.dg/rtti/typeid15.C | 23 ++++++ 8 files changed, 210 insertions(+), 18 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr125886.C create mode 100644 gcc/testsuite/g++.dg/rtti/typeid14.C create mode 100644 gcc/testsuite/g++.dg/rtti/typeid15.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 132139f9d..df0f253cc 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7486,6 +7486,7 @@ extern tree current_nonlambda_class_type (void); extern tree finish_struct (tree, tree); extern void finish_struct_1 (tree); extern int resolves_to_fixed_type_p (tree, int * = NULL); +extern bool typeid_evaluated_p (tree); extern void init_class_processing (void); extern int is_empty_class (tree); extern bool is_really_empty_class (tree, bool); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 8194106c6..8ca806748 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -292,7 +292,8 @@ static void missing_template_diag static FILE *cp_lexer_debug_stream; /* Nonzero if we are parsing an unevaluated operand: an operand to - sizeof, typeof, or alignof. */ + sizeof, typeof, or alignof. This is a count since operands to + sizeof can be nested. */ int cp_unevaluated_operand; /* Nonzero if we are parsing a reflect-expression and shouldn't strip @@ -8570,9 +8571,33 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, else { tree expression; + /* [expr.typeid]/4-5: parse the operand unevaluated first; if it is + a polymorphic glvalue, roll back and re-parse it evaluated, + since an evaluated parse has irreversible side-effects + (mark_used -> instantiation; lambda capture). */ + cp_lexer_save_tokens (parser->lexer); + { + cp_unevaluated u; + expression = cp_parser_expression (parser, &idk); + } + /* If we're already within an unevaluated operand, everything + in the subtree stays not potentially evaluated regardless + of [expr.typeid]/4 ([basic.def.odr]/3), so the evaluated + re-parse below can have nothing to do; skip it. */ + if (expression != error_mark_node + && processing_template_decl == 0 + && !cp_unevaluated_operand + && typeid_evaluated_p (expression)) + { + /* Re-parse the operand evaluated so the /4 side-effects occur. + The unevaluated pass above called no mark_used and captured + nothing, so rolling back has nothing to undo. */ + cp_lexer_rollback_tokens (parser->lexer); + expression = cp_parser_expression (parser, &idk); + } + else + cp_lexer_commit_tokens (parser->lexer); - /* Look for an expression. */ - expression = cp_parser_expression (parser, & idk); /* Compute its typeid. */ postfix_expression = build_typeid (expression, tf_warning_or_error); /* Look for the `)' token. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index f7aa10226..53059b6fc 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -23003,8 +23003,25 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) } else { - operand_0 = RECUR (operand_0); - RETURN (build_typeid (operand_0, complain)); + /* [expr.typeid]/4-5: substitute the operand unevaluated first, then + again evaluated if it is a polymorphic glvalue, so the /4 + side-effects occur. The unevaluated pass instantiates nothing, + so re-substituting has nothing to undo (PR c++/125886). */ + tree operand; + tree uneval; + { + cp_unevaluated u; + uneval = RECUR (operand_0); + } + /* If we're already within an unevaluated operand, everything + in the subtree stays not potentially evaluated regardless + of [expr.typeid]/4 ([basic.def.odr]/3), so the evaluated + re-parse below can have nothing to do; skip it. */ + if (!cp_unevaluated_operand && typeid_evaluated_p (uneval)) + operand = RECUR (operand_0); + else + operand = uneval; + RETURN (build_typeid (operand, complain)); } } diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc index 7e6fa5193..bb38758c0 100644 --- a/gcc/cp/rtti.cc +++ b/gcc/cp/rtti.cc @@ -340,6 +340,32 @@ typeid_ok_p (void) return true; } +/* True if EXP is a glvalue expression of polymorphic class type whose + dynamic type is not known statically, so that typeid (EXP) must be + evaluated per ([expr.typeid]/4). */ + +bool +typeid_evaluated_p (tree exp) +{ + if (exp == error_mark_node) + return false; + tree t = TREE_TYPE (exp); + if (!t || t == error_mark_node) + return false; + if (TYPE_REF_P (t)) + t = TREE_TYPE (t); + if (TREE_CODE (t) != RECORD_TYPE && TREE_CODE (t) != UNION_TYPE) + return false; + int nonnull = 0; + return (TYPE_POLYMORPHIC_P (t) + && !resolves_to_fixed_type_p (exp, &nonnull) + /* Only a glvalue operand is evaluated ([expr.typeid]/4). + The following check is only necessary because + resolves_to_fixed_type_p does not handle all + prvalue cases such as COMPOUND_EXPR. */ + && glvalue_p (exp)); +} + /* Return an expression for "typeid(EXP)". The expression returned is an lvalue of type "const std::type_info". */ @@ -347,7 +373,6 @@ tree build_typeid (tree exp, tsubst_flags_t complain) { tree cond = NULL_TREE, initial_expr = exp; - int nonnull = 0; if (exp == error_mark_node || !typeid_ok_p ()) return error_mark_node; @@ -355,17 +380,18 @@ build_typeid (tree exp, tsubst_flags_t complain) if (processing_template_decl) return build_min (TYPEID_EXPR, const_type_info_type_node, exp); - if (CLASS_TYPE_P (TREE_TYPE (exp)) - && TYPE_POLYMORPHIC_P (TREE_TYPE (exp)) - && ! resolves_to_fixed_type_p (exp, &nonnull) - && ! nonnull) + if (typeid_evaluated_p (exp)) { - /* So we need to look into the vtable of the type of exp. - Make sure it isn't a null lvalue. */ - exp = cp_build_addr_expr (exp, complain); - exp = save_expr (exp); - cond = cp_convert (boolean_type_node, exp, complain); - exp = cp_build_fold_indirect_ref (exp); + int nonnull = 0; + resolves_to_fixed_type_p (exp, &nonnull); + if (!nonnull) + { + /* Make sure it isn't a null lvalue; evaluate it once. */ + exp = cp_build_addr_expr (exp, complain); + exp = save_expr (exp); + cond = cp_convert (boolean_type_node, exp, complain); + exp = cp_build_fold_indirect_ref (exp); + } } exp = get_tinfo_ptr_dynamic (exp, complain); diff --git a/gcc/testsuite/g++.dg/coroutines/unevaluated.C b/gcc/testsuite/g++.dg/coroutines/unevaluated.C index 63dae38de..f763b208c 100644 --- a/gcc/testsuite/g++.dg/coroutines/unevaluated.C +++ b/gcc/testsuite/g++.dg/coroutines/unevaluated.C @@ -17,8 +17,7 @@ struct Task { // We do not permit co_await, co_yield outside a function, and so uses in // noexcept or requirements are covered by that. Task foo() { - /* This one will currently fail - see PR68604. */ - const std::type_info& ti1 = typeid (co_await std::suspend_never{}); // { dg-error {'co_await' cannot be used in an unevaluated context} "" { xfail *-*-* } } + const std::type_info& ti1 = typeid (co_await std::suspend_never{}); // { dg-error {'co_await' cannot be used in an unevaluated context} } std::size_t x = sizeof (co_yield (19)); // { dg-error {'co_yield' cannot be used in an unevaluated context} } decltype (co_await std::suspend_never{}) A; // { dg-error {'co_await' cannot be used in an unevaluated context} } co_return; diff --git a/gcc/testsuite/g++.dg/cpp0x/pr125886.C b/gcc/testsuite/g++.dg/cpp0x/pr125886.C new file mode 100644 index 000000000..96a7ec3cf --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr125886.C @@ -0,0 +1,82 @@ +// PR c++/125886 +// PR c++/68604 +// PR c++/116385 +// { dg-do compile { target c++11 } } + +#include +#include + +// PR c++/125886: a non-polymorphic operand is an unevaluated operand +// ([expr.typeid]/5), so std::declval() is not instantiated and its +// static_assert does not fire. +struct A {}; +void +non_poly_declval () +{ + (void) typeid (std::declval ()); +} + +// PR c++/125886 (template): the evaluated/unevaluated decision is taken +// at instantiation time in tsubst; a non-polymorphic operand is +// unevaluated, so tmpl is well-formed. +template +void +tmpl () +{ + (void) typeid (std::declval ()); +} + +template void tmpl (); + +// PR c++/68604: an id-expression denoting a non-static data member is +// valid in an unevaluated operand (DR613 / N2253, C++11+). +struct C { int i; }; +void +nsm () +{ + (void) typeid (C::i); +} + +// PR c++/116385: function parameters are not odr-used in an unevaluated +// typeid operand, so they need not be captured and may appear in local +// classes and default arguments. +void +params (int n) +{ + [&] { (void) typeid (n); }; + struct Local { void g () { (void) typeid (n); } }; + void g (const std::type_info& = typeid (n)); +} + +// A final-class polymorphic glvalue resolves to a fixed (static) type, +// so typeid is unevaluated: no vtable lookup, no odr-use. +struct F final { virtual ~F (); }; +void +final_glvalue (F& f) +{ + (void) typeid (f); +} + +// Polymorphic glvalue in lambda without capture-default must be captured, +// since typeid is evaluated. +struct B { virtual ~B (); }; +void +lambda_poly_capture (B& b) +{ + [] { (void) typeid (b); }; // { dg-error "not captured" } +} + +// typeid itself inside an unevaluated operand: the polymorphic glvalue +// re-parse must not force evaluation here. +void +nested_unevaluated(B &b) +{ + (void) sizeof (typeid (b)); // no odr-use of b expected +} + +// lambda variant of nested_unevaluated. +void +nested_unevaluated_lambda(B &b) +{ + [] { (void) sizeof (typeid (b)); }; // OK: no capture required +} diff --git a/gcc/testsuite/g++.dg/rtti/typeid14.C b/gcc/testsuite/g++.dg/rtti/typeid14.C new file mode 100644 index 000000000..422a81fa2 --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/typeid14.C @@ -0,0 +1,19 @@ +// PR c++/125886 +// { dg-do compile { target c++11 } } + +#include +#include + +// [expr.typeid]/4: a glvalue expression of a polymorphic class type is +// evaluated, so std::declval() is instantiated and +// __declval_protector's static_assert fires (the non-template case: +// the operand is parsed evaluated after the unevaluated probe). + +struct B { virtual ~B(); }; + +void +non_template () +{ + (void) typeid (std::declval ()); +} +// { dg-error "static assertion failed: declval" "" { target *-*-* } 0 } diff --git a/gcc/testsuite/g++.dg/rtti/typeid15.C b/gcc/testsuite/g++.dg/rtti/typeid15.C new file mode 100644 index 000000000..76478bed3 --- /dev/null +++ b/gcc/testsuite/g++.dg/rtti/typeid15.C @@ -0,0 +1,23 @@ +// PR c++/125886 +// { dg-do compile { target c++11 } } + +#include +#include + +// [expr.typeid]/4 for a template-dependent operand: the evaluated/ +// unevaluated decision is taken at instantiation time in tsubst. For +// a polymorphic operand the operand is re-substituted evaluated, so +// std::declval() is instantiated and __declval_protector's +// static_assert fires. + +struct C { virtual ~C(); }; + +template +void +tmpl () +{ + (void) typeid (std::declval ()); +} + +template void tmpl (); +// { dg-error "static assertion failed: declval" "" { target *-*-* } 0 } -- 2.43.0