From 3e6ea8aff3d534c56e04e39424a27761facc4101 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Wed, 27 Jul 2022 12:11:42 +0200 Subject: [Sema] Return primary merged decl as canonical for concepts Otherwise we get invalid results for ODR checks. See changed test for an example: despite the fact that we merge the first concept, its **uses** were considered different by `Profile`, leading to redefinition errors. After this change, canonical decl for a concept can come from a different module and may not be visible. This behavior looks suspicious, but does not break any tests. We might want to add a mechanism to make the canonical concept declaration visible if we find code that relies on this invariant. Additionally make sure we always merge with the canonical declaration to avoid chains of merged concepts being reported as redefinitions. An example was added to the test. Also change the order of includes in the test. Importing a moduralized header before its textual part causes the include guard macro to be exported and the corresponding `#include` becomes a no-op. Reviewed By: ChuanqiXu Differential Revision: https://reviews.llvm.org/D130585 (cherry picked from commit 42f87bb62d0719848842da60d2a8180b9e4d7c52) --- clang/include/clang/AST/DeclTemplate.h | 8 ++++++-- clang/lib/Sema/SemaTemplate.cpp | 3 ++- clang/test/Modules/merge-concepts.cpp | 27 +++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 725bb0b..baed5ca 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3287,8 +3287,12 @@ public: return isa(getTemplateParameters()->getParam(0)); } - ConceptDecl *getCanonicalDecl() override { return getFirstDecl(); } - const ConceptDecl *getCanonicalDecl() const { return getFirstDecl(); } + ConceptDecl *getCanonicalDecl() override { + return cast(getPrimaryMergedDecl(this)); + } + const ConceptDecl *getCanonicalDecl() const { + return const_cast(this)->getCanonicalDecl(); + } // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return classofKind(D->getKind()); } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1542a07..03fc346 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8758,7 +8758,8 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl, // Other decls (e.g. namespaces) also have this shortcoming. return; } - Context.setPrimaryMergedDecl(NewDecl, OldConcept); + // We unwrap canonical decl late to check for module visibility. + Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl()); } /// \brief Strips various properties off an implicit instantiation diff --git a/clang/test/Modules/merge-concepts.cpp b/clang/test/Modules/merge-concepts.cpp index 9242f27..c774157 100644 --- a/clang/test/Modules/merge-concepts.cpp +++ b/clang/test/Modules/merge-concepts.cpp @@ -28,6 +28,10 @@ module "library" { export * header "concepts.h" } + module "compare" { + export * + header "compare.h" + } module "format" { export * header "format.h" @@ -47,19 +51,34 @@ module "library" { #define SAME_AS_H template -concept same_as = __is_same(T, U); +concept same_as_impl = __is_same(T, U); +template +concept same_as = same_as_impl && same_as_impl; #endif // SAME_AS_H + +//--- compare.h +#ifndef COMPARE_H +#define COMPARE_H + +#include "same_as.h" +#include "concepts.h" + +template void foo() + requires same_as +{} +#endif // COMPARE_H + //--- format.h #ifndef FORMAT_H #define FORMAT_H -#include "concepts.h" #include "same_as.h" +#include "concepts.h" -template void foo() +template void bar() requires same_as {} -#endif // FORMAT_H \ No newline at end of file +#endif // FORMAT_H -- cgit v1.1