Skip to content

Commit

Permalink
[Concepts] Concept definitions (D40381)
Browse files Browse the repository at this point in the history
First in a series of patches to land C++2a Concepts support.
This patch adds AST and parsing support for concept-declarations.

llvm-svn: 365699
  • Loading branch information
saarraz committed Jul 10, 2019
1 parent 0171866 commit d7aae33
Show file tree
Hide file tree
Showing 32 changed files with 450 additions and 47 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/AST/ASTNodeTraverser.h
Expand Up @@ -529,6 +529,11 @@ class ASTNodeTraverser
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());
Expand Down
36 changes: 36 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Expand Up @@ -3090,6 +3090,42 @@ class VarTemplateDecl : public RedeclarableTemplateDecl {
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;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Expand Up @@ -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; };
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/TextNodeDumper.h
Expand Up @@ -347,6 +347,7 @@ class TextNodeDumper
void VisitObjCPropertyDecl(const ObjCPropertyDecl *D);
void VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D);
void VisitBlockDecl(const BlockDecl *D);
void VisitConceptDecl(const ConceptDecl *D);
};

} // namespace clang
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DeclNodes.td
Expand Up @@ -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>;
Expand Down
10 changes: 9 additions & 1 deletion clang/include/clang/Basic/DiagnosticParseKinds.td
Expand Up @@ -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<
Expand Down Expand Up @@ -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
55 changes: 22 additions & 33 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -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"
Expand Down Expand Up @@ -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<
Expand All @@ -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<
Expand Down Expand Up @@ -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">;
Expand Down Expand Up @@ -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">;
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/TemplateKinds.h
Expand Up @@ -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


4 changes: 4 additions & 0 deletions clang/include/clang/Parse/Parser.h
Expand Up @@ -3024,6 +3024,10 @@ class Parser : public CodeCompletionHandler {
SourceLocation &DeclEnd,
ParsedAttributes &AccessAttrs,
AccessSpecifier AS = AS_none);
// C++2a: Template, concept definition [temp]
Decl *
ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo,
SourceLocation &DeclEnd);

//===--------------------------------------------------------------------===//
// Modules
Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -1973,6 +1973,7 @@ class Sema {
VarTemplate,
AliasTemplate,
TemplateTemplateParam,
Concept,
DependentTemplate
};
TemplateNameKindForDiagnostics
Expand Down Expand Up @@ -6551,6 +6552,13 @@ class Sema {
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,
Expand Down Expand Up @@ -6818,6 +6826,11 @@ class Sema {
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])
//===--------------------------------------------------------------------===//
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Serialization/ASTBitCodes.h
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Expand Up @@ -1512,6 +1512,18 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
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()) ||
Expand Down Expand Up @@ -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))
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/DeclBase.cpp
Expand Up @@ -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;
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/AST/DeclPrinter.cpp
Expand Up @@ -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 << ";";
}
}

Expand Down
20 changes: 20 additions & 0 deletions clang/lib/AST/DeclTemplate.cpp
Expand Up @@ -821,6 +821,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
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/TextNodeDumper.cpp
Expand Up @@ -1936,3 +1936,7 @@ void TextNodeDumper::VisitBlockDecl(const BlockDecl *D) {
if (D->capturesCXXThis())
OS << " captures_this";
}

void TextNodeDumper::VisitConceptDecl(const ConceptDecl *D) {
dumpName(D);
}
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGDecl.cpp
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Index/IndexDecl.cpp
Expand Up @@ -652,10 +652,10 @@ class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> {
}

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))
Expand Down

0 comments on commit d7aae33

Please sign in to comment.