r210607 - Recover from missing typenames on template args for MSVC compatibility

Reid Kleckner reid at kleckner.net
Tue Jun 10 16:29:48 PDT 2014


Author: rnk
Date: Tue Jun 10 18:29:48 2014
New Revision: 210607

URL: http://llvm.org/viewvc/llvm-project?rev=210607&view=rev
Log:
Recover from missing typenames on template args for MSVC compatibility

While matching a non-type template argument against a known template
type parameter we now modify the AST's TemplateArgumentLoc to assume the
user wrote typename.  Under -fms-compatibility, we downgrade our
diagnostic from an error to an extwarn.

Reviewed by: rsmith

Differential Revision: http://reviews.llvm.org/D4049

Modified:
    cfe/trunk/include/clang/AST/TemplateBase.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/SemaTemplate/typename-specifier.cpp

Modified: cfe/trunk/include/clang/AST/TemplateBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/TemplateBase.h?rev=210607&r1=210606&r2=210607&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/TemplateBase.h (original)
+++ cfe/trunk/include/clang/AST/TemplateBase.h Tue Jun 10 18:29:48 2014
@@ -543,6 +543,10 @@ public:
     return Arguments[I];
   }
 
+  TemplateArgumentLoc &operator[](unsigned I) {
+    return Arguments[I];
+  }
+
   void addArgument(const TemplateArgumentLoc &Loc) {
     Arguments.push_back(Loc);
   }

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=210607&r1=210606&r2=210607&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Jun 10 18:29:48 2014
@@ -3111,6 +3111,10 @@ def err_template_arg_must_be_type : Erro
   "template argument for template type parameter must be a type">;
 def err_template_arg_must_be_type_suggest : Error<
   "template argument for template type parameter must be a type; did you forget 'typename'?">;
+def ext_ms_template_type_arg_missing_typename : ExtWarn<
+  "template argument for template type parameter must be a type; "
+  "omitted 'typename' is a Microsoft extension">,
+  InGroup<Microsoft>;
 def err_template_arg_must_be_expr : Error<
   "template argument for non-type template parameter must be an expression">;
 def err_template_arg_nontype_ambig : Error<

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=210607&r1=210606&r2=210607&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Tue Jun 10 18:29:48 2014
@@ -5397,7 +5397,7 @@ public:
   };
 
   bool CheckTemplateArgument(NamedDecl *Param,
-                             const TemplateArgumentLoc &Arg,
+                             TemplateArgumentLoc &Arg,
                              NamedDecl *Template,
                              SourceLocation TemplateLoc,
                              SourceLocation RAngleLoc,
@@ -5433,7 +5433,7 @@ public:
                            SmallVectorImpl<TemplateArgument> &Converted);
 
   bool CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
-                                 const TemplateArgumentLoc &Arg,
+                                 TemplateArgumentLoc &Arg,
                            SmallVectorImpl<TemplateArgument> &Converted);
 
   bool CheckTemplateArgument(TemplateTypeParmDecl *Param,
@@ -5443,7 +5443,7 @@ public:
                                    TemplateArgument &Converted,
                                CheckTemplateArgumentKind CTAK = CTAK_Specified);
   bool CheckTemplateArgument(TemplateTemplateParmDecl *Param,
-                             const TemplateArgumentLoc &Arg,
+                             TemplateArgumentLoc &Arg,
                              unsigned ArgumentPackIndex);
 
   ExprResult

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=210607&r1=210606&r2=210607&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Jun 10 18:29:48 2014
@@ -2181,6 +2181,17 @@ Sema::BuildQualifiedDeclarationNameExpr(
     return ExprError();
   }
 
+  if (R.isSingleResult() && R.getAsSingle<TypeDecl>()) {
+    // Diagnose a missing typename if this resolved unambiguously to a type in a
+    // dependent context.
+    // FIXME: Issue a fixit and recover as though the user had written
+    // 'typename'.
+    Diag(SS.getBeginLoc(), diag::err_typename_missing)
+        << SS.getScopeRep() << NameInfo.getName().getAsString()
+        << SourceRange(SS.getBeginLoc(), NameInfo.getEndLoc());
+    return ExprError();
+  }
+
   // Defend against this resolving to an implicit member access. We usually
   // won't get here if this might be a legitimate a class member (we end up in
   // BuildMemberReferenceExpr instead), but this can be valid if we're forming

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=210607&r1=210606&r2=210607&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Tue Jun 10 18:29:48 2014
@@ -2999,9 +2999,11 @@ TemplateNameKind Sema::ActOnDependentTem
 }
 
 bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
