From patchwork Fri Sep 27 05:57:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1990099 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=GQylY2LE; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (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 4XFKX33l9Bz1xt9 for ; Fri, 27 Sep 2024 15:58:27 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B89A03858406 for ; Fri, 27 Sep 2024 05:58:25 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pl1-x636.google.com (mail-pl1-x636.google.com [IPv6:2607:f8b0:4864:20::636]) by sourceware.org (Postfix) with ESMTPS id 2ED2A3858406 for ; Fri, 27 Sep 2024 05:58:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2ED2A3858406 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 2ED2A3858406 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::636 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416688; cv=none; b=k9P3rkEGPC74RNW7WXgSHItAHj3WQcc3cyhDO2XRuKttPYxYjWUFvBj6cuLKSTz/TC6K1IvkjsB0MkMmdeDAw+LdFfpCcwoI9bog6Bg+PMO/5n7Qh1AVyhD8FCzJmHuJ28OITt/YbSnI2mD/2ug4d9GMhsb/91iHbUnPOO+5FDc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416688; c=relaxed/simple; bh=27qV1pLTExnFeUdMk0pOEuuTtc/BG2Eidi5RhZH1RTY=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=KICpUs/k5nQD1WT0noKLW53KtIHsQMlDU/Pctrl6qCkxhNc+9S6bcBrdX1pfN4I5o4L84BHl+3PmUOR9R6dcix/FKONDX+tB/iklGkozQSD7zW/OHk0ufJEkt7ySGD5FIrgnS+aVo9Ap6luKn8WwjDAEA55a8IDDkd9dLc87pI0= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pl1-x636.google.com with SMTP id d9443c01a7336-20b05ba4192so1658295ad.1 for ; Thu, 26 Sep 2024 22:58:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727416682; x=1728021482; darn=gcc.gnu.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:from:to:cc:subject:date:message-id:reply-to; bh=asFQLc/XlXEw6tHvIM1AvX7kTef+CQ0C4iZK5az3TBw=; b=GQylY2LEDSPnMFZSiGziDmHOCDc2bIpwUD1UcyAsZfnFYIUxKPn4KfHjhFdCidS+H/ gAVGwCvjXcyGKruAagl1hcBqYisGtgvTydYGPxn1K4CGVWl2zmc8YTxmwnBfb8VMAlSV sjDbKqFxIT/ok/yT3L10FBks0APrO9XdKe0n+hnObNPEW1uyd6Av5YCyksz//SqAJW8x dmJjgbBeP7DVu9mfMHN8lyX5Br9vfagSHipa29V7Y3hsNx4GmCeDTMyi+5K8twl5nulq XZUxM0TSdxwykAwMTRv7RpXH5PyuP28Wx2A0jg04vl9jqy5NPrCkZ0u6aUdBF5Obaf24 4y8w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727416682; x=1728021482; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=asFQLc/XlXEw6tHvIM1AvX7kTef+CQ0C4iZK5az3TBw=; b=Yb1wr5q8+aw2dQgIRybJYfgH9aLnU7gIgoeUaXrKjA5oNjJ8IRhwqBPoPCfmzqZ9O0 RSJi923U5Q5RwuhSQA2M6bxUaadHUHNVkMtcfhyCM6GyCtpQ/vRxuKcYSE+NmeWcP+Qm xq9+PqZSJZzTe2ymzEnvwYjI5qfYME/wKmayTjbjprv7FqfMlXT8uLxy1afKi5q0wwa/ oV/qaBJb76VD5agn81nBXZDYLrOYvfxiGkG+s4lPcgcrzGvPf6dRPBI2ERWQPCfrQzP4 2of9l+17yC4Ft+TV5DEX+eHIMxvaqtkYwZXzbBkqopkP1sM56Y+E5y48zK8/JZQioiYi 0v2A== X-Gm-Message-State: AOJu0YxsGsoMlSczHCdrvToEoxwb0QllJbeEm63qdgglF4+D5InDRijI nwytYkfxo38OSA65eJqZ7xJiYgyIPR8MuKm5SAy+gEdBQu+8kj1G5To0lg== X-Google-Smtp-Source: AGHT+IHRjf4sAdc1o3wStmRfJVjUEnmJL8+Ja9fffk+xUl1dWKVp5LjYRxGhHy0SLTWYFGvAUDwX3g== X-Received: by 2002:a17:903:234f:b0:205:7b03:ec40 with SMTP id d9443c01a7336-20b37b4dbadmr14040365ad.10.1727416681710; Thu, 26 Sep 2024 22:58:01 -0700 (PDT) Received: from Thaum. (163-47-68-2.ipv4.originbroadband.com.au. [163.47.68.2]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-20b37e50c2esm6873965ad.235.2024.09.26.22.57.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Sep 2024 22:58:01 -0700 (PDT) Message-ID: <66f64969.170a0220.2e3b0a.3c61@mx.google.com> X-Google-Original-Message-ID: Date: Fri, 27 Sep 2024 15:57:57 +1000 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v2 1/6] c++/modules: Detect exposures of TU-local entities References: <66f64939.050a0220.261230.4b66@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <66f64939.050a0220.261230.4b66@mx.google.com> X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.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~incoming=patchwork.ozlabs.org@gcc.gnu.org I feel like there should be a way to make use of LAMBDA_TYPE_EXTRA_SCOPE to avoid the need for the new TYPE_DEFINED_IN_INITIALIZER_P flag, perhaps once something like my patch here[1] is accepted (but with further embellishments for concepts, probably), but I wasn't able to work it out. Since currently as far as I'm aware only lambdas can satisfy being a type with no name defined in an 'initializer' this does seem a little overkill but I've applied it to all class types just in case. [1]: https://gcc.gnu.org/pipermail/gcc-patches/2024-September/662393.html Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk? -- >8 -- Currently, the modules streaming code implements some checks for declarations in the CMI that reference (some kinds of) internal-linkage entities, and errors if so. This patch expands on that support to implement the logic for exposures of TU-local entities as defined in [basic.link] since P1815. This will cause some code that previously errored in modules to start compiling; for instance, template specialisations of internal linkage functions. However, some code that previously appeared valid will with this patch no longer compile, notably some kinds of usages of internal linkage functions included from the GMF. This appears to be related to P2808 and FR-025, however as yet there doesn't appear to be consensus for changing these rules so I've implemented them as-is. This patch leaves a couple of things out. In particular, a couple of the rules for what is a TU-local entity currently seem to me to be redundant; I've left them as FIXMEs to be handled once I can find testcases that aren't adequately supported by the other logic here. Additionally, there are some exceptions for when naming a TU-local entity is not always an exposure; I've left support for this to a follow-up patch for easier review, as it has broader implications for streaming. Finally, this patch makes a couple of small adjustments to the modules streaming logic to prune any leftover TU-local deps (that aren't erroneous exposures). This is required for this patch to ensure that later stages don't get confused by any leftover TU-local entities floating around. gcc/cp/ChangeLog: * cp-tree.h (TYPE_DEPENDENT_P_VALID): Fix whitespace. (TYPE_DEFINED_IN_INITIALIZER_P): New accessor. * module.cc (DB_IS_INTERNAL_BIT): Rename to... (DB_TU_LOCAL_BIT): ...this. (DB_REFS_INTERNAL_BIT): Rename to... (DB_EXPOSURE_BIT): ...this. (depset::hash::is_internal): Rename to... (depset::hash::is_tu_local): ...this. (depset::hash::refs_internal): Rename to... (depset::hash::is_exposure): ...this. (depset::hash::is_tu_local_entity): New function. (depset::hash::has_tu_local_tmpl_arg): New function. (depset::hash::is_tu_local_value): New function. (depset::hash::make_dependency): Check for TU-local entities. (depset::hash::add_dependency): Make current an exposure whenever it references a TU-local entity. (depset::hash::add_binding_entity): Don't create bindings for any TU-local entity. (depset::hash::finalize_dependencies): Rename flags and adjust diagnostic messages to report exposures of TU-local entities. (depset::tarjan::connect): Don't include any TU-local depsets. (depset::hash::connect): Likewise. (trees_out::core_bools): Stream TYPE_LANG_FLAG_7. (trees_in::core_bools): Read it. * parser.h (struct cp_parser::in_initializer_p): New flag. * parser.cc (cp_debug_parser): Print the new flag. (cp_parser_new): Set the new flag to false. (cp_parser_lambda_expression): Mark whether the lambda was defined in an initializer. (cp_parser_initializer): Set the new flag to true while parsing. (cp_parser_class_head): Mark whether the class was defined in an initializer. (cp_parser_concept_definition): Set the new flag to true while parsing. gcc/testsuite/ChangeLog: * g++.dg/modules/block-decl-2.C: Adjust messages. * g++.dg/modules/internal-1.C: Adjust messages, remove XFAILs. * g++.dg/modules/linkage-2.C: Adjust messages, remove XFAILS. * g++.dg/modules/internal-3.C: New test. * g++.dg/modules/internal-4_a.H: New test. * g++.dg/modules/internal-4_a.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/cp-tree.h | 7 +- gcc/cp/module.cc | 382 +++++++++++++++++--- gcc/cp/parser.cc | 16 + gcc/cp/parser.h | 3 + gcc/testsuite/g++.dg/modules/block-decl-2.C | 2 +- gcc/testsuite/g++.dg/modules/internal-1.C | 15 +- gcc/testsuite/g++.dg/modules/internal-3.C | 18 + gcc/testsuite/g++.dg/modules/internal-4_a.H | 4 + gcc/testsuite/g++.dg/modules/internal-4_b.C | 124 +++++++ gcc/testsuite/g++.dg/modules/linkage-2.C | 5 +- 10 files changed, 505 insertions(+), 71 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/internal-3.C create mode 100644 gcc/testsuite/g++.dg/modules/internal-4_a.H create mode 100644 gcc/testsuite/g++.dg/modules/internal-4_b.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 7c438eca16d..cfaf82b018c 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -534,6 +534,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; AUTO_IS_DECLTYPE (in TEMPLATE_TYPE_PARM) TEMPLATE_TEMPLATE_PARM_SIMPLE_P (in TEMPLATE_TEMPLATE_PARM) 6: TYPE_DEPENDENT_P_VALID + 7: TYPE_DEFINED_IN_INITIALIZER_P Usage of DECL_LANG_FLAG_?: 0: DECL_TEMPLATE_PARM_P (in PARM_DECL, CONST_DECL, TYPE_DECL, or TEMPLATE_DECL) @@ -2292,7 +2293,11 @@ enum languages { lang_c, lang_cplusplus }; /* True if dependent_type_p has been called for this type, with the result that TYPE_DEPENDENT_P is valid. */ -#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6(NODE) +#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6 (NODE) + +/* True if this type was defined in an initializer. Used for determining + whether an entity is TU-local. */ +#define TYPE_DEFINED_IN_INITIALIZER_P(NODE) TYPE_LANG_FLAG_7 (NODE) /* Nonzero if this type is const-qualified. */ #define CP_TYPE_CONST_P(NODE) \ diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 65b37b4b554..570320ea627 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2330,10 +2330,8 @@ private: DB_KIND_BITS = EK_BITS, DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS, DB_IS_MEMBER_BIT, /* Is an out-of-class member. */ - DB_IS_INTERNAL_BIT, /* It is an (erroneous) - internal-linkage entity. */ - DB_REFS_INTERNAL_BIT, /* Refers to an internal-linkage - entity. */ + DB_TU_LOCAL_BIT, /* It is a TU-local entity. */ + DB_EXPOSURE_BIT, /* Exposes a TU-local entity. */ DB_IMPORTED_BIT, /* An imported entity. */ DB_UNREACHED_BIT, /* A yet-to-be reached entity. */ DB_HIDDEN_BIT, /* A hidden binding. */ @@ -2414,13 +2412,13 @@ public: return get_flag_bit (); } public: - bool is_internal () const + bool is_tu_local () const { - return get_flag_bit (); + return get_flag_bit (); } - bool refs_internal () const + bool is_exposure () const { - return get_flag_bit (); + return get_flag_bit (); } bool is_import () const { @@ -2580,6 +2578,11 @@ public: depset *add_dependency (tree decl, entity_kind); void add_namespace_context (depset *, tree ns); + private: + bool has_tu_local_tmpl_arg (tree decl, tree args, bool explain); + bool is_tu_local_entity (tree decl, bool explain = false); + bool is_tu_local_value (tree decl, tree expr, bool explain = false); + private: static bool add_binding_entity (tree, WMB_Flags, void *); @@ -5404,6 +5407,7 @@ trees_out::core_bools (tree t, bits_out& bits) WB (t->type_common.lang_flag_4); WB (t->type_common.lang_flag_5); WB (t->type_common.lang_flag_6); + WB (t->type_common.lang_flag_7); WB (t->type_common.typeless_storage); } @@ -5605,6 +5609,7 @@ trees_in::core_bools (tree t, bits_in& bits) RB (t->type_common.lang_flag_4); RB (t->type_common.lang_flag_5); RB (t->type_common.lang_flag_6); + RB (t->type_common.lang_flag_7); RB (t->type_common.typeless_storage); } @@ -12886,6 +12891,246 @@ depset::hash::find_binding (tree ctx, tree name) return slot ? *slot : NULL; } +/* Returns true if DECL is a TU-local entity, as defined by [basic.link]. + If EXPLAIN is true, emit an informative note about why DECL is TU-local. */ + +bool +depset::hash::is_tu_local_entity (tree decl, bool explain/*=false*/) +{ + gcc_checking_assert (DECL_P (decl)); + + /* An explicit type alias is not an entity, and so is never TU-local. */ + if (TREE_CODE (decl) == TYPE_DECL + && !DECL_IMPLICIT_TYPEDEF_P (decl) + && !DECL_SELF_REFERENCE_P (decl)) + return false; + + location_t loc = DECL_SOURCE_LOCATION (decl); + tree type = TREE_TYPE (decl); + + /* Check specializations first for slightly better explanations. */ + int use_tpl = -1; + tree ti = node_template_info (decl, use_tpl); + if (use_tpl > 0 && TREE_CODE (TI_TEMPLATE (ti)) == TEMPLATE_DECL) + { + /* A specialization of a TU-local template. */ + tree tmpl = TI_TEMPLATE (ti); + if (is_tu_local_entity (tmpl)) + { + if (explain) + { + inform (loc, "%qD is a specialization of TU-local template %qD", + decl, tmpl); + is_tu_local_entity (tmpl, /*explain=*/true); + } + return true; + } + + /* A specialization of a template with any TU-local template argument. */ + if (has_tu_local_tmpl_arg (decl, TI_ARGS (ti), explain)) + return true; + + /* FIXME A specialization of a template whose (possibly instantiated) + declaration is an exposure. This should always be covered by the + above cases?? */ + } + + /* A type, function, variable, or template with internal linkage. */ + linkage_kind kind = decl_linkage (decl); + if (kind == lk_internal + /* But although weakrefs are marked static, don't consider them + to be TU-local. */ + && !lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))) + { + if (explain) + inform (loc, "%qD declared with internal linkage", decl); + return true; + } + + /* Does not have a name with linkage and is declared, or introduced by a + lambda-expression, within the definition of a TU-local entity. */ + if (kind == lk_none) + { + tree ctx = CP_DECL_CONTEXT (decl); + if (LAMBDA_TYPE_P (type)) + if (tree extra = LAMBDA_TYPE_EXTRA_SCOPE (type)) + ctx = extra; + + if (TREE_CODE (ctx) == NAMESPACE_DECL) + { + if (!TREE_PUBLIC (ctx)) + { + if (explain) + inform (loc, "%qD has no linkage and is declared in an " + "anonymous namespace", decl); + return true; + } + } + else if (TYPE_P (ctx)) + { + tree ctx_decl = TYPE_MAIN_DECL (ctx); + if (is_tu_local_entity (ctx_decl)) + { + if (explain) + { + inform (loc, "%qD has no linkage and is declared within " + "TU-local entity %qT", decl, ctx); + is_tu_local_entity (ctx_decl, /*explain=*/true); + } + return true; + } + } + else if (is_tu_local_entity (ctx)) + { + if (explain) + { + inform (loc, "%qD has no linkage and is declared within " + "TU-local entity %qD", decl, ctx); + is_tu_local_entity (ctx, /*explain=*/true); + } + return true; + } + } + + /* A type with no name that is defined outside a class-specifier, function + body, or initializer; or is introduced by a defining-type-specifier that + is used to declare only TU-local entities. + + We consider types with names for linkage purposes as having names, since + these aren't really TU-local, and also consider constraint-expressions + as initializers. */ + if (TREE_CODE (decl) == TYPE_DECL + && TYPE_ANON_P (type) + && !DECL_SELF_REFERENCE_P (decl) + /* An enum with an enumerator name for linkage. */ + && !(UNSCOPED_ENUM_P (type) && TYPE_VALUES (type))) + { + tree main_decl = TYPE_MAIN_DECL (type); + if (!TYPE_DEFINED_IN_INITIALIZER_P (type) + && !DECL_CLASS_SCOPE_P (main_decl) + && !decl_function_context (main_decl)) + { + if (explain) + inform (loc, "%qT has no name and is not defined within a class, " + "function, or initializer", type); + return true; + } + + // FIXME introduced by a defining-type-specifier only declaring TU-local + // entities; does this refer to e.g. 'static struct {} a;"? I can't + // think of any cases where this isn't covered by earlier cases. */ + } + + return false; +} + +/* Helper for is_tu_local_entity. Returns true if one of the ARGS of + DECL is TU-local. Emits an explanation if EXPLAIN is true. */ + +bool +depset::hash::has_tu_local_tmpl_arg (tree decl, tree args, bool explain) +{ + if (!args || TREE_CODE (args) != TREE_VEC) + return false; + + for (tree a : tree_vec_range (args)) + { + if (TREE_CODE (a) == TREE_VEC) + { + if (has_tu_local_tmpl_arg (decl, a, explain)) + return true; + } + else if (!WILDCARD_TYPE_P (a)) + { + if (DECL_P (a) && is_tu_local_entity (a)) + { + if (explain) + { + inform (DECL_SOURCE_LOCATION (decl), + "%qD has TU-local template argument %qD", + decl, a); + is_tu_local_entity (a, /*explain=*/true); + } + return true; + } + + if (TYPE_P (a) && TYPE_NAME (a) && is_tu_local_entity (TYPE_NAME (a))) + { + if (explain) + { + inform (DECL_SOURCE_LOCATION (decl), + "%qD has TU-local template argument %qT", + decl, a); + is_tu_local_entity (TYPE_NAME (a), /*explain=*/true); + } + return true; + } + + if (EXPR_P (a) && is_tu_local_value (decl, a, explain)) + return true; + } + } + + return false; +} + +/* Returns true if EXPR (part of the initializer for DECL) is a TU-local value + or object. Emits an explanation if EXPLAIN is true. */ + +bool +depset::hash::is_tu_local_value (tree decl, tree expr, bool explain) +{ + if (!expr) + return false; + + tree e = expr; + STRIP_ANY_LOCATION_WRAPPER (e); + STRIP_NOPS (e); + if (TREE_CODE (e) == TARGET_EXPR) + e = TARGET_EXPR_INITIAL (e); + if (!e) + return false; + + /* It is, or is a pointer to, a TU-local function or the object associated + with a TU-local variable. */ + tree object = NULL_TREE; + if (TREE_CODE (e) == ADDR_EXPR) + object = TREE_OPERAND (e, 0); + else if (TREE_CODE (e) == PTRMEM_CST) + object = PTRMEM_CST_MEMBER (e); + else if (VAR_OR_FUNCTION_DECL_P (e)) + object = e; + + if (object + && VAR_OR_FUNCTION_DECL_P (object) + && is_tu_local_entity (object)) + { + if (explain) + { + /* We've lost a lot of location information by the time we get here, + so let's just do our best effort. */ + auto loc = cp_expr_loc_or_loc (expr, DECL_SOURCE_LOCATION (decl)); + if (VAR_P (object)) + inform (loc, "%qD refers to TU-local object %qD", decl, object); + else + inform (loc, "%qD refers to TU-local function %qD", decl, object); + is_tu_local_entity (object, true); + } + return true; + } + + /* It is an object of class or array type and any of its subobjects or + any of the objects or functions to which its non-static data members + of reference type refer is TU-local and is usable in constant + expressions. */ + if (TREE_CODE (e) == CONSTRUCTOR && AGGREGATE_TYPE_P (TREE_TYPE (e))) + for (auto& f : CONSTRUCTOR_ELTS (e)) + if (is_tu_local_value (decl, f.value, explain)) + return true; + + return false; +} + /* DECL is a newly discovered dependency. Create the depset, if it doesn't already exist. Add it to the worklist if so. @@ -12992,6 +13237,7 @@ depset::hash::make_dependency (tree decl, entity_kind ek) if (ek != EK_USING) { tree not_tmpl = STRIP_TEMPLATE (decl); + bool imported_from_module_p = false; if (DECL_LANG_SPECIFIC (not_tmpl) && DECL_MODULE_IMPORT_P (not_tmpl)) @@ -13007,28 +13253,36 @@ depset::hash::make_dependency (tree decl, entity_kind ek) dep->cluster = index - from->entity_lwm; dep->section = from->remap; dep->set_flag_bit (); + + if (!from->is_header ()) + imported_from_module_p = true; } } - if (ek == EK_DECL - && !dep->is_import () - && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL - && !(TREE_CODE (decl) == TEMPLATE_DECL - && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))) + /* Check for TU-local entities. This is unnecessary in header + units because we can export internal-linkage decls, and + no declarations are exposures. Similarly, if the decl was + imported from a non-header module we know it cannot have + been TU-local. */ + if (!header_module_p () && !imported_from_module_p) { - tree ctx = CP_DECL_CONTEXT (decl); + if (is_tu_local_entity (decl)) + dep->set_flag_bit (); - if (!TREE_PUBLIC (ctx)) - /* Member of internal namespace. */ - dep->set_flag_bit (); - else if (VAR_OR_FUNCTION_DECL_P (not_tmpl) - && DECL_THIS_STATIC (not_tmpl)) + if (VAR_P (decl) + && decl_maybe_constant_var_p (decl) + && is_tu_local_value (decl, DECL_INITIAL (decl))) { - /* An internal decl. This is ok in a GM entity. */ - if (!(header_module_p () - || !DECL_LANG_SPECIFIC (not_tmpl) - || !DECL_MODULE_PURVIEW_P (not_tmpl))) - dep->set_flag_bit (); + /* A potentially-constant variable initialized to a TU-local + value is not usable in constant expressions within other + translation units. We can achieve this by simply not + streaming the definition in such cases. */ + dep->clear_flag_bit (); + + if (DECL_DECLARED_CONSTEXPR_P (decl)) + /* Also, a constexpr variable initialized to a TU-local + value is an exposure. */ + dep->set_flag_bit (); } } } @@ -13055,8 +13309,8 @@ depset::hash::add_dependency (depset *dep) gcc_checking_assert (current && !is_key_order ()); current->deps.safe_push (dep); - if (dep->is_internal () && !current->is_internal ()) - current->set_flag_bit (); + if (dep->is_tu_local ()) + current->set_flag_bit (); if (current->get_entity_kind () == EK_USING && DECL_IMPLICIT_TYPEDEF_P (dep->get_entity ()) @@ -13164,13 +13418,9 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_) /* Ignore entities not within the module purview. */ return false; - if (VAR_OR_FUNCTION_DECL_P (inner) - && DECL_THIS_STATIC (inner)) - { - if (!header_module_p ()) - /* Ignore internal-linkage entitites. */ - return false; - } + if (!header_module_p () && data->hash->is_tu_local_entity (decl)) + /* Ignore TU-local entitites. */ + return false; if ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == TYPE_DECL) @@ -13869,10 +14119,8 @@ bool depset::hash::finalize_dependencies () { bool ok = true; - depset::hash::iterator end (this->end ()); - for (depset::hash::iterator iter (begin ()); iter != end; ++iter) + for (depset *dep : *this) { - depset *dep = *iter; if (dep->is_binding ()) { /* Keep the containing namespace dep first. */ @@ -13885,23 +14133,41 @@ depset::hash::finalize_dependencies () gcc_qsort (&dep->deps[1], dep->deps.length () - 1, sizeof (dep->deps[1]), binding_cmp); } - else if (dep->refs_internal ()) + else if (dep->is_exposure () && !dep->is_tu_local ()) { - for (unsigned ix = dep->deps.length (); ix--;) + ok = false; + bool explained = false; + tree decl = dep->get_entity (); + + for (depset *rdep : dep->deps) + if (!rdep->is_binding () && rdep->is_tu_local ()) + { + // FIXME:QOI Better location information? We're + // losing, so it doesn't matter about efficiency + tree exposed = rdep->get_entity (); + auto_diagnostic_group d; + error_at (DECL_SOURCE_LOCATION (decl), + "%qD exposes TU-local entity %qD", decl, exposed); + bool informed = is_tu_local_entity (exposed, /*explain=*/true); + gcc_checking_assert (informed); + explained = true; + break; + } + + if (!explained && VAR_P (decl) && DECL_DECLARED_CONSTEXPR_P (decl)) { - depset *rdep = dep->deps[ix]; - if (rdep->is_internal ()) - { - // FIXME:QOI Better location information? We're - // losing, so it doesn't matter about efficiency - tree decl = dep->get_entity (); - error_at (DECL_SOURCE_LOCATION (decl), - "%q#D references internal linkage entity %q#D", - decl, rdep->get_entity ()); - break; - } + auto_diagnostic_group d; + error_at (DECL_SOURCE_LOCATION (decl), + "%qD is declared % and is initialized to " + "a TU-local value", decl); + bool informed = is_tu_local_value (decl, DECL_INITIAL (decl), + /*explain=*/true); + gcc_checking_assert (informed); + explained = true; } - ok = false; + + /* We should have emitted an error above. */ + gcc_checking_assert (explained); } } @@ -13925,7 +14191,9 @@ void depset::tarjan::connect (depset *v) { gcc_checking_assert (v->is_binding () - || !(v->is_unreached () || v->is_import ())); + || !(v->is_tu_local () + || v->is_unreached () + || v->is_import ())); v->cluster = v->section = ++index; stack.safe_push (v); @@ -13935,7 +14203,8 @@ depset::tarjan::connect (depset *v) { depset *dep = v->deps[ix]; - if (dep->is_binding () || !dep->is_import ()) + if (dep->is_binding () + || !(dep->is_import () || dep->is_tu_local ())) { unsigned lwm = dep->cluster; @@ -14138,14 +14407,12 @@ depset::hash::connect () tarjan connector (size ()); vec deps; deps.create (size ()); - iterator end (this->end ()); - for (iterator iter (begin ()); iter != end; ++iter) + for (depset *item : *this) { - depset *item = *iter; - entity_kind kind = item->get_entity_kind (); if (kind == EK_BINDING || !(kind == EK_REDIRECT + || item->is_tu_local () || item->is_unreached () || item->is_import ())) deps.quick_push (item); @@ -18407,7 +18674,8 @@ module_state::write_begin (elf_out *to, cpp_reader *reader, note_defs = note_defs_table_t::create_ggc (1000); #endif - /* Determine Strongy Connected Components. */ + /* Determine Strongy Connected Components. This will also strip any + unnecessary dependencies on imported or TU-local entities. */ vec sccs = table.connect (); vec_alloc (ool, modules->length ()); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index f50534f5f39..6519d9e9061 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -564,6 +564,8 @@ cp_debug_parser (FILE *file, cp_parser *parser) parser->in_unbraced_export_declaration_p); cp_debug_print_flag (file, "Parsing a declarator", parser->in_declarator_p); + cp_debug_print_flag (file, "Parsing an initializer", + parser->in_initializer_p); cp_debug_print_flag (file, "In template argument list", parser->in_template_argument_list_p); cp_debug_print_flag (file, "Parsing an iteration statement", @@ -4455,6 +4457,9 @@ cp_parser_new (cp_lexer *lexer) /* We are not processing a declarator. */ parser->in_declarator_p = false; + /* We are not processing an initializer. */ + parser->in_initializer_p = false; + /* We are not processing a template-argument-list. */ parser->in_template_argument_list_p = false; @@ -11426,6 +11431,7 @@ cp_parser_lambda_expression (cp_parser* parser) record_lambda_scope (lambda_expr); record_lambda_scope_discriminator (lambda_expr); + TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p; /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ determine_visibility (TYPE_NAME (type)); @@ -26347,6 +26353,9 @@ cp_parser_initializer (cp_parser *parser, bool *is_direct_init /*=nullptr*/, if (non_constant_p) *non_constant_p = false; + bool saved_in_initializer_p = parser->in_initializer_p; + parser->in_initializer_p = true; + if (token->type == CPP_EQ) { /* Consume the `='. */ @@ -26383,6 +26392,8 @@ cp_parser_initializer (cp_parser *parser, bool *is_direct_init /*=nullptr*/, if (!subexpression_p && check_for_bare_parameter_packs (init)) init = error_mark_node; + parser->in_initializer_p = saved_in_initializer_p; + return init; } @@ -27953,6 +27964,8 @@ cp_parser_class_head (cp_parser* parser, } else if (type == error_mark_node) type = NULL_TREE; + else + TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p; if (type) { @@ -31383,6 +31396,8 @@ cp_parser_concept_definition (cp_parser *parser) } processing_constraint_expression_sentinel parsing_constraint; + parser->in_initializer_p = true; + tree init = cp_parser_constraint_expression (parser); if (init == error_mark_node) cp_parser_skip_to_end_of_statement (parser); @@ -31391,6 +31406,7 @@ cp_parser_concept_definition (cp_parser *parser) but continue as if it were. */ cp_parser_consume_semicolon_at_end_of_statement (parser); + parser->in_initializer_p = false; return finish_concept_definition (id, init, attrs); } diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 09b356e5e73..ee8c4f9001e 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -323,6 +323,9 @@ struct GTY(()) cp_parser { direct-declarator. */ bool in_declarator_p; + /* TRUE if we are parsing an initializer. */ + bool in_initializer_p; + /* TRUE if we are presently parsing a template-argument-list. */ bool in_template_argument_list_p; diff --git a/gcc/testsuite/g++.dg/modules/block-decl-2.C b/gcc/testsuite/g++.dg/modules/block-decl-2.C index d491a18dfb1..104a98ab052 100644 --- a/gcc/testsuite/g++.dg/modules/block-decl-2.C +++ b/gcc/testsuite/g++.dg/modules/block-decl-2.C @@ -11,7 +11,7 @@ export extern "C++" auto foo() { struct X { // `foo` is not attached to a named module, and as such // `X::f` should be implicitly `inline` here - void f() { // { dg-error "references internal linkage entity" } + void f() { // { dg-error "exposes TU-local entity" } internal(); } }; diff --git a/gcc/testsuite/g++.dg/modules/internal-1.C b/gcc/testsuite/g++.dg/modules/internal-1.C index 9f7299a5fc7..3ed74c9cc42 100644 --- a/gcc/testsuite/g++.dg/modules/internal-1.C +++ b/gcc/testsuite/g++.dg/modules/internal-1.C @@ -3,13 +3,10 @@ export module frob; // { dg-module-cmi !frob } -namespace { -// We shouldn't be complaining about members of internal linkage -// entities -class X // { dg-bogus "internal linkage" "" { xfail *-*-* } } -{ // { dg-bogus "internal linkage" "" { xfail *-*-* } } -}; - +namespace +{ + // We shouldn't be complaining about members of internal linkage entities + class X {}; } static int frob () @@ -17,5 +14,5 @@ static int frob () return 1; } -export int f (int = frob ()); // { dg-error "references internal linkage" } -int goof (X &); // { dg-error "references internal linkage" } +export int f (int = frob ()); // { dg-error "exposes TU-local entity" } +int goof (X &); // { dg-error "exposes TU-local entity" } diff --git a/gcc/testsuite/g++.dg/modules/internal-3.C b/gcc/testsuite/g++.dg/modules/internal-3.C new file mode 100644 index 00000000000..91aae32783f --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-3.C @@ -0,0 +1,18 @@ +// { dg-additional-options "-fmodules-ts -Wno-global-module" } +// { dg-module-cmi !M } +// TU-local entities in the GMF can be exposed. + +module; + +static inline void foo() {} + +export module M; + +inline void bar() { // { dg-error "exposes TU-local entity" } + foo(); +} + +// OK +void qux() { + foo(); +} diff --git a/gcc/testsuite/g++.dg/modules/internal-4_a.H b/gcc/testsuite/g++.dg/modules/internal-4_a.H new file mode 100644 index 00000000000..04dfac8c936 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-4_a.H @@ -0,0 +1,4 @@ +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi {} } + +static void header_f() {} diff --git a/gcc/testsuite/g++.dg/modules/internal-4_b.C b/gcc/testsuite/g++.dg/modules/internal-4_b.C new file mode 100644 index 00000000000..c5a3e80366e --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-4_b.C @@ -0,0 +1,124 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi !bad } +// Test for determining various kinds of entities being marked TU-local + +export module bad; +import "internal-4_a.H"; + + +// A type, function variable, or template with internal linkage +namespace { + struct internal_t { int m; }; + enum internal_e {}; + void internal_f() {} + int internal_v; + template void internal_x() {} +} + +inline void expose_type() { // { dg-error "exposes TU-local entity" } + internal_t x; +} +inline void expose_func() { // { dg-error "exposes TU-local entity" } + internal_f(); +} +inline void expose_var() { // { dg-error "exposes TU-local entity" } + int* p = &internal_v; +} +inline void expose_tmpl() { // { dg-error "exposes TU-local entity" } + internal_x(); +} +inline void expose_header_decl() { // { dg-error "exposes TU-local entity" } + header_f(); +} + +// But we don't consider a weakref as being TU-local, despite being +// marked static; this is to support uses of weakrefs in header files +// (such as via the standard library). +static void weakref() __attribute__((weakref("target"))); +inline void expose_weakref() { + weakref(); +} + + +// Does not have a name with linkage and is declared, or introduced by +// a lambda-expression, within the definition of a TU-local entity +static auto get_local_ok() { + return 0; +} +static auto get_local_type() { + struct no_linkage {}; + return no_linkage(); +} +static auto get_local_lambda() { + return []{}; +} +using T = decltype(get_local_ok()); // OK +using U = decltype(get_local_type()); // { dg-error "exposes TU-local entity" } +using V = decltype(get_local_lambda()); // { dg-error "exposes TU-local entity" } + +static auto internal_lambda = []{ internal_f(); }; // OK +auto expose_lambda = internal_lambda; // { dg-error "exposes TU-local entity" } + +int not_in_tu_local + = ([]{ internal_f(); }(), // { dg-error "exposes TU-local entity" } + 0); + + +// A type with no name that is defined outside a class-specifier, function +// body, or initializer + +struct {} no_name; // { dg-error "exposes TU-local entity" } +enum {} e; // { dg-error "exposes TU-local entity" } +using not_an_initializer = class {}; // { dg-error "exposes TU-local entity" } + +class in_class_specifier { struct {} x; }; // OK +void in_function_body() { struct {} x; } // OK +auto in_initializer = []{}; // OK + +#if __cplusplus >= 202002L +decltype([]{}) d_lambda; // { dg-error "exposes TU-local entity" "" { target c++20 } } + +template +concept in_constraint_expression = requires { + // Strictly by the standard this is currently ill-formed + // (this is a constraint-expression not an initializer) + // but I don't think that is intended. + []{}; // OK? +}; +#endif + +// (But consider unnamed types with names for linkage purposes as having names) +typedef struct {} no_name_typedef_t; +no_name_typedef_t linkage_name_struct; // OK + +enum { enum_name } linkage_name_enum; // OK + + +// Specialisation of a TU-local template +template static void f(T) {} +template <> void f(int) {} // OK +inline void f_use(int x) { // { dg-error "exposes TU-local entity" } + f(x); +} + + +// Specialisation of a template with any TU-local argument +template void g(T) {} +template <> void g(internal_t) { internal_f(); } // OK +template <> void g(internal_e) { internal_f(); } // OK +template <> void g(decltype(no_name)) { internal_f(); } // OK +template <> void g(decltype(get_local_lambda())) { internal_f(); } // OK + +template struct h {}; +template struct h<&internal_v>; +template <> struct h<&internal_f> { internal_t x; }; // OK +template <> struct h<&internal_t::m> { void foo() { internal_f(); } }; // OK + + +// TODO: I can't come up with testcases for these that aren't already covered +// by one of the above cases: +// +// - A type with no name introduced by a defining-type-specifier that is +// used to declare only TU-local entities +// - A specialisation of a template whose (possibly instantiated) declaration +// is an exposure diff --git a/gcc/testsuite/g++.dg/modules/linkage-2.C b/gcc/testsuite/g++.dg/modules/linkage-2.C index 4b20411572c..97421bfad8e 100644 --- a/gcc/testsuite/g++.dg/modules/linkage-2.C +++ b/gcc/testsuite/g++.dg/modules/linkage-2.C @@ -25,6 +25,5 @@ export void use() { // Additionally, unnamed types have no linkage but are also TU-local, and thus // cannot be exposed in a module interface unit. The non-TU-local entity 's' -// here is an exposure of this type, so this should be an error; we don't yet -// implement this checking however. -struct {} s; // { dg-error "TU-local" "" { xfail *-*-* } } +// here is an exposure of this type. +struct {} s; // { dg-error "exposes TU-local entity" } From patchwork Fri Sep 27 05:58:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1990100 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=OU6uCkcf; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (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 4XFKYC4YFWz1xt9 for ; Fri, 27 Sep 2024 15:59:27 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 74174385840F for ; Fri, 27 Sep 2024 05:59:25 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x1035.google.com (mail-pj1-x1035.google.com [IPv6:2607:f8b0:4864:20::1035]) by sourceware.org (Postfix) with ESMTPS id EEAB13858408 for ; Fri, 27 Sep 2024 05:58:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EEAB13858408 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 EEAB13858408 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1035 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416713; cv=none; b=qm0e2nXDe7V/nVGkHYuylypHj2CEeMsmo9gOm/w7thQ6Z45tLlMzFr4QzuJdFonQXp5FP8tI9sfNxj8jEgqms27Wwty3+nHR3yubs5EVTqDK1/LTuLEtJX4WyhtEd2NzdfMmQiFBkjmbvp0bslCs7bggdIYA/arlTsgmUAFv7TI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416713; c=relaxed/simple; bh=KyrTyYCONUbJ2oiRm58wWhaq3ECWX4kLRuyZO2wRdBg=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=C/lDCIJJsjDDbgzGpkNbXPrKL0ovKYSKSdisKLApu6AO8l926MrOGk8gRJR3SfK3KwqzOJ17TMkUrrmdLRWUMocFhvi/+QJJ5qNkU8PF78MFD4dVxiNGcfmE7pt5ZvPjrh9hgZVoWJb3eN6+qYIxMYZ/NUdU2ep6ARwDzzOv6sM= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x1035.google.com with SMTP id 98e67ed59e1d1-2e0752be2d1so325333a91.1 for ; Thu, 26 Sep 2024 22:58:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727416707; x=1728021507; darn=gcc.gnu.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:from:to:cc:subject:date:message-id:reply-to; bh=EVUhZ/8MGaEzLQp/O6ygtGVV41haUKkMF+fWrz8qsKQ=; b=OU6uCkcfR9gXVMgiYlwBCkapah+afvq/awXVhSWS2stDwxA0XjS98rWcgu8LTQxTcs AiyaNFim+sMIDwSHKh90mlsqwob7x92K0Ia9GrbS7tBRVUz1UVCho1/LN9eJLeZTQ2Zd Gq5YEm2aZXTgk2PwID5G+uYvhwDL5YDTqmilvmaFpcloIvd/uqW8v84rRZR3po6rz06p 53KSPYG3seh3fjKQOSn0MMWfbjcv9Ydy90XVAu5S2i/EeDrw0UUnYCn/eWSOCZ1zz1M+ I99BTVw5GTayXXfpHFLnCGY9k93kKjZmd4bxSAw6nw1bCT1U7ZflUUa6bc/R6JATYyI9 x6Fg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727416707; x=1728021507; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=EVUhZ/8MGaEzLQp/O6ygtGVV41haUKkMF+fWrz8qsKQ=; b=KQd26ovJyJh1XlvZaREFc32m2I15e0r5XFqo7vH6h2H6EsTG58IFQB48VMpnV8ElHZ N9StNJvnIcnasbt6dyKdrY9zIAxZzPXFfsF3bfqacUVx4lU/hyZXonC2FET1T/c6aTMt vbbx/F18o2h1xJLmPWTUiEdqfYfmUlzBzLVSIpG6ag0MkR4UKLxgGjM57HHiUqxt9kEH KoQzY9Hx4J9mFJ+MyYcPaS8I5wlah6pAhSXfmTGR1GP829x6Fpoo17BxuhPQsieVCudM BqE+xUhrJkAkgzKQdxQDpWxqFvzex3HbjPlRq2dqq4EoR0Ds880xQ00dfFjdktNU3amO k/hA== X-Gm-Message-State: AOJu0YwISzmCg4A/UQy7ttoGgfU4F8B01gX+8sysxQfeSKlmzOvSiDv6 fzsd+ilYJk3ZV56zW/j3X9NBijmzPNKCaSP/AgzKA2FnWTXJkg3G+h4ZWw== X-Google-Smtp-Source: AGHT+IHuLfaZE0/4/ddOkcH9OiKGGhb5PQkijVvnA7xhn0mjBrAOMkUscdqLQAh9r6TFp9Yn6SB3PA== X-Received: by 2002:aa7:8881:0:b0:70a:efec:6b88 with SMTP id d2e1a72fcca58-71b2605647amr1463028b3a.3.1727416706434; Thu, 26 Sep 2024 22:58:26 -0700 (PDT) Received: from Thaum. ([163.47.68.2]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-71b26536605sm820401b3a.184.2024.09.26.22.58.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Sep 2024 22:58:25 -0700 (PDT) Message-ID: <66f64981.a70a0220.2fd01e.465b@mx.google.com> X-Google-Original-Message-ID: Date: Fri, 27 Sep 2024 15:58:21 +1000 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v2 2/6] c++/modules: Implement ignored TU-local exposures References: <66f64939.050a0220.261230.4b66@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <66f64939.050a0220.261230.4b66@mx.google.com> X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.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~incoming=patchwork.ozlabs.org@gcc.gnu.org Currently I just stream DECL_NAME in TU_LOCAL_ENTITYs for use in diagnostics, but this feels perhaps insufficient. Are there any better approaches here? Otherwise I don't think it matters too much, as which entity it is will also be hopefully clear from the 'declared here' notes. I've put the new warning in Wextra, but maybe it would be better to just leave it out of any of the normal warning groups since there's currently no good way to work around the warnings it produces? Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk? -- >8 -- [basic.link] p14 lists a number of circumstances where a declaration naming a TU-local entity is not an exposure, notably the bodies of non-inline templates and friend declarations in classes. This patch ensures that these references do not error when exporting the module. We do need to still error on instantiation from a different module, however, in case this refers to a TU-local entity. As such this patch adds a new tree TU_LOCAL_ENTITY which is used purely as a placeholder to poison any attempted template instantiations that refer to it. This is also streamed for friend decls so that merging (based on the index of an entity into the friend decl list) doesn't break and to prevent complicating the logic; I imagine this shouldn't ever come up though. We also add a new warning, '-Wignored-exposures', to handle the case where someone accidentally refers to a TU-local value from within a non-inline function template. This will compile without errors as-is, but any attempt to instantiate the decl will fail; this warning can be used to ensure that this doesn't happen. Unfortunately the warning has quite some false positives; for instance, a user could deliberately only call explicit instantiations of the decl, or use 'if constexpr' to avoid instantiating the TU-local entity from other TUs, neither of which are currently detected. The main piece that this patch doesn't yet attempt to solve is ADL: as specified, if ADL adds an overload set that includes a translation-unit local entity when instantiating a template, that overload set is now poisoned and counts as an exposure. Unfortunately, we don't currently differentiate between decls that are hidden due to not being exported, or decls that are hidden due to being hidden friends, so this patch instead just keeps the current (wrong) behaviour of non-exported entities not being visible to ADL at all. gcc/c-family/ChangeLog: * c.opt: New warning '-Wignored-exposures'. gcc/cp/ChangeLog: * cp-objcp-common.cc (cp_tree_size): Add TU_LOCAL_ENTITY. * cp-tree.def (TU_LOCAL_ENTITY): New tree code. * cp-tree.h (struct tree_tu_local_entity): New type. (TU_LOCAL_ENTITY_NAME): New accessor. (TU_LOCAL_ENTITY_LOCATION): New accessor. (enum cp_tree_node_structure_enum): Add TS_CP_TU_LOCAL_ENTITY. (union GTY): Add tu_local_entity field. * module.cc (enum tree_tag): New flag DB_IGNORED_EXPOSURE_BIT. (depset::is_ignored_exposure): New accessor. (depset::has_defn): Override for TU-local entities. (depset::hash::ignore_exposure): New field. (depset::hash::hash): Initialize it. (trees_out::tree_tag::tt_tu_local): New flag. (trees_out::writing_local_entities): New field. (trees_out::is_initial_scan): New function. (trees_out::tu_local_count): New counter. (trees_out::trees_out): Initialize writing_local_entities. (dumper::impl::nested_name): Handle TU_LOCAL_ENTITY. (trees_out::instrument): Report TU-local entity counts. (trees_out::decl_value): Early exit for TU-local entities. (trees_in::decl_value): Handle typedefs of TU-local entities. (trees_out::decl_node): Adjust assertion to cope with early exit of TU-local deps. Always write TU-local entities by value. (trees_out::type_node): Handle TU-local types. (trees_out::has_tu_local_dep): New function. (trees_out::find_tu_local_decl): New function. (trees_out::tree_node): Intercept TU-local entities and write placeholder values for them instead of normal streaming. (trees_in::tree_node): Read TU-local entities; handle TU-local template results. (trees_out::write_function_def): Ignore exposures in non-inline function bodies. (trees_out::write_var_def): Ignore exposures in initializers. (trees_out::write_class_def): Ignore exposures in friend decls. (trees_in::read_class_def): Skip TU-local friends. (trees_out::write_definition): Record whether we're writing a decl which refers to TU-local entities. (depset::hash::add_dependency): Handle ignored exposures. (depset::hash::find_dependencies): Use depset's own is_key_order function rather than delegating via walker. Pass whether the decl has ignored TU-local entities in its definition. (depset::hash::finalize_dependencies): Implement new warning Wignored-exposures. (module_state::intercluster_seed): Don't seed TU-local deps. (module_state::write_cluster): Pass whether the decl has ignored TU-local entities in its definition. * pt.cc (complain_about_tu_local_entity): New function. (expr_contains_tu_local_entity): New function. (function_contains_tu_local_entity): New function. (instantiate_class_template): Skip TU-local friends. (tsubst_decl): Handle typedefs of TU-local entities. (tsubst): Complain about TU-local entities. (dependent_operand_p): Early exit for TU-local entities so we don't attempt to constant-evaluate them. (tsubst_expr): Detect and complain about TU-local entities. gcc/ChangeLog: * doc/invoke.texi: Document -Wignored-exposures. gcc/testsuite/ChangeLog: * g++.dg/modules/internal-5_a.C: New test. * g++.dg/modules/internal-5_b.C: New test. * g++.dg/modules/internal-6.C: New test. * g++.dg/modules/internal-7_a.C: New test. * g++.dg/modules/internal-7_b.C: New test. Signed-off-by: Nathaniel Shead --- gcc/c-family/c.opt | 4 + gcc/cp/cp-objcp-common.cc | 1 + gcc/cp/cp-tree.def | 6 + gcc/cp/cp-tree.h | 21 +- gcc/cp/module.cc | 315 +++++++++++++++++--- gcc/cp/pt.cc | 98 +++++- gcc/doc/invoke.texi | 19 +- gcc/testsuite/g++.dg/modules/internal-5_a.C | 104 +++++++ gcc/testsuite/g++.dg/modules/internal-5_b.C | 29 ++ gcc/testsuite/g++.dg/modules/internal-6.C | 24 ++ gcc/testsuite/g++.dg/modules/internal-7_a.C | 75 +++++ gcc/testsuite/g++.dg/modules/internal-7_b.C | 21 ++ 12 files changed, 669 insertions(+), 48 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_a.C create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_b.C create mode 100644 gcc/testsuite/g++.dg/modules/internal-6.C create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_a.C create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_b.C diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index b5983093e24..67d50323c2a 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -832,6 +832,10 @@ Wif-not-aligned C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning Warn when the field in a struct is not aligned. +Wignored-exposures +C++ ObjC++ Var(warn_ignored_exposures) Warning EnabledBy(Wextra) +Warn about ignored exposures of TU-local entities in a module. + Wignored-qualifiers C C++ Var(warn_ignored_qualifiers) Warning EnabledBy(Wextra) Warn whenever type qualifiers are ignored. diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc index cd379514991..b959533bcb1 100644 --- a/gcc/cp/cp-objcp-common.cc +++ b/gcc/cp/cp-objcp-common.cc @@ -233,6 +233,7 @@ cp_tree_size (enum tree_code code) case ASSERTION_STMT: return sizeof (tree_exp); case PRECONDITION_STMT: return sizeof (tree_exp); case POSTCONDITION_STMT: return sizeof (tree_exp); + case TU_LOCAL_ENTITY: return sizeof (tree_tu_local_entity); default: switch (TREE_CODE_CLASS (code)) { diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 18f75108c7b..7580dc3667d 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -573,6 +573,12 @@ DEFTREECODE (ASSERTION_STMT, "assertion_stmt", tcc_statement, 3) DEFTREECODE (PRECONDITION_STMT, "precondition_stmt", tcc_statement, 3) DEFTREECODE (POSTCONDITION_STMT, "postcondition_stmt", tcc_statement, 4) +/* A reference to a translation-unit local entity. + + This is emitted by modules streaming when writing a TU-local entity that + wasn't an exposure (e.g. in a non-inline function template). */ +DEFTREECODE (TU_LOCAL_ENTITY, "tu_local_entity", tcc_exceptional, 0) + /* Local variables: mode:c diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index cfaf82b018c..48a99f116b0 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1779,6 +1779,22 @@ check_constraint_info (tree t) it for unscoped enums. */ #define DECL_MODULE_EXPORT_P(NODE) TREE_LANG_FLAG_3 (NODE) +/* Represents a streamed-in translation-unit-local entity. Any use of this + should emit an error. */ +struct GTY(()) tree_tu_local_entity { + struct tree_base base; + tree name; + location_t loc; +}; + +/* The name of a translation-unit local-entity. */ +#define TU_LOCAL_ENTITY_NAME(NODE) \ + (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->name) + +/* The source location of the translation-unit-local entity. */ +#define TU_LOCAL_ENTITY_LOCATION(NODE) \ + (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->loc) + /* The list of local parameters introduced by this requires-expression, in the form of a chain of PARM_DECLs. */ @@ -1812,7 +1828,8 @@ enum cp_tree_node_structure_enum { TS_CP_LAMBDA_EXPR, TS_CP_TEMPLATE_INFO, TS_CP_CONSTRAINT_INFO, - TS_CP_USERDEF_LITERAL + TS_CP_USERDEF_LITERAL, + TS_CP_TU_LOCAL_ENTITY }; /* The resulting tree type. */ @@ -1843,6 +1860,8 @@ union GTY((desc ("cp_tree_node_structure (&%h)"), constraint_info; struct tree_userdef_literal GTY ((tag ("TS_CP_USERDEF_LITERAL"))) userdef_literal; + struct tree_tu_local_entity GTY ((tag ("TS_CP_TU_LOCAL_ENTITY"))) + tu_local_entity; }; diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 570320ea627..5d7f4941b2a 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2332,6 +2332,7 @@ private: DB_IS_MEMBER_BIT, /* Is an out-of-class member. */ DB_TU_LOCAL_BIT, /* It is a TU-local entity. */ DB_EXPOSURE_BIT, /* Exposes a TU-local entity. */ + DB_IGNORED_EXPOSURE_BIT, /* Will stream a TU-local entity. */ DB_IMPORTED_BIT, /* An imported entity. */ DB_UNREACHED_BIT, /* A yet-to-be reached entity. */ DB_HIDDEN_BIT, /* A hidden binding. */ @@ -2401,7 +2402,9 @@ public: public: bool has_defn () const { - return get_flag_bit (); + /* Never consider TU-local entities as having definitions, since + we will never be accessing them from importers anyway. */ + return get_flag_bit () && !is_tu_local (); } public: @@ -2420,6 +2423,10 @@ public: { return get_flag_bit (); } + bool is_ignored_exposure () const + { + return get_flag_bit (); + } bool is_import () const { return get_flag_bit (); @@ -2543,11 +2550,12 @@ public: depset *current; /* Current depset being depended. */ unsigned section; /* When writing out, the section. */ bool reached_unreached; /* We reached an unreached entity. */ + bool ignore_exposure; /* In a context where exposures are ignored. */ public: hash (size_t size, hash *c = NULL) : parent (size), chain (c), current (NULL), section (0), - reached_unreached (false) + reached_unreached (false), ignore_exposure (false) { worklist.create (size); } @@ -2748,6 +2756,7 @@ static GTY((cache)) decl_tree_cache_map *imported_temploid_friends; /* Tree tags. */ enum tree_tag { tt_null, /* NULL_TREE. */ + tt_tu_local, /* A TU-local entity. */ tt_fixed, /* Fixed vector index. */ tt_node, /* By-value node. */ @@ -3032,6 +3041,8 @@ private: depset::hash *dep_hash; /* Dependency table. */ int ref_num; /* Back reference number. */ unsigned section; + bool writing_local_entities; /* Whether we might walk into a TU-local + entity we need to emit placeholders for. */ #if CHECKING_P int importedness; /* Checker that imports not occurring inappropriately. +ve imports ok, @@ -3061,6 +3072,18 @@ public: }; public: + /* The walk is used for three similar purposes: + + 1. The initial scan for dependencies. + 2. Once dependencies have been found, ordering them. + 3. Writing dependencies to file (streaming_p). + + For cases where it matters, these accessers can be used to determine + which state we're in. */ + bool is_initial_scan () const + { + return !streaming_p () && !is_key_order (); + } bool is_key_order () const { return dep_hash->is_key_order (); @@ -3101,6 +3124,10 @@ private: void tree_pair_vec (vec *); void tree_list (tree, bool has_purpose); +private: + bool has_tu_local_dep (tree) const; + tree find_tu_local_decl (tree); + public: /* Mark a node for by-value walking. */ void mark_by_value (tree); @@ -3138,7 +3165,7 @@ public: public: /* Serialize various definitions. */ - void write_definition (tree decl); + void write_definition (tree decl, bool has_tu_local = false); void mark_declaration (tree decl, bool do_defn); private: @@ -3166,6 +3193,7 @@ private: static unsigned tree_val_count; static unsigned decl_val_count; static unsigned back_ref_count; + static unsigned tu_local_count; static unsigned null_count; }; } // anon namespace @@ -3174,12 +3202,14 @@ private: unsigned trees_out::tree_val_count; unsigned trees_out::decl_val_count; unsigned trees_out::back_ref_count; +unsigned trees_out::tu_local_count; unsigned trees_out::null_count; trees_out::trees_out (allocator *mem, module_state *state, depset::hash &deps, unsigned section) :parent (mem), state (state), tree_map (500), - dep_hash (&deps), ref_num (0), section (section) + dep_hash (&deps), ref_num (0), section (section), + writing_local_entities (false) { #if CHECKING_P importedness = 0; @@ -4301,6 +4331,9 @@ dumper::impl::nested_name (tree t) int origin = -1; tree name = NULL_TREE; + if (t && TREE_CODE (t) == TU_LOCAL_ENTITY) + t = TU_LOCAL_ENTITY_NAME (t); + if (t && TREE_CODE (t) == TREE_BINFO) t = BINFO_TYPE (t); @@ -4853,6 +4886,7 @@ trees_out::instrument () dump (" %u decl trees", decl_val_count); dump (" %u other trees", tree_val_count); dump (" %u back references", back_ref_count); + dump (" %u TU-local entities", tu_local_count); dump (" %u null trees", null_count); } } @@ -7811,6 +7845,17 @@ trees_out::decl_value (tree decl, depset *dep) || DECL_ORIGINAL_TYPE (decl) || !TYPE_PTRMEMFUNC_P (TREE_TYPE (decl))); + /* There's no need to walk any of the contents of a known TU-local entity, + since importers should never see any of it regardless. But make sure we + at least note its location so importers can use it for diagnostics. */ + if (dep && dep->is_tu_local ()) + { + gcc_checking_assert (is_initial_scan ()); + insert (decl, WK_value); + state->note_location (DECL_SOURCE_LOCATION (decl)); + return; + } + merge_kind mk = get_merge_kind (decl, dep); bool is_imported_temploid_friend = imported_temploid_friends->get (decl); @@ -8392,14 +8437,17 @@ trees_in::decl_value () /* Frob it to be ready for cloning. */ TREE_TYPE (inner) = DECL_ORIGINAL_TYPE (inner); DECL_ORIGINAL_TYPE (inner) = NULL_TREE; - set_underlying_type (inner); - if (tdef_flags & 2) + if (TREE_CODE (TREE_TYPE (inner)) != TU_LOCAL_ENTITY) { - /* Match instantiate_alias_template's handling. */ - tree type = TREE_TYPE (inner); - TYPE_DEPENDENT_P (type) = true; - TYPE_DEPENDENT_P_VALID (type) = true; - SET_TYPE_STRUCTURAL_EQUALITY (type); + set_underlying_type (inner); + if (tdef_flags & 2) + { + /* Match instantiate_alias_template's handling. */ + tree type = TREE_TYPE (inner); + TYPE_DEPENDENT_P (type) = true; + TYPE_DEPENDENT_P_VALID (type) = true; + SET_TYPE_STRUCTURAL_EQUALITY (type); + } } } @@ -8904,10 +8952,14 @@ trees_out::decl_node (tree decl, walk_kind ref) } tree_node (tpl); - /* Streaming TPL caused us to visit DECL and maybe its type. */ - gcc_checking_assert (TREE_VISITED (decl)); - if (DECL_IMPLICIT_TYPEDEF_P (decl)) - gcc_checking_assert (TREE_VISITED (TREE_TYPE (decl))); + /* Streaming TPL caused us to visit DECL and maybe its type, + if it wasn't TU-local. */ + if (CHECKING_P && !has_tu_local_dep (tpl)) + { + gcc_checking_assert (TREE_VISITED (decl)); + if (DECL_IMPLICIT_TYPEDEF_P (decl)) + gcc_checking_assert (TREE_VISITED (TREE_TYPE (decl))); + } return false; } @@ -8927,10 +8979,10 @@ trees_out::decl_node (tree decl, walk_kind ref) dep = dep_hash->add_dependency (decl, kind); } - if (!dep) + if (!dep || dep->is_tu_local ()) { /* Some internal entity of context. Do by value. */ - decl_value (decl, NULL); + decl_value (decl, dep); return false; } @@ -9086,7 +9138,10 @@ trees_out::type_node (tree type) if (streaming_p ()) dump (dumper::TREE) && dump ("Wrote typedef %C:%N%S", TREE_CODE (name), name, name); - gcc_checking_assert (TREE_VISITED (type)); + + /* We'll have either visited this type or have newly discovered + that it's TU-local; either way we won't need to visit it again. */ + gcc_checking_assert (TREE_VISITED (type) || has_tu_local_dep (name)); return; } @@ -9365,6 +9420,64 @@ trees_in::tree_value () return existing; } +/* Whether DECL has a TU-local dependency in the hash. */ + +bool +trees_out::has_tu_local_dep (tree decl) const +{ + /* Only the contexts of fields or enums remember that they're + TU-local. */ + if (DECL_CONTEXT (decl) + && (TREE_CODE (decl) == FIELD_DECL + || TREE_CODE (decl) == CONST_DECL)) + decl = TYPE_NAME (DECL_CONTEXT (decl)); + + depset *dep = dep_hash->find_dependency (decl); + return dep && dep->is_tu_local (); +} + +/* If T depends on a TU-local entity, return that decl. */ + +tree +trees_out::find_tu_local_decl (tree t) +{ + /* We need to have walked all deps first before we can check. */ + gcc_checking_assert (!is_initial_scan ()); + + auto walker = [](tree *tp, int *walk_subtrees, void *data) -> tree + { + auto self = (trees_out *)data; + + tree decl = NULL_TREE; + if (TYPE_P (*tp)) + { + /* A PMF type is a record type, which we otherwise wouldn't walk; + return whether the function type is TU-local. */ + if (TYPE_PTRMEMFUNC_P (*tp)) + { + *walk_subtrees = 0; + return self->find_tu_local_decl (TYPE_PTRMEMFUNC_FN_TYPE (*tp)); + } + else + decl = TYPE_MAIN_DECL (*tp); + } + else if (DECL_P (*tp)) + decl = *tp; + + if (decl) + { + /* We found a DECL, this will tell us whether we're TU-local. */ + *walk_subtrees = 0; + return self->has_tu_local_dep (decl) ? decl : NULL_TREE; + } + return NULL_TREE; + }; + + /* We need to walk without duplicates so that we step into the pointed-to + types of array types. */ + return cp_walk_tree_without_duplicates (&t, walker, this); +} + /* Stream out tree node T. We automatically create local back references, which is essentially a single pass lisp self-referential structure pretty-printer. */ @@ -9377,6 +9490,46 @@ trees_out::tree_node (tree t) if (ref == WK_none) goto done; + /* Find TU-local entities and intercept streaming to instead write a + placeholder value; this way we don't need to expose such decls. + We only need to do this when writing a definition of an entity + that we know is an ignored exposure. */ + if (!is_initial_scan () && writing_local_entities) + { + tree local_decl = NULL_TREE; + if (DECL_P (t) && has_tu_local_dep (t)) + local_decl = t; + /* Consider a type to be TU-local if it refers to any TU-local decl, + no matter how deep. + + This worsens diagnostics slightly, as we often no longer point + directly to the at-fault entity when instantiating. However, this + reduces the module size slightly and means that much less of pt.cc + needs to know about us. */ + else if (TYPE_P (t)) + local_decl = find_tu_local_decl (t); + else if (EXPR_P (t)) + local_decl = find_tu_local_decl (TREE_TYPE (t)); + + if (local_decl) + { + int tag = insert (t, WK_value); + if (streaming_p ()) + { + tu_local_count++; + i (tt_tu_local); + dump (dumper::TREE) + && dump ("Writing TU-local entity:%d %C:%N", + tag, TREE_CODE (t), t); + } + /* TODO: Get a more descriptive name? */ + tree_node (DECL_NAME (local_decl)); + if (state) + state->write_location (*this, DECL_SOURCE_LOCATION (local_decl)); + goto done; + } + } + if (ref != WK_normal) goto skip_normal; @@ -9533,6 +9686,18 @@ trees_in::tree_node (bool is_use) /* NULL_TREE. */ break; + case tt_tu_local: + { + /* A translation-unit local entity. */ + res = make_node (TU_LOCAL_ENTITY); + int tag = insert (res); + + TU_LOCAL_ENTITY_NAME (res) = tree_node (); + TU_LOCAL_ENTITY_LOCATION (res) = state->read_location (*this); + dump (dumper::TREE) && dump ("Read TU-local entity:%d %N", tag, res); + } + break; + case tt_fixed: /* A fixed ref, find it in the fixed_ref array. */ { @@ -10149,7 +10314,8 @@ trees_in::tree_node (bool is_use) /* A template. */ if (tree tpl = tree_node ()) { - res = DECL_TEMPLATE_RESULT (tpl); + res = (TREE_CODE (tpl) == TU_LOCAL_ENTITY ? + tpl : DECL_TEMPLATE_RESULT (tpl)); dump (dumper::TREE) && dump ("Read template %C:%N", TREE_CODE (res), res); } @@ -11919,8 +12085,18 @@ void trees_out::write_function_def (tree decl) { tree_node (DECL_RESULT (decl)); - tree_node (DECL_INITIAL (decl)); - tree_node (DECL_SAVED_TREE (decl)); + + { + /* The function body for a non-inline function or function template + is ignored for determining exposures. This should only matter + for templates (we don't emit the bodies of non-inline functions + to begin with). */ + auto ovr = make_temp_override (dep_hash->ignore_exposure, + !DECL_DECLARED_INLINE_P (decl)); + tree_node (DECL_INITIAL (decl)); + tree_node (DECL_SAVED_TREE (decl)); + } + tree_node (DECL_FRIEND_CONTEXT (decl)); constexpr_fundef *cexpr = retrieve_constexpr_fundef (decl); @@ -12022,6 +12198,10 @@ trees_in::read_function_def (tree decl, tree maybe_template) void trees_out::write_var_def (tree decl) { + /* The initializer of a variable or variable template is ignored for + determining exposures. */ + auto ovr = make_temp_override (dep_hash->ignore_exposure, VAR_P (decl)); + tree init = DECL_INITIAL (decl); tree_node (init); if (!init) @@ -12209,21 +12389,28 @@ trees_out::write_class_def (tree defn) for (; vtables; vtables = TREE_CHAIN (vtables)) write_definition (vtables); - /* Write the friend classes. */ - tree_list (CLASSTYPE_FRIEND_CLASSES (type), false); + { + /* Friend declarations in class definitions are ignored when + determining exposures. */ + auto ovr = make_temp_override (dep_hash->ignore_exposure, true); - /* Write the friend functions. */ - for (tree friends = DECL_FRIENDLIST (defn); - friends; friends = TREE_CHAIN (friends)) - { - /* Name of these friends. */ - tree_node (TREE_PURPOSE (friends)); - tree_list (TREE_VALUE (friends), false); - } - /* End of friend fns. */ - tree_node (NULL_TREE); + /* Write the friend classes. */ + tree_list (CLASSTYPE_FRIEND_CLASSES (type), false); - /* Write the decl list. */ + /* Write the friend functions. */ + for (tree friends = DECL_FRIENDLIST (defn); + friends; friends = TREE_CHAIN (friends)) + { + tree_node (FRIEND_NAME (friends)); + tree_list (FRIEND_DECLS (friends), false); + } + /* End of friend fns. */ + tree_node (NULL_TREE); + } + + /* Write the decl list. We don't need to ignore exposures of friend + decls here as any such decls should already have been added and + ignored above. */ tree_list (CLASSTYPE_DECL_LIST (type), true); if (TYPE_CONTAINS_VPTR_P (type)) @@ -12576,6 +12763,8 @@ trees_in::read_class_def (tree defn, tree maybe_template) friend_decls; friend_decls = TREE_CHAIN (friend_decls)) { tree f = TREE_VALUE (friend_decls); + if (TREE_CODE (f) == TU_LOCAL_ENTITY) + continue; DECL_BEFRIENDING_CLASSES (f) = tree_cons (NULL_TREE, type, DECL_BEFRIENDING_CLASSES (f)); @@ -12724,8 +12913,11 @@ trees_in::read_enum_def (tree defn, tree maybe_template) /* Write out the body of DECL. See above circularity note. */ void -trees_out::write_definition (tree decl) +trees_out::write_definition (tree decl, bool has_tu_local) { + auto ovr = make_temp_override (writing_local_entities, + writing_local_entities || has_tu_local); + if (streaming_p ()) { assert_definition (decl); @@ -13310,7 +13502,12 @@ depset::hash::add_dependency (depset *dep) current->deps.safe_push (dep); if (dep->is_tu_local ()) - current->set_flag_bit (); + { + if (ignore_exposure) + current->set_flag_bit (); + else + current->set_flag_bit (); + } if (current->get_entity_kind () == EK_USING && DECL_IMPLICIT_TYPEDEF_P (dep->get_entity ()) @@ -13960,7 +14157,7 @@ depset::hash::find_dependencies (module_state *module) { walker.mark_declaration (decl, current->has_defn ()); - if (!walker.is_key_order () + if (!is_key_order () && (item->get_entity_kind () == EK_SPECIALIZATION || item->get_entity_kind () == EK_PARTIAL || (item->get_entity_kind () == EK_DECL @@ -13972,15 +14169,16 @@ depset::hash::find_dependencies (module_state *module) walker.decl_value (decl, current); if (current->has_defn ()) - walker.write_definition (decl); + walker.write_definition (decl, + current->is_ignored_exposure ()); } walker.end (); - if (!walker.is_key_order () + if (!is_key_order () && DECL_CLASS_TEMPLATE_P (decl)) add_deduction_guides (decl); - if (!walker.is_key_order () + if (!is_key_order () && TREE_CODE (decl) == TEMPLATE_DECL && !DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) { @@ -14169,6 +14367,36 @@ depset::hash::finalize_dependencies () /* We should have emitted an error above. */ gcc_checking_assert (explained); } + else if (warn_ignored_exposures && dep->is_ignored_exposure ()) + { + tree decl = dep->get_entity (); + + /* Friend decls in a class body are ignored, but this is harmless: + it should not impact any consumers. */ + if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl))) + continue; + + /* We should now only be warning about templates. */ + gcc_checking_assert + (TREE_CODE (decl) == TEMPLATE_DECL + && VAR_OR_FUNCTION_DECL_P (DECL_TEMPLATE_RESULT (decl))); + + /* Ideally we would only warn in cases where there are no explicit + instantiations of the template, but we don't currently track this + in an easy-to-find way. */ + for (depset *rdep : dep->deps) + if (!rdep->is_binding () && rdep->is_tu_local ()) + { + tree exposed = rdep->get_entity (); + auto_diagnostic_group d; + if (warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wignored_exposures, + "%qD refers to TU-local entity %qD and cannot " + "be instantiated in other TUs", decl, exposed)) + is_tu_local_entity (exposed, /*explain=*/true); + break; + } + } } return ok; @@ -15332,8 +15560,9 @@ enum ct_bind_flags void module_state::intercluster_seed (trees_out &sec, unsigned index_hwm, depset *dep) { - if (dep->is_import () - || dep->cluster < index_hwm) + if (dep->is_tu_local ()) + /* We only stream placeholders for TU-local entities anyway. */; + else if (dep->is_import () || dep->cluster < index_hwm) { tree ent = dep->get_entity (); if (!TREE_VISITED (ent)) @@ -15554,7 +15783,7 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size, sec.u (ct_defn); sec.tree_node (decl); dump () && dump ("Writing definition %N", decl); - sec.write_definition (decl); + sec.write_definition (decl, b->is_ignored_exposure ()); if (!namer->has_defn ()) namer = b; diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index a3a697dd126..d60837e7bb9 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -9869,6 +9869,71 @@ add_pending_template (tree d) pop_tinst_level (); } +/* Emit a diagnostic about instantiating a reference to TU-local entity E. */ + +static void +complain_about_tu_local_entity (tree e) +{ + auto_diagnostic_group d; + error ("instantiation exposes TU-local entity %qD", + TU_LOCAL_ENTITY_NAME (e)); + inform (TU_LOCAL_ENTITY_LOCATION (e), "declared here"); +} + +/* Checks if T contains a TU-local entity. */ + +static bool +expr_contains_tu_local_entity (tree t) +{ + if (!modules_p ()) + return false; + + auto walker = [](tree *tp, int *walk_subtrees, void *) -> tree + { + if (TREE_CODE (*tp) == TU_LOCAL_ENTITY) + return *tp; + if (!EXPR_P (*tp)) + *walk_subtrees = false; + return NULL_TREE; + }; + return cp_walk_tree (&t, walker, nullptr, nullptr); +} + +/* Errors and returns TRUE if X is a function that contains a TU-local + entity in its overload set. */ + +static bool +function_contains_tu_local_entity (tree x) +{ + if (!modules_p ()) + return false; + + if (!x || x == error_mark_node) + return false; + + if (TREE_CODE (x) == OFFSET_REF + || TREE_CODE (x) == COMPONENT_REF) + x = TREE_OPERAND (x, 1); + x = MAYBE_BASELINK_FUNCTIONS (x); + if (TREE_CODE (x) == TEMPLATE_ID_EXPR) + x = TREE_OPERAND (x, 0); + + if (OVL_P (x)) + for (tree ovl : lkp_range (x)) + if (TREE_CODE (ovl) == TU_LOCAL_ENTITY) + { + x = ovl; + break; + } + + if (TREE_CODE (x) == TU_LOCAL_ENTITY) + { + complain_about_tu_local_entity (x); + return true; + } + + return false; +} /* Return a TEMPLATE_ID_EXPR corresponding to the indicated FNS and ARGLIST. Valid choices for FNS are given in the cp-tree.def @@ -12734,8 +12799,10 @@ instantiate_class_template (tree type) } else { - if (TYPE_P (t) || DECL_CLASS_TEMPLATE_P (t) - || DECL_TEMPLATE_TEMPLATE_PARM_P (t)) + if (TREE_CODE (t) == TU_LOCAL_ENTITY) + /* Ignore. */; + else if (TYPE_P (t) || DECL_CLASS_TEMPLATE_P (t) + || DECL_TEMPLATE_TEMPLATE_PARM_P (t)) { /* Build new CLASSTYPE_FRIEND_CLASSES. */ @@ -15513,7 +15580,8 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, RETURN (error_mark_node); if (TREE_CODE (t) == TYPE_DECL - && t == TYPE_MAIN_DECL (TREE_TYPE (t))) + && (TREE_CODE (TREE_TYPE (t)) == TU_LOCAL_ENTITY + || t == TYPE_MAIN_DECL (TREE_TYPE (t)))) { /* If this is the canonical decl, we don't have to mess with instantiations, and often we can't (for @@ -16241,6 +16309,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) || TREE_CODE (t) == TRANSLATION_UNIT_DECL) return t; + /* Any instantiation of a template containing a TU-local entity is an + exposure, so always issue a hard error irrespective of complain. */ + if (TREE_CODE (t) == TU_LOCAL_ENTITY) + { + complain_about_tu_local_entity (t); + return error_mark_node; + } + tsubst_flags_t tst_ok_flag = (complain & tf_tst_ok); complain &= ~tf_tst_ok; @@ -18477,6 +18553,12 @@ dependent_operand_p (tree t) { while (TREE_CODE (t) == IMPLICIT_CONV_EXPR) t = TREE_OPERAND (t, 0); + + /* If we contain a TU_LOCAL_ENTITY assume we're non-dependent; we'll error + later when instantiating. */ + if (expr_contains_tu_local_entity (t)) + return false; + ++processing_template_decl; bool r = (potential_constant_expression (t) ? value_dependent_expression_p (t) @@ -20246,6 +20328,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) else object = NULL_TREE; + if (function_contains_tu_local_entity (templ)) + RETURN (error_mark_node); + tree tid = lookup_template_function (templ, targs); protected_set_expr_location (tid, EXPR_LOCATION (t)); @@ -20938,6 +21023,9 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) qualified_p = true; } + if (function_contains_tu_local_entity (function)) + RETURN (error_mark_node); + nargs = call_expr_nargs (t); releasing_vec call_args; tsubst_call_args (t, args, complain, in_decl, call_args); @@ -21959,6 +22047,10 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) RETURN (op); } + case TU_LOCAL_ENTITY: + complain_about_tu_local_entity (t); + RETURN (error_mark_node); + default: /* Handle Objective-C++ constructs, if appropriate. */ if (tree subst = objcp_tsubst_expr (t, args, complain, in_decl)) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index bd1208a62ee..bb4659ceeb8 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -259,7 +259,7 @@ in the following sections. -Weffc++ -Wno-elaborated-enum-base -Wno-exceptions -Wextra-semi -Wno-global-module -Wno-inaccessible-base -Wno-inherited-variadic-ctor -Wno-init-list-lifetime --Winvalid-constexpr -Winvalid-imported-macros +-Wignored-exposures -Winvalid-constexpr -Winvalid-imported-macros -Wno-invalid-offsetof -Wno-literal-suffix -Wmismatched-new-delete -Wmismatched-tags -Wmultiple-inheritance -Wnamespaces -Wnarrowing @@ -4086,6 +4086,23 @@ the variable declaration statement. @end itemize +@opindex Wignored-exposures +@opindex Wno-ignored-exposures +@item -Wignored-exposures +Warn when a template body hides an exposure of a translation-unit-local +entity. In most cases, referring to a translation-unit-local entity +(such as an internal linkage declaration) within an entity that is +emitted into a module's CMI is an error. However, within the +initializer of a variable, or in the body of a non-inline function, +this error is suppressed. + +This can cause variable or function templates to accidentally become +unusable if they reference such an entity, because other translation +units that import the template will never be able to instantiate it. +This warning attempts to detect cases where this might occur. + +This warning is enabled by @option{-Wextra}. + @opindex Winvalid-constexpr @opindex Wno-invalid-constexpr @item -Winvalid-constexpr diff --git a/gcc/testsuite/g++.dg/modules/internal-5_a.C b/gcc/testsuite/g++.dg/modules/internal-5_a.C new file mode 100644 index 00000000000..a36fcb0d834 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-5_a.C @@ -0,0 +1,104 @@ +// { dg-additional-options "-fmodules-ts -Wignored-exposures" } +// { dg-module-cmi M } +// Ignore exposures in these cases + +export module M; + +namespace { + inline namespace ns { + struct internal_t {}; + template struct internal_tmpl_t {}; + + int internal_x; + void internal_ovl(int&) {} + void internal_ovl(internal_t) {} + + template void internal_tmpl() {} + } +} +export struct ok_inst_tag {}; + + +// The function body for a non-inline function or function template +export void function() { + internal_t i {}; + internal_tmpl_t ii {}; + internal_ovl(internal_x); + internal_tmpl(); +} + +export template void function_tmpl() { // { dg-warning "refers to TU-local entity" } + internal_t i {}; + internal_tmpl_t ii {}; + internal_ovl(internal_x); + internal_tmpl(); +} +template void function_tmpl(); + + +// The initializer for a variable or variable template +export int var + = (internal_t{}, internal_tmpl_t{}, + internal_ovl(internal_x), internal_tmpl(), 0); + +export template int var_tmpl // { dg-warning "refers to TU-local entity" } + = (internal_t{}, internal_tmpl_t{}, + internal_ovl(internal_x), internal_tmpl(), 0); + +template int var_tmpl // { dg-warning "refers to TU-local entity" } + = (internal_t{}, internal_tmpl_t{}, + internal_ovl(internal_x), internal_tmpl(), 0); + +template int var_tmpl; +template int var_tmpl; + +export int& constant_ref = internal_x; +static_assert (&constant_ref == &internal_x); + + +// Friend declarations in a class definition +export struct klass { // { dg-bogus "TU-local" } + friend ns::internal_t; + friend ns::internal_tmpl_t; + friend void ns::internal_ovl(int&); + friend void ns::internal_ovl(internal_t); + friend void ns::internal_tmpl(); + + template friend struct ns::internal_tmpl_t; + template friend void ns::internal_tmpl(); +}; + +export template +class klass_tmpl { // { dg-bogus "TU-local" } + friend ns::internal_t; + friend ns::internal_tmpl_t; + friend void ns::internal_ovl(int&); + friend void ns::internal_ovl(internal_t); + friend void ns::internal_tmpl(); + + template friend struct ns::internal_tmpl_t; + template friend void ns::internal_tmpl(); +}; + +template class klass_tmpl { // { dg-bogus "TU-local" } + friend ns::internal_t; + friend ns::internal_tmpl_t; + friend void ns::internal_ovl(int&); + friend void ns::internal_ovl(internal_t); + friend void ns::internal_tmpl(); + + template friend struct ns::internal_tmpl_t; + template friend void ns::internal_tmpl(); +}; + + +// Any reference to a non-volatile const object or reference with internal or +// no linkage initialized with a constant expression that is not an ODR-use +static const int value = 123; +static const int& ref = value; +static const internal_t internal {}; +export inline void no_odr_use() { + int x = value; + int y = ref; + int z = (internal, 0); +} diff --git a/gcc/testsuite/g++.dg/modules/internal-5_b.C b/gcc/testsuite/g++.dg/modules/internal-5_b.C new file mode 100644 index 00000000000..ecf78861fb7 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-5_b.C @@ -0,0 +1,29 @@ +// { dg-additional-options "-fmodules-ts" } + +import M; + +int main() { + // These are all OK + function(); + int a = var; + klass k; + klass_tmpl kt; + klass_tmpl ktp; + no_odr_use(); + + function_tmpl(); + int b = var_tmpl; + int c = var_tmpl; + + // But don't ignore exposures in these cases + function_tmpl(); // { dg-message "required from here" } + int x = var_tmpl; // { dg-message "required from here" } + int y = var_tmpl; // { dg-message "required from here" } + + // And decls initialized to a TU-local value are not constant here + // Unfortunately the error does not currently point to this decl + constexpr int& r = constant_ref; + // { dg-error "is not a constant expression" "" { target *-*-* } 0 } +} + +// { dg-error "instantiation exposes TU-local entity" "" { target *-*-* } 0 } diff --git a/gcc/testsuite/g++.dg/modules/internal-6.C b/gcc/testsuite/g++.dg/modules/internal-6.C new file mode 100644 index 00000000000..0f138781ad5 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-6.C @@ -0,0 +1,24 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi !M } +// Exposures (or not) of TU-local values + +export module M; + +static void f() {} +auto& fr = f; // OK +constexpr auto& fr2 = fr; // { dg-error "initialized to a TU-local value" } +static constexpr auto fp2 = fr; // OK + +struct S { void (&ref)(); } s{ f }; // OK, value is TU-local +constexpr extern struct W { S& s; } wrap{ s }; // OK, value is not TU-local +constexpr S s2{ f }; // { dg-error "initialized to a TU-local value" } + +constexpr int a = 123; +static constexpr int b = 456; +struct X { + union { + const int* p[2]; + }; +}; +constexpr X x { &a }; // OK +constexpr X y { &a, &b }; // { dg-error "initialized to a TU-local value" } diff --git a/gcc/testsuite/g++.dg/modules/internal-7_a.C b/gcc/testsuite/g++.dg/modules/internal-7_a.C new file mode 100644 index 00000000000..47b7f2eb54e --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-7_a.C @@ -0,0 +1,75 @@ +// { dg-additional-options "-fmodules-ts -Wignored-exposures" } +// Test streaming and instantiations of various kinds of exposures + +export module M; + +namespace { + int x; + constexpr int y = 1; + + struct S { int m; void d(); }; + enum class E { e }; + + template int f(T t) { return (int)t; } +} + +template void g() {} + +template +int expose_1() { // { dg-warning "TU-local" } + return x; +} + +template +void expose_2() { // { dg-warning "TU-local" } + T t = &y; +} + +template +bool expose_3() { // { dg-warning "TU-local" } + return !(T{} * (x + 5) > 123); +} + +template +bool expose_4() { // { dg-warning "TU-local" } + return __is_same(S, T); +} + +template +void expose_5() { // { dg-warning "TU-local" } + static_assert(T{} == (int)E::e); +} + +template +void expose_6() { // { dg-warning "TU-local" } + f(T{}); +} + +template +void expose_7() { // { dg-warning "TU-local" } + g<&y>(); +} + +template +void expose_8() { // { dg-warning "TU-local" } + decltype(T{} .* &S::m)* (*x)[5][10]; +}; + +template +bool expose_9() { // { dg-warning "TU-local" } + return noexcept((T{} .* &S::d)()); +} + +template +void expose_10() { // { dg-warning "TU-local" } + using U = decltype(f()); +} + +template +void expose_11() { // { dg-warning "TU-local" } + static thread_local E r; +} + +template +int expose_var // { dg-warning "TU-local" } + = f(sizeof(T)); diff --git a/gcc/testsuite/g++.dg/modules/internal-7_b.C b/gcc/testsuite/g++.dg/modules/internal-7_b.C new file mode 100644 index 00000000000..2a11e449d6e --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-7_b.C @@ -0,0 +1,21 @@ +// { dg-additional-options "-fmodules-ts" } + +module M; + +void inst() { + expose_1(); // { dg-message "required from here" } + expose_2(); // { dg-message "required from here" } + expose_3(); // { dg-message "required from here" } + expose_4(); // { dg-message "required from here" } + expose_5(); // { dg-message "required from here" } + expose_6(); // { dg-message "required from here" } + expose_7(); // { dg-message "required from here" } + expose_8(); // { dg-message "required from here" } + expose_9(); // { dg-message "required from here" } + expose_10(); // { dg-message "required from here" } + expose_11(); // { dg-message "required from here" } + + expose_var; // { dg-message "required from here" } +} + +// { dg-error "instantiation exposes TU-local entity" "" { target *-*-* } 0 } From patchwork Fri Sep 27 05:58:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1990102 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=KgenHs45; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (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 4XFKZS10jRz1xt8 for ; Fri, 27 Sep 2024 16:00:32 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id ECF413858283 for ; Fri, 27 Sep 2024 06:00:28 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x1030.google.com (mail-pj1-x1030.google.com [IPv6:2607:f8b0:4864:20::1030]) by sourceware.org (Postfix) with ESMTPS id B27F43858D28 for ; Fri, 27 Sep 2024 05:58:53 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B27F43858D28 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 B27F43858D28 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1030 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416737; cv=none; b=g1oV86skLgiR1Yox9xaQ5Sp+OouoIp2NHIiglErNzAfSJVW7kSzSh3Y2dvGRLG/mJsWRzDt007JZsteYWTG8RDFl//kaCZZti8sRZzuXaQ6XDEMRqm/WicDO+QMxw2VJkVd/i5c4V/UsZL3Gjz/GH0NHBsBEXY4QgBsryH2lP7M= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416737; c=relaxed/simple; bh=f6bx7ZXdYPgbOxMdFcwUQ/XDyrBbV2wEwHHFJFs42L0=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=PbVw0jkS6qTCB/RN/0ZUhjeNqFNJ/mpWXVr4gNZSckGbm+notASMHmxzAfJBkMCtaL07mGWhqS0COPDiaM4JqwSA9BbwKVW0KZBGqc8+k+3TDdQKFC7+ADhdzlnqx/JYuszN3HyrU1TWrbz5z5lCVqKhakGJYnbowPS33+afHQE= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x1030.google.com with SMTP id 98e67ed59e1d1-2da516e6940so354793a91.3 for ; Thu, 26 Sep 2024 22:58:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727416733; x=1728021533; darn=gcc.gnu.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:from:to:cc:subject:date:message-id:reply-to; bh=NDYzWxxQmAC8vfJhDWj5ZMqUvBLM4QxCrBzOL2LMRYE=; b=KgenHs45LN72pAao1yiQRLZKSz5jTruDXbS2M5YYDPcF8qXHmuyczL+BhzVGHPp2BT GB7HjNh07MMjrv0U+MvILim5u7ILXiqes9zaT8i7rTBxLcNnA7IelUNG5nNld2w07vmN i0M61no8BWeKsO7OphbyCzGTuKoN27Plq1IpoAbAU1waUilw1SxPCm72fSz/QIVrAVSs 1HVG9ldMP2rk6TcacN8JsjPzav4payvNeOmMZqDtW/XYzO5GQ02eKh+jtkHofHFKP+Bm Ikvepvll9LQBdkqGCGjCX6+E9Aa4dqw1UFoEPOi52PQwm11+wmuSUQct9WwqWYW43Izu iJEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727416733; x=1728021533; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=NDYzWxxQmAC8vfJhDWj5ZMqUvBLM4QxCrBzOL2LMRYE=; b=jURwjXLY9cbiwUIYwihg+HbTyOzFn3z9LxMmTlFgMeBHCiYZt5WRHqcs0PeRYad1Tm BpR49tDubc+g1HhxldaP6LKeFP7nxTVf133lCSpGLxZSmyQNh20V24g3RoU53l1ccGIH EdRZ/7A3sytio/K6pFhErtLgzzJXpY5RFp0NFTi7YwBPxVBuguqkAFDLgSM7qd7/QWbr bgj8QeJ8Zbjy96Ye+Ew1v98AJD3LfV4E0lV8iggElSdhU8bqU9bG9Dn6LEcJEGs+uoEw l93IFbKZyBVNQuLq0h4vdPqv9JUJ2n7zK4VKksAHhbJBk93an0IpsRZbN7qGKwAwwbnA Lyrw== X-Gm-Message-State: AOJu0YyN5mXdYoc2tsdn1cA8H+HCsApU4j4YIJr2HzYvKCWWDwrVYwmQ vAT80GPVyLkHBGw/AbEDn3In1Vn830hPyZwMP4E7HcbaUI9lON318Y5yqA== X-Google-Smtp-Source: AGHT+IFpP8gOJ3oAnh1EiSepM03cNWrVuUBlmY8vO1O7qX0OwKSuSPZC7vIP+h00NB5UACMZuCGCYQ== X-Received: by 2002:a17:90a:d802:b0:2da:71f8:7ff with SMTP id 98e67ed59e1d1-2e0b8e841bfmr1073362a91.5.1727416732550; Thu, 26 Sep 2024 22:58:52 -0700 (PDT) Received: from Thaum. (163-47-68-2.ipv4.originbroadband.com.au. [163.47.68.2]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2e0b6c74071sm1104240a91.18.2024.09.26.22.58.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Sep 2024 22:58:52 -0700 (PDT) Message-ID: <66f6499c.170a0220.1abf25.5cf3@mx.google.com> X-Google-Original-Message-ID: Date: Fri, 27 Sep 2024 15:58:48 +1000 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v2 3/6] c++/modules: Support anonymous namespaces in header units References: <66f64939.050a0220.261230.4b66@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <66f64939.050a0220.261230.4b66@mx.google.com> X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.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~incoming=patchwork.ozlabs.org@gcc.gnu.org Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk? -- >8 -- A header unit may contain anonymous namespaces, and those declarations are exported (as with any declaration in a header unit). This patch ensures that such declarations are correctly handled. The change to 'make_namespace_finish' is required so that if an anonymous namespace is first seen by an import it is correctly handled within 'add_imported_namespace'. I don't see any particular reason why handling of anonymous namespaces here had to be handled separately outside that function since these are the only two callers. gcc/cp/ChangeLog: * module.cc (depset::hash::add_binding_entity): Also walk anonymous namespaces. (module_state::write_namespaces): Adjust assertion. * name-lookup.cc (push_namespace): Move anon using-directive handling to... (make_namespace_finish): ...here. gcc/testsuite/ChangeLog: * g++.dg/modules/internal-8_a.H: New test. * g++.dg/modules/internal-8_b.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/module.cc | 7 +++-- gcc/cp/name-lookup.cc | 8 +++--- gcc/testsuite/g++.dg/modules/internal-8_a.H | 28 ++++++++++++++++++++ gcc/testsuite/g++.dg/modules/internal-8_b.C | 29 +++++++++++++++++++++ 4 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/internal-8_a.H create mode 100644 gcc/testsuite/g++.dg/modules/internal-8_b.C diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 5d7f4941b2a..df407c1fd55 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -13723,15 +13723,15 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_) return (flags & WMB_Using ? flags & WMB_Export : DECL_MODULE_EXPORT_P (decl)); } - else if (DECL_NAME (decl) && !data->met_namespace) + else if (!data->met_namespace) { /* Namespace, walk exactly once. */ - gcc_checking_assert (TREE_PUBLIC (decl)); data->met_namespace = true; if (data->hash->add_namespace_entities (decl, data->partitions)) { /* It contains an exported thing, so it is exported. */ gcc_checking_assert (DECL_MODULE_PURVIEW_P (decl)); + gcc_checking_assert (TREE_PUBLIC (decl) || header_module_p ()); DECL_MODULE_EXPORT_P (decl) = true; } @@ -16126,8 +16126,7 @@ module_state::write_namespaces (elf_out *to, vec spaces, tree ns = b->get_entity (); gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL); - /* P1815 may have something to say about this. */ - gcc_checking_assert (TREE_PUBLIC (ns)); + gcc_checking_assert (TREE_PUBLIC (ns) || header_module_p ()); unsigned flags = 0; if (TREE_PUBLIC (ns)) diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index eb365b259d9..fe2eb2e0917 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -9098,6 +9098,9 @@ make_namespace_finish (tree ns, tree *slot, bool from_import = false) if (DECL_NAMESPACE_INLINE_P (ns) || !DECL_NAME (ns)) emit_debug_info_using_namespace (ctx, ns, true); + + if (!DECL_NAMESPACE_INLINE_P (ns) && !DECL_NAME (ns)) + add_using_namespace (NAMESPACE_LEVEL (ctx)->using_directives, ns); } /* Push into the scope of the NAME namespace. If NAME is NULL_TREE, @@ -9234,11 +9237,6 @@ push_namespace (tree name, bool make_inline) gcc_checking_assert (slot); } make_namespace_finish (ns, slot); - - /* Add the anon using-directive here, we don't do it in - make_namespace_finish. */ - if (!DECL_NAMESPACE_INLINE_P (ns) && !name) - add_using_namespace (current_binding_level->using_directives, ns); } } diff --git a/gcc/testsuite/g++.dg/modules/internal-8_a.H b/gcc/testsuite/g++.dg/modules/internal-8_a.H new file mode 100644 index 00000000000..57fe60bb3c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-8_a.H @@ -0,0 +1,28 @@ +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi {} } + +static int x = 123; +static void f() {} +template static void t() {} + +namespace { + int y = 456; + void g() {}; + template void u() {} + + namespace ns { int in_ns = 456; } + + struct A {}; + template struct B {}; + + enum E { X }; + enum class F { Y }; + + template using U = int; + +#if __cplusplus >= 202002L + template concept C = true; +#endif +} + +namespace ns2 = ns; diff --git a/gcc/testsuite/g++.dg/modules/internal-8_b.C b/gcc/testsuite/g++.dg/modules/internal-8_b.C new file mode 100644 index 00000000000..a2d74a87473 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-8_b.C @@ -0,0 +1,29 @@ +// { dg-additional-options "-fmodules-ts" } + +import "internal-8_a.H"; + +int main() { + auto x2 = x; + f(); + t(); + + auto y2 = y; + g(); + u(); + + int val1 = ns::in_ns; + + A a; + B b; + + E e = X; + F f = F::Y; + + U temp; + +#if __cplusplus >= 202002L + static_assert(C); +#endif + + int val2 = ns2::in_ns; +} From patchwork Fri Sep 27 05:59:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1990101 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=fMp561Vs; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (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 4XFKYj5rPvz1xt8 for ; Fri, 27 Sep 2024 15:59:53 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B184B3858C53 for ; Fri, 27 Sep 2024 05:59:51 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x1034.google.com (mail-pj1-x1034.google.com [IPv6:2607:f8b0:4864:20::1034]) by sourceware.org (Postfix) with ESMTPS id 49B083858C33 for ; Fri, 27 Sep 2024 05:59:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 49B083858C33 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 49B083858C33 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1034 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416760; cv=none; b=S3/HPeDfaYLbZOCAjDP0CLUUjdIyPxSJbXmVdpvSBiOlrx90f+UrB7FKyXoCdjojc+AWqp59VrqmfpzHfjPX/nxIB3y1k9glk22yqX7NZJwnPHQnmM3s24TKY0VfxHx2cWtv+BHsWb8qSjwLewog85JMAPaiPZqlYwZEoAosxj0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416760; c=relaxed/simple; bh=w2dq+KkNZb4MsJUtYRmbSxKKihKS4/pIRRYOpnFMjrA=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=FAsSQzr21vj69FuWgV2iXgfUNGgr+vd2SWWnwRG9K45xNpqqmpS+WH/+aCQ8IaOjKkyYHMn/Cltu0JRbQw9NmsrNBrr2qVxmkXE/6lmFryfzDc0pWA+IGefktmVUI0s5Siwpt0sbK+vD/WuEax+dYrLNU7CLAWX6T2TMF1DeIlE= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x1034.google.com with SMTP id 98e67ed59e1d1-2db6e80555dso327529a91.0 for ; Thu, 26 Sep 2024 22:59:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727416756; x=1728021556; darn=gcc.gnu.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:from:to:cc:subject:date:message-id:reply-to; bh=QCtrMe2elLt55zC0BDC/BEIF4Use+wgXtMA9SGdEmKw=; b=fMp561VsKEWxYmwRCG9Qrj2cBdXfxwZbn0lUdBim4lpNY+n+WFyZ2+OR/0MHGhpGdi MMATQwJ7aRpl/IvH31vroFSzQrMHa1YP9WEo8zC4NYx9lYTcAxjQD6Do4s0xZzPTsaAx jhgJeJhU7TdZhHcT8gktqM02iYQQkPLlg9MdZNXwK0ljvW0aWA8JBwKg9qsbO0dwGO9Z nrSycKToyIp9vyMOxXyZjVaN3WSWiMXPWiZS/OCCWbs7oofLQIOn+5FYHL/ncin7NA3i qsVg44mBmblYx0SZl/qzmwnW98Ve81SIwQw3eCenKa+jsek5j8vwrrlDujTV7ldvads1 4lrw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727416756; x=1728021556; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=QCtrMe2elLt55zC0BDC/BEIF4Use+wgXtMA9SGdEmKw=; b=TfH3b+wJwvvDV1QHJpvHcR9i3j75tF+g8507NT9QbIJQodWGIBcuRm9o342rnE7IxY PMQ836yjcMKhCPjCDUz12exFcd2liRBYe9do2KJ+HUjuhiH4bliF6UHsIIA9mjnkhSJN YGIPevMOXRudvShzlPAfnHEMOEp+o2zpF4eMmC1ZJrzLAr+WJA8e9BBkIXPNA40etIOJ E5kwoIB9+J31RHaIrjzEIwgsafjCsB9RqpMZ+eI2pGXZ2Mar2GPXLsn4a2AcJ/BVw7rz 8F0Qv+IzWUGjqyrtfQOtdx9GVlodTYfhlpm7yNJy9mITJSUKI6XAGwYZdems5DFbyPjD c3kQ== X-Gm-Message-State: AOJu0YxJbvkyHkmNvKJk7CO9ZPIJe3iN0TRw0dguErZnQCflBfmSpK/M ELXRPvxYyjeLf93Dpaiszpvtikj7pY2+MyCUMByWkP2gXBpM0tqvk+SleQ== X-Google-Smtp-Source: AGHT+IF+/ds16OdBu7CW8WEiu3PZCzOG7DyhF/7Lw4p9jIvmlxd0vsdcYUX4b0RuQmrKvTkgHXz66Q== X-Received: by 2002:a17:90b:3586:b0:2db:60b:eec with SMTP id 98e67ed59e1d1-2e0b8ea0d68mr1035570a91.7.1727416756128; Thu, 26 Sep 2024 22:59:16 -0700 (PDT) Received: from Thaum. (163-47-68-2.ipv4.originbroadband.com.au. [163.47.68.2]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2e06e2d7001sm4599333a91.44.2024.09.26.22.59.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Sep 2024 22:59:15 -0700 (PDT) Message-ID: <66f649b3.170a0220.269c48.26e1@mx.google.com> X-Google-Original-Message-ID: Date: Fri, 27 Sep 2024 15:59:11 +1000 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v2 4/6] c++/modules: Check linkage for exported declarations References: <66f64939.050a0220.261230.4b66@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <66f64939.050a0220.261230.4b66@mx.google.com> X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.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~incoming=patchwork.ozlabs.org@gcc.gnu.org Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk? -- >8 -- By [module.interface] p3, if an exported declaration is not within a header unit, it shall not declare a name with internal linkage. Unfortunately we cannot just do this within set_originating_module, since at the locations its called the linkage for declarations are not always fully determined yet. We could move the calls but this causes the checking assertion to fail as the originating module declaration may have moved, and in general for some kinds of declarations it's not always obvious where it should be moved to. This patch instead introduces a new function to check that the linkage of a declaration within a module is correct, to be called for all declarations once their linkage is fully determined. As a drive-by fix this patch also improves the source location of namespace aliases to point at the identifier rather than the terminating semicolon. gcc/cp/ChangeLog: * cp-tree.h (check_module_decl_linkage): Declare. * decl2.cc (finish_static_data_member_decl): Check linkage. * module.cc (set_originating_module): Adjust comment. (check_module_decl_linkage): New function. * name-lookup.cc (do_namespace_alias): Build alias with specified location, check linkage. (pushtag): Check linkage. (push_namespace): Slightly clarify error message. * name-lookup.h (do_namespace_alias): Add location parameter. * parser.cc (cp_parser_namespace_alias_definition): Pass identifier location to do_namespace_alias. (cp_parser_alias_declaration): Check linkage. (cp_parser_init_declarator): Check linkage. (cp_parser_function_definition_after_declarator): Check linkage. (cp_parser_save_member_function_body): Check linkage. * pt.cc (finish_concept_definition): Mark as public, check linkage. libcc1/ChangeLog: * libcp1plugin.cc (plugin_add_namespace_alias): Call do_namespace_alias with input_location. gcc/testsuite/ChangeLog: * g++.dg/modules/export-3.C: Adjust error message. * g++.dg/modules/export-6.C: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/cp-tree.h | 1 + gcc/cp/decl2.cc | 1 + gcc/cp/module.cc | 29 +++++++++++++++++--- gcc/cp/name-lookup.cc | 15 ++++++++--- gcc/cp/name-lookup.h | 2 +- gcc/cp/parser.cc | 9 ++++++- gcc/cp/pt.cc | 2 ++ gcc/testsuite/g++.dg/modules/export-3.C | 2 +- gcc/testsuite/g++.dg/modules/export-6.C | 35 +++++++++++++++++++++++++ libcc1/libcp1plugin.cc | 2 +- 10 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/export-6.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 48a99f116b0..5c142fa51c6 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7455,6 +7455,7 @@ extern void set_originating_module (tree, bool friend_p = false); extern tree get_originating_module_decl (tree) ATTRIBUTE_PURE; extern int get_originating_module (tree, bool for_mangle = false) ATTRIBUTE_PURE; extern unsigned get_importing_module (tree, bool = false) ATTRIBUTE_PURE; +extern void check_module_decl_linkage (tree); /* Where current instance of the decl got declared/defined/instantiated. */ extern void set_instantiating_module (tree); diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 0279372488c..97ce4473b1c 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -1019,6 +1019,7 @@ finish_static_data_member_decl (tree decl, } cp_finish_decl (decl, init, init_const_expr_p, asmspec_tree, flags); + check_module_decl_linkage (decl); } /* DECLARATOR and DECLSPECS correspond to a class member. The other diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index df407c1fd55..a4343044d1a 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -19926,11 +19926,34 @@ set_originating_module (tree decl, bool friend_p ATTRIBUTE_UNUSED) DECL_MODULE_ATTACH_P (decl) = true; } - if (!module_exporting_p ()) + /* It is illegal to export a declaration with internal linkage. However, at + the point this function is called we don't always know yet whether this + declaration has internal linkage; instead we defer this check for callers + to do once visibility has been determined. */ + if (module_exporting_p ()) + DECL_MODULE_EXPORT_P (decl) = true; +} + +/* Checks whether DECL within a module unit has valid linkage for its kind. + Must be called after visibility for DECL has been finalised. */ + +void +check_module_decl_linkage (tree decl) +{ + if (!module_has_cmi_p ()) return; - // FIXME: Check ill-formed linkage - DECL_MODULE_EXPORT_P (decl) = true; + /* An internal-linkage declaration cannot be generally be exported. + But it's OK to export any declaration from a header unit, including + internal linkage declarations. */ + if (!header_module_p () + && DECL_MODULE_EXPORT_P (decl) + && decl_linkage (decl) == lk_internal) + { + error_at (DECL_SOURCE_LOCATION (decl), + "exporting declaration %qD with internal linkage", decl); + DECL_MODULE_EXPORT_P (decl) = false; + } } /* DECL is keyed to CTX for odr purposes. */ diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index fe2eb2e0917..9fabab82f42 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -6598,7 +6598,7 @@ pop_decl_namespace (void) /* Process a namespace-alias declaration. */ void -do_namespace_alias (tree alias, tree name_space) +do_namespace_alias (location_t loc, tree alias, tree name_space) { if (name_space == error_mark_node) return; @@ -6608,12 +6608,13 @@ do_namespace_alias (tree alias, tree name_space) name_space = ORIGINAL_NAMESPACE (name_space); /* Build the alias. */ - alias = build_lang_decl (NAMESPACE_DECL, alias, void_type_node); + alias = build_lang_decl_loc (loc, NAMESPACE_DECL, alias, void_type_node); DECL_NAMESPACE_ALIAS (alias) = name_space; DECL_EXTERNAL (alias) = 1; DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ()); TREE_PUBLIC (alias) = TREE_PUBLIC (DECL_CONTEXT (alias)); set_originating_module (alias); + check_module_decl_linkage (alias); pushdecl (alias); @@ -8546,6 +8547,7 @@ pushtag (tree name, tree type, TAG_how how) /* Set type visibility now if this is a forward declaration. */ TREE_PUBLIC (decl) = 1; determine_visibility (decl); + check_module_decl_linkage (decl); return type; } @@ -9249,8 +9251,13 @@ push_namespace (tree name, bool make_inline) if (TREE_PUBLIC (ns)) DECL_MODULE_EXPORT_P (ns) = true; else if (!header_module_p ()) - error_at (input_location, - "exporting namespace with internal linkage"); + { + if (name) + error_at (input_location, + "exporting namespace %qD with internal linkage", ns); + else + error_at (input_location, "exporting anonymous namespace"); + } } if (module_purview_p ()) DECL_MODULE_PURVIEW_P (ns) = true; diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index f39f5309150..26b3b1ffc62 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -444,7 +444,7 @@ extern tree cp_namespace_decls (tree); extern void set_decl_namespace (tree, tree, bool); extern void push_decl_namespace (tree); extern void pop_decl_namespace (void); -extern void do_namespace_alias (tree, tree); +extern void do_namespace_alias (location_t, tree, tree); extern tree do_class_using_decl (tree, tree); extern tree lookup_arg_dependent (tree, tree, vec *); extern tree search_anon_aggr (tree, tree, bool = false); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 6519d9e9061..63f5ebb5e77 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -22365,6 +22365,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser) /* Look for the `namespace' keyword. */ cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE); /* Look for the identifier. */ + location_t id_location = cp_lexer_peek_token (parser->lexer)->location; identifier = cp_parser_identifier (parser); if (identifier == error_mark_node) return; @@ -22388,7 +22389,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser) cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); /* Register the alias in the symbol table. */ - do_namespace_alias (identifier, namespace_specifier); + do_namespace_alias (id_location, identifier, namespace_specifier); } /* Parse a qualified-namespace-specifier. @@ -22837,6 +22838,8 @@ cp_parser_alias_declaration (cp_parser* parser) check_member_template (decl); } + check_module_decl_linkage (decl); + return decl; } @@ -23894,6 +23897,7 @@ cp_parser_init_declarator (cp_parser* parser, `explicit' constructor cannot be used. */ ((is_direct_init || !is_initialized) ? LOOKUP_NORMAL : LOOKUP_IMPLICIT)); + check_module_decl_linkage (decl); } else if ((cxx_dialect != cxx98) && friend_p && decl && TREE_CODE (decl) == FUNCTION_DECL) @@ -33140,6 +33144,7 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, /* Finish the function. */ fn = finish_function (inline_p); + check_module_decl_linkage (fn); if (modules_p () && !inline_p @@ -33794,6 +33799,8 @@ cp_parser_save_member_function_body (cp_parser* parser, /* Add FN to the queue of functions to be parsed later. */ vec_safe_push (unparsed_funs_with_definitions, fn); + check_module_decl_linkage (fn); + return fn; } diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index d60837e7bb9..f158326842e 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -29724,11 +29724,13 @@ finish_concept_definition (cp_expr id, tree init, tree attrs) tree decl = build_lang_decl_loc (loc, CONCEPT_DECL, *id, boolean_type_node); DECL_CONTEXT (decl) = current_scope (); DECL_INITIAL (decl) = init; + TREE_PUBLIC (decl) = true; if (attrs) cplus_decl_attributes (&decl, attrs, 0); set_originating_module (decl, false); + check_module_decl_linkage (decl); /* Push the enclosing template. */ return push_template_decl (decl); diff --git a/gcc/testsuite/g++.dg/modules/export-3.C b/gcc/testsuite/g++.dg/modules/export-3.C index 6af314b9519..cdb8e9c0841 100644 --- a/gcc/testsuite/g++.dg/modules/export-3.C +++ b/gcc/testsuite/g++.dg/modules/export-3.C @@ -25,4 +25,4 @@ namespace { export namespace ns {} // { dg-error "internal linkage" } } -export namespace {} // { dg-error "internal linkage" } +export namespace {} // { dg-error "exporting anonymous namespace" } diff --git a/gcc/testsuite/g++.dg/modules/export-6.C b/gcc/testsuite/g++.dg/modules/export-6.C new file mode 100644 index 00000000000..a2cc86a7b8e --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/export-6.C @@ -0,0 +1,35 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi !bad } + +export module bad; + +export static int x = 123; // { dg-error "internal linkage" } +export static void f(); // { dg-error "internal linkage" } +export static void g() {} // { dg-error "internal linkage" } +export template static void t(); // { dg-error "internal linkage" } +export template static void u() {} // { dg-error "internal linkage" } + +namespace { + export int y = 456; // { dg-error "internal linkage" } + export void h(); // { dg-error "internal linkage" } + export void i() {} // { dg-error "internal linkage" } + export template void v(); // { dg-error "internal linkage" } + export template void w() {} // { dg-error "internal linkage" } + + export namespace ns {} // { dg-error "internal linkage" } + + export struct A {}; // { dg-error "internal linkage" } + export template struct B {}; // { dg-error "internal linkage" } + + export enum E {}; // { dg-error "internal linkage" } + export enum class F {}; // { dg-error "internal linkage" } + + export template using U = int; // { dg-error "internal linkage" } + +#if __cplusplus >= 202002L + export template concept C = true; // { dg-error "internal linkage" "" { target c++20 } } +#endif +} + +export namespace {} // { dg-error "exporting anonymous namespace" } +export namespace ns2 = ns; // { dg-error "internal linkage" } diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc index da68c5d0ac1..97877ad9138 100644 --- a/libcc1/libcp1plugin.cc +++ b/libcc1/libcp1plugin.cc @@ -799,7 +799,7 @@ plugin_add_namespace_alias (cc1_plugin::connection *, tree name = get_identifier (id); tree target = convert_in (target_in); - do_namespace_alias (name, target); + do_namespace_alias (input_location, name, target); return 1; } From patchwork Fri Sep 27 05:59:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1990104 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=G+Y+q+Cj; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (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 4XFKZv1FQbz1xt8 for ; Fri, 27 Sep 2024 16:00:55 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0423A385DDE1 for ; Fri, 27 Sep 2024 06:00:53 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x1031.google.com (mail-pj1-x1031.google.com [IPv6:2607:f8b0:4864:20::1031]) by sourceware.org (Postfix) with ESMTPS id A68B5385840F for ; Fri, 27 Sep 2024 05:59:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A68B5385840F 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 A68B5385840F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::1031 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416790; cv=none; b=sRY1/6Hr7wLC1SLRByfJFo71y6QSLYKIHCvxln9XU1T7u3QPIF2YMDDW1EL6i9PUHLY9vJppWfEgV8Rc93M4hu4pr8cE9ykqT4s0aIniKESb65MO/aX4ts14nt/7AFad7EePvmvwMz2rXwvD5hCrSwG+Nm7WNwJxGLExBpliAzo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416790; c=relaxed/simple; bh=IJkuhhANKgM4B9N0BImIse7LxhrOPOILNa4k12xMaMc=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=bn8ufj50akpJTfBf88NuxqczDGZX6ZuB6ev/GXNqK2xFLBsAATrkWoMVl8VB1W8Dcr8P0usGEb3x/c7D51n9xGWvSlLx6an1ul+4sIlc4dYbdjINYvUW6Ur2dgdmd3YtESDz+WgixyF/xBJ4blNMVPlIRcWZneWMvMpPza8n88w= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x1031.google.com with SMTP id 98e67ed59e1d1-2e0b02ed295so186536a91.2 for ; Thu, 26 Sep 2024 22:59:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727416786; x=1728021586; darn=gcc.gnu.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:from:to:cc:subject:date:message-id:reply-to; bh=aambrW3gmnmWZIjUF/wzTN3+gy/dfiO5jGetCRvgwzI=; b=G+Y+q+Cjyu5K33acBdYC78Dx0fw78qtQqTzV23kvyQpYfjK+UzAIT2ERfD3GEmaImJ AuJN4PwFYKj0cCkBSumkMhYKh7G3LZVeIxgMF0UQrz60D9/BrsXsDn+TlG/xoLrQ3j6u 4w1F6vQ9s+Em8yoj98xkTs6b9quh4jdrBaxwnQ5JTeTdKshCIt5msizyTbGqw6gaoT22 4u1lYOtv8yxu4hS+QZmwScBP91ivKt1Jf73kQ4DmLuXZ1C0kAgjLOCw8N40pLy0Zl46y 6fZ+U8Ky8BRN1nQngeij6pH9QOLT7vJlxKD9lB5eA0fLvujyy0jc0D1gHWde8sruNH7K EEjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727416786; x=1728021586; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=aambrW3gmnmWZIjUF/wzTN3+gy/dfiO5jGetCRvgwzI=; b=AIvs1Tbnz5jw7y4eT+IzKs7Y+Jmd/tIsOsv9PvZPuvmg1YvvPm99pl695bd4gd5FAI 2eflgjt07inYWcVKKZlgFeDHY22BiZsoBZt+i+nZXXn0kHsXtS9IVlnbLYI4oUoNP5kR CMtF2WvirqNtKBGGV+XTYVgjfhXUQY6hAyri4FHiU/NjF5Zhob3rPa1xCI09oB9uxxqS xl1LgU/Dl4UllVPmlocyEm6RfTSuJlJHSDtrjpULZQQfZI0YW/BWoazoPlaqGKgd2gJB e6U3L7IBgveunORDNUNcPiTJpdl66w1WRDfqTYhkLK2qFPWNi8ZtHQ/vDJTjroIWUAxt 2WWQ== X-Gm-Message-State: AOJu0YxzmHc9lQQaYedcxX5PGbUbZho/0JUt/rUe/P1cnQon2+9YuG3P XEPkQmicbowvWe3mmlOoX5jhiBAQWK5Y5cwTE3mb9gO23a/k1zqm0CcRaA== X-Google-Smtp-Source: AGHT+IHaRKcBa9AjjwHkudvc5xOwtm82dSvWN9XBar2CNGfhDPAnw3O6iJ8Esk4hrb7R+snA7PHhjQ== X-Received: by 2002:a05:6a00:39a8:b0:710:5243:4161 with SMTP id d2e1a72fcca58-71b26079b75mr1442628b3a.5.1727416786427; Thu, 26 Sep 2024 22:59:46 -0700 (PDT) Received: from Thaum. ([163.47.68.2]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-71b264b63d9sm834110b3a.52.2024.09.26.22.59.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Sep 2024 22:59:45 -0700 (PDT) Message-ID: <66f649d1.050a0220.ea27b.491a@mx.google.com> X-Google-Original-Message-ID: Date: Fri, 27 Sep 2024 15:59:41 +1000 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v2 5/6] c++/modules: Validate external linkage definitions in header units [PR116401] References: <66f64939.050a0220.261230.4b66@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <66f64939.050a0220.261230.4b66@mx.google.com> X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.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~incoming=patchwork.ozlabs.org@gcc.gnu.org Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk? -- >8 -- [module.import] p6 says "A header unit shall not contain a definition of a non-inline function or variable whose name has external linkage." This patch implements this requirement, and cleans up some issues in the testsuite where this was already violated. To handle deduction guides we mark them as inline, since although we give them a definition for implementation reasons, by the standard they have no definition, and so we should not error in this case. PR c++/116401 gcc/cp/ChangeLog: * decl.cc (grokfndecl): Mark deduction guides as 'inline'. * module.cc (check_module_decl_linkage): Implement checks for non-inline external linkage definitions in headers. gcc/testsuite/ChangeLog: * g++.dg/modules/macro-4_c.H: Add missing 'inline'. * g++.dg/modules/pr106761.h: Likewise. * g++.dg/modules/pr98843_b.H: Likewise. * g++.dg/modules/pr99468.H: Likewise. * g++.dg/modules/pragma-1_a.H: Likewise. * g++.dg/modules/tpl-ary-1.h: Likewise. * g++.dg/modules/hdr-2.H: New test. Signed-off-by: Nathaniel Shead --- gcc/cp/decl.cc | 1 + gcc/cp/module.cc | 18 +++ gcc/testsuite/g++.dg/modules/hdr-2.H | 172 ++++++++++++++++++++++ gcc/testsuite/g++.dg/modules/macro-4_c.H | 2 +- gcc/testsuite/g++.dg/modules/pr106761.h | 2 +- gcc/testsuite/g++.dg/modules/pr98843_b.H | 2 +- gcc/testsuite/g++.dg/modules/pr99468.H | 2 +- gcc/testsuite/g++.dg/modules/pragma-1_a.H | 2 +- gcc/testsuite/g++.dg/modules/tpl-ary-1.h | 2 +- 9 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/hdr-2.H diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 6a7ba416cf8..5ddb7eafa50 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10823,6 +10823,7 @@ grokfndecl (tree ctype, have one: the restriction that you can't repeat a deduction guide makes them more like a definition anyway. */ DECL_INITIAL (decl) = void_node; + DECL_DECLARED_INLINE_P (decl) = true; break; default: break; diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index a4343044d1a..b4030c62eec 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -19943,6 +19943,24 @@ check_module_decl_linkage (tree decl) if (!module_has_cmi_p ()) return; + /* A header unit shall not contain a definition of a non-inline function + or variable (not template) whose name has external linkage. */ + if (header_module_p () + && !processing_template_decl + && ((TREE_CODE (decl) == FUNCTION_DECL + && !DECL_DECLARED_INLINE_P (decl) + && DECL_INITIAL (decl)) + || (TREE_CODE (decl) == VAR_DECL + && !DECL_INLINE_VAR_P (decl) + && DECL_INITIALIZED_P (decl) + && !DECL_IN_AGGR_P (decl))) + && !(DECL_LANG_SPECIFIC (decl) + && DECL_TEMPLATE_INSTANTIATION (decl)) + && decl_linkage (decl) == lk_external) + error_at (DECL_SOURCE_LOCATION (decl), + "external linkage definition of %qD in header module must " + "be declared %", decl); + /* An internal-linkage declaration cannot be generally be exported. But it's OK to export any declaration from a header unit, including internal linkage declarations. */ diff --git a/gcc/testsuite/g++.dg/modules/hdr-2.H b/gcc/testsuite/g++.dg/modules/hdr-2.H new file mode 100644 index 00000000000..097546d5667 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/hdr-2.H @@ -0,0 +1,172 @@ +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi !{} } +// external linkage variables or functions in header units must +// not have non-inline definitions + +int x_err; // { dg-error "external linkage definition" } +int y_err = 123; // { dg-error "external linkage definition" } +void f_err() {} // { dg-error "external linkage definition" } + +struct Err { + Err(); + void m(); + static void s(); + static int x; + static int y; +}; +Err::Err() = default; // { dg-error "external linkage definition" } +void Err::m() {} // { dg-error "external linkage definition" } +void Err::s() {} // { dg-error "external linkage definition" } +int Err::x; // { dg-error "external linkage definition" } +int Err::y = 123; // { dg-error "external linkage definition" } + +// No definition, OK +extern int y_decl; +void f_decl(); + +template struct DeductionGuide {}; +DeductionGuide() -> DeductionGuide; + +struct NoDefStatics { + enum E { V }; + static const int x = 123; + static const E e = V; +}; + +// But these have definitions again (though the error locations aren't great) +struct YesDefStatics { + enum E { V }; + static const int x = 123; // { dg-error "external linkage definition" } + static const E e = V; // { dg-error "external linkage definition" } +}; +const int YesDefStatics::x; +const YesDefStatics::E YesDefStatics::e; + +// Inline decls are OK +inline int x_inl; +inline int y_inl = 123; +inline void f_inl() {} +constexpr void g_inl() {} +void h_inl() = delete; + +struct Inl { + void m() {} + static void s() {} + static inline int x; + static inline int y = 123; +}; + +// Internal linkage decls are OK +static int x_internal; +static int y_internal = 123; +static void f_internal() {} + +namespace { + struct Internal { + void m(); + static void s(); + static int x; + static int y; + }; + void Internal::m() {} + void Internal::s() {} + int Internal::x; + int Internal::y = 123; +} + +// Function-scope entities are OK +inline void f_static() { + static int x_static; + static int y_static = 123; + thread_local int x_thread_local; + thread_local int y_thread_local = 123; + + x_static = y_static; + x_thread_local = y_thread_local; +} + +// Templates (not functions or variables) are OK +template int x_tpl; +template int y_tpl = 123; +template void f_tpl() {} + +struct Template_Body { + template void m(); + template static void s(); + template static int x; + template static int y; +}; +template void Template_Body::m() {} +template void Template_Body::s() {} +template int Template_Body::x; +template int Template_Body::y = 123; + +template struct Template_Type { + void m(); + static void s(); + static int x; + static int y; +}; +template void Template_Type::m() {} +template void Template_Type::s() {} +template int Template_Type::x; +template int Template_Type::y = 123; + +// Implicit instantiations are OK +inline void instantiate_tmpls() { + x_tpl = y_tpl; + f_tpl(); + + Template_Body{}.m(); + Template_Body::s(); + Template_Body::x = Template_Body::y; + + using TT = Template_Type; + TT{}.m(); + TT::s(); + TT::x = TT::y; +} + +// Explicit instantiations are also OK (extern or otherwise) +template int x_tpl; +template int y_tpl; +template void f_tpl(); + +template void Template_Body::m(); +template void Template_Body::s(); +template int Template_Body::x; +template int Template_Body::y; + +template void Template_Type::m(); +template void Template_Type::s(); +template int Template_Type::x; +template int Template_Type::y; + +extern template int x_tpl; +extern template int y_tpl; +extern template void f_tpl(); + +extern template void Template_Body::m(); +extern template void Template_Body::s(); +extern template int Template_Body::x; +extern template int Template_Body::y; + +extern template void Template_Type::m(); +extern template void Template_Type::s(); +extern template int Template_Type::x; +extern template int Template_Type::y; + +// But explicit specialisations are not (though note [temp.expl.spec] p13) +template <> int x_tpl; // { dg-error "inline" } +template <> int y_tpl = 123; // { dg-error "inline" } +template <> void f_tpl() {} // { dg-error "inline" } + +template <> void Template_Body::m() {} // { dg-error "inline" } +template <> void Template_Body::s() {} // { dg-error "inline" } +template <> int Template_Body::x; // { dg-bogus "inline" "not a definition" } +template <> int Template_Body::y = 123; // { dg-error "inline" } + +template <> void Template_Type::m() {} // { dg-error "inline" } +template <> void Template_Type::s() {} // { dg-error "inline" } +template <> int Template_Type::x; // { dg-bogus "inline" "not a definition" } +template <> int Template_Type::y = 123; // { dg-error "inline" } diff --git a/gcc/testsuite/g++.dg/modules/macro-4_c.H b/gcc/testsuite/g++.dg/modules/macro-4_c.H index ec2bed91ccd..5692e8e41d2 100644 --- a/gcc/testsuite/g++.dg/modules/macro-4_c.H +++ b/gcc/testsuite/g++.dg/modules/macro-4_c.H @@ -6,7 +6,7 @@ #undef FIVE // no effect import "macro-4_a.H"; -int a; +inline int a; #undef THREE #undef FOUR #define FOUR 4c diff --git a/gcc/testsuite/g++.dg/modules/pr106761.h b/gcc/testsuite/g++.dg/modules/pr106761.h index 9f22a22a45d..5c13fc0f118 100644 --- a/gcc/testsuite/g++.dg/modules/pr106761.h +++ b/gcc/testsuite/g++.dg/modules/pr106761.h @@ -19,4 +19,4 @@ struct tuple { = typename _TupleConstraints::template __constructible; }; -tuple t; +inline tuple t; diff --git a/gcc/testsuite/g++.dg/modules/pr98843_b.H b/gcc/testsuite/g++.dg/modules/pr98843_b.H index d6734bd382d..af504a840c7 100644 --- a/gcc/testsuite/g++.dg/modules/pr98843_b.H +++ b/gcc/testsuite/g++.dg/modules/pr98843_b.H @@ -6,7 +6,7 @@ template int Fn () return I; } -template<> int Fn<1> () +template<> inline int Fn<1> () { return 0; } diff --git a/gcc/testsuite/g++.dg/modules/pr99468.H b/gcc/testsuite/g++.dg/modules/pr99468.H index d7da3a83e1c..df63c613b2c 100644 --- a/gcc/testsuite/g++.dg/modules/pr99468.H +++ b/gcc/testsuite/g++.dg/modules/pr99468.H @@ -3,4 +3,4 @@ module M; // { dg-error "module-declaration not permitted" } -int i; +inline int i; diff --git a/gcc/testsuite/g++.dg/modules/pragma-1_a.H b/gcc/testsuite/g++.dg/modules/pragma-1_a.H index 6eb0a5900a7..a69be2fd05e 100644 --- a/gcc/testsuite/g++.dg/modules/pragma-1_a.H +++ b/gcc/testsuite/g++.dg/modules/pragma-1_a.H @@ -1,4 +1,4 @@ // { dg-additional-options -fmodule-header } // { dg-module-cmi {} } -int i; +inline int i; diff --git a/gcc/testsuite/g++.dg/modules/tpl-ary-1.h b/gcc/testsuite/g++.dg/modules/tpl-ary-1.h index 2f745afba36..2f9a8106412 100644 --- a/gcc/testsuite/g++.dg/modules/tpl-ary-1.h +++ b/gcc/testsuite/g++.dg/modules/tpl-ary-1.h @@ -1,5 +1,5 @@ -int ary[4]; +inline int ary[4]; extern int unb[]; typedef int z[0]; From patchwork Fri Sep 27 06:00:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1990103 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=ZJ5NF8dr; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (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 4XFKZb1PYtz1xt8 for ; Fri, 27 Sep 2024 16:00:39 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6435B385C6CB for ; Fri, 27 Sep 2024 06:00:37 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pj1-x102f.google.com (mail-pj1-x102f.google.com [IPv6:2607:f8b0:4864:20::102f]) by sourceware.org (Postfix) with ESMTPS id A2515385843B for ; Fri, 27 Sep 2024 06:00:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A2515385843B 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 A2515385843B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::102f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416817; cv=none; b=OLaP706qBu3jfNNr2XtuOaapVrgLcTp9yLliOD12mY4X2v+9mfRxTEykdA26HAhpAB4bxNsEc3ZVUNRSF+e6IaRqzhRfzzfWXau4nwXznlbh9b8d7xFZwwVQ9XeedGUbALFem3BTKWgvaqnrp7mHmBIq5KVIzLlH5Kmrb6hynpo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727416817; c=relaxed/simple; bh=wThSclN4GL+RXMfDXWvOw3h65zo1wHniMZsjH+DAvuc=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=CMvAr7Zk0z22tsD0qV67BcoOWfQvRAQ3H4lbt7owwG7CJdhZukJukHqSNb0QKoVxR4+bvayQNEGYJStC2fOW0J/rhSze59DEs2layIAmwealo5yqyzQXME9YEavlq4A8VTXTq+TyEUTo9vMFcjx5lipHrsFJrZQbkY5Ntarvol8= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x102f.google.com with SMTP id 98e67ed59e1d1-2da516e6940so355014a91.3 for ; Thu, 26 Sep 2024 23:00:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727416807; x=1728021607; darn=gcc.gnu.org; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:from:to:cc:subject:date:message-id:reply-to; bh=bCOwE9PfBRR7GIvCKBHdJEv8kx5xPZOOHJ+dbQFVlHU=; b=ZJ5NF8drVBRL1A+Gn+va+qM9vL9JH71+G3CMudDyUsiEbehsbp9t4ygYngmkpDhH3u Wr6KPhHtgGu9a8OGIoa9wByTU6TJZdhuL9RcXeShqtE4WhvZvX7vwIYhI+BBGT3LSp2c AdfqFeUSJRM+K1o5n2/9QQo2+iMauQJzeRbu4Tn4hKNMw72WIQmkYMXU2069hvVauuhy T0tOxDz1VL4ZSPxD5NMqQSCHhtESiYeGbqTj1WdG5IaMVhYrkBXXMADxvWSTkNBYf9kz 01c25pbEKxz2QR3sTifIf/xLtMg86Sexy7iCdcPUlUYNMciVqICvBt0gaTWYI4Dr9BJi yl2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727416807; x=1728021607; h=in-reply-to:content-disposition:mime-version:references:subject:cc :to:from:date:message-id:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=bCOwE9PfBRR7GIvCKBHdJEv8kx5xPZOOHJ+dbQFVlHU=; b=bRBxHyUyz0j6hTl3ugOsbmFwlqKq66vBmXa/39SHzzI2KXjzeVsX4ObcyMmqPrQ+41 12BkjGOOQBLQmaBGve90CdALbD6xCUfjGwMWhqtLFDG9zwM2egCUAmL98DhhtbyQ0qUC eYCYIZgfIMkGhbUzwSaseZIyl0rXG8v+E6gvywzDvXG30OVQLmCZYlWYMrCVNIkFLbJb FXSV5qJUHMeRboqCVi8krncApUoqajC1V10TsUw5emXv60wVyhBLmsZ1c12CfDJRy3UC pFlxRbLKV7PTwWy/wpIIwzgTN3rNuTYmUCelJ9FLSofIbUDAi/p/+v9MJjAzrwF2eT50 A0IA== X-Gm-Message-State: AOJu0YyC3X1tQWYY6Uh/R5Ng4GVohx66uIQwjbKyIlp3PnEY4lswVIDu qo4cJU8CyyK7BCXDKihgluOt0QK3UArpdLBEuWnT4EMSJJYxXHSSStv+Kg== X-Google-Smtp-Source: AGHT+IGTvLB8KRcex9zZew1BLQBPUNgARwqpQtsPxWNqZTXhtUAjZ1oYdAJ68jNOxfULazkaDTuQSA== X-Received: by 2002:a17:902:d2c3:b0:205:76f3:fc13 with SMTP id d9443c01a7336-20b36ad9b61mr14114915ad.7.1727416806093; Thu, 26 Sep 2024 23:00:06 -0700 (PDT) Received: from Thaum. (163-47-68-2.ipv4.originbroadband.com.au. [163.47.68.2]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-20b37d913c4sm7004585ad.88.2024.09.26.23.00.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 26 Sep 2024 23:00:05 -0700 (PDT) Message-ID: <66f649e5.170a0220.1308b0.3e86@mx.google.com> X-Google-Original-Message-ID: Date: Fri, 27 Sep 2024 16:00:01 +1000 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v2 6/6] c++/modules: Add testcase for standard-library exposures [PR115126] References: <66f64939.050a0220.261230.4b66@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <66f64939.050a0220.261230.4b66@mx.google.com> X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.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~incoming=patchwork.ozlabs.org@gcc.gnu.org Bootstrapped and regtested on x86_64-pc-linux-gnu and aarch64-unknown-linux-gnu, OK for trunk? -- >8 -- This adds a new xtreme-header testcase to ensure that we have no regressions with regards to exposures of TU-local declarations in the standard library header files. A more restrictive test would be to do 'export extern "C++"' here, but unfortunately the system headers on some targets declare TU-local entities, so we'll make do with checking that at least the C++ standard library headers don't refer to such entities. PR c++/115126 gcc/testsuite/ChangeLog: * g++.dg/modules/xtreme-header-8.C: New test. Signed-off-by: Nathaniel Shead --- gcc/testsuite/g++.dg/modules/xtreme-header-8.C | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 gcc/testsuite/g++.dg/modules/xtreme-header-8.C diff --git a/gcc/testsuite/g++.dg/modules/xtreme-header-8.C b/gcc/testsuite/g++.dg/modules/xtreme-header-8.C new file mode 100644 index 00000000000..9da4e01cc68 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/xtreme-header-8.C @@ -0,0 +1,8 @@ +// PR c++/115126 +// { dg-additional-options "-fmodules-ts -Wignored-exposures" } +// { dg-module-cmi xstd } + +export module xstd; +extern "C++" { + #include "xtreme-header.h" +}