diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -88,6 +88,7 @@ class BlockExpr; class BuiltinTemplateDecl; class CharUnits; +class ConceptDecl; class CXXABI; class CXXConstructorDecl; class CXXMethodDecl; @@ -211,7 +212,7 @@ mutable llvm::FoldingSet ObjCObjectPointerTypes; mutable llvm::FoldingSet DependentUnaryTransformTypes; - mutable llvm::FoldingSet AutoTypes; + mutable llvm::ContextualFoldingSet AutoTypes; mutable llvm::FoldingSet DeducedTemplateSpecializationTypes; mutable llvm::FoldingSet AtomicTypes; @@ -1542,7 +1543,9 @@ /// 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 TypeConstraintArgs ={}) const; /// C++11 deduction pattern for 'auto' type. QualType getAutoDeductType() const; diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -548,8 +548,8 @@ } 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(), diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1102,6 +1102,17 @@ /// template. ArrayRef getInjectedTemplateArgs(); + /// Return whether this function template is an abbreviated function template, + /// e.g. `void foo(auto x)` or `template 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); @@ -1215,7 +1226,6 @@ bool ParameterPack, bool HasTypeConstraint = false, Optional NumExpanded = None); - static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, unsigned ID); static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, @@ -1374,7 +1384,8 @@ : public DeclaratorDecl, protected TemplateParmPosition, private llvm::TrailingObjects> { + std::pair, + Expr *> { friend class ASTDeclReader; friend TrailingObjects; @@ -1429,10 +1440,12 @@ ArrayRef 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; @@ -1543,20 +1556,22 @@ 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() : + nullptr; + } + + void setPlaceholderTypeConstraint(Expr *E) { + *getTrailingObjects() = 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. @@ -1566,8 +1581,8 @@ /// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for /// concepts APIs that accept an ArrayRef of constraint expressions. void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { - if (TypeConstraint *TC = getPlaceholderTypeConstraint()) - AC.push_back(TC->getImmediatelyDeclaredConstraint()); + if (Expr *E = getPlaceholderTypeConstraint()) + AC.push_back(E); } // Implement isa/cast/dyncast/etc. diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -99,6 +99,8 @@ SubclassPropertyType<"TagDecl", DeclRef>; def TemplateDeclRef : SubclassPropertyType<"TemplateDecl", DeclRef>; + def ConceptDeclRef : + SubclassPropertyType<"ConceptDecl", DeclRef>; def TemplateTypeParmDeclRef : SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>; def TemplateTemplateParmDeclRef : diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1039,7 +1039,13 @@ 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())); @@ -1286,6 +1292,12 @@ 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, { diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -637,7 +637,7 @@ } 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., @@ -702,6 +702,11 @@ 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 diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -58,6 +58,7 @@ class ExtQuals; class QualType; +class ConceptDecl; class TagDecl; class Type; @@ -1683,6 +1684,15 @@ /// 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 { @@ -4814,8 +4824,7 @@ /// 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 @@ -4860,18 +4869,50 @@ } }; -/// 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 TypeConstraintArgs); + + const TemplateArgument *getArgBuffer() const { + return reinterpret_cast(this+1); + } + + TemplateArgument *getArgBuffer() { + return reinterpret_cast(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 getTypeConstraintArguments() const { + return {getArgs(), getNumArgs()}; + } + + ConceptDecl *getTypeConstraintConcept() const { + return TypeConstraintConcept; + } + + bool isConstrained() const { + return TypeConstraintConcept != nullptr; + } + bool isDecltypeAuto() const { return getKeyword() == AutoTypeKeyword::DecltypeAuto; } @@ -4880,18 +4921,15 @@ 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 Arguments); static bool classof(const Type *T) { return T->getTypeClass() == Auto; diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_TYPELOC_H #define LLVM_CLANG_AST_TYPELOC_H +#include "clang/AST/DeclarationName.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" @@ -34,6 +35,7 @@ class Attr; class ASTContext; class CXXRecordDecl; +class ConceptDecl; class Expr; class ObjCInterfaceDecl; class ObjCProtocolDecl; @@ -181,6 +183,11 @@ /// AttributedTypeLoc, for those type attributes that behave as qualifiers TypeLoc findExplicitQualifierLoc() const; + /// Get the typeloc of an AutoType whose type will be deduced for a variable + /// with an initializer of this type. This looks through declarators like + /// pointer types, but not through decltype or typedefs. + AutoTypeLoc getContainedAutoTypeLoc() const; + /// Initializes this to state that every location in this /// type is the given location. /// @@ -1923,8 +1930,137 @@ : public InheritingConcreteTypeLoc {}; +struct AutoTypeLocInfo : TypeSpecLocInfo { + NestedNameSpecifierLoc NestedNameSpec; + SourceLocation TemplateKWLoc; + SourceLocation ConceptNameLoc; + NamedDecl *FoundDecl; + SourceLocation LAngleLoc; + SourceLocation RAngleLoc; +}; + class AutoTypeLoc - : public InheritingConcreteTypeLoc { + : public ConcreteTypeLoc { +public: + AutoTypeKeyword getAutoKeyword() const { + return getTypePtr()->getKeyword(); + } + + bool isConstrained() const { + return getTypePtr()->isConstrained(); + } + + const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { + return getLocalData()->NestedNameSpec; + } + + void setNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + getLocalData()->NestedNameSpec = NNS; + } + + SourceLocation getTemplateKWLoc() const { + return getLocalData()->TemplateKWLoc; + } + + void setTemplateKWLoc(SourceLocation Loc) { + getLocalData()->TemplateKWLoc = Loc; + } + + SourceLocation getConceptNameLoc() const { + return getLocalData()->ConceptNameLoc; + } + + void setConceptNameLoc(SourceLocation Loc) { + getLocalData()->ConceptNameLoc = Loc; + } + + NamedDecl *getFoundDecl() const { + return getLocalData()->FoundDecl; + } + + void setFoundDecl(NamedDecl *D) { + getLocalData()->FoundDecl = D; + } + + ConceptDecl *getNamedConcept() const { + return getTypePtr()->getTypeConstraintConcept(); + } + + DeclarationNameInfo getConceptNameInfo() const; + + bool hasExplicitTemplateArgs() const { + return getLocalData()->LAngleLoc.isValid(); + } + + SourceLocation getLAngleLoc() const { + return this->getLocalData()->LAngleLoc; + } + + void setLAngleLoc(SourceLocation Loc) { + this->getLocalData()->LAngleLoc = Loc; + } + + SourceLocation getRAngleLoc() const { + return this->getLocalData()->RAngleLoc; + } + + void setRAngleLoc(SourceLocation Loc) { + this->getLocalData()->RAngleLoc = Loc; + } + + unsigned getNumArgs() const { + return getTypePtr()->getNumArgs(); + } + + void setArgLocInfo(unsigned i, TemplateArgumentLocInfo AI) { + getArgInfos()[i] = AI; + } + + TemplateArgumentLocInfo getArgLocInfo(unsigned i) const { + return getArgInfos()[i]; + } + + TemplateArgumentLoc getArgLoc(unsigned i) const { + return TemplateArgumentLoc(getTypePtr()->getTypeConstraintArguments()[i], + getArgLocInfo(i)); + } + + SourceRange getLocalSourceRange() const { + return{ + isConstrained() + ? (getNestedNameSpecifierLoc() + ? getNestedNameSpecifierLoc().getBeginLoc() + : (getTemplateKWLoc().isValid() + ? getTemplateKWLoc() + : getConceptNameLoc())) + : getNameLoc(), + getNameLoc() + }; + } + + void copy(AutoTypeLoc Loc) { + unsigned size = getFullDataSize(); + assert(size == Loc.getFullDataSize()); + memcpy(Data, Loc.Data, size); + } + + void initializeLocal(ASTContext &Context, SourceLocation Loc); + + unsigned getExtraLocalDataSize() const { + return getNumArgs() * sizeof(TemplateArgumentLocInfo); + } + + unsigned getExtraLocalDataAlignment() const { + return alignof(TemplateArgumentLocInfo); + } + +private: + TemplateArgumentLocInfo *getArgInfos() const { + return static_cast(getExtraLocalData()); + } }; class DeducedTemplateSpecializationTypeLoc diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -395,6 +395,13 @@ def : Property<"keyword", AutoTypeKeyword> { let Read = [{ node->getKeyword() }]; } + def : Property<"typeConstraintConcept", Optional> { + let Read = [{ makeOptionalFromPointer( + const_cast(node->getTypeConstraintConcept())) }]; + } + def : Property<"typeConstraintArguments", Array> { + let Read = [{ node->getTypeConstraintArguments() }]; + } // FIXME: better enumerated value // Only really required when the deduced type is null def : Property<"dependence", UInt32> { @@ -406,7 +413,9 @@ def : Creator<[{ return ctx.getAutoType(makeNullableFromOptional(deducedType), keyword, /*isDependentWithoutDeducedType*/ dependence > 0, - /*isPackWithoutDeducedType*/ dependence > 1); + /*isPackWithoutDeducedType*/ dependence > 1, + makePointerFromOptional(typeConstraintConcept), + typeConstraintArguments); }]>; } diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1344,6 +1344,10 @@ def ext_concept_legacy_bool_keyword : ExtWarn< "ISO C++2a does not permit the 'bool' keyword after 'concept'">, InGroup>; +def err_placeholder_missing_auto_after_type_constraint : Error< + "ISO C++2a requires 'auto' after a concept name for placeholders">; +def err_placeholder_decltype_non_auto : Error< + "expected 'decltype(auto)' or 'auto' after concept name">; } } // end of Parser diagnostics diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2102,7 +2102,8 @@ "|in template argument|in typedef|in type alias|in function return type" "|in conversion function type|here|in lambda parameter" "|in type allocated by 'new'|in K&R-style function parameter" - "|in template parameter|in friend declaration}1">; + "|in template parameter|in friend declaration|in function prototype that is " + "not a function declaration}1">; def err_dependent_deduced_tst : Error< "typename specifier refers to " "%select{class template|function template|variable template|alias template|" @@ -2609,6 +2610,9 @@ "same concept">; def note_ambiguous_atomic_constraints_similar_expression : Note< "similar constraint expression here">; +def err_unsupported_placeholder_constraint : Error< + "constrained placeholder types other than simple 'auto' on non-type template " + "parameters not supported yet">; def err_template_different_requires_clause : Error< "requires clause differs in template redeclaration">; @@ -2623,6 +2627,8 @@ def err_type_constraint_missing_arguments : Error< "%0 requires more than 1 template argument; provide the remaining arguments " "explicitly to use it here">; +def err_placeholder_constraints_not_satisfied : Error< + "deduced type %0 does not satisfy %1">; // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3079,7 +3079,7 @@ SourceLocation &RAngleLoc); bool ParseTemplateParameterList(unsigned Depth, SmallVectorImpl &TemplateParams); - bool isStartOfTemplateTypeParameter(bool &ScopeError); + bool isStartOfTemplateTypeParameter(bool &Error); NamedDecl *ParseTemplateParameter(unsigned Depth, unsigned Position); NamedDecl *ParseTypeParameter(unsigned Depth, unsigned Position); NamedDecl *ParseTemplateTemplateParameter(unsigned Depth, unsigned Position); diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -349,6 +349,7 @@ unsigned TypeSpecOwned : 1; unsigned TypeSpecPipe : 1; unsigned TypeSpecSat : 1; + unsigned ConstrainedAuto : 1; // type-qualifiers unsigned TypeQualifiers : 5; // Bitwise OR of TQ. @@ -369,6 +370,7 @@ UnionParsedType TypeRep; Decl *DeclRep; Expr *ExprRep; + TemplateIdAnnotation *TemplateIdRep; }; /// ExplicitSpecifier - Store information about explicit spicifer. @@ -413,6 +415,9 @@ static bool isExprRep(TST T) { return (T == TST_typeofExpr || T == TST_decltype); } + static bool isTemplateIdRep(TST T) { + return (T == TST_auto || T == TST_decltype_auto); + } DeclSpec(const DeclSpec &) = delete; void operator=(const DeclSpec &) = delete; @@ -430,7 +435,8 @@ TypeSpecComplex(TSC_unspecified), TypeSpecSign(TSS_unspecified), TypeSpecType(TST_unspecified), TypeAltiVecVector(false), TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false), - TypeSpecPipe(false), TypeSpecSat(false), TypeQualifiers(TQ_unspecified), + TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false), + TypeQualifiers(TQ_unspecified), FS_inline_specified(false), FS_forceinline_specified(false), FS_virtual_specified(false), FS_noreturn_specified(false), Friend_specified(false), ConstexprSpecifier(CSK_unspecified), @@ -478,6 +484,7 @@ bool isTypeRep() const { return isTypeRep((TST) TypeSpecType); } bool isTypeSpecPipe() const { return TypeSpecPipe; } bool isTypeSpecSat() const { return TypeSpecSat; } + bool isConstrainedAuto() const { return ConstrainedAuto; } ParsedType getRepAsType() const { assert(isTypeRep((TST) TypeSpecType) && "DeclSpec does not store a type"); @@ -491,6 +498,11 @@ assert(isExprRep((TST) TypeSpecType) && "DeclSpec does not store an expr"); return ExprRep; } + TemplateIdAnnotation *getRepAsTemplateId() const { + assert(isTemplateIdRep((TST) TypeSpecType) && + "DeclSpec does not store a template id"); + return TemplateIdRep; + } CXXScopeSpec &getTypeSpecScope() { return TypeScope; } const CXXScopeSpec &getTypeSpecScope() const { return TypeScope; } @@ -666,6 +678,9 @@ SourceLocation TagNameLoc, const char *&PrevSpec, unsigned &DiagID, Decl *Rep, bool Owned, const PrintingPolicy &Policy); + bool SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID, TemplateIdAnnotation *Rep, + const PrintingPolicy &Policy); bool SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, Expr *Rep, @@ -1830,6 +1845,14 @@ /// requires-clause, or null if no such clause was specified. Expr *TrailingRequiresClause; + /// If this declarator declares a template, its template parameter lists. + ArrayRef TemplateParameterLists; + + /// If the declarator declares an abbreviated function template, the innermost + /// template parameter list containing the invented and explicit template + /// parameters (if any). + TemplateParameterList *InventedTemplateParameterList; + #ifndef _MSC_VER union { #endif @@ -1860,7 +1883,8 @@ Redeclaration(false), Extension(false), ObjCIvar(false), ObjCWeakProperty(false), InlineStorageUsed(false), Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr), - TrailingRequiresClause(nullptr) {} + TrailingRequiresClause(nullptr), + InventedTemplateParameterList(nullptr) {} ~Declarator() { clear(); @@ -2422,6 +2446,30 @@ return TrailingRequiresClause != nullptr; } + /// Sets the template parameter lists that preceded the declarator. + void setTemplateParameterLists(ArrayRef TPLs) { + TemplateParameterLists = TPLs; + } + + /// The template parameter lists that preceded the declarator. + ArrayRef getTemplateParameterLists() const { + return TemplateParameterLists; + } + + /// Sets the template parameter list generated from the explicit template + /// parameters along with any invented template parameters from + /// placeholder-typed parameters. + void setInventedTemplateParameterList(TemplateParameterList *Invented) { + InventedTemplateParameterList = Invented; + } + + /// The template parameter list generated from the explicit template + /// parameters along with any invented template parameters from + /// placeholder-typed parameters, if there were any such parameters. + TemplateParameterList * getInventedTemplateParameterList() const { + return InventedTemplateParameterList; + } + /// takeAttributes - Takes attributes from the given parsed-attributes /// set and add them to this declarator. /// @@ -2622,6 +2670,26 @@ } }; +struct InventedTemplateParameterInfo { + /// The number of parameters in the template parameter list that were + /// explicitly specified by the user, as opposed to being invented by use + /// of an auto parameter. + unsigned NumExplicitTemplateParams = 0; + + /// If this is a generic lambda or abbreviated function template, use this + /// as the depth of each 'auto' parameter, during initial AST construction. + unsigned AutoTemplateParameterDepth = 0; + + /// Store the list of the template parameters for a generic lambda or an + /// abbreviated function template. + /// If this is a generic lambda or abbreviated function template, this holds + /// the explicit template parameters followed by the auto parameters + /// converted into TemplateTypeParmDecls. + /// It can be used to construct the generic lambda or abbreviated template's + /// template parameter list during initial AST construction. + SmallVector TemplateParams; +}; + } // end namespace clang #endif // LLVM_CLANG_SEMA_DECLSPEC_H diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h --- a/clang/include/clang/Sema/Scope.h +++ b/clang/include/clang/Sema/Scope.h @@ -385,6 +385,12 @@ return getFlags() & Scope::FunctionPrototypeScope; } + /// isFunctionDeclarationScope - Return true if this scope is a + /// function prototype scope. + bool isFunctionDeclarationScope() const { + return getFlags() & Scope::FunctionDeclarationScope; + } + /// isAtCatchScope - Return true if this scope is \@catch. bool isAtCatchScope() const { return getFlags() & Scope::AtCatchScope; diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -22,6 +22,7 @@ #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Sema/CleanupInfo.h" +#include "clang/Sema/DeclSpec.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/MapVector.h" @@ -789,7 +790,8 @@ } }; -class LambdaScopeInfo final : public CapturingScopeInfo { +class LambdaScopeInfo final : + public CapturingScopeInfo, public InventedTemplateParameterInfo { public: /// The class that describes the lambda. CXXRecordDecl *Lambda = nullptr; @@ -823,25 +825,9 @@ /// Packs introduced by this lambda, if any. SmallVector LocalPacks; - /// If this is a generic lambda, use this as the depth of - /// each 'auto' parameter, during initial AST construction. - unsigned AutoTemplateParameterDepth = 0; - - /// The number of parameters in the template parameter list that were - /// explicitly specified by the user, as opposed to being invented by use - /// of an auto parameter. - unsigned NumExplicitTemplateParams = 0; - /// Source range covering the explicit template parameter list (if it exists). SourceRange ExplicitTemplateParamsRange; - /// Store the list of the template parameters for a generic lambda. - /// If this is a generic lambda, this holds the explicit template parameters - /// followed by the auto parameters converted into TemplateTypeParmDecls. - /// It can be used to construct the generic lambda's template parameter list - /// during initial AST construction. - SmallVector TemplateParams; - /// If this is a generic lambda, and the template parameter /// list has been created (from the TemplateParams) then store /// a reference to it (cache it to avoid reconstructing it). diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -619,6 +619,13 @@ /// function, block, and method scopes that are currently active. SmallVector FunctionScopes; + /// Stack containing information needed when in C++2a an 'auto' is encountered + /// in a function declaration parameter type specifier in order to invent a + /// corresponding template parameter in the enclosing abbreviated function + /// template. This information is also present in LambdaScopeInfo, stored in + /// the FunctionScopes stack. + SmallVector InventedParameterInfos; + typedef LazyVector ExtVectorDeclsType; @@ -1424,6 +1431,11 @@ /// Retrieve the module loader associated with the preprocessor. ModuleLoader &getModuleLoader() const; + /// Invent a new identifier for parameters of abbreviated templates. + IdentifierInfo * + InventAbbreviatedTemplateParameterTypeName(IdentifierInfo *ParamName, + unsigned Index); + void emitAndClearUnusedLocalTypedefWarnings(); enum TUFragmentKind { @@ -1518,6 +1530,15 @@ /// WeakTopLevelDeclDecls - access to \#pragma weak-generated Decls SmallVectorImpl &WeakTopLevelDecls() { return WeakTopLevelDecl; } + /// Called before parsing a function declarator belonging to a function + /// declaration. + void ActOnStartFunctionDeclarationDeclarator(Declarator &D, + unsigned TemplateParameterDepth); + + /// Called after parsing a function declarator belonging to a function + /// declaration. + void ActOnFinishFunctionDeclarationDeclarator(Declarator &D); + void ActOnComment(SourceRange Comment); //===--------------------------------------------------------------------===// @@ -1921,6 +1942,8 @@ NC_FunctionTemplate, /// The name was classified as an ADL-only function template name. NC_UndeclaredTemplate, + /// The name was classified as a concept name. + NC_Concept, }; class NameClassification { @@ -1985,6 +2008,12 @@ return Result; } + static NameClassification Concept(TemplateName Name) { + NameClassification Result(NC_Concept); + Result.Template = Name; + return Result; + } + static NameClassification UndeclaredTemplate(TemplateName Name) { NameClassification Result(NC_UndeclaredTemplate); Result.Template = Name; @@ -2010,7 +2039,8 @@ TemplateName getTemplateName() const { assert(Kind == NC_TypeTemplate || Kind == NC_FunctionTemplate || - Kind == NC_VarTemplate || Kind == NC_UndeclaredTemplate); + Kind == NC_VarTemplate || Kind == NC_Concept || + Kind == NC_UndeclaredTemplate); return Template; } @@ -2022,6 +2052,8 @@ return TNK_Function_template; case NC_VarTemplate: return TNK_Var_template; + case NC_Concept: + return TNK_Concept_template; case NC_UndeclaredTemplate: return TNK_Undeclared_template; default: @@ -6884,6 +6916,10 @@ TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc); + bool AttachTypeConstraint(AutoTypeLoc TL, + NonTypeTemplateParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc); + QualType CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI, SourceLocation Loc); QualType CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc); @@ -6933,7 +6969,8 @@ SourceLocation DeclStartLoc, SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId, ArrayRef ParamLists, - bool IsFriend, bool &IsMemberSpecialization, bool &Invalid); + bool IsFriend, bool &IsMemberSpecialization, bool &Invalid, + bool SuppressDiagnostic = false); DeclResult CheckClassTemplate( Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, @@ -7794,10 +7831,12 @@ DeduceAutoResult DeduceAutoType(TypeSourceInfo *AutoType, Expr *&Initializer, QualType &Result, - Optional DependentDeductionDepth = None); + Optional DependentDeductionDepth = None, + bool IgnoreConstraints = false); DeduceAutoResult DeduceAutoType(TypeLoc AutoTypeLoc, Expr *&Initializer, QualType &Result, - Optional DependentDeductionDepth = None); + Optional DependentDeductionDepth = None, + bool IgnoreConstraints = false); void DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init); bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, bool Diagnose = true); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -716,6 +716,61 @@ RequiresClause->Profile(ID, C, /*Canonical=*/true); } +static Expr * +canonicalizeImmediatelyDeclaredConstraint(const ASTContext &C, Expr *IDC, + QualType ConstrainedType) { + // This is a bit ugly - we need to form a new immediately-declared + // constraint that references the new parameter; this would ideally + // require semantic analysis (e.g. template struct S {}; - the + // converted arguments of C could be an argument pack if C is + // declared as template concept C = ...). + // We don't have semantic analysis here so we dig deep into the + // ready-made constraint expr and change the thing manually. + ConceptSpecializationExpr *CSE; + if (const auto *Fold = dyn_cast(IDC)) + CSE = cast(Fold->getLHS()); + else + CSE = cast(IDC); + ArrayRef OldConverted = CSE->getTemplateArguments(); + SmallVector NewConverted; + NewConverted.reserve(OldConverted.size()); + if (OldConverted.front().getKind() == TemplateArgument::Pack) { + // The case: + // template concept C = true; + // template T> struct S; -> constraint is C<{T, int}> + NewConverted.push_back(ConstrainedType); + for (auto &Arg : OldConverted.front().pack_elements().drop_front(1)) + NewConverted.push_back(Arg); + TemplateArgument NewPack(NewConverted); + + NewConverted.clear(); + NewConverted.push_back(NewPack); + assert(OldConverted.size() == 1 && + "Template parameter pack should be the last parameter"); + } else { + assert(OldConverted.front().getKind() == TemplateArgument::Type && + "Unexpected first argument kind for immediately-declared " + "constraint"); + NewConverted.push_back(ConstrainedType); + for (auto &Arg : OldConverted.drop_front(1)) + NewConverted.push_back(Arg); + } + Expr *NewIDC = ConceptSpecializationExpr::Create( + C, NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(), + CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(), + CSE->getNamedConcept(), + // Actually canonicalizing a TemplateArgumentLoc is difficult so we + // simply omit the ArgsAsWritten + /*ArgsAsWritten=*/nullptr, NewConverted, nullptr); + + if (auto *OrigFold = dyn_cast(IDC)) + NewIDC = new (C) CXXFoldExpr(OrigFold->getType(), SourceLocation(), NewIDC, + BinaryOperatorKind::BO_LAnd, + SourceLocation(), /*RHS=*/nullptr, + SourceLocation(), /*NumExpansions=*/None); + return NewIDC; +} + TemplateTemplateParmDecl * ASTContext::getCanonicalTemplateTemplateParmDecl( TemplateTemplateParmDecl *TTP) const { @@ -743,68 +798,23 @@ TTP->isExpandedParameterPack() ? llvm::Optional(TTP->getNumExpansionParameters()) : None); if (const auto *TC = TTP->getTypeConstraint()) { - // This is a bit ugly - we need to form a new immediately-declared - // constraint that references the new parameter; this would ideally - // require semantic analysis (e.g. template struct S {}; - the - // converted arguments of C could be an argument pack if C is - // declared as template concept C = ...). - // We don't have semantic analysis here so we dig deep into the - // ready-made constraint expr and change the thing manually. - Expr *IDC = TC->getImmediatelyDeclaredConstraint(); - ConceptSpecializationExpr *CSE; - if (const auto *Fold = dyn_cast(IDC)) - CSE = cast(Fold->getLHS()); - else - CSE = cast(IDC); - ArrayRef OldConverted = CSE->getTemplateArguments(); - SmallVector NewConverted; - NewConverted.reserve(OldConverted.size()); - QualType ParamAsArgument(NewTTP->getTypeForDecl(), 0); - if (OldConverted.front().getKind() == TemplateArgument::Pack) { - // The case: - // template concept C = true; - // template T> struct S; -> constraint is C<{T, int}> - NewConverted.push_back(ParamAsArgument); - for (auto &Arg : OldConverted.front().pack_elements().drop_front(1)) - NewConverted.push_back(Arg); - TemplateArgument NewPack(NewConverted); - - NewConverted.clear(); - NewConverted.push_back(NewPack); - assert(OldConverted.size() == 1 && - "Template parameter pack should be the last parameter"); - } else { - assert(OldConverted.front().getKind() == TemplateArgument::Type && - "Unexpected first argument kind for immediately-declared " - "constraint"); - NewConverted.push_back(ParamAsArgument); - for (auto &Arg : OldConverted.drop_front(1)) - NewConverted.push_back(Arg); - } - Expr *NewIDC = ConceptSpecializationExpr::Create(*this, - NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(), - CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(), - CSE->getNamedConcept(), - // Actually canonicalizing a TemplateArgumentLoc is difficult so we - // simply omit the ArgsAsWritten - /*ArgsAsWritten=*/nullptr, NewConverted, nullptr); - - if (auto *OrigFold = dyn_cast(IDC)) - NewIDC = new (*this) CXXFoldExpr(OrigFold->getType(), - SourceLocation(), NewIDC, - BinaryOperatorKind::BO_LAnd, - SourceLocation(), /*RHS=*/nullptr, - SourceLocation(), - /*NumExpansions=*/None); - + Expr *NewIDC = canonicalizeImmediatelyDeclaredConstraint( + *this, TC->getImmediatelyDeclaredConstraint(), + ParamAsArgument); + TemplateArgumentListInfo CanonArgsAsWritten; + if (auto *Args = TC->getTemplateArgsAsWritten()) + for (const auto &ArgLoc : Args->arguments()) + CanonArgsAsWritten.addArgument( + TemplateArgumentLoc(ArgLoc.getArgument(), + TemplateArgumentLocInfo())); NewTTP->setTypeConstraint( NestedNameSpecifierLoc(), DeclarationNameInfo(TC->getNamedConcept()->getDeclName(), SourceLocation()), /*FoundDecl=*/nullptr, // Actually canonicalizing a TemplateArgumentLoc is difficult so we // simply omit the ArgsAsWritten - CSE->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); + TC->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); } CanonParams.push_back(NewTTP); } else if (const auto *NTTP = dyn_cast(*P)) { @@ -839,6 +849,11 @@ NTTP->isParameterPack(), TInfo); } + if (AutoType *AT = T->getContainedAutoType()) + if (AT->isConstrained()) + Param->setPlaceholderTypeConstraint( + canonicalizeImmediatelyDeclaredConstraint( + *this, NTTP->getPlaceholderTypeConstraint(), T)); CanonParams.push_back(Param); } else @@ -943,7 +958,7 @@ Builtin::Context &builtins) : ConstantArrayTypes(this_()), FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()), - DependentTemplateSpecializationTypes(this_()), + DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()), SubstTemplateTemplateParmPacks(this_()), CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), @@ -5124,21 +5139,29 @@ /// getAutoType - Return the uniqued reference to the 'auto' type which has been /// deduced to the given type, or to the canonical undeduced 'auto' type, or the /// canonical deduced-but-dependent 'auto' type. -QualType ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, - bool IsDependent, bool IsPack) const { +QualType +ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, + bool IsDependent, bool IsPack, + ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs) const { assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack"); - if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && !IsDependent) + if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && + !TypeConstraintConcept && !IsDependent) return getAutoDeductType(); // Look in the folding set for an existing type. void *InsertPos = nullptr; llvm::FoldingSetNodeID ID; - AutoType::Profile(ID, DeducedType, Keyword, IsDependent, IsPack); + AutoType::Profile(ID, *this, DeducedType, Keyword, IsDependent, + TypeConstraintConcept, TypeConstraintArgs); if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(AT, 0); - auto *AT = new (*this, TypeAlignment) - AutoType(DeducedType, Keyword, IsDependent, IsPack); + void *Mem = Allocate(sizeof(AutoType) + + sizeof(TemplateArgument) * TypeConstraintArgs.size(), + TypeAlignment); + auto *AT = new (Mem) AutoType(DeducedType, Keyword, IsDependent, IsPack, + TypeConstraintConcept, TypeConstraintArgs); Types.push_back(AT); if (InsertPos) AutoTypes.InsertNode(AT, InsertPos); @@ -5200,7 +5223,8 @@ if (AutoDeductTy.isNull()) AutoDeductTy = QualType( new (*this, TypeAlignment) AutoType(QualType(), AutoTypeKeyword::Auto, - /*dependent*/false, /*pack*/false), + /*dependent*/false, /*pack*/false, + /*concept*/nullptr, /*args*/{}), 0); return AutoDeductTy; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1366,9 +1366,21 @@ if (!ToDeducedTypeOrErr) return ToDeducedTypeOrErr.takeError(); - return Importer.getToContext().getAutoType(*ToDeducedTypeOrErr, - T->getKeyword(), - /*IsDependent*/false); + ExpectedDecl ToTypeConstraintConcept = import(T->getTypeConstraintConcept()); + if (!ToTypeConstraintConcept) + return ToTypeConstraintConcept.takeError(); + + SmallVector ToTemplateArgs; + ArrayRef FromTemplateArgs = T->getTypeConstraintArguments(); + if (Error Err = ImportTemplateArguments(FromTemplateArgs.data(), + FromTemplateArgs.size(), + ToTemplateArgs)) + return std::move(Err); + + return Importer.getToContext().getAutoType( + *ToDeducedTypeOrErr, T->getKeyword(), /*IsDependent*/false, + /*IsPack=*/false, cast(*ToTypeConstraintConcept), + ToTemplateArgs); } ExpectedType ASTNodeImporter::VisitInjectedClassNameType( diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -729,11 +729,31 @@ return false; break; - case Type::Auto: - if (!IsStructurallyEquivalent(Context, cast(T1)->getDeducedType(), - cast(T2)->getDeducedType())) + case Type::Auto: { + auto *Auto1 = cast(T1); + auto *Auto2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Auto1->getDeducedType(), + Auto2->getDeducedType())) return false; + if (Auto1->isConstrained() != Auto2->isConstrained()) + return false; + if (Auto1->isConstrained()) { + if (Auto1->getTypeConstraintConcept() != + Auto2->getTypeConstraintConcept()) + return false; + ArrayRef Auto1Args = + Auto1->getTypeConstraintArguments(); + ArrayRef Auto2Args = + Auto2->getTypeConstraintArguments(); + if (Auto1Args.size() != Auto2Args.size()) + return false; + for (unsigned I = 0, N = Auto1Args.size(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Auto1Args[I], Auto2Args[I])) + return false; + } + } break; + } case Type::DeducedTemplateSpecialization: { const auto *DT1 = cast(T1); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -164,10 +164,15 @@ void TemplateParameterList:: getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { if (HasConstrainedParameters) - for (const NamedDecl *Param : *this) - if (const auto *TTP = dyn_cast(Param)) + for (const NamedDecl *Param : *this) { + if (const auto *TTP = dyn_cast(Param)) { if (const auto *TC = TTP->getTypeConstraint()) AC.push_back(TC->getImmediatelyDeclaredConstraint()); + } else if (const auto *NTTP = dyn_cast(Param)) { + if (const Expr *E = NTTP->getPlaceholderTypeConstraint()) + AC.push_back(E); + } + } if (HasRequiresClause) AC.push_back(getRequiresClause()); } @@ -684,8 +689,13 @@ unsigned D, unsigned P, IdentifierInfo *Id, QualType T, bool ParameterPack, TypeSourceInfo *TInfo) { - return new (C, DC) NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, - T, ParameterPack, TInfo); + AutoType *AT = TInfo->getType()->getContainedAutoType(); + return new (C, DC, + additionalSizeToAlloc, + Expr *>(0, + AT && AT->isConstrained() ? 1 : 0)) + NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, ParameterPack, + TInfo); } NonTypeTemplateParmDecl *NonTypeTemplateParmDecl::Create( @@ -693,26 +703,34 @@ SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, ArrayRef ExpandedTypes, ArrayRef ExpandedTInfos) { + AutoType *AT = TInfo->getType()->getContainedAutoType(); return new (C, DC, - additionalSizeToAlloc>( - ExpandedTypes.size())) + additionalSizeToAlloc, + Expr *>( + ExpandedTypes.size(), AT && AT->isConstrained() ? 1 : 0)) NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, TInfo, ExpandedTypes, ExpandedTInfos); } NonTypeTemplateParmDecl * -NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) NonTypeTemplateParmDecl(nullptr, SourceLocation(), - SourceLocation(), 0, 0, nullptr, - QualType(), false, nullptr); +NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, + bool HasTypeConstraint) { + return new (C, ID, additionalSizeToAlloc, + Expr *>(0, + HasTypeConstraint ? 1 : 0)) + NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), + 0, 0, nullptr, QualType(), false, nullptr); } NonTypeTemplateParmDecl * NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, - unsigned NumExpandedTypes) { + unsigned NumExpandedTypes, + bool HasTypeConstraint) { auto *NTTP = - new (C, ID, additionalSizeToAlloc>( - NumExpandedTypes)) + new (C, ID, additionalSizeToAlloc, + Expr *>( + NumExpandedTypes, HasTypeConstraint ? 1 : 0)) NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), 0, 0, nullptr, QualType(), nullptr, None, None); diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -857,6 +857,13 @@ void VisitAutoType(const AutoType *T) { ID.AddInteger((unsigned)T->getKeyword()); + ID.AddInteger(T->isConstrained()); + if (T->isConstrained()) { + AddDecl(T->getTypeConstraintConcept()); + ID.AddInteger(T->getNumArgs()); + for (const auto &TA : T->getTypeConstraintArguments()) + Hash.AddTemplateArgument(TA); + } VisitDeducedType(T); } diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -561,7 +561,7 @@ } const ASTTemplateArgumentListInfo * -ASTTemplateArgumentListInfo::Create(ASTContext &C, +ASTTemplateArgumentListInfo::Create(const ASTContext &C, const TemplateArgumentListInfo &List) { std::size_t size = totalSizeToAlloc(List.size()); void *Mem = C.Allocate(size, alignof(ASTTemplateArgumentListInfo)); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1201,6 +1201,11 @@ OS << " decltype(auto)"; if (!T->isDeduced()) OS << " undeduced"; + if (T->isConstrained()) { + dumpDeclRef(T->getTypeConstraintConcept()); + for (const auto &Arg : T->getTypeConstraintArguments()) + VisitTemplateArgument(Arg); + } } void TextNodeDumper::VisitTemplateSpecializationType( diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1114,7 +1114,9 @@ return QualType(T, 0); return Ctx.getAutoType(deducedType, T->getKeyword(), - T->isDependentType()); + T->isDependentType(), /*IsPack=*/false, + T->getTypeConstraintConcept(), + T->getTypeConstraintArguments()); } // FIXME: Non-trivial to implement, but important for C++ @@ -4158,3 +4160,35 @@ /*HasUnsignedPadding=*/false); APFixedPoint(Val, FXSema).toString(Str); } + +AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, + bool IsDeducedAsDependent, bool IsDeducedAsPack, + ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs) + : DeducedType(Auto, DeducedAsType, IsDeducedAsDependent, + IsDeducedAsDependent, IsDeducedAsPack) { + AutoTypeBits.Keyword = (unsigned)Keyword; + AutoTypeBits.NumArgs = TypeConstraintArgs.size(); + this->TypeConstraintConcept = TypeConstraintConcept; + if (TypeConstraintConcept) { + TemplateArgument *ArgBuffer = getArgBuffer(); + for (const TemplateArgument &Arg : TypeConstraintArgs) { + if (Arg.containsUnexpandedParameterPack()) + setContainsUnexpandedParameterPack(); + + new (ArgBuffer++) TemplateArgument(Arg); + } + } +} + +void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, + QualType Deduced, AutoTypeKeyword Keyword, + bool IsDependent, ConceptDecl *CD, + ArrayRef Arguments) { + ID.AddPointer(Deduced.getAsOpaquePtr()); + ID.AddInteger((unsigned)Keyword); + ID.AddBoolean(IsDependent); + ID.AddPointer(CD); + for (const TemplateArgument &Arg : Arguments) + Arg.Profile(ID, Context); +} diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/TypeLoc.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" @@ -589,3 +590,97 @@ } } } + +DeclarationNameInfo AutoTypeLoc::getConceptNameInfo() const { + return DeclarationNameInfo(getNamedConcept()->getDeclName(), + getLocalData()->ConceptNameLoc); +} + +void AutoTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { + setNestedNameSpecifierLoc(NestedNameSpecifierLoc()); + setTemplateKWLoc(Loc); + setConceptNameLoc(Loc); + setFoundDecl(nullptr); + setRAngleLoc(Loc); + setLAngleLoc(Loc); + TemplateSpecializationTypeLoc::initializeArgLocs(Context, getNumArgs(), + getTypePtr()->getArgs(), + getArgInfos(), Loc); + setNameLoc(Loc); +} + + +namespace { + + class GetContainedAutoTypeLocVisitor : + public TypeLocVisitor { + public: + using TypeLocVisitor::Visit; + + TypeLoc VisitAutoTypeLoc(AutoTypeLoc TL) { + return TL; + } + + // Only these types can contain the desired 'auto' type. + + TypeLoc VisitElaboratedTypeLoc(ElaboratedTypeLoc T) { + return Visit(T.getNamedTypeLoc()); + } + + TypeLoc VisitQualifiedTypeLoc(QualifiedTypeLoc T) { + return Visit(T.getUnqualifiedLoc()); + } + + TypeLoc VisitPointerTypeLoc(PointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitBlockPointerTypeLoc(BlockPointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitReferenceTypeLoc(ReferenceTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitMemberPointerTypeLoc(MemberPointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitArrayTypeLoc(ArrayTypeLoc T) { + return Visit(T.getElementLoc()); + } + + TypeLoc VisitFunctionTypeLoc(FunctionTypeLoc T) { + return Visit(T.getReturnLoc()); + } + + TypeLoc VisitParenTypeLoc(ParenTypeLoc T) { + return Visit(T.getInnerLoc()); + } + + TypeLoc VisitAttributedTypeLoc(AttributedTypeLoc T) { + return Visit(T.getModifiedLoc()); + } + + TypeLoc VisitMacroQualifiedTypeLoc(MacroQualifiedTypeLoc T) { + return Visit(T.getInnerLoc()); + } + + TypeLoc VisitAdjustedTypeLoc(AdjustedTypeLoc T) { + return Visit(T.getOriginalLoc()); + } + + TypeLoc VisitPackExpansionTypeLoc(PackExpansionTypeLoc T) { + return Visit(T.getPatternLoc()); + } + }; + +} // namespace + +AutoTypeLoc TypeLoc::getContainedAutoTypeLoc() const { + TypeLoc Res = GetContainedAutoTypeLocVisitor().Visit(*this); + if (Res.isNull()) + return AutoTypeLoc(); + return Res.getAs(); +} diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1046,6 +1046,13 @@ if (!T->getDeducedType().isNull()) { printBefore(T->getDeducedType(), OS); } else { + if (T->isConstrained()) { + OS << T->getTypeConstraintConcept()->getName(); + auto Args = T->getTypeConstraintArguments(); + if (!Args.empty()) + printTemplateArgumentList(OS, Args, Policy); + OS << ' '; + } switch (T->getKeyword()) { case AutoTypeKeyword::Auto: OS << "auto"; break; case AutoTypeKeyword::DecltypeAuto: OS << "decltype(auto)"; break; @@ -1234,20 +1241,18 @@ void TypePrinter::printTemplateTypeParmBefore(const TemplateTypeParmType *T, raw_ostream &OS) { - if (IdentifierInfo *Id = T->getIdentifier()) - OS << Id->getName(); - else { - bool IsLambdaAutoParam = false; - if (auto D = T->getDecl()) { - if (auto M = dyn_cast_or_null(D->getDeclContext())) - IsLambdaAutoParam = D->isImplicit() && M->getParent()->isLambda(); + TemplateTypeParmDecl *D = T->getDecl(); + if (D && D->isImplicit()) { + if (auto *TC = D->getTypeConstraint()) { + TC->print(OS, Policy); + OS << ' '; } + OS << "auto"; + } else if (IdentifierInfo *Id = T->getIdentifier()) + OS << Id->getName(); + else + OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); - if (IsLambdaAutoParam) - OS << "auto"; - else - OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); - } spaceBeforePlaceHolder(OS); } diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -21,7 +21,7 @@ # Create custom target to generate the VC revision include. add_custom_command(OUTPUT "${version_inc}" - DEPENDS "${llvm_vc}" "${clang_vc}" "${generate_vcs_version_script}" + DEPENDS "${generate_vcs_version_script}" COMMAND ${CMAKE_COMMAND} "-DNAMES=\"LLVM;CLANG\"" "-DLLVM_SOURCE_DIR=${llvm_source_dir}" "-DCLANG_SOURCE_DIR=${clang_source_dir}" diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -133,7 +133,9 @@ LexedMethod* LM = new LexedMethod(this, FnD); getCurrentClass().LateParsedDeclarations.push_back(LM); - LM->TemplateScope = getCurScope()->isTemplateParamScope(); + LM->TemplateScope = getCurScope()->isTemplateParamScope() || + (FnD && isa(FnD) && + cast(FnD)->isAbbreviated()); CachedTokens &Toks = LM->Toks; tok::TokenKind kind = Tok.getKind(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2962,6 +2962,7 @@ case Sema::NC_ContextIndependentExpr: case Sema::NC_VarTemplate: case Sema::NC_FunctionTemplate: + case Sema::NC_Concept: // Might be a redeclaration of a prior entity. break; } @@ -3193,6 +3194,18 @@ continue; } + if (Next.is(tok::annot_template_id) && + static_cast(Next.getAnnotationValue()) + ->Kind == TNK_Concept_template && + GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype)) { + DS.getTypeSpecScope() = SS; + // This is a qualified placeholder-specifier, e.g., ::C auto ... + // Consume the scope annotation and continue to consume the template-id + // as a placeholder-specifier. + ConsumeAnnotationToken(); + continue; + } + if (Next.is(tok::annot_typename)) { DS.getTypeSpecScope() = SS; ConsumeAnnotationToken(); // The C++ scope. @@ -3235,6 +3248,10 @@ // C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the // typename. if (!TypeRep) { + if (TryAnnotateTypeConstraint(SS)) + goto DoneWithDeclSpec; + if (isTypeConstraintAnnotation()) + continue; // Eat the scope spec so the identifier is current. ConsumeAnnotationToken(); ParsedAttributesWithRange Attrs(AttrFactory); @@ -3384,6 +3401,11 @@ // If this is not a typedef name, don't parse it as part of the declspec, // it must be an implicit int or an error. if (!TypeRep) { + CXXScopeSpec SS; + if (TryAnnotateTypeConstraint(SS)) + goto DoneWithDeclSpec; + if (isTypeConstraintAnnotation()) + continue; ParsedAttributesWithRange Attrs(AttrFactory); if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) { if (!Attrs.empty()) { @@ -3433,9 +3455,51 @@ continue; } - // type-name + // type-name or placeholder-specifier case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + if (TemplateId->Kind == TNK_Concept_template) { + if (NextToken().is(tok::identifier)) { + Diag(Loc, diag::err_placeholder_missing_auto_after_type_constraint) + << FixItHint::CreateInsertion(NextToken().getLocation(), "auto"); + // Attempt to continue as if 'auto' was placed here. + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + break; + } + if (!NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) + goto DoneWithDeclSpec; + ConsumeAnnotationToken(); + SourceLocation AutoLoc = Tok.getLocation(); + if (TryConsumeToken(tok::kw_decltype)) { + BalancedDelimiterTracker Tracker(*this, tok::l_paren); + if (Tracker.consumeOpen()) { + // Something like `void foo(Iterator decltype i)` + Diag(Tok, diag::err_expected) << tok::l_paren; + } else { + if (!TryConsumeToken(tok::kw_auto)) { + // Something like `void foo(Iterator decltype(int) i)` + Tracker.skipToEnd(); + Diag(Tok, diag::err_placeholder_decltype_non_auto) + << FixItHint::CreateReplacement(SourceRange(AutoLoc, + Tok.getLocation()), + "auto"); + } else { + Tracker.consumeClose(); + } + } + ConsumedEnd = Tok.getLocation(); + // Even if something went wrong above, continue as if we've seen + // `decltype(auto)`. + isInvalid = DS.SetTypeSpecType(TST_decltype_auto, Loc, PrevSpec, + DiagID, TemplateId, Policy); + } else { + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + } + break; + } + if (TemplateId->Kind != TNK_Type_template && TemplateId->Kind != TNK_Undeclared_template) { // This template-id does not refer to a type name, so we're @@ -6026,11 +6090,12 @@ while (1) { if (Tok.is(tok::l_paren)) { + bool IsFunctionDeclaration = D.isFunctionDeclaratorAFunctionDeclaration(); // Enter function-declaration scope, limiting any declarators to the // function prototype scope, including parameter declarators. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope| - (D.isFunctionDeclaratorAFunctionDeclaration() + (IsFunctionDeclaration ? Scope::FunctionDeclarationScope : 0)); // The paren may be part of a C++ direct initializer, eg. "int x(1);". @@ -6049,7 +6114,12 @@ ParsedAttributes attrs(AttrFactory); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); + if (IsFunctionDeclaration) + Actions.ActOnStartFunctionDeclarationDeclarator(D, + TemplateParameterDepth); ParseFunctionDeclarator(D, attrs, T, IsAmbiguous); + if (IsFunctionDeclaration) + Actions.ActOnFinishFunctionDeclarationDeclarator(D); PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2642,6 +2642,8 @@ } ParsingDeclarator DeclaratorInfo(*this, DS, DeclaratorContext::MemberContext); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(TemplateParams); VirtSpecifiers VS; // Hold late-parsed attributes so we can attach a Decl to them later. diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -240,6 +240,8 @@ // Parse the declarator. ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams); ParseDeclarator(DeclaratorInfo); // Error parsing the declarator? if (!DeclaratorInfo.hasName()) { @@ -499,10 +501,9 @@ /// Determine whether the parser is at the start of a template /// type parameter. -/// \param ScopeError will receive true if there was an error parsing a -/// scope specifier at the current location. -bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { - ScopeError = false; +/// \param Error will receive true if there was a parsing error. +bool Parser::isStartOfTemplateTypeParameter(bool &Error) { + Error = false; if (Tok.is(tok::kw_class)) { // "class" may be the start of an elaborated-type-specifier or a // type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter. @@ -535,34 +536,12 @@ } } - bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); CXXScopeSpec SS; - ScopeError = - ParseOptionalCXXScopeSpecifier(SS, ParsedType(), - /*EnteringContext=*/false, - /*MayBePseudoDestructor=*/nullptr, - // If this is not a type-constraint, then - // this scope-spec is part of the typename - // of a non-type template parameter - /*IsTypename=*/true, /*LastII=*/nullptr, - // We won't find concepts in - // non-namespaces anyway, so might as well - // parse this correctly for possible type - // names. - /*OnlyNamespace=*/false); - if (ScopeError) - return false; - if (TryAnnotateTypeConstraint(SS)) - return false; - bool IsTypeConstraint = isTypeConstraintAnnotation(); - if (!IsTypeConstraint && SS.isNotEmpty()) { - // This isn't a type-constraint but we've already parsed this scope - // specifier - annotate it. - AnnotateScopeToken(SS, /*isNewAnnotation=*/!WasScopeAnnotation); + if (TryAnnotateTypeConstraint(SS)) { + Error = true; return false; } - - if (IsTypeConstraint && + if (isTypeConstraintAnnotation() && // Next token might be 'auto' or 'decltype', indicating that this // type-constraint is in fact part of a placeholder-type-specifier of a // non-type template parameter. @@ -632,8 +611,8 @@ // type-constraint is in fact part of a placeholder-type-specifier of a // non-type template parameter. - bool ScopeError; - if (isStartOfTemplateTypeParameter(ScopeError)) { + bool Error; + if (isStartOfTemplateTypeParameter(Error)) { // Is there just a typo in the input code? ('typedef' instead of // 'typename') if (Tok.is(tok::kw_typedef)) { @@ -650,7 +629,7 @@ return ParseTypeParameter(Depth, Position); } - if (ScopeError) { + if (Error) { // We return an invalid parameter as opposed to null to avoid having bogus // diagnostics about an empty template parameter list. // FIXME: Fix ParseTemplateParameterList to better handle nullptr results @@ -699,12 +678,51 @@ /// /// \returns true if an error occurred, and false otherwise. bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { - if (!getLangOpts().ConceptsTS || Tok.isNot(tok::identifier)) + if (!getLangOpts().ConceptsTS) return false; + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, then + // this scope-spec is part of the typename + // of a non-type template parameter + /*IsTypename=*/true, /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as well + // parse this correctly for possible type + // names. + /*OnlyNamespace=*/false)) + return true; + if (!Tok.isOneOf(tok::identifier, tok::annot_cxxscope)) + return false; + if (Tok.is(tok::annot_template_id)) { + // Parsing of the CXXScopeSpec might have already annotated this for us. + const auto *ExistingAnnot = + static_cast(Tok.getAnnotationValue()); + return ExistingAnnot->Kind == TNK_Concept_template; + } UnqualifiedId PossibleConceptName; - PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), - Tok.getLocation()); + if (Tok.is(tok::annot_cxxscope)) { + if (NextToken().is(tok::annot_template_id)) { + const auto *ExistingAnnot = + static_cast(NextToken().getAnnotationValue()); + if (ExistingAnnot->Kind == TNK_Concept_template) { + // Consume the scope specifier and return to the caller so + // isTypeConstraintAnnotation will work. + ConsumeAnnotationToken(); + return false; + } + } + if (NextToken().isNot(tok::identifier)) + return false; + PossibleConceptName.setIdentifier(NextToken().getIdentifierInfo(), + NextToken().getLocation()); + } else { + assert(Tok.is(tok::identifier)); + PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), + Tok.getLocation()); + } TemplateTy PossibleConcept; bool MemberOfUnknownSpecialization = false; @@ -720,6 +738,9 @@ if (!PossibleConcept || TNK != TNK_Concept_template) return false; + if (Tok.is(tok::annot_cxxscope)) + ConsumeAnnotationToken(); + // At this point we're sure we're dealing with a constrained parameter. It // may or may not have a template parameter list following the concept name. return AnnotateTemplateIdToken(PossibleConcept, TNK, SS, diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1516,6 +1516,16 @@ *InvalidAsDeclSpec = NextToken().is(tok::l_paren); return TPResult::Ambiguous; } + // We have a placeholder-constraint (we check for 'auto' or 'decltype' to + // distinguish 'C;' from 'C auto c = 1;') + if (TemplateId->Kind == TNK_Concept_template && + NextToken().isOneOf(tok::kw_auto, tok::kw_decltype, + // If we have an identifier here, the user probably forgot the + // 'auto' in the placeholder constraint, e.g. 'C x = 2;' + // This will be diagnosed nicely later, so disambiguate as a + // declaration. + tok::identifier)) + return TPResult::True; if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; @@ -1529,6 +1539,9 @@ if (TryAnnotateTypeOrScopeToken()) return TPResult::Error; if (!Tok.is(tok::annot_typename)) { + if (Tok.is(tok::annot_template_id)) + // Probably a type-constraint + return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); // If the next token is an identifier or a type qualifier, then this // can't possibly be a valid expression either. if (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier)) { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1136,6 +1136,7 @@ // Poison SEH identifiers so they are flagged as illegal in function bodies. PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true); const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); // If this is C90 and the declspecs were completely missing, fudge in an // implicit int. We do this here because this is the only place where @@ -1262,6 +1263,15 @@ // safe because we're always the sole owner. D.getMutableDeclSpec().abort(); + // With abbreviated function templates - we need to explicitly add depth to + // account for the implicit template parameter list induced by the template. + if (auto *Template = dyn_cast_or_null(Res)) + if (Template->isAbbreviated() && + Template->getTemplateParameters()->getParam(0)->isImplicit()) + // First template parameter is implicit - meaning no explicit template + // parameter list was specified. + CurTemplateDepthTracker.addDepth(1); + if (TryConsumeToken(tok::equal)) { assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='"); @@ -1732,6 +1742,19 @@ return ANK_Error; return ANK_Success; } + case Sema::NC_Concept: { + if (Next.is(tok::less)) + // We have a concept name followed by '<'. + ConsumeToken(); + UnqualifiedId Id; + Id.setIdentifier(Name, NameLoc); + if (AnnotateTemplateIdToken( + TemplateTy::make(Classification.getTemplateName()), + Classification.getTemplateNameKind(), SS, SourceLocation(), Id, + /*AllowTypeAnnotation=*/false, /*TypeConstraint=*/true)) + return ANK_Error; + return ANK_Success; + } } // Unable to classify the name, but maybe we can annotate a scope specifier. @@ -1985,6 +2008,9 @@ // annotation token to a type annotation token now. AnnotateTemplateIdTokenAsType(); return false; + } else if (TemplateId->Kind == TNK_Concept_template) { + // Leave the template-id annotation as-is for type-constraints etc. + return false; } } diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -784,6 +784,15 @@ return false; } +bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID, TemplateIdAnnotation *Rep, + const PrintingPolicy &Policy) { + assert(T == TST_auto || T == TST_decltype_auto); + ConstrainedAuto = true; + TemplateIdRep = Rep; + return SetTypeSpecType(T, Loc, PrevSpec, DiagID, Policy); +} + bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -52,6 +52,21 @@ ModuleLoader &Sema::getModuleLoader() const { return PP.getModuleLoader(); } +IdentifierInfo * +Sema::InventAbbreviatedTemplateParameterTypeName(IdentifierInfo *ParamName, + unsigned int Index) { + std::string InventedName; + llvm::raw_string_ostream OS(InventedName); + + if (!ParamName) + OS << "auto:" << Index + 1; + else + OS << ParamName->getName() << ":auto"; + + OS.flush(); + return &Context.Idents.get(OS.str()); +} + PrintingPolicy Sema::getPrintingPolicy(const ASTContext &Context, const Preprocessor &PP) { PrintingPolicy Policy = Context.getPrintingPolicy(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -1153,6 +1154,10 @@ return ParsedType::make(T); } + if (isa(FirstDecl)) + return NameClassification::Concept( + TemplateName(cast(FirstDecl))); + // We can have a type template here if we're classifying a template argument. if (isa(FirstDecl) && !isa(FirstDecl) && !isa(FirstDecl)) @@ -8654,11 +8659,21 @@ NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, - MultiTemplateParamsArg TemplateParamLists, + MultiTemplateParamsArg TemplateParamListsRef, bool &AddToScope) { QualType R = TInfo->getType(); assert(R->isFunctionType()); + SmallVector TemplateParamLists; + for (TemplateParameterList *TPL : TemplateParamListsRef) + TemplateParamLists.push_back(TPL); + if (TemplateParameterList *Invented = D.getInventedTemplateParameterList()) { + if (!TemplateParamLists.empty() && + Invented->getDepth() == TemplateParamLists.back()->getDepth()) + TemplateParamLists.back() = Invented; + else + TemplateParamLists.push_back(Invented); + } // TODO: consider using NameInfo for diagnostic. DeclarationNameInfo NameInfo = GetNameForDeclarator(D); @@ -8738,15 +8753,16 @@ // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. bool Invalid = false; - if (TemplateParameterList *TemplateParams = - MatchTemplateParametersToScopeSpecifier( - D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), - D.getCXXScopeSpec(), - D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId - ? D.getName().TemplateId - : nullptr, - TemplateParamLists, isFriend, isMemberSpecialization, - Invalid)) { + TemplateParameterList *TemplateParams = + MatchTemplateParametersToScopeSpecifier( + D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), + D.getCXXScopeSpec(), + D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId + ? D.getName().TemplateId + : nullptr, + TemplateParamLists, isFriend, isMemberSpecialization, + Invalid); + if (TemplateParams) { if (TemplateParams->size() > 0) { // This is a function template @@ -8779,7 +8795,8 @@ // For source fidelity, store the other template param lists. if (TemplateParamLists.size() > 1) { NewFD->setTemplateParameterListsInfo(Context, - TemplateParamLists.drop_back(1)); + ArrayRef(TemplateParamLists) + .drop_back(1)); } } else { // This is a function template specialization. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17386,3 +17386,50 @@ return NewPD; } + +void Sema::ActOnStartFunctionDeclarationDeclarator( + Declarator &Declarator, unsigned TemplateParameterDepth) { + auto &Info = InventedParameterInfos.emplace_back(); + TemplateParameterList *ExplicitParams = nullptr; + ArrayRef ExplicitLists = + Declarator.getTemplateParameterLists(); + if (!ExplicitLists.empty()) { + bool IsMemberSpecialization, IsInvalid; + ExplicitParams = MatchTemplateParametersToScopeSpecifier( + Declarator.getBeginLoc(), Declarator.getIdentifierLoc(), + Declarator.getCXXScopeSpec(), /*TemplateId=*/nullptr, + ExplicitLists, /*IsFriend=*/false, IsMemberSpecialization, IsInvalid, + /*SuppressDiagnostic=*/true); + } + if (ExplicitParams) { + Info.AutoTemplateParameterDepth = ExplicitParams->getDepth(); + for (NamedDecl *Param : *ExplicitParams) + Info.TemplateParams.push_back(Param); + Info.NumExplicitTemplateParams = ExplicitParams->size(); + } else { + Info.AutoTemplateParameterDepth = TemplateParameterDepth; + Info.NumExplicitTemplateParams = 0; + } +} + +void Sema::ActOnFinishFunctionDeclarationDeclarator(Declarator &Declarator) { + auto &FSI = InventedParameterInfos.back(); + if (FSI.TemplateParams.size() > FSI.NumExplicitTemplateParams) { + if (FSI.NumExplicitTemplateParams != 0) { + TemplateParameterList *ExplicitParams = + Declarator.getTemplateParameterLists().back(); + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, ExplicitParams->getTemplateLoc(), + ExplicitParams->getLAngleLoc(), FSI.TemplateParams, + ExplicitParams->getRAngleLoc(), + ExplicitParams->getRequiresClause())); + } else { + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, SourceLocation(), SourceLocation(), FSI.TemplateParams, + SourceLocation(), /*RequiresClause=*/nullptr)); + } + } + InventedParameterInfos.pop_back(); +} diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -791,7 +791,8 @@ // deduce against. QualType DeductType = Context.getAutoDeductType(); TypeLocBuilder TLB; - TLB.pushTypeSpec(DeductType).setNameLoc(Loc); + AutoTypeLoc TL = TLB.push(DeductType); + TL.setNameLoc(Loc); if (ByRef) { DeductType = BuildReferenceType(DeductType, true, Loc, Id); assert(!DeductType.isNull() && "can't build reference to auto"); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1088,6 +1088,50 @@ ConstrainedParameter, EllipsisLoc); } +template +static ExprResult formImmediatelyDeclaredConstraint( + Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, SourceLocation LAngleLoc, + SourceLocation RAngleLoc, QualType ConstrainedType, + SourceLocation ParamNameLoc, ArgumentLocAppender Appender, + SourceLocation EllipsisLoc) { + + TemplateArgumentListInfo ConstraintArgs; + ConstraintArgs.addArgument( + S.getTrivialTemplateArgumentLoc(TemplateArgument(ConstrainedType), + /*NTTPType=*/QualType(), ParamNameLoc)); + + ConstraintArgs.setRAngleLoc(RAngleLoc); + ConstraintArgs.setLAngleLoc(LAngleLoc); + Appender(ConstraintArgs); + + // C++2a [temp.param]p4: + // [...] This constraint-expression E is called the immediately-declared + // constraint of T. [...] + CXXScopeSpec SS; + SS.Adopt(NS); + ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId( + SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, + /*FoundDecl=*/NamedConcept, NamedConcept, &ConstraintArgs); + if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid()) + return ImmediatelyDeclaredConstraint; + + // C++2a [temp.param]p4: + // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). + // + // We have the following case: + // + // template concept C1 = true; + // template struct s1; + // + // The constraint: (C1 && ...) + return S.BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(), + ImmediatelyDeclaredConstraint.get(), BO_LAnd, + EllipsisLoc, /*RHS=*/nullptr, + /*RParenLoc=*/SourceLocation(), + /*NumExpansions=*/None); +} + /// Attach a type-constraint to a template parameter. /// \returns true if an error occured. This can happen if the /// immediately-declared constraint could not be formed (e.g. incorrect number @@ -1106,51 +1150,21 @@ *TemplateArgs) : nullptr; QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0); - TemplateArgumentListInfo ConstraintArgs; - ConstraintArgs.addArgument( - TemplateArgumentLoc( - TemplateArgument(ParamAsArgument), - TemplateArgumentLocInfo( - Context.getTrivialTypeSourceInfo(ParamAsArgument, - ConstrainedParameter->getLocation())))); - if (TemplateArgs) { - ConstraintArgs.setRAngleLoc(TemplateArgs->getRAngleLoc()); - ConstraintArgs.setLAngleLoc(TemplateArgs->getLAngleLoc()); - for (const TemplateArgumentLoc &ArgLoc : TemplateArgs->arguments()) - ConstraintArgs.addArgument(ArgLoc); - } - // C++2a [temp.param]p4: - // [...] This constraint-expression E is called the immediately-declared - // constraint of T. [...] - CXXScopeSpec SS; - SS.Adopt(NS); - ExprResult ImmediatelyDeclaredConstraint = CheckConceptTemplateId(SS, - /*TemplateKWLoc=*/SourceLocation(), NameInfo, /*FoundDecl=*/NamedConcept, - NamedConcept, &ConstraintArgs); + ExprResult ImmediatelyDeclaredConstraint = + formImmediatelyDeclaredConstraint( + *this, NS, NameInfo, NamedConcept, + TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(), + TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(), + ParamAsArgument, ConstrainedParameter->getLocation(), + [&] (TemplateArgumentListInfo &ConstraintArgs) { + if (TemplateArgs) + for (const auto &ArgLoc : TemplateArgs->arguments()) + ConstraintArgs.addArgument(ArgLoc); + }, EllipsisLoc); if (ImmediatelyDeclaredConstraint.isInvalid()) return true; - if (ConstrainedParameter->isParameterPack()) { - // C++2a [temp.param]p4: - // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). - // - // We have the following case: - // - // template concept C1 = true; - // template struct s1; - // - // The constraint: (C1 && ...) - ImmediatelyDeclaredConstraint = - BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(), - ImmediatelyDeclaredConstraint.get(), BO_LAnd, - EllipsisLoc, /*RHS=*/nullptr, - /*RParenLoc=*/SourceLocation(), - /*NumExpansions=*/None).get(); - if (ImmediatelyDeclaredConstraint.isInvalid()) - return true; - } - ConstrainedParameter->setTypeConstraint(NS, NameInfo, /*FoundDecl=*/NamedConcept, NamedConcept, ArgsAsWritten, @@ -1158,6 +1172,38 @@ return false; } +bool Sema::AttachTypeConstraint(AutoTypeLoc TL, NonTypeTemplateParmDecl *NTTP, + SourceLocation EllipsisLoc) { + if (NTTP->getType() != TL.getType() || + TL.getAutoKeyword() != AutoTypeKeyword::Auto) { + Diag(NTTP->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), + diag::err_unsupported_placeholder_constraint) + << NTTP->getTypeSourceInfo()->getTypeLoc().getSourceRange(); + return true; + } + // FIXME: Concepts: This should be the type of the placeholder, but this is + // unclear in the wording right now. + DeclRefExpr *Ref = BuildDeclRefExpr(NTTP, NTTP->getType(), VK_RValue, + NTTP->getLocation()); + if (!Ref) + return true; + ExprResult ImmediatelyDeclaredConstraint = + formImmediatelyDeclaredConstraint( + *this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(), + TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(), + BuildDecltypeType(Ref, NTTP->getLocation()), NTTP->getLocation(), + [&] (TemplateArgumentListInfo &ConstraintArgs) { + for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I) + ConstraintArgs.addArgument(TL.getArgLoc(I)); + }, EllipsisLoc); + if (ImmediatelyDeclaredConstraint.isInvalid() || + !ImmediatelyDeclaredConstraint.isUsable()) + return true; + + NTTP->setPlaceholderTypeConstraint(ImmediatelyDeclaredConstraint.get()); + return false; +} + /// Check that the type of a non-type template parameter is /// well-formed. /// @@ -1319,6 +1365,11 @@ TInfo); Param->setAccess(AS_public); + if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc()) + if (TL.isConstrained()) + if (AttachTypeConstraint(TL, Param, D.getEllipsisLoc())) + Invalid = true; + if (Invalid) Param->setInvalidDecl(); @@ -2762,7 +2813,7 @@ SourceLocation DeclStartLoc, SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId, ArrayRef ParamLists, bool IsFriend, - bool &IsMemberSpecialization, bool &Invalid) { + bool &IsMemberSpecialization, bool &Invalid, bool SuppressDiagnostic) { IsMemberSpecialization = false; Invalid = false; @@ -2870,8 +2921,9 @@ auto CheckExplicitSpecialization = [&](SourceRange Range, bool Recovery) { if (SawNonEmptyTemplateParameterList) { - Diag(DeclLoc, diag::err_specialize_member_of_template) - << !Recovery << Range; + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_specialize_member_of_template) + << !Recovery << Range; Invalid = true; IsMemberSpecialization = false; return true; @@ -2892,9 +2944,10 @@ else ExpectedTemplateLoc = DeclStartLoc; - Diag(DeclLoc, diag::err_template_spec_needs_header) - << Range - << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_template_spec_needs_header) + << Range + << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); return false; }; @@ -2984,12 +3037,13 @@ if (ParamIdx < ParamLists.size()) { if (ParamLists[ParamIdx]->size() > 0) { // The header has template parameters when it shouldn't. Complain. - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - diag::err_template_param_list_matches_nontemplate) - << T - << SourceRange(ParamLists[ParamIdx]->getLAngleLoc(), - ParamLists[ParamIdx]->getRAngleLoc()) - << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + if (!SuppressDiagnostic) + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + diag::err_template_param_list_matches_nontemplate) + << T + << SourceRange(ParamLists[ParamIdx]->getLAngleLoc(), + ParamLists[ParamIdx]->getRAngleLoc()) + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); Invalid = true; return nullptr; } @@ -3025,7 +3079,7 @@ if (ExpectedTemplateParams && !TemplateParameterListsAreEqual(ParamLists[ParamIdx], ExpectedTemplateParams, - true, TPL_TemplateMatch)) + !SuppressDiagnostic, TPL_TemplateMatch)) Invalid = true; if (!Invalid && @@ -3037,9 +3091,10 @@ continue; } - Diag(DeclLoc, diag::err_template_spec_needs_template_parameters) - << T - << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_template_spec_needs_template_parameters) + << T + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); Invalid = true; continue; } @@ -3075,16 +3130,18 @@ AllExplicitSpecHeaders = false; } - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - AllExplicitSpecHeaders ? diag::warn_template_spec_extra_headers - : diag::err_template_spec_extra_headers) - << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), - ParamLists[ParamLists.size() - 2]->getRAngleLoc()); + if (!SuppressDiagnostic) + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + AllExplicitSpecHeaders ? diag::warn_template_spec_extra_headers + : diag::err_template_spec_extra_headers) + << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), + ParamLists[ParamLists.size() - 2]->getRAngleLoc()); // If there was a specialization somewhere, such that 'template<>' is // not required, and there were any 'template<>' headers, note where the // specialization occurred. - if (ExplicitSpecLoc.isValid() && HasAnyExplicitSpecHeader) + if (ExplicitSpecLoc.isValid() && HasAnyExplicitSpecHeader && + !SuppressDiagnostic) Diag(ExplicitSpecLoc, diag::note_explicit_template_spec_does_not_need_header) << NestedTypes.back(); @@ -6530,7 +6587,12 @@ DeductionArg = PE->getPattern(); if (DeduceAutoType( Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation()), - DeductionArg, ParamType, Depth) == DAR_Failed) { + DeductionArg, ParamType, Depth, + // We do not check constraints right now because the + // immediately-declared constraint of the auto type is also an + // associated constraint, and will be checked along with the other + // associated constraints after checking the template argument list. + /*IgnoreConstraints=*/true) == DAR_Failed) { Diag(Arg->getExprLoc(), diag::err_non_type_template_parm_type_deduction_failure) << Param->getDeclName() << Param->getType() << Arg->getType() diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4414,9 +4414,10 @@ QualType Result = SemaRef.Context.getAutoType( Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull(), - ReplacementIsPack); + ReplacementIsPack, TL.getTypePtr()->getTypeConstraintConcept(), + TL.getTypePtr()->getTypeConstraintArguments()); auto NewTL = TLB.push(Result); - NewTL.setNameLoc(TL.getNameLoc()); + NewTL.copy(TL); return Result; } @@ -4451,9 +4452,10 @@ Sema::DeduceAutoResult Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, QualType &Result, - Optional DependentDeductionDepth) { + Optional DependentDeductionDepth, + bool IgnoreConstraints) { return DeduceAutoType(Type->getTypeLoc(), Init, Result, - DependentDeductionDepth); + DependentDeductionDepth, IgnoreConstraints); } /// Attempt to produce an informative diagostic explaining why auto deduction @@ -4481,6 +4483,49 @@ } } +static Sema::DeduceAutoResult +CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, + AutoTypeLoc TypeLoc, QualType Deduced) { + ConstraintSatisfaction Satisfaction; + ConceptDecl *Concept = Type.getTypeConstraintConcept(); + TemplateArgumentListInfo TemplateArgs(TypeLoc.getLAngleLoc(), + TypeLoc.getRAngleLoc()); + TemplateArgs.addArgument( + TemplateArgumentLoc(TemplateArgument(Deduced), + S.Context.getTrivialTypeSourceInfo( + Deduced, TypeLoc.getNameLoc()))); + for (unsigned I = 0, C = TypeLoc.getNumArgs(); I != C; ++I) + TemplateArgs.addArgument(TypeLoc.getArgLoc(I)); + + llvm::SmallVector Converted; + if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, + /*PartialTemplateArgs=*/false, Converted)) + return Sema::DAR_FailedAlreadyDiagnosed; + if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, + Converted, TypeLoc.getLocalSourceRange(), + Satisfaction)) + return Sema::DAR_FailedAlreadyDiagnosed; + if (!Satisfaction.IsSatisfied) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "'" << Concept->getName(); + if (TypeLoc.hasExplicitTemplateArgs()) { + OS << "<"; + for (const auto &Arg : Type.getTypeConstraintArguments()) + Arg.print(S.getPrintingPolicy(), OS); + OS << ">"; + } + OS << "'"; + OS.flush(); + S.Diag(TypeLoc.getConceptNameLoc(), + diag::err_placeholder_constraints_not_satisfied) + << Deduced << Buf << TypeLoc.getLocalSourceRange(); + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + return Sema::DAR_FailedAlreadyDiagnosed; + } + return Sema::DAR_Succeeded; +} + /// Deduce the type for an auto type-specifier (C++11 [dcl.spec.auto]p6) /// /// Note that this is done even if the initializer is dependent. (This is @@ -4495,9 +4540,12 @@ /// dependent cases. This is necessary for template partial ordering with /// 'auto' template parameters. The value specified is the template /// parameter depth at which we should perform 'auto' deduction. +/// \param IgnoreConstraints Set if we should not fail if the deduced type does +/// not satisfy the type-constraint in the auto type. Sema::DeduceAutoResult Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, - Optional DependentDeductionDepth) { + Optional DependentDeductionDepth, + bool IgnoreConstraints) { if (Init->getType()->isNonOverloadPlaceholderType()) { ExprResult NonPlaceholder = CheckPlaceholderExpr(Init); if (NonPlaceholder.isInvalid()) @@ -4538,6 +4586,14 @@ return DAR_FailedAlreadyDiagnosed; // FIXME: Support a non-canonical deduced type for 'auto'. Deduced = Context.getCanonicalType(Deduced); + if (AT->isConstrained() && !IgnoreConstraints) { + auto ConstraintsResult = + CheckDeducedPlaceholderConstraints(*this, *AT, + Type.getContainedAutoTypeLoc(), + Deduced); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } Result = SubstituteDeducedTypeTransform(*this, Deduced).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; @@ -4645,6 +4701,17 @@ return DAR_FailedAlreadyDiagnosed; } + if (const auto *AT = Type.getType()->getAs()) { + if (AT->isConstrained() && !IgnoreConstraints) { + auto ConstraintsResult = + CheckDeducedPlaceholderConstraints(*this, *AT, + Type.getContainedAutoTypeLoc(), + DeducedType); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } + } + Result = SubstituteDeducedTypeTransform(*this, DeducedType).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2685,6 +2685,16 @@ D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getPosition(), D->getIdentifier(), T, D->isParameterPack(), DI); + if (AutoTypeLoc AutoLoc = DI->getTypeLoc().getContainedAutoTypeLoc()) + if (AutoLoc.isConstrained()) + if (SemaRef.AttachTypeConstraint( + AutoLoc, Param, + IsExpandedParameterPack + ? DI->getTypeLoc().getAs() + .getEllipsisLoc() + : SourceLocation())) + Invalid = true; + Param->setAccess(AS_public); Param->setImplicit(D->isImplicit()); if (Invalid) diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TypeLocBuilder.h" +#include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" @@ -27,6 +28,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" @@ -1251,6 +1253,26 @@ return OpenCLAccessAttr::Keyword_read_only; } +static QualType ConvertConstrainedAutoDeclSpecToType(Sema &S, DeclSpec &DS, + AutoTypeKeyword AutoKW) { + assert(DS.isConstrainedAuto()); + TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId(); + TemplateArgumentListInfo TemplateArgsInfo; + TemplateArgsInfo.setLAngleLoc(TemplateId->LAngleLoc); + TemplateArgsInfo.setRAngleLoc(TemplateId->RAngleLoc); + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + llvm::SmallVector TemplateArgs; + for (auto &ArgLoc : TemplateArgsInfo.arguments()) + TemplateArgs.push_back(ArgLoc.getArgument()); + return S.Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false, + /*IsPack=*/false, + cast(TemplateId->Template.get() + .getAsTemplateDecl()), + TemplateArgs); +} + /// Convert the specified declspec to the appropriate type /// object. /// \param state Specifies the declarator containing the declaration specifier @@ -1595,6 +1617,11 @@ break; case DeclSpec::TST_auto: + if (DS.isConstrainedAuto()) { + Result = ConvertConstrainedAutoDeclSpecToType(S, DS, + AutoTypeKeyword::Auto); + break; + } Result = Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false); break; @@ -1603,6 +1630,12 @@ break; case DeclSpec::TST_decltype_auto: + if (DS.isConstrainedAuto()) { + Result = + ConvertConstrainedAutoDeclSpecToType(S, DS, + AutoTypeKeyword::DecltypeAuto); + break; + } Result = Context.getAutoType(QualType(), AutoTypeKeyword::DecltypeAuto, /*IsDependent*/ false); break; @@ -2921,6 +2954,87 @@ D.getDeclSpec().getUnalignedSpecLoc()); } +static void CopyTypeConstraintFromAutoType(Sema &SemaRef, const AutoType *Auto, + AutoTypeLoc AutoLoc, + TemplateTypeParmDecl *TP, + SourceLocation EllipsisLoc) { + + TemplateArgumentListInfo TAL(AutoLoc.getLAngleLoc(), AutoLoc.getRAngleLoc()); + for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) + TAL.addArgument(AutoLoc.getArgLoc(Idx)); + + SemaRef.AttachTypeConstraint( + AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(), + AutoLoc.getNamedConcept(), + AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, TP, EllipsisLoc); +} + +static QualType InventTemplateParameter( + TypeProcessingState &state, QualType T, TypeSourceInfo *TSI, AutoType *Auto, + InventedTemplateParameterInfo &Info) { + Sema &S = state.getSema(); + Declarator &D = state.getDeclarator(); + + const unsigned TemplateParameterDepth = Info.AutoTemplateParameterDepth; + const unsigned AutoParameterPosition = Info.TemplateParams.size(); + const bool IsParameterPack = D.hasEllipsis(); + + // If auto is mentioned in a lambda parameter or abbreviated function + // template context, convert it to a template parameter type. + + // Create the TemplateTypeParmDecl here to retrieve the corresponding + // template parameter type. Template parameters are temporarily added + // to the TU until the associated TemplateDecl is created. + TemplateTypeParmDecl *InventedTemplateParam = + TemplateTypeParmDecl::Create( + S.Context, S.Context.getTranslationUnitDecl(), + /*KeyLoc=*/D.getDeclSpec().getTypeSpecTypeLoc(), + /*NameLoc=*/D.getIdentifierLoc(), + TemplateParameterDepth, AutoParameterPosition, + S.InventAbbreviatedTemplateParameterTypeName( + D.getIdentifier(), AutoParameterPosition), false, + IsParameterPack, /*HasTypeConstraint=*/Auto->isConstrained()); + InventedTemplateParam->setImplicit(); + Info.TemplateParams.push_back(InventedTemplateParam); + // Attach type constraints + if (Auto->isConstrained()) { + if (TSI) { + CopyTypeConstraintFromAutoType( + S, Auto, TSI->getTypeLoc().getContainedAutoTypeLoc(), + InventedTemplateParam, D.getEllipsisLoc()); + } else { + TemplateIdAnnotation *TemplateId = D.getDeclSpec().getRepAsTemplateId(); + TemplateArgumentListInfo TemplateArgsInfo; + if (TemplateId->LAngleLoc.isValid()) { + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + } + S.AttachTypeConstraint( + TemplateId->SS.getWithLocInContext(S.Context), + DeclarationNameInfo(DeclarationName(TemplateId->Name), + TemplateId->TemplateNameLoc), + cast(TemplateId->Template.get().getAsTemplateDecl()), + TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr, + InventedTemplateParam, D.getEllipsisLoc()); + } + } + + // If TSI is nullptr, this is a constrained declspec auto and the type + // constraint will be attached later in TypeSpecLocFiller + + // Replace the 'auto' in the function parameter with this invented + // template type parameter. + // FIXME: Retain some type sugar to indicate that this was written + // as 'auto'? + return state.ReplaceAutoType( + T, QualType(InventedTemplateParam->getTypeForDecl(), 0)); +} + +static TypeSourceInfo * +GetTypeSourceInfoForDeclarator(TypeProcessingState &State, + QualType T, TypeSourceInfo *ReturnTypeInfo); + static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, TypeSourceInfo *&ReturnTypeInfo) { Sema &SemaRef = state.getSema(); @@ -2991,43 +3105,40 @@ break; case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: - case DeclaratorContext::PrototypeContext: Error = 0; break; - case DeclaratorContext::LambdaExprParameterContext: - // In C++14, generic lambdas allow 'auto' in their parameters. - if (!SemaRef.getLangOpts().CPlusPlus14 || - !Auto || Auto->getKeyword() != AutoTypeKeyword::Auto) - Error = 16; - else { - // If auto is mentioned in a lambda parameter context, convert it to a - // template parameter type. - sema::LambdaScopeInfo *LSI = SemaRef.getCurLambda(); - assert(LSI && "No LambdaScopeInfo on the stack!"); - const unsigned TemplateParameterDepth = LSI->AutoTemplateParameterDepth; - const unsigned AutoParameterPosition = LSI->TemplateParams.size(); - const bool IsParameterPack = D.hasEllipsis(); - - // Create the TemplateTypeParmDecl here to retrieve the corresponding - // template parameter type. Template parameters are temporarily added - // to the TU until the associated TemplateDecl is created. - TemplateTypeParmDecl *CorrespondingTemplateParam = - TemplateTypeParmDecl::Create( - SemaRef.Context, SemaRef.Context.getTranslationUnitDecl(), - /*KeyLoc*/ SourceLocation(), /*NameLoc*/ D.getBeginLoc(), - TemplateParameterDepth, AutoParameterPosition, - /*Identifier*/ nullptr, false, IsParameterPack, - /*HasTypeConstraint=*/false); - CorrespondingTemplateParam->setImplicit(); - LSI->TemplateParams.push_back(CorrespondingTemplateParam); - // Replace the 'auto' in the function parameter with this invented - // template type parameter. - // FIXME: Retain some type sugar to indicate that this was written - // as 'auto'. - T = state.ReplaceAutoType( - T, QualType(CorrespondingTemplateParam->getTypeForDecl(), 0)); + case DeclaratorContext::PrototypeContext: + case DeclaratorContext::LambdaExprParameterContext: { + InventedTemplateParameterInfo *Info = nullptr; + if (D.getContext() == DeclaratorContext::PrototypeContext) { + // With concepts we allow 'auto' in function parameters. + if (!SemaRef.getLangOpts().ConceptsTS || !Auto || + Auto->getKeyword() != AutoTypeKeyword::Auto) { + Error = 0; + break; + } else if (!SemaRef.getCurScope()->isFunctionDeclarationScope()) { + Error = 21; + break; + } else if (D.hasTrailingReturnType()) { + // This might be OK, but we'll need to convert the trailing return + // type later. + break; + } + + Info = &SemaRef.InventedParameterInfos.back(); + } else { + // In C++14, generic lambdas allow 'auto' in their parameters. + if (!SemaRef.getLangOpts().CPlusPlus14 || !Auto || + Auto->getKeyword() != AutoTypeKeyword::Auto) { + Error = 16; + break; + } + Info = SemaRef.getCurLambda(); + assert(Info && "No LambdaScopeInfo on the stack!"); } + T = InventTemplateParameter(state, T, nullptr, Auto, *Info); break; + } case DeclaratorContext::MemberContext: { if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static || D.isFunctionDeclarator()) @@ -4028,10 +4139,6 @@ return false; } -static TypeSourceInfo * -GetTypeSourceInfoForDeclarator(TypeProcessingState &State, - QualType T, TypeSourceInfo *ReturnTypeInfo); - static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -4606,7 +4713,8 @@ } else if (D.getContext() != DeclaratorContext::LambdaExprContext && (T.hasQualifiers() || !isa(T) || cast(T)->getKeyword() != - AutoTypeKeyword::Auto)) { + AutoTypeKeyword::Auto || + cast(T)->isConstrained())) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_trailing_return_without_auto) << T << D.getDeclSpec().getSourceRange(); @@ -4617,7 +4725,12 @@ // An error occurred parsing the trailing return type. T = Context.IntTy; D.setInvalidType(true); - } + } else if (S.getLangOpts().ConceptsTS) + // Handle cases like: `auto f() -> auto` or `auto f() -> C auto`. + if (AutoType *Auto = T->getContainedAutoType()) + if (S.getCurScope()->isFunctionDeclarationScope()) + T = InventTemplateParameter(state, T, TInfo, Auto, + S.InventedParameterInfos.back()); } else { // This function type is not the type of the entity being declared, // so checking the 'auto' is not the responsibility of this chunk. @@ -5236,7 +5349,8 @@ // // We represent function parameter packs as function parameters whose // type is a pack expansion. - if (!T->containsUnexpandedParameterPack()) { + if (!T->containsUnexpandedParameterPack() && + (!LangOpts.ConceptsTS || !T->getContainedAutoType())) { S.Diag(D.getEllipsisLoc(), diag::err_function_parameter_pack_without_parameter_packs) << T << D.getSourceRange(); @@ -5444,14 +5558,15 @@ namespace { class TypeSpecLocFiller : public TypeLocVisitor { + Sema &SemaRef; ASTContext &Context; TypeProcessingState &State; const DeclSpec &DS; public: - TypeSpecLocFiller(ASTContext &Context, TypeProcessingState &State, + TypeSpecLocFiller(Sema &S, ASTContext &Context, TypeProcessingState &State, const DeclSpec &DS) - : Context(Context), State(State), DS(DS) {} + : SemaRef(S), Context(Context), State(State), DS(DS) {} void VisitAttributedTypeLoc(AttributedTypeLoc TL) { Visit(TL.getModifiedLoc()); @@ -5579,6 +5694,32 @@ TL.copy( TInfo->getTypeLoc().castAs()); } + void VisitAutoTypeLoc(AutoTypeLoc TL) { + assert(DS.getTypeSpecType() == TST_auto || + DS.getTypeSpecType() == TST_decltype_auto || + DS.getTypeSpecType() == TST_auto_type || + DS.getTypeSpecType() == TST_unspecified); + TL.setNameLoc(DS.getTypeSpecTypeLoc()); + if (!DS.isConstrainedAuto()) + return; + TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId(); + if (TemplateId->SS.isNotEmpty()) + TL.setNestedNameSpecifierLoc( + TemplateId->SS.getWithLocInContext(Context)); + else + TL.setNestedNameSpecifierLoc(NestedNameSpecifierLoc()); + TL.setConceptNameLoc(TemplateId->TemplateNameLoc); + TL.setLAngleLoc(TemplateId->LAngleLoc); + TL.setRAngleLoc(TemplateId->RAngleLoc); + if (TemplateId->NumArgs == 0) + return; + TemplateArgumentListInfo TemplateArgsInfo; + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + SemaRef.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + for (unsigned I = 0; I < TemplateId->NumArgs; ++I) + TL.setArgLocInfo(I, TemplateArgsInfo.arguments()[I].getLocInfo()); + } void VisitTagTypeLoc(TagTypeLoc TL) { TL.setNameLoc(DS.getTypeSpecTypeNameLoc()); } @@ -5848,7 +5989,7 @@ assert(TL.getFullDataSize() == CurrTL.getFullDataSize()); memcpy(CurrTL.getOpaqueData(), TL.getOpaqueData(), TL.getFullDataSize()); } else { - TypeSpecLocFiller(S.Context, State, D.getDeclSpec()).Visit(CurrTL); + TypeSpecLocFiller(S, S.Context, State, D.getDeclSpec()).Visit(CurrTL); } return TInfo; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -941,12 +941,16 @@ /// Build a new C++11 auto type. /// /// By default, builds a new AutoType with the given deduced type. - QualType RebuildAutoType(QualType Deduced, AutoTypeKeyword Keyword) { + QualType RebuildAutoType(QualType Deduced, AutoTypeKeyword Keyword, + ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs) { // Note, IsDependent is always false here: we implicitly convert an 'auto' // which has been deduced to a dependent type into an undeduced 'auto', so // that we'll retry deduction after the transformation. return SemaRef.Context.getAutoType(Deduced, Keyword, - /*IsDependent*/ false); + /*IsDependent*/ false, /*IsPack=*/false, + TypeConstraintConcept, + TypeConstraintArgs); } /// By default, builds a new DeducedTemplateSpecializationType with the given @@ -4456,7 +4460,10 @@ Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(), Qs); T = SemaRef.Context.getAutoType(Deduced, AutoTy->getKeyword(), - AutoTy->isDependentType()); + AutoTy->isDependentType(), + /*isPack=*/false, + AutoTy->getTypeConstraintConcept(), + AutoTy->getTypeConstraintArguments()); } else { // Otherwise, complain about the addition of a qualifier to an // already-qualified type. @@ -5189,21 +5196,29 @@ PackExpansionTypeLoc ExpansionTL = TL.castAs(); TypeLoc Pattern = ExpansionTL.getPatternLoc(); SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); - assert(Unexpanded.size() > 0 && "Could not find parameter packs!"); // Determine whether we should expand the parameter packs. bool ShouldExpand = false; bool RetainExpansion = false; - Optional OrigNumExpansions = - ExpansionTL.getTypePtr()->getNumExpansions(); - NumExpansions = OrigNumExpansions; - if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), - Pattern.getSourceRange(), - Unexpanded, - ShouldExpand, - RetainExpansion, - NumExpansions)) { - return true; + Optional OrigNumExpansions; + if (Unexpanded.size() > 0) { + OrigNumExpansions = ExpansionTL.getTypePtr()->getNumExpansions(); + NumExpansions = OrigNumExpansions; + if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), + Pattern.getSourceRange(), + Unexpanded, + ShouldExpand, + RetainExpansion, + NumExpansions)) { + return true; + } + } else { +#ifndef NDEBUG + const AutoType *AT = + Pattern.getType().getTypePtr()->getContainedAutoType(); + assert((AT && (!AT->isDeduced() || AT->getDeducedType().isNull())) && + "Could not find parameter packs or undeduced auto type!"); +#endif } if (ShouldExpand) { @@ -5263,6 +5278,9 @@ indexAdjustment, NumExpansions, /*ExpectParameterPack=*/true); + assert(NewParm->isParameterPack() && + "Parameter pack no longer a parameter pack after " + "transformation."); } else { NewParm = getDerived().TransformFunctionTypeParam( OldParm, indexAdjustment, None, /*ExpectParameterPack=*/ false); @@ -5767,32 +5785,6 @@ return Result; } -template -QualType TreeTransform::TransformAutoType(TypeLocBuilder &TLB, - AutoTypeLoc TL) { - const AutoType *T = TL.getTypePtr(); - QualType OldDeduced = T->getDeducedType(); - QualType NewDeduced; - if (!OldDeduced.isNull()) { - NewDeduced = getDerived().TransformType(OldDeduced); - if (NewDeduced.isNull()) - return QualType(); - } - - QualType Result = TL.getType(); - if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || - T->isDependentType()) { - Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword()); - if (Result.isNull()) - return QualType(); - } - - AutoTypeLoc NewTL = TLB.push(Result); - NewTL.setNameLoc(TL.getNameLoc()); - - return Result; -} - template QualType TreeTransform::TransformDeducedTemplateSpecializationType( TypeLocBuilder &TLB, DeducedTemplateSpecializationTypeLoc TL) { @@ -6054,6 +6046,71 @@ } }; +template +QualType TreeTransform::TransformAutoType(TypeLocBuilder &TLB, + AutoTypeLoc TL) { + const AutoType *T = TL.getTypePtr(); + QualType OldDeduced = T->getDeducedType(); + QualType NewDeduced; + if (!OldDeduced.isNull()) { + NewDeduced = getDerived().TransformType(OldDeduced); + if (NewDeduced.isNull()) + return QualType(); + } + + ConceptDecl *NewCD = nullptr; + TemplateArgumentListInfo NewTemplateArgs; + NestedNameSpecifierLoc NewNestedNameSpec; + if (TL.getTypePtr()->isConstrained()) { + NewCD = cast_or_null( + getDerived().TransformDecl( + TL.getConceptNameLoc(), + TL.getTypePtr()->getTypeConstraintConcept())); + + NewTemplateArgs.setLAngleLoc(TL.getLAngleLoc()); + NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc()); + typedef TemplateArgumentLocContainerIterator ArgIterator; + if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), + ArgIterator(TL, + TL.getNumArgs()), + NewTemplateArgs)) + return QualType(); + + if (TL.getNestedNameSpecifierLoc()) { + NewNestedNameSpec + = getDerived().TransformNestedNameSpecifierLoc( + TL.getNestedNameSpecifierLoc()); + if (!NewNestedNameSpec) + return QualType(); + } + } + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || + T->isDependentType()) { + llvm::SmallVector NewArgList; + NewArgList.reserve(NewArgList.size()); + for (const auto &ArgLoc : NewTemplateArgs.arguments()) + NewArgList.push_back(ArgLoc.getArgument()); + Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword(), NewCD, + NewArgList); + if (Result.isNull()) + return QualType(); + } + + AutoTypeLoc NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + NewTL.setNestedNameSpecifierLoc(NewNestedNameSpec); + NewTL.setTemplateKWLoc(TL.getTemplateKWLoc()); + NewTL.setConceptNameLoc(TL.getConceptNameLoc()); + NewTL.setFoundDecl(TL.getFoundDecl()); + NewTL.setLAngleLoc(TL.getLAngleLoc()); + NewTL.setRAngleLoc(TL.getRAngleLoc()); + for (unsigned I = 0; I < TL.getNumArgs(); ++I) + NewTL.setArgLocInfo(I, NewTemplateArgs.arguments()[I].getLocInfo()); + + return Result; +} template QualType TreeTransform::TransformTemplateSpecializationType( diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6576,6 +6576,17 @@ void TypeLocReader::VisitAutoTypeLoc(AutoTypeLoc TL) { TL.setNameLoc(readSourceLocation()); + if (Reader.readBool()) { + TL.setNestedNameSpecifierLoc(ReadNestedNameSpecifierLoc()); + TL.setTemplateKWLoc(readSourceLocation()); + TL.setConceptNameLoc(readSourceLocation()); + TL.setFoundDecl(Reader.readDeclAs()); + TL.setLAngleLoc(readSourceLocation()); + TL.setRAngleLoc(readSourceLocation()); + for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i) + TL.setArgLocInfo(i, Reader.readTemplateArgumentLocInfo( + TL.getTypePtr()->getArg(i).getKind())); + } } void TypeLocReader::VisitDeducedTemplateSpecializationTypeLoc( diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2313,12 +2313,12 @@ D->setDeclaredWithTypename(Record.readInt()); - if (Record.readInt()) { + if (Record.readBool()) { NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc(); DeclarationNameInfo DN = Record.readDeclarationNameInfo(); - ConceptDecl *NamedConcept = cast(Record.readDecl()); + ConceptDecl *NamedConcept = Record.readDeclAs(); const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; - if (Record.readInt()) + if (Record.readBool()) ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); Expr *ImmediatelyDeclaredConstraint = Record.readExpr(); D->setTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, NamedConcept, @@ -2336,6 +2336,8 @@ // TemplateParmPosition. D->setDepth(Record.readInt()); D->setPosition(Record.readInt()); + if (D->hasPlaceholderTypeConstraint()) + D->setPlaceholderTypeConstraint(Record.readExpr()); if (D->isExpandedParameterPack()) { auto TypesAndInfos = D->getTrailingObjects>(); @@ -3819,13 +3821,19 @@ HasTypeConstraint); break; } - case DECL_NON_TYPE_TEMPLATE_PARM: - D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID); + case DECL_NON_TYPE_TEMPLATE_PARM: { + bool HasTypeConstraint = Record.readInt(); + D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID, + HasTypeConstraint); break; - case DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK: + } + case DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK: { + bool HasTypeConstraint = Record.readInt(); D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID, - Record.readInt()); + Record.readInt(), + HasTypeConstraint); break; + } case DECL_TEMPLATE_TEMPLATE_PARM: D = TemplateTemplateParmDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -349,6 +349,18 @@ void TypeLocWriter::VisitAutoTypeLoc(AutoTypeLoc TL) { Record.AddSourceLocation(TL.getNameLoc()); + Record.push_back(TL.isConstrained()); + if (TL.isConstrained()) { + Record.AddNestedNameSpecifierLoc(TL.getNestedNameSpecifierLoc()); + Record.AddSourceLocation(TL.getTemplateKWLoc()); + Record.AddSourceLocation(TL.getConceptNameLoc()); + Record.AddDeclRef(TL.getFoundDecl()); + Record.AddSourceLocation(TL.getLAngleLoc()); + Record.AddSourceLocation(TL.getRAngleLoc()); + for (unsigned I = 0; I < TL.getNumArgs(); ++I) + Record.AddTemplateArgumentLocInfo(TL.getTypePtr()->getArg(I).getKind(), + TL.getArgLocInfo(I)); + } } void TypeLocWriter::VisitDeducedTemplateSpecializationTypeLoc( diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1670,6 +1670,8 @@ // For an expanded parameter pack, record the number of expansion types here // so that it's easier for deserialization to allocate the right amount of // memory. + Expr *TypeConstraint = D->getPlaceholderTypeConstraint(); + Record.push_back(!!TypeConstraint); if (D->isExpandedParameterPack()) Record.push_back(D->getNumExpansionTypes()); @@ -1677,6 +1679,8 @@ // TemplateParmPosition. Record.push_back(D->getDepth()); Record.push_back(D->getPosition()); + if (TypeConstraint) + Record.AddStmt(TypeConstraint); if (D->isExpandedParameterPack()) { for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) { diff --git a/clang/test/AST/ast-dump-record-definition-data-json.cpp b/clang/test/AST/ast-dump-record-definition-data-json.cpp --- a/clang/test/AST/ast-dump-record-definition-data-json.cpp +++ b/clang/test/AST/ast-dump-record-definition-data-json.cpp @@ -417,15 +417,24 @@ // CHECK-NEXT: "id": "0x{{.*}}", // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 193, -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, -// CHECK-NEXT: "end": {} +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 193, +// CHECK-NEXT: "col": 29, +// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "auto:1", // CHECK-NEXT: "tagUsed": "class", // CHECK-NEXT: "depth": 0, // CHECK-NEXT: "index": 0 @@ -524,15 +533,24 @@ // CHECK-NEXT: "id": "0x{{.*}}", // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 193, -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, -// CHECK-NEXT: "end": {} +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 193, +// CHECK-NEXT: "col": 29, +// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "auto:1", // CHECK-NEXT: "tagUsed": "class", // CHECK-NEXT: "depth": 0, // CHECK-NEXT: "index": 0 @@ -590,15 +608,24 @@ // CHECK-NEXT: "id": "0x{{.*}}", // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 193, -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, -// CHECK-NEXT: "end": {} +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 193, +// CHECK-NEXT: "col": 29, +// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "auto:1", // CHECK-NEXT: "tagUsed": "class", // CHECK-NEXT: "depth": 0, // CHECK-NEXT: "index": 0 diff --git a/clang/test/CXX/dcl/dcl.fct/p17.cpp b/clang/test/CXX/dcl/dcl.fct/p17.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl/dcl.fct/p17.cpp @@ -0,0 +1,260 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s +template constexpr bool is_same_v = false; +template constexpr bool is_same_v = true; + +template +struct type_list; + +namespace unconstrained { + decltype(auto) f1(auto x) { return x; } + static_assert(is_same_v); + static_assert(is_same_v); + + decltype(auto) f2(auto &x) { return x; } + // expected-note@-1{{candidate function [with x:auto = int] not viable: expects an l-value for 1st argument}} + // expected-note@-2{{candidate function [with x:auto = char] not viable: expects an l-value for 1st argument}} + static_assert(is_same_v); // expected-error{{no matching}} + static_assert(is_same_v); // expected-error{{no matching}} + + decltype(auto) f3(const auto &x) { return x; } + static_assert(is_same_v); + static_assert(is_same_v); + + decltype(auto) f4(auto (*x)(auto y)) { return x; } // expected-error{{'auto' not allowed in function prototype}} + + decltype(auto) f5(void (*x)(decltype(auto) y)) { return x; } // expected-error{{'decltype(auto)' not allowed in function prototype}} + + int return_int(); void return_void(); int foo(int); + + decltype(auto) f6(auto (*x)()) { return x; } + // expected-note@-1{{candidate template ignored: failed template argument deduction}} + static_assert(is_same_v); + static_assert(is_same_v); + using f6c1 = decltype(f6(foo)); // expected-error{{no matching}} + + decltype(auto) f7(auto (*x)() -> int) { return x; } + // expected-note@-1{{candidate function not viable: no known conversion from 'void ()' to 'auto (*)() -> int' for 1st argument}} + // expected-note@-2{{candidate function not viable: no known conversion from 'int (int)' to 'auto (*)() -> int' for 1st argument}} + static_assert(is_same_v); + using f7c1 = decltype(f7(return_void)); // expected-error{{no matching}} + using f7c2 = decltype(f7(foo)); // expected-error{{no matching}} + static_assert(is_same_v); + + decltype(auto) f8(auto... x) { return (x + ...); } + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + decltype(auto) f9(auto &... x) { return (x, ...); } + // expected-note@-1{{candidate function [with x:auto = ] not viable: expects an l-value for 2nd argument}} + using f9c1 = decltype(f9(return_int, 1)); // expected-error{{no matching}} + + decltype(auto) f11(decltype(auto) x) { return x; } // expected-error{{'decltype(auto)' not allowed in function prototype}} + + template + auto f12(auto x, T y) -> type_list; + static_assert(is_same_v>); + static_assert(is_same_v(1, 'c')), type_list>); + + template + auto f13(T x, auto y) -> type_list; + static_assert(is_same_v>); + static_assert(is_same_v(1, 'c')), type_list>); + + template + auto f14(auto y) -> type_list; + static_assert(is_same_v('c')), type_list>); + static_assert(is_same_v('c')), type_list>); + + template + auto f15(auto y, U u) -> type_list; + static_assert(is_same_v('c', nullptr)), type_list>); + static_assert(is_same_v('c', nullptr)), type_list>); + + auto f16(auto x, auto y) -> type_list; + static_assert(is_same_v>); + static_assert(is_same_v('c', 1)), type_list>); + static_assert(is_same_v('c', 1)), type_list>); + + void f17(auto x, auto y) requires (sizeof(x) > 1); + // expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = char, y:auto = int]}} + // expected-note@-2{{because 'sizeof (x) > 1' (1 > 1) evaluated to false}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v('c', 1)), void>); + static_assert(is_same_v('c', 1)), void>); + + void f18(auto... x) requires (sizeof...(x) == 2); + // expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = ]}} + // expected-note@-2{{candidate template ignored: constraints not satisfied [with x:auto = ]}} + // expected-note@-3{{because 'sizeof...(x) == 2' (1 == 2) evaluated to false}} + // expected-note@-4{{because 'sizeof...(x) == 2' (3 == 2) evaluated to false}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + + template + struct S { + constexpr auto f1(auto x, T t) -> decltype(x + t); + + template + constexpr auto f2(U u, auto x, T t) -> decltype(x + u + t); + }; + + template + constexpr auto S::f1(auto x, T t) -> decltype(x + t) { return x + t; } + + template + template + constexpr auto S::f2(auto x, U u, T t) -> decltype(x + u + t) { return x + u + t; } + // expected-error@-1 {{out-of-line definition of 'f2' does not match any declaration in 'S'}} + + template + template + constexpr auto S::f2(U u, auto x, T t) -> decltype(x + u + t) { return x + u + t; } + + template<> + template<> + constexpr auto S::f2(double u, char x, int t) -> double { return 42; } + + static_assert(S{}.f1(1, 2) == 3); + static_assert(S{}.f2(1, 2, '\x00') == 3); + static_assert(S{}.f2(1, 2, '\x00') == 3.); + static_assert(S{}.f2(1, '2', '\x00') == 42); +} + +namespace constrained { + template + concept C = is_same_v; + // expected-note@-1 12{{because}} + template + concept C2 = is_same_v; + // expected-note@-1 12{{because}} + + int i; + const int ci = 1; + char c; + const char cc = 'a'; + int g(int); + char h(int); + + void f1(C auto x); + // expected-note@-1 {{candidate template ignored: constraints not satisfied [with x:auto = }} + // expected-note@-2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f2(C auto &x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f3(const C auto &x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f4(C auto (*x)(C auto y)); // expected-error{{'auto' not allowed}} + void f5(C auto (*x)(int y)); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f6(C auto (*x)() -> int); // expected-error{{function with trailing return type must specify return type 'auto', not 'C auto'}} + void f7(C auto... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f8(C auto &... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f9(const C auto &... x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f10(C decltype(auto) x); + auto f11 = [] (C auto x) { }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + + void f12(C2 auto x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f13(C2 auto &x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f14(const C2 auto &x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + static_assert(is_same_v); + void f15(C2 auto (*x)(C2 auto y)); // expected-error{{'auto' not allowed}} + void f16(C2 auto (*x)(int y)); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f17(C2 auto (*x)() -> int); // expected-error{{function with trailing return type must specify return type 'auto', not 'C2 auto'}} + void f18(C2 auto... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f19(C2 auto &... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f20(const C2 auto &... x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f21(C2 decltype(auto) x); + auto f22 = [] (C2 auto x) { }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + + struct S1 { S1(C auto); }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + // expected-note@-2 2{{candidate constructor}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + struct S2 { S2(C2 auto); }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + // expected-note@-2 2{{candidate constructor}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); +} diff --git a/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp b/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template +concept LargerThan = sizeof(T) > size; +// expected-note@-1 2{{because 'sizeof(char) > 1U' (1 > 1) evaluated to false}} +// expected-note@-2 {{because 'sizeof(int) > 10U' (4 > 10) evaluated to false}} +// expected-note@-3 {{because 'sizeof(int) > 4U' (4 > 4) evaluated to false}} + +template +concept Large = LargerThan; +// expected-note@-1 2{{because 'LargerThan' evaluated to false}} + +namespace X { + template + concept SmallerThan = sizeof(T) < size; + template + concept Small = SmallerThan; +} + +Large auto test1() { // expected-error{{deduced type 'char' does not satisfy 'Large'}} + Large auto i = 'a'; + // expected-error@-1{{deduced type 'char' does not satisfy 'Large'}} + Large auto j = 10; + ::Large auto k = 10; + LargerThan<1> auto l = 10; + ::LargerThan<1> auto m = 10; + LargerThan<10> auto n = 10; + // expected-error@-1{{deduced type 'int' does not satisfy 'LargerThan<10>'}} + X::Small auto o = 'x'; + X::SmallerThan<5> auto p = 1; + return 'a'; +} + +::Large auto test2() { return 10; } +LargerThan<4> auto test3() { return 10; } +// expected-error@-1{{deduced type 'int' does not satisfy 'LargerThan<4>'}} +::LargerThan<2> auto test4() { return 10; } + +Large auto test5() -> void; +// expected-error@-1{{function with trailing return type must specify return type 'auto', not 'Large auto'}} +auto test6() -> Large auto { return 1; } + +X::Small auto test7() { return 'a'; } +X::SmallerThan<5> auto test8() { return 10; } \ No newline at end of file diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s auto l1 = [] (auto x) requires (sizeof(decltype(x)) == 1) { return x; }; -// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = int]}} +// expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = int]}} // expected-note@-2{{because 'sizeof(decltype(x)) == 1' (4 == 1) evaluated to false}} auto l1t1 = l1('a'); @@ -9,8 +9,8 @@ // expected-error@-1{{no matching function for call to object of type '(lambda at}} auto l2 = [] (auto... x) requires ((sizeof(decltype(x)) >= 2) && ...) { return (x + ...); }; -// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = ]}} -// expected-note@-2{{candidate template ignored: constraints not satisfied [with $0 = ]}} +// expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = ]}} +// expected-note@-2{{candidate template ignored: constraints not satisfied [with x:auto = ]}} // expected-note@-3 2{{because 'sizeof(decltype(x)) >= 2' (1 >= 2) evaluated to false}} auto l2t1 = l2('a'); diff --git a/clang/test/CXX/temp/temp.param/p10-2a.cpp b/clang/test/CXX/temp/temp.param/p10-2a.cpp --- a/clang/test/CXX/temp/temp.param/p10-2a.cpp +++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp @@ -94,6 +94,8 @@ // expected-note@-5 {{and 'is_same_v' evaluated to false}} // expected-note@-6 3{{because 'is_same_v' evaluated to false}} // expected-note@-7 3{{and 'is_same_v' evaluated to false}} +// expected-note@-8 2{{because 'is_same_v' evaluated to false}} +// expected-note@-9 2{{and 'is_same_v' evaluated to false}} template T, OneOf U> // expected-note@-1 2{{because 'OneOf' evaluated to false}} @@ -114,4 +116,25 @@ // expected-error@-1 {{constraints not satisfied for alias template 'H' [with Ts = ]}} using h2 = H; // expected-error@-1 {{constraints not satisfied for alias template 'H' [with Ts = ]}} -using h3 = H; \ No newline at end of file +using h3 = H; + +template auto x> +// expected-note@-1 {{because 'OneOf' evaluated to false}} +using I = int; + +using i1 = I<1>; +using i2 = I<'a'>; +using i3 = I; +// expected-error@-1 {{constraints not satisfied for alias template 'I' [with x = nullptr]}} + +template auto... x> +// expected-note@-1 {{because 'OneOf' evaluated to false}} +using J = int; + +using j1 = J<1, 'b'>; +using j2 = J<'a', nullptr>; +// expected-error@-1 {{constraints not satisfied for alias template 'J' [with x = <'a', nullptr>]}} + +template auto &x> +// expected-error@-1 {{constrained placeholder types other than simple 'auto' on non-type template parameters not supported yet}} +using K = int; diff --git a/clang/test/Parser/cxx2a-placeholder-type-constraint.cpp b/clang/test/Parser/cxx2a-placeholder-type-constraint.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/cxx2a-placeholder-type-constraint.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +template +concept C = true; + +int foo() { + C auto a4 = 1; + C<> auto a5 = 1; + C auto a6 = 1; + const C auto &a7 = 1; + const C<> auto &a8 = 1; + const C auto &a9 = 1; + C decltype(auto) a10 = 1; + C<> decltype(auto) a11 = 1; + C decltype(auto) a12 = 1; + const C<> decltype(auto) &a13 = 1; // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}} + // expected-error@-1{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} + const C decltype(auto) &a14 = 1; // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}} + // expected-error@-1{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} + C a15 = 1; + // expected-error@-1{{ISO C++2a requires 'auto' after a concept name for placeholders}} + C decltype a19 = 1; + // expected-error@-1{{expected '('}} + C decltype(1) a20 = 1; + // expected-error@-1{{expected 'decltype(auto)' or 'auto' after concept name}} +} \ No newline at end of file diff --git a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp --- a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp +++ b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp @@ -181,7 +181,7 @@ int (*fp2)(int) = [](auto b) -> int { return b; }; int (*fp3)(char) = [](auto c) -> int { return c; }; char (*fp4)(int) = [](auto d) { return d; }; //expected-error{{no viable conversion}}\ - //expected-note{{candidate function [with $0 = int]}} + //expected-note{{candidate function [with d:auto = int]}} char (*fp5)(char) = [](auto e) -> int { return e; }; //expected-error{{no viable conversion}}\ //expected-note{{candidate template ignored}} diff --git a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp --- a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp +++ b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp @@ -94,6 +94,7 @@ template struct Bar { T x; }; template *P> // expected-error {{use of undeclared identifier 'Xylophone'}} +// expected-note@-1{{template parameter is declared here}} struct Foo { }; typedef int Xylophone;