Skip to content

Commit

Permalink
Add a concept AST node.
Browse files Browse the repository at this point in the history
This patch adds a concept AST node (`ConceptLoc`) and uses it at the corresponding places.

There are three objects that might have constraints via concepts:
`TypeConstraint`,  `ConceptSpecializationExpr` and `AutoTypeLoc`.
The first two inherit from `ConceptReference` while the latter has
the information about a possible constraint directly stored in `AutoTypeLocInfo`. It would be nice if the concept information would be stored the same way in all three cases.

Moreover the current structure makes it difficult to deal with these concepts. For example in Clangd accessing the locations of constraints of a `AutoTypeLoc` can only be done with quite ugly hacks.

So we think that it makes sense to create a new AST node for such concepts.

In details we propose the following:
- Rename `ConceptReference` to `ConceptLoc` (or something else what is approriate)
and make it the new AST node.
- `TypeConstraint` and `ConceptSpecializationExpr` do not longer inherit from `ConceptReference` but store a pointer to a `ConceptLoc`.
- `AutoTypeLoc` stores a pointer to `ConceptLoc` instead of storing the concept info in `AutoTypeLocInfo`.

This patch implements a first version of this idea which compiles and where the existing tests pass.
To make this patch as small as possible we keep the existing member functions to access concept data. Later these can be replaced by directly calling the corresponding functions of the `ConceptLoc`s.

Differential Revision: https://reviews.llvm.org/D155858
  • Loading branch information
jensmassberg committed Aug 31, 2023
1 parent 831b509 commit c2bf9ba
Show file tree
Hide file tree
Showing 24 changed files with 572 additions and 309 deletions.
110 changes: 94 additions & 16 deletions clang/include/clang/AST/ASTConcept.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#ifndef LLVM_CLANG_AST_ASTCONCEPT_H
#define LLVM_CLANG_AST_ASTCONCEPT_H

#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
Expand Down Expand Up @@ -107,10 +109,18 @@ struct ASTConstraintSatisfaction final :
Rebuild(const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction);
};

