Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -8728,9 +8728,12 @@ if (Previous.empty()) return; - auto *OldConcept = dyn_cast(Previous.getRepresentativeDecl()); + NamedDecl *Old = Previous.getRepresentativeDecl(); + if (auto *USD = dyn_cast(Old)) + Old = USD->getTargetDecl(); + + auto *OldConcept = dyn_cast(Old); if (!OldConcept) { - auto *Old = Previous.getRepresentativeDecl(); Diag(NewDecl->getLocation(), diag::err_redefinition_different_kind) << NewDecl->getDeclName(); notePreviousDefinition(Old, NewDecl->getLocation()); @@ -8746,7 +8749,11 @@ AddToScope = false; return; } - if (hasReachableDefinition(OldConcept)) { + if (hasReachableDefinition(OldConcept) && + // When the old decl lives in the GMF and the new decl is same with the + // old one, we should merge them instead of complaining. + !(OldConcept->getOwningModule() && + OldConcept->getOwningModule()->isGlobalModule())) { Diag(NewDecl->getLocation(), diag::err_redefinition) << NewDecl->getDeclName(); notePreviousDefinition(OldConcept, NewDecl->getLocation()); @@ -8758,7 +8765,7 @@ // Other decls (e.g. namespaces) also have this shortcoming. return; } - Context.setPrimaryMergedDecl(NewDecl, OldConcept); + Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl()); } /// \brief Strips various properties off an implicit instantiation Index: clang/test/Modules/merge-concepts-in-GMF.cppm =================================================================== --- /dev/null +++ clang/test/Modules/merge-concepts-in-GMF.cppm @@ -0,0 +1,63 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -I%t %t/A.cppm -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -I%t %t/B.cppm -o %t/B.pcm +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/Use2.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/Use3.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/Use4.cpp -verify -fsyntax-only + +//--- foo.h +template +concept same_as = __is_same(T, U); + +//--- A.cppm +module; +#include "foo.h" +export module A; +export using ::same_as; + +//--- B.cppm +module; +#include "foo.h" +export module B; +export using ::same_as; + +//--- Use.cpp +// expected-no-diagnostics +import A; +import B; + +template void foo() + requires same_as +{} + +//--- Use2.cpp +// expected-no-diagnostics +#include "foo.h" +import A; + +template void foo() + requires same_as +{} + +//--- Use3.cpp +// expected-no-diagnostics +import A; +#include "foo.h" + +template void foo() + requires same_as +{} + +//--- Use4.cpp +// expected-no-diagnostics +import A; +import B; +#include "foo.h" + +template void foo() + requires same_as +{}