diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -6512,6 +6512,7 @@ // and patterns match. if (const auto *TemplateX = dyn_cast(X)) { const auto *TemplateY = cast(Y); + // FIXME: for C++20 concepts, check their requirements are the same. return isSameEntity(TemplateX->getTemplatedDecl(), TemplateY->getTemplatedDecl()) && isSameTemplateParameterList(TemplateX->getTemplateParameters(), diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8655,12 +8655,21 @@ // Check for conflicting previous declaration. DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NameLoc); LookupResult Previous(*this, NameInfo, LookupOrdinaryName, - ForVisibleRedeclaration); + forRedeclarationInCurContext()); LookupName(Previous, S); FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage=*/false, /*AllowInlineNamespace*/false); - if (!Previous.empty()) { + + auto *Old = + Previous.isSingleResult() ? Previous.getAsSingle() : nullptr; + bool AddToScope = true; + // Check for redefinition and merge with decl from other module if needed. + if (Old && !hasVisibleDefinition(Old) && Context.isSameEntity(NewDecl, Old)) { + Context.setPrimaryMergedDecl(NewDecl, Old); + makeMergedDefinitionVisible(Old); + AddToScope = false; + } else if (!Previous.empty()) { auto *Old = Previous.getRepresentativeDecl(); Diag(NameLoc, isa(Old) ? diag::err_redefinition : diag::err_redefinition_different_kind) << NewDecl->getDeclName(); @@ -8668,7 +8677,8 @@ } ActOnDocumentableDecl(NewDecl); - PushOnScopeChains(NewDecl, S); + if (AddToScope) + PushOnScopeChains(NewDecl, S); return NewDecl; } diff --git a/clang/test/Modules/Inputs/merge-concepts/concepts.h b/clang/test/Modules/Inputs/merge-concepts/concepts.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-concepts/concepts.h @@ -0,0 +1,6 @@ +#ifndef SAMEAS_CONCEPTS_H_ +#define SAMEAS_CONCEPTS_H_ + +#include "same_as.h" + +#endif // SAMEAS_CONCEPTS_H diff --git a/clang/test/Modules/Inputs/merge-concepts/format.h b/clang/test/Modules/Inputs/merge-concepts/format.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-concepts/format.h @@ -0,0 +1,11 @@ +#ifndef FORMAT_H +#define FORMAT_H + +#include "concepts.h" +#include "same_as.h" + +template void foo() + requires same_as +{} + +#endif // FORMAT_H diff --git a/clang/test/Modules/Inputs/merge-concepts/modules.map b/clang/test/Modules/Inputs/merge-concepts/modules.map new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-concepts/modules.map @@ -0,0 +1,11 @@ +module "library" { + export * + module "concepts" { + export * + header "concepts.h" + } + module "format" { + export * + header "format.h" + } +} diff --git a/clang/test/Modules/Inputs/merge-concepts/same_as.h b/clang/test/Modules/Inputs/merge-concepts/same_as.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/Inputs/merge-concepts/same_as.h @@ -0,0 +1,7 @@ +#ifndef SAME_AS_H +#define SAME_AS_H + +template +concept same_as = __is_same(T, U); + +#endif // SAME_AS_H diff --git a/clang/test/Modules/merge-concepts.m b/clang/test/Modules/merge-concepts.m new file mode 100644 --- /dev/null +++ b/clang/test/Modules/merge-concepts.m @@ -0,0 +1,9 @@ +// RUN: rm -rf %t.dir +// RUN: mkdir %t.dir +// +// RUN: %clang_cc1 -xc++ -std=c++20 -fmodules -fmodule-name=library \ +// RUN: -emit-module %S/Inputs/merge-concepts/modules.map \ +// RUN: -o %t.dir/module.pcm +// +// Note that this file merely checks the module compiles. The contents of this +// file are never read by the compiler.