]> granicus.if.org Git - clang/commitdiff
Fix a couple of cases where we would fail to correctly parse deduced class template...
authorRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 28 Feb 2018 03:02:23 +0000 (03:02 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Wed, 28 Feb 2018 03:02:23 +0000 (03:02 +0000)
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
include/clang/Sema/Sema.h
lib/AST/TemplateName.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseTemplate.cpp
lib/Parse/ParseTentative.cpp
lib/Parse/Parser.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaType.cpp
test/CXX/temp/temp.deduct.guide/p3.cpp
test/Parser/cxx1z-class-template-argument-deduction.cpp

index 0baaa76f28612e1233f1da99fb6863b8db424997..e9b116fb71e3ad30b861c71e7125425ab8e9c031 100644 (file)
@@ -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;
     }
 
index ed037a82ced79df9207988ee6a274a6d6a160cd4..35599bf6ce05b6e4fe9e1194d323eb76d58d287e 100644 (file)
@@ -6168,6 +6168,8 @@ public:
   void translateTemplateArguments(const ASTTemplateArgsPtr &In,
                                   TemplateArgumentListInfo &Out);
 
+  ParsedTemplateArgument ActOnTemplateTypeArgument(TypeResult ParsedType);
+
   void NoteAllFoundTemplates(TemplateName Name);
 
   QualType CheckTemplateIdType(TemplateName Template,
index bd04fd8366b3fc0e14fc8598e396fcecb56162fa..548468ed17cdcc5ee7e77920976f75b777054808 100644 (file)
@@ -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<TemplateTemplateParmDecl>(Template))
index 3a3096fd0593e0b260df2e35368094693bd7a30e..deefcaf3251d75df9f6050833b5d3be445e90a91 100644 (file)
@@ -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<int N> is an accidental identifier; T<int N indicates a missing '>'.
       DiagnoseIdentifier =
           NextToken().isOneOf(tok::comma, tok::greater, tok::greatergreater);
index 463673167194fae08e5960e8a6807a3b68760218..88a5745350d6d45da1b7ea0e4f2989309af926e7 100644 (file)
@@ -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.
index 10c084b66dac49ad5ed1b82108a930542c7c3697..88c0e176b02e8b1da1e8c6a3eec1f7af94384838 100644 (file)
@@ -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;
index ce5a479b4b3f7fbd35792de17bacfbc1af9907cd..f617d240b0927fea3e3dfbd7db73e8ed0e6de653 100644 (file)
@@ -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();
index 7c6af5793fc55f552f1233668d4612af2ff27a86..8dbae9cdcea62af6437e2fb780b18b3ba82e98a7 100644 (file)
@@ -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<PackExpansionTypeLoc>()) {
+      EllipsisLoc = PET.getEllipsisLoc();
+      TL = PET.getPatternLoc();
+    }
+
+    CXXScopeSpec SS;
+    if (auto ET = TL.getAs<ElaboratedTypeLoc>()) {
+      SS.Adopt(ET.getQualifierLoc());
+      TL = ET.getNamedTypeLoc();
+    }
+
+    if (auto DTST = TL.getAs<DeducedTemplateSpecializationTypeLoc>()) {
+      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())
index e323b31f8e7539fe7409afceeac2760d15ff3f25..72ade3b1c1e3e34351d71de84bf49b6e2ef9f660 100644 (file)
@@ -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<DeducedTemplateSpecializationType>(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.
index e12f7b6ef255e18e5fa93eaaacfb66ba7b73a398..07d1be0593f42d2702d77b2992d9515dd05c79ec 100644 (file)
@@ -65,8 +65,8 @@ namespace WrongScope {
   };
   template<typename T> struct Local {};
   void f() {
-    Local(int) -> Local<int>; // expected-error 2{{expected}} expected-note {{to match}}
+    Local(int) -> Local<int>; // expected-error {{expected}}
     using WrongScope::Local;
-    Local(int) -> Local<int>; // expected-error 2{{expected}} expected-note {{to match}}
+    Local(int) -> Local<int>; // expected-error {{expected}}
   }
 }
index b8f5a82d461bfa12dcee4fc402e319a464a99b8a..bf1d004c8d2d1028cf28dc8c477c734eab20594e 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -verify %s
 
-template <typename T> struct A { // expected-note 35{{declared here}}
+template <typename T> 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<A> ya; // expected-error {{requires template arguments}}
   X<::A> xcca;
   Y<::A> ycca; // expected-error {{requires template arguments}}
+  X<A*> xap; // expected-error {{requires template arguments}}
+  X<const A> xca; // expected-error {{requires template arguments}}
+  X<A const> xac; // expected-error {{requires template arguments}}
+  // FIXME: This should not parse as a template template argument due to the
+  // trailing attributes.
+  X<A [[]]> xa_attr;
 
   template<template<typename> typename = A> struct XD {};
   template<typename = A> struct YD {}; // expected-error {{requires template arguments}}
@@ -28,6 +34,23 @@ namespace template_template_arg {
   template<typename T = A> struct G { }; // expected-error {{requires template arguments}}
 }
 
+namespace template_template_arg_pack {
+  template<template<typename> typename...> struct XP {};
+  template<typename...> struct YP {};
+
+  struct Z { template<typename T> struct Q {}; }; // expected-note 2{{here}}
+  
+  template<typename T> using ZId = Z;
+
+  template<typename ...Ts> struct A {
+    XP<ZId<Ts>::Q...> xe;
+    YP<ZId<Ts>::Q...> ye; // expected-error {{requires template arguments}}
+
+    XP<ZId<Ts>::Q> xp; // expected-error {{unexpanded parameter pack}}
+    YP<ZId<Ts>::Q> yp; // expected-error {{requires template arguments}}
+  };
+}
+
 namespace injected_class_name {
   template<typename T> struct A {
     A(T);
@@ -193,3 +216,18 @@ namespace typename_specifier {
   template<typename T> void g(typename T::A = 0); // expected-note {{refers to class template member}}
   void h() { g<X>(); } // expected-error {{no matching function}}
 }
+
+namespace parenthesized {
+  template<typename T> struct X { X(T); };                    
+  auto n = (X([]{}));
+}
+
+namespace within_template_arg_list {
+  template<typename T> struct X { constexpr X(T v) : v(v) {} T v; };
+  template<int N = X(1).v> struct Y {};
+  using T = Y<>;
+  using T = Y<X(1).v>;
+  using T = Y<within_template_arg_list::X(1).v>;
+
+  template<int ...N> struct Z { Y<X(N)...> y; };
+}