[c++20] Implement P0846R0: allow (ADL-only) calls to template-ids whose
template name is not visible to unqualified lookup.

In order to support this without a severe degradation in our ability to
diagnose typos in template names, this change significantly restructures
the way we handle template-id-shaped syntax for which lookup of the
template name finds nothing.

Instead of eagerly diagnosing an undeclared template name, we now form a
placeholder template-name representing a name that is known to not find
any templates. When the parser sees such a name, it attempts to
disambiguate whether we have a less-than comparison or a template-id.
Any diagnostics or typo-correction for the name are delayed until its
point of use.

The upshot should be a small improvement of our diagostic quality
overall: we now take more syntactic context into account when trying to
resolve an undeclared identifier on the left hand side of a '<'. In
fact, this works well enough that the backwards-compatible portion (for
an undeclared identifier rather than a lookup that finds functions but
no function templates) is enabled in all language modes.

llvm-svn: 360308
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 0504f0d..2d2705d 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1178,12 +1178,17 @@
 /// be either a decl-specifier or a function-style cast, and TPResult::Error
 /// if a parsing error was found and reported.
 ///
-/// If HasMissingTypename is provided, a name with a dependent scope specifier
-/// will be treated as ambiguous if the 'typename' keyword is missing. If this
-/// happens, *HasMissingTypename will be set to 'true'. This will also be used
-/// as an indicator that undeclared identifiers (which will trigger a later
-/// parse error) should be treated as types. Returns TPResult::Ambiguous in
-/// such cases.
+/// If InvalidAsDeclSpec is not null, some cases that would be ill-formed as
+/// declaration specifiers but possibly valid as some other kind of construct
+/// return TPResult::Ambiguous instead of TPResult::False. When this happens,
+/// the intent is to keep trying to disambiguate, on the basis that we might
+/// find a better reason to treat this construct as a declaration later on.
+/// When this happens and the name could possibly be valid in some other
+/// syntactic context, *InvalidAsDeclSpec is set to 'true'. The current cases
+/// that trigger this are:
+///
+///   * When parsing X::Y (with no 'typename') where X is dependent
+///   * When parsing X<Y> where X is undeclared
 ///
 ///         decl-specifier:
 ///           storage-class-specifier
@@ -1281,7 +1286,7 @@
 ///
 Parser::TPResult
 Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
-                                  bool *HasMissingTypename) {
+                                  bool *InvalidAsDeclSpec) {
   switch (Tok.getKind()) {
   case tok::identifier: {
     // Check for need to substitute AltiVec __vector keyword
@@ -1321,7 +1326,7 @@
         // argument is an error, and was probably intended to be a type.
         return GreaterThanIsOperator ? TPResult::True : TPResult::False;
       case ANK_Unresolved:
-        return HasMissingTypename ? TPResult::Ambiguous : TPResult::False;
+        return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False;
       case ANK_Success:
         break;
       }
@@ -1342,7 +1347,7 @@
     }
 
     // We annotated this token as something. Recurse to handle whatever we got.
-    return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
+    return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
   }
 
   case tok::kw_typename:  // typename T::type
@@ -1350,7 +1355,7 @@
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())
       return TPResult::Error;
-    return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
+    return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
 
   case tok::coloncolon: {    // ::foo::bar
     const Token &Next = NextToken();
@@ -1365,7 +1370,7 @@
     // recurse to handle whatever we get.
     if (TryAnnotateTypeOrScopeToken())
       return TPResult::Error;
-    return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename);
+    return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
 
     // decl-specifier:
     //   storage-class-specifier