-                                     const TemplateArgumentLoc &AL,
+                                     TemplateArgumentLoc &AL,
                           SmallVectorImpl<TemplateArgument> &Converted) {
   const TemplateArgument &Arg = AL.getArgument();
+  QualType ArgType;
+  TypeSourceInfo *TSI = nullptr;
 
   // Check template type parameter.
   switch(Arg.getKind()) {
@@ -3009,6 +3011,8 @@ bool Sema::CheckTemplateTypeArgument(Tem
     // C++ [temp.arg.type]p1:
     //   A template-argument for a template-parameter which is a
     //   type shall be a type-id.
+    ArgType = Arg.getAsType();
+    TSI = AL.getTypeSourceInfo();
     break;
   case TemplateArgument::Template: {
     // We have a template type parameter but the template argument
@@ -3043,18 +3047,38 @@ bool Sema::CheckTemplateTypeArgument(Tem
       }
     }
 
-    if (NameInfo.getName().isIdentifier()) {
+    if (auto *II = NameInfo.getName().getAsIdentifierInfo()) {
       LookupResult Result(*this, NameInfo, LookupOrdinaryName);
       LookupParsedName(Result, CurScope, &SS);
 
       if (Result.getAsSingle<TypeDecl>() ||
           Result.getResultKind() ==
-            LookupResult::NotFoundInCurrentInstantiation) {
-        // FIXME: Add a FixIt and fix up the template argument for recovery.
+              LookupResult::NotFoundInCurrentInstantiation) {
+        // Suggest that the user add 'typename' before the NNS.
         SourceLocation Loc = AL.getSourceRange().getBegin();
-        Diag(Loc, diag::err_template_arg_must_be_type_suggest);
+        Diag(Loc, getLangOpts().MSVCCompat
+                      ? diag::ext_ms_template_type_arg_missing_typename
+                      : diag::err_template_arg_must_be_type_suggest)
+            << FixItHint::CreateInsertion(Loc, "typename ");
         Diag(Param->getLocation(), diag::note_template_param_here);
-        return true;
+
+        // Recover by synthesizing a type using the location information that we
+        // already have.
+        ArgType =
+            Context.getDependentNameType(ETK_Typename, SS.getScopeRep(), II);
+        TypeLocBuilder TLB;
+        DependentNameTypeLoc TL = TLB.push<DependentNameTypeLoc>(ArgType);
+        TL.setElaboratedKeywordLoc(SourceLocation(/*synthesized*/));
+        TL.setQualifierLoc(SS.getWithLocInContext(Context));
+        TL.setNameLoc(NameInfo.getLoc());
+        TSI = TLB.getTypeSourceInfo(Context, ArgType);
+
+        // Overwrite our input TemplateArgumentLoc so that we can recover
+        // properly.
+        AL = TemplateArgumentLoc(TemplateArgument(ArgType),
+                                 TemplateArgumentLocInfo(TSI));
+
+        break;
       }
     }
     // fallthrough
@@ -3070,11 +3094,11 @@ bool Sema::CheckTemplateTypeArgument(Tem
   }
   }
 
-  if (CheckTemplateArgument(Param, AL.getTypeSourceInfo()))
+  if (CheckTemplateArgument(Param, TSI))
     return true;
 
   // Add the converted template type argument.
-  QualType ArgType = Context.getCanonicalType(Arg.getAsType());
+  ArgType = Context.getCanonicalType(ArgType);
   
   // Objective-C ARC:
   //   If an explicitly-specified template argument type is a lifetime type
@@ -3356,7 +3380,7 @@ Sema::SubstDefaultTemplateArgumentIfAvai
 ///
 /// \returns true on error, false otherwise.
 bool Sema::CheckTemplateArgument(NamedDecl *Param,
-                                 const TemplateArgumentLoc &Arg,
+                                 TemplateArgumentLoc &Arg,
                                  NamedDecl *Template,
                                  SourceLocation TemplateLoc,
                                  SourceLocation RAngleLoc,
@@ -5078,7 +5102,7 @@ ExprResult Sema::CheckTemplateArgument(N
 /// This routine implements the semantics of C++ [temp.arg.template].
 /// It returns true if an error occurred, and false otherwise.
 bool Sema::CheckTemplateArgument(TemplateTemplateParmDecl *Param,
-                                 const TemplateArgumentLoc &Arg,
+                                 TemplateArgumentLoc &Arg,
                                  unsigned ArgumentPackIndex) {
   TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
   TemplateDecl *Template = Name.getAsTemplateDecl();

Modified: cfe/trunk/test/SemaTemplate/typename-specifier.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/typename-specifier.cpp?rev=210607&r1=210606&r2=210607&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/typename-specifier.cpp (original)
+++ cfe/trunk/test/SemaTemplate/typename-specifier.cpp Tue Jun 10 18:29:48 2014
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unused
+// RUN: %clang_cc1 -fsyntax-only -verify %s -Wno-unused -fms-compatibility -DMSVC
 namespace N {
   struct A {
     typedef int type;
@@ -136,19 +137,106 @@ class ExampleClass1 {
   };
 
   void foo() {
-    pair<ExampleItemSet::iterator, int> i; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#ifdef MSVC
+    // expected-warning at +4 {{omitted 'typename' is a Microsoft extension}}
+#else
+    // expected-error at +2 {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#endif
+    pair<ExampleItemSet::iterator, int> i;
     pair<this->ExampleItemSet::iterator, int> i; // expected-error-re {{template argument for template type parameter must be a type{{$}}}}
     pair<ExampleItemSet::operator[], int> i; // expected-error-re {{template argument for template type parameter must be a type{{$}}}}
   }
-  pair<ExampleItemSet::iterator, int> elt; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#ifdef MSVC
+    // expected-warning at +4 {{omitted 'typename' is a Microsoft extension}}
+#else
+  // expected-error at +2 {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#endif
+  pair<ExampleItemSet::iterator, int> elt;
 
 
   typedef map<int, ExampleItem*> ExampleItemMap;
 
   static void bar() {
-    pair<ExampleItemMap::iterator, int> i; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#ifdef MSVC
+    // expected-warning at +4 {{omitted 'typename' is a Microsoft extension}}
+#else
+    // expected-error at +2 {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#endif
+    pair<ExampleItemMap::iterator, int> i;
   }
-  pair<ExampleItemMap::iterator, int> entry; // expected-error {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#ifdef MSVC
+    // expected-warning at +4 {{omitted 'typename' is a Microsoft extension}}
+#else
+  // expected-error at +2 {{template argument for template type parameter must be a type; did you forget 'typename'?}}
+#endif
+  pair<ExampleItemMap::iterator, int> entry;
   pair<bar, int> foobar; // expected-error {{template argument for template type parameter must be a type}}
 };
 } // namespace missing_typename
+
+namespace missing_typename_and_base {
+template <class T> struct Bar {}; // expected-note 1+ {{template parameter is declared here}}
+template <typename T>
+struct Foo : T {
+
+  // FIXME: MSVC accepts this code.
+  Bar<TypeInBase> x; // expected-error {{use of undeclared identifier 'TypeInBase'}}
+
+#ifdef MSVC
+  // expected-warning at +4 {{omitted 'typename' is a Microsoft extension}}
+#else
+  // expected-error at +2 {{must be a type; did you forget 'typename'?}}
+#endif
+  Bar<T::TypeInBase> y;
+
+#ifdef MSVC
+  // expected-warning at +4 {{omitted 'typename' is a Microsoft extension}}
+#else
+  // expected-error at +2 {{must be a type; did you forget 'typename'?}}
+#endif
+  Bar<T::NestedRD::TypeInNestedRD> z;
+
+};
+struct Base {
+  typedef int TypeInBase;
+  struct NestedRD {
+    typedef int TypeInNestedRD;
+  };
+};
+Foo<Base> x;
+} // namespace missing_typename_and_base
+
+namespace func_type_vs_construct_tmp {
+template <typename> struct S { typedef int type; };
+template <typename T> void f();
+template <int N> void f();
+
+// expected-error at +1 {{missing 'typename' prior to dependent type name 'S<int>::type'}}
+template <typename T> void g() { f</*typename*/ S<T>::type(int())>(); }
+
+// Adding typename does fix the diagnostic.
+template <typename T> void h() { f<typename S<T>::type(int())>(); }
+
+void j() {
+  g<int>(); // expected-note-re {{in instantiation {{.*}} requested here}}
+  h<int>();
+}
+} // namespace func_type_vs_construct_tmp
+
+namespace pointer_vs_multiply {
+int x;
+// expected-error at +1 {{missing 'typename' prior to dependent type name 'B::type_or_int'}}
+template <typename T> void g() { T::type_or_int * x; }
+// expected-error at +1 {{typename specifier refers to non-type member 'type_or_int' in 'pointer_vs_multiply::A'}}
+template <typename T> void h() { typename T::type_or_int * x; }
+
+struct A { static const int type_or_int = 5; }; // expected-note {{referenced member 'type_or_int' is declared here}}
+struct B { typedef int type_or_int; };
+
+void j() {
+  g<A>();
+  g<B>(); // expected-note-re {{in instantiation {{.*}} requested here}}
+  h<A>(); // expected-note-re {{in instantiation {{.*}} requested here}}
+  h<B>();
+}
+} // namespace pointer_vs_multiply





More information about the cfe-commits mailing list