Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -130,6 +130,7 @@ class TemplateParameterList; class TemplateTemplateParmDecl; class TemplateTypeParmDecl; +class TypeConstraint; class UnresolvedSetIterator; class UsingShadowDecl; class VarTemplateDecl; @@ -2664,6 +2665,18 @@ /// 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 that they + /// may be used in re-declarations. + /// + /// 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 type contraint are similar enough that they could + /// used in declarations of the same template. + bool isSameTypeConstraint(const TypeConstraint *XTC, + const TypeConstraint *YTC) 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,57 @@ 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::isSameTypeConstraint(const TypeConstraint *XTC, + const TypeConstraint *YTC) const { + if (!XTC != !YTC) + return false; + + if (!XTC) + return true; + + auto *NCX = XTC->getNamedConcept(); + auto *NCY = YTC->getNamedConcept(); + if (!NCX || !NCY || !isSameEntity(NCX, NCY)) + return false; + if (XTC->hasExplicitTemplateArgs() != YTC->hasExplicitTemplateArgs()) + return false; + if (XTC->hasExplicitTemplateArgs()) + if (XTC->getTemplateArgsAsWritten()->NumTemplateArgs != + YTC->getTemplateArgsAsWritten()->NumTemplateArgs) + return false; + + // Compare slowly by profiling. + // + // 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. + return isSameConstraintExpr(XTC->getImmediatelyDeclaredConstraint(), + YTC->getImmediatelyDeclaredConstraint()); +} + bool ASTContext::isSameTemplateParameter(const NamedDecl *X, const NamedDecl *Y) const { if (X->getKind() != Y->getKind()) @@ -6229,32 +6280,8 @@ return false; if (TX->hasTypeConstraint() != TY->hasTypeConstraint()) return false; - const TypeConstraint *TXTC = TX->getTypeConstraint(); - const TypeConstraint *TYTC = TY->getTypeConstraint(); - if (!TXTC != !TYTC) - return false; - if (TXTC && TYTC) { - auto *NCX = TXTC->getNamedConcept(); - auto *NCY = TYTC->getNamedConcept(); - if (!NCX || !NCY || !isSameEntity(NCX, NCY)) - return false; - if (TXTC->hasExplicitTemplateArgs() != TYTC->hasExplicitTemplateArgs()) - return false; - if (TXTC->hasExplicitTemplateArgs()) { - auto *TXTCArgs = TXTC->getTemplateArgsAsWritten(); - 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; - } - } - return true; + return isSameTypeConstraint(TX->getTypeConstraint(), + TY->getTypeConstraint()); } if (auto *TX = dyn_cast(X)) { @@ -6279,19 +6306,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 +6504,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