Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -89,6 +89,7 @@ class BlockExpr; class BuiltinTemplateDecl; class CharUnits; +class ConceptDecl; class CXXABI; class CXXConstructorDecl; class CXXMethodDecl; @@ -213,7 +214,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; @@ -1529,7 +1530,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; Index: include/clang/AST/ASTNodeTraverser.h =================================================================== --- include/clang/AST/ASTNodeTraverser.h +++ include/clang/AST/ASTNodeTraverser.h @@ -525,8 +525,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(), Index: include/clang/AST/DeclTemplate.h =================================================================== --- include/clang/AST/DeclTemplate.h +++ include/clang/AST/DeclTemplate.h @@ -1152,6 +1152,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); @@ -1404,7 +1415,8 @@ : public DeclaratorDecl, protected TemplateParmPosition, private llvm::TrailingObjects> { + std::pair, + Expr *> { friend class ASTDeclReader; friend TrailingObjects; @@ -1459,10 +1471,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; @@ -1573,20 +1587,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. @@ -1596,8 +1612,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. Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -1037,7 +1037,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())); @@ -1282,6 +1288,11 @@ DEF_TRAVERSE_TYPELOC(AutoType, { TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType())); + if (TL.isConstrained()) { + TRY_TO(TraverseNestedNameSpecifierLoc(TL.getNestedNameSpecifierLoc())); + for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) + TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I))); + } }) DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, { Index: include/clang/AST/TemplateBase.h =================================================================== --- include/clang/AST/TemplateBase.h +++ 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 Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -58,6 +58,7 @@ class ExtQuals; class QualType; +class ConceptDecl; class TagDecl; class Type; @@ -1644,6 +1645,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 { @@ -4752,8 +4762,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 @@ -4798,18 +4807,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; } @@ -4818,18 +4859,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; Index: include/clang/AST/TypeLoc.h =================================================================== --- include/clang/AST/TypeLoc.h +++ include/clang/AST/TypeLoc.h @@ -35,6 +35,7 @@ class ASTContext; class CXXRecordDecl; +class ConceptDecl; class Expr; class ObjCInterfaceDecl; class ObjCProtocolDecl; @@ -184,6 +185,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 findAutoTypeLoc() const; + /// Initializes this to state that every location in this /// type is the given location. /// @@ -1941,8 +1947,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 wereArgumentsSpecified() 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 Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -1331,6 +1331,11 @@ 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< + "only 'decltype(auto)' or 'auto' may appear after a concept name for " + "placeholders">; } } // end of Parser diagnostics Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2025,6 +2025,8 @@ def err_auto_not_allowed_in_return_type_requirement : Error< "%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed in expression " "type requirement">; +def err_param_nested_auto_not_allowed : Error< + "'auto' only allowed in top-level function declaration parameters">; def err_dependent_deduced_tst : Error< "typename specifier refers to " "%select{class template|function template|variable template|alias template|" @@ -2546,6 +2548,9 @@ "'%0' in the two declarations is not considered equivalent - move it to a " "concept and reference it from here:">; def note_ambiguous_atomic_constraints_second : Note<"and 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">; @@ -2560,6 +2565,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< Index: include/clang/Sema/DeclSpec.h =================================================================== --- include/clang/Sema/DeclSpec.h +++ 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, Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -6570,6 +6570,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); @@ -7498,10 +7502,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); @@ -11267,6 +11273,14 @@ /// The parser maintains this state here. Scope *CurScope; + /// \brief The parser's current TemplateParameterDepth to assign to the next + /// template parameter scope + /// + /// The parser maintains this state - Sema only reads it during initial AST + /// construction (do not rely on this during any subsequent + /// transformations/instantiations) + const unsigned *ParsingTemplateParameterDepthPtr; + mutable IdentifierInfo *Ident_super; mutable IdentifierInfo *Ident___float128; @@ -11283,6 +11297,11 @@ /// directives, like -Wpragma-pack. sema::SemaPPCallbacks *SemaPPCallbackHandler; + const unsigned &getParsingTemplateParameterDepth() const { + assert(ParsingTemplateParameterDepthPtr); + return *ParsingTemplateParameterDepthPtr; + } + protected: friend class Parser; friend class InitializationSequence; Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -636,6 +636,60 @@ 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(), SourceLocation(), + DeclarationNameInfo(CSE->getNamedConcept()->getDeclName(), + SourceLocation()), nullptr, CSE->getNamedConcept(), + CSE->getTemplateArgsAsWritten(), NewConverted, ConstraintSatisfaction()); + + if (auto *OrigFold = dyn_cast(IDC)) + NewIDC = new (C) CXXFoldExpr(OrigFold->getType(), SourceLocation(), NewIDC, + BinaryOperatorKind::BO_LAnd, + SourceLocation(), OrigFold->getRHS(), + SourceLocation(), /*NumExpansions=*/None); + return NewIDC; +} + TemplateTemplateParmDecl * ASTContext::getCanonicalTemplateTemplateParmDecl( TemplateTemplateParmDecl *TTP) const { @@ -661,63 +715,24 @@ TTP->getDepth(), TTP->getIndex(), nullptr, false, TTP->isParameterPack(), TTP->hasTypeConstraint()); 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, - CSE->getNestedNameSpecifierLoc(), CSE->getTemplateKWLoc(), - CSE->getConceptNameInfo(), CSE->getFoundDecl(), - CSE->getNamedConcept(), CSE->getTemplateArgsAsWritten(), - NewConverted, ConstraintSatisfaction()); - - if (auto *OrigFold = dyn_cast(IDC)) - NewIDC = new (*this) CXXFoldExpr(OrigFold->getType(), - OrigFold->getBeginLoc(), NewIDC, - BinaryOperatorKind::BO_LAnd, - OrigFold->getEllipsisLoc(), - OrigFold->getRHS(), - OrigFold->getEndLoc(), - /*NumExpansions=*/None); - - NewTTP->setTypeConstraint(TC->getNestedNameSpecifierLoc(), - TC->getConceptNameInfo(), /*FoundDecl=*/nullptr, - CSE->getNamedConcept(), TC->getTemplateArgsAsWritten(), NewIDC); + 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, + TC->getNamedConcept(), + ASTTemplateArgumentListInfo::Create(*this, CanonArgsAsWritten), + NewIDC + ); } CanonParams.push_back(NewTTP); } else if (const auto *NTTP = dyn_cast(*P)) { @@ -752,6 +767,11 @@ NTTP->isParameterPack(), TInfo); } + if (AutoType *AT = T->getContainedAutoType()) + if (AT->isConstrained()) + Param->setPlaceholderTypeConstraint( + canonicalizeImmediatelyDeclaredConstraint( + *this, NTTP->getPlaceholderTypeConstraint(), T)); CanonParams.push_back(Param); } else @@ -844,7 +864,7 @@ IdentifierTable &idents, SelectorTable &sels, Builtin::Context &builtins) : FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()), - DependentTemplateSpecializationTypes(this_()), + DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()), SubstTemplateTemplateParmPacks(this_()), CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), @@ -4964,21 +4984,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); @@ -5040,7 +5068,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; } Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -1283,9 +1283,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( Index: lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- lib/AST/ASTStructuralEquivalence.cpp +++ lib/AST/ASTStructuralEquivalence.cpp @@ -729,11 +729,29 @@ 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; + auto Auto1Args = Auto1->getTypeConstraintArguments(); + auto 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); Index: lib/AST/DeclTemplate.cpp =================================================================== --- lib/AST/DeclTemplate.cpp +++ lib/AST/DeclTemplate.cpp @@ -167,10 +167,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 auto *E = NTTP->getPlaceholderTypeConstraint()) + AC.push_back(E); + } + } if (HasRequiresClause) AC.push_back(getRequiresClause()); } @@ -643,8 +648,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( @@ -652,26 +662,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); Index: lib/AST/ODRHash.cpp =================================================================== --- lib/AST/ODRHash.cpp +++ 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); } Index: lib/AST/TemplateBase.cpp =================================================================== --- lib/AST/TemplateBase.cpp +++ 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)); Index: lib/AST/TextNodeDumper.cpp =================================================================== --- lib/AST/TextNodeDumper.cpp +++ lib/AST/TextNodeDumper.cpp @@ -1199,6 +1199,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( Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -1062,7 +1062,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++ @@ -4128,3 +4130,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); +} Index: lib/AST/TypeLoc.cpp =================================================================== --- lib/AST/TypeLoc.cpp +++ 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/Expr.h" #include "clang/AST/NestedNameSpecifier.h" @@ -566,3 +567,93 @@ } } } + +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 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::findAutoTypeLoc() const { + TypeLoc Res = GetContainedAutoTypeLocVisitor().Visit(*this); + if (Res.isNull()) + return AutoTypeLoc(); + return Res.getAs(); +} Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ 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; Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -3207,6 +3207,8 @@ // C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the // typename. if (!TypeRep) { + if (TryAnnotateTypeConstraint(SS)) + continue; // Eat the scope spec so the identifier is current. ConsumeAnnotationToken(); ParsedAttributesWithRange Attrs(AttrFactory); @@ -3356,6 +3358,9 @@ // 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)) + continue; ParsedAttributesWithRange Attrs(AttrFactory); if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) { if (!Attrs.empty()) { @@ -3405,9 +3410,52 @@ 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); + // 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)) { + if (!Tok.is(tok::l_paren)) { + // Something like `void foo(Iterator decltype i)` + Diag(Tok, diag::err_expected) << tok::l_paren + << FixItHint::CreateInsertion(Tok.getLocation(), "(auto)"); + UnconsumeToken(Tok); + } else { + ConsumeParen(); + if (!TryConsumeToken(tok::kw_auto)) { + // Something like `void foo(Iterator decltype(int) i)` + SkipUntil(tok::r_paren, SkipUntilFlags::StopBeforeMatch); + Diag(Tok, diag::err_placeholder_decltype_non_auto) + << FixItHint::CreateReplacement(SourceRange(AutoLoc, + Tok.getLocation()), + "auto"); + } else if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected) << tok::r_paren + << FixItHint::CreateInsertion(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 Index: lib/Parse/ParseTentative.cpp =================================================================== --- lib/Parse/ParseTentative.cpp +++ lib/Parse/ParseTentative.cpp @@ -1518,6 +1518,11 @@ *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)) + return TPResult::True; if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -56,6 +56,7 @@ Tok.startToken(); Tok.setKind(tok::eof); Actions.CurScope = nullptr; + Actions.ParsingTemplateParameterDepthPtr = &TemplateParameterDepth; NumCachedScopes = 0; CurParsedObjCImpl = nullptr; @@ -1132,6 +1133,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 @@ -1259,6 +1261,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 '='"); Index: lib/Sema/DeclSpec.cpp =================================================================== --- lib/Sema/DeclSpec.cpp +++ 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, Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -156,7 +156,8 @@ CurrentInstantiationScope(nullptr), DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr), - CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { + CurScope(nullptr), ParsingTemplateParameterDepthPtr(nullptr), + Ident_super(nullptr), Ident___float128(nullptr) { TUScope = nullptr; isConstantEvaluatedOverride = false; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -8392,14 +8393,183 @@ return S; } +namespace { + class AbbreviatedFunctionTemplateParameterReplacer : + public TreeTransform { + llvm::SmallVectorImpl &InventedTemplateParams; + llvm::SmallVector TransformedParams; + DeclaratorChunk::ParamInfo *Params; + unsigned Depth; + unsigned Index; + AutoTypeLoc AutoTL; + TemplateTypeParmDecl *CurrentParam = nullptr; + bool TopLevelParameter = true; + unsigned TransformedParamIndex = 0; + bool TransformingParameters = false; + unsigned ParameterCount = 0; + public: + AbbreviatedFunctionTemplateParameterReplacer( + Sema &SemaRef, llvm::SmallVectorImpl &InventedTemplateParams, + unsigned Depth, unsigned Index, DeclaratorChunk::ParamInfo *Params) + : TreeTransform(SemaRef), + InventedTemplateParams(InventedTemplateParams), + Params(Params), Depth(Depth), Index(Index) {} + + TypeSourceInfo *ProcessFunctionType(TypeSourceInfo *FTI) { + TopLevelParameter = true; + ParameterCount = + FTI->getTypeLoc().getAs().getNumParams(); + TypeSourceInfo *Result = TransformType(FTI); + assert(TransformedParams.size() == ParameterCount); + TopLevelParameter = false; + return Result; + } + + QualType TransformAutoType(TypeLocBuilder &TLB, AutoTypeLoc TL) { + if (CurrentParam) { + AutoTL = TL; + QualType T = QualType(CurrentParam->getTypeForDecl(), 0); + TemplateTypeParmTypeLoc NewTL = TLB.push(T); + NewTL.setNameLoc(TL.getNameLoc()); + return T; + } + AutoTypeLoc NewTL = TLB.push(TL.getType()); + NewTL.copy(TL); + return TL.getType(); + } + + bool TransformFunctionTypeParams( + SourceLocation Loc, ArrayRef Params, + const QualType *ParamTypes, + const FunctionProtoType::ExtParameterInfo *ParamInfos, + SmallVectorImpl &PTypes, SmallVectorImpl *PVars, + Sema::ExtParameterInfoBuilder &PInfos) { + bool PrevTransformingParameters = TransformingParameters; + TransformingParameters = true; + TransformedParamIndex = 0; + bool Result = TreeTransform::TransformFunctionTypeParams(Loc, Params, + ParamTypes, + ParamInfos, + PTypes, PVars, + PInfos); + TransformingParameters = PrevTransformingParameters; + return Result; + } + + ExprResult TransformDeclRefExpr(DeclRefExpr *E) { + for (unsigned I = 0; I != ParameterCount; ++I) + if (E->getDecl() == Params[I].Param) + return RebuildDeclRefExpr( + E->getQualifierLoc(), TransformedParams[I], E->getNameInfo(), + nullptr); + return TreeTransform::TransformDeclRefExpr(E); + } + + ParmVarDecl * + TransformFunctionTypeParam(ParmVarDecl *OldParm, int indexAdjustment, + Optional NumExpansions, + bool ExpectParameterPack) { + ParmVarDecl *NewParm = + TreeTransform::TransformFunctionTypeParam(OldParm, indexAdjustment, + NumExpansions, + ExpectParameterPack); + if (TransformingParameters && TopLevelParameter) + TransformedParams.push_back(NewParm); + return NewParm; + } + using TreeTransform::TransformType; + + + TypeSourceInfo *TransformType(TypeSourceInfo *DI) { + if (!TransformingParameters) { + return TreeTransform::TransformType(DI); + } + TransformingParameters = false; + bool IsTopLevelParameter = TopLevelParameter; + TopLevelParameter = false; + + AutoType *AT = DI->getType()->getContainedAutoType(); + if (!AT || AT->isDeduced() || !IsTopLevelParameter) { + if (AT && !AT->isDeduced()) { + // This means 'auto' or similar was used in the parameter of a + // function type which is not the top-level parameter of the + // abbreviated function template (e.g. 'void foo(int (*x)(auto));'). + if (AT->getKeyword() == AutoTypeKeyword::Auto) + SemaRef.Diag(DI->getTypeLoc().getBeginLoc(), + diag::err_param_nested_auto_not_allowed); + else + SemaRef.Diag(DI->getTypeLoc().getBeginLoc(), + diag::err_auto_not_allowed) + << (AT->getKeyword() == AutoTypeKeyword::DecltypeAuto ? 1 : 2) + << 0; + } + TypeSourceInfo *NewDI = TreeTransform::TransformType(DI); + + TransformedParamIndex++; + TopLevelParameter = IsTopLevelParameter; + TransformingParameters = true; + return NewDI; + } + + // We're transforming the type of an undeduced, top level, auto-typed + // parameter. + ParmVarDecl *OldParm = + cast(Params[TransformedParamIndex++].Param); + assert(OldParm->getType() == DI->getType()); + + // 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=*/OldParm->getLocation(), + Depth, Index++, /*Identifier=*/nullptr, /*Typename=*/false, + OldParm->isParameterPack(), + /*OwnsTypeConstraint=*/AT->isConstrained()); + CorrespondingTemplateParam->setImplicit(); + InventedTemplateParams.push_back(CorrespondingTemplateParam); + + // This will also find the TypeLoc of the AutoType and place it in AutoTL. + CurrentParam = CorrespondingTemplateParam; + TypeSourceInfo *NewParmType = TransformType(OldParm->getTypeSourceInfo()); + CurrentParam = nullptr; + + if (AT->isConstrained()) { + TemplateArgumentListInfo TemplateArgs(AutoTL.getLAngleLoc(), + AutoTL.getRAngleLoc()); + if (AutoTL.wereArgumentsSpecified()) + for (unsigned I = 0, C = AutoTL.getNumArgs(); I != C; ++I) + TemplateArgs.addArgument(AutoTL.getArgLoc(I)); + SemaRef.AttachTypeConstraint(AutoTL.getNestedNameSpecifierLoc(), + AutoTL.getConceptNameInfo(), AutoTL.getNamedConcept(), + AutoTL.wereArgumentsSpecified() ? &TemplateArgs : nullptr, + CorrespondingTemplateParam, + OldParm->isParameterPack() ? + OldParm->getTypeSourceInfo()->getTypeLoc() + .getAs().getEllipsisLoc() : + SourceLocation()); + } + + TopLevelParameter = IsTopLevelParameter; + TransformingParameters = true; + return NewParmType; + } + }; +} + + 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); // TODO: consider using NameInfo for diagnostic. DeclarationNameInfo NameInfo = GetNameForDeclarator(D); @@ -8429,6 +8599,17 @@ DeclContext *OriginalDC = DC; bool IsLocalExternDecl = adjustContextForLocalExternDecl(DC); + bool IsAbbreviatedTemplate = false; + if (LangOpts.ConceptsTS) { + const auto *FPT = R->castAs(); + for (auto Type : FPT->param_types()) + if (Type->getContainedAutoType()) { + IsAbbreviatedTemplate = true; + assert(D.isFunctionDeclarator()); + break; + } + } + FunctionDecl *NewFD = CreateNewFunctionDecl(*this, D, DC, R, TInfo, SC, isVirtualOkay); if (!NewFD) return nullptr; @@ -8480,18 +8661,59 @@ // 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)) { - if (TemplateParams->size() > 0) { + 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 (IsAbbreviatedTemplate || TemplateParams) { + if (IsAbbreviatedTemplate || TemplateParams->size() > 0) { // This is a function template + // If this is an abbreviated template (e.g. void foo(auto x)) - replace + // all placeholder-typed parameters with corresponding invented template + // parameters, and either add them to the matched template parameter + // list, or create a new template parameter list + if (IsAbbreviatedTemplate) { + SmallVector NewTemplateParams; + if (TemplateParams) + NewTemplateParams.append(TemplateParams->begin(), + TemplateParams->end()); + TInfo = cast_or_null( + AbbreviatedFunctionTemplateParameterReplacer(*this, + NewTemplateParams, + TemplateParams + ? TemplateParams->getDepth() + : getParsingTemplateParameterDepth(), + NewTemplateParams.size(), D.getFunctionTypeInfo().Params) + .ProcessFunctionType(TInfo)); + if (!TInfo) + return nullptr; + NewFD->setType(TInfo->getType()); + NewFD->setTypeSourceInfo(TInfo); + R = TInfo->getType(); + TemplateParameterList *NewTPL; + if (TemplateParams) + NewTPL = TemplateParameterList::Create( + Context, TemplateParams->getTemplateLoc(), + TemplateParams->getLAngleLoc(), NewTemplateParams, + TemplateParams->getRAngleLoc(), + TemplateParams->getRequiresClause()); + else + NewTPL = TemplateParameterList::Create(Context, SourceLocation(), + SourceLocation(), NewTemplateParams, SourceLocation(), + /*RequiresClause=*/nullptr); + if (TemplateParams) + TemplateParamLists.back() = NewTPL; + else + TemplateParamLists.push_back(NewTPL); + TemplateParams = NewTPL; + } + // Check that we can declare a template here. if (CheckTemplateDeclScope(S, TemplateParams)) NewFD->setInvalidDecl(); @@ -8521,7 +8743,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. @@ -8780,8 +9003,13 @@ // We let through "const void" here because Sema::GetTypeForDeclarator // already checks for that case. if (FTIHasNonVoidParameters(FTI) && FTI.Params[0].Param) { + auto FLoc = TInfo->getTypeLoc().getAs(); for (unsigned i = 0, e = FTI.NumParams; i != e; ++i) { - ParmVarDecl *Param = cast(FTI.Params[i].Param); + ParmVarDecl *Param; + if (IsAbbreviatedTemplate) + Param = FLoc.getParam(i); + else + Param = cast(FTI.Params[i].Param); assert(Param->getDeclContext() != NewFD && "Was set before ?"); Param->setDeclContext(NewFD); Params.push_back(Param); Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -1074,6 +1074,53 @@ 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( + TemplateArgumentLoc(TemplateArgument(ConstrainedType), + S.Context.getTrivialTypeSourceInfo(ConstrainedType, + ParamNameLoc))); + + ConstraintArgs.setRAngleLoc(RAngleLoc); + ConstraintArgs.setLAngleLoc(LAngleLoc); + Appender(ConstraintArgs); + + // C++2a [temp]p6: + // [...] 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() || + !ImmediatelyDeclaredConstraint.isUsable() || + !EllipsisLoc.isValid()) + return ImmediatelyDeclaredConstraint; + + // C++2a [temp]p6: + // [...] 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 @@ -1092,53 +1139,22 @@ *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]p6: - // [...] 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() || - !ImmediatelyDeclaredConstraint.isUsable()) + !ImmediatelyDeclaredConstraint.isUsable()) return true; - if (ConstrainedParameter->isParameterPack()) { - // C++2a [temp]p6: - // [...] 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() || - !ImmediatelyDeclaredConstraint.isUsable()) - return true; - } - ConstrainedParameter->setTypeConstraint(NS, NameInfo, /*FoundDecl=*/NamedConcept, NamedConcept, ArgsAsWritten, @@ -1146,6 +1162,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. /// @@ -1301,6 +1349,11 @@ TInfo); Param->setAccess(AS_public); + if (AutoTypeLoc TL = TInfo->getTypeLoc().findAutoTypeLoc()) + if (TL.isConstrained()) + if (AttachTypeConstraint(TL, Param, D.getEllipsisLoc())) + Invalid = true; + if (Invalid) Param->setInvalidDecl(); @@ -6503,7 +6556,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() Index: lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- lib/Sema/SemaTemplateDeduction.cpp +++ lib/Sema/SemaTemplateDeduction.cpp @@ -4372,9 +4372,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; } @@ -4409,9 +4410,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 @@ -4439,6 +4441,51 @@ } } +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; + MultiLevelTemplateArgumentList MLTAL; + MLTAL.addOuterTemplateArguments(Converted); + if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, + MLTAL, TypeLoc.getLocalSourceRange(), + Satisfaction)) + return Sema::DAR_FailedAlreadyDiagnosed; + if (!Satisfaction.IsSatisfied) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "'" << Concept->getName(); + if (TypeLoc.wereArgumentsSpecified()) { + 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 @@ -4453,9 +4500,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()) @@ -4495,6 +4545,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.getAs(), + Deduced); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } Result = SubstituteDeducedTypeTransform(*this, Deduced).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; @@ -4596,6 +4654,17 @@ return DAR_FailedAlreadyDiagnosed; } + if (const auto *AT = Type.getType()->getAs()) { + if (AT->isConstrained() && !IgnoreConstraints) { + auto ConstraintsResult = + CheckDeducedPlaceholderConstraints(*this, *AT, + Type.getAs(), + DeducedType); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } + } + Result = SubstituteDeducedTypeTransform(*this, DeducedType).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2500,6 +2500,16 @@ D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getPosition(), D->getIdentifier(), T, D->isParameterPack(), DI); + if (AutoTypeLoc AutoLoc = DI->getTypeLoc().findAutoTypeLoc()) + if (AutoLoc.isConstrained()) + if (SemaRef.AttachTypeConstraint( + AutoLoc, Param, + IsExpandedParameterPack + ? DI->getTypeLoc().getAs() + .getEllipsisLoc() + : SourceLocation())) + Invalid = true; + Param->setAccess(AS_public); if (Invalid) Param->setInvalidDecl(); Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -27,6 +27,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 +1252,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 +1616,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 +1629,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; @@ -2940,9 +2972,13 @@ break; case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: - case DeclaratorContext::PrototypeContext: Error = 0; break; + case DeclaratorContext::PrototypeContext: + if (!SemaRef.getLangOpts().ConceptsTS || !Auto || + Auto->getKeyword() != AutoTypeKeyword::Auto) + Error = 0; + break; case DeclaratorContext::RequiresExprContext: Error = 21; break; @@ -4561,7 +4597,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(); @@ -5176,7 +5213,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(); @@ -5384,14 +5422,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()); @@ -5519,6 +5558,31 @@ TL.copy( TInfo->getTypeLoc().castAs()); } + void VisitAutoTypeLoc(AutoTypeLoc TL) { + assert(DS.getTypeSpecType() == TST_auto || + DS.getTypeSpecType() == TST_decltype_auto || + DS.getTypeSpecType() == TST_auto_type); + 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()); } @@ -5788,7 +5852,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; Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -943,12 +943,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 @@ -4474,7 +4478,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. @@ -5202,21 +5209,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()) && + "Could not find parameter packs or undeduced auto type!"); +#endif } if (ShouldExpand) { @@ -5276,6 +5291,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); @@ -5787,32 +5805,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) { @@ -6074,6 +6066,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( Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -6517,13 +6517,22 @@ case TYPE_AUTO: { QualType Deduced = readType(*Loc.F, Record, Idx); AutoTypeKeyword Keyword = (AutoTypeKeyword)Record[Idx++]; + ConceptDecl *CD = nullptr; + SmallVector Args; + if (Record[Idx++]) { + CD = ReadDeclAs(*Loc.F, Record, Idx); + unsigned NumArgs = Record[Idx++]; + Args.reserve(NumArgs); + while (NumArgs--) + Args.push_back(ReadTemplateArgument(*Loc.F, Record, Idx)); + } bool IsDependent = false, IsPack = false; if (Deduced.isNull()) { IsDependent = Record[Idx] > 0; IsPack = Record[Idx] > 1; ++Idx; } - return Context.getAutoType(Deduced, Keyword, IsDependent, IsPack); + return Context.getAutoType(Deduced, Keyword, IsDependent, IsPack, CD, Args); } case TYPE_DEDUCED_TEMPLATE_SPECIALIZATION: { @@ -7042,6 +7051,19 @@ void TypeLocReader::VisitAutoTypeLoc(AutoTypeLoc TL) { TL.setNameLoc(ReadSourceLocation()); + if (Record[Idx++]) { + TL.setNestedNameSpecifierLoc(ReadNestedNameSpecifierLoc()); + TL.setTemplateKWLoc(ReadSourceLocation()); + TL.setConceptNameLoc(ReadSourceLocation()); + TL.setFoundDecl(Reader->ReadDeclAs(*F, Record, Idx)); + TL.setLAngleLoc(ReadSourceLocation()); + TL.setRAngleLoc(ReadSourceLocation()); + for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i) + TL.setArgLocInfo( + i, + Reader->GetTemplateArgumentLocInfo( + *F, TL.getTypePtr()->getArg(i).getKind(), Record, Idx)); + } } void TypeLocReader::VisitDeducedTemplateSpecializationTypeLoc( Index: lib/Serialization/ASTReaderDecl.cpp =================================================================== --- lib/Serialization/ASTReaderDecl.cpp +++ lib/Serialization/ASTReaderDecl.cpp @@ -2399,6 +2399,8 @@ // TemplateParmPosition. D->setDepth(Record.readInt()); D->setPosition(Record.readInt()); + if (D->hasPlaceholderTypeConstraint()) + D->setPlaceholderTypeConstraint(Record.readExpr()); if (D->isExpandedParameterPack()) { auto TypesAndInfos = D->getTrailingObjects>(); @@ -3844,13 +3846,19 @@ OwnsTypeConstraint); 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; Index: lib/Serialization/ASTWriter.cpp =================================================================== --- lib/Serialization/ASTWriter.cpp +++ lib/Serialization/ASTWriter.cpp @@ -368,6 +368,14 @@ void ASTTypeWriter::VisitAutoType(const AutoType *T) { Record.AddTypeRef(T->getDeducedType()); Record.push_back((unsigned)T->getKeyword()); + Record.push_back((unsigned)T->isConstrained()); + if (T->isConstrained()) { + Record.AddDeclRef(T->getTypeConstraintConcept()); + auto Args = T->getTypeConstraintArguments(); + Record.push_back(Args.size()); + for (const auto &Arg : Args) + Record.AddTemplateArgument(Arg); + } if (T->getDeducedType().isNull()) Record.push_back(T->containsUnexpandedParameterPack() ? 2 : T->isDependentType() ? 1 : 0); @@ -760,6 +768,19 @@ 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()); + Record.push_back(TL.getNumArgs()); + for (unsigned I = 0; I < TL.getNumArgs(); ++I) + Record.AddTemplateArgumentLocInfo(TL.getTypePtr()->getArg(I).getKind(), + TL.getArgLocInfo(I)); + } } void TypeLocWriter::VisitDeducedTemplateSpecializationTypeLoc( Index: lib/Serialization/ASTWriterDecl.cpp =================================================================== --- lib/Serialization/ASTWriterDecl.cpp +++ lib/Serialization/ASTWriterDecl.cpp @@ -1611,11 +1611,11 @@ bool OwnsTC = !D->typeConstraintWasInherited(); Record.push_back(OwnsTC); if (OwnsTC) { - Record.AddNestedNameSpecifierLoc(TC->getNestedNameSpecifierLoc()); - Record.AddDeclarationNameInfo(TC->getConceptNameInfo()); - Record.AddDeclRef(TC->getNamedConcept()); - Record.AddASTTemplateArgumentListInfo(TC->getTemplateArgsAsWritten()); - Record.AddStmt(TC->getImmediatelyDeclaredConstraint()); + Record.AddNestedNameSpecifierLoc(TC->getNestedNameSpecifierLoc()); + Record.AddDeclarationNameInfo(TC->getConceptNameInfo()); + Record.AddDeclRef(TC->getNamedConcept()); + Record.AddASTTemplateArgumentListInfo(TC->getTemplateArgsAsWritten()); + Record.AddStmt(TC->getImmediatelyDeclaredConstraint()); } else Record.AddDeclRef(D->getInheritedFromTypeConstraintDecl()); } @@ -1633,6 +1633,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()); @@ -1640,6 +1642,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) { Index: test/CXX/concepts-ts/dcl/dcl.fct/p17.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl/dcl.fct/p17.cpp @@ -0,0 +1,123 @@ +// 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 $0 = int] not viable: expects an l-value for 1st argument}} + // expected-note@-2{{candidate function [with $0 = char] not viable: expects an l-value for 1st argument}} + static_assert(is_same_v); // expected-error{{no matching function for call to 'f2'}} + static_assert(is_same_v); // expected-error{{no matching function for call to 'f2'}} + + 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' only allowed in top-level function declaration parameters}} + + 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 function for call to 'f6'}} + + 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 function for call to 'f7'}} + using f7c2 = decltype(f7(foo)); // expected-error{{no matching function for call to 'f7'}} + 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 $0 = ] not viable: expects an l-value for 2nd argument}} + using f9c1 = decltype(f9(return_int, 1)); // expected-error{{no matching function for call to 'f9'}} + + 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>); + + 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 = true; + + void f(C auto x); + void f(C auto &x); + void f(const C auto &x); + void f(C auto (*x)(C auto y)); // expected-error{{'auto' only allowed in top-level function declaration parameters}} + void f(C auto (*x)(int y)); + void f(C auto (*x)() -> int); // expected-error{{function with trailing return type must specify return type 'auto', not 'C auto'}} + void f(C auto... x); + void f(C auto &... x); + void f(const C auto &... x); + void f(C decltype(auto) x); +} Index: test/CXX/concepts-ts/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp =================================================================== --- /dev/null +++ test/CXX/concepts-ts/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp @@ -0,0 +1,30 @@ +// 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}} + +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; + LargerThan<1> auto k = 10; + LargerThan<10> auto l = 10; + // expected-error@-1{{deduced type 'int' does not satisfy 'LargerThan<10>'}} + 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; } \ No newline at end of file Index: test/CXX/concepts-ts/temp/temp.param/p10.cpp =================================================================== --- test/CXX/concepts-ts/temp/temp.param/p10.cpp +++ test/CXX/concepts-ts/temp/temp.param/p10.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; Index: test/Parser/cxx2a-placeholder-type-constraint.cpp =================================================================== --- /dev/null +++ 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{{only 'decltype(auto)' or 'auto' may appear after a concept name for placeholders}} +} \ No newline at end of file