@@ -1471,6 +1476,16 @@
 
   case tok::annot_template_id: {
     TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+    // If lookup for the template-name found nothing, don't assume we have a
+    // definitive disambiguation result yet.
+    if (TemplateId->Kind == TNK_Undeclared_template && InvalidAsDeclSpec) {
+      // 'template-id(' can be a valid expression but not a valid decl spec if
+      // the template-name is not declared, but we don't consider this to be a
+      // definitive disambiguation. In any other context, it's an error either
+      // way.
+      *InvalidAsDeclSpec = NextToken().is(tok::l_paren);
+      return TPResult::Ambiguous;
+    }
     if (TemplateId->Kind != TNK_Type_template)
       return TPResult::False;
     CXXScopeSpec SS;
@@ -1499,19 +1514,19 @@
           TPResult TPR = TPResult::False;
           if (!isIdentifier)
             TPR = isCXXDeclarationSpecifier(BracedCastResult,
-                                            HasMissingTypename);
+                                            InvalidAsDeclSpec);
 
           if (isIdentifier ||
               TPR == TPResult::True || TPR == TPResult::Error)
             return TPResult::Error;
 
-          if (HasMissingTypename) {
+          if (InvalidAsDeclSpec) {
             // We can't tell whether this is a missing 'typename' or a valid
             // expression.
-            *HasMissingTypename = true;
+            *InvalidAsDeclSpec = true;
             return TPResult::Ambiguous;
           } else {
-            // In MS mode, if HasMissingTypename is not provided, and the tokens
+            // In MS mode, if InvalidAsDeclSpec is not provided, and the tokens
             // are or the form *) or &) *> or &> &&>, this can't be an expression.
             // The typename must be missing.
             if (getLangOpts().MSVCCompat) {
@@ -1547,8 +1562,7 @@
                        ? TPResult::True
                        : TPResult::False;
           case ANK_Unresolved:
-            return HasMissingTypename ? TPResult::Ambiguous
-                                      : TPResult::False;
+            return InvalidAsDeclSpec ? TPResult::Ambiguous : TPResult::False;
           case ANK_Success:
             break;
           }
@@ -1556,8 +1570,7 @@
           // Annotated it, check again.
           assert(Tok.isNot(tok::annot_cxxscope) ||
                  NextToken().isNot(tok::identifier));
-          return isCXXDeclarationSpecifier(BracedCastResult,
-                                           HasMissingTypename);
+          return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
         }
       }
       return TPResult::False;
@@ -2029,3 +2042,56 @@
 
   return TPResult::Ambiguous;
 }
+
+/// Determine whether we might be looking at the '<' template-argument-list '>'
+/// of a template-id or simple-template-id, rather than a less-than comparison.
+/// This will often fail and produce an ambiguity, but should never be wrong
+/// if it returns True or False.
+Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
+  if (!TokensToSkip) {
+    if (Tok.isNot(tok::less))
+      return TPResult::False;
+    if (NextToken().is(tok::greater))
+      return TPResult::True;
+  }
+
+  RevertingTentativeParsingAction PA(*this);
+
+  while (TokensToSkip) {
+    ConsumeAnyToken();
+    --TokensToSkip;
+  }
+
+  if (!TryConsumeToken(tok::less))
+    return TPResult::False;
+
+  bool InvalidAsTemplateArgumentList = false;
+  while (true) {
+    // We can't do much to tell an expression apart from a template-argument,
+    // but one good distinguishing factor is that a "decl-specifier" not
+    // followed by '(' or '{' can't appear in an expression.
+    if (isCXXDeclarationSpecifier(
+            TPResult::False, &InvalidAsTemplateArgumentList) == TPResult::True)
+      return TPResult::True;
+
+    // That didn't help, try the next template-argument.
+    SkipUntil({tok::comma, tok::greater, tok::greatergreater,
+               tok::greatergreatergreater},
+              StopAtSemi | StopBeforeMatch);
+    switch (Tok.getKind()) {
+    case tok::comma:
+      ConsumeToken();
+      break;
+
+    case tok::greater:
+    case tok::greatergreater:
+    case tok::greatergreatergreater:
+      if (InvalidAsTemplateArgumentList)
+        return TPResult::False;
+      return TPResult::Ambiguous;
+
+    default:
+      return TPResult::False;
+    }
+  }
+}