From patchwork Fri Oct 11 13:30:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1996130 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=IZys8kiU; 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 4XQ6vN2XV3z1xtv for ; Sat, 12 Oct 2024 00:30:40 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8B4963857002 for ; Fri, 11 Oct 2024 13:30:38 +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 2C330385703F for ; Fri, 11 Oct 2024 13:30:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2C330385703F 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 2C330385703F 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=1728653416; cv=none; b=VcCP+mt03XKp4EVI3sZdQrCLHkrttNH3PgGgQ1dj1eTXgIkoF534nQIZ87oLCb7jm00fMEOA8+UAAmJu+biVl9eV6ms6vYWs/T3O61+7LJ9srtV4GSODTk7eq4fPetdFCfVfM+aZjk73T4OaFXWWK1zaZd+7rOgEurxbloQ0VLo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653416; c=relaxed/simple; bh=I2/jJOwqXJ0Fro8U/JSaW++YcGUt3ya6diCt/kN3nzk=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=Evu8InfvIxWS6sFi5MwaX6mkb+bQ9CdhYVrOiEBsysmrji1QmXQVeJFYu1DHWQGf0FP+rGME58rBk5SEbZXeY2vxEhW/cJWFDUgMizNGEL4HSxzoobbMhnwck8VZzAdg7RDMsTL3XI7GGIrnnBs8JaAaWt70oR1bPP1r0wnjUik= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x102f.google.com with SMTP id 98e67ed59e1d1-2e2b4110341so352671a91.1 for ; Fri, 11 Oct 2024 06:30:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728653410; x=1729258210; darn=gcc.gnu.org; h=in-reply-to:content-transfer-encoding:content-disposition :mime-version:references:subject:cc:to:from:date:message-id:from:to :cc:subject:date:message-id:reply-to; bh=UY1nnZ6IIeODbL5AfAjJLgPcWZuYtQXc0Y1pp/RuXNE=; b=IZys8kiUQJDs4brjG1yXuhIVDBgntKLzdPdavA35UeHpr72KDTv/lNUaaG/ujWat3Y Rcwvc7myR2LjqaiYZGPaWTfcxLHqyW+JIHJIDSzS9DTFTVyTu5wSPj7UYby/PWP5fL+u iecJ7/YSxpmtSKe8wRGDx/G2KKOLqk3Fy1pW+mOEhR2OpxQ4JdRYft5VQOcSDmtC6jlD iQvOn91eFjcZZMNaQe9Y/lif4GPH2d0B54wEB1nAS97StT8fGrn+QhPI0IOdkqSOyhXv ZyFHvnZ4qc6F5irOCv4Vu1GBb8B5rTvErYzHi06PT+MA2gwB4IvyDFWcSEZvEprWi7uC q7Ww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728653410; x=1729258210; h=in-reply-to:content-transfer-encoding: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=UY1nnZ6IIeODbL5AfAjJLgPcWZuYtQXc0Y1pp/RuXNE=; b=Dc/SJx0oXhAO+CquOH7w771ejMfs8TPHDzHKQlz/THMhsirjzk+U4qa9DtxMmk4OE3 yAGREzwl2lvSJtRJTrXP8aslisLRcdA5/9TV24OLDMeERPlMu6BY+yZS8ermjt82CyVe vFD48nSncX+yGFkRhZ9WwCB6cja9Jl6tlwTRsKRJ2kU1LOLM1XzObIduzd20dAbH8VoY 4DaQOkjQOWg3VnJ70hHYYV133/k0T2teNzDSxfT3/uyQ4VLIYwLwmNLFz3qiTElFaVGQ 19UEMGfA9WNhdlBFZ9HKYrcYGojODTAc6M+N6MU5cIw5eItI13YMMShiJBx4JI5x7Jp9 AfCg== X-Gm-Message-State: AOJu0Yzpx9t2VOLf1eB1HLJUhhq1cwuarHbMFvRj187As1CklCkUM/2Q oOWPOEJem3FjS8zzbH3+7NhFQPAxvNQKJLaM1MZUg9Rw9OvUfX32uSi7p2UL X-Google-Smtp-Source: AGHT+IEljkr/+QN5LGiEzuEJU5mCf1+WVOerqiT12QIvs+lRFW6frSliposIbbNGG1iOMMX7Q0752w== X-Received: by 2002:a17:90a:ee94:b0:2e2:e139:447d with SMTP id 98e67ed59e1d1-2e2f07ebe2dmr1465630a91.0.1728653409638; Fri, 11 Oct 2024 06:30:09 -0700 (PDT) Received: from Thaum. ([163.47.68.2]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2e2a5caa207sm5441310a91.55.2024.10.11.06.30.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 11 Oct 2024 06:30:09 -0700 (PDT) Message-ID: <67092861.170a0220.3dc447.37a4@mx.google.com> X-Google-Original-Message-ID: Date: Sat, 12 Oct 2024 00:30:04 +1100 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v3 1/5] c++/modules: Detect exposures of TU-local entities References: <6709282a.a70a0220.1dd501.9a14@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <6709282a.a70a0220.1dd501.9a14@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, RCVD_IN_DNSWL_NONE, 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 On Sat, Sep 28, 2024 at 1:56 AM Jason Merrill wrote: > I agree that LAMBDA_TYPE_EXTRA_SCOPE should do the trick; a lambda is > TU-local if it has no mangling scope. I'd prefer to pursue fixing holes > in LAMBDA_TYPE_EXTRA_SCOPE as needed to make it work, starting with the > patch above. > > Maybe be permissive for lambdas for this patch, and fix them up later? Sounds good to me; I found the patch I linked earlier is still missing quite a bit that I'm not currently sure how to fix, so I'll defer that till later; this patch instead just assumes all lambdas could have a mangling scope for determining TU-localness. 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. TU-local lambdas are also not yet properly implemented, due to other bugs with regards to LAMBDA_TYPE_EXTRA_SCOPE not being set in all cases that it probably should be (see also PR c++/116568). We can revisit this once that issue has been fixed. 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: * 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. 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_b.C: New test. Signed-off-by: Nathaniel Shead Reviewed-by: Jason Merrill --- gcc/cp/module.cc | 381 +++++++++++++++++--- 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 +- 7 files changed, 479 insertions(+), 70 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/module.cc b/gcc/cp/module.cc index 65b37b4b554..136ffb1a7ac 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 *); @@ -12886,6 +12889,247 @@ 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. */ + 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 (!DECL_CLASS_SCOPE_P (main_decl) + && !decl_function_context (main_decl) + /* FIXME: Lambdas defined outside initializers. We'll need to more + thoroughly set LAMBDA_TYPE_EXTRA_SCOPE to check this. */ + && !LAMBDA_TYPE_P (type)) + { + 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 +13236,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 +13252,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 +13308,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 +13417,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 +14118,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 +14132,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 +14190,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 +14202,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 +14406,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 +18673,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 Strongly 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/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..1a88bf7f0f9 --- /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" "" { xfail *-*-* } } + +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. + []{}; // { dg-bogus "exposes TU-local entity" } +}; +#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 Oct 11 13:30:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1996131 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=izZBXDq5; 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 4XQ6wS4kmBz1xtv for ; Sat, 12 Oct 2024 00:31:36 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6231B3857B91 for ; Fri, 11 Oct 2024 13:31:34 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pf1-x42a.google.com (mail-pf1-x42a.google.com [IPv6:2607:f8b0:4864:20::42a]) by sourceware.org (Postfix) with ESMTPS id 3599F385701B for ; Fri, 11 Oct 2024 13:30:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3599F385701B 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 3599F385701B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::42a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653464; cv=none; b=gMPaaJTddvToFtxIdP33sHHchL6MHp7Ld69ip7/cAP8XWVdNn6Ur78caRanPMbbzKNxVWafBi6lVw/l0dkEi35LxYW8MdxWpEKpmGneWIO8PIrP5ANVn+m75TcLv4XEPumk06qstLz4Ovvz1KYq8LKEeO2XIUGSoycwX7nBz4VM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653464; c=relaxed/simple; bh=m6g8Ep9/MArURkCi25tKk7gPyCLn8FRmG9tzDhl3LDQ=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=QOQnGRWiICB6pv9HV3gI8RmHnoxdukex+jNoesXDaA0YyNLqvq7uNX0jRarqGHDrdtoeFyEEKMWTeNayi5R6CxnPSCFK/VXuyXsRgTa2i4qpAWHVKnA2Z3McBcxQxfXz8TJWLXxJghtkzTvWPz5e5JY8HAbfcuPYEc7AF3Q9z00= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pf1-x42a.google.com with SMTP id d2e1a72fcca58-71e3914c39fso44764b3a.2 for ; Fri, 11 Oct 2024 06:30:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728653456; x=1729258256; 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=+pLZQcZlK9rwnFjmN8uc0Hu89tZxa5XRrm3b9xNVq0U=; b=izZBXDq5DErCWtH4SEOXpNMZ46sXgwVD4nATjppS677RUdOaiR4P+bqoqk7yFvy2Al vdN4Be9+Q6/HfLhS223NrqdgmcE8CgoTOn7arVm5YC7tVOBtB9rySDl4cbqa+eyRC4fG gs945TnraJhcIsHysEzPOkCqoWVLj46a6rDFb40jsNdmP+FAXnq6A7U+Uez7lYkM4MNP NcpOvPb11zWvC+Beqwnqpol4R4tOHN4OF14FsC+COkgSE0gyPK4l+A3qFQduXewozrTM ja3SLm75iw1xzlQPSH/er+gPuLxj5aR2zhGfBv4JNk5qPtxIP8XRUlulZmfp6c2BK5Pa 1gsQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728653456; x=1729258256; 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=+pLZQcZlK9rwnFjmN8uc0Hu89tZxa5XRrm3b9xNVq0U=; b=t0Mfrp91bjTuTv+905FWEs2JNo0KUQYWmZuh2RQBSg/rhO03531+oj0n9nhHObLZat wN+/fAR9lAHcjLbI3TxPnoyVI//9JgmLpKVFo7J1gCO1WHNTURhw/tcEj2d5mQ4Xgux/ 1W5Lzag05fIL/gcwYE5C/F6Q9J3Kt+76oiEamzWDdT13bpNK71kqLUGR9Nmsel2hvjrl /m6ojNDhWDkVCnSUBWCdxeK6NmL9mlQT7yFSr285JWK3F+jXIg8rVdvyw6MSGnxXHkn5 BxZvcp1gJrBVGQheU3hbMvGS1pRNpZm+PSm6QqjLJP+ZidfuyIBVu+IOprMIcIAHt613 WiuQ== X-Gm-Message-State: AOJu0YwH2UF6RFs5oWbqaOSP0qciw1YDkAwQ6EQDI3AwdGYFkjjCL1Mu IMdZ4+icyIoK+BVS8Vffaf2B4ZC6dG6nZwi4geuqlg/K+QS21NKXO6zfFi4j X-Google-Smtp-Source: AGHT+IGF0coxXUUmuqG4RnxmH0hOwDKQONwWngf3dTVSGe1uViOUq/Rb0AMbme9dzSBqUoRmuPIfFw== X-Received: by 2002:a05:6a00:2e86:b0:71e:3b8f:92b with SMTP id d2e1a72fcca58-71e3b8f1212mr1446393b3a.3.1728653455427; Fri, 11 Oct 2024 06:30:55 -0700 (PDT) Received: from Thaum. ([163.47.68.2]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-71e2a9f53a7sm2565963b3a.54.2024.10.11.06.30.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 11 Oct 2024 06:30:54 -0700 (PDT) Message-ID: <6709288e.a70a0220.3b7655.8be9@mx.google.com> X-Google-Original-Message-ID: Date: Sat, 12 Oct 2024 00:30:50 +1100 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v3 2/5] c++/modules: Ignore TU-local entities where necessary References: <6709282a.a70a0220.1dd501.9a14@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <6709282a.a70a0220.1dd501.9a14@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, RCVD_IN_DNSWL_NONE, 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 This version rewords all "ignored exposures" language. I haven't fixed up the issue with DECL_TEMPLATE_INSTANTIATIONS for this patch. I'll try to get to that as a separate patch if I find the time, but it's not 100% needed here I don't think. It seems the discussion around PR115126 RE: the libgcc changes that people are leaning towards reverting the changes I made to the gthread static variables and instead having some sort of attribute to instruct the compiler to ignore the ODR issues; that should be relatively straight-forward to add (maybe something like [[gnu::ignore_tu_local]]?) but I'll probably handle that as a separate patch once I'm sure that's what we actually want to do, and would probably be worth corresponding with the Clang folks to see what thoughts they have. Finally, I wasn't able to work out a good way to fold non-ODR usages of TU-local constants early. I attempted modifying 'mark_use', which helped for 'constexpr' usages (though I ran into issues with e.g. constexpr int n[1] = 0; constexpr int x = n ? 1 : 2; segfaulting as build_address was not expecting to see a constructor rather than a declaration here). But I didn't look too hard into solving this as it appears that any modifications made by 'mark_use' are not actually applied to the primary template at all, and this is consistent across many places I investigated. Given that erroring on these cases is still the status-quo, how easy it is to workaround most of the time, and that I'm still not sure how to solve this, I've also left this as a FIXME (with an XFAILed testcase) to revisit later. 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, '-Wtemplate-names-tu-local', 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. Additionally, this patch doesn't attempt to ignore non-ODR uses of constants in constexpr functions or templates. The obvious approach of folding them early in 'mark_use' doesn't seem to work (for a variety of reasons), so this leaves this to a later patch to implement, as it's at least no worse than the current behaviour and easy enough to workaround. For completeness this patch 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. gcc/c-family/ChangeLog: * c.opt: New warning '-Wtemplate-names-tu-local'. 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_REFS_TU_LOCAL_BIT. (depset::has_defn): Override for TU-local entities. (depset::refs_tu_local): New accessor. (depset::hash::ignore_tu_local): 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): Handle TU-local entities and 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): Only mark as exposure if we're not ignoring TU-local entities. (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 Wtemplate-names-tu-local. (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 -Wtemplate-names-tu-local. 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. * g++.dg/modules/internal-8_a.C: New test. * g++.dg/modules/xtreme-header-8.C: New test. Signed-off-by: Nathaniel Shead Reviewed-by: Jason Merrill --- 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 | 318 +++++++++++++++--- gcc/cp/pt.cc | 98 +++++- gcc/doc/invoke.texi | 19 +- gcc/testsuite/g++.dg/modules/internal-5_a.C | 110 ++++++ gcc/testsuite/g++.dg/modules/internal-5_b.C | 30 ++ 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 ++ gcc/testsuite/g++.dg/modules/internal-8_a.C | 35 ++ .../g++.dg/modules/xtreme-header-8.C | 8 + 14 files changed, 721 insertions(+), 49 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 create mode 100644 gcc/testsuite/g++.dg/modules/internal-8_a.C create mode 100644 gcc/testsuite/g++.dg/modules/xtreme-header-8.C diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 9d1fccadbf9..6e5ddefd0e2 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1450,6 +1450,10 @@ Wtemplate-id-cdtor C++ ObjC++ Var(warn_template_id_cdtor) Warning Warn about simple-template-id in a constructor or destructor. +Wtemplate-names-tu-local +C++ ObjC++ Var(warn_template_names_tu_local) Warning EnabledBy(Wextra) +Warn about templates naming TU-local entities in a module. + Wterminate C++ ObjC++ Warning Var(warn_terminate) Init(1) Warn if a throw expression will always result in a call to terminate(). 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 dc153a97dc4..a71d0704f8b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1778,6 +1778,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 node when instantiating a template 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. */ @@ -1811,7 +1827,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. */ @@ -1842,6 +1859,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 136ffb1a7ac..4b26dc5d367 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2330,7 +2330,9 @@ 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_TU_LOCAL_BIT, /* It is a TU-local entity. */ + DB_TU_LOCAL_BIT, /* Is a TU-local entity. */ + DB_REFS_TU_LOCAL_BIT, /* Refers to a TU-local entity (but is not + necessarily an exposure.) */ DB_EXPOSURE_BIT, /* Exposes a TU-local entity. */ DB_IMPORTED_BIT, /* An imported entity. */ DB_UNREACHED_BIT, /* A yet-to-be reached entity. */ @@ -2401,7 +2403,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: @@ -2416,6 +2420,10 @@ public: { return get_flag_bit (); } + bool refs_tu_local () const + { + return get_flag_bit (); + } bool is_exposure () const { return get_flag_bit (); @@ -2543,11 +2551,13 @@ public: depset *current; /* Current depset being depended. */ unsigned section; /* When writing out, the section. */ bool reached_unreached; /* We reached an unreached entity. */ + bool ignore_tu_local; /* In a context where referencing a TU-local + entity is not an exposure. */ public: hash (size_t size, hash *c = NULL) : parent (size), chain (c), current (NULL), section (0), - reached_unreached (false) + reached_unreached (false), ignore_tu_local (false) { worklist.create (size); } @@ -2748,6 +2758,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 +3043,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 +3074,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 +3126,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 +3167,7 @@ public: public: /* Serialize various definitions. */ - void write_definition (tree decl); + void write_definition (tree decl, bool refs_tu_local = false); void mark_declaration (tree decl, bool do_defn); private: @@ -3166,6 +3195,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 +3204,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 +4333,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 +4888,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); } } @@ -7809,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); @@ -8390,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); + } } } @@ -8902,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; } @@ -8925,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; } @@ -9084,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; } @@ -9363,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. */ @@ -9375,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 emit such decls. + We only need to do this when writing a definition of an entity + that we know names a TU-local entity. */ + 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; @@ -9531,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. */ { @@ -10147,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); } @@ -11917,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_tu_local, + !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); @@ -12020,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_tu_local, VAR_P (decl)); + tree init = DECL_INITIAL (decl); tree_node (init); if (!init) @@ -12207,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_tu_local, 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)) @@ -12574,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)); @@ -12722,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 refs_tu_local) { + auto ovr = make_temp_override (writing_local_entities, + writing_local_entities || refs_tu_local); + if (streaming_p ()) { assert_definition (decl); @@ -13309,7 +13503,11 @@ depset::hash::add_dependency (depset *dep) current->deps.safe_push (dep); if (dep->is_tu_local ()) - current->set_flag_bit (); + { + current->set_flag_bit (); + if (!ignore_tu_local) + current->set_flag_bit (); + } if (current->get_entity_kind () == EK_USING && DECL_IMPLICIT_TYPEDEF_P (dep->get_entity ()) @@ -13959,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 @@ -13971,15 +14169,15 @@ 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->refs_tu_local ()); } 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)) { @@ -14168,6 +14366,37 @@ depset::hash::finalize_dependencies () /* We should have emitted an error above. */ gcc_checking_assert (explained); } + else if (warn_template_names_tu_local + && dep->refs_tu_local () && !dep->is_tu_local ()) + { + 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 ref = rdep->get_entity (); + auto_diagnostic_group d; + if (warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wtemplate_names_tu_local, + "%qD refers to TU-local entity %qD and cannot " + "be instantiated in other TUs", decl, ref)) + is_tu_local_entity (ref, /*explain=*/true); + break; + } + } } return ok; @@ -15331,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)) @@ -15553,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->refs_tu_local ()); if (!namer->has_defn ()) namer = b; diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 03a1144765b..c7c6da0969e 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -9881,6 +9881,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 @@ -12743,8 +12808,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. */ @@ -15522,7 +15589,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 @@ -16250,6 +16318,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; @@ -18486,6 +18562,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) @@ -20255,6 +20337,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)); @@ -20947,6 +21032,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); @@ -21968,6 +22056,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 d38c1feb86f..b5f767f6f12 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -272,7 +272,7 @@ in the following sections. -Woverloaded-virtual -Wno-pmf-conversions -Wself-move -Wsign-promo -Wsized-deallocation -Wsuggest-final-methods -Wsuggest-final-types -Wsuggest-override -Wno-template-body --Wno-template-id-cdtor +-Wno-template-id-cdtor -Wtemplate-names-tu-local -Wno-terminate -Wno-vexing-parse -Wvirtual-inheritance -Wno-virtual-move-assign -Wvolatile -Wzero-as-null-pointer-constant} @@ -4686,6 +4686,23 @@ template struct S @{ @option{-Wtemplate-id-cdtor} is enabled by default with @option{-std=c++20}; it is also enabled by @option{-Wc++20-compat}. +@opindex Wtemplate-names-tu-local +@opindex Wno-template-names-tu-local +@item -Wtemplate-names-tu-local +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 is not an exposure and no error is emitted. + +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 flag is enabled by @option{-Wextra}. + @opindex Wterminate @opindex Wno-terminate @item -Wno-terminate @r{(C++ and Objective-C++ only)} 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..c5ef3752f5a --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-5_a.C @@ -0,0 +1,110 @@ +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } +// { 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(); +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 = 0; + +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 = 456; +static const internal_t internal {}; +void f(int) {} +export inline void no_odr_use() { + int x = value; + int y = ref; + int z = (internal, 0); + + value; + bool b = value < value; + f(value); +} 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..baf60fdafa2 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-5_b.C @@ -0,0 +1,30 @@ +// { 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(); + 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..39f53ea382e --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-7_a.C @@ -0,0 +1,75 @@ +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } +// 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 } diff --git a/gcc/testsuite/g++.dg/modules/internal-8_a.C b/gcc/testsuite/g++.dg/modules/internal-8_a.C new file mode 100644 index 00000000000..46e07a23cf0 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-8_a.C @@ -0,0 +1,35 @@ +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } +// Non-ODR usages of const variables currently are erroneously +// reported in templates and constexpr functions; this test +// XFAILS them until we can implement a fix. + +export module M; + +namespace { struct internal_t {}; }; +static const int value = 123; +static const int& ref = 456; +static const internal_t internal {}; + +constexpr void f(int) {} + +export constexpr +void no_odr_use_cexpr() { // { dg-bogus "TU-local" "" { xfail *-*-* } } + int x = value; + int y = ref; + int z = (internal, 0); + + value; + bool b = value < value; + f(value); +} + +export template +void no_odr_use_templ() { // { dg-bogus "TU-local" "" { xfail *-*-* } } + int x = value; + int y = ref; + int z = (internal, 0); + + value; + bool b = value < value; + f(value); +} 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..82c0b59fefe --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/xtreme-header-8.C @@ -0,0 +1,8 @@ +// PR c++/115126 +// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" } +// { dg-module-cmi xstd } + +export module xstd; +extern "C++" { + #include "xtreme-header.h" +} From patchwork Fri Oct 11 13:31:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1996133 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=gHv26Ihw; 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 4XQ6xl64fwz1xtv for ; Sat, 12 Oct 2024 00:32:43 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 13F993857358 for ; Fri, 11 Oct 2024 13:32:42 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by sourceware.org (Postfix) with ESMTPS id 810D9385AC1F for ; Fri, 11 Oct 2024 13:31:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 810D9385AC1F 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 810D9385AC1F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::62a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653483; cv=none; b=jkh2y+F/V1XS81tAKrTllLBxEbDlO/TDUcG5AVr+k/QMRlv33RpxYc/BuL6FK/Wgz/zoKD+FDCPvJqB/M6WG9EDLhGPtZAsEQgnJ5RVXE2zdm4J/HyOFq6NfqXv76inlBzOohuykbd3ObSIOq/O7LvfMD/9tGT+QZd8St2cuMs8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653483; c=relaxed/simple; bh=NaeDkqYFod3iuQOpotYgrNghGVg7pNbWGmgh9b2mnuE=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=wnu2nep+Fy2DhaoMPEifBblGuZgT7qusdGHWCgfnLRczAJS/8SsjobiIfx28ivlUGXio+7Mt1W96ZpLIAGGvKRIjEIIPePROlY38e09O7mmga1tKoLgw3uWljjpCcmjHmx+rS9HciBq/p6C5gl8Ezd/EnyC/MrWaKwnR3YEwZ3o= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pl1-x62a.google.com with SMTP id d9443c01a7336-20c804409b0so805485ad.2 for ; Fri, 11 Oct 2024 06:31:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728653479; x=1729258279; 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=l4af7uBo+8Ddh8NeCTGyJb25AdiNBiJDI1IGZFwHN5w=; b=gHv26Ihwb/Ff9KNTW24wi7Akm5AXxlWz1COiqfjJ8VyUdUu9J5q8EbqnNaRBmYApBM QlqkYIe/ZlnuoJex1yUsZgPmQoBazEv0RpDvkE2zi/pNZ1d3bYe6jO2fwB+NUIfCPLnb 6v2gaMKlQFjuQ5Gaqt2g0gEvl6xUECMxnP2IwKmZXtACHC08KaiM+bDAQTqO6CAoD7nH 5W5cSH6oQMT13LJT4+ZQ7zr6z0QzJHuj+G0etD19UXiXO34yWNqKDOJ52ZVbtZZQ3AOk Ca1h1LC7IW/btL74zzP9Afoi/oJyruoUvsYJ57aCdzoluAk1DiY25xlPtFsoHiYITGKU LYeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728653479; x=1729258279; 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=l4af7uBo+8Ddh8NeCTGyJb25AdiNBiJDI1IGZFwHN5w=; b=rspeyrskvOw8lrgEB6jZlcj8nwAo9AC2D9poEWNXkqZ57vhvnaAyb5SZySRPhrnDoA l+AofD/z9ZYNmF9Wsx8WnpIXDDr1xgdqNqcxemA4FvC7RJ2qnt3QB8D/ps7TawrqIPYJ 2q6IBAMatVLavuIOX9lbUzYQX2ZmpedEhYO8p8JKmuL4XCVhk+swDyWcMr56TumrIxCH bxaHjuR0ki0rY6k3xHB3LYSZ5BkaFgBsWgx32oZpRW1Q71/SCtUDGLePTPwnNTbIB3uN evSgPVZIpbxpkQinRHiFTG14BWkq2L1AMN3Xg2xs66zr8lJRPwsDIsSAZ/2aMT3afz3/ Uh9g== X-Gm-Message-State: AOJu0YwwxuBCbAwUxWlIDgG/kooV/Z8QQHvfoId81vHaeNV1weuI2hwI MD7Lwxbli5752E5PCI2tlJ108tjJcl8myx1AXEAuXkWn5og8TMzl/HsbU0wD X-Google-Smtp-Source: AGHT+IGnNY4b1UiV+5GQxQdcdi0P2OKLj4cRiSWJ/FXiDba2PjTzx6X1mggiCkdC7OC2YC8aayA0tg== X-Received: by 2002:a17:902:d48f:b0:20c:5bf8:bd70 with SMTP id d9443c01a7336-20ca1478a7amr16050535ad.6.1728653479277; Fri, 11 Oct 2024 06:31:19 -0700 (PDT) Received: from Thaum. ([163.47.68.2]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-20c8bc136c5sm23471895ad.83.2024.10.11.06.31.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 11 Oct 2024 06:31:18 -0700 (PDT) Message-ID: <670928a6.170a0220.2beec7.848e@mx.google.com> X-Google-Original-Message-ID: Date: Sat, 12 Oct 2024 00:31:14 +1100 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v3 3/5] c++/modules: Support unnamed namespaces in header units References: <6709282a.a70a0220.1dd501.9a14@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <6709282a.a70a0220.1dd501.9a14@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, RCVD_IN_DNSWL_NONE, 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 Added the rationale comment for the 'make_namespace_finish' changes, changed commit message to mention 'unnamed' rather than 'anonymous' namespaces. -- >8 -- A header unit may contain unnamed 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 unnamed 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 unnamed 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 unnamed 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-9_a.H: New test. * g++.dg/modules/internal-9_b.C: New test. Signed-off-by: Nathaniel Shead Reviewed-by: Jason Merrill --- gcc/cp/module.cc | 7 +++-- gcc/cp/name-lookup.cc | 10 +++---- gcc/testsuite/g++.dg/modules/internal-9_a.H | 28 ++++++++++++++++++++ gcc/testsuite/g++.dg/modules/internal-9_b.C | 29 +++++++++++++++++++++ 4 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/internal-9_a.H create mode 100644 gcc/testsuite/g++.dg/modules/internal-9_b.C diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 4b26dc5d367..f75e211e0e1 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 609bd6e8c9b..22391110fd7 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -9102,6 +9102,11 @@ 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); + + /* An unnamed namespace implicitly has a using-directive inserted so + that its contents are usable in the surrounding context. */ + 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, @@ -9238,11 +9243,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-9_a.H b/gcc/testsuite/g++.dg/modules/internal-9_a.H new file mode 100644 index 00000000000..57fe60bb3c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-9_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-9_b.C b/gcc/testsuite/g++.dg/modules/internal-9_b.C new file mode 100644 index 00000000000..5f8eeac2340 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/internal-9_b.C @@ -0,0 +1,29 @@ +// { dg-additional-options "-fmodules-ts" } + +import "internal-9_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 Oct 11 13:31:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nathaniel Shead X-Patchwork-Id: 1996134 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=S3dmnLNi; 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 4XQ6yy4pkYz1xtv for ; Sat, 12 Oct 2024 00:33:46 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DBD6A385AC32 for ; Fri, 11 Oct 2024 13:33:44 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pf1-x42f.google.com (mail-pf1-x42f.google.com [IPv6:2607:f8b0:4864:20::42f]) by sourceware.org (Postfix) with ESMTPS id 596F43857354 for ; Fri, 11 Oct 2024 13:31:41 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 596F43857354 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 596F43857354 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::42f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653505; cv=none; b=TzgobVuRihlePE35Y1wD3wfNrcq2QPdvRWzcwF4HTLo1qD44rnSul8BkshwlvT4UjDmhBfsQ6cWAk1SxCPPROANjlI4mgbmxNC+GdMBfradqNz8ddJA9iTswmZO5vEKr8Pc4yCm2F74JvlW3IVeq38a9+qJ2uKdgXKhZT0L8auk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653505; c=relaxed/simple; bh=Iqi923pAFpnVC/GyN4c2ClyFOhq6VxqGtk9QzenDkQA=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=pPgvgcGKtMj3zDNC4hxGVVmSMm3OYT9HpWc/lPgzPOgpTbrRdodSK0CWjgKGIH+pH/R85U1R4rfGQzzMDmNtStyJ2IphecX4OGJii8Bg6yWNWOLwLOLI3ERk5NmQ638at1mxcLYZpfYHMzgUP+9G5F1nB8ZDqaQnsqM0Om55++M= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pf1-x42f.google.com with SMTP id d2e1a72fcca58-71927b62fa1so202258b3a.2 for ; Fri, 11 Oct 2024 06:31:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728653500; x=1729258300; 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=YKMNo01YiATiU22WIKWsQgaHpOYhP3HYi3A5eI5OUqc=; b=S3dmnLNiXq2NZXNo2iete4QA+ObGdZQ1Scdk37+ZIHVMnbAKJgQzqImeCXg2vd8y5Z DlDG0LWVQH1rUcU7H3Z+3hJBXLT9qkUuZMqLlXk9txnQA5HnjPSDF9C5TlBSB/zHbU8Q 7jrvzrNk0wIPfyxU4psgbKSD10KQ4QcQxwAPrDo0PNY3fQxOMdhCAI6S+3C4imsQgf2k ny3zVrWCOZRt30VzJ2b+fdojQSpqCC8HccHs+daJ/YSq68NJNvr0yxvDvb4Xd1VsKKBf 9c4p4Jf4jN7wZ5GmuYiZJQc7yQuq228yRj4cZoLVYPI8ONEY8mStEOGdkHUeVpVJirGU xVGw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728653500; x=1729258300; 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=YKMNo01YiATiU22WIKWsQgaHpOYhP3HYi3A5eI5OUqc=; b=kLN9kUDzuQxL90N3BvGEk0P7trcO7V5uEe3/Jq3qSRvBb7wYVHimntzLBfRTkMMQQL DAoWCqRdoygN1KEf9fQPAdWSOEOkm9yfQSMsLlk1Ce1zz9KcPMjcxjEoD2kRC1SdzLev xCao50R5FXppel/w5Xt8YXwYxa490CRsePSl5o23urg2Y7VCaY7xWyQMsSdMXw+LZ0Ic gYO1Oe1rI81QD3z9C5d67YUVRZpEwmo4ddcNqXqLZc7N/1wV7U/VPN/F+DmFE7MTrWjB 80nwL25pazkAiFVzrlfN/CU/VL3XaDHIBGsPkVnvhe+kTKAmp7aFoIZcLXft5h3UJaHv jTBQ== X-Gm-Message-State: AOJu0YwdKTtNc/vGq6dsJYMVHGxNRs+jfBPq/uPYQxTlLkAwB+nqG0Qs v1V8b51L2yw7Nw3Yxy+PMsGUhphyHZLUFDtFrKLUrsTTOsCEj8WsmOd4MT6Q X-Google-Smtp-Source: AGHT+IGQlZh7ggOF4uF9C5Y5oyGuoc4woXjDyCHD/a+3Ndqt3yugDwqTet+r6qoO7Iv8OmrPmi5BSQ== X-Received: by 2002:a17:90b:318:b0:2e2:a70a:f107 with SMTP id 98e67ed59e1d1-2e2f0a11479mr1541289a91.1.1728653500126; Fri, 11 Oct 2024 06:31:40 -0700 (PDT) Received: from Thaum. ([163.47.68.2]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2e2d5c99db3sm3167262a91.0.2024.10.11.06.31.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 11 Oct 2024 06:31:39 -0700 (PDT) Message-ID: <670928bb.170a0220.48198.aa1a@mx.google.com> X-Google-Original-Message-ID: Date: Sat, 12 Oct 2024 00:31:35 +1100 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v3 4/5] c++/modules: Check linkage for exported declarations References: <6709282a.a70a0220.1dd501.9a14@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <6709282a.a70a0220.1dd501.9a14@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, RCVD_IN_DNSWL_NONE, 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 Updated to use 'unnamed namespace' instead, added some more details to the internal linkage namespace diagnostic. -- >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 Reviewed-by: Jason Merrill --- gcc/cp/cp-tree.h | 1 + gcc/cp/decl2.cc | 1 + gcc/cp/module.cc | 29 +++++++++++++++++--- gcc/cp/name-lookup.cc | 20 +++++++++++--- 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, 92 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 a71d0704f8b..f57a9b0b6ce 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7456,6 +7456,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 f75e211e0e1..456b73d59b6 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 ill-formed to export a declaration with internal linkage. However, + at the point this function is called we don't yet always know 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 22391110fd7..10d8f4bd87b 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -6605,7 +6605,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; @@ -6615,12 +6615,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); @@ -8550,6 +8551,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; } @@ -9255,8 +9257,18 @@ 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) + { + auto_diagnostic_group d; + error_at (input_location, "exporting namespace %qD with " + "internal linkage", ns); + inform (input_location, "%qD has internal linkage because " + "it was declared in an unnamed namespace", ns); + } + else + error_at (input_location, "exporting unnamed 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 9d31a975dcf..393d7b91aee 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -22396,6 +22396,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; @@ -22419,7 +22420,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. @@ -22868,6 +22869,8 @@ cp_parser_alias_declaration (cp_parser* parser) check_member_template (decl); } + check_module_decl_linkage (decl); + return decl; } @@ -23925,6 +23928,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) @@ -33161,6 +33165,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 @@ -33815,6 +33820,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 c7c6da0969e..81d0a8f5203 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -29733,11 +29733,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..5a001d7cff2 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 unnamed 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..3dd3876b13e --- /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 unnamed 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 Oct 11 13:32: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: 1996132 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=lU9K/O2J; 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 4XQ6xV3Zr3z1xtv for ; Sat, 12 Oct 2024 00:32:30 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9FBAE385780C for ; Fri, 11 Oct 2024 13:32:28 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pf1-x435.google.com (mail-pf1-x435.google.com [IPv6:2607:f8b0:4864:20::435]) by sourceware.org (Postfix) with ESMTPS id 73C033857358 for ; Fri, 11 Oct 2024 13:32:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 73C033857358 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 73C033857358 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::435 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653530; cv=none; b=tF7bhnLHHXozIJrYpYAlmiS6ZZXNzeNiD1ZMadkfRiqqnUnVCd2DCyopHMgNxYiDDwrOHlSkF3xiqBAnfRuA1g+L6RLbP7fDxFA4dXrQYVvgL1tTlen8VyLgMGREGYmuFFQ5j9n1RVHyvp0+jXFRsBzAATro73aoETHXjtcFmmE= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1728653530; c=relaxed/simple; bh=BMIaKSpInrUGtMCuYCsL6KX6dXEfN3oZsLPjcHWqOig=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=S6Ce0F8narcSlJw7sOv46WsWo9rpdSjLk3E1N+RJIz3Zc9ASeRc2Eq3x/wqCBYL7YAPOgWqPYKgqs2HRX5HQpo6dd5hYUMXNcqP4kR8gvMsPl+7woZyFlxjXV7IQT7rnovrgGbZ9U0odr1wHwbMNU6giVIGmPT8l2+PHleXWoXQ= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pf1-x435.google.com with SMTP id d2e1a72fcca58-71e01eff868so150731b3a.1 for ; Fri, 11 Oct 2024 06:32:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728653526; x=1729258326; 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=c3g3DOsZmxBA8pWIE6JI1V5y1Z19zRpasjfbUijJBBI=; b=lU9K/O2JBZUm0CA+8wXatzfK80mfTPEWXHyuQF3quDRH/vHFJFGbzRhLCaHXjKunoe 1adQI29DGd+k9WBi8Ul96PcnknDbUY3yFyzGduZeppSlyRNT5CvkdanNPZxY2thKIMXX pFnBzoUMJRWeXlkd2p17qMO87MDz8UMMBfPUATNU5Z140ukL5S72uMuxeBTd52ae9E/W QdS710gYQRFz6gFwB1tgl6nXlLVFWRhYoBYn1goYXBNvgAicQ0WZ1nSpsCIpqCeJWSfr woEf4Z8lpof5BTbdPYa9oiKZQ4qwtNSTXS811EJpgkQnJblX+aaz1uxiOAv6Jj+TqspT tg1w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728653526; x=1729258326; 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=c3g3DOsZmxBA8pWIE6JI1V5y1Z19zRpasjfbUijJBBI=; b=l26joQ2LPeFxl34DjG+gHvfm3vWRpafu04Tasbr3SKcSDgnAtWprvYKj27UhKH+11L F7TkaRQwDrPdkrTDpTva0bnWOLlVOY4glDB5+2WJ6dZEivk1bZtdLEIw5rESo+3WmEBl sTIbdBCTeLNrbnhl74hXDhbItLcQHIbPTdkMmKmJTUCFgJQr2DTeYac/uH31lRTHrq7l 4XxZ2ngAOZOCyKt5akK29ZyO86T3R57Ja+g12mRoiCHn9Sdg4TvdGu+LRTTBFJHE1BLn okp06sbr476mT4cGQ0+U6UOpwimAiP7AnBEwSLjdUNK7ExOrDoTfAdgl6nhgeSgiVD+Z EsjQ== X-Gm-Message-State: AOJu0YwnyCmN8SPeAgflU4VUICWanUIRZ6kEgUaQEXyllVmd8mvHhJ/E QcVXiuSfEKbSrx8iNJa1ERTQxPxKsFfCe0kVgxhilXLAR3NBI5W8b6xrYOpp X-Google-Smtp-Source: AGHT+IGq9TgatMRAYwYbQ6Rq9KM+sJvXFL2x7RXUVSh3qdPZKaptZOisZUaSHRvonMzx5W0gMkkMag== X-Received: by 2002:a05:6a00:10c8:b0:71d:f457:bf74 with SMTP id d2e1a72fcca58-71e37e24bfemr1798461b3a.2.1728653526127; Fri, 11 Oct 2024 06:32:06 -0700 (PDT) Received: from Thaum. ([163.47.68.2]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-71e3fd1fd48sm895124b3a.17.2024.10.11.06.32.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 11 Oct 2024 06:32:05 -0700 (PDT) Message-ID: <670928d5.a70a0220.3a555e.2b24@mx.google.com> X-Google-Original-Message-ID: Date: Sat, 12 Oct 2024 00:32:01 +1100 From: Nathaniel Shead To: gcc-patches@gcc.gnu.org Cc: Jason Merrill , Nathan Sidwell Subject: [PATCH v3 5/5] c++/modules: Validate external linkage definitions in header units [PR116401] References: <6709282a.a70a0220.1dd501.9a14@mx.google.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <6709282a.a70a0220.1dd501.9a14@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, RCVD_IN_DNSWL_NONE, 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 Added a comment to grokfndecl about marking deduction guides as inline. An alternative would be to instead specially handle deduction guides in check_module_decl_linkage; would that maybe be preferable? -- >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 Reviewed-by: Jason Merrill --- gcc/cp/decl.cc | 4 + 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, 200 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 07fb9855cd2..572a4e7d98b 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10833,6 +10833,10 @@ 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; + /* But to ensure that external-linkage deduction guides in header units + don't fall afoul of [module.import] p6, mark them as inline to give + the same effect as them not having a definition. */ + DECL_DECLARED_INLINE_P (decl) = true; break; default: break; diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 456b73d59b6..09256bf843a 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];