/// \brief Common data class for constructs that reference concepts with
/// template arguments.
/// A reference to a concept and its template args, as it appears in the code.
///
/// Examples:
/// template <int X> requires is_even<X> int half = X/2;
/// ~~~~~~~~~~ (in ConceptSpecializationExpr)
///
/// std::input_iterator auto I = Container.begin();
/// ~~~~~~~~~~~~~~~~~~~ (in AutoTypeLoc)
///
/// template <std::derives_from<Expr> T> void dump();
/// ~~~~~~~~~~~~~~~~~~~~~~~ (in TemplateTypeParmDecl)
class ConceptReference {
protected:
// \brief The optional nested name specifier used when naming the concept.
NestedNameSpecifierLoc NestedNameSpec;

Expand All @@ -134,7 +144,6 @@ class ConceptReference {
/// concept.
const ASTTemplateArgumentListInfo *ArgsAsWritten;

public:
ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
ConceptDecl *NamedConcept,
Expand All @@ -143,8 +152,15 @@ class ConceptReference {
ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
NamedConcept(NamedConcept), ArgsAsWritten(ArgsAsWritten) {}

ConceptReference()
: FoundDecl(nullptr), NamedConcept(nullptr), ArgsAsWritten(nullptr) {}
public:
static ConceptReference *
Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
return new (C) ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo,
FoundDecl, NamedConcept, ArgsAsWritten);
}

const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
return NestedNameSpec;
Expand All @@ -158,6 +174,26 @@ class ConceptReference {

SourceLocation getTemplateKWLoc() const { return TemplateKWLoc; }

SourceLocation getLocation() const { return getConceptNameLoc(); }

SourceLocation getBeginLoc() const LLVM_READONLY {
// Note that if the qualifier is null the template KW must also be null.
if (auto QualifierLoc = getNestedNameSpecifierLoc())
return QualifierLoc.getBeginLoc();
return getConceptNameInfo().getBeginLoc();
}

SourceLocation getEndLoc() const LLVM_READONLY {
return getTemplateArgsAsWritten() &&
getTemplateArgsAsWritten()->getRAngleLoc().isValid()
? getTemplateArgsAsWritten()->getRAngleLoc()
: getConceptNameInfo().getEndLoc();
}

SourceRange getSourceRange() const LLVM_READONLY {
return SourceRange(getBeginLoc(), getEndLoc());
}

NamedDecl *getFoundDecl() const {
return FoundDecl;
}
Expand All @@ -175,22 +211,30 @@ class ConceptReference {
bool hasExplicitTemplateArgs() const {
return ArgsAsWritten != nullptr;
}

void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const;
};

class TypeConstraint : public ConceptReference {
/// Models the abbreviated syntax to constrain a template type parameter:
/// template <convertible_to<string> T> void print(T object);
/// ~~~~~~~~~~~~~~~~~~~~~~
/// Semantically, this adds an "immediately-declared constraint" with extra arg:
/// requires convertible_to<T, string>
///
/// In the C++ grammar, a type-constraint is also used for auto types:
/// convertible_to<string> auto X = ...;
/// We do *not* model these as TypeConstraints, but AutoType(Loc) directly.
class TypeConstraint {
/// \brief The immediately-declared constraint expression introduced by this
/// type-constraint.
Expr *ImmediatelyDeclaredConstraint = nullptr;
ConceptReference *ConceptRef;

public:
TypeConstraint(NestedNameSpecifierLoc NNS,
DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
Expr *ImmediatelyDeclaredConstraint) :
ConceptReference(NNS, /*TemplateKWLoc=*/SourceLocation(), ConceptNameInfo,
FoundDecl, NamedConcept, ArgsAsWritten),
ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint) {}
TypeConstraint(ConceptReference *ConceptRef,
Expr *ImmediatelyDeclaredConstraint)
: ImmediatelyDeclaredConstraint(ImmediatelyDeclaredConstraint),
ConceptRef(ConceptRef) {}

/// \brief Get the immediately-declared constraint expression introduced by
/// this type-constraint, that is - the constraint expression that is added to
Expand All @@ -199,7 +243,41 @@ class TypeConstraint : public ConceptReference {
return ImmediatelyDeclaredConstraint;
}

void print(llvm::raw_ostream &OS, PrintingPolicy Policy) const;
ConceptReference *getConceptReference() const { return ConceptRef; }

// FIXME: Instead of using these concept related functions the callers should
// directly work with the corresponding ConceptReference.
ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }

SourceLocation getConceptNameLoc() const {
return ConceptRef->getConceptNameLoc();
}

bool hasExplicitTemplateArgs() const {
return ConceptRef->hasExplicitTemplateArgs();
}

const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
return ConceptRef->getTemplateArgsAsWritten();
}

SourceLocation getTemplateKWLoc() const {
return ConceptRef->getTemplateKWLoc();
}

NamedDecl *getFoundDecl() const { return ConceptRef->getFoundDecl(); }

const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
return ConceptRef->getNestedNameSpecifierLoc();
}

const DeclarationNameInfo &getConceptNameInfo() const {
return ConceptRef->getConceptNameInfo();
}

void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const {
ConceptRef->print(OS, Policy);
}
};

} // clang
Expand Down
5 changes: 1 addition & 4 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -1374,10 +1374,7 @@ class TemplateTypeParmDecl final : public TypeDecl,
nullptr;
}

