| Message ID | 67d42eee.a70a0220.156528.a5d4@mx.google.com |
|---|---|
| State | New |
| Headers |
Return-Path: <gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A1D2E385781B for <patchwork@sourceware.org>; Fri, 14 Mar 2025 13:29:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A1D2E385781B Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=gCupmBKW X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by sourceware.org (Postfix) with ESMTPS id 72B993858C48 for <gcc-patches@gcc.gnu.org>; Fri, 14 Mar 2025 13:28:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 72B993858C48 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 72B993858C48 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::631 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741958896; cv=none; b=Cn0fI0kpczT4jKwziSdFePyHKz1yz3YKoypf8VtRMODUQVZPZ1bOFmSw6NwyLtAfeQ7DrzYOaGVPOxx8g1l0Cjeb3LQXpP8/lbRKl7P5xBmk5K6C+f4IbLVopWONbwEHDTSW3R9hYzfFUv1eaH6bfj4+56CT97UeSH+O5w1MQSk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1741958896; c=relaxed/simple; bh=Ogm2FXxsTOP3KOZs4fYpbmdleKlryrncBZQvyX3s/WQ=; h=DKIM-Signature:Message-ID:Date:From:To:Subject:MIME-Version; b=L4hJbZqI+kS+d4iM7kExbUC9O7TTQ5MOKU53glsltzqRaVq3rxaMrqndTFigJdiwHZVD2hy8pmMCZzzuyzrn3fzCxk9jhJqlZHcaHPoPENJsdP5iteHVI42soAtJhGBA6fOOlaxPfptAkPc7gQwpTQkJ52mhNZ8cjvJkIO2IKro= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 72B993858C48 Received: by mail-pl1-x631.google.com with SMTP id d9443c01a7336-22400301e40so5679595ad.1 for <gcc-patches@gcc.gnu.org>; Fri, 14 Mar 2025 06:28:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1741958895; x=1742563695; 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=R0HKW80XFyl1bj/pW5MUfuTdCmqsgiVTdo1Fue+HqNA=; b=gCupmBKWzDMEDGHdpq0zHmsyYthBcHh0OWdiaUOtrn1hnGfJa+dhwAZyHRjOkFogKe bQ5gaxQDpfAtxSFMDseLcmqB2x/46+Vlfx8jW2Ac9nyNTGnmbnmLs71R6f2ws+MUv0AV vczPfdgRGGHry8MjMWdKasJz6fxPlT8mXDSuk8SnRzPep2odI5UF3+GjSQbzAzbNHM9j fhVyYkfaS8KvmppGq6OPi+aLOGr1CTaGqLHUUHLxXlvIemZVFBSjH3IHClQGIuh3TaFp e4POalSVMqjHQ/0VhC26ZBbI/XQ2g2SVEJn1ivl+uND+X+vp4ry+NJZGoXIT21YeTafP 46gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1741958895; x=1742563695; 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=R0HKW80XFyl1bj/pW5MUfuTdCmqsgiVTdo1Fue+HqNA=; b=hGUBhOSnxUJ8Wny4OARrhH1i6Jm7SJhkllWJFiPuwVyYs3yMBe2kTe72JTf6zt25fd f18/IZ6FZdZbC3mwuY+RpgBevs4fF6LeppkQwrWKlhrbhAFNpUWBpaHxVttAhMbW8Qyn k7mVnWs0k5GUHioxkizrr1Iv54bKeSLrr4/Yjo13F3TodWf+hkpFatLhs92XvZlgEAzy SHgE4Ektp4e1E3p9tsA/Avw2jeNmoxMWIhJu18sdEAxttV+HcZiF2aExB0AkL3DuGHsB fkGTewwSjum6G7gu4wUTGwRu0Zqwr5/s/TE1n7C0pgyNF6IrDE1583i2P+/IFo7wNajq ZSHg== X-Gm-Message-State: AOJu0Yz5b4pAXNGLwTc0yL4QhRh8JiQMuqClPfOTBS3mmcWvR4qCLFwV 6d7pEV3rO+XY5AzfiBFIsnLaRKja2LKIQlL6Hh7yf68Pq71+5IsA X-Gm-Gg: ASbGncscNzi0qy3TxolIueADgFhaDLaqPpHjqdV4mGeUl+9OznEaUGzsABUd6O6KaPm I5D/3eikpb7z9rMJGHvfMrPMx4G3ne2DRWXOIsab4EbHWxSfYfhx1qNQ8OMh5ztAXs95xEnYSW0 y9Kwl75AzlXNdxIo/Pz2twha2UxuBHVs/lZFot1YSxbQ1OwtugVAXH9BEXZ4aCGgBCVFz+TLv0v osEd5GV+yNN5a8PZ6Nz7jAUzOeRqUKT/nyVJB1ZSohstDyHTxLaU0kkRUELUaWrDhVC7gD6JHe7 HLdPe6Ml4lIS2gZT6qJmSlopJ0eLBEzsB56/dEwf3CpRHMKr07YIKD87n92mmUdI54JHoiYUj+R hsTd3aBP9BtZL X-Google-Smtp-Source: AGHT+IGVXzBpHM4mLfa4yejHnxUAq3WN1xrgamMvnCyuh032ub66B8Tv2RrhM/PI8sVuJlQK5y97eA== X-Received: by 2002:a17:902:e802:b0:215:8d29:af0b with SMTP id d9443c01a7336-225e0b07470mr12572655ad.14.1741958895175; Fri, 14 Mar 2025 06:28:15 -0700 (PDT) Received: from Thaum. (163-47-68-46.ipv4.originbroadband.com.au. [163.47.68.46]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-7371157369bsm2851563b3a.75.2025.03.14.06.28.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Mar 2025 06:28:14 -0700 (PDT) Message-ID: <67d42eee.a70a0220.156528.a5d4@mx.google.com> X-Google-Original-Message-ID: <Z9Qu6srfWQAxaYlE@Thaum.> Date: Sat, 15 Mar 2025 00:28:10 +1100 From: Nathaniel Shead <nathanieloshead@gmail.com> To: Jason Merrill <jason@redhat.com> Cc: gcc-patches@gcc.gnu.org Subject: [PATCH 2/1] c++/modules: Handle gnu_inline, again [PR119154] References: <67d2f6d7.170a0220.5992f.ecc4@mx.google.com> <4eca90c5-7b9d-4df4-bc46-3faffd027928@redhat.com> <67d37e6c.170a0220.4d47f.658b@mx.google.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <67d37e6c.170a0220.4d47f.658b@mx.google.com> X-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, 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 <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org |
| Series |
c++: Make explicit instantiations not vague linkage
|
|
Commit Message
Nathaniel Shead
March 14, 2025, 1:28 p.m. UTC
Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
Alternatively, could still mark gnu_inline functions as non-vague, we
just need to do so more aggressively; but given this is specifically to
solve a modules issue I felt may as well keep it confined to there given
your previous comments.
Note that this patch is on top of my patch for explicit instantiations
here: https://gcc.gnu.org/pipermail/gcc-patches/2025-March/677617.html
-- >8 --
My change in r15-8012 was not sufficient to solve all issues with
gnu_inline; in some cases, a gnu_inline function could be DECL_COMDAT
and still pass through the adjusted vague_linkage_p, to then have
import_export_decl called on it during lazy loading at EOF and ICEing.
This patch reworks gnu_inline handling. As well as ensuring that
DECL_INTERFACE_KNOWN will be set for imported gnu_inline functions, we
make sure that we properly handle importing a gnu_inline function over
the top of an existing forward-declaration.
The other case that duplicate_decls handles (importing a regular
definition over the top of a gnu_inline function) doesn't seem like
something we need to handle specially in modules; we'll just use the
existing gnu_inline function and rely on the guarantee that there is a
single non-inline function definition provided elsewhere.
PR c++/119154
gcc/cp/ChangeLog:
* decl2.cc (vague_linkage_p): Revert change to treat gnu_inline
functions as not vague linkage.
* module.cc (trees_out::core_bools): Don't redetermine linkage
of gnu_inline functions, which are always external.
(trees_in::read_function_def): Merge needed attributes when
installing a gnu_inline definition on top of an existing
declaration.
gcc/testsuite/ChangeLog:
* g++.dg/modules/pr119154_a.C: Move to...
* g++.dg/modules/gnu-inline-1_a.C: ...here.
* g++.dg/modules/pr119154_b.C: Move to...
* g++.dg/modules/gnu-inline-1_b.C: ...here, and check that the
gnu_inline function is not emitted.
* g++.dg/modules/gnu-inline-1_c.C: New test.
* g++.dg/modules/gnu-inline-2_a.C: New test.
* g++.dg/modules/gnu-inline-2_b.C: New test.
Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
gcc/cp/decl2.cc | 4 +--
gcc/cp/module.cc | 27 +++++++++++++++++--
.../{pr119154_a.C => gnu-inline-1_a.C} | 0
gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C | 12 +++++++++
gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C | 13 +++++++++
gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C | 11 ++++++++
gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C | 14 ++++++++++
gcc/testsuite/g++.dg/modules/pr119154_b.C | 10 -------
8 files changed, 76 insertions(+), 15 deletions(-)
rename gcc/testsuite/g++.dg/modules/{pr119154_a.C => gnu-inline-1_a.C} (100%)
create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C
create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C
create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C
create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C
delete mode 100644 gcc/testsuite/g++.dg/modules/pr119154_b.C
Comments
On 3/14/25 9:28 AM, Nathaniel Shead wrote: > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? > > Alternatively, could still mark gnu_inline functions as non-vague, we > just need to do so more aggressively; but given this is specifically to > solve a modules issue I felt may as well keep it confined to there given > your previous comments. I think we want a function import_interface_unknown or import_might_emit or something that is vague_linkage_p with additional restrictions. As I think of it, an explicitly instantiated template is still vague linkage, but vague linkage is subject to various heuristics and controls like explicit instantiation and the ABI tinfo rule. The ABI spec recently drifted away from this interpretation toward vague linkage meaning COMDAT, but I think that was a conceptual mistake. gnu_inline seems borderline to me, but I lean toward the change in this patch to treat all inlines as vague linkage and check gnu_inline in the new function. Jason
On Mon, Mar 17, 2025 at 09:42:13AM -0400, Jason Merrill wrote: > On 3/14/25 9:28 AM, Nathaniel Shead wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? > > > > Alternatively, could still mark gnu_inline functions as non-vague, we > > just need to do so more aggressively; but given this is specifically to > > solve a modules issue I felt may as well keep it confined to there given > > your previous comments. > > I think we want a function import_interface_unknown or import_might_emit or > something that is vague_linkage_p with additional restrictions. > > As I think of it, an explicitly instantiated template is still vague > linkage, but vague linkage is subject to various heuristics and controls > like explicit instantiation and the ABI tinfo rule. The ABI spec recently > drifted away from this interpretation toward vague linkage meaning COMDAT, > but I think that was a conceptual mistake. > Makes sense. > gnu_inline seems borderline to me, but I lean toward the change in this > patch to treat all inlines as vague linkage and check gnu_inline in the new > function. > > Jason > I found the clearest way to go was to make the function a tristate (vague, always external, always emitted by importer) to handle header units more naturally. Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? -- >8 -- Subject: [PATCH] c++/modules: Fix explicit instantiations and gnu_inlines [PR119154] My change in r15-8012 for PR c++/119154 exposed a bug with explicit instantation declarations. The change cleared DECL_INTERFACE_KNOWN for all vague-linkage entities, including explicit instantiations. When we then perform lazy loading at EOF (due to processing deferred function bodies), expand_or_defer_fn ends up calling import_export_decl which will error because DECL_INTERFACE_KNOWN is still unset but no definition is available in the file, violating some assertions. It turns out that for function templates marked inline we would not respect an 'extern template' imported in general, either; this patch fixes both of these issues by always treating explicit instantiations as external, and so marking DECL_INTERFACE_KNOWN eagerly. For an explicit instantiation declaration we don't want to emit the body of the function as it must be emitted in a different TU anyway. And for explicit instantiation definitions we similarly know that it will have been emitted in the interface TU we streamed it in from, so there's no need to emit it. The same error can happen with lazy-loaded gnu_inlines at EOF; in some cases they'll be marked DECL_COMDAT and pass through the vague_linkage_p check anyway. This patch reworks the handling of gnu_inlines to ensure that both DECL_INTERFACE_KNOWN is always correctly set and that importing a gnu_inline function over the top of an existing forward declaration works correctly. The other case that duplicate_decls handles (importing a regular definition over the top of a gnu_inline function) doesn't seem like something we need to handle specially in modules; we'll just use the existing gnu_inline function and rely on the guarantee that there is a single non-inline function definition provided elsewhere. PR c++/119154 gcc/cp/ChangeLog: * decl2.cc (vague_linkage_p): Revert gnu_linkage handling. * module.cc (importer_interface): New enumeration. (get_importer_interface): New function. (trees_out::core_bools): Use it to determine interface. (trees_in::is_matching_decl): Propagate gnu_inline handling onto existing forward declarations. (trees_in::read_var_def): Also note explicit instantiation definitions of variable templates to be emitted. gcc/testsuite/ChangeLog: * g++.dg/modules/pr119154_a.C: Move to... * g++.dg/modules/gnu-inline-1_a.C: ...here, and add decl. * g++.dg/modules/pr119154_b.C: Move to... * g++.dg/modules/gnu-inline-1_b.C: here, and add check. * g++.dg/modules/gnu-inline-1_c.C: New test. * g++.dg/modules/gnu-inline-1_d.C: New test. * g++.dg/modules/gnu-inline-2_a.C: New test. * g++.dg/modules/gnu-inline-2_b.C: New test. * g++.dg/modules/extern-tpl-3_a.C: New test. * g++.dg/modules/extern-tpl-3_b.C: New test. * g++.dg/modules/extern-tpl-4_a.H: New test. * g++.dg/modules/extern-tpl-4_b.C: New test. * g++.dg/modules/extern-tpl-4_c.C: New test. Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> --- gcc/cp/decl2.cc | 4 +- gcc/cp/module.cc | 69 +++++++++++++++- gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C | 11 +++ gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C | 12 +++ gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H | 22 +++++ gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C | 24 ++++++ gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C | 80 +++++++++++++++++++ gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C | 7 ++ gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C | 14 ++++ gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C | 16 ++++ gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C | 16 ++++ gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C | 11 +++ gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C | 14 ++++ gcc/testsuite/g++.dg/modules/pr119154_a.C | 6 -- gcc/testsuite/g++.dg/modules/pr119154_b.C | 10 --- 15 files changed, 293 insertions(+), 23 deletions(-) create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C delete mode 100644 gcc/testsuite/g++.dg/modules/pr119154_a.C delete mode 100644 gcc/testsuite/g++.dg/modules/pr119154_b.C diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 4a9fb1c3c00..fe9c56b6637 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -2482,9 +2482,7 @@ vague_linkage_p (tree decl) DECL_COMDAT. */ if (DECL_COMDAT (decl) || (TREE_CODE (decl) == FUNCTION_DECL - && DECL_DECLARED_INLINE_P (decl) - /* But gnu_inline functions are always external. */ - && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) + && DECL_DECLARED_INLINE_P (decl)) || (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INSTANTIATION (decl)) || (VAR_P (decl) && DECL_INLINE_VAR_P (decl))) diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 0d9e50bba7f..95f1ef0c77b 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -5541,6 +5541,50 @@ trees_in::start (unsigned code) return t; } +/* The kinds of interface an importer could have for a decl. */ + +enum class importer_interface { + unknown, /* The definition may or may not need to be emitted. */ + always_import, /* The definition can always be found in another TU. */ + always_emit, /* The definition must be emitted in the importer's TU. */ +}; + +/* Returns what kind of interface an importer will have of DECL. */ + +static importer_interface +get_importer_interface (tree decl) +{ + /* Internal linkage entities must be emitted in each importer if + there is a definition available. */ + if (!TREE_PUBLIC (decl)) + return importer_interface::always_emit; + + /* Entities that aren't vague linkage are either not definitions or + will be emitted in this TU, so importers can just refer to an + external definition. */ + if (!vague_linkage_p (decl)) + return importer_interface::always_import; + + /* For explicit instantiations, importers can always rely on there + being a definition in another TU, unless this is a definition + in a header module: in which case the importer will always need + to emit it. */ + if (DECL_LANG_SPECIFIC (decl) + && DECL_EXPLICIT_INSTANTIATION (decl)) + return (header_module_p () && !DECL_EXTERNAL (decl) + ? importer_interface::always_emit + : importer_interface::always_import); + + /* A gnu_inline function is never emitted in any TU. */ + if (TREE_CODE (decl) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (decl) + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) + return importer_interface::always_import; + + /* Everything else has vague linkage. */ + return importer_interface::unknown; +} + /* The structure streamers access the raw fields, because the alternative, of using the accessor macros can require using different accessors for the same underlying field, depending on the @@ -5660,7 +5704,8 @@ trees_out::core_bools (tree t, bits_out& bits) we need to import or export any vague-linkage entities on stream-in. */ bool interface_known = t->decl_common.lang_flag_5; - if (interface_known && vague_linkage_p (t)) + if (interface_known + && get_importer_interface (t) == importer_interface::unknown) interface_known = false; WB (interface_known); } @@ -5694,8 +5739,8 @@ trees_out::core_bools (tree t, bits_out& bits) is_external = true; gcc_fallthrough (); case FUNCTION_DECL: - if (TREE_PUBLIC (t) - && !vague_linkage_p (t)) + if (get_importer_interface (t) + == importer_interface::always_import) is_external = true; break; } @@ -12159,7 +12204,21 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) if (TREE_CODE (d_inner) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (d_inner)) - DECL_DECLARED_INLINE_P (e_inner) = true; + { + DECL_DECLARED_INLINE_P (e_inner) = true; + if (!DECL_SAVED_TREE (e_inner) + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (d_inner)) + && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (e_inner))) + { + DECL_INTERFACE_KNOWN (e_inner) + |= DECL_INTERFACE_KNOWN (d_inner); + DECL_DISREGARD_INLINE_LIMITS (e_inner) + |= DECL_DISREGARD_INLINE_LIMITS (d_inner); + // TODO: we will eventually want to merge all decl attributes + duplicate_one_attribute (&DECL_ATTRIBUTES (e_inner), + DECL_ATTRIBUTES (d_inner), "gnu_inline"); + } + } if (!DECL_EXTERNAL (d_inner)) DECL_EXTERNAL (e_inner) = false; @@ -12626,6 +12685,8 @@ trees_in::read_var_def (tree decl, tree maybe_template) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; tentative_decl_linkage (decl); if (DECL_IMPLICIT_INSTANTIATION (decl) + || (DECL_EXPLICIT_INSTANTIATION (decl) + && !DECL_EXTERNAL (decl)) || (DECL_CLASS_SCOPE_P (decl) && !DECL_VTABLE_OR_VTT_P (decl) && !DECL_TEMPLATE_INFO (decl))) diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C b/gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C new file mode 100644 index 00000000000..def3cd1413d --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C @@ -0,0 +1,11 @@ +// { dg-additional-options "-fmodules -Wno-global-module" } +// { dg-module-cmi M } + +module; +template <typename> +struct S { + S() {} +}; +export module M; +extern template class S<int>; +S<int> s; diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C b/gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C new file mode 100644 index 00000000000..5d96937ce02 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C @@ -0,0 +1,12 @@ +// { dg-additional-options "-fmodules" } + +template <typename> +struct S { + S() {} +}; + +void foo() { S<double> x;} + +import M; + +// Lazy loading of extern S<int> at EOF should not ICE diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H b/gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H new file mode 100644 index 00000000000..8238c2a8fab --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H @@ -0,0 +1,22 @@ +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi {} } + +template <typename T> inline void ha() {} +extern template void ha<int>(); +extern template void ha<bool>(); +template void ha<char>(); + +template <typename T> void hb() {} +extern template void hb<int>(); +extern template void hb<bool>(); +template void hb<char>(); + +template <typename T> inline int hc = 123; +extern template int hc<int>; +extern template int hc<bool>; +template int hc<char>; + +template <typename T> int hd = 123; +extern template int hd<int>; +extern template int hd<bool>; +template int hd<char>; diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C b/gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C new file mode 100644 index 00000000000..9b46f7fd943 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C @@ -0,0 +1,24 @@ +// { dg-additional-options "-fmodules" } +// { dg-module-cmi M } + +export module M; + +export template <typename T> inline void ma() {} +extern template void ma<int>(); +extern template void ma<bool>(); +template void ma<char>(); + +export template <typename T> void mb() {} +extern template void mb<int>(); +extern template void mb<bool>(); +template void mb<char>(); + +export template <typename T> inline int mc = 123; +extern template int mc<int>; +extern template int mc<bool>; +template int mc<char>; + +export template <typename T> int md = 123; +extern template int md<int>; +extern template int md<bool>; +template int md<char>; diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C b/gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C new file mode 100644 index 00000000000..71858978f77 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C @@ -0,0 +1,80 @@ +// { dg-additional-options "-fmodules" } + +import "extern-tpl-4_a.H"; +import M; + +int main() { + ha<int>(); + ha<char>(); + ha<double>(); + + ma<int>(); + ma<char>(); + ma<double>(); + + hb<int>(); + hb<char>(); + hb<double>(); + + mb<int>(); + mb<char>(); + mb<double>(); + + int x1 = hc<int> + hc<char> + hc<double>; + int x2 = hd<int> + hd<char> + hd<double>; + int x3 = mc<int> + mc<char> + mc<double>; + int x4 = md<int> + md<char> + md<double>; + return x1 + x2 + x3 + x4; +} + + +// 'int': imported explicit instantiation decls should not be emitted here: +// { dg-final { scan-assembler-not "_Z2haIiEvv:" } } +// { dg-final { scan-assembler-not "_Z2hbIiEvv:" } } +// { dg-final { scan-assembler-not "_Z2hcIiE:" } } +// { dg-final { scan-assembler-not "_Z2hdIiE:" } } +// { dg-final { scan-assembler-not "_ZW1M2maIiEvv:" } } +// { dg-final { scan-assembler-not "_ZW1M2mbIiEvv:" } } +// { dg-final { scan-assembler-not "_ZW1M2mcIiE:" } } +// { dg-final { scan-assembler-not "_ZW1M2mdIiE:" } } + +// 'char': explicit instantiation definitions don't need to be emitted for +// modules, but need to be emitted for header units (as there's no other TU): +// { dg-final { scan-assembler "_Z2haIcEvv:" } } +// { dg-final { scan-assembler "_Z2hbIcEvv:" } } +// { dg-final { scan-assembler "_Z2hcIcE:" } } +// { dg-final { scan-assembler "_Z2hdIcE:" } } +// { dg-final { scan-assembler-not "_ZW1M2maIcEvv:" } } +// { dg-final { scan-assembler-not "_ZW1M2mbIcEvv:" } } +// { dg-final { scan-assembler-not "_ZW1M2mcIcE:" } } +// { dg-final { scan-assembler-not "_ZW1M2mdIcE:" } } + +// 'double': these are not explicitly instantiated and should be emitted here: +// { dg-final { scan-assembler "_Z2haIdEvv:" } } +// { dg-final { scan-assembler "_Z2hbIdEvv:" } } +// { dg-final { scan-assembler "_Z2hcIdE:" } } +// { dg-final { scan-assembler "_Z2hdIdE:" } } +// { dg-final { scan-assembler "_ZW1M2maIdEvv:" } } +// { dg-final { scan-assembler "_ZW1M2mbIdEvv:" } } +// { dg-final { scan-assembler "_ZW1M2mcIdE:" } } +// { dg-final { scan-assembler "_ZW1M2mdIdE:" } } + +template void ha<bool>(); +template void hb<bool>(); +template int hc<bool>; +template int hd<bool>; + +template void ma<bool>(); +template void mb<bool>(); +template int mc<bool>; +template int md<bool>; + +// 'bool': instantiated in this file, and so must be emitted here: +// { dg-final { scan-assembler "_Z2haIbEvv:" } } +// { dg-final { scan-assembler "_Z2hbIbEvv:" } } +// { dg-final { scan-assembler "_Z2hcIbE:" } } +// { dg-final { scan-assembler "_Z2hdIbE:" } } +// { dg-final { scan-assembler "_ZW1M2maIbEvv:" } } +// { dg-final { scan-assembler "_ZW1M2mbIbEvv:" } } +// { dg-final { scan-assembler "_ZW1M2mcIbE:" } } +// { dg-final { scan-assembler "_ZW1M2mdIbE:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C new file mode 100644 index 00000000000..41a1b2850d4 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C @@ -0,0 +1,7 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } +// { dg-module-cmi foo } + +export module foo; +export extern "C++" inline __attribute__((__gnu_inline__)) void bar() {} +export extern "C++" inline __attribute__((__gnu_inline__)) void decl(); diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C new file mode 100644 index 00000000000..a91486d8403 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C @@ -0,0 +1,14 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +void bar(); +void decl(); // { dg-warning "used but never defined" } +import foo; + +void test_b() { + bar(); + decl(); +} + +// A function only defined with gnu_inline should not be emitted here. +// { dg-final { scan-assembler-not "_Z3barv:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C new file mode 100644 index 00000000000..34dde878745 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C @@ -0,0 +1,16 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +void bar() {} +void decl() {} +import foo; + +void test_c() { + bar(); + decl(); +}; + +// Make sure importing a gnu_inline definition didn't stop us from emitting +// the non-gnu_inline definition we had before the module import. +// { dg-final { scan-assembler "_Z3barv:" } } +// { dg-final { scan-assembler "_Z4declv:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C new file mode 100644 index 00000000000..5b5d802dcd9 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C @@ -0,0 +1,16 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules -fno-module-lazy" } + +import foo; +void bar() {} +void decl() {} + +void test_c() { + bar(); + decl(); +}; + +// Make sure importing a gnu_inline definition didn't stop us from emitting +// the non-gnu_inline definition we had after the module import. +// { dg-final { scan-assembler "_Z3barv:" } } +// { dg-final { scan-assembler "_Z4declv:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C new file mode 100644 index 00000000000..7f59fb7f716 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C @@ -0,0 +1,11 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } +// { dg-module-cmi xstd } + +export module xstd; + +inline __attribute__((__gnu_inline__)) void wmemset() {} + +extern "C++" template <class> struct char_traits { + void assign() { wmemset(); } +}; diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C new file mode 100644 index 00000000000..e2f12d2d0b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C @@ -0,0 +1,14 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +template <typename> struct char_traits { + void assign(); +}; + +void foo(char_traits<wchar_t> s) { + s.assign(); +} + +import xstd; + +// Lazy loading at EOF of a gnu_inline declaration should not ICE. diff --git a/gcc/testsuite/g++.dg/modules/pr119154_a.C b/gcc/testsuite/g++.dg/modules/pr119154_a.C deleted file mode 100644 index 23bd186fe19..00000000000 --- a/gcc/testsuite/g++.dg/modules/pr119154_a.C +++ /dev/null @@ -1,6 +0,0 @@ -// PR c++/119154 -// { dg-additional-options "-fmodules" } -// { dg-module-cmi foo } - -export module foo; -extern "C++" inline __attribute__((__gnu_inline__)) void bar() {} diff --git a/gcc/testsuite/g++.dg/modules/pr119154_b.C b/gcc/testsuite/g++.dg/modules/pr119154_b.C deleted file mode 100644 index 1558e717761..00000000000 --- a/gcc/testsuite/g++.dg/modules/pr119154_b.C +++ /dev/null @@ -1,10 +0,0 @@ -// PR c++/119154 -// { dg-module-do link } -// { dg-additional-options "-fmodules" } - -void bar(); -import foo; - -int main() { - bar(); -}
On 3/22/25 8:37 AM, Nathaniel Shead wrote: > On Mon, Mar 17, 2025 at 09:42:13AM -0400, Jason Merrill wrote: >> On 3/14/25 9:28 AM, Nathaniel Shead wrote: >>> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? >>> >>> Alternatively, could still mark gnu_inline functions as non-vague, we >>> just need to do so more aggressively; but given this is specifically to >>> solve a modules issue I felt may as well keep it confined to there given >>> your previous comments. >> >> I think we want a function import_interface_unknown or import_might_emit or >> something that is vague_linkage_p with additional restrictions. >> >> As I think of it, an explicitly instantiated template is still vague >> linkage, but vague linkage is subject to various heuristics and controls >> like explicit instantiation and the ABI tinfo rule. The ABI spec recently >> drifted away from this interpretation toward vague linkage meaning COMDAT, >> but I think that was a conceptual mistake. >> > > Makes sense. > >> gnu_inline seems borderline to me, but I lean toward the change in this >> patch to treat all inlines as vague linkage and check gnu_inline in the new >> function. >> >> Jason >> > > I found the clearest way to go was to make the function a tristate > (vague, always external, always emitted by importer) to handle header > units more naturally. > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk? OK. > -- >8 -- > > Subject: [PATCH] c++/modules: Fix explicit instantiations and gnu_inlines > [PR119154] > > My change in r15-8012 for PR c++/119154 exposed a bug with explicit > instantation declarations. The change cleared DECL_INTERFACE_KNOWN for > all vague-linkage entities, including explicit instantiations. When we > then perform lazy loading at EOF (due to processing deferred function > bodies), expand_or_defer_fn ends up calling import_export_decl which > will error because DECL_INTERFACE_KNOWN is still unset but no definition > is available in the file, violating some assertions. > > It turns out that for function templates marked inline we would not > respect an 'extern template' imported in general, either; this patch > fixes both of these issues by always treating explicit instantiations as > external, and so marking DECL_INTERFACE_KNOWN eagerly. > > For an explicit instantiation declaration we don't want to emit the body > of the function as it must be emitted in a different TU anyway. And for > explicit instantiation definitions we similarly know that it will have > been emitted in the interface TU we streamed it in from, so there's > no need to emit it. > > The same error can happen with lazy-loaded gnu_inlines at EOF; in some > cases they'll be marked DECL_COMDAT and pass through the vague_linkage_p > check anyway. This patch reworks the handling of gnu_inlines to ensure > that both DECL_INTERFACE_KNOWN is always correctly set and that > importing a gnu_inline function over the top of an existing forward > declaration works correctly. > > The other case that duplicate_decls handles (importing a regular > definition over the top of a gnu_inline function) doesn't seem like > something we need to handle specially in modules; we'll just use the > existing gnu_inline function and rely on the guarantee that there is a > single non-inline function definition provided elsewhere. > > PR c++/119154 > > gcc/cp/ChangeLog: > > * decl2.cc (vague_linkage_p): Revert gnu_linkage handling. > * module.cc (importer_interface): New enumeration. > (get_importer_interface): New function. > (trees_out::core_bools): Use it to determine interface. > (trees_in::is_matching_decl): Propagate gnu_inline handling onto > existing forward declarations. > (trees_in::read_var_def): Also note explicit instantiation > definitions of variable templates to be emitted. > > gcc/testsuite/ChangeLog: > > * g++.dg/modules/pr119154_a.C: Move to... > * g++.dg/modules/gnu-inline-1_a.C: ...here, and add decl. > * g++.dg/modules/pr119154_b.C: Move to... > * g++.dg/modules/gnu-inline-1_b.C: here, and add check. > * g++.dg/modules/gnu-inline-1_c.C: New test. > * g++.dg/modules/gnu-inline-1_d.C: New test. > * g++.dg/modules/gnu-inline-2_a.C: New test. > * g++.dg/modules/gnu-inline-2_b.C: New test. > * g++.dg/modules/extern-tpl-3_a.C: New test. > * g++.dg/modules/extern-tpl-3_b.C: New test. > * g++.dg/modules/extern-tpl-4_a.H: New test. > * g++.dg/modules/extern-tpl-4_b.C: New test. > * g++.dg/modules/extern-tpl-4_c.C: New test. > > Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com> > --- > gcc/cp/decl2.cc | 4 +- > gcc/cp/module.cc | 69 +++++++++++++++- > gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C | 11 +++ > gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C | 12 +++ > gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H | 22 +++++ > gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C | 24 ++++++ > gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C | 80 +++++++++++++++++++ > gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C | 7 ++ > gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C | 14 ++++ > gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C | 16 ++++ > gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C | 16 ++++ > gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C | 11 +++ > gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C | 14 ++++ > gcc/testsuite/g++.dg/modules/pr119154_a.C | 6 -- > gcc/testsuite/g++.dg/modules/pr119154_b.C | 10 --- > 15 files changed, 293 insertions(+), 23 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C > create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C > create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H > create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C > create mode 100644 gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C > create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C > create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C > create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C > create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C > create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C > create mode 100644 gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C > delete mode 100644 gcc/testsuite/g++.dg/modules/pr119154_a.C > delete mode 100644 gcc/testsuite/g++.dg/modules/pr119154_b.C > > diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc > index 4a9fb1c3c00..fe9c56b6637 100644 > --- a/gcc/cp/decl2.cc > +++ b/gcc/cp/decl2.cc > @@ -2482,9 +2482,7 @@ vague_linkage_p (tree decl) > DECL_COMDAT. */ > if (DECL_COMDAT (decl) > || (TREE_CODE (decl) == FUNCTION_DECL > - && DECL_DECLARED_INLINE_P (decl) > - /* But gnu_inline functions are always external. */ > - && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) > + && DECL_DECLARED_INLINE_P (decl)) > || (DECL_LANG_SPECIFIC (decl) > && DECL_TEMPLATE_INSTANTIATION (decl)) > || (VAR_P (decl) && DECL_INLINE_VAR_P (decl))) > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc > index 0d9e50bba7f..95f1ef0c77b 100644 > --- a/gcc/cp/module.cc > +++ b/gcc/cp/module.cc > @@ -5541,6 +5541,50 @@ trees_in::start (unsigned code) > return t; > } > > +/* The kinds of interface an importer could have for a decl. */ > + > +enum class importer_interface { > + unknown, /* The definition may or may not need to be emitted. */ > + always_import, /* The definition can always be found in another TU. */ > + always_emit, /* The definition must be emitted in the importer's TU. */ > +}; > + > +/* Returns what kind of interface an importer will have of DECL. */ > + > +static importer_interface > +get_importer_interface (tree decl) > +{ > + /* Internal linkage entities must be emitted in each importer if > + there is a definition available. */ > + if (!TREE_PUBLIC (decl)) > + return importer_interface::always_emit; > + > + /* Entities that aren't vague linkage are either not definitions or > + will be emitted in this TU, so importers can just refer to an > + external definition. */ > + if (!vague_linkage_p (decl)) > + return importer_interface::always_import; > + > + /* For explicit instantiations, importers can always rely on there > + being a definition in another TU, unless this is a definition > + in a header module: in which case the importer will always need > + to emit it. */ > + if (DECL_LANG_SPECIFIC (decl) > + && DECL_EXPLICIT_INSTANTIATION (decl)) > + return (header_module_p () && !DECL_EXTERNAL (decl) > + ? importer_interface::always_emit > + : importer_interface::always_import); > + > + /* A gnu_inline function is never emitted in any TU. */ > + if (TREE_CODE (decl) == FUNCTION_DECL > + && DECL_DECLARED_INLINE_P (decl) > + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) > + return importer_interface::always_import; > + > + /* Everything else has vague linkage. */ > + return importer_interface::unknown; > +} > + > /* The structure streamers access the raw fields, because the > alternative, of using the accessor macros can require using > different accessors for the same underlying field, depending on the > @@ -5660,7 +5704,8 @@ trees_out::core_bools (tree t, bits_out& bits) > we need to import or export any vague-linkage entities on > stream-in. */ > bool interface_known = t->decl_common.lang_flag_5; > - if (interface_known && vague_linkage_p (t)) > + if (interface_known > + && get_importer_interface (t) == importer_interface::unknown) > interface_known = false; > WB (interface_known); > } > @@ -5694,8 +5739,8 @@ trees_out::core_bools (tree t, bits_out& bits) > is_external = true; > gcc_fallthrough (); > case FUNCTION_DECL: > - if (TREE_PUBLIC (t) > - && !vague_linkage_p (t)) > + if (get_importer_interface (t) > + == importer_interface::always_import) > is_external = true; > break; > } > @@ -12159,7 +12204,21 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) > > if (TREE_CODE (d_inner) == FUNCTION_DECL > && DECL_DECLARED_INLINE_P (d_inner)) > - DECL_DECLARED_INLINE_P (e_inner) = true; > + { > + DECL_DECLARED_INLINE_P (e_inner) = true; > + if (!DECL_SAVED_TREE (e_inner) > + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (d_inner)) > + && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (e_inner))) > + { > + DECL_INTERFACE_KNOWN (e_inner) > + |= DECL_INTERFACE_KNOWN (d_inner); > + DECL_DISREGARD_INLINE_LIMITS (e_inner) > + |= DECL_DISREGARD_INLINE_LIMITS (d_inner); > + // TODO: we will eventually want to merge all decl attributes > + duplicate_one_attribute (&DECL_ATTRIBUTES (e_inner), > + DECL_ATTRIBUTES (d_inner), "gnu_inline"); > + } > + } > if (!DECL_EXTERNAL (d_inner)) > DECL_EXTERNAL (e_inner) = false; > > @@ -12626,6 +12685,8 @@ trees_in::read_var_def (tree decl, tree maybe_template) > DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; > tentative_decl_linkage (decl); > if (DECL_IMPLICIT_INSTANTIATION (decl) > + || (DECL_EXPLICIT_INSTANTIATION (decl) > + && !DECL_EXTERNAL (decl)) > || (DECL_CLASS_SCOPE_P (decl) > && !DECL_VTABLE_OR_VTT_P (decl) > && !DECL_TEMPLATE_INFO (decl))) > diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C b/gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C > new file mode 100644 > index 00000000000..def3cd1413d > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-3_a.C > @@ -0,0 +1,11 @@ > +// { dg-additional-options "-fmodules -Wno-global-module" } > +// { dg-module-cmi M } > + > +module; > +template <typename> > +struct S { > + S() {} > +}; > +export module M; > +extern template class S<int>; > +S<int> s; > diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C b/gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C > new file mode 100644 > index 00000000000..5d96937ce02 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-3_b.C > @@ -0,0 +1,12 @@ > +// { dg-additional-options "-fmodules" } > + > +template <typename> > +struct S { > + S() {} > +}; > + > +void foo() { S<double> x;} > + > +import M; > + > +// Lazy loading of extern S<int> at EOF should not ICE > diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H b/gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H > new file mode 100644 > index 00000000000..8238c2a8fab > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-4_a.H > @@ -0,0 +1,22 @@ > +// { dg-additional-options "-fmodule-header" } > +// { dg-module-cmi {} } > + > +template <typename T> inline void ha() {} > +extern template void ha<int>(); > +extern template void ha<bool>(); > +template void ha<char>(); > + > +template <typename T> void hb() {} > +extern template void hb<int>(); > +extern template void hb<bool>(); > +template void hb<char>(); > + > +template <typename T> inline int hc = 123; > +extern template int hc<int>; > +extern template int hc<bool>; > +template int hc<char>; > + > +template <typename T> int hd = 123; > +extern template int hd<int>; > +extern template int hd<bool>; > +template int hd<char>; > diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C b/gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C > new file mode 100644 > index 00000000000..9b46f7fd943 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-4_b.C > @@ -0,0 +1,24 @@ > +// { dg-additional-options "-fmodules" } > +// { dg-module-cmi M } > + > +export module M; > + > +export template <typename T> inline void ma() {} > +extern template void ma<int>(); > +extern template void ma<bool>(); > +template void ma<char>(); > + > +export template <typename T> void mb() {} > +extern template void mb<int>(); > +extern template void mb<bool>(); > +template void mb<char>(); > + > +export template <typename T> inline int mc = 123; > +extern template int mc<int>; > +extern template int mc<bool>; > +template int mc<char>; > + > +export template <typename T> int md = 123; > +extern template int md<int>; > +extern template int md<bool>; > +template int md<char>; > diff --git a/gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C b/gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C > new file mode 100644 > index 00000000000..71858978f77 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/extern-tpl-4_c.C > @@ -0,0 +1,80 @@ > +// { dg-additional-options "-fmodules" } > + > +import "extern-tpl-4_a.H"; > +import M; > + > +int main() { > + ha<int>(); > + ha<char>(); > + ha<double>(); > + > + ma<int>(); > + ma<char>(); > + ma<double>(); > + > + hb<int>(); > + hb<char>(); > + hb<double>(); > + > + mb<int>(); > + mb<char>(); > + mb<double>(); > + > + int x1 = hc<int> + hc<char> + hc<double>; > + int x2 = hd<int> + hd<char> + hd<double>; > + int x3 = mc<int> + mc<char> + mc<double>; > + int x4 = md<int> + md<char> + md<double>; > + return x1 + x2 + x3 + x4; > +} > + > + > +// 'int': imported explicit instantiation decls should not be emitted here: > +// { dg-final { scan-assembler-not "_Z2haIiEvv:" } } > +// { dg-final { scan-assembler-not "_Z2hbIiEvv:" } } > +// { dg-final { scan-assembler-not "_Z2hcIiE:" } } > +// { dg-final { scan-assembler-not "_Z2hdIiE:" } } > +// { dg-final { scan-assembler-not "_ZW1M2maIiEvv:" } } > +// { dg-final { scan-assembler-not "_ZW1M2mbIiEvv:" } } > +// { dg-final { scan-assembler-not "_ZW1M2mcIiE:" } } > +// { dg-final { scan-assembler-not "_ZW1M2mdIiE:" } } > + > +// 'char': explicit instantiation definitions don't need to be emitted for > +// modules, but need to be emitted for header units (as there's no other TU): > +// { dg-final { scan-assembler "_Z2haIcEvv:" } } > +// { dg-final { scan-assembler "_Z2hbIcEvv:" } } > +// { dg-final { scan-assembler "_Z2hcIcE:" } } > +// { dg-final { scan-assembler "_Z2hdIcE:" } } > +// { dg-final { scan-assembler-not "_ZW1M2maIcEvv:" } } > +// { dg-final { scan-assembler-not "_ZW1M2mbIcEvv:" } } > +// { dg-final { scan-assembler-not "_ZW1M2mcIcE:" } } > +// { dg-final { scan-assembler-not "_ZW1M2mdIcE:" } } > + > +// 'double': these are not explicitly instantiated and should be emitted here: > +// { dg-final { scan-assembler "_Z2haIdEvv:" } } > +// { dg-final { scan-assembler "_Z2hbIdEvv:" } } > +// { dg-final { scan-assembler "_Z2hcIdE:" } } > +// { dg-final { scan-assembler "_Z2hdIdE:" } } > +// { dg-final { scan-assembler "_ZW1M2maIdEvv:" } } > +// { dg-final { scan-assembler "_ZW1M2mbIdEvv:" } } > +// { dg-final { scan-assembler "_ZW1M2mcIdE:" } } > +// { dg-final { scan-assembler "_ZW1M2mdIdE:" } } > + > +template void ha<bool>(); > +template void hb<bool>(); > +template int hc<bool>; > +template int hd<bool>; > + > +template void ma<bool>(); > +template void mb<bool>(); > +template int mc<bool>; > +template int md<bool>; > + > +// 'bool': instantiated in this file, and so must be emitted here: > +// { dg-final { scan-assembler "_Z2haIbEvv:" } } > +// { dg-final { scan-assembler "_Z2hbIbEvv:" } } > +// { dg-final { scan-assembler "_Z2hcIbE:" } } > +// { dg-final { scan-assembler "_Z2hdIbE:" } } > +// { dg-final { scan-assembler "_ZW1M2maIbEvv:" } } > +// { dg-final { scan-assembler "_ZW1M2mbIbEvv:" } } > +// { dg-final { scan-assembler "_ZW1M2mcIbE:" } } > +// { dg-final { scan-assembler "_ZW1M2mdIbE:" } } > diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C > new file mode 100644 > index 00000000000..41a1b2850d4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C > @@ -0,0 +1,7 @@ > +// PR c++/119154 > +// { dg-additional-options "-fmodules" } > +// { dg-module-cmi foo } > + > +export module foo; > +export extern "C++" inline __attribute__((__gnu_inline__)) void bar() {} > +export extern "C++" inline __attribute__((__gnu_inline__)) void decl(); > diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C > new file mode 100644 > index 00000000000..a91486d8403 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C > @@ -0,0 +1,14 @@ > +// PR c++/119154 > +// { dg-additional-options "-fmodules" } > + > +void bar(); > +void decl(); // { dg-warning "used but never defined" } > +import foo; > + > +void test_b() { > + bar(); > + decl(); > +} > + > +// A function only defined with gnu_inline should not be emitted here. > +// { dg-final { scan-assembler-not "_Z3barv:" } } > diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C > new file mode 100644 > index 00000000000..34dde878745 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C > @@ -0,0 +1,16 @@ > +// PR c++/119154 > +// { dg-additional-options "-fmodules" } > + > +void bar() {} > +void decl() {} > +import foo; > + > +void test_c() { > + bar(); > + decl(); > +}; > + > +// Make sure importing a gnu_inline definition didn't stop us from emitting > +// the non-gnu_inline definition we had before the module import. > +// { dg-final { scan-assembler "_Z3barv:" } } > +// { dg-final { scan-assembler "_Z4declv:" } } > diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C > new file mode 100644 > index 00000000000..5b5d802dcd9 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_d.C > @@ -0,0 +1,16 @@ > +// PR c++/119154 > +// { dg-additional-options "-fmodules -fno-module-lazy" } > + > +import foo; > +void bar() {} > +void decl() {} > + > +void test_c() { > + bar(); > + decl(); > +}; > + > +// Make sure importing a gnu_inline definition didn't stop us from emitting > +// the non-gnu_inline definition we had after the module import. > +// { dg-final { scan-assembler "_Z3barv:" } } > +// { dg-final { scan-assembler "_Z4declv:" } } > diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C > new file mode 100644 > index 00000000000..7f59fb7f716 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C > @@ -0,0 +1,11 @@ > +// PR c++/119154 > +// { dg-additional-options "-fmodules" } > +// { dg-module-cmi xstd } > + > +export module xstd; > + > +inline __attribute__((__gnu_inline__)) void wmemset() {} > + > +extern "C++" template <class> struct char_traits { > + void assign() { wmemset(); } > +}; > diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C > new file mode 100644 > index 00000000000..e2f12d2d0b6 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C > @@ -0,0 +1,14 @@ > +// PR c++/119154 > +// { dg-additional-options "-fmodules" } > + > +template <typename> struct char_traits { > + void assign(); > +}; > + > +void foo(char_traits<wchar_t> s) { > + s.assign(); > +} > + > +import xstd; > + > +// Lazy loading at EOF of a gnu_inline declaration should not ICE. > diff --git a/gcc/testsuite/g++.dg/modules/pr119154_a.C b/gcc/testsuite/g++.dg/modules/pr119154_a.C > deleted file mode 100644 > index 23bd186fe19..00000000000 > --- a/gcc/testsuite/g++.dg/modules/pr119154_a.C > +++ /dev/null > @@ -1,6 +0,0 @@ > -// PR c++/119154 > -// { dg-additional-options "-fmodules" } > -// { dg-module-cmi foo } > - > -export module foo; > -extern "C++" inline __attribute__((__gnu_inline__)) void bar() {} > diff --git a/gcc/testsuite/g++.dg/modules/pr119154_b.C b/gcc/testsuite/g++.dg/modules/pr119154_b.C > deleted file mode 100644 > index 1558e717761..00000000000 > --- a/gcc/testsuite/g++.dg/modules/pr119154_b.C > +++ /dev/null > @@ -1,10 +0,0 @@ > -// PR c++/119154 > -// { dg-module-do link } > -// { dg-additional-options "-fmodules" } > - > -void bar(); > -import foo; > - > -int main() { > - bar(); > -}
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 4a9fb1c3c00..fe9c56b6637 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -2482,9 +2482,7 @@ vague_linkage_p (tree decl) DECL_COMDAT. */ if (DECL_COMDAT (decl) || (TREE_CODE (decl) == FUNCTION_DECL - && DECL_DECLARED_INLINE_P (decl) - /* But gnu_inline functions are always external. */ - && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) + && DECL_DECLARED_INLINE_P (decl)) || (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INSTANTIATION (decl)) || (VAR_P (decl) && DECL_INLINE_VAR_P (decl))) diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 7440a9015b4..320e89ffdba 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -5660,11 +5660,17 @@ trees_out::core_bools (tree t, bits_out& bits) we need to import or export any vague-linkage entities on stream-in. */ bool interface_known = t->decl_common.lang_flag_5; - if (interface_known && vague_linkage_p (t) + if (interface_known + && vague_linkage_p (t) /* But explicit instantiations are not vague linkage; we can always rely on there being a definition in another TU. */ && !(DECL_LANG_SPECIFIC (t) - && DECL_EXPLICIT_INSTANTIATION (t))) + && DECL_EXPLICIT_INSTANTIATION (t)) + /* A gnu_inline function is also not vague linkage; we never want + to emit it in any TU. */ + && !(TREE_CODE (t) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (t) + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (t)))) interface_known = false; WB (interface_known); } @@ -12555,6 +12561,23 @@ trees_in::read_function_def (tree decl, tree maybe_template) DECL_INITIAL (decl) = initial; DECL_SAVED_TREE (decl) = saved; + /* If we're importing a gnu_inline definition on top of a forward decl, + propagate the appropriate flags and information. We can't do this + in is_matching_decl because we don't know whether we'll get a + definition at that point. */ + if (maybe_dup != decl + && DECL_DECLARED_INLINE_P (maybe_dup) + && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (maybe_dup)) + && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) + { + DECL_INTERFACE_KNOWN (decl) + |= DECL_INTERFACE_KNOWN (maybe_dup); + DECL_DISREGARD_INLINE_LIMITS (decl) + |= DECL_DISREGARD_INLINE_LIMITS (maybe_dup); + duplicate_one_attribute (&DECL_ATTRIBUTES (decl), + DECL_ATTRIBUTES (maybe_dup), "gnu_inline"); + } + if (context) SET_DECL_FRIEND_CONTEXT (decl, context); if (cexpr.decl) diff --git a/gcc/testsuite/g++.dg/modules/pr119154_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C similarity index 100% rename from gcc/testsuite/g++.dg/modules/pr119154_a.C rename to gcc/testsuite/g++.dg/modules/gnu-inline-1_a.C diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C new file mode 100644 index 00000000000..52e52aed9db --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_b.C @@ -0,0 +1,12 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +void bar(); +import foo; + +void test_b() { + bar(); +} + +// A function only defined with gnu_inline should not be emitted here. +// { dg-final { scan-assembler-not "_Z3barv:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C new file mode 100644 index 00000000000..a28f8d972a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-1_c.C @@ -0,0 +1,13 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +void bar() {} +import foo; + +void test_c() { + bar(); +}; + +// Make sure importing a gnu_inline definition didn't stop us from emitting +// the non-gnu_inline definition we had before the module import. +// { dg-final { scan-assembler "_Z3barv:" } } diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C new file mode 100644 index 00000000000..7f59fb7f716 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_a.C @@ -0,0 +1,11 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } +// { dg-module-cmi xstd } + +export module xstd; + +inline __attribute__((__gnu_inline__)) void wmemset() {} + +extern "C++" template <class> struct char_traits { + void assign() { wmemset(); } +}; diff --git a/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C new file mode 100644 index 00000000000..e2f12d2d0b6 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/gnu-inline-2_b.C @@ -0,0 +1,14 @@ +// PR c++/119154 +// { dg-additional-options "-fmodules" } + +template <typename> struct char_traits { + void assign(); +}; + +void foo(char_traits<wchar_t> s) { + s.assign(); +} + +import xstd; + +// Lazy loading at EOF of a gnu_inline declaration should not ICE. diff --git a/gcc/testsuite/g++.dg/modules/pr119154_b.C b/gcc/testsuite/g++.dg/modules/pr119154_b.C deleted file mode 100644 index 1558e717761..00000000000 --- a/gcc/testsuite/g++.dg/modules/pr119154_b.C +++ /dev/null @@ -1,10 +0,0 @@ -// PR c++/119154 -// { dg-module-do link } -// { dg-additional-options "-fmodules" } - -void bar(); -import foo; - -int main() { - bar(); -}