[clang] f6b0ae1 - [AST] Accept identical TypeConstraint referring to other template
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 12 08:58:38 PDT 2022
Author: Chuanqi Xu
Date: 2022-07-12T23:57:44+08:00
New Revision: f6b0ae144ed8085618c12ba83d95affd786e6a49
URL: https://github.com/llvm/llvm-project/commit/f6b0ae144ed8085618c12ba83d95affd786e6a49
DIFF: https://github.com/llvm/llvm-project/commit/f6b0ae144ed8085618c12ba83d95affd786e6a49.diff
LOG: [AST] Accept identical TypeConstraint referring to other template
parameters.
The current implementation to judge the similarity of TypeConstraint in
ASTContext::isSameTemplateParameter is problematic, it couldn't handle
the following case:
```C++
template <__integer_like _Tp, C<_Tp> Sentinel>
constexpr _Tp operator()(_Tp &&__t, Sentinel &&last) const {
return __t;
}
```
When we see 2 such declarations from different modules, we would judge
their similarity by `ASTContext::isSame*` methods. But problems come for
the TypeConstraint. Originally, we would profile each argument one by
one. But it is not right. Since the profiling result of `_Tp` would
refer to two different template type declarations. So it would get
different results. It is right since the `_Tp` in different modules
refers to different declarations indeed. So the same declaration in
different modules would meet incorrect our-checking results.
It is not the thing we want. We want to know if the TypeConstraint have
the same expression.
Reviewer: vsapsai, ilya-biryukov
Differential Revision: https://reviews.llvm.org/D129068
Added:
Modified:
clang/include/clang/AST/ASTContext.h
clang/lib/AST/ASTContext.cpp
clang/test/Modules/concept.cppm
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 87b5a6053f1f2..85eba45e4de6e 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -130,6 +130,7 @@ class TemplateDecl;
class TemplateParameterList;
class TemplateTemplateParmDecl;
class TemplateTypeParmDecl;
+class TypeConstraint;
class UnresolvedSetIterator;
class UsingShadowDecl;
class VarTemplateDecl;
@@ -2664,6 +2665,18 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// 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,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index aa29ce7f13840..cfd7bf6045422 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6218,6 +6218,57 @@ bool ASTContext::hasSameTemplateName(const TemplateName &X,
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
diff erent 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
diff erent
+ // modules, it will compare the type of `_Tp` in
diff erent modules.
+ // However, the type of `_Tp` in
diff erent modules refer to
diff erent
+ // 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 @@ bool ASTContext::isSameTemplateParameter(const NamedDecl *X,
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<NonTypeTemplateParmDecl>(X)) {
@@ -6279,19 +6306,7 @@ bool ASTContext::isSameTemplateParameterList(
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 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
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.
diff --git a/clang/test/Modules/concept.cppm b/clang/test/Modules/concept.cppm
index 84bc0fef83926..0e85a46411a54 100644
--- a/clang/test/Modules/concept.cppm
+++ b/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 @@ concept __integer_like = true;
template <class _Tp>
concept __member_size = requires(_Tp &&t) { t.size(); };
+template <class First, class Second>
+concept C = requires(First x, Second y) { x + y; };
+
struct A {
public:
template <Range T>
@@ -29,6 +33,29 @@ struct __fn {
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 <template <class> class H, class S, C<H<S>> Sentinel>
+ constexpr H<S> operator()(H<S> &&__s, Sentinel &&last) const {
+ return __s;
+ }
+
+// Tests that we could find
diff erent 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 @@ module;
export module A;
//--- B.cppm
-// expected-no-diagnostics
module;
#include "foo.h"
export module B;
import A;
+#ifdef DIFFERENT
+// expected-error at foo.h:41 {{'__fn::operator()' from module 'A.<global>' is not present in definition of '__fn' provided earlier}}
+// expected-note@* 1+{{declaration of 'operator()' does not match}}
+#else
+// expected-no-diagnostics
+#endif
+
+template <class T>
+struct U {
+ auto operator+(U) { return 0; }
+};
+
void foo() {
A a;
struct S {
@@ -51,4 +89,8 @@ void foo() {
auto operator+(S s) { return 0; }
};
__fn{}(S());
+ __fn{}(S(), S());
+ __fn{}(S(), S(), S());
+
+ __fn{}(U<int>(), U<int>());
}
More information about the cfe-commits
mailing list