Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -3058,9 +3058,9 @@ ConstraintExpr = CE; } - // TODO: Should do source range properly. + SourceRange getSourceRange() const override LLVM_READONLY { - return SourceRange(getLocation(), getLocation()); + return SourceRange(getLocation(), getConstraintExpr()->getLocEnd()); } // Implement isa/cast/dyncast/etc. Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -57,6 +57,8 @@ class LambdaCapture; class NonTypeTemplateParmDecl; class TemplateParameterList; +class ConceptDecl; +class Sema; //===--------------------------------------------------------------------===// // C++ Expressions. @@ -4407,6 +4409,96 @@ } }; +/// \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 { +protected: + /// \brief The concept named. + ConceptDecl *NamedConcept; + + /// \brief The template argument list used to specialize the concept. + TemplateArgumentList *TemplateArgs; + TemplateArgumentListInfo TemplateArgInfo; + + /// \brief The location of the concept name in the expression. + SourceLocation ConceptNameLoc; + + /// \brief Whether or not the concept with the given arguments was satisfied + /// when the expression was created. If any of the template arguments are + /// dependent (this expr would then be isValueDependent()), this is to be + /// ignored. + bool IsSatisfied : 1; + + /// \brief Evaluates this concept specialization to determine whether or not + /// the concept is satisfied, and updates the IsSatisfied field. Returns true + /// if an error occured and the concept could not be checked for satisfaction. + bool calculateSatisfaction(Sema &S); + +public: + ConceptSpecializationExpr(ASTContext &C, Sema &S, + SourceLocation ConceptNameLoc, ConceptDecl *CD, + const TemplateArgumentListInfo *TALI); + + ConceptSpecializationExpr(ASTContext &C, EmptyShell Empty); + + ConceptDecl *getNamedConcept() { + return NamedConcept; + } + const ConceptDecl *getNamedConcept() const { + return NamedConcept; + } + void setNamedConcept(ConceptDecl *C) { + NamedConcept = C; + } + + const TemplateArgumentList *getTemplateArguments() const { + return TemplateArgs; + } + const TemplateArgumentListInfo *getTemplateArgumentListInfo() const { + return &TemplateArgInfo; + } + + /// \brief Set new template arguments for this concept specialization. Returns + /// true if an error occured (the template arguments do not match the concept, + /// probably) + bool setTemplateArguments(Sema *S, const TemplateArgumentListInfo *TALI); + + /// \brief Whether or not the concept with the given arguments was satisfied + /// when the expression was created. This method assumes that the expression + /// is not dependent! + bool isSatisfied() const { + assert(!isValueDependent() + && "isSatisfied called on a dependent ConceptSpecializationExpr"); + return IsSatisfied; + } + + void setSatisfied(bool Satisfied) { + IsSatisfied = Satisfied; + } + + SourceLocation getConceptNameLoc() const { return ConceptNameLoc; } + void setConceptNameLoc(SourceLocation Loc) { + ConceptNameLoc = Loc; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ConceptSpecializationExprClass; + } + + SourceLocation getLocStart() const LLVM_READONLY { return ConceptNameLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { + return TemplateArgInfo.getRAngleLoc(); + } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } +}; + } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2585,6 +2585,12 @@ } }) +DEF_TRAVERSE_STMT(ConceptSpecializationExpr, { + TRY_TO(TraverseTemplateArgumentLocsHelper( + S->getTemplateArgumentListInfo()->getArgumentArray(), + S->getTemplateArgumentListInfo()->size())); +}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(CharacterLiteral, {}) Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2394,14 +2394,18 @@ def err_concept_specialized : Error< "%select{function|variable}0 concept cannot be " "%select{explicitly instantiated|explicitly specialized|partially specialized}1">; -def err_concept_initialized_with_non_bool_type : Error< - "constraint expression must be 'bool' but found %0">; def err_concept_decls_may_only_appear_in_global_namespace_scope : Error< "concept declarations may only appear in global or namespace scope">; def err_concept_extra_headers : Error< "extraneous template parameter list in concept definition">; def err_concept_no_associated_constraints : Error< "concept may not have associated constraints">; +def err_concept_non_constant_constraint_expression : Error< + "concept specialization '%0' resulted in a non-constant expression '%1'">; +def err_non_bool_atomic_constraint : Error< + "atomic constraint '%0' must be of type 'bool' (found %1)">; +def note_in_concept_specialization : Note< + "in concept specialization '%0'">; def err_template_different_associated_constraints : Error< "associated constraints differ in template redeclaration">; Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -153,6 +153,9 @@ def DependentCoawaitExpr : DStmt; def CoyieldExpr : DStmt; +// C++2a Concepts expressions +def ConceptSpecializationExpr : DStmt; + // Obj-C Expressions. def ObjCStringLiteral : DStmt; def ObjCBoxedExpr : DStmt; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5572,7 +5572,15 @@ CXXConversionDecl *Conv, Expr *Src); - // ParseObjCStringLiteral - Parse Objective-C string literals. + ExprResult CreateConceptSpecializationExpr(SourceLocation ConceptNameLoc, + ConceptDecl *CTD, + const TemplateArgumentListInfo *TALI); + + /// Check whether the given expression is a valid constraint expression. + /// A diagnostic is emitted if it is not, and false is returned. + bool CheckConstraintExpression(Expr *CE); + + // ParseObjCStringLiteral - Parse Objective-C string literals. ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs, ArrayRef Strings); Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1789,6 +1789,7 @@ EXPR_FUNCTION_PARM_PACK, // FunctionParmPackExpr EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr EXPR_CXX_FOLD, // CXXFoldExpr + EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2998,6 +2998,7 @@ case ObjCAvailabilityCheckExprClass: case CXXUuidofExprClass: case OpaqueValueExprClass: + case ConceptSpecializationExprClass: // These never have a side-effect. return false; Index: lib/AST/ExprCXX.cpp =================================================================== --- lib/AST/ExprCXX.cpp +++ lib/AST/ExprCXX.cpp @@ -29,6 +29,9 @@ #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" +#include "clang/Sema/Template.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Sema/Sema.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" @@ -1433,3 +1436,96 @@ } void ArrayTypeTraitExpr::anchor() {} + +ConceptSpecializationExpr::ConceptSpecializationExpr( + ASTContext &C, Sema &S, SourceLocation ConceptNameLoc, ConceptDecl *CD, + const TemplateArgumentListInfo *TALI) + : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TypeDependent=*/false, + // All the flags below are set in setTemplateArguments. + /*ValueDependent=*/false, /*InstantiationDependent=*/false, + /*ContainsUnexpandedParameterPacks=*/false), + NamedConcept(CD), ConceptNameLoc(ConceptNameLoc) { + setTemplateArguments(&S, TALI); +} + +ConceptSpecializationExpr::ConceptSpecializationExpr(ASTContext &C, + EmptyShell Empty) + : Expr(ConceptSpecializationExprClass, Empty) { } + +bool ConceptSpecializationExpr::calculateSatisfaction(Sema &S) +{ + llvm::SmallVector Converted; + if (S.CheckTemplateArgumentList(NamedConcept, NamedConcept->getLocStart(), + TemplateArgInfo, + /*PartialTemplateArgs=*/false, Converted, + /*UpdateArgsWithConversion=*/false)) { + // We converted these arguments back in CheckConceptTemplateId, this should + // work. + return true; + } + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); + + ExprResult E; + { + // We do not want error diagnostics escaping here. + Sema::SFINAETrap Trap(S); + E = S.SubstExpr(NamedConcept->getConstraintExpr(), MLTAL); + if (E.isInvalid() || Trap.hasErrorOccurred()) { + // C++2a [temp.constr.atomic]p1 + // ...If substitution results in an invalid type or expression, the + // constraint is not satisfied. + IsSatisfied = false; + return true; + } + } + + if (!S.CheckConstraintExpression(E.get())) { + S.Diag(getLocStart(), diag::note_in_concept_specialization) << this; + return true; + } + + bool Satisfied = false; + if (!E.get()->EvaluateAsBooleanCondition(Satisfied, S.Context)) { + // C++2a [temp.constr.atomic]p1 + // ...E shall be a constant expression of type bool. + S.Diag(getLocStart(), diag::err_concept_non_constant_constraint_expression) + << this << E.get(); + return true; + } + IsSatisfied = Satisfied; + return false; +} + +bool ConceptSpecializationExpr::setTemplateArguments(Sema *S, + const TemplateArgumentListInfo *TALI){ + bool IsDependent = false; + bool ContainsUnexpandedParameterPack = false; + TemplateArgInfo = *TALI; + for (const TemplateArgumentLoc& LocInfo : TALI->arguments()) { + if (LocInfo.getArgument().isInstantiationDependent()) { + IsDependent = true; + if (ContainsUnexpandedParameterPack) { + break; + } + } + if (LocInfo.getArgument().containsUnexpandedParameterPack()) { + ContainsUnexpandedParameterPack = true; + if (IsDependent) { + break; + } + } + } + setValueDependent(IsDependent); + setInstantiationDependent(IsDependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); + if (!IsDependent && S) { + return calculateSatisfaction(*S); + } + return false; +} \ No newline at end of file Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -190,6 +190,7 @@ case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: + case Expr::ConceptSpecializationExprClass: return Cl::CL_PRValue; // Next come the complicated cases. Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -7074,6 +7074,7 @@ bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E); bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); + bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; } // end anonymous namespace @@ -9079,6 +9080,15 @@ return Success(E->getValue(), E); } +bool IntExprEvaluator::VisitConceptSpecializationExpr( + const ConceptSpecializationExpr *E) { + if (E->isValueDependent()) { + return Error(E); + } + return Success(E->isSatisfied(), E); +} + + //===----------------------------------------------------------------------===// // Float Evaluation //===----------------------------------------------------------------------===// @@ -10368,6 +10378,7 @@ case Expr::CXXBoolLiteralExprClass: case Expr::CXXScalarValueInitExprClass: case Expr::TypeTraitExprClass: + case Expr::ConceptSpecializationExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3464,6 +3464,7 @@ case Expr::ConvertVectorExprClass: case Expr::StmtExprClass: case Expr::TypeTraitExprClass: + case Expr::ConceptSpecializationExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::VAArgExprClass: Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -2549,6 +2549,12 @@ OS << ")"; } +void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { + OS << E->getNamedConcept()->getName(); + printTemplateArgumentList(OS, E->getTemplateArgumentListInfo()->arguments(), + Policy); +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1233,6 +1233,14 @@ ID.AddInteger(S->getOp()); } +void StmtProfiler::VisitConceptSpecializationExpr( + const ConceptSpecializationExpr *S) { + VisitExpr(S); + VisitDecl(S->getNamedConcept()); + VisitTemplateArguments(S->getTemplateArgumentListInfo()->getArgumentArray(), + S->getTemplateArgumentListInfo()->size()); +} + static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, UnaryOperatorKind &UnaryOp, BinaryOperatorKind &BinaryOp) { Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -602,6 +602,10 @@ return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue()); } + Value *VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) { + return Builder.getInt1(E->isSatisfied()); + } + Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) { return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue()); } Index: lib/Parse/ParseExpr.cpp =================================================================== --- lib/Parse/ParseExpr.cpp +++ lib/Parse/ParseExpr.cpp @@ -216,18 +216,14 @@ /// \brief Parse a constraint-expression. /// /// \verbatim -/// constraint-expression: [Concepts TS temp.constr.decl p1] +/// constraint-expression: C++2a[temp.constr.decl]p1 /// logical-or-expression /// \endverbatim ExprResult Parser::ParseConstraintExpression() { - // FIXME: this may erroneously consume a function-body as the braced - // initializer list of a compound literal - // - // FIXME: this may erroneously consume a parenthesized rvalue reference - // declarator as a parenthesized address-of-label expression ExprResult LHS(ParseCastExpression(/*isUnaryExpression=*/false)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr)); - + if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) + return ExprError(); return Res; } Index: lib/Parse/ParseTemplate.cpp =================================================================== --- lib/Parse/ParseTemplate.cpp +++ lib/Parse/ParseTemplate.cpp @@ -372,8 +372,7 @@ ExprResult ConstraintExprResult = ParseConstraintExpression(); if (ConstraintExprResult.isInvalid()) { - Diag(Tok.getLocation(), diag::err_expected_expression) - << "constraint-expression"; + // ParseConstraintExpression will have given a sufficient diagnostic. return nullptr; } Index: lib/Sema/CMakeLists.txt =================================================================== --- lib/Sema/CMakeLists.txt +++ lib/Sema/CMakeLists.txt @@ -25,6 +25,7 @@ SemaCast.cpp SemaChecking.cpp SemaCodeComplete.cpp + SemaConcept.cpp SemaConsumer.cpp SemaCoroutine.cpp SemaCUDA.cpp Index: lib/Sema/SemaConcept.cpp =================================================================== --- /dev/null +++ lib/Sema/SemaConcept.cpp @@ -0,0 +1,44 @@ +//===-- SemaConcept.cpp - Semantic Analysis for Constraints and Concepts --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis for C++ constraints and concepts. +// +//===----------------------------------------------------------------------===// + +#include "clang/Sema/SemaInternal.h" +using namespace clang; +using namespace sema; + +bool Sema::CheckConstraintExpression(Expr *ConstraintExpression) { + // C++2a [temp.constr.atomic]p1 + // ..E shall be a constant expression of type bool. + + if (BinaryOperator* BinOp = dyn_cast(ConstraintExpression)) { + if (BinOp->getOpcode() == BO_LAnd || BinOp->getOpcode() == BO_LOr) { + return CheckConstraintExpression(BinOp->getLHS()) + && CheckConstraintExpression(BinOp->getRHS()); + } + } + // An atomic constraint! + if (!ConstraintExpression->isTypeDependent()) { + if (!Context.hasSameType(ConstraintExpression->getType() + .getNonReferenceType().getUnqualifiedType(), + Context.BoolTy)) { + Diag(ConstraintExpression->getExprLoc(), + diag::err_non_bool_atomic_constraint) + << ConstraintExpression << ConstraintExpression->getType(); + return false; + } else if (ImplicitCastExpr *E = + dyn_cast(ConstraintExpression)) { + // This will catch expressions such as '2 && true' + return CheckConstraintExpression(E->getSubExpr()); + } + } + return true; +} \ No newline at end of file Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1271,6 +1271,7 @@ case Expr::PredefinedExprClass: case Expr::SizeOfPackExprClass: case Expr::StringLiteralClass: + case Expr::ConceptSpecializationExprClass: // These expressions can never throw. return CT_Cannot; Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -7678,3 +7678,12 @@ return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo); } + + +ExprResult +Sema::CreateConceptSpecializationExpr(SourceLocation ConceptNameLoc, + ConceptDecl *CTD, + const TemplateArgumentListInfo *TALI) { + return new (Context) ConceptSpecializationExpr(Context, *this, ConceptNameLoc, + CTD, TALI); +} \ No newline at end of file Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -3893,10 +3893,17 @@ ConceptDecl *Template, SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs) { - // TODO: Do more stuff here, caller expects constraint - // expression to be returned. Works for non-dependent bool - // constraint expressions right now. - return Template->getConstraintExpr(); + assert(Template && "A concept template id without template?"); + + // Check that the template argument list is well-formed for this template. + SmallVector Converted; + if (CheckTemplateArgumentList(Template, TemplateLoc, + const_cast(*TemplateArgs), false, + Converted, /*UpdateArgsWithConversions=*/false)) + return ExprError(); + + return CreateConceptSpecializationExpr(NameInfo.getLoc(), Template, + TemplateArgs); } ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, @@ -7710,19 +7717,6 @@ if (!NewDecl) return nullptr; - if (!ConstraintExpr->isTypeDependent() && - ConstraintExpr->getType() != Context.BoolTy) { - // C++2a [temp.constr.atomic]p3: - // E shall be a constant expression of type bool. - // TODO: Do this check for individual atomic constraints - // and not the constraint expression. Probably should do it in - // ParseConstraintExpression. - Diag(ConstraintExpr->getSourceRange().getBegin(), - diag::err_concept_initialized_with_non_bool_type) - << ConstraintExpr->getType(); - NewDecl->setInvalidDecl(); - } - if (NewDecl->getAssociatedConstraints()) { // C++2a [temp.concept]p4: // A concept shall not have associated constraints. Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -2940,7 +2940,22 @@ RParenLoc, Length, PartialArgs); } - /// \brief Build a new Objective-C boxed expression. + /// \brief Build a new concept specialization expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildConceptSpecializationExpr(SourceLocation ConceptNameLoc, + ConceptDecl *CTD, + TemplateArgumentListInfo *TALI) { + ExprResult Result + = getSema().CreateConceptSpecializationExpr(ConceptNameLoc, CTD, + TALI); + if (Result.isInvalid()) + return ExprError(); + return Result; + } + + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. @@ -10616,6 +10631,23 @@ E->getLocEnd()); } +template +ExprResult +TreeTransform::TransformConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + const TemplateArgumentListInfo *Old = E->getTemplateArgumentListInfo(); + TemplateArgumentListInfo TransArgs(Old->getLAngleLoc(), Old->getRAngleLoc()); + if (getDerived().TransformTemplateArguments(Old->getArgumentArray(), + Old->size(), TransArgs)) { + return ExprError(); + } + + return getDerived().RebuildConceptSpecializationExpr(E->getConceptNameLoc(), + E->getNamedConcept(), + &TransArgs); +} + + template ExprResult TreeTransform::TransformArrayTypeTraitExpr(ArrayTypeTraitExpr *E) { Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -610,6 +610,22 @@ E->setRParenLoc(ReadSourceLocation()); } +void ASTStmtReader::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + E->setNamedConcept(ReadDeclAs()); + E->setConceptNameLoc(Record.readSourceLocation()); + + TemplateArgumentListInfo ArgInfo; + unsigned NumTemplateArgs = Record.readInt(); + ArgInfo.setLAngleLoc(Record.readSourceLocation()); + ArgInfo.setRAngleLoc(Record.readSourceLocation()); + for (unsigned i = 0; i != NumTemplateArgs; ++i) + ArgInfo.addArgument(Record.readTemplateArgumentLoc()); + E->setTemplateArguments(/*Sema=*/nullptr, &ArgInfo); + E->setSatisfied(Record.readInt() == 1); +} + void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { VisitExpr(E); E->setLHS(Record.readSubExpr()); @@ -4033,6 +4049,9 @@ S = new (Context) DependentCoawaitExpr(Empty); break; + case EXPR_CONCEPT_SPECIALIZATION: + S = new (Context) ConceptSpecializationExpr(Context, Empty); + break; } // We hit a STMT_STOP, so we're done with this expression. Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -344,6 +344,23 @@ Code = serialization::EXPR_DEPENDENT_COAWAIT; } +void ASTStmtWriter::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + Record.AddDeclRef(E->getNamedConcept()); + Record.AddSourceLocation(E->getConceptNameLoc()); + const TemplateArgumentListInfo *TALI = E->getTemplateArgumentListInfo(); + Record.push_back(TALI->size()); + Record.AddSourceLocation(TALI->getLAngleLoc()); + Record.AddSourceLocation(TALI->getRAngleLoc()); + for (unsigned i = 0; i < TALI->size(); ++i) { + Record.AddTemplateArgumentLoc(TALI->getArgumentArray()[i]); + } + Record.push_back(E->isSatisfied()); + Code = serialization::EXPR_CONCEPT_SPECIALIZATION; +} + + void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); // NumCaptures Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1018,6 +1018,7 @@ case Stmt::CUDAKernelCallExprClass: case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: + case Stmt::ConceptSpecializationExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp =================================================================== --- test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp @@ -1,5 +1,69 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s -// expected-no-diagnostics -template concept C = true; -static_assert(C); +template concept C1 = true; +static_assert(C1); + +template concept C2 = sizeof(T) == 4; +static_assert(C2); +static_assert(!C2); +static_assert(C2); +static_assert(!C2); + +template concept C3 = sizeof(*T{}) == 4; +static_assert(C3); +static_assert(!C3); + +struct A { + static constexpr int add(int a, int b) { + return a + b; + } +}; +struct B { + static int add(int a, int b) { + return a + b; + } +}; +template +concept C4 = U::add(1, 2) == 3; +static_assert(C4); +static_assert(!C4); // expected-error {{concept specialization 'C4' resulted in a non-constant expression 'B::add(1, 2) == 3'}} + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +template +concept Same = is_same_v; + +static_assert(Same); +static_assert(Same); +static_assert(!Same); +static_assert(!Same); +static_assert(Same); + +static_assert(Same)>); +static_assert(Same)>); +static_assert(Same)>); +static_assert(Same)>); + +template concept C5 = T{}; // expected-error {{atomic constraint 'int{}' must be of type 'bool' (found 'int')}} +constexpr bool x = C5; // expected-note {{in concept specialization 'C5'}} + +template +concept IsEven = (x % 2) == 0; + +static_assert(IsEven<20>); +static_assert(!IsEven<11>); + +template typename P> +concept IsTypePredicate = is_same_v::value), const bool> + && is_same_v::value), const bool> + && is_same_v::value), const bool>; + +template struct T1 {}; +template struct T2 { static constexpr bool value = sizeof(T) == 2; }; + +static_assert(IsTypePredicate); +static_assert(!IsTypePredicate); \ No newline at end of file Index: test/Parser/cxx-concept-declaration.cpp =================================================================== --- test/Parser/cxx-concept-declaration.cpp +++ test/Parser/cxx-concept-declaration.cpp @@ -9,8 +9,6 @@ template concept D1 = true; // expected-error {{expected template parameter}} -template concept C2 = 0.f; // expected-error {{constraint expression must be 'bool'}} - struct S1 { template concept C1 = true; // expected-error {{concept declarations may only appear in global or namespace scope}} }; @@ -29,3 +27,22 @@ // TODO: Add test to prevent explicit specialization, partial specialization // and explicit instantiation of concepts. + +template concept C7 = 2; // expected-error {{atomic constraint '2' must be of type 'bool' (found 'int')}} +template concept C8 = 2 && x; // expected-error {{atomic constraint '2' must be of type 'bool' (found 'int')}} +template concept C9 = x || 2 || x; // expected-error {{atomic constraint '2' must be of type 'bool' (found 'int')}} +template concept C10 = 8ull && x || x; // expected-error {{atomic constraint '8ULL' must be of type 'bool' (found 'unsigned long long')}} +template concept C11 = sizeof(T); // expected-error {{atomic constraint 'sizeof(T)' must be of type 'bool' (found 'unsigned long')}} +template concept C12 = T{}; +template concept C13 = (bool&&)true; +template concept C14 = (const bool&)true; +template concept C15 = (const bool)true; + +template +struct integral_constant { static constexpr T value = v; }; + +template concept C16 = integral_constant::value && true; +template concept C17 = integral_constant::value; + +bool a = C16; +bool b = C17; Index: tools/libclang/CXCursor.cpp =================================================================== --- tools/libclang/CXCursor.cpp +++ tools/libclang/CXCursor.cpp @@ -231,6 +231,7 @@ case Stmt::TypeTraitExprClass: case Stmt::CoroutineBodyStmtClass: case Stmt::CoawaitExprClass: + case Stmt::ConceptSpecializationExprClass: case Stmt::DependentCoawaitExprClass: case Stmt::CoreturnStmtClass: case Stmt::CoyieldExprClass: