Skip to content

Commit

Permalink
[Concepts] Placeholder constraints and abbreviated templates
Browse files Browse the repository at this point in the history
This patch implements P1141R2 "Yet another approach for constrained declarations".

General strategy for this patch was:

- Expand AutoType to include optional type-constraint, reflecting the wording and easing the integration of constraints.
- Replace autos in parameter type specifiers with invented parameters in GetTypeSpecTypeForDeclarator, using the same logic
  previously used for generic lambdas, now unified with abbreviated templates, by:
  - Tracking the template parameter lists in the Declarator object
  - Tracking the template parameter depth before parsing function declarators (at which point we can match template
    parameters against scope specifiers to know if we have an explicit template parameter list to append invented parameters
    to or not).
- When encountering an AutoType in a parameter context we check a stack of InventedTemplateParameterInfo structures that
  contain the info required to create and accumulate invented template parameters (fields that were already present in
  LambdaScopeInfo, which now inherits from this class and is looked up when an auto is encountered in a lambda context).

Resubmit after fixing MSAN failures caused by incomplete initialization of AutoTypeLocs in TypeSpecLocFiller.

Differential Revision: https://reviews.llvm.org/D65042

(cherry picked from commit b481f02)
  • Loading branch information
saarraz committed Jan 24, 2020
1 parent dd58206 commit 6a8cd9f
Show file tree
Hide file tree
Showing 53 changed files with 1,871 additions and 362 deletions.
7 changes: 5 additions & 2 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -88,6 +88,7 @@ class AtomicExpr;
class BlockExpr;
class BuiltinTemplateDecl;
class CharUnits;
class ConceptDecl;
class CXXABI;
class CXXConstructorDecl;
class CXXMethodDecl;
Expand Down Expand Up @@ -211,7 +212,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::FoldingSet<ObjCObjectPointerType> ObjCObjectPointerTypes;
mutable llvm::FoldingSet<DependentUnaryTransformType>
DependentUnaryTransformTypes;
mutable llvm::FoldingSet<AutoType> AutoTypes;
mutable llvm::ContextualFoldingSet<AutoType, ASTContext&> AutoTypes;
mutable llvm::FoldingSet<DeducedTemplateSpecializationType>
DeducedTemplateSpecializationTypes;
mutable llvm::FoldingSet<AtomicType> AtomicTypes;
Expand Down Expand Up @@ -1542,7 +1543,9 @@ class ASTContext : public RefCountedBase<ASTContext> {

/// C++11 deduced auto type.
QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
bool IsDependent, bool IsPack = false) const;
bool IsDependent, bool IsPack = false,
ConceptDecl *TypeConstraintConcept = nullptr,
ArrayRef<TemplateArgument> TypeConstraintArgs ={}) const;

/// C++11 deduction pattern for 'auto' type.
QualType getAutoDeductType() const;
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/AST/ASTNodeTraverser.h
Expand Up @@ -548,8 +548,8 @@ class ASTNodeTraverser
}

void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
if (const auto *TC = D->getPlaceholderTypeConstraint())
Visit(TC->getImmediatelyDeclaredConstraint());
if (const auto *E = D->getPlaceholderTypeConstraint())
Visit(E);
if (D->hasDefaultArgument())
Visit(D->getDefaultArgument(), SourceRange(),
D->getDefaultArgStorage().getInheritedFrom(),
Expand Down
43 changes: 29 additions & 14 deletions clang/include/clang/AST/DeclTemplate.h
Expand Up @@ -1102,6 +1102,17 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl {
/// template.
ArrayRef<TemplateArgument> getInjectedTemplateArgs();

/// Return whether this function template is an abbreviated function template,
/// e.g. `void foo(auto x)` or `template<typename T> void foo(auto x)`
bool isAbbreviated() const {
// Since the invented template parameters generated from 'auto' parameters
// are either appended to the end of the explicit template parameter list or
// form a new template paramter list, we can simply observe the last
// parameter to determine if such a thing happened.
const TemplateParameterList *TPL = getTemplateParameters();
return TPL->getParam(TPL->size() - 1)->isImplicit();
}

/// Merge \p Prev with our RedeclarableTemplateDecl::Common.
void mergePrevDecl(FunctionTemplateDecl *Prev);

Expand Down Expand Up @@ -1215,7 +1226,6 @@ class TemplateTypeParmDecl final : public TypeDecl,
bool ParameterPack,
bool HasTypeConstraint = false,
Optional<unsigned> NumExpanded = None);

static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C,
unsigned ID);
static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C,
Expand Down Expand Up @@ -1374,7 +1384,8 @@ class NonTypeTemplateParmDecl final
: public DeclaratorDecl,
protected TemplateParmPosition,
private llvm::TrailingObjects<NonTypeTemplateParmDecl,
std::pair<QualType, TypeSourceInfo *>> {
std::pair<QualType, TypeSourceInfo *>,
Expr *> {
friend class ASTDeclReader;
friend TrailingObjects;

Expand Down Expand Up @@ -1429,10 +1440,12 @@ class NonTypeTemplateParmDecl final
ArrayRef<TypeSourceInfo *> ExpandedTInfos);

static NonTypeTemplateParmDecl *CreateDeserialized(ASTContext &C,
unsigned ID);
unsigned ID,
bool HasTypeConstraint);
static NonTypeTemplateParmDecl *CreateDeserialized(ASTContext &C,
unsigned ID,
unsigned NumExpandedTypes);
unsigned NumExpandedTypes,
bool HasTypeConstraint);

