Add dumping support for RequiresExpr.

In passing, fix an ast-print bug that inserted a spurious extra `;`
after a concept definition.
This commit is contained in:
Richard Smith 2021-06-30 14:05:34 -07:00
parent d86b0073cf
commit ef227b32b6
8 changed files with 145 additions and 11 deletions

View File

@ -53,6 +53,7 @@ struct {
void Visit(const OMPClause *C);
void Visit(const BlockDecl::Capture &C);
void Visit(const GenericSelectionExpr::ConstAssociation &A);
void Visit(const concepts::Requirement *R);
void Visit(const APValue &Value, QualType Ty);
};
*/
@ -141,7 +142,8 @@ public:
ConstStmtVisitor<Derived>::Visit(S);
// Some statements have custom mechanisms for dumping their children.
if (isa<DeclStmt>(S) || isa<GenericSelectionExpr>(S))
if (isa<DeclStmt>(S) || isa<GenericSelectionExpr>(S) ||
isa<RequiresExpr>(S))
return;
if (Traversal == TK_IgnoreUnlessSpelledInSource &&
@ -228,6 +230,28 @@ public:
});
}
void Visit(const concepts::Requirement *R) {
getNodeDelegate().AddChild([=] {
getNodeDelegate().Visit(R);
if (!R)
return;
if (auto *TR = dyn_cast<concepts::TypeRequirement>(R)) {
if (!TR->isSubstitutionFailure())
Visit(TR->getType()->getType().getTypePtr());
} else if (auto *ER = dyn_cast<concepts::ExprRequirement>(R)) {
if (!ER->isExprSubstitutionFailure())
Visit(ER->getExpr());
if (!ER->getReturnTypeRequirement().isEmpty())
Visit(ER->getReturnTypeRequirement()
.getTypeConstraint()
->getImmediatelyDeclaredConstraint());
} else if (auto *NR = dyn_cast<concepts::NestedRequirement>(R)) {
if (!NR->isSubstitutionFailure())
Visit(NR->getConstraintExpr());
}
});
}
void Visit(const APValue &Value, QualType Ty) {
getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(Value, Ty); });
}
@ -689,6 +713,13 @@ public:
}
}
void VisitRequiresExpr(const RequiresExpr *E) {
for (auto *D : E->getLocalParameters())
Visit(D);
for (auto *R : E->getRequirements())
Visit(R);
}
void VisitLambdaExpr(const LambdaExpr *Node) {
if (Traversal == TK_IgnoreUnlessSpelledInSource) {
for (unsigned I = 0, N = Node->capture_size(); I != N; ++I) {

View File

@ -21,6 +21,7 @@
#include "clang/AST/AttrVisitor.h"
#include "clang/AST/CommentCommandTraits.h"
#include "clang/AST/CommentVisitor.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/Type.h"
@ -204,6 +205,7 @@ public:
void Visit(const OMPClause *C);
void Visit(const BlockDecl::Capture &C);
void Visit(const GenericSelectionExpr::ConstAssociation &A);
void Visit(const concepts::Requirement *R);
void Visit(const APValue &Value, QualType Ty);
void VisitTypedefType(const TypedefType *TT);
@ -290,6 +292,7 @@ public:
void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE);
void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *MTE);
void VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *ME);
void VisitRequiresExpr(const RequiresExpr *RE);
void VisitObjCEncodeExpr(const ObjCEncodeExpr *OEE);
void VisitObjCMessageExpr(const ObjCMessageExpr *OME);

View File

@ -19,6 +19,7 @@
#include "clang/AST/CommentCommandTraits.h"
#include "clang/AST/CommentVisitor.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/TemplateArgumentVisitor.h"
@ -188,6 +189,8 @@ public:
void Visit(const GenericSelectionExpr::ConstAssociation &A);
void Visit(const concepts::Requirement *R);
void Visit(const APValue &Value, QualType Ty);
void dumpPointer(const void *Ptr);
@ -296,6 +299,7 @@ public:
void VisitObjCBoolLiteralExpr(const ObjCBoolLiteralExpr *Node);
void VisitOMPIteratorExpr(const OMPIteratorExpr *Node);
void VisitConceptSpecializationExpr(const ConceptSpecializationExpr *Node);
void VisitRequiresExpr(const RequiresExpr *Node);
void VisitRValueReferenceType(const ReferenceType *T);
void VisitArrayType(const ArrayType *T);

View File

@ -1146,7 +1146,6 @@ void DeclPrinter::VisitTemplateDecl(const TemplateDecl *D) {
Out << "concept " << Concept->getName() << " = " ;
Concept->getConstraintExpr()->printPretty(Out, nullptr, Policy, Indentation,
"\n", &Context);
Out << ";";
}
}

View File

