diff options
32 files changed, 450 insertions, 47 deletions
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 1d4025f63bb..e43eacef86c 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -529,6 +529,11 @@ public: 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()); diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 5933810ec84..235b31c1c31 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3090,6 +3090,42 @@ public: 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; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 292b57af76f..698fba2f4ed 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1800,6 +1800,11 @@ DEF_TRAVERSE_DECL(TypeAliasTemplateDecl, { 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; }; diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index a26b06058b2..4c2d0710963 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -347,6 +347,7 @@ public: void VisitObjCPropertyDecl(const ObjCPropertyDecl *D); void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D); void VisitBlockDecl(const BlockDecl *D); + void VisitConceptDecl(const ConceptDecl *D); }; } // namespace clang diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 45eba454152..2d3fa6b6147 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -70,6 +70,7 @@ def Named : Decl<"named declarations", 1>; 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>; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 2cafbfc9307..608337ed316 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -684,7 +684,7 @@ def warn_cxx14_compat_template_template_param_typename : Warning< 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 @@ def err_for_co_await_not_range_for : Error< "'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 diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d59155f9931..380db32ba4b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2010,8 +2010,8 @@ def err_auto_not_allowed : Error< "%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 @@ def err_deduced_class_template_compound_type : Error< "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_invalid_specifier : Error< 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 warn_private_extern : Warning< 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">; @@ -4019,11 +4007,12 @@ def ext_adl_only_template_id : ExtWarn< 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">; diff --git a/clang/include/clang/Basic/TemplateKinds.h b/clang/include/clang/Basic/TemplateKinds.h index ee7e2f52e43..62e484ce42e 100644 --- a/clang/include/clang/Basic/TemplateKinds.h +++ b/clang/include/clang/Basic/TemplateKinds.h @@ -48,9 +48,9 @@ enum TemplateNameKind { /// 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 - - diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 32bbf4dcd5c..7c67c35f615 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3024,6 +3024,10 @@ private: SourceLocation &DeclEnd, ParsedAttributes &AccessAttrs, AccessSpecifier AS = AS_none); + // C++2a: Template, concept definition [temp] + Decl * + ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, + SourceLocation &DeclEnd); //===--------------------------------------------------------------------===// // Modules diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4cb04746bf1..2e60ac9dfb0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1973,6 +1973,7 @@ public: VarTemplate, AliasTemplate, TemplateTemplateParam, + Concept, DependentTemplate }; TemplateNameKindForDiagnostics @@ -6551,6 +6552,13 @@ public: 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 @@ public: 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]) //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index ee545c7186a..0e1b9e0af9e 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1489,7 +1489,10 @@ namespace serialization { /// 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. diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 744b30f8533..bb2e353eeef 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -1513,6 +1513,18 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, } 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()) || (D1->getFriendDecl() && D2->getFriendType())) { @@ -1771,6 +1783,14 @@ bool StructuralEquivalenceContext::CheckKindSpecificEquivalence( // 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)) diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index f5853b49804..fd80e1532eb 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -710,6 +710,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { 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; diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 73dcdc6854f..f5c69944034 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1123,8 +1123,13 @@ void DeclPrinter::VisitTemplateDecl(const TemplateDecl *D) { 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 << ";"; } } diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 3c4cf72f7f5..40c39c845db 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -822,6 +822,26 @@ ClassTemplateSpecializationDecl::getSourceRange() const { } //===----------------------------------------------------------------------===// +// 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 //===----------------------------------------------------------------------===// void ClassTemplatePartialSpecializationDecl::anchor() {} diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 01bcc22753e..44c2612a721 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1936,3 +1936,7 @@ void TextNodeDumper::VisitBlockDecl(const BlockDecl *D) { if (D->capturesCXXThis()) OS << " captures_this"; } + +void TextNodeDumper::VisitConceptDecl(const ConceptDecl *D) { + dumpName(D); +}
\ No newline at end of file diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 758737e86c2..e8c6a479a9e 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -108,6 +108,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::OMPCapturedExpr: case Decl::OMPRequires: case Decl::Empty: + case Decl::Concept: // None of these decls require codegen support. return; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b0e3b0bb98d..ea52f0c61fc 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -5162,6 +5162,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::UsingShadow: case Decl::ClassTemplate: case Decl::VarTemplate: + case Decl::Concept: case Decl::VarTemplatePartialSpecialization: case Decl::FunctionTemplate: case Decl::TypeAliasTemplate: diff --git a/clang/lib/Index/IndexDecl.cpp b/clang/lib/Index/IndexDecl.cpp index 7e6be5d7f68..e41b5fecdd2 100644 --- a/clang/lib/Index/IndexDecl.cpp +++ b/clang/lib/Index/IndexDecl.cpp @@ -652,10 +652,10 @@ public: } 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)) diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 78e0c04f53e..6ae75eda243 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -49,6 +49,15 @@ Decl *Parser::ParseDeclarationStartingWithTemplate( /// 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 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization( 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 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate( 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. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 03b6cc91657..1c0c5135a2d 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1189,6 +1189,8 @@ Sema::getTemplateNameKindForDiagnostics(TemplateName Name) { return TemplateNameKindForDiagnostics::AliasTemplate; if (isa<TemplateTemplateParmDecl>(TD)) return TemplateNameKindForDiagnostics::TemplateTemplateParam; + if (isa<ConceptDecl>(TD)) + return TemplateNameKindForDiagnostics::Concept; return TemplateNameKindForDiagnostics::DependentTemplate; } diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 4f24f88be18..b23352bffbf 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -9802,7 +9802,7 @@ UsingShadowDecl *Sema::BuildUsingShadowDecl(Scope *S, NonTemplateTarget = TargetTD->getTemplatedDecl(); UsingShadowDecl *Shadow; - if (isa<CXXConstructorDecl>(NonTemplateTarget)) { + if (NonTemplateTarget && isa<CXXConstructorDecl>(NonTemplateTarget)) { bool IsVirtualBase = isVirtualDirectBase(cast<CXXRecordDecl>(CurContext), UD->getQualifier()->getAsRecordDecl()); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index c50592c4ff8..c0b946a98d1 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -5169,7 +5169,8 @@ static NamedDecl *getDefinitionToImport(NamedDecl *D) { 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; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 49ba771379e..fba8cd4eee0 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -271,9 +271,11 @@ TemplateNameKind Sema::isTemplateName(Scope *S, } 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 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, 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 @@ void Sema::diagnoseMissingTemplateArguments(TemplateName Name, } } +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 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, 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 @@ Decl *Sema::ActOnTemplateDeclarator(Scope *S, 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>(); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index cbfb0fed232..67343d11d33 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3401,6 +3401,10 @@ Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) { 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"); } diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index de95825f3cb..aa3477a7d35 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -395,6 +395,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::BuiltinTemplate: case Decl::Decomposition: case Decl::Binding: + case Decl::Concept: 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 a2b80999e6b..b40e3cf8926 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -382,6 +382,7 @@ namespace clang { 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 @@ DeclID ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) { 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 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { 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; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 218983c732c..a22e97aaa5c 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1293,6 +1293,7 @@ void ASTWriter::WriteBlockInfoBlock() { 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); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index f52fc014bb3..3d9dd7131b1 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -102,6 +102,7 @@ namespace clang { 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 @@ void ASTDeclWriter::VisitTemplateDecl(TemplateDecl *D) { 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); diff --git a/clang/test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp b/clang/test/CXX/concepts-ts/expr/expr.prim/expr.prim.id/p3.cpp new file mode 100644 index 00000000000..16da146a966 --- /dev/null +++ b/clang/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}} diff --git a/clang/test/Parser/cxx2a-concept-declaration.cpp b/clang/test/Parser/cxx2a-concept-declaration.cpp new file mode 100644 index 00000000000..d80b3db428c --- /dev/null +++ b/clang/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}} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 17c40615335..1dc961f58a2 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6271,6 +6271,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { 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 |