r332076 - Improve diagnostics and error recovery for template name lookup.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu May 10 19:43:08 PDT 2018


Author: rsmith
Date: Thu May 10 19:43:08 2018
New Revision: 332076

URL: http://llvm.org/viewvc/llvm-project?rev=332076&view=rev
Log:
Improve diagnostics and error recovery for template name lookup.

For 'x::template y', consistently give a "no member named 'y' in 'x'"
diagnostic if there is no such member, and give a 'template keyword not
followed by a template' name error if there is such a member but it's not a
template. In the latter case, add a note pointing at the non-template.

Don't suggest inserting a 'template' keyword in 'X::Y<' if X is dependent
if the lookup of X::Y was actually not a dependent lookup and found only
non-templates.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseExprCXX.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprMember.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/TreeTransform.h
    cfe/trunk/test/CXX/drs/dr1xx.cpp
    cfe/trunk/test/CXX/drs/dr3xx.cpp
    cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
    cfe/trunk/test/SemaCXX/invalid-template-specifier.cpp
    cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm
    cfe/trunk/test/SemaTemplate/dependent-base-classes.cpp
    cfe/trunk/test/SemaTemplate/metafun-apply.cpp
    cfe/trunk/test/SemaTemplate/nested-name-spec-template.cpp
    cfe/trunk/test/SemaTemplate/template-id-expr.cpp
    cfe/trunk/test/SemaTemplate/typo-dependent-name.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu May 10 19:43:08 2018
@@ -4505,6 +4505,8 @@ def note_using_value_decl_missing_typena
 
 def err_template_kw_refers_to_non_template : Error<
   "%0 following the 'template' keyword does not refer to a template">;
+def note_template_kw_refers_to_non_template : Note<
+  "declared as a non-template here">;
 def err_template_kw_refers_to_class_template : Error<
   "'%0%1' instantiated to a class template, not a function template">;
 def note_referenced_class_template : Note<

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu May 10 19:43:08 2018
@@ -6090,9 +6090,10 @@ public:
   bool hasAnyAcceptableTemplateNames(LookupResult &R,
                                      bool AllowFunctionTemplates = true);
 
-  void LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS,
+  bool LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS,
                           QualType ObjectType, bool EnteringContext,
-                          bool &MemberOfUnknownSpecialization);
+                          bool &MemberOfUnknownSpecialization,
+                          SourceLocation TemplateKWLoc = SourceLocation());
 
   TemplateNameKind isTemplateName(Scope *S,
                                   CXXScopeSpec &SS,

Modified: cfe/trunk/lib/Parse/ParseExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExprCXX.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExprCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExprCXX.cpp Thu May 10 19:43:08 2018
@@ -515,7 +515,7 @@ bool Parser::ParseOptionalCXXScopeSpecif
           << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
 
         if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName(
-                getCurScope(), SS, SourceLocation(), TemplateName, ObjectType,
+                getCurScope(), SS, Tok.getLocation(), TemplateName, ObjectType,
                 EnteringContext, Template, /*AllowInjectedClassName*/ true)) {
           // Consume the identifier.
           ConsumeToken();

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Thu May 10 19:43:08 2018
@@ -2087,8 +2087,9 @@ Sema::ActOnIdExpression(Scope *S, CXXSco
     // this becomes a performance hit, we can work harder to preserve those
     // results until we get here but it's likely not worth it.
     bool MemberOfUnknownSpecialization;
-    LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false,
-                       MemberOfUnknownSpecialization);
+    if (LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false,
+                           MemberOfUnknownSpecialization, TemplateKWLoc))
+      return ExprError();
 
     if (MemberOfUnknownSpecialization ||
         (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation))

Modified: cfe/trunk/lib/Sema/SemaExprMember.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprMember.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprMember.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprMember.cpp Thu May 10 19:43:08 2018
@@ -640,6 +640,7 @@ static bool LookupMemberExprInRecord(Sem
                                      const RecordType *RTy,
                                      SourceLocation OpLoc, bool IsArrow,
                                      CXXScopeSpec &SS, bool HasTemplateArgs,
+                                     SourceLocation TemplateKWLoc,
                                      TypoExpr *&TE) {
   SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange();
   RecordDecl *RDecl = RTy->getDecl();
@@ -649,13 +650,13 @@ static bool LookupMemberExprInRecord(Sem
                                   BaseRange))
     return true;
 
-  if (HasTemplateArgs) {
+  if (HasTemplateArgs || TemplateKWLoc.isValid()) {
     // LookupTemplateName doesn't expect these both to exist simultaneously.
     QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0);
 
     bool MOUS;
-    SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS);
-    return false;
+    return SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS,
+                                      TemplateKWLoc);
   }
 
   DeclContext *DC = RDecl;