using TemplateParmPosition::getDepth;
using TemplateParmPosition::setDepth;
Expand Down Expand Up @@ -1543,20 +1556,22 @@ class NonTypeTemplateParmDecl final
return TypesAndInfos[I].second;
}

/// Return the type-constraint in the placeholder type of this non-type
/// Return the constraint introduced by the placeholder type of this non-type
/// template parameter (if any).
TypeConstraint *getPlaceholderTypeConstraint() const {
// TODO: Concepts: Implement once we have actual placeholders with type
// constraints.
return nullptr;
Expr *getPlaceholderTypeConstraint() const {
return hasPlaceholderTypeConstraint() ? *getTrailingObjects<Expr *>() :
nullptr;
}

void setPlaceholderTypeConstraint(Expr *E) {
*getTrailingObjects<Expr *>() = E;
}

/// Determine whether this non-type template parameter's type has a
/// placeholder with a type-constraint.
bool hasPlaceholderTypeConstraint() const {
// TODO: Concepts: Implement once we have actual placeholders with type
// constraints.
return false;
auto *AT = getType()->getContainedAutoType();
return AT && AT->isConstrained();
}

/// \brief Get the associated-constraints of this template parameter.
Expand All @@ -1566,8 +1581,8 @@ class NonTypeTemplateParmDecl final
/// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for
/// concepts APIs that accept an ArrayRef of constraint expressions.
void getAssociatedConstraints(llvm::SmallVectorImpl<const Expr *> &AC) const {
if (TypeConstraint *TC = getPlaceholderTypeConstraint())
AC.push_back(TC->getImmediatelyDeclaredConstraint());
if (Expr *E = getPlaceholderTypeConstraint())
AC.push_back(E);
}

// Implement isa/cast/dyncast/etc.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/PropertiesBase.td
Expand Up @@ -99,6 +99,8 @@ def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; }
SubclassPropertyType<"TagDecl", DeclRef>;
def TemplateDeclRef :
SubclassPropertyType<"TemplateDecl", DeclRef>;
def ConceptDeclRef :
SubclassPropertyType<"ConceptDecl", DeclRef>;
def TemplateTypeParmDeclRef :
SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>;
def TemplateTemplateParmDeclRef :
Expand Down
14 changes: 13 additions & 1 deletion clang/include/clang/AST/RecursiveASTVisitor.h
Expand Up @@ -1040,7 +1040,13 @@ DEF_TRAVERSE_TYPE(UnaryTransformType, {
TRY_TO(TraverseType(T->getUnderlyingType()));
})

DEF_TRAVERSE_TYPE(AutoType, { TRY_TO(TraverseType(T->getDeducedType())); })
DEF_TRAVERSE_TYPE(AutoType, {
TRY_TO(TraverseType(T->getDeducedType()));
if (T->isConstrained()) {
TRY_TO(TraverseDecl(T->getTypeConstraintConcept()));
TRY_TO(TraverseTemplateArguments(T->getArgs(), T->getNumArgs()));
}
})
DEF_TRAVERSE_TYPE(DeducedTemplateSpecializationType, {
TRY_TO(TraverseTemplateName(T->getTemplateName()));
TRY_TO(TraverseType(T->getDeducedType()));
Expand Down Expand Up @@ -1287,6 +1293,12 @@ DEF_TRAVERSE_TYPELOC(UnaryTransformType, {

DEF_TRAVERSE_TYPELOC(AutoType, {
TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType()));
if (TL.isConstrained()) {
TRY_TO(TraverseNestedNameSpecifierLoc(TL.getNestedNameSpecifierLoc()));
TRY_TO(TraverseDeclarationNameInfo(TL.getConceptNameInfo()));
for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I)
TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I)));
}
})

DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, {
Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang/AST/TemplateBase.h
Expand Up @@ -637,7 +637,7 @@ struct ASTTemplateArgumentListInfo final
}

static const ASTTemplateArgumentListInfo *
Create(ASTContext &C, const TemplateArgumentListInfo &List);
Create(const ASTContext &C, const TemplateArgumentListInfo &List);
};

/// Represents an explicit template argument list in C++, e.g.,
Expand Down Expand Up @@ -702,6 +702,11 @@ inline const TemplateArgument &
return getArgs()[Idx];
}

