summaryrefslogtreecommitdiffstats
path: root/clang/lib/Parse/ParseTentative.cpp
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2019-05-09 03:31:27 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2019-05-09 03:31:27 +0000
commitb23c5e8c3df850177449268c5ca7dbf986157525 (patch)
tree676dabf23d28ebd62d0f453558f8874ea7eafe29 /clang/lib/Parse/ParseTentative.cpp
parenta438a898b02a93a0179be1dd5ed67fb5b01dcf6b (diff)
downloadbcm5719-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.cpp104
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;
+ }
+ }
+}
OpenPOWER on IntegriCloud