From: Richard Smith Date: Mon, 30 Jan 2017 04:38:28 +0000 (+0000) Subject: Towards P0091R3: parsing support for class template argument deduction in typename... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a20c033fff75dd646149ff39536609b795eab81f;p=clang Towards P0091R3: parsing support for class template argument deduction in typename-specifiers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@293455 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index dc50a190de..761d3cedc7 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -2946,6 +2946,16 @@ inline NamedDecl *getAsNamedDecl(TemplateParameter P) { return P.get(); } +inline TemplateDecl *getAsTypeTemplateDecl(Decl *D) { + auto *TD = dyn_cast(D); + return TD && (isa(TD) || + isa(TD) || + isa(TD) || + isa(TD)) + ? TD + : nullptr; +} + } /* end of namespace clang */ #endif diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 5429b12bd0..ae127fd4a1 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1884,6 +1884,11 @@ def err_auto_not_allowed : Error< "|in conversion function type|here|in lambda parameter" "|in type allocated by 'new'|in K&R-style function parameter" "|in template parameter|in friend declaration}1">; +def err_dependent_deduced_tst : Error< + "typename specifier refers to " + "%select{class template|function template|variable template|alias template|" + "template template parameter|template}0 member in %1; " + "argument deduction not allowed here">; def err_auto_not_allowed_var_inst : Error< "'auto' variable template instantiation is not allowed">; def err_auto_var_requires_init : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index a405d2b988..d2e3f8c36c 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7359,7 +7359,8 @@ public: TypeSourceInfo *SubstType(TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs, - SourceLocation Loc, DeclarationName Entity); + SourceLocation Loc, DeclarationName Entity, + bool AllowDeducedTST = false); QualType SubstType(QualType T, const MultiLevelTemplateArgumentList &TemplateArgs, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ce843a587b..60daae056b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -60,11 +60,6 @@ Sema::DeclGroupPtrTy Sema::ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType) { return DeclGroupPtrTy::make(DeclGroupRef(Ptr)); } -static bool isTypeTemplate(NamedDecl *ND) { - return isa(ND) || isa(ND) || - isa(ND); -} - namespace { class TypeNameValidatorCCC : public CorrectionCandidateCallback { @@ -81,7 +76,7 @@ class TypeNameValidatorCCC : public CorrectionCandidateCallback { bool ValidateCandidate(const TypoCorrection &candidate) override { if (NamedDecl *ND = candidate.getCorrectionDecl()) { bool IsType = isa(ND) || isa(ND); - bool AllowedTemplate = AllowTemplates && isTypeTemplate(ND); + bool AllowedTemplate = AllowTemplates && getAsTypeTemplateDecl(ND); return (IsType || AllowedTemplate) && (AllowInvalidDecl || !ND->isInvalidDecl()); } @@ -405,7 +400,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, for (LookupResult::iterator Res = Result.begin(), ResEnd = Result.end(); Res != ResEnd; ++Res) { if (isa(*Res) || isa(*Res) || - (AllowDeducedTemplate && isTypeTemplate(*Res))) { + (AllowDeducedTemplate && getAsTypeTemplateDecl(*Res))) { if (!IIDecl || (*Res)->getLocation().getRawEncoding() < IIDecl->getLocation().getRawEncoding()) @@ -458,9 +453,10 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc, (void)DiagnoseUseOfDecl(IDecl, NameLoc); if (!HasTrailingDot) T = Context.getObjCInterfaceType(IDecl); - } else if (AllowDeducedTemplate && isTypeTemplate(IIDecl)) { - T = Context.getDeducedTemplateSpecializationType( - TemplateName(cast(IIDecl)), QualType(), false); + } else if (AllowDeducedTemplate) { + if (auto *TD = getAsTypeTemplateDecl(IIDecl)) + T = Context.getDeducedTemplateSpecializationType(TemplateName(TD), + QualType(), false); } if (T.isNull()) { diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 8f46ad056b..d3b47f1341 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -8792,8 +8792,18 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, Context.getTypeDeclType(Type)); } - // FIXME: Form a deduced template specialization type if we get a template - // declaration here. + // C++ [dcl.type.simple]p2: + // A type-specifier of the form + // typename[opt] nested-name-specifier[opt] template-name + // is a placeholder for a deduced class type [...]. + if (getLangOpts().CPlusPlus1z) { + if (auto *TD = getAsTypeTemplateDecl(Result.getFoundDecl())) { + return Context.getElaboratedType( + Keyword, QualifierLoc.getNestedNameSpecifier(), + Context.getDeducedTemplateSpecializationType(TemplateName(TD), + QualType(), false)); + } + } DiagID = diag::err_typename_nested_not_type; Referenced = Result.getFoundDecl(); diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index aff19ec597..723a6c0e4d 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1490,12 +1490,16 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType( /// a cast expression) or that the entity has no name (e.g., an /// unnamed function parameter). /// +/// \param AllowDeducedTST Whether a DeducedTemplateSpecializationType is +/// acceptable as the top level type of the result. +/// /// \returns If the instantiation succeeds, the instantiated /// type. Otherwise, produces diagnostics and returns a NULL type. TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, const MultiLevelTemplateArgumentList &Args, SourceLocation Loc, - DeclarationName Entity) { + DeclarationName Entity, + bool AllowDeducedTST) { assert(!ActiveTemplateInstantiations.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -1505,7 +1509,8 @@ TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, return T; TemplateInstantiator Instantiator(*this, Args, Loc, Entity); - return Instantiator.TransformType(T); + return AllowDeducedTST ? Instantiator.TransformTypeWithDeducedTST(T) + : Instantiator.TransformType(T); } TypeSourceInfo *Sema::SubstType(TypeLoc TL, diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 2310e99ab9..46de4a1565 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -657,10 +657,9 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D, ArrayRef *Bindings) { // Do substitution on the type of the declaration - TypeSourceInfo *DI = SemaRef.SubstType(D->getTypeSourceInfo(), - TemplateArgs, - D->getTypeSpecStartLoc(), - D->getDeclName()); + TypeSourceInfo *DI = SemaRef.SubstType( + D->getTypeSourceInfo(), TemplateArgs, D->getTypeSpecStartLoc(), + D->getDeclName(), /*AllowDeducedTST*/true); if (!DI) return nullptr; diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index d27cd77040..7198fce752 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -307,6 +307,17 @@ public: /// QualType TransformType(TypeLocBuilder &TLB, TypeLoc TL); + /// \brief Transform a type that is permitted to produce a + /// DeducedTemplateSpecializationType. + /// + /// This is used in the (relatively rare) contexts where it is acceptable + /// for transformation to produce a class template type with deduced + /// template arguments. + /// @{ + QualType TransformTypeWithDeducedTST(QualType T); + TypeSourceInfo *TransformTypeWithDeducedTST(TypeSourceInfo *DI); + /// @} + /// \brief Transform the given statement. /// /// By default, this routine transforms a statement by delegating to the @@ -898,7 +909,7 @@ public: /// By default, builds a new ParenType type from the inner type. /// Subclasses may override this routine to provide different behavior. QualType RebuildParenType(QualType InnerType) { - return SemaRef.Context.getParenType(InnerType); + return SemaRef.BuildParenType(InnerType); } /// \brief Build a new qualified name type. @@ -968,7 +979,8 @@ public: SourceLocation KeywordLoc, NestedNameSpecifierLoc QualifierLoc, const IdentifierInfo *Id, - SourceLocation IdLoc) { + SourceLocation IdLoc, + bool DeducedTSTContext) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); @@ -980,9 +992,25 @@ public: Id); } - if (Keyword == ETK_None || Keyword == ETK_Typename) - return SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, - *Id, IdLoc); + if (Keyword == ETK_None || Keyword == ETK_Typename) { + QualType T = SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, + *Id, IdLoc); + // If a dependent name resolves to a deduced template specialization type, + // check that we're in one of the syntactic contexts permitting it. + if (!DeducedTSTContext) { + if (auto *Deduced = dyn_cast_or_null( + T.isNull() ? nullptr : T->getContainedDeducedType())) { + SemaRef.Diag(IdLoc, diag::err_dependent_deduced_tst) + << (int)SemaRef.getTemplateNameKindForDiagnostics( + Deduced->getTemplateName()) + << QualType(QualifierLoc.getNestedNameSpecifier()->getAsType(), 0); + if (auto *TD = Deduced->getTemplateName().getAsTemplateDecl()) + SemaRef.Diag(TD->getLocation(), diag::note_template_decl_here); + return QualType(); + } + } + return T; + } TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword); @@ -3157,6 +3185,10 @@ private: TypeSourceInfo *TransformTSIInObjectScope(TypeLoc TL, QualType ObjectType, NamedDecl *FirstQualifierInScope, CXXScopeSpec &SS); + + QualType TransformDependentNameType(TypeLocBuilder &TLB, + DependentNameTypeLoc TL, + bool DeducibleTSTContext); }; template @@ -4048,6 +4080,52 @@ TreeTransform::TransformType(TypeLocBuilder &TLB, TypeLoc T) { llvm_unreachable("unhandled type loc!"); } +template +QualType TreeTransform::TransformTypeWithDeducedTST(QualType T) { + if (!isa(T)) + return TransformType(T); + + if (getDerived().AlreadyTransformed(T)) + return T; + TypeSourceInfo *DI = getSema().Context.getTrivialTypeSourceInfo(T, + getDerived().getBaseLocation()); + TypeSourceInfo *NewDI = getDerived().TransformTypeWithDeducedTST(DI); + return NewDI ? NewDI->getType() : QualType(); +} + +template +TypeSourceInfo * +TreeTransform::TransformTypeWithDeducedTST(TypeSourceInfo *DI) { + if (!isa(DI->getType())) + return TransformType(DI); + + // Refine the base location to the type's location. + TemporaryBase Rebase(*this, DI->getTypeLoc().getBeginLoc(), + getDerived().getBaseEntity()); + if (getDerived().AlreadyTransformed(DI->getType())) + return DI; + + TypeLocBuilder TLB; + + TypeLoc TL = DI->getTypeLoc(); + TLB.reserve(TL.getFullDataSize()); + + Qualifiers Quals; + if (auto QTL = TL.getAs()) { + Quals = QTL.getType().getLocalQualifiers(); + TL = QTL.getUnqualifiedLoc(); + } + + auto DNTL = TL.castAs(); + + QualType Result = getDerived().TransformDependentNameType( + TLB, DNTL, /*DeducedTSTContext*/true); + if (Result.isNull()) + return nullptr; + + return TLB.getTypeSourceInfo(SemaRef.Context, Result); +} + /// FIXME: By default, this routine adds type qualifiers only to types /// that can have qualifiers, and silently suppresses those qualifiers /// that are not permitted (e.g., qualifiers on reference or function @@ -5854,8 +5932,14 @@ TreeTransform::TransformParenType(TypeLocBuilder &TLB, } template -QualType TreeTransform::TransformDependentNameType(TypeLocBuilder &TLB, - DependentNameTypeLoc TL) { +QualType TreeTransform::TransformDependentNameType( + TypeLocBuilder &TLB, DependentNameTypeLoc TL) { + return TransformDependentNameType(TLB, TL, false); +} + +template +QualType TreeTransform::TransformDependentNameType( + TypeLocBuilder &TLB, DependentNameTypeLoc TL, bool DeducedTSTContext) { const DependentNameType *T = TL.getTypePtr(); NestedNameSpecifierLoc QualifierLoc @@ -5868,7 +5952,8 @@ QualType TreeTransform::TransformDependentNameType(TypeLocBuilder &TLB, TL.getElaboratedKeywordLoc(), QualifierLoc, T->getIdentifier(), - TL.getNameLoc()); + TL.getNameLoc(), + DeducedTSTContext); if (Result.isNull()) return QualType(); @@ -9474,7 +9559,8 @@ template ExprResult TreeTransform::TransformCXXFunctionalCastExpr( CXXFunctionalCastExpr *E) { - TypeSourceInfo *Type = getDerived().TransformType(E->getTypeInfoAsWritten()); + TypeSourceInfo *Type = + getDerived().TransformTypeWithDeducedTST(E->getTypeInfoAsWritten()); if (!Type) return ExprError(); @@ -9663,8 +9749,8 @@ template ExprResult TreeTransform::TransformCXXNewExpr(CXXNewExpr *E) { // Transform the type that we're allocating - TypeSourceInfo *AllocTypeInfo - = getDerived().TransformType(E->getAllocatedTypeSourceInfo()); + TypeSourceInfo *AllocTypeInfo = + getDerived().TransformTypeWithDeducedTST(E->getAllocatedTypeSourceInfo()); if (!AllocTypeInfo) return ExprError(); @@ -10375,7 +10461,8 @@ template ExprResult TreeTransform::TransformCXXTemporaryObjectExpr( CXXTemporaryObjectExpr *E) { - TypeSourceInfo *T = getDerived().TransformType(E->getTypeSourceInfo()); + TypeSourceInfo *T = + getDerived().TransformTypeWithDeducedTST(E->getTypeSourceInfo()); if (!T) return ExprError(); @@ -10672,7 +10759,8 @@ template ExprResult TreeTransform::TransformCXXUnresolvedConstructExpr( CXXUnresolvedConstructExpr *E) { - TypeSourceInfo *T = getDerived().TransformType(E->getTypeSourceInfo()); + TypeSourceInfo *T = + getDerived().TransformTypeWithDeducedTST(E->getTypeSourceInfo()); if (!T) return ExprError(); diff --git a/test/Parser/cxx1z-class-template-argument-deduction.cpp b/test/Parser/cxx1z-class-template-argument-deduction.cpp index e53d64f2ed..65b1105d34 100644 --- a/test/Parser/cxx1z-class-template-argument-deduction.cpp +++ b/test/Parser/cxx1z-class-template-argument-deduction.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -verify %s -template struct A {}; // expected-note 31{{declared here}} +template struct A {}; // expected-note 35{{declared here}} // Make sure we still correctly parse cases where a template can appear without arguments. namespace template_template_arg { @@ -101,6 +101,8 @@ namespace expr { (void)reinterpret_cast(&n); // expected-error{{requires template arguments; argument deduction not allowed here}} (void)const_cast(n); // expected-error{{requires template arguments; argument deduction not allowed here}} (void)*(A*)(&n); // expected-error{{requires template arguments; argument deduction not allowed here}} + (void)(A)(n); // expected-error{{requires template arguments; argument deduction not allowed here}} + (void)(A){n}; // expected-error{{requires template arguments; argument deduction not allowed here}} (void)A(n); // expected-error {{not yet supported}} (void)A{n}; // expected-error {{not yet supported}} @@ -121,6 +123,7 @@ namespace decl { A a; // expected-error {{requires an initializer}} A b = 0; // expected-error {{not yet supported}} + const A c = 0; // expected-error {{not yet supported}} A (parens) = 0; // expected-error {{cannot use parentheses when declaring variable with deduced class template specialization type}} A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}} A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}} @@ -129,3 +132,57 @@ namespace decl { A (*fp)() = 0; // expected-error {{cannot form function returning deduced class template specialization type}} A [x, y] = 0; // expected-error {{cannot be declared with type 'A'}} expected-error {{not yet supported}} } + +namespace typename_specifier { + struct F {}; + + void e() { + (void) typename ::A(0); // expected-error {{not yet supported}} + (void) typename ::A{0}; // expected-error {{not yet supported}} + new typename ::A(0); // expected-error {{not yet supported}} + new typename ::A{0}; // expected-error {{not yet supported}} + typename ::A a = 0; // expected-error {{not yet supported}} + const typename ::A b = 0; // expected-error {{not yet supported}} + if (typename ::A a = 0) {} // expected-error {{not yet supported}} + for (typename ::A a = 0; typename ::A b = 0; /**/) {} // expected-error 2{{not yet supported}} + + (void)(typename ::A)(0); // expected-error{{requires template arguments; argument deduction not allowed here}} + (void)(typename ::A){0}; // expected-error{{requires template arguments; argument deduction not allowed here}} + } + typename ::A a = 0; // expected-error {{not yet supported}} + const typename ::A b = 0; // expected-error {{not yet supported}} + typename ::A (parens) = 0; // expected-error {{cannot use parentheses when declaring variable with deduced class template specialization type}} + typename ::A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}} + typename ::A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}} + typename ::A arr[3] = 0; // expected-error {{cannot form array of deduced class template specialization type}} + typename ::A F::*pm = 0; // expected-error {{cannot form pointer to deduced class template specialization type}} + typename ::A (*fp)() = 0; // expected-error {{cannot form function returning deduced class template specialization type}} + typename ::A [x, y] = 0; // expected-error {{cannot be declared with type 'typename ::A'}} expected-error {{not yet supported}} + + struct X { template struct A {}; }; // expected-note 8{{template}} + + template void f() { + (void) typename T::A(0); // expected-error {{not yet supported}} + (void) typename T::A{0}; // expected-error {{not yet supported}} + new typename T::A(0); // expected-error {{not yet supported}} + new typename T::A{0}; // expected-error {{not yet supported}} + typename T::A a = 0; // expected-error {{not yet supported}} + const typename T::A b = 0; // expected-error {{not yet supported}} + if (typename T::A a = 0) {} // expected-error {{not yet supported}} + for (typename T::A a = 0; typename T::A b = 0; /**/) {} // expected-error 2{{not yet supported}} + + {(void)(typename T::A)(0);} // expected-error{{refers to class template member}} + {(void)(typename T::A){0};} // expected-error{{refers to class template member}} + {typename T::A (parens) = 0;} // expected-error {{refers to class template member in 'typename_specifier::X'; argument deduction not allowed here}} + {typename T::A *p = 0;} // expected-error {{refers to class template member}} + {typename T::A &r = *p;} // expected-error {{refers to class template member}} + {typename T::A arr[3] = 0;} // expected-error {{refers to class template member}} + {typename T::A F::*pm = 0;} // expected-error {{refers to class template member}} + {typename T::A (*fp)() = 0;} // expected-error {{refers to class template member}} + {typename T::A [x, y] = 0;} // expected-error {{cannot be declared with type 'typename T::A'}} expected-error {{not yet supported}} + } + template void f(); // expected-note {{instantiation of}} + + template void g(typename T::A = 0); // expected-note {{refers to class template member}} + void h() { g(); } // expected-error {{no matching function}} +}