inline const TemplateArgument &AutoType::getArg(unsigned Idx) const {
assert(Idx < getNumArgs() && "Template argument out of range");
return getArgs()[Idx];
}

} // namespace clang

#endif // LLVM_CLANG_AST_TEMPLATEBASE_H
74 changes: 56 additions & 18 deletions clang/include/clang/AST/Type.h
Expand Up @@ -58,6 +58,7 @@ namespace clang {

class ExtQuals;
class QualType;
class ConceptDecl;
class TagDecl;
class Type;

Expand Down Expand Up @@ -1683,6 +1684,15 @@ class alignas(8) Type : public ExtQualsTypeCommonBase {
/// Was this placeholder type spelled as 'auto', 'decltype(auto)',
/// or '__auto_type'? AutoTypeKeyword value.
unsigned Keyword : 2;

/// The number of template arguments in the type-constraints, which is
/// expected to be able to hold at least 1024 according to [implimits].
/// However as this limit is somewhat easy to hit with template
/// metaprogramming we'd prefer to keep it as large as possible.
/// At the moment it has been left as a non-bitfield since this type
/// safely fits in 64 bits as an unsigned, so there is no reason to
/// introduce the performance impact of a bitfield.
unsigned NumArgs;
};

class SubstTemplateTypeParmPackTypeBitfields {
Expand Down Expand Up @@ -4814,8 +4824,7 @@ class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode {

/// Common base class for placeholders for types that get replaced by
/// placeholder type deduction: C++11 auto, C++14 decltype(auto), C++17 deduced
/// class template types, and (eventually) constrained type names from the C++
/// Concepts TS.
/// class template types, and constrained type names.
///
/// These types are usually a placeholder for a deduced type. However, before
/// the initializer is attached, or (usually) if the initializer is
Expand Down Expand Up @@ -4860,18 +4869,50 @@ class DeducedType : public Type {
}
};

/// Represents a C++11 auto or C++14 decltype(auto) type.
class AutoType : public DeducedType, public llvm::FoldingSetNode {
/// Represents a C++11 auto or C++14 decltype(auto) type, possibly constrained
/// by a type-constraint.
class alignas(8) AutoType : public DeducedType, public llvm::FoldingSetNode {
friend class ASTContext; // ASTContext creates these

ConceptDecl *TypeConstraintConcept;

AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
bool IsDeducedAsDependent, bool IsDeducedAsPack)
: DeducedType(Auto, DeducedAsType, IsDeducedAsDependent,
IsDeducedAsDependent, IsDeducedAsPack) {
AutoTypeBits.Keyword = (unsigned)Keyword;
bool IsDeducedAsDependent, bool IsDeducedAsPack, ConceptDecl *CD,
ArrayRef<TemplateArgument> TypeConstraintArgs);

const TemplateArgument *getArgBuffer() const {
return reinterpret_cast<const TemplateArgument*>(this+1);
}

TemplateArgument *getArgBuffer() {
return reinterpret_cast<TemplateArgument*>(this+1);
}

public:
/// Retrieve the template arguments.
const TemplateArgument *getArgs() const {
return getArgBuffer();
}

/// Retrieve the number of template arguments.
unsigned getNumArgs() const {
return AutoTypeBits.NumArgs;
}

const TemplateArgument &getArg(unsigned Idx) const; // in TemplateBase.h

ArrayRef<TemplateArgument> getTypeConstraintArguments() const {
return {getArgs(), getNumArgs()};
}

ConceptDecl *getTypeConstraintConcept() const {
return TypeConstraintConcept;
}

bool isConstrained() const {
return TypeConstraintConcept != nullptr;
}

bool isDecltypeAuto() const {
return getKeyword() == AutoTypeKeyword::DecltypeAuto;
}
Expand All @@ -4880,18 +4921,15 @@ class AutoType : public DeducedType, public llvm::FoldingSetNode {
return (AutoTypeKeyword)AutoTypeBits.Keyword;
}

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getDeducedType(), getKeyword(), isDependentType(),
containsUnexpandedParameterPack());
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(),
getTypeConstraintConcept(), getTypeConstraintArguments());
}

static void Profile(llvm::FoldingSetNodeID &ID, QualType Deduced,
AutoTypeKeyword Keyword, bool IsDependent, bool IsPack) {
ID.AddPointer(Deduced.getAsOpaquePtr());
ID.AddInteger((unsigned)Keyword);
ID.AddBoolean(IsDependent);
ID.AddBoolean(IsPack);
}
static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
QualType Deduced, AutoTypeKeyword Keyword,
bool IsDependent, ConceptDecl *CD,
ArrayRef<TemplateArgument> Arguments);

static bool classof(const Type *T) {
return T->getTypeClass() == Auto;
Expand Down

0 comments on commit 6a8cd9f

Please sign in to comment.