@@ -733,7 +734,8 @@ static bool LookupMemberExprInRecord(Sem
 static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
                                    ExprResult &BaseExpr, bool &IsArrow,
                                    SourceLocation OpLoc, CXXScopeSpec &SS,
-                                   Decl *ObjCImpDecl, bool HasTemplateArgs);
+                                   Decl *ObjCImpDecl, bool HasTemplateArgs,
+                                   SourceLocation TemplateKWLoc);
 
 ExprResult
 Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
@@ -761,7 +763,7 @@ Sema::BuildMemberReferenceExpr(Expr *Bas
     if (IsArrow) RecordTy = RecordTy->getAs<PointerType>()->getPointeeType();
     if (LookupMemberExprInRecord(
             *this, R, nullptr, RecordTy->getAs<RecordType>(), OpLoc, IsArrow,
-            SS, TemplateKWLoc.isValid() || TemplateArgs != nullptr, TE))
+            SS, TemplateArgs != nullptr, TemplateKWLoc, TE))
       return ExprError();
     if (TE)
       return TE;
@@ -772,7 +774,7 @@ Sema::BuildMemberReferenceExpr(Expr *Bas
     ExprResult Result =
         LookupMemberExpr(*this, R, BaseResult, IsArrow, OpLoc, SS,
                          ExtraArgs ? ExtraArgs->ObjCImpDecl : nullptr,
-                         TemplateKWLoc.isValid() || TemplateArgs != nullptr);
+                         TemplateArgs != nullptr, TemplateKWLoc);
 
     if (BaseResult.isInvalid())
       return ExprError();
@@ -1226,7 +1228,8 @@ Sema::PerformMemberExprBaseConversion(Ex
 static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
                                    ExprResult &BaseExpr, bool &IsArrow,
                                    SourceLocation OpLoc, CXXScopeSpec &SS,
-                                   Decl *ObjCImpDecl, bool HasTemplateArgs) {
+                                   Decl *ObjCImpDecl, bool HasTemplateArgs,
+                                   SourceLocation TemplateKWLoc) {
   assert(BaseExpr.get() && "no base expression");
 
   // Perform default conversions.
@@ -1276,8 +1279,8 @@ static ExprResult LookupMemberExpr(Sema
   // Handle field access to simple records.
   if (const RecordType *RTy = BaseType->getAs<RecordType>()) {
     TypoExpr *TE = nullptr;
-    if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy,
-                                 OpLoc, IsArrow, SS, HasTemplateArgs, TE))
+    if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, OpLoc, IsArrow, SS,
+                                 HasTemplateArgs, TemplateKWLoc, TE))
       return ExprError();
 
     // Returning valid-but-null is how we indicate to the caller that
@@ -1315,7 +1318,7 @@ static ExprResult LookupMemberExpr(Sema
                                            OpLoc, S.Context.getObjCClassType());
       if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
         return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
-                                ObjCImpDecl, HasTemplateArgs);
+                                ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
       goto fail;
     }
 
@@ -1509,7 +1512,7 @@ static ExprResult LookupMemberExpr(Sema
       // use the 'id' redefinition in this case.
       if (IsArrow && ShouldTryAgainWithRedefinitionType(S, BaseExpr))
         return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
-                                ObjCImpDecl, HasTemplateArgs);
+                                ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
 
       return ExprError(S.Diag(MemberLoc, diag::err_property_not_found)
                          << MemberName << BaseType);
@@ -1522,7 +1525,7 @@ static ExprResult LookupMemberExpr(Sema
       if (!MD) {
         if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
           return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
-                                  ObjCImpDecl, HasTemplateArgs);
+                                  ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
 
         goto fail;
       }
@@ -1564,7 +1567,7 @@ static ExprResult LookupMemberExpr(Sema
 
       if (ShouldTryAgainWithRedefinitionType(S, BaseExpr))
         return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
-                                ObjCImpDecl, HasTemplateArgs);
+                                ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
 
       return ExprError(S.Diag(MemberLoc, diag::err_property_not_found)
                          << MemberName << BaseType);
@@ -1609,7 +1612,7 @@ static ExprResult LookupMemberExpr(Sema
     BaseExpr = S.ImpCastExprToType(
         BaseExpr.get(), S.Context.getObjCSelRedefinitionType(), CK_BitCast);
     return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
-                            ObjCImpDecl, HasTemplateArgs);
+                            ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
   }
 
   // Failure cases.
