From 6ba4f924d92bc11dc5c9c79892a16a769dbfb7ab Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 28 Feb 2018 03:02:23 +0000 Subject: [PATCH] Fix a couple of cases where we would fail to correctly parse deduced class template specialization types. Specifically, we would not properly parse these types within template arguments (for non-type template parameters), and in tentative parses. Fixing both of these essentially requires that we parse deduced template specialization types as types in all contexts, even in template argument lists -- in particular, tentative parsing may look ahead and annotate a deduced template specialization type before we figure out that we're actually supposed to treat the tokens as a template-name. We deal with this by simply permitting deduced template specialization types when parsing template arguments, and converting them to template template arguments. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@326299 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/DeclSpec.h | 12 +++- include/clang/Sema/Sema.h | 2 + lib/AST/TemplateName.cpp | 5 ++ lib/Parse/ParseDecl.cpp | 5 +- lib/Parse/ParseTemplate.cpp | 10 +--- lib/Parse/ParseTentative.cpp | 39 ++++++++++--- lib/Parse/Parser.cpp | 4 +- lib/Sema/SemaTemplate.cpp | 55 ++++++++++++++++++- lib/Sema/SemaType.cpp | 12 ++++ test/CXX/temp/temp.deduct.guide/p3.cpp | 4 +- ...xx1z-class-template-argument-deduction.cpp | 40 +++++++++++++- 11 files changed, 161 insertions(+), 27 deletions(-) diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 0baaa76f28..e9b116fb71 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -326,6 +326,7 @@ public: PQ_TypeSpecifier = 2, PQ_TypeQualifier = 4, PQ_FunctionSpecifier = 8 + // FIXME: Attributes should be included here. }; private: @@ -1732,7 +1733,8 @@ enum class DeclaratorContext { ConversionIdContext, // C++ conversion-type-id. TrailingReturnContext, // C++11 trailing-type-specifier. TrailingReturnVarContext, // C++11 trailing-type-specifier for variable. - TemplateTypeArgContext, // Template type argument. + TemplateArgContext, // Any template argument (in template argument list). + TemplateTypeArgContext, // Template type argument (in default argument). AliasDeclContext, // C++11 alias-declaration. AliasTemplateContext // C++11 alias-declaration template. }; @@ -1949,6 +1951,7 @@ public: case DeclaratorContext::BlockLiteralContext: case DeclaratorContext::LambdaExprContext: case DeclaratorContext::ConversionIdContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: @@ -1986,6 +1989,7 @@ public: case DeclaratorContext::BlockLiteralContext: case DeclaratorContext::LambdaExprContext: case DeclaratorContext::ConversionIdContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: @@ -2027,6 +2031,7 @@ public: case DeclaratorContext::BlockLiteralContext: case DeclaratorContext::LambdaExprContext: case DeclaratorContext::ConversionIdContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: @@ -2082,6 +2087,7 @@ public: case DeclaratorContext::BlockLiteralContext: case DeclaratorContext::LambdaExprContext: case DeclaratorContext::ConversionIdContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: return false; @@ -2291,6 +2297,7 @@ public: case DeclaratorContext::BlockLiteralContext: case DeclaratorContext::LambdaExprContext: case DeclaratorContext::ConversionIdContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: @@ -2326,13 +2333,14 @@ public: case DeclaratorContext::ConversionIdContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: + case DeclaratorContext::TemplateTypeArgContext: return false; case DeclaratorContext::BlockContext: case DeclaratorContext::ForContext: case DeclaratorContext::InitStmtContext: case DeclaratorContext::ConditionContext: - case DeclaratorContext::TemplateTypeArgContext: + case DeclaratorContext::TemplateArgContext: return true; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ed037a82ce..35599bf6ce 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6168,6 +6168,8 @@ public: void translateTemplateArguments(const ASTTemplateArgsPtr &In, TemplateArgumentListInfo &Out); + ParsedTemplateArgument ActOnTemplateTypeArgument(TypeResult ParsedType); + void NoteAllFoundTemplates(TemplateName Name); QualType CheckTemplateIdType(TemplateName Template, diff --git a/lib/AST/TemplateName.cpp b/lib/AST/TemplateName.cpp index bd04fd8366..548468ed17 100644 --- a/lib/AST/TemplateName.cpp +++ b/lib/AST/TemplateName.cpp @@ -185,6 +185,11 @@ bool TemplateName::isInstantiationDependent() const { } bool TemplateName::containsUnexpandedParameterPack() const { + if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) { + if (QTN->getQualifier()->containsUnexpandedParameterPack()) + return true; + } + if (TemplateDecl *Template = getAsTemplateDecl()) { if (TemplateTemplateParmDecl *TTP = dyn_cast(Template)) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 3a3096fd05..deefcaf325 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2686,7 +2686,8 @@ Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) { return DeclSpecContext::DSC_top_level; if (Context == DeclaratorContext::TemplateParamContext) return DeclSpecContext::DSC_template_param; - if (Context == DeclaratorContext::TemplateTypeArgContext) + if (Context == DeclaratorContext::TemplateArgContext || + Context == DeclaratorContext::TemplateTypeArgContext) return DeclSpecContext::DSC_template_type_arg; if (Context == DeclaratorContext::TrailingReturnContext || Context == DeclaratorContext::TrailingReturnVarContext) @@ -5637,7 +5638,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // An identifier within parens is unlikely to be intended to be anything // other than a name being "declared". DiagnoseIdentifier = true; - else if (D.getContext() == DeclaratorContext::TemplateTypeArgContext) + else if (D.getContext() == DeclaratorContext::TemplateArgContext) // T is an accidental identifier; T'. DiagnoseIdentifier = NextToken().isOneOf(tok::comma, tok::greater, tok::greatergreater); diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 4636731671..88a5745350 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -1207,15 +1207,9 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() { EnterExpressionEvaluationContext EnterConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); if (isCXXTypeId(TypeIdAsTemplateArgument)) { - SourceLocation Loc = Tok.getLocation(); TypeResult TypeArg = ParseTypeName( - /*Range=*/nullptr, DeclaratorContext::TemplateTypeArgContext); - if (TypeArg.isInvalid()) - return ParsedTemplateArgument(); - - return ParsedTemplateArgument(ParsedTemplateArgument::Type, - TypeArg.get().getAsOpaquePtr(), - Loc); + /*Range=*/nullptr, DeclaratorContext::TemplateArgContext); + return Actions.ActOnTemplateTypeArgument(TypeArg); } // Try to parse a template template argument. diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 10c084b66d..88c0e176b0 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -1246,6 +1246,17 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case ANK_TentativeDecl: return TPResult::False; case ANK_TemplateName: + // In C++17, this could be a type template for class template argument + // deduction. Try to form a type annotation for it. If we're in a + // template template argument, we'll undo this when checking the + // validity of the argument. + if (getLangOpts().CPlusPlus17) { + if (TryAnnotateTypeOrScopeToken()) + return TPResult::Error; + if (Tok.isNot(tok::identifier)) + break; + } + // A bare type template-name which can't be a template template // argument is an error, and was probably intended to be a type. return GreaterThanIsOperator ? TPResult::True : TPResult::False; @@ -1424,8 +1435,6 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, *HasMissingTypename = true; return TPResult::Ambiguous; } - - // FIXME: Fails to either revert or commit the tentative parse! } else { // Try to resolve the name. If it doesn't exist, assume it was // intended to name a type and keep disambiguating. @@ -1435,19 +1444,33 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case ANK_TentativeDecl: return TPResult::False; case ANK_TemplateName: + // In C++17, this could be a type template for class template + // argument deduction. + if (getLangOpts().CPlusPlus17) { + if (TryAnnotateTypeOrScopeToken()) + return TPResult::Error; + if (Tok.isNot(tok::identifier)) + break; + } + // A bare type template-name which can't be a template template // argument is an error, and was probably intended to be a type. - return GreaterThanIsOperator ? TPResult::True : TPResult::False; + // In C++17, this could be class template argument deduction. + return (getLangOpts().CPlusPlus17 || GreaterThanIsOperator) + ? TPResult::True + : TPResult::False; case ANK_Unresolved: return HasMissingTypename ? TPResult::Ambiguous : TPResult::False; case ANK_Success: - // Annotated it, check again. - assert(Tok.isNot(tok::annot_cxxscope) || - NextToken().isNot(tok::identifier)); - return isCXXDeclarationSpecifier(BracedCastResult, - HasMissingTypename); + break; } + + // Annotated it, check again. + assert(Tok.isNot(tok::annot_cxxscope) || + NextToken().isNot(tok::identifier)); + return isCXXDeclarationSpecifier(BracedCastResult, + HasMissingTypename); } } return TPResult::False; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index ce5a479b4b..f617d240b0 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1775,8 +1775,8 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS, false, NextToken().is(tok::period), nullptr, /*IsCtorOrDtorName=*/false, - /*NonTrivialTypeSourceInfo*/ true, - /*IsClassTemplateDeductionContext*/GreaterThanIsOperator)) { + /*NonTrivialTypeSourceInfo*/true, + /*IsClassTemplateDeductionContext*/true)) { SourceLocation BeginLoc = Tok.getLocation(); if (SS.isNotEmpty()) // it was a C++ qualified type name. BeginLoc = SS.getBeginLoc(); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 7c6af5793f..8dbae9cdce 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -783,6 +783,56 @@ static void maybeDiagnoseTemplateParameterShadow(Sema &SemaRef, Scope *S, SemaRef.DiagnoseTemplateParameterShadow(Loc, PrevDecl); } +/// Convert a parsed type into a parsed template argument. This is mostly +/// trivial, except that we may have parsed a C++17 deduced class template +/// specialization type, in which case we should form a template template +/// argument instead of a type template argument. +ParsedTemplateArgument Sema::ActOnTemplateTypeArgument(TypeResult ParsedType) { + TypeSourceInfo *TInfo; + QualType T = GetTypeFromParser(ParsedType.get(), &TInfo); + if (T.isNull()) + return ParsedTemplateArgument(); + assert(TInfo && "template argument with no location"); + + // If we might have formed a deduced template specialization type, convert + // it to a template template argument. + if (getLangOpts().CPlusPlus17) { + TypeLoc TL = TInfo->getTypeLoc(); + SourceLocation EllipsisLoc; + if (auto PET = TL.getAs()) { + EllipsisLoc = PET.getEllipsisLoc(); + TL = PET.getPatternLoc(); + } + + CXXScopeSpec SS; + if (auto ET = TL.getAs()) { + SS.Adopt(ET.getQualifierLoc()); + TL = ET.getNamedTypeLoc(); + } + + if (auto DTST = TL.getAs()) { + TemplateName Name = DTST.getTypePtr()->getTemplateName(); + if (SS.isSet()) + Name = Context.getQualifiedTemplateName(SS.getScopeRep(), + /*HasTemplateKeyword*/ false, + Name.getAsTemplateDecl()); + ParsedTemplateArgument Result(SS, TemplateTy::make(Name), + DTST.getTemplateNameLoc()); + if (EllipsisLoc.isValid()) + Result = Result.getTemplatePackExpansion(EllipsisLoc); + return Result; + } + } + + // This is a normal type template argument. Note, if the type template + // argument is an injected-class-name for a template, it has a dual nature + // and can be used as either a type or a template. We handle that in + // convertTypeTemplateArgumentToTemplate. + return ParsedTemplateArgument(ParsedTemplateArgument::Type, + ParsedType.get().getAsOpaquePtr(), + TInfo->getTypeLoc().getLocStart()); +} + /// ActOnTypeParameter - Called when a C++ template type parameter /// (e.g., "typename T") has been parsed. Typename specifies whether /// the keyword "typename" was used to declare the type parameter @@ -4148,11 +4198,12 @@ bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, ArgType = Arg.getAsType(); TSI = AL.getTypeSourceInfo(); break; - case TemplateArgument::Template: { + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: { // We have a template type parameter but the template argument // is a template without any arguments. SourceRange SR = AL.getSourceRange(); - TemplateName Name = Arg.getAsTemplate(); + TemplateName Name = Arg.getAsTemplateOrTemplatePattern(); Diag(SR.getBegin(), diag::err_template_missing_args) << (int)getTemplateNameKindForDiagnostics(Name) << Name << SR; if (TemplateDecl *Decl = Name.getAsTemplateDecl()) diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index e323b31f8e..72ade3b1c1 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2852,6 +2852,14 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::BlockLiteralContext: Error = 9; // Block literal break; + case DeclaratorContext::TemplateArgContext: + // Within a template argument list, a deduced template specialization + // type will be reinterpreted as a template template argument. + if (isa(Deduced) && + !D.getNumTypeObjects() && + D.getDeclSpec().getParsedSpecifiers() == DeclSpec::PQ_TypeSpecifier) + break; + LLVM_FALLTHROUGH; case DeclaratorContext::TemplateTypeArgContext: Error = 10; // Template type argument break; @@ -2991,6 +2999,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::CXXNewContext: case DeclaratorContext::CXXCatchContext: case DeclaratorContext::ObjCCatchContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: DiagID = diag::err_type_defined_in_type_specifier; break; @@ -4011,6 +4020,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::LambdaExprParameterContext: case DeclaratorContext::ObjCCatchContext: case DeclaratorContext::TemplateParamContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TypeNameContext: case DeclaratorContext::FunctionalCastContext: @@ -4832,6 +4842,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, !(Kind == Member && D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) && !IsTypedefName && + D.getContext() != DeclaratorContext::TemplateArgContext && D.getContext() != DeclaratorContext::TemplateTypeArgContext) { SourceLocation Loc = D.getLocStart(); SourceRange RemovalRange; @@ -4959,6 +4970,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::ConversionIdContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: + case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: // FIXME: We may want to allow parameter packs in block-literal contexts // in the future. diff --git a/test/CXX/temp/temp.deduct.guide/p3.cpp b/test/CXX/temp/temp.deduct.guide/p3.cpp index e12f7b6ef2..07d1be0593 100644 --- a/test/CXX/temp/temp.deduct.guide/p3.cpp +++ b/test/CXX/temp/temp.deduct.guide/p3.cpp @@ -65,8 +65,8 @@ namespace WrongScope { }; template struct Local {}; void f() { - Local(int) -> Local; // expected-error 2{{expected}} expected-note {{to match}} + Local(int) -> Local; // expected-error {{expected}} using WrongScope::Local; - Local(int) -> Local; // expected-error 2{{expected}} expected-note {{to match}} + Local(int) -> Local; // expected-error {{expected}} } } diff --git a/test/Parser/cxx1z-class-template-argument-deduction.cpp b/test/Parser/cxx1z-class-template-argument-deduction.cpp index b8f5a82d46..bf1d004c8d 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 35{{declared here}} +template struct A { // expected-note 38{{declared here}} constexpr A() {} constexpr A(int) {} constexpr operator int() { return 0; } @@ -17,6 +17,12 @@ namespace template_template_arg { Y ya; // expected-error {{requires template arguments}} X<::A> xcca; Y<::A> ycca; // expected-error {{requires template arguments}} + X xap; // expected-error {{requires template arguments}} + X xca; // expected-error {{requires template arguments}} + X xac; // expected-error {{requires template arguments}} + // FIXME: This should not parse as a template template argument due to the + // trailing attributes. + X xa_attr; template typename = A> struct XD {}; template struct YD {}; // expected-error {{requires template arguments}} @@ -28,6 +34,23 @@ namespace template_template_arg { template struct G { }; // expected-error {{requires template arguments}} } +namespace template_template_arg_pack { + template typename...> struct XP {}; + template struct YP {}; + + struct Z { template struct Q {}; }; // expected-note 2{{here}} + + template using ZId = Z; + + template struct A { + XP::Q...> xe; + YP::Q...> ye; // expected-error {{requires template arguments}} + + XP::Q> xp; // expected-error {{unexpanded parameter pack}} + YP::Q> yp; // expected-error {{requires template arguments}} + }; +} + namespace injected_class_name { template struct A { A(T); @@ -193,3 +216,18 @@ namespace typename_specifier { template void g(typename T::A = 0); // expected-note {{refers to class template member}} void h() { g(); } // expected-error {{no matching function}} } + +namespace parenthesized { + template struct X { X(T); }; + auto n = (X([]{})); +} + +namespace within_template_arg_list { + template struct X { constexpr X(T v) : v(v) {} T v; }; + template struct Y {}; + using T = Y<>; + using T = Y; + using T = Y; + + template struct Z { Y y; }; +} -- 2.40.0