diff options
author | Faisal Vali <faisalv@yahoo.com> | 2018-04-25 02:42:26 +0000 |
---|---|---|
committer | Faisal Vali <faisalv@yahoo.com> | 2018-04-25 02:42:26 +0000 |
commit | 45d663da5615045d3c2da1b97ef06319055deb42 (patch) | |
tree | 985c65f7cadf4c48303264e03a14e0bbb2ec286e | |
parent | e4c4fc5523dfcbad4efc78103cbb51ba34613f53 (diff) | |
download | clang-45d663da5615045d3c2da1b97ef06319055deb42.tar.gz |
[c++2a] [concepts] Add rudimentary parsing support for template concept declarations
This patch is a tweak of changyu's patch: https://reviews.llvm.org/D40381. It differs in that the recognition of the 'concept' token is moved into the machinery that recognizes declaration-specifiers - this allows us to leverage the attribute handling machinery more seamlessly.
See the test file to get a sense of the basic parsing that this patch supports.
There is much more work to be done before concepts are usable...
Thanks Changyu!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@330794 91177308-0d34-0410-b5e6-96231b3b80d8
33 files changed, 635 insertions, 72 deletions
diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 9c68352e76..1fe95f4a59 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -3015,6 +3015,46 @@ public: static bool classofKind(Kind K) { return K == VarTemplate; } }; +/// \brief Represents a C++2a ([temp] p1) concept-definition. +class ConceptDecl : public TemplateDecl { +protected: + Expr *ConstraintExpr; + + ConceptDecl(DeclContext *DC, + SourceLocation NameLoc, DeclarationName Name, + TemplateParameterList *Params, + Expr *ConstraintExpr) + : TemplateDecl(nullptr, Concept, DC, NameLoc, Name, Params), + ConstraintExpr(ConstraintExpr) {}; +public: + static ConceptDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation NameLoc, DeclarationName Name, + TemplateParameterList *Params, + Expr *ConstraintExpr); + static ConceptDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + Expr *getConstraintExpr() const { + return ConstraintExpr; + } + + void setConstraintExpr(Expr *CE) { + ConstraintExpr = CE; + } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(getTemplateParameters()->getTemplateLoc(), + getConstraintExpr()->getLocEnd()); + } + + // 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/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 57641793f8..d9cc89b126 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1722,6 +1722,13 @@ DEF_TRAVERSE_TMPL_DECL(Class) DEF_TRAVERSE_TMPL_DECL(Var) DEF_TRAVERSE_TMPL_DECL(Function) +DEF_TRAVERSE_DECL(ConceptDecl, { + TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); + TRY_TO(TraverseStmt(D->getConstraintExpr())); + // FIXME: Traverse all the concept specializations (once we implement forming + // template-ids with them). +}) + DEF_TRAVERSE_DECL(TemplateTemplateParmDecl, { // D is the "T" in something like // template <template <typename> class T> class container { }; diff --git a/include/clang/Basic/DeclNodes.td b/include/clang/Basic/DeclNodes.td index 67ca9e5c6c..0ab6e32e4d 100644 --- a/include/clang/Basic/DeclNodes.td +++ b/include/clang/Basic/DeclNodes.td @@ -69,6 +69,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/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 7c7c0fb24d..4b6c9fcb1a 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -1150,6 +1150,24 @@ def err_pragma_cannot_end_force_cuda_host_device : Error< "force_cuda_host_device begin">; } // end of Parse Issue category. +let CategoryName = "Concepts Issue" in { +def err_concept_at_non_namespace_scope : Error< + "'concept' can only appear in namespace scope">; + +def err_concept_extra_headers : Error< + "extraneous template parameter list in concept definition">; +def err_concept_unexpected_scope_spec : Error< + "invalid nested name specifier; concepts must be defined in their own namespace">; +def err_concept_nontemplate : Error<"concept definition must be a template; " + "missing template parameter list">; +def err_concept_specialized : Error< + "'concept' cannot be " + "%select{explicitly specialized|partially specialized}0">; +def note_concept_specialized : Note< + "'concept' cannot be " + "%select{explicitly specialized|partially specialized}0">; +} + let CategoryName = "Modules Issue" in { def err_unexpected_module_decl : Error< "module declaration can only appear at the top level">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c1ee929e23..59b78b6fe6 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2387,33 +2387,16 @@ 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< + "type of constraint expression must be 'bool' - not '%0'">; +def err_concept_no_associated_constraints : Error< + "concept may not have associated constraints">; +def err_concept_feature_unimplemented : Error< + "unimplemented concept feature: %0 (coming soon)">; def err_template_different_associated_constraints : Error< "associated constraints differ in template redeclaration">; diff --git a/include/clang/Basic/TemplateKinds.h b/include/clang/Basic/TemplateKinds.h index ac99ad185f..85658af007 100644 --- a/include/clang/Basic/TemplateKinds.h +++ b/include/clang/Basic/TemplateKinds.h @@ -43,10 +43,10 @@ enum TemplateNameKind { /// whether the template name is assumed to refer to a type template or a /// function template depends on the context in which the template /// name occurs. - TNK_Dependent_template_name + TNK_Dependent_template_name, + /// The name refers to a concept definition. + TNK_Concept_template }; } #endif - - diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 619b56363f..514656ffb9 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1959,7 +1959,16 @@ private: ParsedAttributesWithRange &Attrs); DeclSpecContext getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context); - void ParseDeclarationSpecifiers( + + /// \brief Parses declaration-specifiers upto a declarator or ';' emitting + /// diagnostics as necessary and storing parsed information within DS. + /// + /// Note: Asides from parsing the routine C/C++ decl-specifiers (which could + /// include entire class or enum definitions), this also parses a concept + /// definition and stores the appropriate AST representations (for + /// class/enum/concept declarations/definitions, decltype + /// expression-operands or types, where appropriate) within DS. + void ParseDeclarationSpecifiersOrConceptDefinition( DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), AccessSpecifier AS = AS_none, @@ -2811,6 +2820,11 @@ private: SourceLocation &DeclEnd, AccessSpecifier AS = AS_none); + /// \brief Parse a single template declaration that declares a concept [c++2a] + /// and store the AST node within DS. + void ParseConceptDefinition(SourceLocation ConceptLoc, DeclSpec &DS, + const ParsedTemplateInfo &TemplateInfo, + AccessSpecifier AS, DeclSpecContext DSC); //===--------------------------------------------------------------------===// // Modules DeclGroupPtrTy ParseModuleDecl(); diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index e9b116fb71..d693012c71 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -38,6 +38,7 @@ namespace clang { class ASTContext; class CXXRecordDecl; + class ConceptDecl; class TypeLoc; class LangOptions; class IdentifierInfo; @@ -221,10 +222,13 @@ public: unsigned location_size() const { return Builder.getBuffer().second; } }; -/// \brief Captures information about "declaration specifiers". +/// \brief Captures information about "decl-specifiers" and also concept +/// definitions. /// /// "Declaration specifiers" encompasses storage-class-specifiers, -/// type-specifiers, type-qualifiers, and function-specifiers. +/// type-specifiers, type-qualifiers, and function-specifiers. This includes +/// class and enum definitions whose AST representations must be stored - same +/// with the expression-operands of decltype. class DeclSpec { public: /// \brief storage-class-specifier @@ -363,8 +367,14 @@ private: unsigned Constexpr_specified : 1; union { + // Valid if isTypeRep is true. UnionParsedType TypeRep; + // If we parsed a concept, class, enum (etc.) defintion or elaborated type + // specifier, this stores the AST representation. This is valid if either + // isDeclRep or isConceptSpecified returns true. Decl *DeclRep; + // If we parsed a typeof(e) or decltype(e) operator, this stores the + // expression 'e'. Valid if isExprRep is true. Expr *ExprRep; }; @@ -393,6 +403,11 @@ private: SourceLocation FS_forceinlineLoc; SourceLocation FriendLoc, ModulePrivateLoc, ConstexprLoc; SourceLocation TQ_pipeLoc; + + // Even though 'concept' is not a specifier, we handle it here. This allows us + // to reuse infrastructure for diagnosing attributes and invalid + // decl-specifiers. + SourceLocation ConceptLoc; WrittenBuiltinSpecs writtenBS; void SaveWrittenBuiltinSpecs(); @@ -482,7 +497,10 @@ public: bool isTypeSpecOwned() const { return TypeSpecOwned; } bool isTypeRep() const { return isTypeRep((TST) TypeSpecType); } bool isTypeSpecPipe() const { return TypeSpecPipe; } - + bool isDeclRep() const { + return isDeclRep((TST)TypeSpecType); + } + ParsedType getRepAsType() const { assert(isTypeRep((TST) TypeSpecType) && "DeclSpec does not store a type"); return TypeRep; @@ -491,6 +509,17 @@ public: assert(isDeclRep((TST) TypeSpecType) && "DeclSpec does not store a decl"); return DeclRep; } + // This is a template that should only be instantiated with the type + // ConceptDecl. By making it a template we only require ConceptDecl to be a + // complete type where this function is called. + template<class ConceptDeclTy = ConceptDecl> + ConceptDeclTy *getRepAsConcept() const { + static_assert(std::is_same<ConceptDeclTy, ConceptDecl>::value, + "Must only be instantiated with ConceptDecl"); + assert(isConceptSpecified() && "DeclSpec does not store a concept"); + + return cast_or_null<ConceptDeclTy>(DeclRep); + } Expr *getRepAsExpr() const { assert(isExprRep((TST) TypeSpecType) && "DeclSpec does not store an expr"); return ExprRep; @@ -665,6 +694,19 @@ public: assert(isDeclRep((TST) TypeSpecType)); DeclRep = Rep; } + // This function can only be instantiated with ConceptDecl. We made it a + // template so that ConceptDecl only has to be defined where this is called. + template <class ConceptDeclTy = ConceptDecl> + void setConceptRep(ConceptDecl *Rep) { + static_assert(std::is_same<ConceptDeclTy, ConceptDecl>::value, + "Must only be instantiated with ConceptDecl"); + assert(isConceptSpecified() && "DeclSpec does not store a concept"); + assert(!DeclRep && + "why is this not null? We expect this function to be called only " + "once, and usually right after DeclRep was set to null"); + DeclRep = Rep; + } + void UpdateTypeRep(ParsedType Rep) { assert(isTypeRep((TST) TypeSpecType)); TypeRep = Rep; @@ -694,6 +736,8 @@ public: unsigned &DiagID); bool SetConstexprSpec(SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID); + bool setConceptSpec(SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID, const PrintingPolicy &P); bool isFriendSpecified() const { return Friend_specified; } SourceLocation getFriendSpecLoc() const { return FriendLoc; } @@ -704,6 +748,8 @@ public: bool isConstexprSpecified() const { return Constexpr_specified; } SourceLocation getConstexprSpecLoc() const { return ConstexprLoc; } + bool isConceptSpecified() const { return ConceptLoc.isValid(); } + SourceLocation getConceptLoc() const { return ConceptLoc; } void ClearConstexprSpec() { Constexpr_specified = false; ConstexprLoc = SourceLocation(); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 485c537949..7897cf2a27 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2126,6 +2126,7 @@ public: Decl *ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, DeclSpec &DS, RecordDecl *&AnonRecord); + Decl *ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, DeclSpec &DS, MultiTemplateParamsArg TemplateParams, bool IsExplicitInstantiation, @@ -6229,6 +6230,13 @@ public: SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs); + ExprResult + CheckConceptTemplateId(const CXXScopeSpec &SS, + const DeclarationNameInfo &NameInfo, + ConceptDecl *Template, + SourceLocation TemplateLoc, + const TemplateArgumentListInfo *TemplateArgs); + ExprResult BuildTemplateIdExpr(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R, @@ -6507,6 +6515,13 @@ public: const TemplateArgument *Args, unsigned NumArgs); + // Concepts + ConceptDecl *ActOnConceptDefinition( + Scope *S, + MultiTemplateParamsArg TemplateParameterLists, + IdentifierInfo *Name, SourceLocation NameLoc, + Expr *ConstraintExpr); + //===--------------------------------------------------------------------===// // C++ Variadic Templates (C++0x [temp.variadic]) //===--------------------------------------------------------------------===// diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 1f4e0347bc..169075983c 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -1408,6 +1408,9 @@ namespace serialization { /// \brief A TypeAliasTemplateDecl record. DECL_TYPE_ALIAS_TEMPLATE, + /// \brief A ConceptDecl record. + DECL_CONCEPT, + /// \brief A StaticAssertDecl record. DECL_STATIC_ASSERT, diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index cf512b9bd3..b0f0c5d0f5 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -466,6 +466,7 @@ namespace { bool DumpRefOnly); template<typename TemplateDecl> void VisitTemplateDecl(const TemplateDecl *D, bool DumpExplicitInst); + void VisitConceptDecl(const ConceptDecl *D); void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D); void VisitClassTemplateDecl(const ClassTemplateDecl *D); void VisitClassTemplateSpecializationDecl( @@ -1577,6 +1578,12 @@ void ASTDumper::VisitTemplateDecl(const TemplateDecl *D, !D->isCanonicalDecl()); } +void ASTDumper::VisitConceptDecl(const ConceptDecl *D) { + dumpName(D); + dumpTemplateParameters(D->getTemplateParameters()); + dumpStmt(D->getConstraintExpr()); +} + void ASTDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { // FIXME: We don't add a declaration of a function template specialization // to its context when it's explicitly instantiated, so dump explicit diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index baf83a393c..ba02903683 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -724,6 +724,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/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 8854f7879a..19778af79f 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -789,6 +789,27 @@ ClassTemplateSpecializationDecl::getSourceRange() const { } //===----------------------------------------------------------------------===// +// ConceptDecl Implementation +//===----------------------------------------------------------------------===// +ConceptDecl *ConceptDecl::Create(ASTContext &C, DeclContext *DC, + SourceLocation NameLoc, DeclarationName Name, + TemplateParameterList *Params, + Expr *ConstraintExpr) { + // TODO: Do we need this? + // AdoptTemplateParameterList(Params, cast<DeclContext>(Decl)); + return new (C, DC) ConceptDecl(DC, NameLoc, 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/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index c9b80e38d4..38eb0c8fe7 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -105,6 +105,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::OMPThreadPrivate: case Decl::OMPCapturedExpr: case Decl::Empty: + case Decl::Concept: // None of these decls require codegen support. return; diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 063b9be4cd..b8bed33b02 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -4423,6 +4423,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::TypeAliasTemplate: case Decl::Block: case Decl::Empty: + case Decl::Concept: break; case Decl::Using: // using X; [C++] if (CGDebugInfo *DI = getModuleDebugInfo()) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 3d3abe32ab..9b0828a6d5 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1739,7 +1739,8 @@ Parser::ParseSimpleDeclaration(DeclaratorContext Context, ParsingDeclSpec DS(*this); DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context); - ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, DSContext); + ParseDeclarationSpecifiersOrConceptDefinition(DS, ParsedTemplateInfo(), + AS_none, DSContext); // If we had a free-standing type definition with a missing semicolon, we // may get this far before the problem becomes obvious. @@ -2386,7 +2387,8 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS, /// specifier-qualifier-list is a subset of declaration-specifiers. Just /// parse declaration-specifiers and complain about extra stuff. /// TODO: diagnose attribute-specifiers and alignment-specifiers. - ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC); + ParseDeclarationSpecifiersOrConceptDefinition(DS, ParsedTemplateInfo(), AS, + DSC); // Validate declspec for type-name. unsigned Specs = DS.getParsedSpecifiers(); @@ -2871,11 +2873,12 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, // and call ParsedFreeStandingDeclSpec as appropriate. DS.ClearTypeSpecType(); ParsedTemplateInfo NotATemplate; - ParseDeclarationSpecifiers(DS, NotATemplate, AS, DSContext, LateAttrs); + ParseDeclarationSpecifiersOrConceptDefinition(DS, NotATemplate, AS, DSContext, + LateAttrs); return false; } -/// ParseDeclarationSpecifiers +/// ParseDeclarationSpecifiersOrConceptDefinition /// declaration-specifiers: [C99 6.7] /// storage-class-specifier declaration-specifiers[opt] /// type-specifier declaration-specifiers[opt] @@ -2902,7 +2905,8 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, /// [OpenCL] '__kernel' /// 'friend': [C++ dcl.friend] /// 'constexpr': [C++0x dcl.constexpr] -void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, +/// [C++2a] 'concept' +void Parser::ParseDeclarationSpecifiersOrConceptDefinition(DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS, DeclSpecContext DSContext, @@ -3680,7 +3684,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumeToken(); ParseEnumSpecifier(Loc, DS, TemplateInfo, AS, DSContext); continue; - + + case tok::kw_concept: + ConsumeToken(); + ParseConceptDefinition(Loc, DS, TemplateInfo, AS, DSContext); + continue; // cv-qualifier: case tok::kw_const: isInvalid = DS.SetTypeQual(DeclSpec::TQ_const, Loc, PrevSpec, DiagID, @@ -6366,7 +6374,7 @@ void Parser::ParseParameterDeclarationClause( // too much hassle. DS.takeAttributesFrom(FirstArgAttrs); - ParseDeclarationSpecifiers(DS); + ParseDeclarationSpecifiersOrConceptDefinition(DS); // Parse the declarator. This is "PrototypeContext" or diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 0c789c9b1c..e0db75ced5 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -2561,8 +2561,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, if (MalformedTypeSpec) DS.SetTypeSpecError(); - ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DeclSpecContext::DSC_class, - &CommonLateParsedAttrs); + ParseDeclarationSpecifiersOrConceptDefinition( + DS, TemplateInfo, AS, DeclSpecContext::DSC_class, &CommonLateParsedAttrs); // Turn off colon protection that was set for declspec. X.restore(); diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 0ac418ad70..203d5e6d0d 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1490,7 +1490,7 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc, cStyleParamWarned = true; } DeclSpec DS(AttrFactory); - ParseDeclarationSpecifiers(DS); + ParseDeclarationSpecifiersOrConceptDefinition(DS); // Parse the declarator. Declarator ParmDecl(DS, DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDecl); @@ -2541,7 +2541,7 @@ StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { Scope::AtCatchScope); if (Tok.isNot(tok::ellipsis)) { DeclSpec DS(AttrFactory); - ParseDeclarationSpecifiers(DS); + ParseDeclarationSpecifiersOrConceptDefinition(DS); Declarator ParmDecl(DS, DeclaratorContext::ObjCCatchContext); ParseDeclarator(ParmDecl); diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 88a5745350..82a049fddd 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -54,6 +54,15 @@ Parser::ParseDeclarationStartingWithTemplate(DeclaratorContext Context, /// 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] +/// 'export'[opt] 'template' '<' template-parameter-list '>' +/// requires-clause[opt] +/// /// explicit-specialization: [ C++ temp.expl.spec] /// 'template' '<' '>' declaration Decl * @@ -148,13 +157,10 @@ Parser::ParseTemplateDeclarationOrSpecialization(DeclaratorContext Context, unsigned NewFlags = getCurScope()->getFlags() & ~Scope::TemplateParamScope; ParseScopeFlags TemplateScopeFlags(this, NewFlags, isSpecialization); - // Parse the actual template declaration. - return ParseSingleDeclarationAfterTemplate(Context, - ParsedTemplateInfo(&ParamLists, - isSpecialization, - LastParamListWasEmpty), - ParsingTemplateParams, - DeclEnd, AS, AccessAttrs); + return ParseSingleDeclarationAfterTemplate( + Context, + ParsedTemplateInfo(&ParamLists, isSpecialization, LastParamListWasEmpty), + ParsingTemplateParams, DeclEnd, AS, AccessAttrs); } /// \brief Parse a single declaration that declares a template, @@ -208,7 +214,7 @@ Parser::ParseSingleDeclarationAfterTemplate( // the template parameters. ParsingDeclSpec DS(*this, &DiagsFromTParams); - ParseDeclarationSpecifiers(DS, TemplateInfo, AS, + ParseDeclarationSpecifiersOrConceptDefinition(DS, TemplateInfo, AS, getDeclSpecContextFromDeclaratorContext(Context)); if (Tok.is(tok::semi)) { @@ -322,6 +328,149 @@ Parser::ParseSingleDeclarationAfterTemplate( return ThisDecl; } + +void +Parser::ParseConceptDefinition(SourceLocation ConceptLoc,
+ DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo,
+ AccessSpecifier AS,
+ DeclSpecContext DSC) { +
+
+ auto DiagnoseAttributes = [this] {
+ ParsedAttributesWithRange attrs(this->AttrFactory);
+ this->MaybeParseGNUAttributes(attrs);
+ this->MaybeParseCXX11Attributes(attrs);
+ this->MaybeParseMicrosoftDeclSpecs(attrs); + this->ProhibitAttributes(attrs); + }; + + + // If attributes exist after 'concept' kw but before the concept name, + // prohibit them for now (if CWG approves attributes on concepts, this is + // likely where they will go...). + DiagnoseAttributes(); + + // Set the concept specifier at ConceptLoc within 'DS' along with emitting any + // incompatible decl-specifier diagnostics. + { + const char *PrevSpec = 0; + unsigned int DiagId = 0; + if (DS.setConceptSpec(ConceptLoc, PrevSpec, DiagId, + Actions.getASTContext().getPrintingPolicy())) { + Diag(ConceptLoc, DiagId) << PrevSpec; + } + Actions.DiagnoseFunctionSpecifiers(DS); + } + + if (DSC != DeclSpecContext::DSC_top_level) { + Diag(ConceptLoc, diag::err_concept_at_non_namespace_scope); + // If we are not in a template parameter context, skip to a '}' or ';'. The + // error messages are better if we just ignore this within template + // parameter lists. + if (DSC != DeclSpecContext::DSC_template_param) { + SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); + } else { + SkipUntil(llvm::makeArrayRef({tok::comma, tok::greater}), + StopAtSemi | StopBeforeMatch); + } + return; + } + + // A scope-guard that (if an error occurs while parsing a concept) skips to + // the next semi or closing brace. + class SkipUntilSemiOrClosingBraceOnScopeExit { + Parser &P; + bool Disabled = false; + + public: + SkipUntilSemiOrClosingBraceOnScopeExit(Parser &P) + : P(P) {} + void disable() { Disabled = true; } + ~SkipUntilSemiOrClosingBraceOnScopeExit() { + if (!Disabled) { + // Skip until the semi-colon or a '}'. + P.SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); + } + } + } SkipUntilSemiOrClosingBraceOnScopeExit(*this); + + if (TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate) { + Diag(ConceptLoc, diag::err_concept_nontemplate); + return; + } + + const TemplateParameterLists &ParamLists = *TemplateInfo.TemplateParams; + + + // More than one TPL wouldn't make sense here. + if (ParamLists.size() != 1) { + Diag(Tok.getLocation(), diag::err_concept_extra_headers); + return; + } + const TemplateParameterList *const TPL = ParamLists[0]; + + // Explicit specializations of concepts are not allowed. + if (TPL->getLAngleLoc().getLocWithOffset(1) == TPL->getRAngleLoc()) { + assert(!TPL->size() && + "can not have template parameters within empty angle brackets!"); + Diag(ConceptLoc, diag::err_concept_specialized) << 0; + return; + } + // Concepts can not be defined with nested name specifiers. + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, nullptr, + /*EnteringContext=*/false) || + SS.isNotEmpty()) { + + if (SS.isNotEmpty()) + Diag(Tok.getLocation(), diag::err_concept_unexpected_scope_spec); + return; + } + // An identifier (i.e. the concept-name) should follow 'concept'. + if (!Tok.is(tok::identifier)) { + Diag(Tok.getLocation(), diag::err_expected) << "concept name"; + return; + } + + IdentifierInfo *Id = Tok.getIdentifierInfo(); + SourceLocation IdLoc = ConsumeToken(); + + // If attributes exist after the identifier, parse them and diagnose + DiagnoseAttributes(); + + if (!TryConsumeToken(tok::equal)) { + Diag(Tok.getLocation(), diag::err_expected) << "equal"; + return; + } + + ExprResult ConstraintExprResult = ParseConstraintExpression(); + if (ConstraintExprResult.isInvalid()) { + Diag(Tok.getLocation(), diag::err_expected_expression) + << "constraint-expression"; + return; + } + + // We can try to create a valid concept decl node now, so disable the + // scope-guard. + SkipUntilSemiOrClosingBraceOnScopeExit.disable(); + + Expr *ConstraintExpr = ConstraintExprResult.get(); + ConceptDecl *const ConDecl = Actions.ActOnConceptDefinition( + getCurScope(), *TemplateInfo.TemplateParams, Id, IdLoc, ConstraintExpr); + DS.setConceptRep(ConDecl); + + if (Tok.isNot(tok::semi)) { + + ExpectAndConsume(tok::semi, diag::err_expected_after, "concept"); + // Push this token back into the preprocessor and change our current token + // to ';' so that the rest of the code recovers as though there were an + // ';' after the definition. + PP.EnterToken(Tok); + Tok.setKind(tok::semi); + } + return; +} + /// 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. @@ -690,8 +839,8 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { // FIXME: The type should probably be restricted in some way... Not all // declarators (parts of declarators?) are accepted for parameters. DeclSpec DS(AttrFactory); - ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, - DeclSpecContext::DSC_template_param); + ParseDeclarationSpecifiersOrConceptDefinition( + DS, ParsedTemplateInfo(), AS_none, DeclSpecContext::DSC_template_param); // Parse this as a typename. Declarator ParamDecl(DS, DeclaratorContext::TemplateParamContext); diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index ebd6f0f5b8..5648d994fc 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -1351,6 +1351,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw_struct: case tok::kw_union: case tok::kw___interface: + case tok::kw_concept: // enum-specifier case tok::kw_enum: // cv-qualifier diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index d8f9f7a390..17d046958d 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -919,7 +919,7 @@ Parser::ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs, AccessSpecifier AS) { MaybeParseMicrosoftAttributes(DS.getAttributes()); // Parse the common declaration-specifiers piece. - ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, + ParseDeclarationSpecifiersOrConceptDefinition(DS, ParsedTemplateInfo(), AS, DeclSpecContext::DSC_top_level); // If we had a free-standing type definition with a missing semicolon, we @@ -1287,7 +1287,7 @@ void Parser::ParseKNRParamDeclarations(Declarator &D) { // Parse the common declaration-specifiers piece. DeclSpec DS(AttrFactory); - ParseDeclarationSpecifiers(DS); + ParseDeclarationSpecifiersOrConceptDefinition(DS); // C99 6.9.1p6: 'each declaration in the declaration list shall have at // least one declarator'. @@ -1647,7 +1647,7 @@ bool Parser::TryKeywordIdentFallback(bool DisableKeyword) { /// Actions.getTypeName will not be needed to be called again (e.g. getTypeName /// will not be called twice, once to check whether we have a declaration /// specifier, and another one to get the actual type inside -/// ParseDeclarationSpecifiers). +/// ParseDeclarationSpecifiersOrConceptDefinition). /// /// This returns true if an error occurred. /// diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index 2fad5a18ba..5f5d94eee3 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -426,6 +426,7 @@ unsigned DeclSpec::getParsedSpecifiers() const { return Res; } + template <class T> static bool BadSpecifier(T TNew, T TPrev, const char *&PrevSpec, unsigned &DiagID, @@ -491,7 +492,6 @@ const char *DeclSpec::getSpecifierName(TSS S) { } llvm_unreachable("Unknown typespec!"); } - const char *DeclSpec::getSpecifierName(DeclSpec::TST T, const PrintingPolicy &Policy) { switch (T) { @@ -969,6 +969,69 @@ bool DeclSpec::SetConstexprSpec(SourceLocation Loc, const char *&PrevSpec, return false; } +bool DeclSpec::setConceptSpec(const SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID, const PrintingPolicy &PP) { + assert(Loc.isValid() && "Loc must be valid, since it is used to identify " + "that this function was called before"); + assert(!ConceptLoc.isValid() && + "how is this called if concept was already encountered and triggered " + "ParseConceptDefinition which parses upto the semi-colon"); + + PrevSpec = nullptr; + if (TypeSpecType != TST_unspecified) { + PrevSpec = DeclSpec::getSpecifierName(static_cast<TST>(TypeSpecType), PP); + ClearTypeSpecType(); + } + if (TypeSpecSign != TSS_unspecified) { + PrevSpec = DeclSpec::getSpecifierName(static_cast<TSS>(TypeSpecSign)); + TypeSpecSign = TSS_unspecified; + } + if (TypeSpecWidth != TSW_unspecified) { + PrevSpec = DeclSpec::getSpecifierName(static_cast<TSW>(TypeSpecWidth)); + TypeSpecWidth = TSW_unspecified; + } + if (StorageClassSpec != SCS_unspecified) { + PrevSpec = DeclSpec::getSpecifierName(static_cast<SCS>(StorageClassSpec)); + ClearStorageClassSpecs(); + } + if (ThreadStorageClassSpec != TSCS_unspecified) { + PrevSpec = + DeclSpec::getSpecifierName(static_cast<TSCS>(ThreadStorageClassSpec)); + ClearStorageClassSpecs(); + } + if (TypeSpecComplex != TSC_unspecified) { + PrevSpec = DeclSpec::getSpecifierName(static_cast<TSC>(TypeSpecComplex)); + TypeSpecComplex = TSC_unspecified; + } + if (getTypeQualifiers()) { + PrevSpec = DeclSpec::getSpecifierName(static_cast<TQ>(TypeQualifiers)); + ClearTypeQualifiers(); + } + if (isFriendSpecified()) { + PrevSpec = "friend"; + Friend_specified = false; + FriendLoc = SourceLocation(); + } + if (isConstexprSpecified()) { + PrevSpec = "constexpr"; + Constexpr_specified = false; + ConstexprLoc = SourceLocation(); + } + if (isInlineSpecified()) { + PrevSpec = "inline"; + FS_inlineLoc = SourceLocation(); + FS_inline_specified = false; + } + + if (PrevSpec) { + DiagID = diag::err_invalid_decl_spec_combination; + } + // We set the concept location regardless of whether an error occurred. + DeclRep = nullptr; + ConceptLoc = Loc; + return PrevSpec; // If this is non-null, an error occurred. +} + void DeclSpec::SaveWrittenBuiltinSpecs() { writtenBS.Sign = getTypeSpecSign(); writtenBS.Width = getTypeSpecWidth(); @@ -1270,6 +1333,7 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { // TODO: return "auto function" and other bad things based on the real type. // 'data definition has no type or storage class'? + } bool DeclSpec::isMissingDeclaratorOk() { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 1bcc9329e4..acbec1aca9 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -4212,6 +4212,12 @@ Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, DeclSpec &DS, MultiTemplateParamsArg TemplateParams, bool IsExplicitInstantiation, RecordDecl *&AnonRecord) { + + // We don't need to do any additional declspecifier checking on concept + // definitions since that should already have been done when the concept kw + // location was set within DS. + if (DS.isConceptSpecified()) return DS.getRepAsConcept(); + Decl *TagD = nullptr; TagDecl *Tag = nullptr; if (DS.getTypeSpecType() == DeclSpec::TST_class || diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 678f6af068..0fc399a375 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9467,7 +9467,7 @@ UsingShadowDecl *Sema::BuildUsingShadowDecl(Scope *S, NonTemplateTarget = TargetTD->getTemplatedDecl(); UsingShadowDecl *Shadow; - if (isa<CXXConstructorDecl>(NonTemplateTarget)) { + if (!isa<ConceptDecl>(Target) && isa<CXXConstructorDecl>(NonTemplateTarget)) { bool IsVirtualBase = isVirtualDirectBase(cast<CXXRecordDecl>(CurContext), UD->getQualifier()->getAsRecordDecl()); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 5f739fd550..fbc65835d9 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -232,9 +232,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; } } @@ -3034,7 +3036,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()) @@ -3988,6 +3991,18 @@ Sema::CheckVarTemplateId(const CXXScopeSpec &SS, /*FoundD=*/nullptr, TemplateArgs); } +ExprResult +Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, + const DeclarationNameInfo &NameInfo, + ConceptDecl *Template, + SourceLocation TemplateLoc, + const TemplateArgumentListInfo *TemplateArgs) { + // TODO: Do concept specialization here. + Diag(Template->getLocation(), diag::err_concept_feature_unimplemented) + << "can not form concept template-id"; + return ExprError(); +} + ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R, @@ -4009,14 +4024,21 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS, // In C++1y, check variable template ids. bool InstantiationDependent; - if (R.getAsSingle<VarTemplateDecl>() && - !TemplateSpecializationType::anyDependentTemplateArguments( - *TemplateArgs, InstantiationDependent)) { + const bool DependentArguments = + TemplateSpecializationType::anyDependentTemplateArguments( + *TemplateArgs, InstantiationDependent); + if (R.getAsSingle<VarTemplateDecl>() && !DependentArguments) { return CheckVarTemplateId(SS, R.getLookupNameInfo(), R.getAsSingle<VarTemplateDecl>(), TemplateKWLoc, TemplateArgs); } + if (R.getAsSingle<ConceptDecl>() && !DependentArguments) { + return CheckConceptTemplateId(SS, R.getLookupNameInfo(), + R.getAsSingle<ConceptDecl>(), + TemplateKWLoc, TemplateArgs); + } + // We don't want lookup warnings at this point. R.suppressDiagnostics(); @@ -7688,6 +7710,61 @@ Decl *Sema::ActOnTemplateDeclarator(Scope *S, return NewDecl; } +ConceptDecl *Sema::ActOnConceptDefinition(Scope *S, + MultiTemplateParamsArg TemplateParameterLists, IdentifierInfo *Name, + SourceLocation NameLoc, Expr *ConstraintExpr) { + // C++2a [temp.concept]p3: + // A concept-definition shall appear in the global scope or in a namespace + // scope. + assert( + CurContext->isFileContext() && + "We check during parsing that 'concept's only occur at namespace scope"); + + // Forbid any prior declaration of this name within the current namespace. + LookupResult Previous(*this, + DeclarationNameInfo(DeclarationName(Name), NameLoc), + LookupOrdinaryName); + LookupName(Previous, S); + if (!Previous.empty()) { + const NamedDecl *PrevDecl = Previous.getRepresentativeDecl(); + if (PrevDecl->getDeclContext()->Equals(CurContext)) { + if (Previous.isSingleResult() && + isa<ConceptDecl>(Previous.getFoundDecl())) { + Diag(NameLoc, diag::err_redefinition) << Name; + } else { + Diag(NameLoc, diag::err_redefinition_different_kind) << Name; + } + Diag(PrevDecl->getLocation(), diag::note_previous_decl) << PrevDecl; + return nullptr; + } + } + + ConceptDecl *NewDecl = ConceptDecl::Create(Context, CurContext, NameLoc, + Name, TemplateParameterLists[0], + ConstraintExpr); + + if (!NewDecl) + return nullptr; + + 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(); + } + + assert((S->isTemplateParamScope() || !TemplateParameterLists[0]->size()) && + "Not in template param scope?"); + assert(S->getParent() && !S->getParent()->isTemplateParamScope() && + "Parent scope should exist and not be template parameter."); + + ActOnDocumentableDecl(NewDecl); + PushOnScopeChains(NewDecl, S->getParent(), /*AddToContext=*/true); + + return NewDecl; +} + /// \brief Strips various properties off an implicit instantiation /// that has just been explicitly specialized. static void StripImplicitInstantiation(NamedDecl *D) { diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index a7883c67b8..0ebcb03ca3 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3073,6 +3073,11 @@ Decl *TemplateDeclInstantiator::VisitFriendTemplateDecl(FriendTemplateDecl *D) { return nullptr; } +Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) { + llvm_unreachable("Concept definitions cannot reside inside a template"); + return nullptr; +} + Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) { llvm_unreachable("Unexpected decl"); } diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp index 535aacb8c4..82809cb465 100644 --- a/lib/Serialization/ASTCommon.cpp +++ b/lib/Serialization/ASTCommon.cpp @@ -313,6 +313,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/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 7a5d22c00f..1b0e1bbbf7 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -381,6 +381,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); @@ -2031,6 +2032,11 @@ DeclID ASTDeclReader::VisitTemplateDecl(TemplateDecl *D) { return PatternID; } +void ASTDeclReader::VisitConceptDecl(ConceptDecl *D) { + VisitTemplateDecl(D); + D->setConstraintExpr(Record.readExpr()); +} + ASTDeclReader::RedeclarableResult ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { RedeclarableResult Redecl = VisitRedeclarable(D); @@ -3595,6 +3601,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/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 7f2b1861d1..a055899009 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1280,6 +1280,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/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 189de14cff..432c2559e4 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/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); @@ -1395,6 +1396,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/test/Parser/cxx-concept-declaration.cpp b/test/Parser/cxx-concept-declaration.cpp deleted file mode 100644 index 2e9d1ac2fc..0000000000 --- a/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; diff --git a/test/Parser/cxx2a-concept-declaration.cpp b/test/Parser/cxx2a-concept-declaration.cpp new file mode 100644 index 0000000000..7c0923cfe7 --- /dev/null +++ b/test/Parser/cxx2a-concept-declaration.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s +// Support parsing of concepts + +concept X; //expected-error {{must be a template}} + +template<typename T> concept C1 = true; //expected-note{{declared here}} <-- previous declaration + +template<typename T> concept C1 = true; // expected-error {{redefinition of 'C1'}} + +template<concept T> concept D1 = true; // expected-error {{'concept' can only appear in namespace scope}} \ + expected-error {{expected template parameter}} + +template<> concept X = true; // expected-error {{cannot be explicitly specialized}} + +namespace ns1 { +template<> concept D1 = true; // expected-error {{cannot be explicitly specialized}} +template<typename T> const concept C1 = true; //expected-error{{cannot combine with}} +namespace ns12 { +template<typename T> decltype(T{}) concept C2 = true; //expected-error{{cannot combine with}} +template<typename T> bool concept C3 = true; //expected-error{{cannot combine with}} +template<typename T> unsigned concept C4 = true; //expected-error{{cannot combine with}} +template<typename T> short concept C5 = true; //expected-error{{cannot combine with}} +template<typename T> typedef concept C6 = true; //expected-error{{cannot combine with}} +} +template<class> concept + const //expected-error{{expected concept name}} + C2 = true; + +void f() { + concept X; //expected-error{{'concept' can only appear in namespace scope}} +} +template<concept X, //expected-error{{'concept' can only appear in namespace scope}} \ + expected-error {{expected template parameter}} + int J> void f(); +} + +template<class T> +concept [[test]] X2 [[test2]] = T::value; //expected-error2{{attribute list cannot appear here}} + +namespace ns2 { +template<class T> +concept [[test]] X2 [[test2]] = T::value; //expected-error2{{attribute list cannot appear here}} + +} + +namespace ns3 { + template<typename T> concept C1 = true; + + namespace ns1 { + using ns3::C1; //expected-note{{declared here}} + template<typename T> concept C1 = true; // expected-error {{redefinition of 'C1'}} + } + +} + +// TODO: +// template<typename T> concept C2 = 0.f; // expected error {{constraint expression must be 'bool'}} + +struct S1 { + template<typename T> concept C1 = true; // expected-error {{can only appear in namespace scope}} +}; + +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{{declared here}} <-- previous declaration +template<typename T> concept C6 = true; // expected-error {{redefinition of 'C6' as different kind of symbol}} + +namespace thing {}; + +template<typename T> concept thing::C7 = true; // expected-error {{concepts must be defined in their own namespace}} + + +namespace ns5 { +} + +// TODO: Add test to prevent explicit specialization, partial specialization +// and explicit instantiation of concepts. diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 497a3ca8f5..17548f3379 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -6173,6 +6173,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 |