https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124311

            Bug ID: 124311
           Summary: [modules] Defaulted-in-class special member re-emitted
                    across modules causes "cannot be overloaded with
                    itself"
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: kachalenko.denis at gmail dot com
  Target Milestone: ---

When a struct with an explicitly defaulted special member function
(= default; in the class body) is exported from module A, and an
importing module B triggers synthesis of that member, a downstream
consumer that loads both modules gets:

  error: 'constexpr S@A::S()' cannot be overloaded with 'constexpr S@A::S()'

This is a regression introduced by commit 5b85364a6dd0 (PR c++/120499).
Reproduces with -std=c++20, -std=c++23, and -std=c++26.


Reproducer (11 lines, 3 files)
==============================

// a.cppm
export module A;
export struct S {
        int x = 0;
        S() = default;
};

// b.cppm
export module B;
export import A;
export struct M { S h; };
M m;

// t.cpp
import B;
M m2;

$ g++ -std=c++26 -fmodules -c a.cppm   # OK
$ g++ -std=c++26 -fmodules -c b.cppm   # OK
$ g++ -std=c++26 -fmodules -c t.cpp    # ERROR


Error output
============

In module A, imported at b.cppm:2,
of module B, imported at t.cpp:1:
a.cppm:4:9: error: 'constexpr S@A::S()' cannot be overloaded with 'constexpr
S@A::S()'
    4 |         S() = default;
      |         ^
a.cppm:4:9: note: previous declaration 'constexpr S@A::S()'
a.cppm:4:9: error: 'constexpr S@A::S()' cannot be overloaded with 'constexpr
S@A::S()'
a.cppm:4:9: note: previous declaration 'constexpr S@A::S()'
t.cpp:2:1: note: during load of binding '::M@B'
    2 | M m2;
      | ^


Version
=======

gcc version 16.0.1 20260221 (experimental) (GCC)
Target: x86_64-w64-mingw32


Essentiality
============

Every element is required; removing any one makes it compile:

  - Remove `S() = default;`  --> OK (implicit ctor, lazy flag stays true)
  - Remove `int x = 0;`      --> OK (no NSDMI, ctor not constexpr)
  - Remove `M m;`            --> OK (no variable forces synthesis in B)
  - Remove wrapper struct M  --> OK (S s; alone doesn't trigger)
  - Move = default outside class  --> OK (not defaulted-in-class)

`export import` is not essential; plain `import A` still triggers the bug
as long as B re-emits S's ctor and the consumer loads the relevant section.


Regression
==========

Regressed with commit 5b85364a6dd0bbfd3e26d3346b075a0819be7cd4:

  c++/modules: Provide definitions of synthesized methods outside their
  defining module [PR120499]

      * method.cc (synthesize_method): Set the instantiating module.

That commit added two lines to synthesize_method in method.cc:

    finish_function_body (stmt);
    finish_function (/*inline_p=*/false);

  + /* Remember that we were defined in this module.  */
  + set_instantiating_module (fndecl);

    if (!DECL_DELETED_FN (fndecl))
      expand_or_defer_fn (fndecl);


Root cause analysis
===================

Writer side (module B)
----------------------

1. Module B imports A.  S::S() is loaded as entity @A:1 with
   DECL_MODULE_IMPORT_P = true.  Because S() = default; is an explicit
   declaration, CLASSTYPE_LAZY_DEFAULT_CTOR(S) = false.

2. `M m;` forces synthesis of M::M(), which in turn triggers
   synthesize_method(S::S()).

3. set_instantiating_module(fndecl) unconditionally clears
   DECL_MODULE_IMPORT_P (module.cc:21969).

4. set_defining_module(fndecl) sees the class context is imported
   (module.cc:21991) and adds the ctor to class_members.

5. add_class_entities creates a non-imported depset via make_dependency
   (because DECL_MODULE_IMPORT_P is now false).

6. B.gcm gets a separate section:1 for S@A:1::__ct with its own
   definition -- a duplicate of what's already in A.gcm's class
   definition of S.

-fdump-lang-module evidence -- A.gcm writes S::__ct as a declaration
only (body not separately streamed because DECL_SAVED_TREE is null
for uninstantiated defaulted members):

  Depset:1 decl entity:1 function_decl:'::S::__ct '
  Writing class definition '::S'

B.gcm re-emits S::__ct with a full definition:

  Writing section:1 1 depsets
   Depset:0 decl entity:0 function_decl:'::S@A:1::__ct '
   Writing definition '::S@A:1::__ct '

Reader side (consumer t.cpp)
----------------------------

7. t.cpp imports B (which re-exports A).  The reader first loads A.gcm:
   S's class definition is read, CLASSTYPE_LAZY_DEFAULT_CTOR(S) becomes
   false, and S::S() is installed as a class member.

8. Then B.gcm section:1 is read.  The reader encounters S::S() as a
   new entity and calls install_implicit_member (module.cc:9024).

9. install_implicit_member checks CLASSTYPE_LAZY_DEFAULT_CTOR(S) --
   it's already false (ctor was installed from A.gcm).  Returns false.
   set_overrun() triggers the "cannot be overloaded with itself" error.


Why PR120499's fix is correct for its case but not ours
-------------------------------------------------------

PR120499 fixed a linker error where _Vector_impl<int>::~_Vector_impl()
was never emitted.  In that case:

  - The destructor was implicitly declared (not = default; in class body)
  - Module A (std) never synthesized it --> no definition in A.gcm
  - CLASSTYPE_LAZY_DESTRUCTOR = true in A.gcm
  - Module B synthesizes it --> set_instantiating_module clears import flag
    --> B.gcm streams the definition --> C can use it
  - Reader: install_implicit_member finds CLASSTYPE_LAZY_DESTRUCTOR = true
    --> installs successfully

In our case:

  - The ctor is explicitly defaulted (S() = default;) in the class body
  - A.gcm has S's class definition which includes the ctor declaration
  - CLASSTYPE_LAZY_DEFAULT_CTOR = false in A.gcm
  - B re-emits the ctor --> C gets two definitions --> reader fails

The fix clears DECL_MODULE_IMPORT_P unconditionally, but it should only
do so when the member's definition is not already available from the
home module's class definition.


Suggested fix
=============

Guard set_instantiating_module in synthesize_method to skip members that
are explicitly defaulted in the class body of an imported class.  Such
members have CLASSTYPE_LAZY_* already false in the home module's CMI,
so re-emitting them causes the reader-side deduplication failure.

--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1853,8 +1853,15 @@ synthesize_method (tree fndecl)
   finish_function_body (stmt);
   finish_function (/*inline_p=*/false);

-  /* Remember that we were defined in this module.  */
-  set_instantiating_module (fndecl);
+  /* Remember that we were defined in this module.  But skip this for
+     members that were explicitly defaulted in the class body of an
+     imported class -- their definition is already covered by the home
+     module's class definition section, and clearing the import flag
+     would cause the writer to re-emit them, leading to a deduplication
+     failure when a downstream importer reads both definitions.  */
+  tree fn = STRIP_TEMPLATE (fndecl);
+  if (!(DECL_LANG_SPECIFIC (fn) && DECL_MODULE_IMPORT_P (fn)
+       && DECL_DEFAULTED_IN_CLASS_P (fn)))
+    set_instantiating_module (fndecl);

   if (!DECL_DELETED_FN (fndecl))
     expand_or_defer_fn (fndecl);

This preserves PR120499's fix for lazily-declared implicit members
(DECL_DEFAULTED_IN_CLASS_P is false for those), while preventing
re-emission of explicitly-defaulted-in-class members whose definitions
are already implied by the home module's class definition.

Reply via email to