void setTypeConstraint(NestedNameSpecifierLoc NNS,
DeclarationNameInfo NameInfo, NamedDecl *FoundDecl,
ConceptDecl *CD,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
void setTypeConstraint(ConceptReference *CR,
Expr *ImmediatelyDeclaredConstraint);

/// Determine whether this template parameter has a type-constraint.
Expand Down
69 changes: 44 additions & 25 deletions clang/include/clang/AST/ExprConcepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,13 @@ class ASTStmtWriter;
///
/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the
/// specialization of a concept results in a prvalue of type bool.
class ConceptSpecializationExpr final : public Expr, public ConceptReference {
class ConceptSpecializationExpr final : public Expr {
friend class ASTReader;
friend class ASTStmtReader;

public:
using SubstitutionDiagnostic = std::pair<SourceLocation, std::string>;
private:
ConceptReference *ConceptRef;

protected:
/// \brief The Implicit Concept Specialization Decl, which holds the template
/// arguments for this specialization.
ImplicitConceptSpecializationDecl *SpecDecl;
Expand All @@ -55,16 +54,11 @@ class ConceptSpecializationExpr final : public Expr, public ConceptReference {
/// ignored.
ASTConstraintSatisfaction *Satisfaction;

ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc,
DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ConceptSpecializationExpr(const ASTContext &C, ConceptReference *ConceptRef,
ImplicitConceptSpecializationDecl *SpecDecl,
const ConstraintSatisfaction *Satisfaction);

ConceptSpecializationExpr(const ASTContext &C, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
ConceptSpecializationExpr(const ASTContext &C, ConceptReference *ConceptRef,
ImplicitConceptSpecializationDecl *SpecDecl,
const ConstraintSatisfaction *Satisfaction,
bool Dependent,
Expand All @@ -73,16 +67,12 @@ class ConceptSpecializationExpr final : public Expr, public ConceptReference {

public:
static ConceptSpecializationExpr *
Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
Create(const ASTContext &C, ConceptReference *ConceptRef,
ImplicitConceptSpecializationDecl *SpecDecl,
const ConstraintSatisfaction *Satisfaction);

static ConceptSpecializationExpr *
Create(const ASTContext &C, ConceptDecl *NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
Create(const ASTContext &C, ConceptReference *ConceptRef,
ImplicitConceptSpecializationDecl *SpecDecl,
const ConstraintSatisfaction *Satisfaction, bool Dependent,
bool ContainsUnexpandedParameterPack);
Expand All @@ -91,6 +81,37 @@ class ConceptSpecializationExpr final : public Expr, public ConceptReference {
return SpecDecl->getTemplateArguments();
}

ConceptReference *getConceptReference() const { return ConceptRef; }

ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }

// FIXME: Several of the following functions can be removed. Instead the
// caller can directly work with the ConceptReference.
bool hasExplicitTemplateArgs() const {
return ConceptRef->hasExplicitTemplateArgs();
}

SourceLocation getConceptNameLoc() const {
return ConceptRef->getConceptNameLoc();
}
const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
return ConceptRef->getTemplateArgsAsWritten();
}

const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
return ConceptRef->getNestedNameSpecifierLoc();
}

SourceLocation getTemplateKWLoc() const {
return ConceptRef->getTemplateKWLoc();
}

NamedDecl *getFoundDecl() const { return ConceptRef->getFoundDecl(); }

const DeclarationNameInfo &getConceptNameInfo() const {
return ConceptRef->getConceptNameInfo();
}

const ImplicitConceptSpecializationDecl *getSpecializationDecl() const {
assert(SpecDecl && "Template Argument Decl not initialized");
return SpecDecl;
Expand Down Expand Up @@ -119,17 +140,15 @@ class ConceptSpecializationExpr final : public Expr, public ConceptReference {
}

SourceLocation getBeginLoc() const LLVM_READONLY {
if (auto QualifierLoc = getNestedNameSpecifierLoc())
return QualifierLoc.getBeginLoc();
return ConceptName.getBeginLoc();
return ConceptRef->getBeginLoc();
}

SourceLocation getEndLoc() const LLVM_READONLY {
// If the ConceptSpecializationExpr is the ImmediatelyDeclaredConstraint
// of a TypeConstraint written syntactically as a constrained-parameter,
// there may not be a template argument list.
return ArgsAsWritten->RAngleLoc.isValid() ? ArgsAsWritten->RAngleLoc
: ConceptName.getEndLoc();
return ConceptRef->getEndLoc();
}

SourceLocation getExprLoc() const LLVM_READONLY {
return ConceptRef->getLocation();
}

// Iterators
Expand Down

0 comments on commit c2bf9ba

Please sign in to comment.