Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -6245,14 +6245,31 @@ auto *TYTCArgs = TYTC->getTemplateArgsAsWritten(); if (TXTCArgs->NumTemplateArgs != TYTCArgs->NumTemplateArgs) return false; - llvm::FoldingSetNodeID XID, YID; - for (auto &ArgLoc : TXTCArgs->arguments()) - ArgLoc.getArgument().Profile(XID, X->getASTContext()); - for (auto &ArgLoc : TYTCArgs->arguments()) - ArgLoc.getArgument().Profile(YID, Y->getASTContext()); - if (XID != YID) - return false; + // We couldn't compare the profiling result for the template + // args here. Consider the following example in different modules: + // + // template <__integer_like _Tp, C<_Tp> Sentinel> + // constexpr _Tp operator()(_Tp &&__t, Sentinel &&last) const { + // return __t; + // } + // + // When we compare the profiling result for `C<_Tp>` in different + // modules, it will compare the type of `_Tp` in different modules. + // However, the type of `_Tp` in different modules refer to different + // types here naturally. So we couldn't compare the profiling result + // for the template args directly. + } + llvm::FoldingSetNodeID XID, YID; + auto *XConstraint = TXTC->getImmediatelyDeclaredConstraint(); + auto *YConstraint = TYTC->getImmediatelyDeclaredConstraint(); + if (!XConstraint != !YConstraint) + return false; + if (XConstraint) { + XConstraint->Profile(XID, *this, /*Canonical=*/true); + YConstraint->Profile(YID, *this, /*Canonical=*/true); } + if (XID != YID) + return false; } return true; } Index: clang/test/Modules/concept.cppm =================================================================== --- clang/test/Modules/concept.cppm +++ clang/test/Modules/concept.cppm @@ -3,6 +3,7 @@ // RUN: split-file %s %t // // RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t -DDIFFERENT %t/B.cppm -verify // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/B.cppm -verify //--- foo.h @@ -18,6 +19,9 @@ template concept __member_size = requires(_Tp &&t) { t.size(); }; +template +concept C = requires(First x, Second y) { x+y; }; + struct A { public: template @@ -29,6 +33,29 @@ constexpr __integer_like auto operator()(_Tp&& __t) const { return __t.size(); } + + template <__integer_like _Tp, C<_Tp> Sentinel> + constexpr _Tp operator()(_Tp &&__t, Sentinel &&last) const { + return __t; + } + + template class H, class S, C> Sentinel> + constexpr H operator()(H &&__s, Sentinel &&last) const { + return __s; + } + +// Tests that we could find different concept definition indeed. +#ifndef DIFFERENT + template <__integer_like _Tp, __integer_like _Up, C<_Tp> Sentinel> + constexpr _Tp operator()(_Tp &&__t, _Up _u, Sentinel &&last) const { + return __t; + } +#else + template <__integer_like _Tp, __integer_like _Up, C<_Up> Sentinel> + constexpr _Tp operator()(_Tp &&__t, _Up _u, Sentinel &&last) const { + return __t; + } +#endif }; #endif @@ -38,12 +65,23 @@ export module A; //--- B.cppm -// expected-no-diagnostics module; #include "foo.h" export module B; import A; +#ifdef DIFFERENT +// expected-error@foo.h:41 {{'__fn::operator()' from module 'A.' is not present in definition of '__fn' provided earlier}} +// expected-note@* 1+{{declaration of 'operator()' does not match}} +#else +// expected-no-diagnostics +#endif + +template +struct U { + auto operator+(U) { return 0; } +}; + void foo() { A a; struct S { @@ -51,4 +89,8 @@ auto operator+(S s) { return 0; } }; __fn{}(S()); + __fn{}(S(), S()); + __fn{}(S(), S(), S()); + + __fn{}(U(), U()); }