Index: include/clang/AST/ASTNodeTraverser.h =================================================================== --- include/clang/AST/ASTNodeTraverser.h +++ include/clang/AST/ASTNodeTraverser.h @@ -529,6 +529,11 @@ D->defaultArgumentWasInherited() ? "inherited from" : "previous"); } + void VisitConceptDecl(const ConceptDecl *D) { + dumpTemplateParameters(D->getTemplateParameters()); + Visit(D->getConstraintExpr()); + } + void VisitUsingShadowDecl(const UsingShadowDecl *D) { if (auto *TD = dyn_cast<TypeDecl>(D->getUnderlyingDecl())) Visit(TD->getTypeForDecl()); Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -3090,6 +3090,42 @@ static bool classofKind(Kind K) { return K == VarTemplate; } }; +// \brief Declaration of a C++2a concept. +class ConceptDecl : public TemplateDecl, public Mergeable<ConceptDecl> { +protected: + Expr *ConstraintExpr; + + ConceptDecl(DeclContext *DC, + SourceLocation L, DeclarationName Name, + TemplateParameterList *Params, + Expr *ConstraintExpr) + : TemplateDecl(nullptr, Concept, DC, L, Name, Params), + ConstraintExpr(ConstraintExpr) {}; +public: + static ConceptDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation L, DeclarationName Name, + TemplateParameterList *Params, + Expr *ConstraintExpr); + static ConceptDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + Expr *getConstraintExpr() const { + return ConstraintExpr; + } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(getTemplateParameters()->getTemplateLoc(), + ConstraintExpr->getEndLoc()); + } + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == Concept; } + + friend class ASTReader; + friend class ASTDeclReader; + friend class ASTDeclWriter; +}; + inline NamedDecl *getAsNamedDecl(TemplateParameter P) { if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>()) return PD; Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -1800,6 +1800,11 @@ TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); }) +DEF_TRAVERSE_DECL(ConceptDecl, { + TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); + TRY_TO(TraverseStmt(D->getConstraintExpr())); +}) + DEF_TRAVERSE_DECL(UnresolvedUsingTypenameDecl, { // A dependent using declaration which was marked with 'typename'. // template<class T> class A : public B<T> { using typename B<T>::foo; }; Index: include/clang/AST/TextNodeDumper.h =================================================================== --- include/clang/AST/TextNodeDumper.h +++ include/clang/AST/TextNodeDumper.h @@ -347,6 +347,7 @@ void VisitObjCPropertyDecl(const ObjCPropertyDecl *D); void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D); void VisitBlockDecl(const BlockDecl *D); + void VisitConceptDecl(const ConceptDecl *D); }; } // namespace clang Index: include/clang/Basic/DeclNodes.td =================================================================== --- include/clang/Basic/DeclNodes.td +++ include/clang/Basic/DeclNodes.td @@ -70,6 +70,7 @@ def TypeAliasTemplate : DDecl<RedeclarableTemplate>; def TemplateTemplateParm : DDecl<Template>; def BuiltinTemplate : DDecl<Template>; + def Concept : DDecl<Template>; def Using : DDecl<Named>; def UsingPack : DDecl<Named>; def UsingShadow : DDecl<Named>; Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -684,7 +684,7 @@ def err_template_spec_syntax_non_template : Error< "identifier followed by '<' indicates a class template specialization but " "%0 %select{does not refer to a template|refers to a function template|" - "<unused>|refers to a variable template|<unused>}1">; + "<unused>|refers to a variable template|<unused>|refers to a concept}1">; def err_id_after_template_in_nested_name_spec : Error< "expected template name after 'template' keyword in nested name specifier">; def err_unexpected_template_in_unqualified_id : Error< @@ -1287,4 +1287,12 @@ "'co_await' modifier can only be applied to range-based for loop">; } +let CategoryName = "Concepts Issue" in { +def err_concept_definition_not_identifier : Error< + "name defined in concept definition must be an identifier">; +def ext_concept_legacy_bool_keyword : ExtWarn< + "ISO C++2a does not permit the 'bool' keyword after 'concept'">, + InGroup<DiagGroup<"concepts-ts-compat">>; +} + } // end of Parser diagnostics Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2010,8 +2010,8 @@ "%select{'auto'|'decltype(auto)'|'__auto_type'|" "use of " "%select{class template|function template|variable template|alias template|" - "template template parameter|template}2 %3 requires template arguments; " - "argument deduction}0 not allowed " + "template template parameter|concept|template}2 %3 requires template " + "arguments; argument deduction}0 not allowed " "%select{in function prototype" "|in non-static struct member|in struct member" "|in non-static union member|in union member" @@ -2100,8 +2100,8 @@ "deduced class template specialization type">; def err_deduced_non_class_template_specialization_type : Error< "%select{<error>|function template|variable template|alias template|" - "template template parameter|template}0 %1 requires template arguments; " - "argument deduction only allowed for class templates">; + "template template parameter|concept|template}0 %1 requires template " + "arguments; argument deduction only allowed for class templates">; def err_deduced_class_template_ctor_ambiguous : Error< "ambiguous deduction for template arguments of %0">; def err_deduced_class_template_ctor_no_viable : Error< @@ -2128,7 +2128,7 @@ def err_deduction_guide_name_not_class_template : Error< "cannot specify deduction guide for " "%select{<error>|function template|variable template|alias template|" - "template template parameter|dependent template name}0 %1">; + "template template parameter|concept|dependent template name}0 %1">; def err_deduction_guide_wrong_scope : Error< "deduction guide must be declared in the same scope as template %q0">; def err_deduction_guide_defines_function : Error< @@ -2456,32 +2456,20 @@ def note_private_extern : Note< "use __attribute__((visibility(\"hidden\"))) attribute instead">; -// C++ Concepts TS -def err_concept_wrong_decl_kind : Error< - "'concept' can only appear on the definition of a function template or variable template">; -def err_concept_decls_may_only_appear_in_namespace_scope : Error< - "concept declarations may only appear in namespace scope">; -def err_function_concept_not_defined : Error< - "function concept declaration must be a definition">; -def err_var_concept_not_initialized : Error< - "variable concept declaration must be initialized">; -def err_function_concept_exception_spec : Error< - "function concept cannot have exception specification">; -def err_concept_decl_invalid_specifiers : Error< - "%select{variable|function}0 concept cannot be declared " - "'%select{thread_local|inline|friend|constexpr}1'">; -def err_function_concept_with_params : Error< - "function concept cannot have any parameters">; -def err_function_concept_bool_ret : Error< - "declared return type of function concept must be 'bool'">; -def err_variable_concept_bool_decl : Error< - "declared type of variable concept must be 'bool'">; -def err_concept_specified_specialization : Error< - "'concept' cannot be applied on an " - "%select{explicit instantiation|explicit specialization|partial specialization}0">; -def err_concept_specialized : Error< - "%select{function|variable}0 concept cannot be " - "%select{explicitly instantiated|explicitly specialized|partially specialized}1">; +// C++ Concepts +def err_concept_initialized_with_non_bool_type : Error< + "constraint expression must be of type 'bool' but is of type %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_no_parameters : Error< + "concept template parameter list must have at least one parameter; explicit " + "specialization of concepts is not allowed">; +def err_concept_extra_headers : Error< + "extraneous template parameter list in concept definition">; +def err_concept_no_associated_constraints : Error< + "concept cannot have associated constraints">; +def err_concept_not_implemented : Error< + "sorry, unimplemented concepts feature %0 used">; def err_template_different_associated_constraints : Error< "associated constraints differ in template redeclaration">; @@ -4012,11 +4000,12 @@ def err_template_missing_args : Error< "use of " "%select{class template|function template|variable template|alias template|" - "template template parameter|template}0 %1 requires template arguments">; + "template template parameter|concept|template}0 %1 requires template " + "arguments">; def err_template_arg_list_different_arity : Error< "%select{too few|too many}0 template arguments for " "%select{class template|function template|variable template|alias template|" - "template template parameter|template}1 %2">; + "template template parameter|concept|template}1 %2">; def note_template_decl_here : Note<"template is declared here">; def err_template_arg_must_be_type : Error< "template argument for template type parameter must be a type">; Index: include/clang/Basic/TemplateKinds.h =================================================================== --- include/clang/Basic/TemplateKinds.h +++ include/clang/Basic/TemplateKinds.h @@ -48,9 +48,9 @@ /// anyway. In C++20, this is mandatory in order to parse ADL-only function /// template specialization calls. TNK_Undeclared_template, + /// The name refers to a concept. + TNK_Concept_template, }; } #endif - - Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -3024,6 +3024,10 @@ SourceLocation &DeclEnd, ParsedAttributes &AccessAttrs, AccessSpecifier AS = AS_none); + // C++2a: Template, concept definition [temp] + Decl * + ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, + SourceLocation &DeclEnd); //===--------------------------------------------------------------------===// // Modules Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1973,6 +1973,7 @@ VarTemplate, AliasTemplate, TemplateTemplateParam, + Concept, DependentTemplate }; TemplateNameKindForDiagnostics @@ -6551,6 +6552,13 @@ SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs); + ExprResult + CheckConceptTemplateId(const CXXScopeSpec &SS, + const DeclarationNameInfo &NameInfo, + ConceptDecl *Template, + SourceLocation TemplateLoc, + const TemplateArgumentListInfo *TemplateArgs); + void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc); ExprResult BuildTemplateIdExpr(const CXXScopeSpec &SS, @@ -6818,6 +6826,11 @@ const TemplateArgument *Args, unsigned NumArgs); + // Concepts + Decl *ActOnConceptDefinition( + Scope *S, MultiTemplateParamsArg TemplateParameterLists, + IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr); + //===--------------------------------------------------------------------===// // C++ Variadic Templates (C++0x [temp.variadic]) //===--------------------------------------------------------------------===// Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -1489,7 +1489,10 @@ /// A TypeAliasTemplateDecl record. DECL_TYPE_ALIAS_TEMPLATE, - /// A StaticAssertDecl record. + /// \brief A ConceptDecl record. + DECL_CONCEPT, + + /// \brief A StaticAssertDecl record. DECL_STATIC_ASSERT, /// A record containing CXXBaseSpecifiers. Index: lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- lib/AST/ASTStructuralEquivalence.cpp +++ lib/AST/ASTStructuralEquivalence.cpp @@ -1512,6 +1512,18 @@ D2->getTemplatedDecl()->getType()); } +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + ConceptDecl *D1, + ConceptDecl *D2) { + // Check template parameters. + if (!IsTemplateDeclCommonStructurallyEquivalent(Context, D1, D2)) + return false; + + // Check the constraint expression. + return IsStructurallyEquivalent(Context, D1->getConstraintExpr(), + D2->getConstraintExpr()); +} + static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, FriendDecl *D1, FriendDecl *D2) { if ((D1->getFriendType() && D2->getFriendDecl()) || @@ -1771,6 +1783,14 @@ // Class template/non-class-template mismatch. return false; } + } else if (auto *ConceptDecl1 = dyn_cast<ConceptDecl>(D1)) { + if (auto *ConceptDecl2 = dyn_cast<ConceptDecl>(D2)) { + if (!::IsStructurallyEquivalent(*this, ConceptDecl1, ConceptDecl2)) + return false; + } else { + // Concept/non-concept mismatch. + return false; + } } else if (auto *TTP1 = dyn_cast<TemplateTypeParmDecl>(D1)) { if (auto *TTP2 = dyn_cast<TemplateTypeParmDecl>(D2)) { if (!::IsStructurallyEquivalent(*this, TTP1, TTP2)) Index: lib/AST/DeclBase.cpp =================================================================== --- lib/AST/DeclBase.cpp +++ lib/AST/DeclBase.cpp @@ -710,6 +710,7 @@ case Binding: case NonTypeTemplateParm: case VarTemplate: + case Concept: // These (C++-only) declarations are found by redeclaration lookup for // tag types, so we include them in the tag namespace. return IDNS_Ordinary | IDNS_Tag; Index: lib/AST/DeclPrinter.cpp =================================================================== --- lib/AST/DeclPrinter.cpp +++ lib/AST/DeclPrinter.cpp @@ -1123,8 +1123,13 @@ if (TTP->isParameterPack()) Out << "..."; Out << D->getName(); - } else { - Visit(D->getTemplatedDecl()); + } else if (auto *TD = D->getTemplatedDecl()) + Visit(TD); + else if (const auto *Concept = dyn_cast<ConceptDecl>(D)) { + Out << "concept " << Concept->getName() << " = " ; + Concept->getConstraintExpr()->printPretty(Out, nullptr, Policy, + Indentation); + Out << ";"; } } Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -821,6 +821,26 @@ } } +//===----------------------------------------------------------------------===// +// ConceptDecl Implementation +//===----------------------------------------------------------------------===// +ConceptDecl *ConceptDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation L, DeclarationName Name, + TemplateParameterList *Params, + Expr *ConstraintExpr) { + AdoptTemplateParameterList(Params, DC); + return new (C, DC) ConceptDecl(DC, L, Name, Params, ConstraintExpr); +} + +ConceptDecl *ConceptDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + ConceptDecl *Result = new (C, ID) ConceptDecl(nullptr, SourceLocation(), + DeclarationName(), + nullptr, nullptr); + + return Result; +} + //===----------------------------------------------------------------------===// // ClassTemplatePartialSpecializationDecl Implementation //===----------------------------------------------------------------------===// Index: lib/AST/TextNodeDumper.cpp =================================================================== --- lib/AST/TextNodeDumper.cpp +++ lib/AST/TextNodeDumper.cpp @@ -1936,3 +1936,7 @@ if (D->capturesCXXThis()) OS << " captures_this"; } + +void TextNodeDumper::VisitConceptDecl(const ConceptDecl *D) { + dumpName(D); +} \ No newline at end of file Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -108,6 +108,7 @@ case Decl::OMPCapturedExpr: case Decl::OMPRequires: case Decl::Empty: + case Decl::Concept: // None of these decls require codegen support. return; Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -5162,6 +5162,7 @@ case Decl::UsingShadow: case Decl::ClassTemplate: case Decl::VarTemplate: + case Decl::Concept: case Decl::VarTemplatePartialSpecialization: case Decl::FunctionTemplate: case Decl::TypeAliasTemplate: Index: lib/Index/IndexDecl.cpp =================================================================== --- lib/Index/IndexDecl.cpp +++ lib/Index/IndexDecl.cpp @@ -652,10 +652,10 @@ } static bool shouldIndexTemplateParameterDefaultValue(const NamedDecl *D) { - if (!D) - return false; // We want to index the template parameters only once when indexing the // canonical declaration. + if (!D) + return false; if (const auto *FD = dyn_cast<FunctionDecl>(D)) return FD->getCanonicalDecl() == FD; else if (const auto *TD = dyn_cast<TagDecl>(D)) Index: lib/Parse/ParseTemplate.cpp =================================================================== --- lib/Parse/ParseTemplate.cpp +++ lib/Parse/ParseTemplate.cpp @@ -49,6 +49,15 @@ /// template-declaration: [C++ temp] /// 'export'[opt] 'template' '<' template-parameter-list '>' declaration /// +/// template-declaration: [C++2a] +/// template-head declaration +/// template-head concept-definition +/// +/// TODO: requires-clause +/// template-head: [C++2a] +/// 'template' '<' template-parameter-list '>' +/// requires-clause[opt] +/// /// explicit-specialization: [ C++ temp.expl.spec] /// 'template' '<' '>' declaration Decl *Parser::ParseTemplateDeclarationOrSpecialization( @@ -142,6 +151,12 @@ ParseScopeFlags TemplateScopeFlags(this, NewFlags, isSpecialization); // Parse the actual template declaration. + if (Tok.is(tok::kw_concept)) + return ParseConceptDefinition( + ParsedTemplateInfo(&ParamLists, isSpecialization, + LastParamListWasEmpty), + DeclEnd); + return ParseSingleDeclarationAfterTemplate( Context, ParsedTemplateInfo(&ParamLists, isSpecialization, LastParamListWasEmpty), @@ -315,6 +330,85 @@ return ThisDecl; } +/// \brief Parse a single declaration that declares a concept. +/// +/// \param DeclEnd will receive the source location of the last token +/// within this declaration. +/// +/// \returns the new declaration. +Decl * +Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, + SourceLocation &DeclEnd) { + assert(TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate && + "Template information required"); + assert(Tok.is(tok::kw_concept) && + "ParseConceptDefinition must be called when at a 'concept' keyword"); + + ConsumeToken(); // Consume 'concept' + + SourceLocation BoolKWLoc; + if (TryConsumeToken(tok::kw_bool, BoolKWLoc)) + Diag(Tok.getLocation(), diag::ext_concept_legacy_bool_keyword) << + FixItHint::CreateRemoval(SourceLocation(BoolKWLoc)); + + DiagnoseAndSkipCXX11Attributes(); + + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) || + SS.isInvalid()) { + SkipUntil(tok::semi); + return nullptr; + } + + if (SS.isNotEmpty()) + Diag(SS.getBeginLoc(), + diag::err_concept_definition_not_identifier); + + UnqualifiedId Result; + if (ParseUnqualifiedId(SS, /*EnteringContext=*/false, + /*AllowDestructorName=*/false, + /*AllowConstructorName=*/false, + /*AllowDeductionGuide=*/false, + /*ObjectType=*/ParsedType(), /*TemplateKWLoc=*/nullptr, + Result)) { + SkipUntil(tok::semi); + return nullptr; + } + + if (Result.getKind() != UnqualifiedIdKind::IK_Identifier) { + Diag(Result.getBeginLoc(), diag::err_concept_definition_not_identifier); + SkipUntil(tok::semi); + return nullptr; + } + + IdentifierInfo *Id = Result.Identifier; + SourceLocation IdLoc = Result.getBeginLoc(); + + DiagnoseAndSkipCXX11Attributes(); + + if (!TryConsumeToken(tok::equal)) { + Diag(Tok.getLocation(), diag::err_expected) << tok::equal; + SkipUntil(tok::semi); + return nullptr; + } + + ExprResult ConstraintExprResult = + Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + if (ConstraintExprResult.isInvalid()) { + SkipUntil(tok::semi); + return nullptr; + } + + DeclEnd = Tok.getLocation(); + ExpectAndConsumeSemi(diag::err_expected_semi_declaration); + Expr *ConstraintExpr = ConstraintExprResult.get(); + return Actions.ActOnConceptDefinition(getCurScope(), + *TemplateInfo.TemplateParams, + Id, IdLoc, ConstraintExpr); +} + /// ParseTemplateParameters - Parses a template-parameter-list enclosed in /// angle brackets. Depth is the depth of this template-parameter-list, which /// is the number of template headers directly enclosing this template header. Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1189,6 +1189,8 @@ return TemplateNameKindForDiagnostics::AliasTemplate; if (isa<TemplateTemplateParmDecl>(TD)) return TemplateNameKindForDiagnostics::TemplateTemplateParam; + if (isa<ConceptDecl>(TD)) + return TemplateNameKindForDiagnostics::Concept; return TemplateNameKindForDiagnostics::DependentTemplate; } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -9802,7 +9802,7 @@ NonTemplateTarget = TargetTD->getTemplatedDecl(); UsingShadowDecl *Shadow; - if (isa<CXXConstructorDecl>(NonTemplateTarget)) { + if (NonTemplateTarget && isa<CXXConstructorDecl>(NonTemplateTarget)) { bool IsVirtualBase = isVirtualDirectBase(cast<CXXRecordDecl>(CurContext), UD->getQualifier()->getAsRecordDecl()); Index: lib/Sema/SemaLookup.cpp =================================================================== --- lib/Sema/SemaLookup.cpp +++ lib/Sema/SemaLookup.cpp @@ -5168,7 +5168,8 @@ if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) return PD->getDefinition(); if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) - return getDefinitionToImport(TD->getTemplatedDecl()); + if (NamedDecl *TTD = TD->getTemplatedDecl()) + return getDefinitionToImport(TTD); return nullptr; } Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -271,9 +271,11 @@ } else { assert(isa<ClassTemplateDecl>(TD) || isa<TemplateTemplateParmDecl>(TD) || isa<TypeAliasTemplateDecl>(TD) || isa<VarTemplateDecl>(TD) || - isa<BuiltinTemplateDecl>(TD)); + isa<BuiltinTemplateDecl>(TD) || isa<ConceptDecl>(TD)); TemplateKind = - isa<VarTemplateDecl>(TD) ? TNK_Var_template : TNK_Type_template; + isa<VarTemplateDecl>(TD) ? TNK_Var_template : + isa<ConceptDecl>(TD) ? TNK_Concept_template : + TNK_Type_template; } } @@ -3227,7 +3229,8 @@ TemplateDecl *Template = Name.getAsTemplateDecl(); if (!Template || isa<FunctionTemplateDecl>(Template) || - isa<VarTemplateDecl>(Template)) { + isa<VarTemplateDecl>(Template) || + isa<ConceptDecl>(Template)) { // We might have a substituted template template parameter pack. If so, // build a template specialization type for it. if (Name.getAsSubstTemplateTemplateParmPack()) @@ -4234,6 +4237,18 @@ } } +ExprResult +Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, + const DeclarationNameInfo &NameInfo, + ConceptDecl *Template, + SourceLocation TemplateLoc, + const TemplateArgumentListInfo *TemplateArgs) { + // TODO: Do concept specialization here. + Diag(NameInfo.getBeginLoc(), diag::err_concept_not_implemented) << + "concept specialization"; + return ExprError(); +} + ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R, @@ -4274,6 +4289,12 @@ TemplateKWLoc, TemplateArgs); } + if (R.getAsSingle<ConceptDecl>() && !AnyDependentArguments()) { + return CheckConceptTemplateId(SS, R.getLookupNameInfo(), + R.getAsSingle<ConceptDecl>(), + TemplateKWLoc, TemplateArgs); + } + // We don't want lookup warnings at this point. R.suppressDiagnostics(); @@ -7974,7 +7995,74 @@ return NewDecl; } -/// Strips various properties off an implicit instantiation +Decl *Sema::ActOnConceptDefinition(Scope *S, + MultiTemplateParamsArg TemplateParameterLists, + IdentifierInfo *Name, SourceLocation NameLoc, + Expr *ConstraintExpr) { + DeclContext *DC = CurContext; + + if (!DC->getRedeclContext()->isFileContext()) { + Diag(NameLoc, + diag::err_concept_decls_may_only_appear_in_global_namespace_scope); + return nullptr; + } + + if (TemplateParameterLists.size() > 1) { + Diag(NameLoc, diag::err_concept_extra_headers); + return nullptr; + } + + if (TemplateParameterLists.front()->size() == 0) { + Diag(NameLoc, diag::err_concept_no_parameters); + return nullptr; + } + + ConceptDecl *NewDecl = ConceptDecl::Create(Context, DC, NameLoc, Name, + TemplateParameterLists.front(), + ConstraintExpr); + + 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. + // TODO: Make a test once we have actual associated constraints. + Diag(NameLoc, diag::err_concept_no_associated_constraints); + NewDecl->setInvalidDecl(); + } + + // Check for conflicting previous declaration. + DeclarationNameInfo NameInfo(NewDecl->getDeclName(), NameLoc); + LookupResult Previous(*this, NameInfo, LookupOrdinaryName, + ForVisibleRedeclaration); + LookupName(Previous, S); + + FilterLookupForScope(Previous, DC, S, /*ConsiderLinkage=*/false, + /*AllowInlineNamespace*/false); + if (!Previous.empty()) { + auto *Old = Previous.getRepresentativeDecl(); + Diag(NameLoc, isa<ConceptDecl>(Old) ? diag::err_redefinition : + diag::err_redefinition_different_kind) << NewDecl->getDeclName(); + Diag(Old->getLocation(), diag::note_previous_definition); + } + + ActOnDocumentableDecl(NewDecl); + PushOnScopeChains(NewDecl, S); + return NewDecl; +} + +/// \brief Strips various properties off an implicit instantiation /// that has just been explicitly specialized. static void StripImplicitInstantiation(NamedDecl *D) { D->dropAttr<DLLImportAttr>(); Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3401,6 +3401,10 @@ return nullptr; } +Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) { + llvm_unreachable("Concept definitions cannot reside inside a template"); +} + Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) { llvm_unreachable("Unexpected decl"); } Index: lib/Serialization/ASTCommon.cpp =================================================================== --- lib/Serialization/ASTCommon.cpp +++ lib/Serialization/ASTCommon.cpp @@ -395,6 +395,7 @@ case Decl::BuiltinTemplate: case Decl::Decomposition: case Decl::Binding: + case Decl::Concept: return false; // These indirectly derive from Redeclarable<T> but are not actually Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -382,6 +382,7 @@ void VisitBindingDecl(BindingDecl *BD); void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); DeclID VisitTemplateDecl(TemplateDecl *D); + void VisitConceptDecl(ConceptDecl *D); RedeclarableResult VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D); @@ -2089,6 +2090,12 @@ return PatternID; } +void ASTDeclReader::VisitConceptDecl(ConceptDecl *D) { + VisitTemplateDecl(D); + D->ConstraintExpr = Record.readExpr(); + mergeMergeable(D); +} + ASTDeclReader::RedeclarableResult ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { RedeclarableResult Redecl = VisitRedeclarable(D); @@ -3829,6 +3836,9 @@ case DECL_TYPE_ALIAS_TEMPLATE: D = TypeAliasTemplateDecl::CreateDeserialized(Context, ID); break; + case DECL_CONCEPT: + D = ConceptDecl::CreateDeserialized(Context, ID); + break; case DECL_STATIC_ASSERT: D = StaticAssertDecl::CreateDeserialized(Context, ID); break; Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -1293,6 +1293,7 @@ RECORD(DECL_TEMPLATE_TYPE_PARM); RECORD(DECL_NON_TYPE_TEMPLATE_PARM); RECORD(DECL_TEMPLATE_TEMPLATE_PARM); + RECORD(DECL_CONCEPT); RECORD(DECL_TYPE_ALIAS_TEMPLATE); RECORD(DECL_STATIC_ASSERT); RECORD(DECL_CXX_BASE_SPECIFIERS); Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -102,6 +102,7 @@ void VisitBindingDecl(BindingDecl *D); void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); void VisitTemplateDecl(TemplateDecl *D); + void VisitConceptDecl(ConceptDecl *D); void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitVarTemplateDecl(VarTemplateDecl *D); @@ -1432,6 +1433,12 @@ Record.AddTemplateParameterList(D->getTemplateParameters()); } +void ASTDeclWriter::VisitConceptDecl(ConceptDecl *D) { + VisitTemplateDecl(D); + Record.AddStmt(D->getConstraintExpr()); + Code = serialization::DECL_CONCEPT; +} + void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { VisitRedeclarable(D); Index: test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template<typename T> concept C = true; +static_assert(C<int>); // expected-error{{sorry, unimplemented concepts feature concept specialization used}} Index: test/Parser/cxx-concept-declaration.cpp =================================================================== --- test/Parser/cxx-concept-declaration.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -// Support parsing of concepts -// Disabled for now. -// expected-no-diagnostics - -// RUN: %clang_cc1 -std=c++14 -fconcepts-ts -x c++ -verify %s -// template<typename T> concept C1 = true; Index: test/Parser/cxx2a-concept-declaration.cpp =================================================================== --- /dev/null +++ test/Parser/cxx2a-concept-declaration.cpp @@ -0,0 +1,73 @@ +// Support parsing of concepts + +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s +template<typename T> concept C1 = true; // expected-note 2{{previous}} + +template<typename T> concept C1 = true; // expected-error{{redefinition}} + +template<concept T> concept D1 = true; +// expected-error@-1{{expected template parameter}} +// expected-error@-2{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}} + +template<template<typename> concept T> concept D2 = true; +// expected-error@-1{{expected identifier}} +// expected-error@-2{{template template parameter requires 'class' after the parameter list}} +// expected-error@-3{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}} + +template<typename T> concept C2 = 0.f; // expected-error {{constraint expression must be of type 'bool' but is of type 'float'}} + +struct S1 { + template<typename T> concept C1 = true; // expected-error {{concept declarations may only appear in global or namespace scope}} +}; + +extern "C++" { + template<typename T> concept C1 = true; // expected-error{{redefinition}} +} + +template<typename A> +template<typename B> +concept C4 = true; // expected-error {{extraneous template parameter list in concept definition}} + +template<typename T> concept C5 = true; // expected-note {{previous}} expected-note {{previous}} +int C5; // expected-error {{redefinition}} +struct C5 {}; // expected-error {{redefinition}} + +struct C6 {}; // expected-note{{previous definition is here}} +template<typename T> concept C6 = true; +// expected-error@-1{{redefinition of 'C6' as different kind of symbol}} + +// TODO: Add test to prevent explicit specialization, partial specialization +// and explicit instantiation of concepts. + +template<typename T, T v> +struct integral_constant { static constexpr T value = v; }; + +namespace N { + template<typename T> concept C7 = true; +} +using N::C7; + +template <bool word> concept C8 = integral_constant<bool, wor>::value; +// expected-error@-1{{use of undeclared identifier 'wor'; did you mean 'word'?}} +// expected-note@-2{{'word' declared here}} + +template<typename T> concept bool C9 = true; +// expected-warning@-1{{ISO C++2a does not permit the 'bool' keyword after 'concept'}} + +template<> concept C10 = false; +// expected-error@-1{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}} + +template<> concept C9<int> = false; +// expected-error@-1{{name defined in concept definition must be an identifier}} + +template<typename T> concept N::C11 = false; +// expected-error@-1{{name defined in concept definition must be an identifier}} + +class A { }; +// expected-note@-1{{'A' declared here}} + +template<typename T> concept A::C12 = false; +// expected-error@-1{{expected namespace name}} + +template<typename T> concept operator int = false; +// expected-error@-1{{name defined in concept definition must be an identifier}} Index: tools/libclang/CIndex.cpp =================================================================== --- tools/libclang/CIndex.cpp +++ tools/libclang/CIndex.cpp @@ -6269,6 +6269,7 @@ case Decl::PragmaComment: case Decl::PragmaDetectMismatch: case Decl::UsingPack: + case Decl::Concept: return C; // Declaration kinds that don't make any sense here, but are