@@ -1632,7 +1635,7 @@ static ExprResult LookupMemberExpr(Sema
       // Recurse as an -> access.
       IsArrow = true;
       return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
-                              ObjCImpDecl, HasTemplateArgs);
+                              ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
     }
   }
 
@@ -1646,7 +1649,7 @@ static ExprResult LookupMemberExpr(Sema
       return ExprError();
     BaseExpr = S.DefaultFunctionArrayConversion(BaseExpr.get());
     return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS,
-                            ObjCImpDecl, HasTemplateArgs);
+                            ObjCImpDecl, HasTemplateArgs, TemplateKWLoc);
   }
 
   S.Diag(OpLoc, diag::err_typecheck_member_reference_struct_union)

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Thu May 10 19:43:08 2018
@@ -189,8 +189,9 @@ TemplateNameKind Sema::isTemplateName(Sc
   QualType ObjectType = ObjectTypePtr.get();
 
   LookupResult R(*this, TName, Name.getLocStart(), LookupOrdinaryName);
-  LookupTemplateName(R, S, SS, ObjectType, EnteringContext,
-                     MemberOfUnknownSpecialization);
+  if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext,
+                         MemberOfUnknownSpecialization))
+    return TNK_Non_template;
   if (R.empty()) return TNK_Non_template;
   if (R.isAmbiguous()) {
     // Suppress diagnostics;  we'll redo this lookup later.
@@ -252,8 +253,10 @@ bool Sema::isDeductionGuideName(Scope *S
   // syntactic form of a deduction guide is enough to identify it even
   // if we can't look up the template name at all.
   LookupResult R(*this, DeclarationName(&Name), NameLoc, LookupOrdinaryName);
-  LookupTemplateName(R, S, SS, /*ObjectType*/QualType(),
-                     /*EnteringContext*/false, MemberOfUnknownSpecialization);
+  if (LookupTemplateName(R, S, SS, /*ObjectType*/ QualType(),
+                         /*EnteringContext*/ false,
+                         MemberOfUnknownSpecialization))
+    return false;
 
   if (R.empty()) return false;
   if (R.isAmbiguous()) {
@@ -298,39 +301,40 @@ bool Sema::DiagnoseUnknownTemplateName(c
   return true;
 }
 
-void Sema::LookupTemplateName(LookupResult &Found,
+bool Sema::LookupTemplateName(LookupResult &Found,
                               Scope *S, CXXScopeSpec &SS,
                               QualType ObjectType,
                               bool EnteringContext,
-                              bool &MemberOfUnknownSpecialization) {
+                              bool &MemberOfUnknownSpecialization,
+                              SourceLocation TemplateKWLoc) {
   // Determine where to perform name lookup
   MemberOfUnknownSpecialization = false;
   DeclContext *LookupCtx = nullptr;
-  bool isDependent = false;
+  bool IsDependent = false;
   if (!ObjectType.isNull()) {
     // This nested-name-specifier occurs in a member access expression, e.g.,
     // x->B::f, and we are looking into the type of the object.
     assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
     LookupCtx = computeDeclContext(ObjectType);
-    isDependent = ObjectType->isDependentType();
-    assert((isDependent || !ObjectType->isIncompleteType() ||
+    IsDependent = !LookupCtx;
+    assert((IsDependent || !ObjectType->isIncompleteType() ||
             ObjectType->castAs<TagType>()->isBeingDefined()) &&
            "Caller should have completed object type");
 
     // Template names cannot appear inside an Objective-C class or object type.
     if (ObjectType->isObjCObjectOrInterfaceType()) {
       Found.clear();
-      return;
+      return false;
     }
   } else if (SS.isSet()) {
     // This nested-name-specifier occurs after another nested-name-specifier,
     // so long into the context associated with the prior nested-name-specifier.
     LookupCtx = computeDeclContext(SS, EnteringContext);
-    isDependent = isDependentScopeSpecifier(SS);
+    IsDependent = !LookupCtx;
 
     // The declaration context must be complete.
     if (LookupCtx && RequireCompleteDeclContext(SS, LookupCtx))
-      return;
+      return true;
   }
 
   bool ObjectTypeSearchedInScope = false;
@@ -341,34 +345,43 @@ void Sema::LookupTemplateName(LookupResu
     // expression or the declaration context associated with a prior
     // nested-name-specifier.
     LookupQualifiedName(Found, LookupCtx);
-    if (!ObjectType.isNull() && Found.empty()) {
-      // C++ [basic.lookup.classref]p1:
-      //   In a class member access expression (5.2.5), if the . or -> token is
-      //   immediately followed by an identifier followed by a <, the
-      //   identifier must be looked up to determine whether the < is the
-      //   beginning of a template argument list (14.2) or a less-than operator.
-      //   The identifier is first looked up in the class of the object
-      //   expression. If the identifier is not found, it is then looked up in
-      //   the context of the entire postfix-expression and shall name a class
-      //   or function template.
-      if (S) LookupName(Found, S);
-      ObjectTypeSearchedInScope = true;
+
+    // FIXME: The C++ standard does not clearly specify what happens in the
+    // case where the object type is dependent, and implementations vary. In
+    // Clang, we treat a name after a . or -> as a template-name if lookup
+    // finds a non-dependent member or member of the current instantiation that
+    // is a type template, or finds no such members and lookup in the context
+    // of the postfix-expression finds a type template. In the latter case, the
+    // name is nonetheless dependent, and we may resolve it to a member of an
+    // unknown specialization when we come to instantiate the template.
+    IsDependent |= Found.wasNotFoundInCurrentInstantiation();
+  }
+
+  if (!SS.isSet() && (ObjectType.isNull() || Found.empty())) {
+    // C++ [basic.lookup.classref]p1:
+    //   In a class member access expression (5.2.5), if the . or -> token is
+    //   immediately followed by an identifier followed by a <, the
+    //   identifier must be looked up to determine whether the < is the
+    //   beginning of a template argument list (14.2) or a less-than operator.
+    //   The identifier is first looked up in the class of the object
+    //   expression. If the identifier is not found, it is then looked up in
+    //   the context of the entire postfix-expression and shall name a class
+    //   template.
+    if (S)
+      LookupName(Found, S);
+
+    if (!ObjectType.isNull()) {
+      //  FIXME: We should filter out all non-type templates here, particularly
+      //  variable templates and concepts. But the exclusion of alias templates
+      //  and template template parameters is a wording defect.
       AllowFunctionTemplatesInLookup = false;
+      ObjectTypeSearchedInScope = true;
     }
-  } else if (isDependent && (!S || ObjectType.isNull())) {
-    // We cannot look into a dependent object type or nested nme
-    // specifier.
-    MemberOfUnknownSpecialization = true;
-    return;
-  } else {
-    // Perform unqualified name lookup in the current scope.
-    LookupName(Found, S);
 
-    if (!ObjectType.isNull())
-      AllowFunctionTemplatesInLookup = false;
+    IsDependent |= Found.wasNotFoundInCurrentInstantiation();
   }
 
-  if (Found.empty() && !isDependent) {
+  if (Found.empty() && !IsDependent) {
     // If we did not find any names, attempt to correct any typos.
     DeclarationName Name = Found.getLookupName();
     Found.clear();
@@ -402,11 +415,27 @@ void Sema::LookupTemplateName(LookupResu
     }
   }
 
+  NamedDecl *ExampleLookupResult =
+      Found.empty() ? nullptr : Found.getRepresentativeDecl();
   FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup);
   if (Found.empty()) {
-    if (isDependent)
+    if (IsDependent) {
       MemberOfUnknownSpecialization = true;
-    return;
+      return false;
+    }
+
+    // If a 'template' keyword was used, a lookup that finds only non-template
+    // names is an error.
+    if (ExampleLookupResult && TemplateKWLoc.isValid()) {
+      Diag(Found.getNameLoc(), diag::err_template_kw_refers_to_non_template)
+        << Found.getLookupName() << SS.getRange();
+      Diag(ExampleLookupResult->getLocation(),
+           diag::note_template_kw_refers_to_non_template)
+          << Found.getLookupName();
+      return true;
+    }
+
+    return false;
   }
 
   if (S && !ObjectType.isNull() && !ObjectTypeSearchedInScope &&
@@ -453,6 +482,8 @@ void Sema::LookupTemplateName(LookupResu
       }
     }
   }
+
+  return false;
 }
 
 void Sema::diagnoseExprIntendedAsTemplateName(Scope *S, ExprResult TemplateName,
@@ -4069,15 +4100,17 @@ Sema::BuildQualifiedTemplateIdExpr(CXXSc
 
   bool MemberOfUnknownSpecialization;
   LookupResult R(*this, NameInfo, LookupOrdinaryName);
-  LookupTemplateName(R, (Scope*)nullptr, SS, QualType(), /*Entering*/ false,
-                     MemberOfUnknownSpecialization);
+  if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(),
+                         /*Entering*/false, MemberOfUnknownSpecialization,
+                         TemplateKWLoc))
+    return ExprError();
 
   if (R.isAmbiguous())
     return ExprError();
 
   if (R.empty()) {
-    Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_non_template)
-      << NameInfo.getName() << SS.getRange();
+    Diag(NameInfo.getLoc(), diag::err_no_member)
+      << NameInfo.getName() << DC << SS.getRange();
     return ExprError();
   }
 
@@ -4140,17 +4173,20 @@ TemplateNameKind Sema::ActOnDependentTem
     TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name,
                                           ObjectType, EnteringContext, Result,
                                           MemberOfUnknownSpecialization);
-    if (TNK == TNK_Non_template && LookupCtx->isDependentContext() &&
-        isa<CXXRecordDecl>(LookupCtx) &&
-        (!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
-         cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases())) {
+    if (TNK == TNK_Non_template && MemberOfUnknownSpecialization) {
       // This is a dependent template. Handle it below.
     } else if (TNK == TNK_Non_template) {
-      Diag(Name.getLocStart(),
-           diag::err_template_kw_refers_to_non_template)
-        << GetNameFromUnqualifiedId(Name).getName()
-        << Name.getSourceRange()
-        << TemplateKWLoc;
+      // Do the lookup again to determine if this is a "nothing found" case or
+      // a "not a template" case. FIXME: Refactor isTemplateName so we don't
+      // need to do this.
+      DeclarationNameInfo DNI = GetNameFromUnqualifiedId(Name);
+      LookupResult R(*this, DNI.getName(), Name.getLocStart(),
+                     LookupOrdinaryName);
+      bool MOUS;
+      if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext,
+                              MOUS, TemplateKWLoc))
+        Diag(Name.getLocStart(), diag::err_no_member)
+            << DNI.getName() << LookupCtx << SS.getRange();
       return TNK_Non_template;
     } else {
       // We found something; return it.

Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Thu May 10 19:43:08 2018
@@ -957,6 +957,7 @@ public:
   QualType RebuildDependentTemplateSpecializationType(
                                           ElaboratedTypeKeyword Keyword,
                                           NestedNameSpecifierLoc QualifierLoc,
+                                          SourceLocation TemplateKWLoc,
                                           const IdentifierInfo *Name,
                                           SourceLocation NameLoc,
                                           TemplateArgumentListInfo &Args,
@@ -965,9 +966,9 @@ public:
     // TODO: avoid TemplateName abstraction
     CXXScopeSpec SS;
     SS.Adopt(QualifierLoc);
-    TemplateName InstName
-      = getDerived().RebuildTemplateName(SS, *Name, NameLoc, QualType(),
-                                         nullptr, AllowInjectedClassName);
+    TemplateName InstName = getDerived().RebuildTemplateName(
+        SS, TemplateKWLoc, *Name, NameLoc, QualType(), nullptr,
+        AllowInjectedClassName);
 
     if (InstName.isNull())
       return QualType();
@@ -1146,9 +1147,9 @@ public:
   /// template name. Subclasses may override this routine to provide different
   /// behavior.
   TemplateName RebuildTemplateName(CXXScopeSpec &SS,
+                                   SourceLocation TemplateKWLoc,
                                    const IdentifierInfo &Name,
-                                   SourceLocation NameLoc,
-                                   QualType ObjectType,
+                                   SourceLocation NameLoc, QualType ObjectType,
                                    NamedDecl *FirstQualifierInScope,
                                    bool AllowInjectedClassName);
 
@@ -1160,9 +1161,9 @@ public:
   /// template name. Subclasses may override this routine to provide different
   /// behavior.
   TemplateName RebuildTemplateName(CXXScopeSpec &SS,
+                                   SourceLocation TemplateKWLoc,
                                    OverloadedOperatorKind Operator,
-                                   SourceLocation NameLoc,
-                                   QualType ObjectType,
+                                   SourceLocation NameLoc, QualType ObjectType,
                                    bool AllowInjectedClassName);
 
   /// Build a new template name given a template template parameter pack
@@ -3752,8 +3753,12 @@ TreeTransform<Derived>::TransformTemplat
         ObjectType.isNull())
       return Name;
 
+    // FIXME: Preserve the location of the "template" keyword.
+    SourceLocation TemplateKWLoc = NameLoc;
+
     if (DTN->isIdentifier()) {
       return getDerived().RebuildTemplateName(SS,
+                                              TemplateKWLoc,
                                               *DTN->getIdentifier(),
                                               NameLoc,
                                               ObjectType,
@@ -3761,7 +3766,8 @@ TreeTransform<Derived>::TransformTemplat
                                               AllowInjectedClassName);
     }
 
-    return getDerived().RebuildTemplateName(SS, DTN->getOperator(), NameLoc,
+    return getDerived().RebuildTemplateName(SS, TemplateKWLoc,
+                                            DTN->getOperator(), NameLoc,
                                             ObjectType, AllowInjectedClassName);
   }
 
@@ -4340,6 +4346,7 @@ TypeSourceInfo *TreeTransform<Derived>::
 
     TemplateName Template
       = getDerived().RebuildTemplateName(SS,
+                                         SpecTL.getTemplateKeywordLoc(),
                                          *SpecTL.getTypePtr()->getIdentifier(),
                                          SpecTL.getTemplateNameLoc(),
                                          ObjectType, UnqualLookup,
@@ -6138,8 +6145,8 @@ TransformDependentTemplateSpecialization
     return QualType();
 
   QualType Result = getDerived().RebuildDependentTemplateSpecializationType(
-      T->getKeyword(), QualifierLoc, T->getIdentifier(),
-      TL.getTemplateNameLoc(), NewTemplateArgs,
+      T->getKeyword(), QualifierLoc, TL.getTemplateKeywordLoc(),
+      T->getIdentifier(), TL.getTemplateNameLoc(), NewTemplateArgs,
       /*AllowInjectedClassName*/ false);
   if (Result.isNull())
     return QualType();
@@ -12510,6 +12517,7 @@ TreeTransform<Derived>::RebuildTemplateN
 template<typename Derived>
 TemplateName
 TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
+                                            SourceLocation TemplateKWLoc,
                                             const IdentifierInfo &Name,
                                             SourceLocation NameLoc,
                                             QualType ObjectType,
@@ -12518,7 +12526,6 @@ TreeTransform<Derived>::RebuildTemplateN
   UnqualifiedId TemplateName;
   TemplateName.setIdentifier(&Name, NameLoc);
   Sema::TemplateTy Template;
-  SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller.
   getSema().ActOnDependentTemplateName(/*Scope=*/nullptr,
                                        SS, TemplateKWLoc, TemplateName,
                                        ParsedType::make(ObjectType),
@@ -12530,6 +12537,7 @@ TreeTransform<Derived>::RebuildTemplateN
 template<typename Derived>
 TemplateName
 TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
+                                            SourceLocation TemplateKWLoc,
                                             OverloadedOperatorKind Operator,
                                             SourceLocation NameLoc,
                                             QualType ObjectType,
@@ -12538,7 +12546,6 @@ TreeTransform<Derived>::RebuildTemplateN
   // FIXME: Bogus location information.
   SourceLocation SymbolLocations[3] = { NameLoc, NameLoc, NameLoc };
   Name.setOperatorFunctionId(NameLoc, Operator, SymbolLocations);
-  SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller.
   Sema::TemplateTy Template;
   getSema().ActOnDependentTemplateName(/*Scope=*/nullptr,
                                        SS, TemplateKWLoc, Name,

Modified: cfe/trunk/test/CXX/drs/dr1xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr1xx.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr1xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr1xx.cpp Thu May 10 19:43:08 2018
@@ -71,7 +71,8 @@ namespace dr109 { // dr109: yes
     using T::template f<int>; // expected-error {{'template' keyword not permitted here}} expected-error {{using declaration cannot refer to a template specialization}}
     // FIXME: We shouldn't suggest using the 'template' keyword in a location where it's not valid.
     using T::f<int>; // expected-error {{use 'template' keyword}} expected-error {{using declaration cannot refer to a template specialization}}
-    void g() { this->f<int>(123); } // expected-error {{use 'template'}}
+    // FIXME: The first 'using' above introduces 'f' as a non-template member of 'B', leading to bad recovery:
+    void g() { this->f<int>(123); } // expected-error {{expected '('}}
   };
 }
 

Modified: cfe/trunk/test/CXX/drs/dr3xx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr3xx.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/CXX/drs/dr3xx.cpp (original)
+++ cfe/trunk/test/CXX/drs/dr3xx.cpp Thu May 10 19:43:08 2018
@@ -123,8 +123,7 @@ namespace dr305 { // dr305: no
     template<typename T> using T2 = T;
   };
   void k(Z *z) {
-    // FIXME: This diagnostic is terrible.
-    z->~T1<int>(); // expected-error {{'T1' following the 'template' keyword does not refer to a template}} expected-error +{{}}
+    z->~T1<int>(); // expected-error {{no member named 'T1' in 'dr305::Z'}} expected-error +{{}}
     z->~T2<int>(); // expected-error {{no member named '~int'}}
     z->~T2<Z>();
   }

Modified: cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1y-variable-templates_in_class.cpp Thu May 10 19:43:08 2018
@@ -296,17 +296,17 @@ namespace in_class_template {
     };
 
     template<typename T> void f() {
-      typename T::template A<int> a; // expected-error {{template name refers to non-type template 'S::A'}}
+      typename T::template A<int> a; // expected-error {{template name refers to non-type template 'S::template A'}}
     }
     template<typename T> void g() {
-      T::template A<int>::B = 0; // expected-error {{template name refers to non-type template 'S::A'}}
+      T::template A<int>::B = 0; // expected-error {{template name refers to non-type template 'S::template A'}}
     }
     template<typename T> void h() {
-      class T::template A<int> c; // expected-error {{template name refers to non-type template 'S::A'}}
+      class T::template A<int> c; // expected-error {{template name refers to non-type template 'S::template A'}}
     }
 
     template<typename T>
-    struct X : T::template A<int> {}; // expected-error {{template name refers to non-type template 'S::A'}}
+    struct X : T::template A<int> {}; // expected-error {{template name refers to non-type template 'S::template A'}}
 
     template void f<S>(); // expected-note {{in instantiation of}}
     template void g<S>(); // expected-note {{in instantiation of}}

Modified: cfe/trunk/test/SemaCXX/invalid-template-specifier.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/invalid-template-specifier.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/invalid-template-specifier.cpp (original)
+++ cfe/trunk/test/SemaCXX/invalid-template-specifier.cpp Thu May 10 19:43:08 2018
@@ -7,6 +7,6 @@ const template basic_istream<char>; // e
 
 namespace S {}
 template <class X> class Y {
-  void x() { S::template y<char>(1); } // expected-error {{does not refer to a template}} \
+  void x() { S::template y<char>(1); } // expected-error {{no member named 'y' in namespace 'S'}} \
                                        // expected-error {{unqualified-id}}
 };

Modified: cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm (original)
+++ cfe/trunk/test/SemaObjCXX/parameterized_classes_subst.mm Thu May 10 19:43:08 2018
@@ -419,7 +419,7 @@ struct DependentTemplate {
 };
 
 struct NSMutableDictionaryBuilder {
-  typedef NSMutableDictionary apply;
+  typedef NSMutableDictionary apply; // expected-note 2{{declared as a non-template here}}
 };
 
 typedef DependentTemplate<NSMutableDictionaryBuilder>::type DependentTemplateFail1; // expected-note{{in instantiation of template class}}

Modified: cfe/trunk/test/SemaTemplate/dependent-base-classes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/dependent-base-classes.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/dependent-base-classes.cpp (original)
+++ cfe/trunk/test/SemaTemplate/dependent-base-classes.cpp Thu May 10 19:43:08 2018
@@ -55,7 +55,7 @@ namespace PR6031 {
   struct NoDepBase {
     int foo() {
       class NoDepBase::Nested nested; // expected-error{{no class named 'Nested' in 'NoDepBase<T>'}}
-      typedef typename NoDepBase::template MemberTemplate<T>::type type; // expected-error{{'MemberTemplate' following the 'template' keyword does not refer to a template}} \
+      typedef typename NoDepBase::template MemberTemplate<T>::type type; // expected-error{{no member named 'MemberTemplate' in 'NoDepBase<T>'}} \
       // FIXME: expected-error{{unqualified-id}}
       return NoDepBase::a; // expected-error{{no member named 'a' in 'NoDepBase<T>'}}
     }
@@ -103,7 +103,7 @@ namespace PR6081 {
     template< class X >
     void f0(const X & k)
     {
-      this->template f1<int>()(k); // expected-error{{'f1' following the 'template' keyword does not refer to a template}} \
+      this->template f1<int>()(k); // expected-error{{no member named 'f1' in 'C<T>'}} \
       // FIXME: expected-error{{unqualified-id}} \
       // expected-error{{function-style cast or type construction}} \
       // expected-error{{expected expression}}

Modified: cfe/trunk/test/SemaTemplate/metafun-apply.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/metafun-apply.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/metafun-apply.cpp (original)
+++ cfe/trunk/test/SemaTemplate/metafun-apply.cpp Thu May 10 19:43:08 2018
@@ -15,7 +15,7 @@ struct add_reference {
 };
 
 struct bogus {
-  struct apply {
+  struct apply { // expected-note{{declared as a non-template here}}
     typedef int type;
   };
 };

Modified: cfe/trunk/test/SemaTemplate/nested-name-spec-template.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/nested-name-spec-template.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/nested-name-spec-template.cpp (original)
+++ cfe/trunk/test/SemaTemplate/nested-name-spec-template.cpp Thu May 10 19:43:08 2018
@@ -49,7 +49,7 @@ namespace N {
     struct X;
   };
 
-  struct B;
+  struct B; // expected-note{{declared as a non-template here}}
 }
 
 struct ::N::A<int>::X {
@@ -131,7 +131,7 @@ namespace PR9226 {
 
   template<typename T, typename U>
   struct Y {
-    typedef typename T::template f<U> type; // expected-error{{template name refers to non-type template 'X::f'}}
+    typedef typename T::template f<U> type; // expected-error{{template name refers to non-type template 'X::template f'}}
   };
 
   Y<X, int> yxi; // expected-note{{in instantiation of template class 'PR9226::Y<PR9226::X, int>' requested here}}
@@ -144,7 +144,7 @@ namespace PR9449 {
   template <typename T>
   void f() {
     int s<T>::template n<T>::* f; // expected-error{{implicit instantiation of undefined template 'PR9449::s<int>'}} \
-    // expected-error{{following the 'template' keyword}}
+    // expected-error{{no member named 'n'}}
   }
 
   template void f<int>(); // expected-note{{in instantiation of}}

Modified: cfe/trunk/test/SemaTemplate/template-id-expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/template-id-expr.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/template-id-expr.cpp (original)
+++ cfe/trunk/test/SemaTemplate/template-id-expr.cpp Thu May 10 19:43:08 2018
@@ -56,7 +56,7 @@ struct Y0 {
   template<typename U>
   static void f2(U);
 
-  void f3(int);
+  void f3(int); // expected-note 2{{declared as a non-template here}}
 
   static int f4(int);
   template<typename U>
@@ -100,7 +100,7 @@ struct Y1 {
   template<typename U>
   static void f2(U);
 
-  void f3(int);
+  void f3(int); // expected-note 4{{declared as a non-template here}}
 
   static int f4(int);
   template<typename U>
@@ -131,10 +131,39 @@ struct Y1 {
 
 void use_Y1(Y1<int> y1) { y1.f<int>(); } // expected-note {{in instantiation of}}
 
+template<typename T>
+struct Y2 : Y1<T> {
+  typedef ::Y1<T> Y1;
+
+  template<typename U>
+  void f(Y1 *p) {
+    Y1::template f1<U>(0);
+    Y1::template f1(0);
+    p->template f1(0);
+
+    Y1::template f2<U>(0);
+    Y1::template f2(0);
+
+    Y1::template f3(0); // expected-error {{'f3' following the 'template' keyword does not refer to a template}}
+    Y1::template f3(); // expected-error {{'f3' following the 'template' keyword does not refer to a template}}
+
+    int x;
+    x = Y1::f4(0);
+    x = Y1::f4<int>(0); // expected-error {{use 'template'}} expected-error {{assigning to 'int' from incompatible type 'void'}}
+    x = Y1::template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}}
+
+    x = p->f4(0);
+    x = p->f4<int>(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{use 'template'}}
+    x = p->template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}}
+  }
+};
+
+void use_Y2(Y2<int> y2) { y2.f<int>(0); } // expected-note {{in instantiation of}}
+
 struct A {
   template<int I>
   struct B {
-    static void b1();
+    static void b1(); // expected-note {{declared as a non-template here}}
   };
 };
 

Modified: cfe/trunk/test/SemaTemplate/typo-dependent-name.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/typo-dependent-name.cpp?rev=332076&r1=332075&r2=332076&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/typo-dependent-name.cpp (original)
+++ cfe/trunk/test/SemaTemplate/typo-dependent-name.cpp Thu May 10 19:43:08 2018
@@ -1,18 +1,45 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
+
+using nullptr_t = decltype(nullptr);
 
 template<typename T>
 struct Base {
   T inner;
 };
 
+int z;
+
 template<typename T>
-struct X {
-  template<typename U>
+struct X : Base<T> {
+  static int z;
+
+  template<int U>
   struct Inner {
   };
 
   bool f(T other) {
-    return this->inner < other;
+    // A pair of comparisons; 'inner' is a dependent name so can't be assumed
+    // to be a template.
+    return this->inner < other > ::z;
   }
 };
+
+void use_x(X<int> x) { x.f(0); }
+
+template<typename T>
+struct Y {
+  static int z;
+
+  template<int U>
+  struct Inner : Y { // expected-note {{declared here}}
+  };
+
+  bool f(T other) {
+    // We can determine that 'inner' does not exist at parse time, so can
+    // perform typo correction in this case.
+    return this->inner<other>::z; // expected-error {{no template named 'inner' in 'Y<T>'; did you mean 'Inner'?}}
+  }
+};
+
+struct Q { constexpr operator int() { return 0; } };
+void use_y(Y<Q> x) { x.f(Q()); }




More information about the cfe-commits mailing list