diff options
author | Saar Raz <saar@raz.email> | 2020-01-18 09:11:43 +0200 |
---|---|---|
committer | Saar Raz <saar@raz.email> | 2020-01-24 02:28:22 +0200 |
commit | c96ef5118857ff938aa6d304ccf7c0f9b81bc5ba (patch) | |
tree | dd3f90c54e9cfd869b0e74b1f6b7bd85e8888094 /clang/lib | |
parent | ab514b91196345ba4c61026e4997871e85ddd97d (diff) | |
download | bcm5719-llvm-c96ef5118857ff938aa6d304ccf7c0f9b81bc5ba.tar.gz bcm5719-llvm-c96ef5118857ff938aa6d304ccf7c0f9b81bc5ba.zip |
[Concepts] Requires Expressions
Implement support for C++2a requires-expressions.
Re-commit after compilation failure on some platforms due to alignment issues with PointerIntPair.
Differential Revision: https://reviews.llvm.org/D50360
(cherry picked from commit a0f50d731639350c7a79f140f026c27a18215531)
Diffstat (limited to 'clang/lib')
38 files changed, 1881 insertions, 196 deletions
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index bd9b0934591..e79c245d2f8 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -46,6 +46,7 @@ add_clang_library(clangAST DeclTemplate.cpp Expr.cpp ExprClassification.cpp + ExprConcepts.cpp ExprConstant.cpp ExprCXX.cpp ExprObjC.cpp diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 6ee767ccecf..cb4d61cac2c 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -804,6 +804,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case OMPCapturedExpr: case Empty: case LifetimeExtendedTemporary: + case RequiresExprBody: // Never looked up by name. return 0; } @@ -1177,6 +1178,7 @@ DeclContext *DeclContext::getPrimaryContext() { case Decl::Captured: case Decl::OMPDeclareReduction: case Decl::OMPDeclareMapper: + case Decl::RequiresExprBody: // There is only one DeclContext for these entities. return this; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 2ead1e70ea0..48e310e858b 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1968,6 +1968,16 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, QualType(), nullptr, SourceLocation()); } +RequiresExprBodyDecl *RequiresExprBodyDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation StartLoc) { + return new (C, DC) RequiresExprBodyDecl(C, DC, StartLoc); +} + +RequiresExprBodyDecl *RequiresExprBodyDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + return new (C, ID) RequiresExprBodyDecl(C, nullptr, SourceLocation()); +} + void CXXMethodDecl::anchor() {} bool CXXMethodDecl::isStatic() const { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 73ddbc62482..83519895876 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3457,6 +3457,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case OpaqueValueExprClass: case SourceLocExprClass: case ConceptSpecializationExprClass: + case RequiresExprClass: // These never have a side-effect. return false; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 422227d787b..e4bd218ae2d 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclAccessPair.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" #include "clang/AST/LambdaCapture.h" @@ -1764,81 +1765,3 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx, alignof(CUDAKernelCallExpr)); return new (Mem) CUDAKernelCallExpr(NumArgs, Empty); } - -ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C, - NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> ConvertedArgs, - const ConstraintSatisfaction *Satisfaction) - : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, - /*TypeDependent=*/false, - // All the flags below are set in setTemplateArguments. - /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, - /*ContainsUnexpandedParameterPacks=*/false), - ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, - NamedConcept, ArgsAsWritten), - NumTemplateArgs(ConvertedArgs.size()), - Satisfaction(Satisfaction ? - ASTConstraintSatisfaction::Create(C, *Satisfaction) : - nullptr) { - setTemplateArguments(ConvertedArgs); -} - -ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, - unsigned NumTemplateArgs) - : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(), - NumTemplateArgs(NumTemplateArgs) { } - -void ConceptSpecializationExpr::setTemplateArguments( - ArrayRef<TemplateArgument> Converted) { - assert(Converted.size() == NumTemplateArgs); - std::uninitialized_copy(Converted.begin(), Converted.end(), - getTrailingObjects<TemplateArgument>()); - bool IsInstantiationDependent = false; - bool ContainsUnexpandedParameterPack = false; - for (const TemplateArgument& Arg : Converted) { - if (Arg.isInstantiationDependent()) - IsInstantiationDependent = true; - if (Arg.containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; - if (ContainsUnexpandedParameterPack && IsInstantiationDependent) - break; - } - - // Currently guaranteed by the fact concepts can only be at namespace-scope. - assert(!NestedNameSpec || - (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() && - !NestedNameSpec.getNestedNameSpecifier() - ->containsUnexpandedParameterPack())); - setInstantiationDependent(IsInstantiationDependent); - setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); - assert((!isValueDependent() || isInstantiationDependent()) && - "should not be value-dependent"); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(const ASTContext &C, - NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, - NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, - const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef<TemplateArgument> ConvertedArgs, - const ConstraintSatisfaction *Satisfaction) { - void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( - ConvertedArgs.size())); - return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, - ConceptNameInfo, FoundDecl, - NamedConcept, ArgsAsWritten, - ConvertedArgs, Satisfaction); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, - unsigned NumTemplateArgs) { - void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( - NumTemplateArgs)); - return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); -} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 9dbf6fe9e0f..d201af31f52 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -193,6 +193,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::DesignatedInitUpdateExprClass: case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: return Cl::CL_PRValue; case Expr::ConstantExprClass: diff --git a/clang/lib/AST/ExprConcepts.cpp b/clang/lib/AST/ExprConcepts.cpp new file mode 100644 index 00000000000..76d57ed5d5b --- /dev/null +++ b/clang/lib/AST/ExprConcepts.cpp @@ -0,0 +1,185 @@ +//===- ExprCXX.cpp - (C++) Expression AST Node Implementation -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the subclesses of Expr class declared in ExprCXX.h +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprConcepts.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConcept.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/TrailingObjects.h" +#include <algorithm> +#include <utility> +#include <string> + +using namespace clang; + +ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C, + NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef<TemplateArgument> ConvertedArgs, + const ConstraintSatisfaction *Satisfaction) + : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TypeDependent=*/false, + // All the flags below are set in setTemplateArguments. + /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, + /*ContainsUnexpandedParameterPacks=*/false), + ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, + NamedConcept, ArgsAsWritten), + NumTemplateArgs(ConvertedArgs.size()), + Satisfaction(Satisfaction ? + ASTConstraintSatisfaction::Create(C, *Satisfaction) : + nullptr) { + setTemplateArguments(ConvertedArgs); +} + +ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, + unsigned NumTemplateArgs) + : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(), + NumTemplateArgs(NumTemplateArgs) { } + +void ConceptSpecializationExpr::setTemplateArguments( + ArrayRef<TemplateArgument> Converted) { + assert(Converted.size() == NumTemplateArgs); + std::uninitialized_copy(Converted.begin(), Converted.end(), + getTrailingObjects<TemplateArgument>()); + bool IsInstantiationDependent = false; + bool ContainsUnexpandedParameterPack = false; + for (const TemplateArgument& Arg : Converted) { + if (Arg.isInstantiationDependent()) + IsInstantiationDependent = true; + if (Arg.containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + if (ContainsUnexpandedParameterPack && IsInstantiationDependent) + break; + } + + // Currently guaranteed by the fact concepts can only be at namespace-scope. + assert(!NestedNameSpec || + (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() && + !NestedNameSpec.getNestedNameSpecifier() + ->containsUnexpandedParameterPack())); + setInstantiationDependent(IsInstantiationDependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); + assert((!isValueDependent() || isInstantiationDependent()) && + "should not be value-dependent"); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(const ASTContext &C, + NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef<TemplateArgument> ConvertedArgs, + const ConstraintSatisfaction *Satisfaction) { + void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( + ConvertedArgs.size())); + return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, + ConceptNameInfo, FoundDecl, + NamedConcept, ArgsAsWritten, + ConvertedArgs, Satisfaction); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, + unsigned NumTemplateArgs) { + void *Buffer = C.Allocate(totalSizeToAlloc<TemplateArgument>( + NumTemplateArgs)); + return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); +} + +const TypeConstraint * +concepts::ExprRequirement::ReturnTypeRequirement::getTypeConstraint() const { + assert(isTypeConstraint()); + auto TPL = + TypeConstraintInfo.getPointer().get<TemplateParameterList *>(); + return cast<TemplateTypeParmDecl>(TPL->getParam(0)) + ->getTypeConstraint(); +} + +RequiresExpr::RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation RBraceLoc) + : Expr(RequiresExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TD=*/false, /*VD=*/false, /*ID=*/false, + /*ContainsUnexpandedParameterPack=*/false), + NumLocalParameters(LocalParameters.size()), + NumRequirements(Requirements.size()), Body(Body), RBraceLoc(RBraceLoc) { + RequiresExprBits.IsSatisfied = false; + RequiresExprBits.RequiresKWLoc = RequiresKWLoc; + bool Dependent = false; + bool ContainsUnexpandedParameterPack = false; + for (ParmVarDecl *P : LocalParameters) { + Dependent |= P->getType()->isInstantiationDependentType(); + ContainsUnexpandedParameterPack |= + P->getType()->containsUnexpandedParameterPack(); + } + RequiresExprBits.IsSatisfied = true; + for (concepts::Requirement *R : Requirements) { + Dependent |= R->isDependent(); + ContainsUnexpandedParameterPack |= R->containsUnexpandedParameterPack(); + if (!Dependent) { + RequiresExprBits.IsSatisfied = R->isSatisfied(); + if (!RequiresExprBits.IsSatisfied) + break; + } + } + std::copy(LocalParameters.begin(), LocalParameters.end(), + getTrailingObjects<ParmVarDecl *>()); + std::copy(Requirements.begin(), Requirements.end(), + getTrailingObjects<concepts::Requirement *>()); + RequiresExprBits.IsSatisfied |= Dependent; + setValueDependent(Dependent); + setInstantiationDependent(Dependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); +} + +RequiresExpr::RequiresExpr(ASTContext &C, EmptyShell Empty, + unsigned NumLocalParameters, + unsigned NumRequirements) + : Expr(RequiresExprClass, Empty), NumLocalParameters(NumLocalParameters), + NumRequirements(NumRequirements) { } + +RequiresExpr * +RequiresExpr::Create(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation RBraceLoc) { + void *Mem = + C.Allocate(totalSizeToAlloc<ParmVarDecl *, concepts::Requirement *>( + LocalParameters.size(), Requirements.size()), + alignof(RequiresExpr)); + return new (Mem) RequiresExpr(C, RequiresKWLoc, Body, LocalParameters, + Requirements, RBraceLoc); +} + +RequiresExpr * +RequiresExpr::Create(ASTContext &C, EmptyShell Empty, + unsigned NumLocalParameters, unsigned NumRequirements) { + void *Mem = + C.Allocate(totalSizeToAlloc<ParmVarDecl *, concepts::Requirement *>( + NumLocalParameters, NumRequirements), + alignof(RequiresExpr)); + return new (Mem) RequiresExpr(C, Empty, NumLocalParameters, NumRequirements); +} diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c4b27b5d1da..c7997350732 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -9912,6 +9912,7 @@ public: bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); bool VisitSourceLocExpr(const SourceLocExpr *E); bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); + bool VisitRequiresExpr(const RequiresExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; @@ -12524,6 +12525,9 @@ bool IntExprEvaluator::VisitConceptSpecializationExpr( return Success(E->isSatisfied(), E); } +bool IntExprEvaluator::VisitRequiresExpr(const RequiresExpr *E) { + return Success(E->isSatisfied(), E); +} bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { switch (E->getOpcode()) { @@ -14182,6 +14186,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CXXScalarValueInitExprClass: case Expr::TypeTraitExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0d567edac52..5d485e00075 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -22,6 +22,7 @@ #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/TypeLoc.h" @@ -3668,6 +3669,7 @@ recurse: case Expr::ConvertVectorExprClass: case Expr::StmtExprClass: case Expr::TypeTraitExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::VAArgExprClass: diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index b6e4d8aff21..7409ae7ddc9 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index c14bb886bb1..45fd8ceae8d 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2269,6 +2269,60 @@ void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { Policy); } +void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) { + OS << "requires "; + auto LocalParameters = E->getLocalParameters(); + if (!LocalParameters.empty()) { + OS << "("; + for (ParmVarDecl *LocalParam : LocalParameters) { + PrintRawDecl(LocalParam); + if (LocalParam != LocalParameters.back()) + OS << ", "; + } + + OS << ") "; + } + OS << "{ "; + auto Requirements = E->getRequirements(); + for (concepts::Requirement *Req : Requirements) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) { + if (TypeReq->isSubstitutionFailure()) + OS << "<<error-type>>"; + else + TypeReq->getType()->getType().print(OS, Policy); + } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) { + if (ExprReq->isCompound()) + OS << "{ "; + if (ExprReq->isExprSubstitutionFailure()) + OS << "<<error-expression>>"; + else + PrintExpr(ExprReq->getExpr()); + if (ExprReq->isCompound()) { + OS << " }"; + if (ExprReq->getNoexceptLoc().isValid()) + OS << " noexcept"; + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (!RetReq.isEmpty()) { + OS << " -> "; + if (RetReq.isSubstitutionFailure()) + OS << "<<error-type>>"; + else if (RetReq.isTypeConstraint()) + RetReq.getTypeConstraint()->print(OS, Policy); + } + } + } else { + auto *NestedReq = cast<concepts::NestedRequirement>(Req); + OS << "requires "; + if (NestedReq->isSubstitutionFailure()) + OS << "<<error-expression>>"; + else + PrintExpr(NestedReq->getConstraintExpr()); + } + OS << "; "; + } + OS << "}"; +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index c0b0f3b0b06..382ea5c8d7e 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1340,6 +1340,49 @@ void StmtProfiler::VisitConceptSpecializationExpr( VisitTemplateArgument(Arg); } +void StmtProfiler::VisitRequiresExpr(const RequiresExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getLocalParameters().size()); + for (ParmVarDecl *LocalParam : S->getLocalParameters()) + VisitDecl(LocalParam); + ID.AddInteger(S->getRequirements().size()); + for (concepts::Requirement *Req : S->getRequirements()) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) { + ID.AddInteger(concepts::Requirement::RK_Type); + ID.AddBoolean(TypeReq->isSubstitutionFailure()); + if (!TypeReq->isSubstitutionFailure()) + VisitType(TypeReq->getType()->getType()); + } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) { + ID.AddInteger(concepts::Requirement::RK_Compound); + ID.AddBoolean(ExprReq->isExprSubstitutionFailure()); + if (!ExprReq->isExprSubstitutionFailure()) + Visit(ExprReq->getExpr()); + // C++2a [expr.prim.req.compound]p1 Example: + // [...] The compound-requirement in C1 requires that x++ is a valid + // expression. It is equivalent to the simple-requirement x++; [...] + // We therefore do not profile isSimple() here. + ID.AddBoolean(ExprReq->getNoexceptLoc().isValid()); + const concepts::ExprRequirement::ReturnTypeRequirement &RetReq = + ExprReq->getReturnTypeRequirement(); + if (RetReq.isEmpty()) { + ID.AddInteger(0); + } else if (RetReq.isTypeConstraint()) { + ID.AddInteger(1); + Visit(RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()); + } else { + assert(RetReq.isSubstitutionFailure()); + ID.AddInteger(2); + } + } else { + ID.AddInteger(concepts::Requirement::RK_Nested); + auto *NestedReq = cast<concepts::NestedRequirement>(Req); + ID.AddBoolean(NestedReq->isSubstitutionFailure()); + if (!NestedReq->isSubstitutionFailure()) + Visit(NestedReq->getConstraintExpr()); + } + } +} + static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, UnaryOperatorKind &UnaryOp, BinaryOperatorKind &BinaryOp) { diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 5aac7a8d54c..60f1dba7c76 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -111,6 +111,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Empty: case Decl::Concept: case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: // None of these decls require codegen support. return; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 3f23fe11e4f..de5c3a03fb6 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -680,6 +680,10 @@ public: return Builder.getInt1(E->isSatisfied()); } + Value *VisitRequiresExpr(const RequiresExpr *E) { + return Builder.getInt1(E->isSatisfied()); + } + Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) { return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue()); } diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 8574d0a7e81..935c64a0fa1 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -429,6 +429,10 @@ private: return "ConstraintNormalization"; case CodeSynthesisContext::ParameterMappingSubstitution: return "ParameterMappingSubstitution"; + case CodeSynthesisContext::RequirementInstantiation: + return "RequirementInstantiation"; + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + return "NestedRequirementConstraintsCheck"; } return ""; } diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 2c7e3a56c04..42e6fd88515 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -385,6 +385,9 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, else Builder.defineMacro("__cplusplus", "199711L"); + if (LangOpts.ConceptsTS) + Builder.defineMacro("__cpp_concepts", "201707L"); + // C++1z [cpp.predefined]p1: // An integer literal of type std::size_t whose value is the alignment // guaranteed by a call to operator new(std::size_t) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 192c0e99e5a..178cb1b661c 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6366,7 +6366,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, + ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6584,9 +6584,9 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// after the opening parenthesis. This function will not parse a K&R-style /// identifier list. /// -/// D is the declarator being parsed. If FirstArgAttrs is non-null, then the -/// caller parsed those arguments immediately after the open paren - they should -/// be considered to be part of the first parameter. +/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs +/// is non-null, then the caller parsed those attributes immediately after the +/// open paren - they should be considered to be part of the first parameter. /// /// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will /// be the location of the ellipsis, if any was parsed. @@ -6612,7 +6612,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorContext, ParsedAttributes &FirstArgAttrs, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, SourceLocation &EllipsisLoc) { @@ -6661,9 +6661,11 @@ void Parser::ParseParameterDeclarationClause( // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. Declarator ParmDeclarator( - DS, D.getContext() == DeclaratorContext::LambdaExprContext - ? DeclaratorContext::LambdaExprParameterContext - : DeclaratorContext::PrototypeContext); + DS, DeclaratorContext == DeclaratorContext::RequiresExprContext + ? DeclaratorContext::RequiresExprContext + : DeclaratorContext == DeclaratorContext::LambdaExprContext + ? DeclaratorContext::LambdaExprParameterContext + : DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -6717,7 +6719,7 @@ void Parser::ParseParameterDeclarationClause( SourceLocation EqualLoc = Tok.getLocation(); // Parse the default argument - if (D.getContext() == DeclaratorContext::MemberContext) { + if (DeclaratorContext == DeclaratorContext::MemberContext) { // If we're inside a class definition, cache the tokens // corresponding to the default argument. We'll actually parse // them when we see the end of the class definition. diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 32dacbcc964..df8388926db 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -756,6 +756,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [C++11] user-defined-literal /// '(' expression ')' /// [C11] generic-selection +/// [C++2a] requires-expression /// '__func__' [C99 6.4.2.2] /// [GNU] '__FUNCTION__' /// [MS] '__FUNCDNAME__' @@ -1601,6 +1602,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, *NotPrimaryExpression = true; return ParseCXXDeleteExpression(false, Tok.getLocation()); + case tok::kw_requires: // [C++2a] requires-expression + return ParseRequiresExpression(); + case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' if (NotPrimaryExpression) *NotPrimaryExpression = true; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 7d477d271dc..036eabb94dd 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/ParseDiagnostic.h" @@ -1299,9 +1301,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( Actions.RecordParsingTemplateParameterDepth( CurTemplateDepthTracker.getOriginalDepth()); - ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); - - // For a generic lambda, each 'auto' within the parameter declaration + ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo, + EllipsisLoc); + // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. // If we've parsed any explicit template parameters, then the depth will // have already been incremented. So we make sure that at most a single @@ -3255,6 +3257,324 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get()); } +/// ParseRequiresExpression - Parse a C++2a requires-expression. +/// C++2a [expr.prim.req]p1 +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// +/// requires-expression: +/// 'requires' requirement-parameter-list[opt] requirement-body +/// +/// requirement-parameter-list: +/// '(' parameter-declaration-clause[opt] ')' +/// +/// requirement-body: +/// '{' requirement-seq '}' +/// +/// requirement-seq: +/// requirement +/// requirement-seq requirement +/// +/// requirement: +/// simple-requirement +/// type-requirement +/// compound-requirement +/// nested-requirement +ExprResult Parser::ParseRequiresExpression() { + assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword"); + SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires' + + llvm::SmallVector<ParmVarDecl *, 2> LocalParameterDecls; + if (Tok.is(tok::l_paren)) { + // requirement parameter list is present. + ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope); + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + if (!Tok.is(tok::r_paren)) { + ParsedAttributes FirstArgAttrs(getAttrFactory()); + SourceLocation EllipsisLoc; + llvm::SmallVector<DeclaratorChunk::ParamInfo, 2> LocalParameters; + DiagnosticErrorTrap Trap(Diags); + ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext, + FirstArgAttrs, LocalParameters, + EllipsisLoc); + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis); + for (auto &ParamInfo : LocalParameters) + LocalParameterDecls.push_back(cast<ParmVarDecl>(ParamInfo.Param)); + if (Trap.hasErrorOccurred()) + SkipUntil(tok::r_paren, StopBeforeMatch); + } + Parens.consumeClose(); + } + + BalancedDelimiterTracker Braces(*this, tok::l_brace); + if (Braces.expectAndConsume()) + return ExprError(); + + // Start of requirement list + llvm::SmallVector<concepts::Requirement *, 2> Requirements; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + + ParseScope BodyScope(this, Scope::DeclScope); + RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr( + RequiresKWLoc, LocalParameterDecls, getCurScope()); + + if (Tok.is(tok::r_brace)) { + // Grammar does not allow an empty body. + // requirement-body: + // { requirement-seq } + // requirement-seq: + // requirement + // requirement-seq requirement + Diag(Tok, diag::err_empty_requires_expr); + // Continue anyway and produce a requires expr with no requirements. + } else { + while (!Tok.is(tok::r_brace)) { + switch (Tok.getKind()) { + case tok::l_brace: { + // Compound requirement + // C++ [expr.prim.req.compound] + // compound-requirement: + // '{' expression '}' 'noexcept'[opt] + // return-type-requirement[opt] ';' + // return-type-requirement: + // trailing-return-type + // '->' cv-qualifier-seq[opt] constrained-parameter + // cv-qualifier-seq[opt] abstract-declarator[opt] + BalancedDelimiterTracker ExprBraces(*this, tok::l_brace); + ExprBraces.consumeOpen(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + ExprBraces.skipToEnd(); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (ExprBraces.consumeClose()) + ExprBraces.skipToEnd(); + + concepts::Requirement *Req = nullptr; + SourceLocation NoexceptLoc; + TryConsumeToken(tok::kw_noexcept, NoexceptLoc); + if (Tok.is(tok::semi)) { + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc); + if (Req) + Requirements.push_back(Req); + break; + } + if (!TryConsumeToken(tok::arrow)) + // User probably forgot the arrow, remind them and try to continue. + Diag(Tok, diag::err_requires_expr_missing_arrow) + << FixItHint::CreateInsertion(Tok.getLocation(), "->"); + // Try to parse a 'type-constraint' + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, + // then this scope-spec is part of + // the typename of a non-type + // template parameter + /*IsTypename=*/true, + /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as + // well parse this correctly for + // possible type names. + /*OnlyNamespace=*/false, + /*SuppressDiagnostic=*/true)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (TryAnnotateTypeConstraint()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!isTypeConstraintAnnotation()) { + Diag(Tok, diag::err_requires_expr_expected_type_constraint); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (Tok.is(tok::annot_cxxscope)) + ConsumeAnnotationToken(); + + Req = Actions.ActOnCompoundRequirement( + Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok), + TemplateParameterDepth); + ConsumeAnnotationToken(); + if (Req) + Requirements.push_back(Req); + break; + } + default: { + bool PossibleRequiresExprInSimpleRequirement = false; + if (Tok.is(tok::kw_requires)) { + auto IsNestedRequirement = [&] { + RevertingTentativeParsingAction TPA(*this); + ConsumeToken(); // 'requires' + if (Tok.is(tok::l_brace)) + // This is a requires expression + // requires (T t) { + // requires { t++; }; + // ... ^ + // } + return false; + if (Tok.is(tok::l_paren)) { + // This might be the parameter list of a requires expression + ConsumeParen(); + auto Res = TryParseParameterDeclarationClause(); + if (Res != TPResult::False) { + // Skip to the closing parenthesis + // FIXME: Don't traverse these tokens twice (here and in + // TryParseParameterDeclarationClause). + unsigned Depth = 1; + while (Depth != 0) { + if (Tok.is(tok::l_paren)) + Depth++; + else if (Tok.is(tok::r_paren)) + Depth--; + ConsumeAnyToken(); + } + // requires (T t) { + // requires () ? + // ... ^ + // - OR - + // requires (int x) ? + // ... ^ + // } + if (Tok.is(tok::l_brace)) + // requires (...) { + // ^ - a requires expression as a + // simple-requirement. + return false; + } + } + return true; + }; + if (IsNestedRequirement()) { + ConsumeToken(); + // Nested requirement + // C++ [expr.prim.req.nested] + // nested-requirement: + // 'requires' constraint-expression ';' + ExprResult ConstraintExpr = + Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + if (auto *Req = + Actions.ActOnNestedRequirement(ConstraintExpr.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } else + PossibleRequiresExprInSimpleRequirement = true; + } else if (Tok.is(tok::kw_typename)) { + // This might be 'typename T::value_type;' (a type requirement) or + // 'typename T::value_type{};' (a simple requirement). + TentativeParsingAction TPA(*this); + + // We need to consume the typename to allow 'requires { typename a; }' + SourceLocation TypenameKWLoc = ConsumeToken(); + if (TryAnnotateCXXScopeToken()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); + ConsumeAnnotationToken(); + } + + if (Tok.isOneOf(tok::identifier, tok::annot_template_id) && + !NextToken().isOneOf(tok::l_brace, tok::l_paren)) { + TPA.Commit(); + SourceLocation NameLoc = Tok.getLocation(); + IdentifierInfo *II = nullptr; + TemplateIdAnnotation *TemplateId = nullptr; + if (Tok.is(tok::identifier)) { + II = Tok.getIdentifierInfo(); + ConsumeToken(); + } else { + TemplateId = takeTemplateIdAnnotation(Tok); + ConsumeAnnotationToken(); + } + + if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS, + NameLoc, II, + TemplateId)) { + Requirements.push_back(Req); + } + break; + } + TPA.Revert(); + } + // Simple requirement + // C++ [expr.prim.req.simple] + // simple-requirement: + // expression ';' + SourceLocation StartLoc = Tok.getLocation(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement) + Diag(StartLoc, diag::warn_requires_expr_in_simple_requirement) + << FixItHint::CreateInsertion(StartLoc, "requires"); + if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + // User may have tried to put some compound requirement stuff here + if (Tok.is(tok::kw_noexcept)) { + Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept) + << FixItHint::CreateInsertion(StartLoc, "{") + << FixItHint::CreateInsertion(Tok.getLocation(), "}"); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } + } + if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + TryConsumeToken(tok::semi); + break; + } + } + if (Requirements.empty()) { + // Don't emit an empty requires expr here to avoid confusing the user with + // other diagnostics quoting an empty requires expression they never + // wrote. + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return ExprError(); + } + } + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls, + Requirements, Braces.getCloseLocation()); +} + static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) { switch (kind) { default: llvm_unreachable("Not a known type trait"); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 2cd158a8b43..f8da1cb89b9 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1261,7 +1261,8 @@ DeclContext *Sema::getFunctionLevelDeclContext() { DeclContext *DC = CurContext; while (true) { - if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC)) { + if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC) || + isa<RequiresExprBodyDecl>(DC)) { DC = DC->getParent(); } else if (isa<CXXMethodDecl>(DC) && cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call && diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 018ac2d7dc9..93e5b4511da 100755 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -17,7 +17,10 @@ #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/Template.h" -#include "clang/AST/ExprCXX.h" +#include "clang/Sema/Overload.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/SemaInternal.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/OperatorPrecedence.h" #include "llvm/ADT/DenseMap.h" @@ -336,6 +339,118 @@ bool Sema::EnsureTemplateArgumentListConstraints( return false; } +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::ExprRequirement *Req, + bool First) { + assert(!Req->isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (Req->getSatisfactionStatus()) { + case concepts::ExprRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + break; + case concepts::ExprRequirement::SS_ExprSubstitutionFailure: { + auto *SubstDiag = Req->getExprSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_NoexceptNotMet: + S.Diag(Req->getNoexceptLoc(), + diag::note_expr_requirement_noexcept_not_met) + << (int)First << Req->getExpr(); + break; + case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: { + auto *SubstDiag = + Req->getReturnTypeRequirement().getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: { + ConceptSpecializationExpr *ConstraintExpr = + Req->getReturnTypeRequirementSubstitutedConstraintExpr(); + if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) + // A simple case - expr type is the type being constrained and the concept + // was not provided arguments. + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied_simple) + << (int)First << S.BuildDecltypeType(Req->getExpr(), + Req->getExpr()->getBeginLoc()) + << ConstraintExpr->getNamedConcept(); + else + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied) + << (int)First << ConstraintExpr; + S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction()); + break; + } + case concepts::ExprRequirement::SS_Satisfied: + llvm_unreachable("We checked this above"); + } +} + +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::TypeRequirement *Req, + bool First) { + assert(!Req->isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (Req->getSatisfactionStatus()) { + case concepts::TypeRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + return; + case concepts::TypeRequirement::SS_SubstitutionFailure: { + auto *SubstDiag = Req->getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_substitution_error) << (int)First + << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + default: + llvm_unreachable("Unknown satisfaction status"); + return; + } +} + +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::NestedRequirement *Req, + bool First) { + if (Req->isSubstitutionFailure()) { + concepts::Requirement::SubstitutionDiagnostic *SubstDiag = + Req->getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + S.DiagnoseUnsatisfiedConstraint(Req->getConstraintSatisfaction(), First); +} + + static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, Expr *SubstExpr, bool First = true) { @@ -412,6 +527,19 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, } S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); return; + } else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) { + for (concepts::Requirement *Req : RE->getRequirements()) + if (!Req->isDependent() && !Req->isSatisfied()) { + if (auto *E = dyn_cast<concepts::ExprRequirement>(Req)) + diagnoseUnsatisfiedRequirement(S, E, First); + else if (auto *T = dyn_cast<concepts::TypeRequirement>(Req)) + diagnoseUnsatisfiedRequirement(S, T, First); + else + diagnoseUnsatisfiedRequirement( + S, cast<concepts::NestedRequirement>(Req), First); + break; + } + return; } S.Diag(SubstExpr->getSourceRange().getBegin(), @@ -434,11 +562,11 @@ static void diagnoseUnsatisfiedConstraintExpr( Record.template get<Expr *>(), First); } -void Sema::DiagnoseUnsatisfiedConstraint( - const ConstraintSatisfaction& Satisfaction) { +void +Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - bool First = true; for (auto &Pair : Satisfaction.Details) { diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; @@ -446,10 +574,10 @@ void Sema::DiagnoseUnsatisfiedConstraint( } void Sema::DiagnoseUnsatisfiedConstraint( - const ASTConstraintSatisfaction &Satisfaction) { + const ASTConstraintSatisfaction &Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - bool First = true; for (auto &Pair : Satisfaction) { diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; @@ -826,3 +954,67 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, << AmbiguousAtomic2->getSourceRange(); return true; } + +concepts::ExprRequirement::ExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + ReturnTypeRequirement Req, SatisfactionStatus Status, + ConceptSpecializationExpr *SubstitutedConstraintExpr) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Status == SS_Dependent, + Status == SS_Dependent && + (E->containsUnexpandedParameterPack() || + Req.containsUnexpandedParameterPack()), + Status == SS_Satisfied), Value(E), NoexceptLoc(NoexceptLoc), + TypeReq(Req), SubstitutedConstraintExpr(SubstitutedConstraintExpr), + Status(Status) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); + assert((Status > SS_TypeRequirementSubstitutionFailure && Req.isTypeConstraint()) == + (SubstitutedConstraintExpr != nullptr)); +} + +concepts::ExprRequirement::ExprRequirement( + SubstitutionDiagnostic *ExprSubstDiag, bool IsSimple, + SourceLocation NoexceptLoc, ReturnTypeRequirement Req) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Req.isDependent(), + Req.containsUnexpandedParameterPack(), /*IsSatisfied=*/false), + Value(ExprSubstDiag), NoexceptLoc(NoexceptLoc), TypeReq(Req), + Status(SS_ExprSubstitutionFailure) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); +} + +concepts::ExprRequirement::ReturnTypeRequirement:: +ReturnTypeRequirement(TemplateParameterList *TPL) : + TypeConstraintInfo(TPL, 0) { + assert(TPL->size() == 1); + const TypeConstraint *TC = + cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint(); + assert(TC && + "TPL must have a template type parameter with a type constraint"); + auto *Constraint = + cast_or_null<ConceptSpecializationExpr>( + TC->getImmediatelyDeclaredConstraint()); + bool Dependent = false; + if (Constraint->getTemplateArgsAsWritten()) { + for (auto &ArgLoc : + Constraint->getTemplateArgsAsWritten()->arguments().drop_front(1)) { + if (ArgLoc.getArgument().isDependent()) { + Dependent = true; + break; + } + } + } + TypeConstraintInfo.setInt(Dependent ? 1 : 0); +} + +concepts::TypeRequirement::TypeRequirement(TypeSourceInfo *T) : + Requirement(RK_Type, T->getType()->isDependentType(), + T->getType()->containsUnexpandedParameterPack(), + // We reach this ctor with either dependent types (in which + // IsSatisfied doesn't matter) or with non-dependent type in + // which the existence of the type indicates satisfaction. + /*IsSatisfied=*/true + ), Value(T), + Status(T->getType()->isDependentType() ? SS_Dependent : SS_Satisfied) {} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 507e4a6cd43..372f3d15859 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6468,6 +6468,8 @@ static bool shouldConsiderLinkage(const VarDecl *VD) { return true; if (DC->isRecord()) return false; + if (isa<RequiresExprBodyDecl>(DC)) + return false; llvm_unreachable("Unexpected context"); } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 5aedbe7644e..193eaa3e01f 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1386,6 +1386,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::StringLiteralClass: case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: // These expressions can never throw. return CT_Cannot; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5f4071924d3..ea4b93ee6a5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -350,6 +350,17 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs, } } + if (isa<ParmVarDecl>(D) && isa<RequiresExprBodyDecl>(D->getDeclContext()) && + !isUnevaluatedContext()) { + // C++ [expr.prim.req.nested] p3 + // A local parameter shall only appear as an unevaluated operand + // (Clause 8) within the constraint-expression. + Diag(Loc, diag::err_requires_expr_parameter_referenced_in_evaluated_context) + << D; + Diag(D->getLocation(), diag::note_entity_declared_at) << D; + return true; + } + return false; } @@ -1904,7 +1915,7 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, bool RefersToCapturedVariable = isa<VarDecl>(D) && NeedToCaptureVariable(cast<VarDecl>(D), NameInfo.getLoc()); - + DeclRefExpr *E = DeclRefExpr::Create( Context, NNS, TemplateKWLoc, D, RefersToCapturedVariable, NameInfo, Ty, VK, FoundD, TemplateArgs, getNonOdrUseReasonInCurrentContext(D)); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 938420d85c6..192c237b6c1 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "clang/Sema/Template.h" #include "clang/Sema/SemaInternal.h" #include "TreeTransform.h" #include "TypeLocBuilder.h" @@ -8331,3 +8332,215 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, SourceLocation KeywordLoc, return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo); } + +concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) { + return BuildExprRequirement(E, /*IsSimple=*/true, + /*NoexceptLoc=*/SourceLocation(), + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement * +Sema::ActOnTypeRequirement(SourceLocation TypenameKWLoc, CXXScopeSpec &SS, + SourceLocation NameLoc, IdentifierInfo *TypeName, + TemplateIdAnnotation *TemplateId) { + assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) && + "Exactly one of TypeName and TemplateId must be specified."); + TypeSourceInfo *TSI = nullptr; + if (TypeName) { + QualType T = CheckTypenameType(ETK_Typename, TypenameKWLoc, + SS.getWithLocInContext(Context), *TypeName, + NameLoc, &TSI, /*DeducedTypeContext=*/false); + if (T.isNull()) + return nullptr; + } else { + ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS, + TemplateId->TemplateKWLoc, + TemplateId->Template, TemplateId->Name, + TemplateId->TemplateNameLoc, + TemplateId->LAngleLoc, ArgsPtr, + TemplateId->RAngleLoc); + if (T.isInvalid()) + return nullptr; + if (GetTypeFromParser(T.get(), &TSI).isNull()) + return nullptr; + } + return BuildTypeRequirement(TSI); +} + +concepts::Requirement * +Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) { + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement * +Sema::ActOnCompoundRequirement( + Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, unsigned Depth) { + // C++2a [expr.prim.req.compound] p1.3.3 + // [..] the expression is deduced against an invented function template + // F [...] F is a void function template with a single type template + // parameter T declared with the constrained-parameter. Form a new + // cv-qualifier-seq cv by taking the union of const and volatile specifiers + // around the constrained-parameter. F has a single parameter whose + // type-specifier is cv T followed by the abstract-declarator. [...] + // + // The cv part is done in the calling function - we get the concept with + // arguments and the abstract declarator with the correct CV qualification and + // have to synthesize T and the single parameter of F. + auto &II = Context.Idents.get("expr-type"); + auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext, + SourceLocation(), + SourceLocation(), Depth, + /*Index=*/0, &II, + /*Typename=*/true, + /*ParameterPack=*/false, + /*HasTypeConstraint=*/true); + + if (ActOnTypeConstraint(SS, TypeConstraint, TParam, + /*EllpsisLoc=*/SourceLocation())) + // Just produce a requirement with no type requirements. + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {}); + + auto *TPL = TemplateParameterList::Create(Context, SourceLocation(), + SourceLocation(), + ArrayRef<NamedDecl *>(TParam), + SourceLocation(), + /*RequiresClause=*/nullptr); + return BuildExprRequirement( + E, /*IsSimple=*/false, NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement(TPL)); +} + +concepts::ExprRequirement * +Sema::BuildExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + auto Status = concepts::ExprRequirement::SS_Satisfied; + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; + if (E->isInstantiationDependent() || ReturnTypeRequirement.isDependent()) + Status = concepts::ExprRequirement::SS_Dependent; + else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can) + Status = concepts::ExprRequirement::SS_NoexceptNotMet; + else if (ReturnTypeRequirement.isSubstitutionFailure()) + Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure; + else if (ReturnTypeRequirement.isTypeConstraint()) { + // C++2a [expr.prim.req]p1.3.3 + // The immediately-declared constraint ([temp]) of decltype((E)) shall + // be satisfied. + TemplateParameterList *TPL = + ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); + QualType MatchedType = + BuildDecltypeType(E, E->getBeginLoc()).getCanonicalType(); + llvm::SmallVector<TemplateArgument, 1> Args; + Args.push_back(TemplateArgument(MatchedType)); + TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args); + MultiLevelTemplateArgumentList MLTAL(TAL); + for (unsigned I = 0; I < TPL->getDepth(); ++I) + MLTAL.addOuterRetainedLevel(); + Expr *IDC = + cast<TemplateTypeParmDecl>(TPL->getParam(0))->getTypeConstraint() + ->getImmediatelyDeclaredConstraint(); + ExprResult Constraint = SubstExpr(IDC, MLTAL); + assert(!Constraint.isInvalid() && + "Substitution cannot fail as it is simply putting a type template " + "argument into a concept specialization expression's parameter."); + + SubstitutedConstraintExpr = + cast<ConceptSpecializationExpr>(Constraint.get()); + if (!SubstitutedConstraintExpr->isSatisfied()) + Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied; + } + return new (Context) concepts::ExprRequirement(E, IsSimple, NoexceptLoc, + ReturnTypeRequirement, Status, + SubstitutedConstraintExpr); +} + +concepts::ExprRequirement * +Sema::BuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic, + bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic, + IsSimple, NoexceptLoc, + ReturnTypeRequirement); +} + +concepts::TypeRequirement * +Sema::BuildTypeRequirement(TypeSourceInfo *Type) { + return new (Context) concepts::TypeRequirement(Type); +} + +concepts::TypeRequirement * +Sema::BuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (Context) concepts::TypeRequirement(SubstDiag); +} + +concepts::Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) { + return BuildNestedRequirement(Constraint); +} + +concepts::NestedRequirement * +Sema::BuildNestedRequirement(Expr *Constraint) { + ConstraintSatisfaction Satisfaction; + if (!Constraint->isInstantiationDependent() && + CheckConstraintSatisfaction(Constraint, Satisfaction)) + return nullptr; + return new (Context) concepts::NestedRequirement(Context, Constraint, + Satisfaction); +} + +concepts::NestedRequirement * +Sema::BuildNestedRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (Context) concepts::NestedRequirement(SubstDiag); +} + +RequiresExprBodyDecl * +Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef<ParmVarDecl *> LocalParameters, + Scope *BodyScope) { + assert(BodyScope); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext, + RequiresKWLoc); + + PushDeclContext(BodyScope, Body); + + for (ParmVarDecl *Param : LocalParameters) { + if (Param->hasDefaultArg()) + // C++2a [expr.prim.req] p4 + // [...] A local parameter of a requires-expression shall not have a + // default argument. [...] + Diag(Param->getDefaultArgRange().getBegin(), + diag::err_requires_expr_local_parameter_default_argument); + // Ignore default argument and move on + + Param->setDeclContext(Body); + // If this has an identifier, add it to the scope stack. + if (Param->getIdentifier()) { + CheckShadow(BodyScope, Param); + PushOnScopeChains(Param, BodyScope); + } + } + return Body; +} + +void Sema::ActOnFinishRequiresExpr() { + assert(CurContext && "DeclContext imbalance!"); + CurContext = CurContext->getLexicalParent(); + assert(CurContext && "Popped translation unit!"); +} + +ExprResult +Sema::ActOnRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation ClosingBraceLoc) { + return RequiresExpr::Create(Context, RequiresKWLoc, Body, LocalParameters, + Requirements, ClosingBraceLoc); +} diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0ed51de0cc1..8d96404a5c2 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1575,7 +1575,9 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() { unsigned N = CodeSynthesisContexts.size(); for (unsigned I = CodeSynthesisContextLookupModules.size(); I != N; ++I) { - Module *M = getDefiningModule(*this, CodeSynthesisContexts[I].Entity); + Module *M = CodeSynthesisContexts[I].Entity ? + getDefiningModule(*this, CodeSynthesisContexts[I].Entity) : + nullptr; if (M && !LookupModulesCache.insert(M).second) M = nullptr; CodeSynthesisContextLookupModules.push_back(M); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 8a50a9e1538..661a66246a5 100755 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10010,24 +10010,12 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, << FixItHint::CreateRemoval(TypenameLoc); NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); + TypeSourceInfo *TSI = nullptr; QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None, - TypenameLoc, QualifierLoc, II, IdLoc); + TypenameLoc, QualifierLoc, II, IdLoc, &TSI, + /*DeducedTSTContext=*/true); if (T.isNull()) return true; - - TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T); - if (isa<DependentNameType>(T)) { - DependentNameTypeLoc TL = TSI->getTypeLoc().castAs<DependentNameTypeLoc>(); - TL.setElaboratedKeywordLoc(TypenameLoc); - TL.setQualifierLoc(QualifierLoc); - TL.setNameLoc(IdLoc); - } else { - ElaboratedTypeLoc TL = TSI->getTypeLoc().castAs<ElaboratedTypeLoc>(); - TL.setElaboratedKeywordLoc(TypenameLoc); - TL.setQualifierLoc(QualifierLoc); - TL.getNamedTypeLoc().castAs<TypeSpecTypeLoc>().setNameLoc(IdLoc); - } - return CreateParsedType(T, TSI); } @@ -10164,6 +10152,35 @@ static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, return true; } +QualType +Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, + SourceLocation KeywordLoc, + NestedNameSpecifierLoc QualifierLoc, + const IdentifierInfo &II, + SourceLocation IILoc, + TypeSourceInfo **TSI, + bool DeducedTSTContext) { + QualType T = CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, II, IILoc, + DeducedTSTContext); + if (T.isNull()) + return QualType(); + + *TSI = Context.CreateTypeSourceInfo(T); + if (isa<DependentNameType>(T)) { + DependentNameTypeLoc TL = + (*TSI)->getTypeLoc().castAs<DependentNameTypeLoc>(); + TL.setElaboratedKeywordLoc(KeywordLoc); + TL.setQualifierLoc(QualifierLoc); + TL.setNameLoc(IILoc); + } else { + ElaboratedTypeLoc TL = (*TSI)->getTypeLoc().castAs<ElaboratedTypeLoc>(); + TL.setElaboratedKeywordLoc(KeywordLoc); + TL.setQualifierLoc(QualifierLoc); + TL.getNamedTypeLoc().castAs<TypeSpecTypeLoc>().setNameLoc(IILoc); + } + return T; +} + /// Build the type that describes a C++ typename specifier, /// e.g., "typename T::type". QualType @@ -10171,32 +10188,38 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, SourceLocation KeywordLoc, NestedNameSpecifierLoc QualifierLoc, const IdentifierInfo &II, - SourceLocation IILoc) { + SourceLocation IILoc, bool DeducedTSTContext) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); - DeclContext *Ctx = computeDeclContext(SS); - if (!Ctx) { - // If the nested-name-specifier is dependent and couldn't be - // resolved to a type, build a typename type. - assert(QualifierLoc.getNestedNameSpecifier()->isDependent()); - return Context.getDependentNameType(Keyword, - QualifierLoc.getNestedNameSpecifier(), - &II); + DeclContext *Ctx = nullptr; + if (QualifierLoc) { + Ctx = computeDeclContext(SS); + if (!Ctx) { + // If the nested-name-specifier is dependent and couldn't be + // resolved to a type, build a typename type. + assert(QualifierLoc.getNestedNameSpecifier()->isDependent()); + return Context.getDependentNameType(Keyword, + QualifierLoc.getNestedNameSpecifier(), + &II); + } + + // If the nested-name-specifier refers to the current instantiation, + // the "typename" keyword itself is superfluous. In C++03, the + // program is actually ill-formed. However, DR 382 (in C++0x CD1) + // allows such extraneous "typename" keywords, and we retroactively + // apply this DR to C++03 code with only a warning. In any case we continue. + + if (RequireCompleteDeclContext(SS, Ctx)) + return QualType(); } - // If the nested-name-specifier refers to the current instantiation, - // the "typename" keyword itself is superfluous. In C++03, the - // program is actually ill-formed. However, DR 382 (in C++0x CD1) - // allows such extraneous "typename" keywords, and we retroactively - // apply this DR to C++03 code with only a warning. In any case we continue. - - if (RequireCompleteDeclContext(SS, Ctx)) - return QualType(); - DeclarationName Name(&II); LookupResult Result(*this, Name, IILoc, LookupOrdinaryName); - LookupQualifiedName(Result, Ctx, SS); + if (Ctx) + LookupQualifiedName(Result, Ctx, SS); + else + LookupName(Result, CurScope); unsigned DiagID = 0; Decl *Referenced = nullptr; switch (Result.getResultKind()) { @@ -10205,7 +10228,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // a more specific diagnostic. SourceRange CondRange; Expr *Cond = nullptr; - if (isEnableIf(QualifierLoc, II, CondRange, Cond)) { + if (Ctx && isEnableIf(QualifierLoc, II, CondRange, Cond)) { // If we have a condition, narrow it down to the specific failed // condition. if (Cond) { @@ -10221,12 +10244,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, return QualType(); } - Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if) + Diag(CondRange.getBegin(), + diag::err_typename_nested_not_found_enable_if) << Ctx << CondRange; return QualType(); } - DiagID = diag::err_typename_nested_not_found; + DiagID = Ctx ? diag::err_typename_nested_not_found + : diag::err_unknown_typename; break; } @@ -10292,6 +10317,19 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // is a placeholder for a deduced class type [...]. if (getLangOpts().CPlusPlus17) { if (auto *TD = getAsTypeTemplateDecl(Result.getFoundDecl())) { + if (!DeducedTSTContext) { + QualType T(QualifierLoc + ? QualifierLoc.getNestedNameSpecifier()->getAsType() + : nullptr, 0); + if (!T.isNull()) + Diag(IILoc, diag::err_dependent_deduced_tst) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << T; + else + Diag(IILoc, diag::err_deduced_tst) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)); + Diag(TD->getLocation(), diag::note_template_decl_here); + return QualType(); + } return Context.getElaboratedType( Keyword, QualifierLoc.getNestedNameSpecifier(), Context.getDeducedTemplateSpecializationType(TemplateName(TD), @@ -10299,12 +10337,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, } } - DiagID = diag::err_typename_nested_not_type; + DiagID = Ctx ? diag::err_typename_nested_not_type + : diag::err_typename_not_type; Referenced = Result.getFoundDecl(); break; case LookupResult::FoundOverloaded: - DiagID = diag::err_typename_nested_not_type; + DiagID = Ctx ? diag::err_typename_nested_not_type + : diag::err_typename_not_type; Referenced = *Result.begin(); break; @@ -10316,9 +10356,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // type. Emit an appropriate diagnostic and return an error. SourceRange FullRange(KeywordLoc.isValid() ? KeywordLoc : SS.getBeginLoc(), IILoc); - Diag(IILoc, DiagID) << FullRange << Name << Ctx; + if (Ctx) + Diag(IILoc, DiagID) << FullRange << Name << Ctx; + else + Diag(IILoc, DiagID) << FullRange << Name; if (Referenced) - Diag(Referenced->getLocation(), diag::note_typename_refers_here) + Diag(Referenced->getLocation(), + Ctx ? diag::note_typename_member_refers_here + : diag::note_typename_refers_here) << Name; return QualType(); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index af41e231134..39bc28d6230 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -26,6 +26,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/TemplateInstCallback.h" +#include "clang/Sema/SemaConcept.h" #include "llvm/Support/TimeProfiler.h" using namespace clang; @@ -199,8 +200,10 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case DeducedTemplateArgumentSubstitution: case PriorTemplateArgumentSubstitution: case ConstraintsCheck: + case NestedRequirementConstraintsCheck: return true; + case RequirementInstantiation: case DefaultTemplateArgumentChecking: case DeclaringSpecialMember: case DeclaringImplicitEqualityComparison: @@ -247,7 +250,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Inst.InstantiationRange = InstantiationRange; SemaRef.pushCodeSynthesisContext(Inst); - AlreadyInstantiating = + AlreadyInstantiating = !Inst.Entity ? false : !SemaRef.InstantiatingSpecializations .insert(std::make_pair(Inst.Entity->getCanonicalDecl(), Inst.Kind)) .second; @@ -366,6 +369,26 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::Requirement *Req, sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::RequirementInstantiation, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None, &DeductionInfo) {} + + +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::NestedRequirement *Req, ConstraintsCheck, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::NestedRequirementConstraintsCheck, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None) {} + + +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, ConstraintsCheck, NamedDecl *Template, ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange) : InstantiatingTemplate( @@ -446,8 +469,9 @@ void Sema::InstantiatingTemplate::Clear() { if (!Invalid) { if (!AlreadyInstantiating) { auto &Active = SemaRef.CodeSynthesisContexts.back(); - SemaRef.InstantiatingSpecializations.erase( - std::make_pair(Active.Entity, Active.Kind)); + if (Active.Entity) + SemaRef.InstantiatingSpecializations.erase( + std::make_pair(Active.Entity, Active.Kind)); } atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef, @@ -684,6 +708,18 @@ void Sema::PrintInstantiationStack() { << Active->InstantiationRange; break; + case CodeSynthesisContext::RequirementInstantiation: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_requirement_instantiation_here) + << Active->InstantiationRange; + break; + + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + Diags.Report(Active->PointOfInstantiation, + diag::note_nested_requirement_here) + << Active->InstantiationRange; + break; + case CodeSynthesisContext::DeclaringSpecialMember: Diags.Report(Active->PointOfInstantiation, diag::note_in_declaration_of_implicit_special_member) @@ -788,6 +824,7 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { case CodeSynthesisContext::ConstraintsCheck: case CodeSynthesisContext::ParameterMappingSubstitution: case CodeSynthesisContext::ConstraintNormalization: + case CodeSynthesisContext::NestedRequirementConstraintsCheck: // This is a template instantiation, so there is no SFINAE. return None; @@ -802,9 +839,10 @@ Optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution: case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: case CodeSynthesisContext::ConstraintSubstitution: - // We're either substituting explicitly-specified template arguments - // or deduced template arguments or a constraint expression, so SFINAE - // applies. + case CodeSynthesisContext::RequirementInstantiation: + // We're either substituting explicitly-specified template arguments, + // deduced template arguments, a constraint expression or a requirement + // in a requires expression, so SFINAE applies. assert(Active->DeductionInfo && "Missing deduction info pointer"); return Active->DeductionInfo; @@ -1056,6 +1094,41 @@ namespace { return TreeTransform<TemplateInstantiator>::TransformLambdaExpr(E); } + ExprResult TransformRequiresExpr(RequiresExpr *E) { + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); + return TreeTransform<TemplateInstantiator>::TransformRequiresExpr(E); + } + + bool TransformRequiresExprRequirements( + ArrayRef<concepts::Requirement *> Reqs, + SmallVectorImpl<concepts::Requirement *> &Transformed) { + bool SatisfactionDetermined = false; + for (concepts::Requirement *Req : Reqs) { + concepts::Requirement *TransReq = nullptr; + if (!SatisfactionDetermined) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) + TransReq = TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) + TransReq = TransformExprRequirement(ExprReq); + else + TransReq = TransformNestedRequirement( + cast<concepts::NestedRequirement>(Req)); + if (!TransReq) + return true; + if (!TransReq->isDependent() && !TransReq->isSatisfied()) + // [expr.prim.req]p6 + // [...] The substitution and semantic constraint checking + // proceeds in lexical order and stops when a condition that + // determines the result of the requires-expression is + // encountered. [..] + SatisfactionDetermined = true; + } else + TransReq = Req; + Transformed.push_back(TransReq); + } + return false; + } + TemplateParameterList *TransformTemplateParameterList( TemplateParameterList *OrigTPL) { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; @@ -1065,6 +1138,14 @@ namespace { /* DeclContext *Owner */ Owner, TemplateArgs); return DeclInstantiator.SubstTemplateParams(OrigTPL); } + + concepts::TypeRequirement * + TransformTypeRequirement(concepts::TypeRequirement *Req); + concepts::ExprRequirement * + TransformExprRequirement(concepts::ExprRequirement *Req); + concepts::NestedRequirement * + TransformNestedRequirement(concepts::NestedRequirement *Req); + private: ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm, SourceLocation loc, @@ -1669,6 +1750,163 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType( return Result; } +template<typename EntityPrinter> +static concepts::Requirement::SubstitutionDiagnostic * +createSubstDiag(Sema &S, TemplateDeductionInfo &Info, EntityPrinter Printer) { + SmallString<128> Message; + SourceLocation ErrorLoc; + if (Info.hasSFINAEDiagnostic()) { + PartialDiagnosticAt PDA(SourceLocation(), + PartialDiagnostic::NullDiagnostic{}); + Info.takeSFINAEDiagnostic(PDA); + PDA.second.EmitToString(S.getDiagnostics(), Message); + ErrorLoc = PDA.first; + } else { + ErrorLoc = Info.getLocation(); + } + char *MessageBuf = new (S.Context) char[Message.size()]; + std::copy(Message.begin(), Message.end(), MessageBuf); + SmallString<128> Entity; + llvm::raw_svector_ostream OS(Entity); + Printer(OS); + char *EntityBuf = new (S.Context) char[Entity.size()]; + std::copy(Entity.begin(), Entity.end(), EntityBuf); + return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{ + StringRef(EntityBuf, Entity.size()), ErrorLoc, + StringRef(MessageBuf, Message.size())}; +} + +concepts::TypeRequirement * +TemplateInstantiator::TransformTypeRequirement(concepts::TypeRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getType()->getTypeLoc().getBeginLoc()); + Sema::InstantiatingTemplate TypeInst(SemaRef, + Req->getType()->getTypeLoc().getBeginLoc(), Req, Info, + Req->getType()->getTypeLoc().getSourceRange()); + if (TypeInst.isInvalid()) + return nullptr; + TypeSourceInfo *TransType = TransformType(Req->getType()); + if (!TransType || Trap.hasErrorOccurred()) + return RebuildTypeRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getType()->getType().print(OS, SemaRef.getPrintingPolicy()); + })); + return RebuildTypeRequirement(TransType); +} + +concepts::ExprRequirement * +TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getExpr()->getBeginLoc()); + + llvm::PointerUnion<Expr *, concepts::Requirement::SubstitutionDiagnostic *> + TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + Sema::InstantiatingTemplate ExprInst(SemaRef, Req->getExpr()->getBeginLoc(), + Req, Info, + Req->getExpr()->getSourceRange()); + if (ExprInst.isInvalid()) + return nullptr; + ExprResult TransExprRes = TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid() || Trap.hasErrorOccurred()) + TransExpr = createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + }); + else + TransExpr = TransExprRes.get(); + } + + llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTypeConstraint()) { + TemplateParameterList *OrigTPL = + RetReq.getTypeConstraintTemplateParameterList(); + Sema::InstantiatingTemplate TPLInst(SemaRef, OrigTPL->getTemplateLoc(), + Req, Info, OrigTPL->getSourceRange()); + if (TPLInst.isInvalid()) + return nullptr; + TemplateParameterList *TPL = + TransformTemplateParameterList(OrigTPL); + if (!TPL) + TransRetReq.emplace(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint() + ->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); + })); + else { + TPLInst.Clear(); + TransRetReq.emplace(TPL); + } + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast<Expr *>()) + return RebuildExprRequirement(E, Req->isSimple(), Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return RebuildExprRequirement( + TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(), + Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +concepts::NestedRequirement * +TemplateInstantiator::TransformNestedRequirement( + concepts::NestedRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + Sema::InstantiatingTemplate ReqInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, + Sema::InstantiatingTemplate::ConstraintsCheck{}, + Req->getConstraintExpr()->getSourceRange()); + + ExprResult TransConstraint; + TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); + { + EnterExpressionEvaluationContext ContextRAII( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Sema::SFINAETrap Trap(SemaRef); + Sema::InstantiatingTemplate ConstrInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, Info, + Req->getConstraintExpr()->getSourceRange()); + if (ConstrInst.isInvalid()) + return nullptr; + TransConstraint = TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid() || Trap.hasErrorOccurred()) + return RebuildNestedRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getConstraintExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + })); + } + return RebuildNestedRequirement(TransConstraint.get()); +} + + /// Perform substitution on the type T with a given set of template /// arguments. /// diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 64500d0a26d..a470cfc8744 100755 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3600,6 +3600,12 @@ Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) { llvm_unreachable("Concept definitions cannot reside inside a template"); } +Decl * +TemplateDeclInstantiator::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { + return RequiresExprBodyDecl::Create(SemaRef.Context, D->getDeclContext(), + D->getBeginLoc()); +} + Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) { llvm_unreachable("Unexpected decl"); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3884fdae8fe..bafeed6e844 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2994,6 +2994,9 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::PrototypeContext: Error = 0; break; + case DeclaratorContext::RequiresExprContext: + Error = 21; + break; case DeclaratorContext::LambdaExprParameterContext: // In C++14, generic lambdas allow 'auto' in their parameters. if (!SemaRef.getLangOpts().CPlusPlus14 || @@ -3221,6 +3224,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: case DeclaratorContext::KNRTypeListContext: + case DeclaratorContext::RequiresExprContext: // C++ [dcl.fct]p6: // Types shall not be defined in return or parameter types. DiagID = diag::err_type_defined_in_param_type; @@ -4279,6 +4283,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TypeNameContext: case DeclaratorContext::FunctionalCastContext: + case DeclaratorContext::RequiresExprContext: // Don't infer in these contexts. break; } @@ -5227,6 +5232,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, switch (D.getContext()) { case DeclaratorContext::PrototypeContext: case DeclaratorContext::LambdaExprParameterContext: + case DeclaratorContext::RequiresExprContext: // C++0x [dcl.fct]p13: // [...] When it is part of a parameter-declaration-clause, the // parameter pack is a function parameter pack (14.5.3). The type T diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 3b827fbc950..1f725117383 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -19,6 +19,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" @@ -509,6 +510,15 @@ public: DeclarationNameInfo TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo); + bool TransformRequiresExprRequirements(ArrayRef<concepts::Requirement *> Reqs, + llvm::SmallVectorImpl<concepts::Requirement *> &Transformed); + concepts::TypeRequirement * + TransformTypeRequirement(concepts::TypeRequirement *Req); + concepts::ExprRequirement * + TransformExprRequirement(concepts::ExprRequirement *Req); + concepts::NestedRequirement * + TransformNestedRequirement(concepts::NestedRequirement *Req); + /// Transform the given template name. /// /// \param SS The nested-name-specifier that qualifies the template @@ -1056,23 +1066,8 @@ public: } if (Keyword == ETK_None || Keyword == ETK_Typename) { - QualType T = SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, - *Id, IdLoc); - // If a dependent name resolves to a deduced template specialization type, - // check that we're in one of the syntactic contexts permitting it. - if (!DeducedTSTContext) { - if (auto *Deduced = dyn_cast_or_null<DeducedTemplateSpecializationType>( - T.isNull() ? nullptr : T->getContainedDeducedType())) { - SemaRef.Diag(IdLoc, diag::err_dependent_deduced_tst) - << (int)SemaRef.getTemplateNameKindForDiagnostics( - Deduced->getTemplateName()) - << QualType(QualifierLoc.getNestedNameSpecifier()->getAsType(), 0); - if (auto *TD = Deduced->getTemplateName().getAsTemplateDecl()) - SemaRef.Diag(TD->getLocation(), diag::note_template_decl_here); - return QualType(); - } - } - return T; + return SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, + *Id, IdLoc, DeducedTSTContext); } TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword); @@ -3078,7 +3073,56 @@ public: return Result; } - /// \brief Build a new Objective-C boxed expression. + /// \brief Build a new requires expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef<ParmVarDecl *> LocalParameters, + ArrayRef<concepts::Requirement *> Requirements, + SourceLocation ClosingBraceLoc) { + return RequiresExpr::Create(SemaRef.Context, RequiresKWLoc, Body, + LocalParameters, Requirements, ClosingBraceLoc); + } + + concepts::TypeRequirement * + RebuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return SemaRef.BuildTypeRequirement(SubstDiag); + } + + concepts::TypeRequirement *RebuildTypeRequirement(TypeSourceInfo *T) { + return SemaRef.BuildTypeRequirement(T); + } + + concepts::ExprRequirement * + RebuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag, bool IsSimple, + SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement Ret) { + return SemaRef.BuildExprRequirement(SubstDiag, IsSimple, NoexceptLoc, + std::move(Ret)); + } + + concepts::ExprRequirement * + RebuildExprRequirement(Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement Ret) { + return SemaRef.BuildExprRequirement(E, IsSimple, NoexceptLoc, + std::move(Ret)); + } + + concepts::NestedRequirement * + RebuildNestedRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return SemaRef.BuildNestedRequirement(SubstDiag); + } + + concepts::NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { + return SemaRef.BuildNestedRequirement(Constraint); + } + + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. @@ -11179,6 +11223,146 @@ TreeTransform<Derived>::TransformConceptSpecializationExpr( &TransArgs); } +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformRequiresExpr(RequiresExpr *E) { + SmallVector<ParmVarDecl*, 4> TransParams; + SmallVector<QualType, 4> TransParamTypes; + Sema::ExtParameterInfoBuilder ExtParamInfos; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create( + getSema().Context, E->getBody()->getDeclContext(), + E->getBody()->getBeginLoc()); + + Sema::ContextRAII SavedContext(getSema(), Body, /*NewThisContext*/false); + + if (getDerived().TransformFunctionTypeParams(E->getRequiresKWLoc(), + E->getLocalParameters(), + /*ParamTypes=*/nullptr, + /*ParamInfos=*/nullptr, + TransParamTypes, &TransParams, + ExtParamInfos)) + return ExprError(); + + for (ParmVarDecl *Param : TransParams) + Param->setDeclContext(Body); + + SmallVector<concepts::Requirement *, 4> TransReqs; + if (getDerived().TransformRequiresExprRequirements(E->getRequirements(), + TransReqs)) + return ExprError(); + + for (concepts::Requirement *Req : TransReqs) { + if (auto *ER = dyn_cast<concepts::ExprRequirement>(Req)) { + if (ER->getReturnTypeRequirement().isTypeConstraint()) { + ER->getReturnTypeRequirement() + .getTypeConstraintTemplateParameterList()->getParam(0) + ->setDeclContext(Body); + } + } + } + + return getDerived().RebuildRequiresExpr(E->getRequiresKWLoc(), Body, + TransParams, TransReqs, + E->getRBraceLoc()); +} + +template<typename Derived> +bool TreeTransform<Derived>::TransformRequiresExprRequirements( + ArrayRef<concepts::Requirement *> Reqs, + SmallVectorImpl<concepts::Requirement *> &Transformed) { + for (concepts::Requirement *Req : Reqs) { + concepts::Requirement *TransReq = nullptr; + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) + TransReq = getDerived().TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) + TransReq = getDerived().TransformExprRequirement(ExprReq); + else + TransReq = getDerived().TransformNestedRequirement( + cast<concepts::NestedRequirement>(Req)); + if (!TransReq) + return true; + Transformed.push_back(TransReq); + } + return false; +} + +template<typename Derived> +concepts::TypeRequirement * +TreeTransform<Derived>::TransformTypeRequirement( + concepts::TypeRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + TypeSourceInfo *TransType = getDerived().TransformType(Req->getType()); + if (!TransType) + return nullptr; + return getDerived().RebuildTypeRequirement(TransType); +} + +template<typename Derived> +concepts::ExprRequirement * +TreeTransform<Derived>::TransformExprRequirement(concepts::ExprRequirement *Req) { + llvm::PointerUnion<Expr *, concepts::Requirement::SubstitutionDiagnostic *> TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + ExprResult TransExprRes = getDerived().TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid()) + return nullptr; + TransExpr = TransExprRes.get(); + } + + llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTypeConstraint()) { + TemplateParameterList *OrigTPL = + RetReq.getTypeConstraintTemplateParameterList(); + TemplateParameterList *TPL = + getDerived().TransformTemplateParameterList(OrigTPL); + if (!TPL) + return nullptr; + TransRetReq.emplace(TPL); + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast<Expr *>()) + return getDerived().RebuildExprRequirement(E, Req->isSimple(), + Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return getDerived().RebuildExprRequirement( + TransExpr.get<concepts::Requirement::SubstitutionDiagnostic *>(), + Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +template<typename Derived> +concepts::NestedRequirement * +TreeTransform<Derived>::TransformNestedRequirement( + concepts::NestedRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + ExprResult TransConstraint = + getDerived().TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid()) + return nullptr; + return getDerived().RebuildNestedRequirement(TransConstraint.get()); +} template<typename Derived> ExprResult diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index cdb5b17022c..f93f1f77405 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -402,6 +402,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Binding: case Decl::Concept: case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: return false; // These indirectly derive from Redeclarable<T> but are not actually diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 96a7d5ae0a3..4fd079e9d8e 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -375,6 +375,7 @@ namespace clang { void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); DeclID VisitTemplateDecl(TemplateDecl *D); void VisitConceptDecl(ConceptDecl *D); + void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D); RedeclarableResult VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D); @@ -2037,6 +2038,9 @@ void ASTDeclReader::VisitConceptDecl(ConceptDecl *D) { mergeMergeable(D); } +void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { +} + ASTDeclReader::RedeclarableResult ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { RedeclarableResult Redecl = VisitRedeclarable(D); @@ -3839,6 +3843,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_CONCEPT: D = ConceptDecl::CreateDeserialized(Context, ID); break; + case DECL_REQUIRES_EXPR_BODY: + D = RequiresExprBodyDecl::CreateDeserialized(Context, ID); + break; case DECL_STATIC_ASSERT: D = StaticAssertDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f558c26b5f1..5dd0ef9d43c 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -724,27 +724,15 @@ void ASTStmtReader::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { E->setRParenLoc(readSourceLocation()); } -void ASTStmtReader::VisitConceptSpecializationExpr( - ConceptSpecializationExpr *E) { - VisitExpr(E); - unsigned NumTemplateArgs = Record.readInt(); - E->NestedNameSpec = Record.readNestedNameSpecifierLoc(); - E->TemplateKWLoc = Record.readSourceLocation(); - E->ConceptName = Record.readDeclarationNameInfo(); - E->NamedConcept = readDeclAs<ConceptDecl>(); - E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); - llvm::SmallVector<TemplateArgument, 4> Args; - for (unsigned I = 0; I < NumTemplateArgs; ++I) - Args.push_back(Record.readTemplateArgument()); - E->setTemplateArguments(Args); +static ConstraintSatisfaction +readConstraintSatisfaction(ASTRecordReader &Record) { ConstraintSatisfaction Satisfaction; Satisfaction.IsSatisfied = Record.readInt(); if (!Satisfaction.IsSatisfied) { unsigned NumDetailRecords = Record.readInt(); for (unsigned i = 0; i != NumDetailRecords; ++i) { Expr *ConstraintExpr = Record.readExpr(); - bool IsDiagnostic = Record.readInt(); - if (IsDiagnostic) { + if (bool IsDiagnostic = Record.readInt()) { SourceLocation DiagLocation = Record.readSourceLocation(); std::string DiagMessage = Record.readString(); Satisfaction.Details.emplace_back( @@ -755,8 +743,137 @@ void ASTStmtReader::VisitConceptSpecializationExpr( Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); } } - E->Satisfaction = ASTConstraintSatisfaction::Create(Record.getContext(), - Satisfaction); + return Satisfaction; +} + +void ASTStmtReader::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + unsigned NumTemplateArgs = Record.readInt(); + E->NestedNameSpec = Record.readNestedNameSpecifierLoc(); + E->TemplateKWLoc = Record.readSourceLocation(); + E->ConceptName = Record.readDeclarationNameInfo(); + E->NamedConcept = readDeclAs<ConceptDecl>(); + E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + llvm::SmallVector<TemplateArgument, 4> Args; + for (unsigned I = 0; I < NumTemplateArgs; ++I) + Args.push_back(Record.readTemplateArgument()); + E->setTemplateArguments(Args); + E->Satisfaction = E->isValueDependent() ? nullptr : + ASTConstraintSatisfaction::Create(Record.getContext(), + readConstraintSatisfaction(Record)); +} + +static concepts::Requirement::SubstitutionDiagnostic * +readSubstitutionDiagnostic(ASTRecordReader &Record) { + std::string SubstitutedEntity = Record.readString(); + SourceLocation DiagLoc = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + return new (Record.getContext()) + concepts::Requirement::SubstitutionDiagnostic{SubstitutedEntity, DiagLoc, + DiagMessage}; +} + +void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + unsigned NumLocalParameters = Record.readInt(); + unsigned NumRequirements = Record.readInt(); + E->RequiresExprBits.RequiresKWLoc = Record.readSourceLocation(); + E->RequiresExprBits.IsSatisfied = Record.readInt(); + E->Body = Record.readDeclAs<RequiresExprBodyDecl>(); + llvm::SmallVector<ParmVarDecl *, 4> LocalParameters; + for (unsigned i = 0; i < NumLocalParameters; ++i) + LocalParameters.push_back(cast<ParmVarDecl>(Record.readDecl())); + std::copy(LocalParameters.begin(), LocalParameters.end(), + E->getTrailingObjects<ParmVarDecl *>()); + llvm::SmallVector<concepts::Requirement *, 4> Requirements; + for (unsigned i = 0; i < NumRequirements; ++i) { + auto RK = + static_cast<concepts::Requirement::RequirementKind>(Record.readInt()); + concepts::Requirement *R = nullptr; + switch (RK) { + case concepts::Requirement::RK_Type: { + auto Status = + static_cast<concepts::TypeRequirement::SatisfactionStatus>( + Record.readInt()); + if (Status == concepts::TypeRequirement::SS_SubstitutionFailure) + R = new (Record.getContext()) + concepts::TypeRequirement(readSubstitutionDiagnostic(Record)); + else + R = new (Record.getContext()) + concepts::TypeRequirement(Record.readTypeSourceInfo()); + } break; + case concepts::Requirement::RK_Simple: + case concepts::Requirement::RK_Compound: { + auto Status = + static_cast<concepts::ExprRequirement::SatisfactionStatus>( + Record.readInt()); + llvm::PointerUnion<concepts::Requirement::SubstitutionDiagnostic *, + Expr *> E; + if (Status == concepts::ExprRequirement::SS_ExprSubstitutionFailure) { + E = readSubstitutionDiagnostic(Record); + } else + E = Record.readExpr(); + + llvm::Optional<concepts::ExprRequirement::ReturnTypeRequirement> Req; + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; + SourceLocation NoexceptLoc; + if (RK == concepts::Requirement::RK_Simple) { + Req.emplace(); + } else { + NoexceptLoc = Record.readSourceLocation(); + switch (auto returnTypeRequirementKind = Record.readInt()) { + case 0: + // No return type requirement. + Req.emplace(); + break; + case 1: { + // type-constraint + TemplateParameterList *TPL = Record.readTemplateParameterList(); + if (Status >= + concepts::ExprRequirement::SS_ConstraintsNotSatisfied) + SubstitutedConstraintExpr = + cast<ConceptSpecializationExpr>(Record.readExpr()); + Req.emplace(TPL); + } break; + case 2: + // Substitution failure + Req.emplace(readSubstitutionDiagnostic(Record)); + break; + } + } + if (Expr *Ex = E.dyn_cast<Expr *>()) + R = new (Record.getContext()) concepts::ExprRequirement( + Ex, RK == concepts::Requirement::RK_Simple, NoexceptLoc, + std::move(*Req), Status, SubstitutedConstraintExpr); + else + R = new (Record.getContext()) concepts::ExprRequirement( + E.get<concepts::Requirement::SubstitutionDiagnostic *>(), + RK == concepts::Requirement::RK_Simple, NoexceptLoc, + std::move(*Req)); + } break; + case concepts::Requirement::RK_Nested: { + if (bool IsSubstitutionDiagnostic = Record.readInt()) { + R = new (Record.getContext()) concepts::NestedRequirement( + readSubstitutionDiagnostic(Record)); + break; + } + Expr *E = Record.readExpr(); + if (E->isInstantiationDependent()) + R = new (Record.getContext()) concepts::NestedRequirement(E); + else + R = new (Record.getContext()) + concepts::NestedRequirement(Record.getContext(), E, + readConstraintSatisfaction(Record)); + } break; + } + if (!R) + continue; + Requirements.push_back(R); + } + std::copy(Requirements.begin(), Requirements.end(), + E->getTrailingObjects<concepts::Requirement *>()); + E->RBraceLoc = Record.readSourceLocation(); } void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { @@ -3566,11 +3683,18 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) DependentCoawaitExpr(Empty); break; - case EXPR_CONCEPT_SPECIALIZATION: + case EXPR_CONCEPT_SPECIALIZATION: { unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields]; S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs); break; - + } + + case EXPR_REQUIRES: + unsigned numLocalParameters = Record[ASTStmtReader::NumExprFields]; + unsigned numRequirement = Record[ASTStmtReader::NumExprFields + 1]; + S = RequiresExpr::Create(Context, Empty, numLocalParameters, + numRequirement); + break; } // We hit a STMT_STOP, so we're done with this expression. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 6eba48a1abe..2dfdcc1f4fb 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -885,6 +885,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(DECL_NON_TYPE_TEMPLATE_PARM); RECORD(DECL_TEMPLATE_TEMPLATE_PARM); RECORD(DECL_CONCEPT); + RECORD(DECL_REQUIRES_EXPR_BODY); RECORD(DECL_TYPE_ALIAS_TEMPLATE); RECORD(DECL_STATIC_ASSERT); RECORD(DECL_CXX_BASE_SPECIFIERS); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index b2a8c118d40..459e61713ed 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -104,6 +104,7 @@ namespace clang { void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); void VisitTemplateDecl(TemplateDecl *D); void VisitConceptDecl(ConceptDecl *D); + void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D); void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitVarTemplateDecl(VarTemplateDecl *D); @@ -1481,6 +1482,10 @@ void ASTDeclWriter::VisitConceptDecl(ConceptDecl *D) { Code = serialization::DECL_CONCEPT; } +void ASTDeclWriter::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { + Code = serialization::DECL_REQUIRES_EXPR_BODY; +} + void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { VisitRedeclarable(D); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 9231f3b2b9b..1b118c257a4 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Serialization/ASTRecordWriter.h" +#include "clang/Sema/DeclSpec.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -388,19 +389,9 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { Code = serialization::EXPR_DEPENDENT_COAWAIT; } -void ASTStmtWriter::VisitConceptSpecializationExpr( - ConceptSpecializationExpr *E) { - VisitExpr(E); - ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments(); - Record.push_back(TemplateArgs.size()); - Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); - Record.AddSourceLocation(E->getTemplateKWLoc()); - Record.AddDeclarationNameInfo(E->getConceptNameInfo()); - Record.AddDeclRef(E->getNamedConcept()); - Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); - for (const TemplateArgument &Arg : TemplateArgs) - Record.AddTemplateArgument(Arg); - const ASTConstraintSatisfaction &Satisfaction = E->getSatisfaction(); +static void +addConstraintSatisfaction(ASTRecordWriter &Record, + const ASTConstraintSatisfaction &Satisfaction) { Record.push_back(Satisfaction.IsSatisfied); if (!Satisfaction.IsSatisfied) { Record.push_back(Satisfaction.NumRecords); @@ -418,10 +409,98 @@ void ASTStmtWriter::VisitConceptSpecializationExpr( } } } +} + +static void +addSubstitutionDiagnostic( + ASTRecordWriter &Record, + const concepts::Requirement::SubstitutionDiagnostic *D) { + Record.AddString(D->SubstitutedEntity); + Record.AddSourceLocation(D->DiagLoc); + Record.AddString(D->DiagMessage); +} + +void ASTStmtWriter::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + ArrayRef<TemplateArgument> TemplateArgs = E->getTemplateArguments(); + Record.push_back(TemplateArgs.size()); + Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); + Record.AddSourceLocation(E->getTemplateKWLoc()); + Record.AddDeclarationNameInfo(E->getConceptNameInfo()); + Record.AddDeclRef(E->getNamedConcept()); + Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); + for (const TemplateArgument &Arg : TemplateArgs) + Record.AddTemplateArgument(Arg); + if (!E->isValueDependent()) + addConstraintSatisfaction(Record, E->getSatisfaction()); Code = serialization::EXPR_CONCEPT_SPECIALIZATION; } +void ASTStmtWriter::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + Record.push_back(E->getLocalParameters().size()); + Record.push_back(E->getRequirements().size()); + Record.AddSourceLocation(E->RequiresExprBits.RequiresKWLoc); + Record.push_back(E->RequiresExprBits.IsSatisfied); + Record.AddDeclRef(E->getBody()); + for (ParmVarDecl *P : E->getLocalParameters()) + Record.AddDeclRef(P); + for (concepts::Requirement *R : E->getRequirements()) { + if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(R)) { + Record.push_back(concepts::Requirement::RK_Type); + Record.push_back(TypeReq->Status); + if (TypeReq->Status == concepts::TypeRequirement::SS_SubstitutionFailure) + addSubstitutionDiagnostic(Record, TypeReq->getSubstitutionDiagnostic()); + else + Record.AddTypeSourceInfo(TypeReq->getType()); + } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(R)) { + Record.push_back(ExprReq->getKind()); + Record.push_back(ExprReq->Status); + if (ExprReq->isExprSubstitutionFailure()) { + addSubstitutionDiagnostic(Record, + ExprReq->Value.get<concepts::Requirement::SubstitutionDiagnostic *>()); + } else + Record.AddStmt(ExprReq->Value.get<Expr *>()); + if (ExprReq->getKind() == concepts::Requirement::RK_Compound) { + Record.AddSourceLocation(ExprReq->NoexceptLoc); + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (RetReq.isSubstitutionFailure()) { + Record.push_back(2); + addSubstitutionDiagnostic(Record, RetReq.getSubstitutionDiagnostic()); + } else if (RetReq.isTypeConstraint()) { + Record.push_back(1); + Record.AddTemplateParameterList( + RetReq.getTypeConstraintTemplateParameterList()); + if (ExprReq->Status >= + concepts::ExprRequirement::SS_ConstraintsNotSatisfied) + Record.AddStmt( + ExprReq->getReturnTypeRequirementSubstitutedConstraintExpr()); + } else { + assert(RetReq.isEmpty()); + Record.push_back(0); + } + } + } else { + auto *NestedReq = cast<concepts::NestedRequirement>(R); + Record.push_back(concepts::Requirement::RK_Nested); + Record.push_back(NestedReq->isSubstitutionFailure()); + if (NestedReq->isSubstitutionFailure()){ + addSubstitutionDiagnostic(Record, + NestedReq->getSubstitutionDiagnostic()); + } else { + Record.AddStmt(NestedReq->Value.get<Expr *>()); + if (!NestedReq->isDependent()) + addConstraintSatisfaction(Record, *NestedReq->Satisfaction); + } + } + } + Record.AddSourceLocation(E->getEndLoc()); + + Code = serialization::EXPR_REQUIRES; +} + void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index f917a4c8637..b542cf2c030 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1386,6 +1386,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::AsTypeExprClass: case Stmt::ConceptSpecializationExprClass: case Stmt::CXXRewrittenBinaryOperatorClass: + case Stmt::RequiresExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need |