@ -185,6 +185,35 @@ void JSONNodeDumper::Visit(const GenericSelectionExpr::ConstAssociation &A) {
attributeOnlyIfTrue("selected", A.isSelected());
}
void JSONNodeDumper::Visit(const concepts::Requirement *R) {
if (!R)
return;
switch (R->getKind()) {
case concepts::Requirement::RK_Type:
JOS.attribute("kind", "TypeRequirement");
break;
case concepts::Requirement::RK_Simple:
JOS.attribute("kind", "SimpleRequirement");
break;
case concepts::Requirement::RK_Compound:
JOS.attribute("kind", "CompoundRequirement");
break;
case concepts::Requirement::RK_Nested:
JOS.attribute("kind", "NestedRequirement");
break;
}
if (auto *ER = dyn_cast<concepts::ExprRequirement>(R))
attributeOnlyIfTrue("noexcept", ER->hasNoexceptRequirement());
attributeOnlyIfTrue("isDependent", R->isDependent());
if (!R->isDependent())
JOS.attribute("satisfied", R->isSatisfied());
attributeOnlyIfTrue("containsUnexpandedPack",
R->containsUnexpandedParameterPack());
}
void JSONNodeDumper::Visit(const APValue &Value, QualType Ty) {
std::string Str;
llvm::raw_string_ostream OS(Str);
@ -713,9 +742,13 @@ void JSONNodeDumper::VisitMemberPointerType(const MemberPointerType *MPT) {
void JSONNodeDumper::VisitNamedDecl(const NamedDecl *ND) {
if (ND && ND->getDeclName()) {
JOS.attribute("name", ND->getNameAsString());
std::string MangledName = ASTNameGen.getName(ND);
if (!MangledName.empty())
JOS.attribute("mangledName", MangledName);
// FIXME: There are likely other contexts in which it makes no sense to ask
// for a mangled name.
if (!isa<RequiresExprBodyDecl>(ND->getDeclContext())) {
std::string MangledName = ASTNameGen.getName(ND);
if (!MangledName.empty())
JOS.attribute("mangledName", MangledName);
}
}
}
@ -1415,6 +1448,11 @@ void JSONNodeDumper::VisitCXXDependentScopeMemberExpr(
}
}
void JSONNodeDumper::VisitRequiresExpr(const RequiresExpr *RE) {
if (!RE->isValueDependent())
JOS.attribute("satisfied", RE->isSatisfied());
}
void JSONNodeDumper::VisitIntegerLiteral(const IntegerLiteral *IL) {
llvm::SmallString<16> Buffer;
IL->getValue().toString(Buffer,

View File

@ -356,6 +356,46 @@ void TextNodeDumper::Visit(const GenericSelectionExpr::ConstAssociation &A) {
OS << " selected";
}
void TextNodeDumper::Visit(const concepts::Requirement *R) {
if (!R) {
ColorScope Color(OS, ShowColors, NullColor);
OS << "<<<NULL>>> Requirement";
return;
}
{
ColorScope Color(OS, ShowColors, StmtColor);
switch (R->getKind()) {
case concepts::Requirement::RK_Type:
OS << "TypeRequirement";
break;
case concepts::Requirement::RK_Simple:
OS << "SimpleRequirement";
break;
case concepts::Requirement::RK_Compound:
OS << "CompoundRequirement";
break;
case concepts::Requirement::RK_Nested:
OS << "NestedRequirement";
break;
}
}
dumpPointer(R);
if (auto *ER = dyn_cast<concepts::ExprRequirement>(R)) {
if (ER->hasNoexceptRequirement())
OS << " noexcept";
}
if (R->isDependent())
OS << " dependent";
else
OS << (R->isSatisfied() ? " satisfied" : " unsatisfied");
if (R->containsUnexpandedParameterPack())
OS << " contains_unexpanded_pack";
}
static double GetApproxValue(const llvm::APFloat &F) {
llvm::APFloat V = F;
bool ignored;
@ -1366,6 +1406,12 @@ void TextNodeDumper::VisitConceptSpecializationExpr(
dumpBareDeclRef(Node->getFoundDecl());
}
void TextNodeDumper::VisitRequiresExpr(
const RequiresExpr *Node) {
if (!Node->isValueDependent())
OS << (Node->isSatisfied() ? " satisfied" : " unsatisfied");
}
void TextNodeDumper::VisitRValueReferenceType(const ReferenceType *T) {
if (T->isSpelledAsLValue())
OS << " written as lvalue reference";

View File

@ -1,9 +1,10 @@
// RUN: %clang_cc1 -std=c++14 -fsyntax-only %s
// RUN: %clang_cc1 -std=c++14 -ast-print %s -o %t.1.cpp
// RUN: %clang_cc1 -std=c++14 -ast-print %t.1.cpp -o %t.2.cpp
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s
// RUN: %clang_cc1 -std=c++20 -ast-print %s -o %t.1.cpp
// RUN: %clang_cc1 -std=c++20 -ast-print %t.1.cpp -o %t.2.cpp
// RUN: diff %t.1.cpp %t.2.cpp
// RUN: %clang_cc1 -std=c++14 -ast-dump %s
// RUN: %clang_cc1 -std=c++14 -ast-dump-all %s
// RUN: %clang_cc1 -std=c++14 -fdump-record-layouts %s
// RUN: %clang_cc1 -std=c++20 -ast-dump %s
// RUN: %clang_cc1 -std=c++20 -ast-dump-all %s
// RUN: %clang_cc1 -std=c++20 -ast-dump=json %s
// RUN: %clang_cc1 -std=c++20 -fdump-record-layouts %s
#include "cxx-language-features.inc"

View File

@ -65,3 +65,15 @@ private:
template <typename T> T varTemplate = 0;
static_assert(true, "");
// Concepts
template<typename T> concept True = true;
template<typename T> concept Concept = requires (T a) {
a;
{ a() } noexcept -> True;
requires false || true;
typename T::type;
};
template<typename T> void constrained1() requires Concept<T>;
template<Concept T> void constrained2();
void constrained3(Concept auto x);