Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2673,6 +2673,11 @@ /// that they may be used in declarations of the same template. bool isSameTemplateParameter(const NamedDecl *X, const NamedDecl *Y) const; + /// Determine whether two 'requires' expressions are similar enough. + /// Use of 'requires' isn't mandatory, works with constraints expressed in + /// other ways too. + bool isSameConstraintExpr(const Expr *XCE, const Expr *YCE) const; + /// Determine whether two default template arguments are similar enough /// that they may be used in declarations of the same template. bool isSameDefaultTemplateArgument(const NamedDecl *X, Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -6218,6 +6218,19 @@ getCanonicalTemplateName(Y).getAsVoidPointer(); } +bool ASTContext::isSameConstraintExpr(const Expr *XCE, const Expr *YCE) const { + if (!XCE != !YCE) + return false; + + if (!XCE) + return true; + + llvm::FoldingSetNodeID XCEID, YCEID; + XCE->Profile(XCEID, *this, /*Canonical=*/true); + YCE->Profile(YCEID, *this, /*Canonical=*/true); + return XCEID == YCEID; +} + bool ASTContext::isSameTemplateParameter(const NamedDecl *X, const NamedDecl *Y) const { if (X->getKind() != Y->getKind()) @@ -6245,14 +6258,23 @@ 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. } + if (!isSameConstraintExpr(TXTC->getImmediatelyDeclaredConstraint(), + TYTC->getImmediatelyDeclaredConstraint())) + return false; } return true; } @@ -6279,19 +6301,7 @@ if (!isSameTemplateParameter(X->getParam(I), Y->getParam(I))) return false; - const Expr *XRC = X->getRequiresClause(); - const Expr *YRC = Y->getRequiresClause(); - if (!XRC != !YRC) - return false; - if (XRC) { - llvm::FoldingSetNodeID XRCID, YRCID; - XRC->Profile(XRCID, *this, /*Canonical=*/true); - YRC->Profile(YRCID, *this, /*Canonical=*/true); - if (XRCID != YRCID) - return false; - } - - return true; + return isSameConstraintExpr(X->getRequiresClause(), Y->getRequiresClause()); } bool ASTContext::isSameDefaultTemplateArgument(const NamedDecl *X, @@ -6489,17 +6499,9 @@ return false; } - const Expr *XRC = FuncX->getTrailingRequiresClause(); - const Expr *YRC = FuncY->getTrailingRequiresClause(); - if (!XRC != !YRC) + if (!isSameConstraintExpr(FuncX->getTrailingRequiresClause(), + FuncY->getTrailingRequiresClause())) return false; - if (XRC) { - llvm::FoldingSetNodeID XRCID, YRCID; - XRC->Profile(XRCID, *this, /*Canonical=*/true); - YRC->Profile(YRCID, *this, /*Canonical=*/true); - if (XRCID != YRCID) - return false; - } auto GetTypeAsWritten = [](const FunctionDecl *FD) { // Map to the first declaration that we've already merged into this one. 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