diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-05-09 03:31:27 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2019-05-09 03:31:27 +0000 |
commit | b23c5e8c3df850177449268c5ca7dbf986157525 (patch) | |
tree | 676dabf23d28ebd62d0f453558f8874ea7eafe29 /clang/lib/Parse/ParseTentative.cpp | |
parent | a438a898b02a93a0179be1dd5ed67fb5b01dcf6b (diff) | |
download | bcm5719-llvm-b23c5e8c3df850177449268c5ca7dbf986157525.tar.gz bcm5719-llvm-b23c5e8c3df850177449268c5ca7dbf986157525.zip |
[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
Diffstat (limited to 'clang/lib/Parse/ParseTentative.cpp')
-rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 104 |
1 files changed, 85 insertions, 19 deletions
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 0504f0ddcbe..2d2705d8471 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1178,12 +1178,17 @@ public: /// 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 @@ public: /// 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, } // 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, ? 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 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // 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 @@ Parser::TPResult Parser::TryParseBracketDeclarator() { 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; + } + } +} |