[clang] a0f50d7 - [Concepts] Requires Expressions
Saar Raz via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 18 14:25:18 PST 2020
Author: Saar Raz
Date: 2020-01-19T00:23:26+02:00
New Revision: a0f50d731639350c7a79f140f026c27a18215531
URL: https://github.com/llvm/llvm-project/commit/a0f50d731639350c7a79f140f026c27a18215531
DIFF: https://github.com/llvm/llvm-project/commit/a0f50d731639350c7a79f140f026c27a18215531.diff
LOG: [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
Added:
clang/include/clang/AST/ExprConcepts.h
clang/lib/AST/ExprConcepts.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
clang/test/PCH/cxx2a-requires-expr.cpp
clang/test/Parser/cxx2a-concepts-requires-expr.cpp
clang/test/SemaTemplate/instantiate-requires-expr.cpp
Modified:
clang/include/clang/AST/ASTConcept.h
clang/include/clang/AST/DeclCXX.h
clang/include/clang/AST/ExprCXX.h
clang/include/clang/AST/RecursiveASTVisitor.h
clang/include/clang/AST/Stmt.h
clang/include/clang/AST/StmtVisitor.h
clang/include/clang/Basic/DeclNodes.td
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/StmtNodes.td
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/DeclSpec.h
clang/include/clang/Sema/Sema.h
clang/include/clang/Sema/SemaConcept.h
clang/include/clang/Serialization/ASTBitCodes.h
clang/lib/AST/CMakeLists.txt
clang/lib/AST/DeclBase.cpp
clang/lib/AST/DeclCXX.cpp
clang/lib/AST/Expr.cpp
clang/lib/AST/ExprCXX.cpp
clang/lib/AST/ExprClassification.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/ItaniumMangle.cpp
clang/lib/AST/Stmt.cpp
clang/lib/AST/StmtPrinter.cpp
clang/lib/AST/StmtProfile.cpp
clang/lib/CodeGen/CGDecl.cpp
clang/lib/CodeGen/CGExprScalar.cpp
clang/lib/Frontend/FrontendActions.cpp
clang/lib/Frontend/InitPreprocessor.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Parse/ParseExpr.cpp
clang/lib/Parse/ParseExprCXX.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaConcept.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExceptionSpec.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaLookup.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Sema/SemaType.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Serialization/ASTCommon.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriter.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
clang/tools/libclang/CIndex.cpp
clang/tools/libclang/CXCursor.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index 896d857d8c96..84a611c14e0b 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -22,6 +22,7 @@
#include <utility>
namespace clang {
class ConceptDecl;
+class ConceptSpecializationExpr;
/// \brief The result of a constraint satisfaction check, containing the
/// necessary information to diagnose an unsatisfied constraint.
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index b716ea453a5a..2e8e31dbf4c7 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1893,6 +1893,37 @@ class CXXDeductionGuideDecl : public FunctionDecl {
static bool classofKind(Kind K) { return K == CXXDeductionGuide; }
};
+/// \brief Represents the body of a requires-expression.
+///
+/// This decl exists merely to serve as the DeclContext for the local
+/// parameters of the requires expression as well as other declarations inside
+/// it.
+///
+/// \code
+/// template<typename T> requires requires (T t) { {t++} -> regular; }
+/// \endcode
+///
+/// In this example, a RequiresExpr object will be generated for the expression,
+/// and a RequiresExprBodyDecl will be created to hold the parameter t and the
+/// template argument list imposed by the compound requirement.
+class RequiresExprBodyDecl : public Decl, public DeclContext {
+ RequiresExprBodyDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc)
+ : Decl(RequiresExprBody, DC, StartLoc), DeclContext(RequiresExprBody) {}
+
+public:
+ friend class ASTDeclReader;
+ friend class ASTDeclWriter;
+
+ static RequiresExprBodyDecl *Create(ASTContext &C, DeclContext *DC,
+ SourceLocation StartLoc);
+
+ static RequiresExprBodyDecl *CreateDeserialized(ASTContext &C, unsigned ID);
+
+ // Implement isa/cast/dyncast/etc.
+ static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+ static bool classofKind(Kind K) { return K == RequiresExprBody; }
+};
+
/// Represents a static or instance method of a struct/union/class.
///
/// In the terminology of the C++ Standard, these are the (static and
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 2c29409e0ca5..cea360d12e91 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -14,7 +14,6 @@
#ifndef LLVM_CLANG_AST_EXPRCXX_H
#define LLVM_CLANG_AST_EXPRCXX_H
-#include "clang/AST/ASTConcept.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
@@ -4836,99 +4835,6 @@ class BuiltinBitCastExpr final
}
};
-/// \brief Represents the specialization of a concept - evaluates to a prvalue
-/// of type bool.
-///
-/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
-/// specialization of a concept results in a prvalue of type bool.
-class ConceptSpecializationExpr final : public Expr, public ConceptReference,
- private llvm::TrailingObjects<ConceptSpecializationExpr,
- TemplateArgument> {
- friend class ASTStmtReader;
- friend TrailingObjects;
-public:
- using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
-
-protected:
- /// \brief The number of template arguments in the tail-allocated list of
- /// converted template arguments.
- unsigned NumTemplateArgs;
-
- /// \brief Information about the satisfaction of the named concept with the
- /// given arguments. If this expression is value dependent, this is to be
- /// ignored.
- ASTConstraintSatisfaction *Satisfaction;
-
- ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
- SourceLocation TemplateKWLoc,
- DeclarationNameInfo ConceptNameInfo,
- NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
- const ASTTemplateArgumentListInfo *ArgsAsWritten,
- ArrayRef<TemplateArgument> ConvertedArgs,
- const ConstraintSatisfaction *Satisfaction);
-
- ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
-
-public:
-
- static ConceptSpecializationExpr *
- Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
- SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
- NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
- const ASTTemplateArgumentListInfo *ArgsAsWritten,
- ArrayRef<TemplateArgument> ConvertedArgs,
- const ConstraintSatisfaction *Satisfaction);
-
- static ConceptSpecializationExpr *
- Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
-
- ArrayRef<TemplateArgument> getTemplateArguments() const {
- return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
- NumTemplateArgs);
- }
-
- /// \brief Set new template arguments for this concept specialization.
- void setTemplateArguments(ArrayRef<TemplateArgument> Converted);
-
- /// \brief Whether or not the concept with the given arguments was satisfied
- /// when the expression was created.
- /// The expression must not be dependent.
- bool isSatisfied() const {
- assert(!isValueDependent()
- && "isSatisfied called on a dependent ConceptSpecializationExpr");
- return Satisfaction->IsSatisfied;
- }
-
- /// \brief Get elaborated satisfaction info about the template arguments'
- /// satisfaction of the named concept.
- /// The expression must not be dependent.
- const ASTConstraintSatisfaction &getSatisfaction() const {
- assert(!isValueDependent()
- && "getSatisfaction called on dependent ConceptSpecializationExpr");
- return *Satisfaction;
- }
-
- static bool classof(const Stmt *T) {
- return T->getStmtClass() == ConceptSpecializationExprClass;
- }
-
- SourceLocation getBeginLoc() const LLVM_READONLY {
- return ConceptName.getBeginLoc();
- }
-
- SourceLocation getEndLoc() const LLVM_READONLY {
- return ArgsAsWritten->RAngleLoc;
- }
-
- // Iterators
- child_range children() {
- return child_range(child_iterator(), child_iterator());
- }
- const_child_range children() const {
- return const_child_range(const_child_iterator(), const_child_iterator());
- }
-};
-
} // namespace clang
#endif // LLVM_CLANG_AST_EXPRCXX_H
diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h
new file mode 100644
index 000000000000..2a64326e8604
--- /dev/null
+++ b/clang/include/clang/AST/ExprConcepts.h
@@ -0,0 +1,540 @@
+//===- ExprConcepts.h - C++2a Concepts expressions --------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Defines Expressions and AST nodes for C++2a concepts.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_EXPRCONCEPTS_H
+#define LLVM_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 <utility>
+#include <string>
+
+namespace clang {
+class ASTStmtReader;
+class ASTStmtWriter;
+
+/// \brief Represents the specialization of a concept - evaluates to a prvalue
+/// of type bool.
+///
+/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
+/// specialization of a concept results in a prvalue of type bool.
+class ConceptSpecializationExpr final : public Expr, public ConceptReference,
+ private llvm::TrailingObjects<ConceptSpecializationExpr,
+ TemplateArgument> {
+ friend class ASTStmtReader;
+ friend TrailingObjects;
+public:
+ using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
+
+protected:
+ /// \brief The number of template arguments in the tail-allocated list of
+ /// converted template arguments.
+ unsigned NumTemplateArgs;
+
+ /// \brief Information about the satisfaction of the named concept with the
+ /// given arguments. If this expression is value dependent, this is to be
+ /// ignored.
+ ASTConstraintSatisfaction *Satisfaction;
+
+ ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc,
+ DeclarationNameInfo ConceptNameInfo,
+ NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs,
+ const ConstraintSatisfaction *Satisfaction);
+
+ ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs);
+
+public:
+
+ static ConceptSpecializationExpr *
+ Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
+ SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
+ NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+ const ASTTemplateArgumentListInfo *ArgsAsWritten,
+ ArrayRef<TemplateArgument> ConvertedArgs,
+ const ConstraintSatisfaction *Satisfaction);
+
+ static ConceptSpecializationExpr *
+ Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs);
+
+ ArrayRef<TemplateArgument> getTemplateArguments() const {
+ return ArrayRef<TemplateArgument>(getTrailingObjects<TemplateArgument>(),
+ NumTemplateArgs);
+ }
+
+ /// \brief Set new template arguments for this concept specialization.
+ void setTemplateArguments(ArrayRef<TemplateArgument> Converted);
+
+ /// \brief Whether or not the concept with the given arguments was satisfied
+ /// when the expression was created.
+ /// The expression must not be dependent.
+ bool isSatisfied() const {
+ assert(!isValueDependent()
+ && "isSatisfied called on a dependent ConceptSpecializationExpr");
+ return Satisfaction->IsSatisfied;
+ }
+
+ /// \brief Get elaborated satisfaction info about the template arguments'
+ /// satisfaction of the named concept.
+ /// The expression must not be dependent.
+ const ASTConstraintSatisfaction &getSatisfaction() const {
+ assert(!isValueDependent()
+ && "getSatisfaction called on dependent ConceptSpecializationExpr");
+ return *Satisfaction;
+ }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == ConceptSpecializationExprClass;
+ }
+
+ SourceLocation getBeginLoc() const LLVM_READONLY {
+ return ConceptName.getBeginLoc();
+ }
+
+ SourceLocation getEndLoc() const LLVM_READONLY {
+ return ArgsAsWritten->RAngleLoc;
+ }
+
+ // Iterators
+ child_range children() {
+ return child_range(child_iterator(), child_iterator());
+ }
+ const_child_range children() const {
+ return const_child_range(const_child_iterator(), const_child_iterator());
+ }
+};
+
+namespace concepts {
+
+/// \brief A static requirement that can be used in a requires-expression to
+/// check properties of types and expression.
+class Requirement {
+public:
+ // Note - simple and compound requirements are both represented by the same
+ // class (ExprRequirement).
+ enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested };
+private:
+ const RequirementKind Kind;
+ bool Dependent : 1;
+ bool ContainsUnexpandedParameterPack : 1;
+ bool Satisfied : 1;
+public:
+ struct SubstitutionDiagnostic {
+ StringRef SubstitutedEntity;
+ // FIXME: Store diagnostics semantically and not as prerendered strings.
+ // Fixing this probably requires serialization of PartialDiagnostic
+ // objects.
+ SourceLocation DiagLoc;
+ StringRef DiagMessage;
+ };
+
+ Requirement(RequirementKind Kind, bool IsDependent,
+ bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) :
+ Kind(Kind), Dependent(IsDependent),
+ ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack),
+ Satisfied(IsSatisfied) {}
+
+ RequirementKind getKind() const { return Kind; }
+
+ bool isSatisfied() const {
+ assert(!Dependent &&
+ "isSatisfied can only be called on non-dependent requirements.");
+ return Satisfied;
+ }
+
+ void setSatisfied(bool IsSatisfied) {
+ assert(!Dependent &&
+ "setSatisfied can only be called on non-dependent requirements.");
+ Satisfied = IsSatisfied;
+ }
+
+ void setDependent(bool IsDependent) { Dependent = IsDependent; }
+ bool isDependent() const { return Dependent; }
+
+ void setContainsUnexpandedParameterPack(bool Contains) {
+ ContainsUnexpandedParameterPack = Contains;
+ }
+ bool containsUnexpandedParameterPack() const {
+ return ContainsUnexpandedParameterPack;
+ }
+};
+
+/// \brief A requires-expression requirement which queries the existence of a
+/// type name or type template specialization ('type' requirements).
+class TypeRequirement : public Requirement {
+public:
+ enum SatisfactionStatus {
+ SS_Dependent,
+ SS_SubstitutionFailure,
+ SS_Satisfied
+ };
+private:
+ llvm::PointerUnion<SubstitutionDiagnostic *, TypeSourceInfo *> Value;
+ SatisfactionStatus Status;
+public:
+ friend ASTStmtReader;
+ friend ASTStmtWriter;
+
+ /// \brief Construct a type requirement from a type. If the given type is not
+ /// dependent, this indicates that the type exists and the requirement will be
+ /// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be
+ /// used.
+ TypeRequirement(TypeSourceInfo *T);
+
+ /// \brief Construct a type requirement when the nested name specifier is
+ /// invalid due to a bad substitution. The requirement is unsatisfied.
+ TypeRequirement(SubstitutionDiagnostic *Diagnostic) :
+ Requirement(RK_Type, false, false, false), Value(Diagnostic),
+ Status(SS_SubstitutionFailure) {}
+
+ SatisfactionStatus getSatisfactionStatus() const { return Status; }
+ void setSatisfactionStatus(SatisfactionStatus Status) {
+ this->Status = Status;
+ }
+
+ bool isSubstitutionFailure() const {
+ return Status == SS_SubstitutionFailure;
+ }
+
+ SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
+ assert(Status == SS_SubstitutionFailure &&
+ "Attempted to get substitution diagnostic when there has been no "
+ "substitution failure.");
+ return Value.get<SubstitutionDiagnostic *>();
+ }
+
+ TypeSourceInfo *getType() const {
+ assert(!isSubstitutionFailure() &&
+ "Attempted to get type when there has been a substitution failure.");
+ return Value.get<TypeSourceInfo *>();
+ }
+
+ static bool classof(const Requirement *R) {
+ return R->getKind() == RK_Type;
+ }
+};
+
+/// \brief A requires-expression requirement which queries the validity and
+/// properties of an expression ('simple' and 'compound' requirements).
+class ExprRequirement : public Requirement {
+public:
+ enum SatisfactionStatus {
+ SS_Dependent,
+ SS_ExprSubstitutionFailure,
+ SS_NoexceptNotMet,
+ SS_TypeRequirementSubstitutionFailure,
+ SS_ConstraintsNotSatisfied,
+ SS_Satisfied
+ };
+ class ReturnTypeRequirement {
+ llvm::PointerIntPair<
+ llvm::PointerUnion<TemplateParameterList *, SubstitutionDiagnostic *>,
+ 1, bool>
+ TypeConstraintInfo;
+ public:
+ friend ASTStmtReader;
+ friend ASTStmtWriter;
+
+ /// \brief No return type requirement was specified.
+ ReturnTypeRequirement() : TypeConstraintInfo(nullptr, 0) {}
+
+ /// \brief A return type requirement was specified but it was a
+ /// substitution failure.
+ ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) :
+ TypeConstraintInfo(SubstDiag, 0) {}
+
+ /// \brief A 'type constraint' style return type requirement.
+ /// \param TPL an invented template parameter list containing a single
+ /// type parameter with a type-constraint.
+ // TODO: Can we maybe not save the whole template parameter list and just
+ // the type constraint? Saving the whole TPL makes it easier to handle in
+ // serialization but is less elegant.
+ ReturnTypeRequirement(TemplateParameterList *TPL);
+
+ bool isDependent() const {
+ return TypeConstraintInfo.getInt();
+ }
+
+ bool containsUnexpandedParameterPack() const {
+ if (!isTypeConstraint())
+ return false;
+ return getTypeConstraintTemplateParameterList()
+ ->containsUnexpandedParameterPack();
+ }
+
+ bool isEmpty() const {
+ return TypeConstraintInfo.getPointer().isNull();
+ }
+
+ bool isSubstitutionFailure() const {
+ return !isEmpty() &&
+ TypeConstraintInfo.getPointer().is<SubstitutionDiagnostic *>();
+ }
+
+ bool isTypeConstraint() const {
+ return !isEmpty() &&
+ TypeConstraintInfo.getPointer().is<TemplateParameterList *>();
+ }
+
+ SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
+ assert(isSubstitutionFailure());
+ return TypeConstraintInfo.getPointer().get<SubstitutionDiagnostic *>();
+ }
+
+ const TypeConstraint *getTypeConstraint() const;
+
+ TemplateParameterList *getTypeConstraintTemplateParameterList() const {
+ assert(isTypeConstraint());
+ return TypeConstraintInfo.getPointer().get<TemplateParameterList *>();
+ }
+ };
+private:
+ llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
+ SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified.
+ ReturnTypeRequirement TypeReq;
+ ConceptSpecializationExpr *SubstitutedConstraintExpr;
+ SatisfactionStatus Status;
+public:
+ friend ASTStmtReader;
+ friend ASTStmtWriter;
+
+ /// \brief Construct a compound requirement.
+ /// \param E the expression which is checked by this requirement.
+ /// \param IsSimple whether this was a simple requirement in source.
+ /// \param NoexceptLoc the location of the noexcept keyword, if it was
+ /// specified, otherwise an empty location.
+ /// \param Req the requirement for the type of the checked expression.
+ /// \param Status the satisfaction status of this requirement.
+ ExprRequirement(
+ Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
+ ReturnTypeRequirement Req, SatisfactionStatus Status,
+ ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr);
+
+ /// \brief Construct a compound requirement whose expression was a
+ /// substitution failure. The requirement is not satisfied.
+ /// \param E the diagnostic emitted while instantiating the original
+ /// expression.
+ /// \param IsSimple whether this was a simple requirement in source.
+ /// \param NoexceptLoc the location of the noexcept keyword, if it was
+ /// specified, otherwise an empty location.
+ /// \param Req the requirement for the type of the checked expression (omit
+ /// if no requirement was specified).
+ ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple,
+ SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {});
+
+ bool isSimple() const { return getKind() == RK_Simple; }
+ bool isCompound() const { return getKind() == RK_Compound; }
+
+ bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); }
+ SourceLocation getNoexceptLoc() const { return NoexceptLoc; }
+
+ SatisfactionStatus getSatisfactionStatus() const { return Status; }
+
+ bool isExprSubstitutionFailure() const {
+ return Status == SS_ExprSubstitutionFailure;
+ }
+
+ const ReturnTypeRequirement &getReturnTypeRequirement() const {
+ return TypeReq;
+ }
+
+ ConceptSpecializationExpr *
+ getReturnTypeRequirementSubstitutedConstraintExpr() const {
+ assert(Status >= SS_TypeRequirementSubstitutionFailure);
+ return SubstitutedConstraintExpr;
+ }
+
+ SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const {
+ assert(isExprSubstitutionFailure() &&
+ "Attempted to get expression substitution diagnostic when there has "
+ "been no expression substitution failure");
+ return Value.get<SubstitutionDiagnostic *>();
+ }
+
+ Expr *getExpr() const {
+ assert(!isExprSubstitutionFailure() &&
+ "ExprRequirement has no expression because there has been a "
+ "substitution failure.");
+ return Value.get<Expr *>();
+ }
+
+ static bool classof(const Requirement *R) {
+ return R->getKind() == RK_Compound || R->getKind() == RK_Simple;
+ }
+};
+
+/// \brief A requires-expression requirement which is satisfied when a general
+/// constraint expression is satisfied ('nested' requirements).
+class NestedRequirement : public Requirement {
+ llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> Value;
+ const ASTConstraintSatisfaction *Satisfaction = nullptr;
+
+public:
+ friend ASTStmtReader;
+ friend ASTStmtWriter;
+
+ NestedRequirement(SubstitutionDiagnostic *SubstDiag) :
+ Requirement(RK_Nested, /*Dependent=*/false,
+ /*ContainsUnexpandedParameterPack*/false,
+ /*Satisfied=*/false), Value(SubstDiag) {}
+
+ NestedRequirement(Expr *Constraint) :
+ Requirement(RK_Nested, /*Dependent=*/true,
+ Constraint->containsUnexpandedParameterPack()),
+ Value(Constraint) {
+ assert(Constraint->isInstantiationDependent() &&
+ "Nested requirement with non-dependent constraint must be "
+ "constructed with a ConstraintSatisfaction object");
+ }
+
+ NestedRequirement(ASTContext &C, Expr *Constraint,
+ const ConstraintSatisfaction &Satisfaction) :
+ Requirement(RK_Nested, Constraint->isInstantiationDependent(),
+ Constraint->containsUnexpandedParameterPack(),
+ Satisfaction.IsSatisfied),
+ Value(Constraint),
+ Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
+
+ bool isSubstitutionFailure() const {
+ return Value.is<SubstitutionDiagnostic *>();
+ }
+
+ SubstitutionDiagnostic *getSubstitutionDiagnostic() const {
+ assert(isSubstitutionFailure() &&
+ "getSubstitutionDiagnostic() may not be called when there was no "
+ "substitution failure.");
+ return Value.get<SubstitutionDiagnostic *>();
+ }
+
+ Expr *getConstraintExpr() const {
+ assert(!isSubstitutionFailure() && "getConstraintExpr() may not be called "
+ "on nested requirements with "
+ "substitution failures.");
+ return Value.get<Expr *>();
+ }
+
+ const ASTConstraintSatisfaction &getConstraintSatisfaction() const {
+ assert(!isSubstitutionFailure() && "getConstraintSatisfaction() may not be "
+ "called on nested requirements with "
+ "substitution failures.");
+ return *Satisfaction;
+ }
+
+ static bool classof(const Requirement *R) {
+ return R->getKind() == RK_Nested;
+ }
+};
+
+} // namespace concepts
+
+/// C++2a [expr.prim.req]:
+/// 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.
+/// [...]
+/// A requires-expression is a prvalue of type bool [...]
+class RequiresExpr final : public Expr,
+ llvm::TrailingObjects<RequiresExpr, ParmVarDecl *,
+ concepts::Requirement *> {
+ friend TrailingObjects;
+ friend class ASTStmtReader;
+
+ unsigned NumLocalParameters;
+ unsigned NumRequirements;
+ RequiresExprBodyDecl *Body;
+ SourceLocation RBraceLoc;
+
+ unsigned numTrailingObjects(OverloadToken<ParmVarDecl *>) const {
+ return NumLocalParameters;
+ }
+
+ unsigned numTrailingObjects(OverloadToken<concepts::Requirement *>) const {
+ return NumRequirements;
+ }
+
+ RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc,
+ RequiresExprBodyDecl *Body,
+ ArrayRef<ParmVarDecl *> LocalParameters,
+ ArrayRef<concepts::Requirement *> Requirements,
+ SourceLocation RBraceLoc);
+ RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
+ unsigned NumRequirements);
+
+public:
+ static RequiresExpr *
+ Create(ASTContext &C, SourceLocation RequiresKWLoc,
+ RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> LocalParameters,
+ ArrayRef<concepts::Requirement *> Requirements,
+ SourceLocation RBraceLoc);
+ static RequiresExpr *
+ Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters,
+ unsigned NumRequirements);
+
+ ArrayRef<ParmVarDecl *> getLocalParameters() const {
+ return {getTrailingObjects<ParmVarDecl *>(), NumLocalParameters};
+ }
+
+ RequiresExprBodyDecl *getBody() const { return Body; }
+
+ ArrayRef<concepts::Requirement *> getRequirements() const {
+ return {getTrailingObjects<concepts::Requirement *>(), NumRequirements};
+ }
+
+ /// \brief Whether or not the requires clause is satisfied.
+ /// The expression must not be dependent.
+ bool isSatisfied() const {
+ assert(!isValueDependent()
+ && "isSatisfied called on a dependent RequiresExpr");
+ return RequiresExprBits.IsSatisfied;
+ }
+
+ SourceLocation getRequiresKWLoc() const {
+ return RequiresExprBits.RequiresKWLoc;
+ }
+
+ SourceLocation getRBraceLoc() const { return RBraceLoc; }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == RequiresExprClass;
+ }
+
+ SourceLocation getBeginLoc() const LLVM_READONLY {
+ return RequiresExprBits.RequiresKWLoc;
+ }
+ SourceLocation getEndLoc() const LLVM_READONLY {
+ return RBraceLoc;
+ }
+
+ // Iterators
+ child_range children() {
+ return child_range(child_iterator(), child_iterator());
+ }
+ const_child_range children() const {
+ return const_child_range(const_child_iterator(), const_child_iterator());
+ }
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_EXPRCONCEPTS_H
\ No newline at end of file
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index f8ab8e451d8c..4633122aba48 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -23,6 +23,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/ExprOpenMP.h"
@@ -2138,6 +2139,8 @@ DEF_TRAVERSE_DECL(ParmVarDecl, {
TRY_TO(TraverseStmt(D->getDefaultArg()));
})
+DEF_TRAVERSE_DECL(RequiresExprBodyDecl, {})
+
#undef DEF_TRAVERSE_DECL
// ----------------- Stmt traversal -----------------
@@ -2709,6 +2712,28 @@ DEF_TRAVERSE_STMT(ConceptSpecializationExpr, {
TRY_TO(TraverseConceptReference(*S));
})
+DEF_TRAVERSE_STMT(RequiresExpr, {
+ TRY_TO(TraverseDecl(S->getBody()));
+ for (ParmVarDecl *Parm : S->getLocalParameters())
+ TRY_TO(TraverseDecl(Parm));
+ for (concepts::Requirement *Req : S->getRequirements())
+ if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req)) {
+ if (!TypeReq->isSubstitutionFailure())
+ TRY_TO(TraverseTypeLoc(TypeReq->getType()->getTypeLoc()));
+ } else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req)) {
+ if (!ExprReq->isExprSubstitutionFailure())
+ TRY_TO(TraverseStmt(ExprReq->getExpr()));
+ auto &RetReq = ExprReq->getReturnTypeRequirement();
+ if (RetReq.isTypeConstraint())
+ TRY_TO(TraverseTemplateParameterListHelper(
+ RetReq.getTypeConstraintTemplateParameterList()));
+ } else {
+ auto *NestedReq = cast<concepts::NestedRequirement>(Req);
+ if (!NestedReq->isSubstitutionFailure())
+ TRY_TO(TraverseStmt(NestedReq->getConstraintExpr()));
+ }
+})
+
// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index eaacb1a5b252..253b76941991 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -910,6 +910,17 @@ class alignas(void *) Stmt {
SourceLocation NameLoc;
};
+ class RequiresExprBitfields {
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
+ friend class RequiresExpr;
+
+ unsigned : NumExprBits;
+
+ unsigned IsSatisfied : 1;
+ SourceLocation RequiresKWLoc;
+ };
+
//===--- C++ Coroutines TS bitfields classes ---===//
class CoawaitExprBitfields {
@@ -1008,6 +1019,7 @@ class alignas(void *) Stmt {
UnresolvedMemberExprBitfields UnresolvedMemberExprBits;
CXXNoexceptExprBitfields CXXNoexceptExprBits;
SubstNonTypeTemplateParmExprBitfields SubstNonTypeTemplateParmExprBits;
+ RequiresExprBitfields RequiresExprBits;
// C++ Coroutines TS expressions
CoawaitExprBitfields CoawaitBits;
diff --git a/clang/include/clang/AST/StmtVisitor.h b/clang/include/clang/AST/StmtVisitor.h
index d3be93d228cc..3e5155199eac 100644
--- a/clang/include/clang/AST/StmtVisitor.h
+++ b/clang/include/clang/AST/StmtVisitor.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_STMTVISITOR_H
#define LLVM_CLANG_AST_STMTVISITOR_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/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index c2c23237285b..d5bbc604819f 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -100,5 +100,6 @@ def OMPThreadPrivate : DeclNode<Decl>;
def OMPAllocate : DeclNode<Decl>;
def OMPRequires : DeclNode<Decl>;
def Empty : DeclNode<Decl>;
+def RequiresExprBody : DeclNode<Decl>, DeclContext;
def LifetimeExtendedTemporary : DeclNode<Decl>;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 41f788e7d9bd..105ed7bf6c84 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -744,6 +744,33 @@ def err_friend_explicit_instantiation : Error<
def err_explicit_instantiation_enum : Error<
"enumerations cannot be explicitly instantiated">;
def err_expected_template_parameter : Error<"expected template parameter">;
+def note_ill_formed_requires_expression_outside_template : Note<
+ "requires expression outside a template declaration may not contain invalid "
+ "types or expressions">;
+def err_empty_requires_expr : Error<
+ "a requires expression must contain at least one requirement">;
+def err_requires_expr_parameter_list_ellipsis : Error<
+ "varargs not allowed in requires expression">;
+def err_requires_expr_type_req_illegal_identifier : Error<
+ "expected identifier or template-id in type requirement">;
+def err_requires_expr_type_req_template_args_on_non_template : Error<
+ "template arguments provided for non-template '%0'">;
+def err_expected_semi_requirement : Error<
+ "expected ';' at end of requirement">;
+def err_requires_expr_missing_arrow : Error<
+ "expected '->' before expression type requirement">;
+def err_requires_expr_expected_type_constraint : Error<
+ "expected concept name with optional arguments">;
+def err_requires_expr_simple_requirement_noexcept : Error<
+ "'noexcept' can only be used in a compound requirement (with '{' '}' around "
+ "the expression)">;
+def err_requires_expr_simple_requirement_unexpected_tok : Error<
+ "unexpected %0 after expression; did you intend to use a compound "
+ "requirement (with '{' '}' around the expression)?">;
+def warn_requires_expr_in_simple_requirement : Warning<
+ "this requires expression will only be checked for syntactic validity; did "
+ "you intend to place it in a nested requirement? (add another 'requires' "
+ "before the expression)">, InGroup<DiagGroup<"requires-expression">>;
def err_missing_dependent_template_keyword : Error<
"use 'template' keyword to treat '%0' as a dependent template name">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ffa326932a1c..a4d042710858 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2102,12 +2102,21 @@ def err_auto_not_allowed : Error<
"|in template argument|in typedef|in type alias|in function return type"
"|in conversion function type|here|in lambda parameter"
"|in type allocated by 'new'|in K&R-style function parameter"
- "|in template parameter|in friend declaration}1">;
+ "|in template parameter|in friend declaration"
+ "|in requires expression parameter}1">;
+def err_auto_not_allowed_in_return_type_requirement : Error<
+ "%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed in expression "
+ "type requirement">;
def err_dependent_deduced_tst : Error<
"typename specifier refers to "
"%select{class template|function template|variable template|alias template|"
"template template parameter|template}0 member in %1; "
"argument deduction not allowed here">;
+def err_deduced_tst : Error<
+ "typename specifier refers to "
+ "%select{class template|function template|variable template|alias template|"
+ "template template parameter|template}0; argument deduction not allowed "
+ "here">;
def err_auto_not_allowed_var_inst : Error<
"'auto' variable template instantiation is not allowed">;
def err_auto_var_requires_init : Error<
@@ -2590,19 +2599,56 @@ def note_constraints_not_satisfied : Note<
def note_substituted_constraint_expr_is_ill_formed : Note<
"because substituted constraint expression is ill-formed%0">;
def note_atomic_constraint_evaluated_to_false : Note<
- "%select{and |because }0'%1' evaluated to false">;
+ "%select{and|because}0 '%1' evaluated to false">;
def note_concept_specialization_constraint_evaluated_to_false : Note<
- "%select{and |because }0'%1' evaluated to false">;
+ "%select{and|because}0 '%1' evaluated to false">;
def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note<
- "%select{and |because }0%1 does not satisfy %2">;
+ "%select{and|because}0 %1 does not satisfy %2">;
def note_atomic_constraint_evaluated_to_false_elaborated : Note<
- "%select{and |because }0'%1' (%2 %3 %4) evaluated to false">;
+ "%select{and|because}0 '%1' (%2 %3 %4) evaluated to false">;
def err_constrained_virtual_method : Error<
"virtual function cannot have a requires clause">;
def err_trailing_requires_clause_on_deduction_guide : Error<
"deduction guide cannot have a requires clause">;
def err_reference_to_function_with_unsatisfied_constraints : Error<
"invalid reference to function %0: constraints not satisfied">;
+def note_requires_expr_ill_formed_expr : Note<
+ "expression is invalid: %0">;
+def note_requires_expr_no_implicit_conversion : Note<
+ "no implicit conversion exists between expression type %0 and expected type "
+ "%1">;
+def err_requires_expr_local_parameter_default_argument : Error<
+ "default arguments not allowed for parameters of a requires expression">;
+def err_requires_expr_parameter_referenced_in_evaluated_context : Error<
+ "constraint variable %0 cannot be used in an evaluated context">;
+def note_expr_requirement_expr_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid: %2">;
+def note_expr_requirement_expr_unknown_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid">;
+def note_expr_requirement_noexcept_not_met : Note<
+ "%select{and|because}0 '%1' may throw an exception">;
+def note_expr_requirement_type_requirement_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid: %2">;
+def note_expr_requirement_type_requirement_unknown_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid">;
+def note_expr_requirement_constraints_not_satisfied : Note<
+ "%select{and|because}0 type constraint '%1' was not satisfied:">;
+def note_expr_requirement_constraints_not_satisfied_simple : Note<
+ "%select{and|because}0 %1 does not satisfy %2:">;
+def note_type_requirement_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid: %2">;
+def note_type_requirement_unknown_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid">;
+def err_type_requirement_non_type_template : Error<
+ "'%0' refers to a %select{class template|function template|"
+ "variable template|alias template|template template parameter|template}1, "
+ "not a type template">;
+def err_type_requirement_no_such_type : Error<
+ "'%0' does not name a type">;
+def note_nested_requirement_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid: %2">;
+def note_nested_requirement_unknown_substitution_error : Note<
+ "%select{and|because}0 '%1' would be invalid">;
def note_ambiguous_atomic_constraints : Note<
"similar constraint expressions not considered equivalent; constraint "
"expressions cannot be considered equivalent unless they originate from the "
@@ -4588,6 +4634,8 @@ def note_template_type_alias_instantiation_here : Note<
"in instantiation of template type alias %0 requested here">;
def note_template_exception_spec_instantiation_here : Note<
"in instantiation of exception specification for %0 requested here">;
+def note_template_requirement_instantiation_here : Note<
+ "in instantiation of requirement here">;
def warn_var_template_missing : Warning<"instantiation of variable %q0 "
"required here, but no definition is available">,
InGroup<UndefinedVarTemplate>;
@@ -4623,6 +4671,8 @@ def note_template_default_arg_checking : Note<
"while checking a default template argument used here">;
def note_concept_specialization_here : Note<
"while checking the satisfaction of concept '%0' requested here">;
+def note_nested_requirement_here : Note<
+ "while checking the satisfaction of nested requirement requested here">;
def note_checking_constraints_for_template_id_here : Note<
"while checking constraint satisfaction for template '%0' required here">;
def note_checking_constraints_for_var_spec_id_here : Note<
@@ -4756,8 +4806,12 @@ def err_typename_nested_not_found_requirement : Error<
"declaration">;
def err_typename_nested_not_type : Error<
"typename specifier refers to non-type member %0 in %1">;
-def note_typename_refers_here : Note<
+def err_typename_not_type : Error<
+ "typename specifier refers to non-type %0">;
+def note_typename_member_refers_here : Note<
"referenced member %0 is declared here">;
+def note_typename_refers_here : Note<
+ "referenced %0 is declared here">;
def err_typename_missing : Error<
"missing 'typename' prior to dependent type name '%0%1'">;
def err_typename_missing_template : Error<
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 294993298a18..41923cddc493 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -164,6 +164,7 @@ def CoyieldExpr : StmtNode<CoroutineSuspendExpr>;
// C++2a Concepts expressions
def ConceptSpecializationExpr : StmtNode<Expr>;
+def RequiresExpr : StmtNode<Expr>;
// Obj-C Expressions.
def ObjCStringLiteral : StmtNode<Expr>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 182024ea5108..41f46861d089 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1933,6 +1933,7 @@ class Parser : public CodeCompletionHandler {
//===--------------------------------------------------------------------===//
// C++ Concepts
+ ExprResult ParseRequiresExpression();
void ParseTrailingRequiresClause(Declarator &D);
//===--------------------------------------------------------------------===//
@@ -2771,7 +2772,7 @@ class Parser : public CodeCompletionHandler {
Declarator &D,
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo);
void ParseParameterDeclarationClause(
- Declarator &D,
+ DeclaratorContext DeclaratorContext,
ParsedAttributes &attrs,
SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo,
SourceLocation &EllipsisLoc);
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index aceec9cbe1c9..1222549161e4 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -1757,7 +1757,8 @@ enum class DeclaratorContext {
TemplateArgContext, // Any template argument (in template argument list).
TemplateTypeArgContext, // Template type argument (in default argument).
AliasDeclContext, // C++11 alias-declaration.
- AliasTemplateContext // C++11 alias-declaration template.
+ AliasTemplateContext, // C++11 alias-declaration template.
+ RequiresExprContext // C++2a requires-expression.
};
@@ -1981,6 +1982,7 @@ class Declarator {
case DeclaratorContext::TemplateTypeArgContext:
case DeclaratorContext::TrailingReturnContext:
case DeclaratorContext::TrailingReturnVarContext:
+ case DeclaratorContext::RequiresExprContext:
return true;
}
llvm_unreachable("unknown context kind!");
@@ -2003,6 +2005,7 @@ class Declarator {
case DeclaratorContext::TemplateParamContext:
case DeclaratorContext::CXXCatchContext:
case DeclaratorContext::ObjCCatchContext:
+ case DeclaratorContext::RequiresExprContext:
return true;
case DeclaratorContext::TypeNameContext:
@@ -2039,6 +2042,7 @@ class Declarator {
case DeclaratorContext::MemberContext:
case DeclaratorContext::PrototypeContext:
case DeclaratorContext::TemplateParamContext:
+ case DeclaratorContext::RequiresExprContext:
// Maybe one day...
return false;
@@ -2116,6 +2120,7 @@ class Declarator {
case DeclaratorContext::TemplateArgContext:
case DeclaratorContext::TemplateTypeArgContext:
case DeclaratorContext::TrailingReturnContext:
+ case DeclaratorContext::RequiresExprContext:
return false;
}
llvm_unreachable("unknown context kind!");
@@ -2337,6 +2342,7 @@ class Declarator {
case DeclaratorContext::TemplateTypeArgContext:
case DeclaratorContext::TrailingReturnContext:
case DeclaratorContext::TrailingReturnVarContext:
+ case DeclaratorContext::RequiresExprContext:
return false;
}
llvm_unreachable("unknown context kind!");
@@ -2370,6 +2376,7 @@ class Declarator {
case DeclaratorContext::TrailingReturnContext:
case DeclaratorContext::TrailingReturnVarContext:
case DeclaratorContext::TemplateTypeArgContext:
+ case DeclaratorContext::RequiresExprContext:
return false;
case DeclaratorContext::BlockContext:
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4d76c877d31b..2fb758bc75d6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -21,6 +21,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ExternalASTSource.h"
@@ -6282,13 +6283,17 @@ class Sema final {
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
+ /// \param First whether this is the first time an unsatisfied constraint is
+ /// diagnosed for this error.
void
- DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction);
+ DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction,
+ bool First = true);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied.
void
- DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction& Satisfaction);
+ DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction,
+ bool First = true);
/// \brief Emit diagnostics explaining why a constraint expression was deemed
/// unsatisfied because it was ill-formed.
@@ -7262,7 +7267,17 @@ class Sema final {
SourceLocation KeywordLoc,
NestedNameSpecifierLoc QualifierLoc,
const IdentifierInfo &II,
- SourceLocation IILoc);
+ SourceLocation IILoc,
+ TypeSourceInfo **TSI,
+ bool DeducedTSTContext);
+
+ QualType CheckTypenameType(ElaboratedTypeKeyword Keyword,
+ SourceLocation KeywordLoc,
+ NestedNameSpecifierLoc QualifierLoc,
+ const IdentifierInfo &II,
+ SourceLocation IILoc,
+ bool DeducedTSTContext = true);
+
TypeSourceInfo *RebuildTypeInCurrentInstantiation(TypeSourceInfo *T,
SourceLocation Loc,
@@ -7282,11 +7297,52 @@ class Sema final {
const TemplateArgument *Args,
unsigned NumArgs);
- // Concepts
+ //===--------------------------------------------------------------------===//
+ // C++ Concepts
+ //===--------------------------------------------------------------------===//
Decl *ActOnConceptDefinition(
Scope *S, MultiTemplateParamsArg TemplateParameterLists,
IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr);
+ RequiresExprBodyDecl *
+ ActOnStartRequiresExpr(SourceLocation RequiresKWLoc,
+ ArrayRef<ParmVarDecl *> LocalParameters,
+ Scope *BodyScope);
+ void ActOnFinishRequiresExpr();
+ concepts::Requirement *ActOnSimpleRequirement(Expr *E);
+ concepts::Requirement *ActOnTypeRequirement(
+ SourceLocation TypenameKWLoc, CXXScopeSpec &SS, SourceLocation NameLoc,
+ IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId);
+ concepts::Requirement *ActOnCompoundRequirement(Expr *E,
+ SourceLocation NoexceptLoc);
+ concepts::Requirement *
+ ActOnCompoundRequirement(
+ Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS,
+ TemplateIdAnnotation *TypeConstraint, unsigned Depth);
+ concepts::Requirement *ActOnNestedRequirement(Expr *Constraint);
+ concepts::ExprRequirement *
+ BuildExprRequirement(
+ Expr *E, bool IsSatisfied, SourceLocation NoexceptLoc,
+ concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement);
+ concepts::ExprRequirement *
+ BuildExprRequirement(
+ concepts::Requirement::SubstitutionDiagnostic *ExprSubstDiag,
+ bool IsSatisfied, SourceLocation NoexceptLoc,
+ concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement);
+ concepts::TypeRequirement *BuildTypeRequirement(TypeSourceInfo *Type);
+ concepts::TypeRequirement *
+ BuildTypeRequirement(
+ concepts::Requirement::SubstitutionDiagnostic *SubstDiag);
+ concepts::NestedRequirement *BuildNestedRequirement(Expr *E);
+ concepts::NestedRequirement *
+ BuildNestedRequirement(
+ concepts::Requirement::SubstitutionDiagnostic *SubstDiag);
+ ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc,
+ RequiresExprBodyDecl *Body,
+ ArrayRef<ParmVarDecl *> LocalParameters,
+ ArrayRef<concepts::Requirement *> Requirements,
+ SourceLocation ClosingBraceLoc);
+
//===--------------------------------------------------------------------===//
// C++ Variadic Templates (C++0x [temp.variadic])
//===--------------------------------------------------------------------===//
@@ -7933,6 +7989,13 @@ class Sema final {
/// template which was deferred until it was needed.
ExceptionSpecInstantiation,
+ /// We are instantiating a requirement of a requires expression.
+ RequirementInstantiation,
+
+ /// We are checking the satisfaction of a nested requirement of a requires
+ /// expression.
+ NestedRequirementConstraintsCheck,
+
/// We are declaring an implicit special member function.
DeclaringSpecialMember,
@@ -8254,6 +8317,19 @@ class Sema final {
ParameterMappingSubstitution, NamedDecl *Template,
SourceRange InstantiationRange);
+ /// \brief Note that we are substituting template arguments into a part of
+ /// a requirement of a requires expression.
+ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ concepts::Requirement *Req,
+ sema::TemplateDeductionInfo &DeductionInfo,
+ SourceRange InstantiationRange = SourceRange());
+
+ /// \brief Note that we are checking the satisfaction of the constraint
+ /// expression inside of a nested requirement.
+ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ concepts::NestedRequirement *Req, ConstraintsCheck,
+ SourceRange InstantiationRange = SourceRange());
+
/// Note that we have finished instantiating this template.
void Clear();
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index acd1e604211a..7fc42a4816ec 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -13,10 +13,17 @@
#ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H
#define LLVM_CLANG_SEMA_SEMACONCEPT_H
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
+#include <string>
+#include <utility>
+
namespace clang {
class Sema;
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 1bfcbda8c9f1..44a12c875da7 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1403,6 +1403,9 @@ namespace serialization {
/// An LifetimeExtendedTemporaryDecl record.
DECL_LIFETIME_EXTENDED_TEMPORARY,
+ /// A RequiresExprBodyDecl record.
+ DECL_REQUIRES_EXPR_BODY,
+
/// An ObjCTypeParamDecl record.
DECL_OBJC_TYPE_PARAM,
@@ -1785,6 +1788,7 @@ namespace serialization {
EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
EXPR_CXX_FOLD, // CXXFoldExpr
EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr
+ EXPR_REQUIRES, // RequiresExpr
// CUDA
EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index bd9b0934591c..e79c245d2f8c 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 6ee767ccecf7..cb4d61cac2c7 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 2ead1e70ea0d..48e310e858b2 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 73ddbc62482d..835198958766 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 422227d787b1..e4bd218ae2d3 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 9dbf6fe9e0f0..d201af31f521 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 000000000000..76d57ed5d5b1
--- /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 c4b27b5d1daa..c79973507323 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9912,6 +9912,7 @@ class IntExprEvaluator
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 0d567edac521..5d485e000750 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 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) {
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 b6e4d8aff21e..7409ae7ddc9e 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 c14bb886bb11..45fd8ceae8d3 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 c0b0f3b0b064..382ea5c8d7ef 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 5aac7a8d54c7..60f1dba7c768 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 3f23fe11e4f5..de5c3a03fb68 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -680,6 +680,10 @@ class ScalarExprEmitter
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 8574d0a7e813..935c64a0fa13 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -429,6 +429,10 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
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 2c7e3a56c043..42e6fd88515a 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 192c0e99e5a5..178cb1b661c7 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 32dacbcc9646..df8388926db1 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 7d477d271dc2..036eabb94dd7 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 2cd158a8b43c..f8da1cb89b9d 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 018ac2d7dc9d..93e5b4511da9 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 507e4a6cd436..372f3d158597 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 5aedbe7644e4..193eaa3e01f9 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 5f4071924d3f..ea4b93ee6a5a 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 938420d85c65..192c237b6c1c 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 0ed51de0cc13..8d96404a5c27 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 8a50a9e1538f..661a66246a53 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 af41e231134d..39bc28d62305 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;
@@ -364,6 +367,26 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
PointOfInstantiation, InstantiationRange, Param, Template,
TemplateArgs) {}
+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,
@@ -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 64500d0a26d5..a470cfc87440 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 a860640444e3..efb4437b3e60 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 3b827fbc950b..1f7251173838 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 @@ class TreeTransform {
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 @@ class TreeTransform {
}
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 @@ class TreeTransform {
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
diff erent 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
diff erent 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 cdb5b17022c2..f93f1f77405d 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 96a7d5ae0a31..4fd079e9d8e1 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 f558c26b5f1e..5dd0ef9d43c3 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 8247f5a84eca..c216b14661d4 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 b2a8c118d401..459e61713ed1 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 9231f3b2b9ba..1b118c257a4c 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 f917a4c8637b..b542cf2c0303 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
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
new file mode 100644
index 000000000000..578efb5b24ea
--- /dev/null
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp
@@ -0,0 +1,175 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+static_assert(requires { { 0 }; });
+static_assert(requires { { "aaaa" }; });
+static_assert(requires { { (0).da }; }); // expected-error{{member reference base type 'int' is not a structure or union}}
+
+void foo() {}
+static_assert(requires { { foo() }; });
+
+// Substitution failure in expression
+
+struct A {};
+struct B {
+ B operator+(const B &other) const { return other; }
+};
+struct C {
+ C operator+(C &other) const { return other; }
+};
+
+template<typename T> requires requires (T a, const T& b) { { a + b }; } // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}} expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}}
+struct r1 {};
+
+using r1i1 = r1<int>;
+using r1i2 = r1<A>; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}}
+using r1i3 = r1<B>;
+using r1i4 = r1<C>; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}}
+
+struct D { void foo() {} };
+
+template<typename T> requires requires (T a) { { a.foo() }; } // expected-note{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}} expected-note{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}} expected-note{{because 'a.foo()' would be invalid: 'this' argument to member function 'foo' has type 'const D', but function is not marked const}}
+struct r2 {};
+
+using r2i1 = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
+using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}}
+using r2i3 = r2<D>;
+using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}
+
+template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
+struct r3 {};
+
+using r3i1 = r3<int>;
+using r3i2 = r3<A>;
+using r3i3 = r3<A &>;
+using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
+using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
+
+// Non-dependent expressions
+
+template<typename T> requires requires (T t) { { 0 }; { "a" }; { (void)'a' }; }
+struct r4 {};
+
+using r4i1 = r4<int>;
+using r4i2 = r4<int[10]>;
+using r4i3 = r4<int(int)>;
+
+// Noexcept requirement
+void maythrow() { }
+static_assert(!requires { { maythrow() } noexcept; });
+static_assert(requires { { 1 } noexcept; });
+
+struct E { void operator++(int) noexcept; };
+struct F { void operator++(int); };
+
+template<typename T> requires requires (T t) { { t++ } noexcept; } // expected-note{{because 't ++' may throw an exception}}
+struct r5 {};
+
+using r5i1 = r5<int>;
+using r5i2 = r5<E>;
+using r5i2 = r5<F>; // expected-error{{constraints not satisfied for class template 'r5' [with T = F]}}
+
+template<typename T> requires requires (T t) { { t.foo() } noexcept; } // expected-note{{because 't.foo()' would be invalid: no member named 'foo' in 'E'}}
+struct r6 {};
+
+using r6i = r6<E>; // expected-error{{constraints not satisfied for class template 'r6' [with T = E]}}
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+template<typename T, typename U>
+concept Same = is_same_v<T, U>;
+
+template<typename T>
+concept Large = sizeof(T) >= 4; // expected-note{{because 'sizeof(short) >= 4' (2 >= 4) evaluated to false}}
+
+template<typename T> requires requires (T t) { { t } -> Large; } // expected-note{{because 'decltype(t)' (aka 'short') does not satisfy 'Large':}}
+struct r7 {};
+
+using r7i1 = r7<int>;
+using r7i2 = r7<short>; // expected-error{{constraints not satisfied for class template 'r7' [with T = short]}}
+
+template<typename T> requires requires (T t) { { t } -> Same<T>; }
+struct r8 {};
+
+using r8i1 = r8<int>;
+using r8i2 = r8<short*>;
+
+// Substitution failure in type constraint
+
+template<typename T> requires requires (T t) { { t } -> Same<typename T::type>; } // expected-note{{because 'Same<expr-type, typename T::type>' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
+struct r9 {};
+
+struct M { using type = M; };
+
+using r9i1 = r9<M>;
+using r9i2 = r9<int>; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}}
+
+// Substitution failure in both expression and return type requirement
+
+template<typename T> requires requires (T t) { { t.foo() } -> Same<typename T::type>; } // expected-note{{because 't.foo()' would be invalid: member reference base type 'int' is not a structure or union}}
+struct r10 {};
+
+using r10i = r10<int>; // expected-error{{constraints not satisfied for class template 'r10' [with T = int]}}
+
+// Non-type concept in type constraint
+
+template<int T>
+concept IsEven = (T % 2) == 0;
+
+template<typename T> requires requires (T t) { { t } -> IsEven; } // expected-error{{concept named in type constraint is not a type concept}}
+struct r11 {};
+
+// C++ [expr.prim.req.compound] Example
+namespace std_example {
+ template<typename T> concept C1 =
+ requires(T x) {
+ {x++};
+ };
+
+ template<typename T, typename U> constexpr bool is_same_v = false;
+ template<typename T> constexpr bool is_same_v<T, T> = true;
+
+ template<typename T, typename U> concept same_as = is_same_v<T, U>;
+ // expected-note at -1 {{because 'is_same_v<int, int *>' evaluated to false}}
+
+ static_assert(C1<int>);
+ static_assert(C1<int*>);
+ template<C1 T> struct C1_check {};
+ using c1c1 = C1_check<int>;
+ using c1c2 = C1_check<int[10]>;
+
+ template<typename T> concept C2 =
+ requires(T x) {
+ {*x} -> same_as<typename T::inner>;
+ // expected-note at -1{{because type constraint 'same_as<int, typename T2::inner>' was not satisfied:}}
+ // expected-note at -2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}}
+ };
+
+ struct T1 {
+ using inner = int;
+ inner operator *() { return 0; }
+ };
+ struct T2 {
+ using inner = int *;
+ int operator *() { return 0; }
+ };
+ static_assert(C2<T1>);
+ template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}}
+ using c2c1 = C2_check<int>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}}
+ using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}}
+
+ template<typename T>
+ void g(T t) noexcept(sizeof(T) == 1) {}
+
+ template<typename T> concept C5 =
+ requires(T x) {
+ {g(x)} noexcept; // expected-note{{because 'g(x)' may throw an exception}}
+ };
+
+ static_assert(C5<char>);
+ template<C5 T> struct C5_check {}; // expected-note{{because 'short' does not satisfy 'C5'}}
+ using c5 = C5_check<short>; // expected-error{{constraints not satisfied for class template 'C5_check' [with T = short]}}
+}
\ No newline at end of file
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp
new file mode 100644
index 000000000000..ea3453201902
--- /dev/null
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp
@@ -0,0 +1,125 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+template<typename T, typename U> constexpr bool is_same_v = false;
+template<typename T> constexpr bool is_same_v<T, T> = true;
+
+template<typename T> struct identity { using type = T; };
+template<typename T> using identity_t = T;
+
+// Type requirements
+template<typename T> requires requires { typename identity_t<T>; }
+struct r1;
+template<typename U> requires requires { typename identity_t<U>; } // expected-note{{previous template declaration is here}}
+struct r1;
+template<typename T> requires requires { typename identity_t<T*>; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r1;
+template<typename T> requires requires { typename ::identity_t<T>; }
+struct r1;
+
+template<typename Y> requires requires { typename identity<Y>::type; }
+struct r2;
+template<typename U> requires requires { typename identity<U>::type; }
+struct r2;
+template<typename T> requires requires { typename ::identity<T>::type; } // expected-note 2{{previous template declaration is here}}
+struct r2;
+template<typename T> requires requires { typename identity<T>::typr; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r2;
+namespace ns {
+ template<typename T> struct identity { using type = T; };
+}
+template<typename T> requires requires { typename ns::identity<T>::type; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r2;
+
+template<typename T> requires requires { typename T::template identity<T>::type; }
+struct r3;
+template<typename U> requires requires { typename U::template identity<U>::type; } // expected-note{{previous template declaration is here}}
+struct r3;
+template<typename T> requires requires { typename T::template identitr<T>::type; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r3;
+
+template<typename T> requires requires { typename T::template temp<>; }
+struct r4;
+template<typename U> requires requires { typename U::template temp<>; }
+struct r4;
+
+// Expr requirements
+template<typename T> requires requires { 0; } // expected-note{{previous template declaration is here}}
+struct r5;
+template<typename T> requires requires { 1; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r5;
+
+template<typename T>
+concept C1 = true;
+
+template<typename T> requires requires { sizeof(T); }
+struct r6;
+template<typename U> requires requires { sizeof(U); } // expected-note{{previous template declaration is here}}
+struct r6;
+template<typename U> requires requires { sizeof(U) - 1; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r6;
+template<typename T> requires requires { { sizeof(T) }; } // expected-note 2{{previous template declaration is here}}
+struct r6;
+template<typename T> requires requires { { sizeof(T) } noexcept; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r6;
+template<typename T> requires requires { { sizeof(T) } -> C1; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r6;
+
+template<typename T> requires requires { { sizeof(T) } -> C1; }
+struct r7;
+template<typename U> requires requires { { sizeof(U) } -> C1; }
+struct r7;
+template<typename T> requires requires { { sizeof(T) } -> C1<>; } // expected-note {{previous template declaration is here}}
+struct r7;
+template<typename U> requires requires { { sizeof(U) }; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r7;
+
+template<typename T, typename U>
+concept C2 = true;
+
+template<typename T> requires requires { { sizeof(T) } -> C2<T>; }
+struct r8;
+template<typename U> requires requires { { sizeof(U) } -> C2<U>; } // expected-note{{previous template declaration is here}}
+struct r8;
+template<typename T> requires requires { { sizeof(T) } -> C2<T*>; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r8;
+
+// Nested requirements
+template<typename T> requires requires { requires sizeof(T) == 0; }
+struct r9;
+template<typename U> requires requires { requires sizeof(U) == 0; } // expected-note{{previous template declaration is here}}
+struct r9;
+template<typename T> requires requires { requires sizeof(T) == 1; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r9;
+
+// Parameter list
+template<typename T> requires requires { requires true; }
+struct r10;
+template<typename T> requires requires() { requires true; } // expected-note{{previous template declaration is here}}
+struct r10;
+template<typename T> requires requires(T i) { requires true; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r10;
+
+template<typename T> requires requires(T i, T *j) { requires true; } // expected-note 2{{previous template declaration is here}}
+struct r11;
+template<typename T> requires requires(T i) { requires true; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r11;
+template<typename T> requires requires(T i, T *j, T &k) { requires true; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r11;
+
+// Parameter names
+template<typename T> requires requires(int i) { requires sizeof(i) == 1; }
+struct r12;
+template<typename T> requires requires(int j) { requires sizeof(j) == 1; } // expected-note 2{{previous template declaration is here}}
+struct r12;
+template<typename T> requires requires(int k) { requires sizeof(k) == 2; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r12;
+template<typename T> requires requires(const int k) { requires sizeof(k) == 1; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r12;
+
+// Order of requirements
+template<typename T> requires requires { requires true; 0; }
+struct r13;
+template<typename T> requires requires { requires true; 0; } // expected-note{{previous template declaration is here}}
+struct r13;
+template<typename T> requires requires { 0; requires true; } // expected-error{{requires clause
diff ers in template redeclaration}}
+struct r13;
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
new file mode 100644
index 000000000000..f8776832d33f
--- /dev/null
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+static_assert(requires { requires true; });
+
+template<typename T> requires requires { requires false; } // expected-note{{because 'false' evaluated to false}}
+struct r1 {};
+
+using r1i = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
+
+template<typename T> requires requires { requires sizeof(T) == 0; } // expected-note{{because 'sizeof(int) == 0' (4 == 0) evaluated to false}}
+struct r2 {};
+
+using r2i = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
+
+template<typename T> requires requires (T t) { requires sizeof(t) == 0; } // expected-note{{because 'sizeof (t) == 0' (4 == 0) evaluated to false}}
+struct r3 {};
+
+using r3i = r3<int>; // expected-error{{constraints not satisfied for class template 'r3' [with T = int]}}
+
+template<typename T>
+struct X {
+ template<typename U> requires requires (U u) { requires sizeof(u) == sizeof(T); } // expected-note{{because 'sizeof (u) == sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}}
+ struct r4 {};
+};
+
+using r4i = X<void>::r4<int>; // expected-error{{constraints not satisfied for class template 'r4' [with U = int]}}
+
+// C++ [expr.prim.req.nested] Examples
+namespace std_example {
+ template<typename U> concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}}
+ template<typename T> concept D =
+ requires (T t) {
+ requires C1<decltype (+t)>; // expected-note{{because 'decltype(+t)' (aka 'int') does not satisfy 'C1'}}
+ };
+
+ struct T1 { char operator+() { return 'a'; } };
+ static_assert(D<T1>);
+ template<D T> struct D_check {}; // expected-note{{because 'short' does not satisfy 'D'}}
+ using dc1 = D_check<short>; // expected-error{{constraints not satisfied for class template 'D_check' [with T = short]}}
+
+ template<typename T>
+ concept C2 = requires (T a) { // expected-note{{'a' declared here}}
+ requires sizeof(a) == 4; // OK
+ requires a == 0; // expected-error{{constraint variable 'a' cannot be used in an evaluated context}}
+ };
+}
\ No newline at end of file
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp
new file mode 100644
index 000000000000..a1a745253dd4
--- /dev/null
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+// Examples from standard
+
+template<typename T, typename U>
+concept convertible_to = requires(T t) { U(t); };
+
+template<typename T>
+concept R = requires (T i) {
+ typename T::type;
+ {*i} -> convertible_to<const typename T::type&>;
+};
+
+template<typename T> requires R<T> struct S {};
+
+struct T {
+ using type = int;
+ type i;
+ const type &operator*() { return i; }
+};
+
+using si = S<T>;
+
+template<typename T>
+requires requires (T x) { x + x; } // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}}
+T add(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}}
+
+int x = add(1, 2);
+int y = add(T{}, T{}); // expected-error{{no matching function for call to 'add'}}
+
+template<typename T>
+concept C = requires (T x) { x + x; }; // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}}
+template<typename T> requires C<T> // expected-note{{because 'T' does not satisfy 'C'}}
+T add2(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}}
+
+int z = add2(1, 2);
+int w = add2(T{}, T{}); // expected-error{{no matching function for call to 'add2'}}
\ No newline at end of file
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp
new file mode 100644
index 000000000000..f9d8c3eed111
--- /dev/null
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+using A = int;
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+template<typename T, typename U>
+concept same_as = is_same_v<T, U>;
+
+static_assert(requires { requires true; 0; typename A;
+ { 0 } -> same_as<int>; });
+static_assert(is_same_v<bool, decltype(requires { requires false; })>);
+
+// Check that requires expr is an unevaluated context.
+struct Y {
+ int i;
+ static constexpr bool r = requires { i; };
+};
+
+template<typename T> requires requires (T t) {
+ requires false; // expected-note{{because 'false' evaluated to false}}
+ requires false;
+ requires requires {
+ requires false;
+ };
+}
+struct r1 { };
+
+using r1i = r1<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r1' [with T = int]}}
+
+template<typename T> requires requires (T t) {
+ requires requires {
+ requires false; // expected-note{{because 'false' evaluated to false}}
+ };
+}
+struct r2 { };
+
+using r2i = r2<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r2' [with T = int]}}
+
+template<typename T> requires requires (T t) {
+ requires requires {
+ requires true;
+ };
+ requires true;
+ requires requires {
+ requires false; // expected-note{{because 'false' evaluated to false}}
+ };
+}
+struct r3 { };
+
+using r3i = r3<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r3' [with T = int]}}
+
+template<typename T>
+struct S { static const int s = T::value; };
+
+template<typename T> requires requires { T::value; S<T>::s; }
+// expected-note at -1 {{because 'T::value' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
+struct r4 { };
+
+using r4i = r4<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r4' [with T = int]}}
\ No newline at end of file
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp
new file mode 100644
index 000000000000..a5e6c3057fe6
--- /dev/null
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp
@@ -0,0 +1,106 @@
+// RUN: %clang_cc1 %s -I%S -std=c++2a -fconcepts-ts -verify
+
+namespace std { struct type_info; }
+
+static_assert(requires { 0; });
+static_assert(requires { "aaaa"; });
+static_assert(requires { (0).da; }); // expected-error{{member reference base type 'int' is not a structure or union}}
+
+struct A {};
+struct B {
+ B operator+(const B &other) const { return other; }
+};
+struct C {
+ C operator+(C &other) const { return other; }
+};
+
+template<typename T> requires requires (T a, const T& b) { a + b; }
+// expected-note at -1{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}}
+// expected-note at -2{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}}
+struct r1 {};
+
+using r1i1 = r1<int>;
+using r1i2 = r1<A>; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}}
+using r1i3 = r1<B>;
+using r1i4 = r1<C>; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}}
+
+struct D { void foo() {} };
+
+template<typename T> requires requires (T a) { a.foo(); }
+// expected-note at -1{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}}
+// expected-note at -2{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}}
+// expected-note at -3{{because 'a.foo()' would be invalid: 'this' argument to member function 'foo' has type 'const D', but function is not marked const}}
+struct r2 {};
+
+using r2i1 = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
+using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}}
+using r2i3 = r2<D>;
+using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}
+
+template<typename T> requires requires { sizeof(T); }
+// expected-note at -1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}}
+// expected-note at -2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
+struct r3 {};
+
+using r3i1 = r3<int>;
+using r3i2 = r3<A>;
+using r3i3 = r3<A &>;
+using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
+using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
+
+template<typename T> requires requires (T t) { 0; "a"; (void)'a'; }
+struct r4 {};
+
+using r4i1 = r4<int>;
+using r4i2 = r4<int[10]>;
+using r4i3 = r4<int(int)>;
+
+template<class T> void f(T) = delete;
+template<class T> requires (sizeof(T) == 1) void f(T) { }
+
+template<typename T> requires requires(T t) { f(t); }
+// expected-note at -1{{because 'f(t)' would be invalid: call to deleted function 'f'}}
+struct r5 {};
+
+using r5i1 = r5<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r5' [with T = int]}}
+using r5i2 = r5<char>;
+
+template<typename T>
+struct E {
+ struct non_default_constructible { non_default_constructible(T t) { } };
+};
+
+template<typename T> requires requires(T t) { typename E<T>::non_default_constructible{}; }
+// expected-note at -1 {{because 'typename E<T>::non_default_constructible({})' would be invalid: no matching constructor for initialization of 'typename E<int>::non_default_constructible'}}
+struct r6 {};
+
+using r6i1 = r6<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r6' [with T = int]}}
+
+template<typename T> requires requires(T t) { typename E<T>::non_default_constructible(); }
+// expected-note at -1 {{because 'typename E<T>::non_default_constructible()' would be invalid: no matching constructor for initialization of 'typename E<int>::non_default_constructible'}}
+struct r7 {};
+
+using r7i1 = r7<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r7' [with T = int]}}
+
+// C++ [expr.prim.req.simple] Example
+namespace std_example {
+ template<typename T> concept C =
+ requires (T a, T b) { // expected-note{{because substituted constraint expression is ill-formed: argument may not have 'void' type}}
+ a + b; // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('int *' and 'int *')}}
+ };
+
+ static_assert(C<int>);
+ template<C T> struct C_check {}; // expected-note{{because 'void' does not satisfy 'C'}} expected-note{{because 'int *' does not satisfy 'C'}}
+ using c1c1 = C_check<void>; // expected-error{{constraints not satisfied for class template 'C_check' [with T = void]}}
+ using c1c2 = C_check<int *>; // expected-error{{constraints not satisfied for class template 'C_check' [with T = int *]}}
+}
+
+// typeid() of an expression becomes potentially evaluated if the expression is
+// of a polymorphic type.
+class X { virtual ~X(); };
+constexpr bool b = requires (X &x) { static_cast<int(*)[(typeid(x), 0)]>(nullptr); };
+// expected-error at -1{{constraint variable 'x' cannot be used in an evaluated context}}
+// expected-note at -2{{'x' declared here}}
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
new file mode 100644
index 000000000000..8e402a88c403
--- /dev/null
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
@@ -0,0 +1,194 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+using A = int;
+
+template<typename T> using identity_t = T; // expected-note 4{{template is declared here}}}
+
+template<typename T> struct identity { using type = T; };
+// expected-note at -1 2{{template is declared here}}
+
+struct C {};
+
+struct D { static int type; }; // expected-note{{referenced member 'type' is declared here}}
+
+// Basic unqualified and global-qualified lookups
+
+static_assert(requires { typename A; typename ::A; });
+static_assert(requires { typename identity_t<A>; typename ::identity_t<A>; });
+static_assert(!requires { typename identity_t<A, A>; }); // expected-error{{too many template arguments for alias template 'identity_t'}}
+static_assert(!requires { typename ::identity_t<A, A>; }); // expected-error{{too many template arguments for alias template 'identity_t'}}
+static_assert(requires { typename identity<A>; });
+static_assert(!requires { typename identity; });
+// expected-error at -1 {{typename specifier refers to class template; argument deduction not allowed here}}
+static_assert(!requires { typename ::identity; });
+// expected-error at -1 {{typename specifier refers to class template; argument deduction not allowed here}}
+static_assert(!requires { typename identity_t; });
+// expected-error at -1 {{typename specifier refers to alias template; argument deduction not allowed here}}
+static_assert(!requires { typename ::identity_t; });
+// expected-error at -1 {{typename specifier refers to alias template; argument deduction not allowed here}}
+
+namespace ns {
+ using B = int;
+ int C = 0;
+ // expected-note at -1 {{referenced 'C' is declared here}}
+ static_assert(requires { typename A; typename B; typename ::A; });
+ static_assert(!requires { typename ns::A; }); // expected-error{{no type named 'A' in namespace 'ns'}}
+ static_assert(!requires { typename ::B; }); // expected-error{{no type named 'B' in the global namespace}}
+ static_assert(requires { typename C; });
+ // expected-error at -1 {{typename specifier refers to non-type 'C'}}
+}
+
+// member type lookups
+
+static_assert(requires { typename identity<int>::type; typename ::identity<int>::type; });
+static_assert(!requires { typename identity<int>::typr; }); // expected-error{{no type named 'typr' in 'identity<int>'}}
+static_assert(!requires { typename ::identity<int>::typr; }); // expected-error{{no type named 'typr' in 'identity<int>'}}
+
+template<typename T> requires requires { typename T::type; }
+// expected-note at -1 {{because 'typename T::type' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
+// expected-note at -2 {{because 'typename T::type' would be invalid: no type named 'type' in 'C'}}
+// expected-note at -3 {{because 'typename T::type' would be invalid: typename specifier refers to non-type member 'type' in 'D'}}
+// expected-note at -4 {{in instantiation of template class 'invalid<D>' requested here}}
+// expected-note at -5 {{in instantiation of requirement here}}
+// expected-note at -6 {{while substituting template arguments into constraint expression here}}
+// expected-note at -7 {{because 'typename T::type' would be invalid}}
+struct r1 {};
+
+using r1i1 = r1<identity<int>>;
+using r1i2 = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
+using r1i3 = r1<C>; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}}
+using r1i4 = r1<D>; // expected-error{{constraints not satisfied for class template 'r1' [with T = D]}}
+
+template<typename T> struct invalid { typename T::type x; };
+// expected-error at -1 {{typename specifier refers to non-type member 'type' in 'D'}}
+using r1i5 = r1<invalid<D>>;
+// expected-error at -1 {{constraints not satisfied for class template 'r1' [with T = invalid<D>]}}
+// expected-note at -2 {{while checking constraint satisfaction for template 'r1<invalid<D> >' required here}}
+
+// mismatching template arguments
+
+template<typename... Ts> requires requires { typename identity<Ts...>; } // expected-note{{because 'typename identity<Ts...>' would be invalid: too many template arguments for class template 'identity'}}
+struct r2 {};
+
+using r2i1 = r2<int>;
+using r2i2 = r2<void>;
+using r2i3 = r2<int, int>; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = <int, int>]}}
+
+namespace ns2 {
+ template<typename T, typename U> struct identity {};
+
+ template<typename... Ts> requires requires { typename identity<Ts...>; } // expected-note 2{{because 'typename identity<Ts...>' would be invalid: too few template arguments for class template 'identity'}}
+ struct r4 {};
+
+ using r4i1 = r4<int>; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = <int>]}}
+}
+
+using r4i2 = ns2::r4<int>; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = <int>]}}
+
+using E = int;
+template<typename T> requires requires { typename E<T>; } // expected-error{{expected ';' at end of requirement}}
+struct r5v1 {};
+template<typename T> requires requires { typename ::E<T>; } // expected-error{{expected ';' at end of requirement}}
+struct r5v2 {};
+
+template<typename T> requires (sizeof(T) == 1)
+struct chars_only {};
+
+template<typename T> requires requires { typename chars_only<T>; } // expected-note{{because 'typename chars_only<T>' would be invalid: constraints not satisfied for class template 'chars_only' [with T = int]}}
+struct r6 {};
+
+using r6i = r6<int>; // expected-error{{constraints not satisfied for class template 'r6' [with T = int]}}
+
+template<typename T> int F = 0; // expected-note 2{{variable template 'F' declared here}}
+
+static_assert(!requires { typename F<int>; });
+// expected-error at -1{{template name refers to non-type template 'F'}}
+static_assert(!requires { typename ::F<int>; });
+// expected-error at -1{{template name refers to non-type template '::F'}}
+
+struct G { template<typename T> static T temp; };
+
+template<typename T> requires requires { typename T::template temp<int>; }
+// expected-note at -1{{because 'typename T::temp<int>' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
+// expected-note at -2{{because 'typename T::temp<int>' would be invalid: no member named 'temp' in 'D'}}
+// expected-note at -3{{because 'typename T::temp<int>' would be invalid: template name refers to non-type template 'G::template temp'}}
+struct r7 {};
+
+using r7i1 = r7<int>; // expected-error{{constraints not satisfied for class template 'r7' [with T = int]}}
+using r7i2 = r7<D>; // expected-error{{constraints not satisfied for class template 'r7' [with T = D]}}
+using r7i3 = r7<G>; // expected-error{{constraints not satisfied for class template 'r7' [with T = G]}}
+
+template<typename T> struct H;
+
+template<typename T> requires requires { typename H<T>; }
+struct r8 {};
+
+using r8i = r8<int>;
+
+template<typename T> struct I { struct incomplete; }; // expected-note{{member is declared here}}
+
+static_assert(!requires { I<int>::incomplete::inner; }); // expected-error{{implicit instantiation of undefined member 'I<int>::incomplete'}}
+
+template<typename T> requires requires { typename I<T>::incomplete::inner; } // expected-note{{because 'typename I<T>::incomplete::inner' would be invalid: implicit instantiation of undefined member 'I<int>::incomplete'}}
+struct r9 {};
+
+using r9i = r9<int>; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}}
+
+namespace ns3 {
+ struct X { }; // expected-note 2{{candidate found by name lookup is 'ns3::X'}}
+}
+
+struct X { using inner = int; }; // expected-note 2{{candidate found by name lookup is 'X'}}
+
+using namespace ns3;
+static_assert(requires { typename X; }); // expected-error{{reference to 'X' is ambiguous}}
+static_assert(requires { typename X::inner; }); // expected-error{{reference to 'X' is ambiguous}}
+// expected-error at -1{{unknown type name 'inner'}}
+
+// naming a type template specialization in a type requirement does not require
+// it to be complete and should not care about partial specializations.
+
+template<typename T>
+struct Z;
+
+template<typename T> requires (sizeof(T) >= 1)
+struct Z<T> {}; // expected-note{{partial specialization matches [with T = int]}}
+
+template<typename T> requires (sizeof(T) <= 4)
+struct Z<T> {}; // expected-note{{partial specialization matches [with T = int]}}
+
+Z<int> x; // expected-error{{ambiguous partial specializations of 'Z<int>'}}
+
+static_assert(requires { typename Z<int>; });
+
+// C++ [expr.prim.req.type] Example
+namespace std_example {
+ template<typename T, typename T::type = 0> struct S;
+ // expected-note at -1 {{because 'typename S<T>' would be invalid: no type named 'type' in 'std_example::has_inner}}
+ template<typename T> using Ref = T&; // expected-note{{because 'typename Ref<T>' would be invalid: cannot form a reference to 'void'}}
+ template<typename T> concept C1 =
+ requires {
+ typename T::inner;
+ // expected-note at -1 {{because 'typename T::inner' would be invalid: type 'int' cannot be used prior to '::' because it has no members}}
+ // expected-note at -2 {{because 'typename T::inner' would be invalid: no type named 'inner' in 'std_example::has_type'}}
+ };
+ template<typename T> concept C2 = requires { typename S<T>; };
+ template<typename T> concept C3 = requires { typename Ref<T>; };
+
+ struct has_inner { using inner = int;};
+ struct has_type { using type = int; };
+ struct has_inner_and_type { using inner = int; using type = int; };
+
+ static_assert(C1<has_inner_and_type> && C2<has_inner_and_type> && C3<has_inner_and_type>);
+ template<C1 T> struct C1_check {};
+ // expected-note at -1 {{because 'int' does not satisfy 'C1'}}
+ // expected-note at -2 {{because 'std_example::has_type' does not satisfy 'C1'}}
+ template<C2 T> struct C2_check {};
+ // expected-note at -1 {{because 'std_example::has_inner' does not satisfy 'C2'}}
+ template<C3 T> struct C3_check {};
+ // expected-note at -1 {{because 'void' does not satisfy 'C3'}}
+ using c1 = C1_check<int>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}}
+ using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}}
+ using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}}
+ using c4 = C3_check<void>; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}}
+}
\ No newline at end of file
diff --git a/clang/test/PCH/cxx2a-requires-expr.cpp b/clang/test/PCH/cxx2a-requires-expr.cpp
new file mode 100644
index 000000000000..d52d451c2d68
--- /dev/null
+++ b/clang/test/PCH/cxx2a-requires-expr.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -emit-pch -std=c++2a -fconcepts-ts -o %t %s
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x ast -ast-print %t | FileCheck %s
+
+template<typename T>
+concept C = true;
+
+template<typename T, typename U>
+concept C2 = true;
+
+template<typename T>
+bool f() {
+ // CHECK: requires (T t) { t++; { t++ } noexcept -> C; { t++ } -> C2<int>; typename T::a; requires T::val; };
+ return requires (T t) {
+ t++;
+ { t++ } noexcept -> C;
+ { t++ } -> C2<int>;
+ typename T::a;
+ requires T::val;
+ };
+}
diff --git a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp
new file mode 100644
index 000000000000..a53189caa5f7
--- /dev/null
+++ b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp
@@ -0,0 +1,141 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify
+
+bool r1 = requires () {};
+// expected-error at -1 {{a requires expression must contain at least one requirement}}
+
+bool r2 = requires { requires true; };
+
+bool r3 = requires (int a, ...) { requires true; };
+// expected-error at -1 {{varargs not allowed in requires expression}}
+
+template<typename... T>
+bool r4 = requires (T... ts) { requires true; };
+
+bool r5 = requires (bool c, int d) { c; d; };
+
+bool r6 = requires (bool c, int d) { c; d; } && decltype(d){};
+// expected-error at -1 {{use of undeclared identifier 'd'}}
+
+bool r7 = requires (bool c) { c; (requires (int d) { c; d; }); d; } && decltype(c){} && decltype(d){};
+// expected-error at -1 2{{use of undeclared identifier 'd'}}
+// expected-error at -2 {{use of undeclared identifier 'c'}}
+
+bool r8 = requires (bool, int) { requires true; };
+
+bool r9 = requires (bool a, int a) { requires true; };
+// expected-error at -1 {{redefinition of parameter 'a'}}
+// expected-note at -2 {{previous declaration is here}}
+
+bool r10 = requires (struct new_struct { int x; } s) { requires true; };
+// expected-error at -1 {{'new_struct' cannot be defined in a parameter type}}
+
+bool r11 = requires (int x(1)) { requires true; };
+// expected-error at -1 {{expected parameter declarator}}
+// expected-error at -2 {{expected ')'}}
+// expected-note at -3 {{to match this '('}}
+
+bool r12 = requires (int x = 10) { requires true; };
+// expected-error at -1 {{default arguments not allowed for parameters of a requires expression}}
+
+bool r13 = requires (int f(int)) { requires true; };
+
+bool r14 = requires (int (*f)(int)) { requires true; };
+
+bool r15 = requires (10) { requires true; };
+// expected-error at -1 {{expected parameter declarator}}
+
+bool r16 = requires (auto x) { requires true; };
+// expected-error at -1 {{'auto' not allowed in requires expression parameter}}
+
+bool r17 = requires (auto [x, y]) { requires true; };
+// expected-error at -1 {{'auto' not allowed in requires expression parameter}}
+// expected-error at -2 {{use of undeclared identifier 'x'}}
+
+using a = int;
+
+bool r18 = requires { typename a; };
+
+bool r19 = requires { typename ::a; };
+
+template<typename T> struct identity { using type = T; };
+
+template<typename T> using identity_t = T;
+
+bool r20 = requires {
+ typename identity<int>::type;
+ typename identity<int>;
+ typename ::identity_t<int>;
+};
+
+struct s { bool operator==(const s&); ~s(); };
+
+bool r21 = requires { typename s::operator==; };
+// expected-error at -1 {{expected an identifier or template-id after '::'}}
+
+bool r22 = requires { typename s::~s; };
+// expected-error at -1 {{expected an identifier or template-id after '::'}}
+
+template<typename T>
+bool r23 = requires { typename identity<T>::temp<T>; };
+// expected-error at -1 {{use 'template' keyword to treat 'temp' as a dependent template name}}
+
+template<typename T>
+bool r24 = requires {
+ typename identity<T>::template temp<T>;
+ typename identity<T>::template temp; // expected-error{{expected an identifier or template-id after '::'}}
+};
+
+bool r25 = requires { ; };
+// expected-error at -1 {{expected expression}}
+
+bool r26 = requires { {}; };
+// expected-error at -1 {{expected expression}}
+
+bool r27 = requires { { 0 } noexcept; };
+
+bool r28 = requires { { 0 } noexcept noexcept; };
+// expected-error at -1 {{expected '->' before expression type requirement}}
+// expected-error at -2 {{expected concept name with optional arguments}}
+
+template<typename T>
+concept C1 = true;
+
+template<typename T, typename U>
+concept C2 = true;
+
+bool r29 = requires { { 0 } noexcept C1; };
+// expected-error at -1 {{expected '->' before expression type requirement}}
+
+bool r30 = requires { { 0 } noexcept -> C2<int>; };
+
+template<typename T>
+T i1 = 0;
+
+bool r31 = requires { requires false, 1; };
+// expected-error at -1 {{expected ';' at end of requirement}}
+
+bool r32 = requires { 0 noexcept; };
+// expected-error at -1 {{'noexcept' can only be used in a compound requirement (with '{' '}' around the expression)}}
+
+bool r33 = requires { 0 int; };
+// expected-error at -1 {{expected ';' at end of requirement}}
+
+bool r34 = requires { requires true };
+// expected-error at -1 {{expected ';' at end of requirement}}
+
+bool r35 = requires (bool b) { requires sizeof(b) == 1; };
+
+void r36(bool b) requires requires { 1 } {}
+// expected-error at -1 {{expected ';' at end of requirement}}
+
+bool r37 = requires { requires { 1; }; };
+// expected-warning at -1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
+
+bool r38 = requires { requires () { 1; }; };
+// expected-warning at -1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
+
+bool r39 = requires { requires (int i) { i; }; };
+// expected-warning at -1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}}
+
+bool r40 = requires { requires (); };
+// expected-error at -1 {{expected expression}}
diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
new file mode 100644
index 000000000000..3304fd25cdc9
--- /dev/null
+++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp
@@ -0,0 +1,216 @@
+// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify -Wno-unused-value
+
+template<typename T, typename U>
+constexpr bool is_same_v = false;
+
+template<typename T>
+constexpr bool is_same_v<T, T> = true;
+
+// We use a hack in this file to make the compiler print out the requires
+// expression after it has been instantiated - we put false_v<requires {...}> as
+// the requires clause of a class template, then instantiate the template.
+// The requirement will not be satisfied, and the explaining diagnostic will
+// print out false_v<requires {...}> in its raw form (the false_v serves to
+// prevent the diagnostic from elaborating on why the requires expr wasn't
+// satisfied).
+
+template<bool v>
+constexpr bool false_v = false;
+
+template<typename... Ts>
+using void_t = void;
+
+// Check that requires parameters are instantiated correctly.
+
+template<typename T> requires
+false_v<requires (T t) { requires is_same_v<decltype(t), int>; }>
+// expected-note at -1 {{because 'false_v<requires (int t) { requires is_same_v<decltype(t), int>; }>' evaluated to false}}
+// expected-note at -2 {{because 'false_v<requires (char t) { requires is_same_v<decltype(t), int>; }>' evaluated to false}}
+struct r1 {};
+
+using r1i1 = r1<int>; // expected-error {{constraints not satisfied for class template 'r1' [with T = int]}}
+using r1i2 = r1<char>; // expected-error {{constraints not satisfied for class template 'r1' [with T = char]}}
+
+// Check that parameter packs work.
+
+template<typename... Ts> requires
+false_v<requires (Ts... ts) {requires ((sizeof(ts) == 2) && ...);}>
+// expected-note at -1 {{because 'false_v<requires (short ts, unsigned short ts) { requires (sizeof (ts) == 2) && (sizeof (ts) == 2); }>'}}
+// expected-note at -2 {{because 'false_v<requires (short ts) { requires (sizeof (ts) == 2); }>' evaluated to false}}
+struct r2 {};
+
+using r2i1 = r2<short, unsigned short>; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = <short, unsigned short>]}}
+using r2i2 = r2<short>; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = <short>]}}
+
+template<typename... Ts> requires
+false_v<(requires (Ts ts) {requires sizeof(ts) != 0;} && ...)>
+// expected-note at -1 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; } && requires (unsigned short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
+// expected-note at -2 {{because 'false_v<requires (short ts) { requires sizeof (ts) != 0; }>' evaluated to false}}
+struct r3 {};
+
+using r3i1 = r3<short, unsigned short>; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = <short, unsigned short>]}}
+using r3i2 = r3<short>; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = <short>]}}
+
+template<typename T>
+struct identity { using type = T; };
+
+namespace type_requirement {
+ struct A {};
+
+ // check that nested name specifier is instantiated correctly.
+ template<typename T> requires false_v<requires { typename T::type; }> // expected-note{{because 'false_v<requires { typename identity<int>::type; }>' evaluated to false}}
+ struct r1 {};
+
+ using r1i = r1<identity<int>>; // expected-error{{constraints not satisfied for class template 'r1' [with T = identity<int>]}}
+
+ // check that template argument list is instantiated correctly.
+ template<typename T>
+ struct contains_template {
+ template<typename U> requires is_same_v<contains_template<T>, U>
+ using temp = int;
+ };
+
+ template<typename T> requires
+ false_v<requires { typename T::template temp<T>; }>
+ // expected-note at -1 {{because 'false_v<requires { typename contains_template<int>::temp<contains_template<int> >; }>' evaluated to false}}
+ // expected-note at -2 {{because 'false_v<requires { typename contains_template<short>::temp<contains_template<short> >; }>' evaluated to false}}
+ struct r2 {};
+
+ using r2i1 = r2<contains_template<int>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<int>]}}
+ using r2i2 = r2<contains_template<short>>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template<short>]}}
+
+ // substitution error occurs, then requires expr is instantiated again
+
+ template<typename T>
+ struct a {
+ template<typename U> requires (requires { typename T::a::a; }, false)
+ // expected-note at -1{{because 'requires { <<error-type>>; } , false' evaluated to false}}
+ struct r {};
+ };
+
+ using ari = a<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
+
+ // Parameter pack inside expr
+ template<typename... Ts> requires
+ false_v<(requires { typename Ts::type; } && ...)>
+ // expected-note at -1 {{because 'false_v<requires { typename identity<short>::type; } && requires { typename identity<int>::type; } && requires { <<error-type>>; }>' evaluated to false}}
+ struct r5 {};
+
+ using r5i = r5<identity<short>, identity<int>, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = <identity<short>, identity<int>, short>]}}
+ template<typename... Ts> requires
+ false_v<(requires { typename void_t<Ts>; } && ...)> // expected-note{{because 'false_v<requires { typename void_t<int>; } && requires { typename void_t<short>; }>' evaluated to false}}
+ struct r6 {};
+
+ using r6i = r6<int, short>; // expected-error{{constraints not satisfied for class template 'r6' [with Ts = <int, short>]}}
+
+ template<typename... Ts> requires
+ false_v<(requires { typename Ts::template aaa<Ts>; } && ...)>
+ // expected-note at -1 {{because 'false_v<requires { <<error-type>>; } && requires { <<error-type>>; }>' evaluated to false}}
+ struct r7 {};
+
+ using r7i = r7<int, A>; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = <int, type_requirement::A>]}}
+}
+
+namespace expr_requirement {
+ // check that compound/simple requirements are instantiated correctly.
+
+ template<typename T> requires false_v<requires { sizeof(T); { sizeof(T) }; }>
+ // expected-note at -1 {{because 'false_v<requires { sizeof(int); { sizeof(int) }; }>' evaluated to false}}
+ // expected-note at -2 {{because 'false_v<requires { <<error-expression>>; { sizeof(T) }; }>' evaluated to false}}
+ struct r1 {};
+
+ using r1i1 = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
+ using r1i2 = r1<void>; // expected-error{{constraints not satisfied for class template 'r1' [with T = void]}}
+
+ // substitution error occurs in expr, then expr is instantiated again.
+
+ template<typename T>
+ struct a {
+ template<typename U> requires (requires { sizeof(T::a); }, false) // expected-note{{because 'requires { <<error-expression>>; } , false' evaluated to false}}
+ struct r {};
+ };
+
+ using ari = a<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
+
+ // check that the return-type-requirement is instantiated correctly.
+
+ template<typename T, typename U = int>
+ concept C1 = is_same_v<T, U>;
+
+ template<typename T> requires false_v<requires(T t) { { t } -> C1<T>; }>
+ // expected-note at -1 {{because 'false_v<requires (int t) { { t } -> C1<int>; }>' evaluated to false}}
+ // expected-note at -2 {{because 'false_v<requires (double t) { { t } -> C1<double>; }>' evaluated to false}}
+ struct r2 {};
+
+ using r2i1 = r2<int>; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}}
+ using r2i2 = r2<double>; // expected-error{{constraints not satisfied for class template 'r2' [with T = double]}}
+
+
+ // substitution error occurs in return type requirement, then requires expr is
+ // instantiated again.
+
+ template<typename T>
+ struct b {
+ template<typename U> requires (requires { { 0 } -> C1<typename T::a>; }, false) // expected-note{{because 'requires { { 0 } -> <<error-type>>; } , false' evaluated to false}}
+ struct r {};
+ };
+
+ using bri = b<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
+
+
+ template<typename... Ts> requires
+ false_v<(requires { { 0 } noexcept -> C1<Ts>; } && ...)>
+ // expected-note at -1 {{because 'false_v<requires { { 0 } noexcept -> C1<int>; } && requires { { 0 } noexcept -> C1<unsigned int>; }>' evaluated to false}}
+ struct r3 {};
+
+ using r3i = r3<int, unsigned int>; // expected-error{{constraints not satisfied for class template 'r3' [with Ts = <int, unsigned int>]}}
+}
+
+namespace nested_requirement {
+ // check that constraint expression is instantiated correctly
+ template<typename T> requires false_v<requires { requires sizeof(T) == 2; }> // expected-note{{because 'false_v<requires { requires sizeof(int) == 2; }>' evaluated to false}}
+ struct r1 {};
+
+ using r1i = r1<int>; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}}
+
+ // substitution error occurs in expr, then expr is instantiated again.
+ template<typename T>
+ struct a {
+ template<typename U> requires
+ (requires { requires sizeof(T::a) == 0; }, false) // expected-note{{because 'requires { requires <<error-expression>>; } , false' evaluated to false}}
+ struct r {};
+ };
+
+ using ari = a<int>::r<short>; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}}
+
+ // Parameter pack inside expr
+ template<typename... Ts> requires
+ false_v<(requires { requires sizeof(Ts) == 0; } && ...)>
+ // expected-note at -1 {{because 'false_v<requires { requires sizeof(int) == 0; } && requires { requires sizeof(short) == 0; }>' evaluated to false}}
+ struct r2 {};
+
+ using r2i = r2<int, short>; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = <int, short>]}}
+}
+
+// Parameter pack inside multiple requirements
+template<typename... Ts> requires
+false_v<(requires { requires sizeof(Ts) == 0; sizeof(Ts); } && ...)>
+// expected-note at -1 {{because 'false_v<requires { requires sizeof(int) == 0; sizeof(Ts); } && requires { requires sizeof(short) == 0; sizeof(Ts); }>' evaluated to false}}
+struct r4 {};
+
+using r4i = r4<int, short>; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = <int, short>]}}
+
+template<typename... Ts> requires
+false_v<(requires(Ts t) { requires sizeof(t) == 0; t++; } && ...)>
+// expected-note at -1 {{because 'false_v<requires (int t) { requires sizeof (t) == 0; t++; } && requires (short t) { requires sizeof (t) == 0; t++; }>' evaluated to false}}
+struct r5 {};
+
+using r5i = r5<int, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = <int, short>]}}
+
+template<typename T> requires
+false_v<(requires(T t) { T{t}; })> // T{t} creates an "UnevaluatedList" context.
+// expected-note at -1 {{because 'false_v<(requires (int t) { int{t}; })>' evaluated to false}}
+struct r6 {};
+
+using r6i = r6<int>;
+// expected-error at -1 {{constraints not satisfied for class template 'r6' [with T = int]}}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 6f3490471de5..9a09fca5e269 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -6340,6 +6340,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::UsingPack:
case Decl::Concept:
case Decl::LifetimeExtendedTemporary:
+ case Decl::RequiresExprBody:
return C;
// Declaration kinds that don't make any sense here, but are
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index 3632e0f85d10..04b713c68b80 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -257,6 +257,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::TypeTraitExprClass:
case Stmt::CoawaitExprClass:
case Stmt::ConceptSpecializationExprClass:
+ case Stmt::RequiresExprClass:
case Stmt::DependentCoawaitExprClass:
case Stmt::CoyieldExprClass:
case Stmt::CXXBindTemporaryExprClass:
More information about the cfe-commits
mailing list