Skip to content

Commit 037fb12

Browse files
committed
[Clang][Parse] Fix ambiguity with nested-name-specifiers that may be declarative
1 parent 43ca631 commit 037fb12

File tree

8 files changed

+182
-101
lines changed

8 files changed

+182
-101
lines changed

clang/include/clang/Lex/Preprocessor.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,9 @@ class Preprocessor {
11601160
/// invoked (at which point the last position is popped).
11611161
std::vector<CachedTokensTy::size_type> BacktrackPositions;
11621162

1163+
std::vector<std::pair<CachedTokensTy, CachedTokensTy::size_type>>
1164+
UnannotatedBacktrackPositions;
1165+
11631166
/// True if \p Preprocessor::SkipExcludedConditionalBlock() is running.
11641167
/// This is used to guard against calling this function recursively.
11651168
///
@@ -1722,7 +1725,7 @@ class Preprocessor {
17221725
/// at some point after EnableBacktrackAtThisPos. If you don't, caching of
17231726
/// tokens will continue indefinitely.
17241727
///
1725-
void EnableBacktrackAtThisPos();
1728+
void EnableBacktrackAtThisPos(bool Unannotated = false);
17261729

17271730
/// Disable the last EnableBacktrackAtThisPos call.
17281731
void CommitBacktrackedTokens();
@@ -1733,7 +1736,11 @@ class Preprocessor {
17331736

17341737
/// True if EnableBacktrackAtThisPos() was called and
17351738
/// caching of tokens is on.
1739+
17361740
bool isBacktrackEnabled() const { return !BacktrackPositions.empty(); }
1741+
bool isUnannotatedBacktrackEnabled() const {
1742+
return !UnannotatedBacktrackPositions.empty();
1743+
}
17371744

17381745
/// Lex the next token for this preprocessor.
17391746
void Lex(Token &Result);
@@ -1841,8 +1848,9 @@ class Preprocessor {
18411848
void RevertCachedTokens(unsigned N) {
18421849
assert(isBacktrackEnabled() &&
18431850
"Should only be called when tokens are cached for backtracking");
1844-
assert(signed(CachedLexPos) - signed(N) >= signed(BacktrackPositions.back())
1845-
&& "Should revert tokens up to the last backtrack position, not more");
1851+
assert(signed(CachedLexPos) - signed(N) >=
1852+
signed(BacktrackPositions.back() >> 1) &&
1853+
"Should revert tokens up to the last backtrack position, not more");
18461854
assert(signed(CachedLexPos) - signed(N) >= 0 &&
18471855
"Corrupted backtrack positions ?");
18481856
CachedLexPos -= N;

clang/include/clang/Parse/Parser.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,15 +1034,15 @@ class Parser : public CodeCompletionHandler {
10341034
bool isActive;
10351035

10361036
public:
1037-
explicit TentativeParsingAction(Parser &p)
1037+
explicit TentativeParsingAction(Parser &p, bool Unannotated = false)
10381038
: P(p), PrevPreferredType(P.PreferredType) {
10391039
PrevTok = P.Tok;
10401040
PrevTentativelyDeclaredIdentifierCount =
10411041
P.TentativelyDeclaredIdentifiers.size();
10421042
PrevParenCount = P.ParenCount;
10431043
PrevBracketCount = P.BracketCount;
10441044
PrevBraceCount = P.BraceCount;
1045-
P.PP.EnableBacktrackAtThisPos();
1045+
P.PP.EnableBacktrackAtThisPos(Unannotated);
10461046
isActive = true;
10471047
}
10481048
void Commit() {
@@ -1073,13 +1073,11 @@ class Parser : public CodeCompletionHandler {
10731073
class RevertingTentativeParsingAction
10741074
: private Parser::TentativeParsingAction {
10751075
public:
1076-
RevertingTentativeParsingAction(Parser &P)
1077-
: Parser::TentativeParsingAction(P) {}
1076+
using TentativeParsingAction::TentativeParsingAction;
1077+
10781078
~RevertingTentativeParsingAction() { Revert(); }
10791079
};
10801080

1081-
class UnannotatedTentativeParsingAction;
1082-
10831081
/// ObjCDeclContextSwitch - An object used to switch context from
10841082
/// an objective-c decl context to its enclosing decl context and
10851083
/// back.
@@ -1984,7 +1982,8 @@ class Parser : public CodeCompletionHandler {
19841982
CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHasErrors,
19851983
bool EnteringContext, bool *MayBePseudoDestructor = nullptr,
19861984
bool IsTypename = false, const IdentifierInfo **LastII = nullptr,
1987-
bool OnlyNamespace = false, bool InUsingDeclaration = false);
1985+
bool OnlyNamespace = false, bool InUsingDeclaration = false,
1986+
bool Disambiguation = false);
19881987

19891988
//===--------------------------------------------------------------------===//
19901989
// C++11 5.1.2: Lambda expressions

clang/lib/Lex/PPCaching.cpp

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,52 @@ using namespace clang;
2222
// Nested backtracks are allowed, meaning that EnableBacktrackAtThisPos can
2323
// be called multiple times and CommitBacktrackedTokens/Backtrack calls will
2424
// be combined with the EnableBacktrackAtThisPos calls in reverse order.
25-
void Preprocessor::EnableBacktrackAtThisPos() {
25+
void Preprocessor::EnableBacktrackAtThisPos(bool Unannotated) {
2626
assert(LexLevel == 0 && "cannot use lookahead while lexing");
27-
BacktrackPositions.push_back(CachedLexPos);
27+
BacktrackPositions.push_back((CachedLexPos << 1) | Unannotated);
28+
if (Unannotated)
29+
UnannotatedBacktrackPositions.emplace_back(CachedTokens,
30+
CachedTokens.size());
2831
EnterCachingLexMode();
2932
}
3033

3134
// Disable the last EnableBacktrackAtThisPos call.
3235
void Preprocessor::CommitBacktrackedTokens() {
3336
assert(!BacktrackPositions.empty()
3437
&& "EnableBacktrackAtThisPos was not called!");
38+
auto BacktrackPos = BacktrackPositions.back();
3539
BacktrackPositions.pop_back();
40+
if (BacktrackPos & 1) {
41+
assert(!UnannotatedBacktrackPositions.empty() &&
42+
"missing unannotated tokens?");
43+
auto [UnannotatedTokens, NumCachedToks] =
44+
std::move(UnannotatedBacktrackPositions.back());
45+
if (!UnannotatedBacktrackPositions.empty())
46+
UnannotatedBacktrackPositions.back().first.append(
47+
UnannotatedTokens.begin() + NumCachedToks, UnannotatedTokens.end());
48+
UnannotatedBacktrackPositions.pop_back();
49+
}
3650
}
3751

3852
// Make Preprocessor re-lex the tokens that were lexed since
3953
// EnableBacktrackAtThisPos() was previously called.
4054
void Preprocessor::Backtrack() {
4155
assert(!BacktrackPositions.empty()
4256
&& "EnableBacktrackAtThisPos was not called!");
43-
CachedLexPos = BacktrackPositions.back();
57+
auto BacktrackPos = BacktrackPositions.back();
4458
BacktrackPositions.pop_back();
59+
CachedLexPos = BacktrackPos >> 1;
60+
if (BacktrackPos & 1) {
61+
assert(!UnannotatedBacktrackPositions.empty() &&
62+
"missing unannotated tokens?");
63+
auto [UnannotatedTokens, NumCachedToks] =
64+
std::move(UnannotatedBacktrackPositions.back());
65+
UnannotatedBacktrackPositions.pop_back();
66+
if (!UnannotatedBacktrackPositions.empty())
67+
UnannotatedBacktrackPositions.back().first.append(
68+
UnannotatedTokens.begin() + NumCachedToks, UnannotatedTokens.end());
69+
CachedTokens = std::move(UnannotatedTokens);
70+
}
4571
recomputeCurLexerKind();
4672
}
4773

@@ -67,6 +93,8 @@ void Preprocessor::CachingLex(Token &Result) {
6793
EnterCachingLexModeUnchecked();
6894
CachedTokens.push_back(Result);
6995
++CachedLexPos;
96+
if (isUnannotatedBacktrackEnabled())
97+
UnannotatedBacktrackPositions.back().first.push_back(Result);
7098
return;
7199
}
72100

@@ -108,6 +136,8 @@ const Token &Preprocessor::PeekAhead(unsigned N) {
108136
for (size_t C = CachedLexPos + N - CachedTokens.size(); C > 0; --C) {
109137
CachedTokens.push_back(Token());
110138
Lex(CachedTokens.back());
139+
if (isUnannotatedBacktrackEnabled())
140+
UnannotatedBacktrackPositions.back().first.push_back(CachedTokens.back());
111141
}
112142
EnterCachingLexMode();
113143
return CachedTokens.back();
@@ -124,7 +154,8 @@ void Preprocessor::AnnotatePreviousCachedTokens(const Token &Tok) {
124154
for (CachedTokensTy::size_type i = CachedLexPos; i != 0; --i) {
125155
CachedTokensTy::iterator AnnotBegin = CachedTokens.begin() + i-1;
126156
if (AnnotBegin->getLocation() == Tok.getLocation()) {
127-
assert((BacktrackPositions.empty() || BacktrackPositions.back() <= i) &&
157+
assert((BacktrackPositions.empty() ||
158+
(BacktrackPositions.back() >> 1) <= i) &&
128159
"The backtrack pos points inside the annotated tokens!");
129160
// Replace the cached tokens with the single annotation token.
130161
if (i < CachedLexPos)

clang/lib/Parse/ParseCXXInlineMethods.cpp

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,41 +1205,6 @@ bool Parser::ConsumeAndStoreConditional(CachedTokens &Toks) {
12051205
return true;
12061206
}
12071207

1208-
/// A tentative parsing action that can also revert token annotations.
1209-
class Parser::UnannotatedTentativeParsingAction : public TentativeParsingAction {
1210-
public:
1211-
explicit UnannotatedTentativeParsingAction(Parser &Self,
1212-
tok::TokenKind EndKind)
1213-
: TentativeParsingAction(Self), Self(Self), EndKind(EndKind) {
1214-
// Stash away the old token stream, so we can restore it once the
1215-
// tentative parse is complete.
1216-
TentativeParsingAction Inner(Self);
1217-
Self.ConsumeAndStoreUntil(EndKind, Toks, true, /*ConsumeFinalToken*/false);
1218-
Inner.Revert();
1219-
}
1220-
1221-
void RevertAnnotations() {
1222-
Revert();
1223-
1224-
// Put back the original tokens.
1225-
Self.SkipUntil(EndKind, StopAtSemi | StopBeforeMatch);
1226-
if (Toks.size()) {
1227-
auto Buffer = std::make_unique<Token[]>(Toks.size());
1228-
std::copy(Toks.begin() + 1, Toks.end(), Buffer.get());
1229-
Buffer[Toks.size() - 1] = Self.Tok;
1230-
Self.PP.EnterTokenStream(std::move(Buffer), Toks.size(), true,
1231-
/*IsReinject*/ true);
1232-
1233-
Self.Tok = Toks.front();
1234-
}
1235-
}
1236-
1237-
private:
1238-
Parser &Self;
1239-
CachedTokens Toks;
1240-
tok::TokenKind EndKind;
1241-
};
1242-
12431208
/// ConsumeAndStoreInitializer - Consume and store the token at the passed token
12441209
/// container until the end of the current initializer expression (either a
12451210
/// default argument or an in-class initializer for a non-static data member).
@@ -1277,9 +1242,7 @@ bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks,
12771242
// syntactically-valid init-declarator-list, then this comma ends
12781243
// the default initializer.
12791244
{
1280-
UnannotatedTentativeParsingAction PA(*this,
1281-
CIK == CIK_DefaultInitializer
1282-
? tok::semi : tok::r_paren);
1245+
TentativeParsingAction TPA(*this, /*Unannotated=*/true);
12831246
Sema::TentativeAnalysisScope Scope(Actions);
12841247

12851248
TPResult Result = TPResult::Error;
@@ -1307,7 +1270,7 @@ bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks,
13071270
// Put the token stream back and undo any annotations we performed
13081271
// after the comma. They may reflect a different parse than the one
13091272
// we will actually perform at the end of the class.
1310-
PA.RevertAnnotations();
1273+
TPA.Revert();
13111274

13121275
// If what follows could be a declaration, it is a declaration.
13131276
if (Result != TPResult::False && Result != TPResult::Error)

clang/lib/Parse/ParseDecl.cpp

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6650,48 +6650,67 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
66506650
(Tok.is(tok::identifier) &&
66516651
(NextToken().is(tok::coloncolon) || NextToken().is(tok::less))) ||
66526652
Tok.is(tok::annot_cxxscope))) {
6653+
TentativeParsingAction TPA(*this, /*Unannotated=*/true);
66536654
bool EnteringContext = D.getContext() == DeclaratorContext::File ||
66546655
D.getContext() == DeclaratorContext::Member;
6656+
66556657
CXXScopeSpec SS;
66566658
SS.setTemplateParamLists(D.getTemplateParameterLists());
6657-
ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
6658-
/*ObjectHasErrors=*/false, EnteringContext);
66596659

6660-
if (SS.isNotEmpty()) {
6661-
if (Tok.isNot(tok::star)) {
6662-
// The scope spec really belongs to the direct-declarator.
6663-
if (D.mayHaveIdentifier())
6664-
D.getCXXScopeSpec() = SS;
6665-
else
6666-
AnnotateScopeToken(SS, true);
6660+
if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
6661+
/*ObjectHasErrors=*/false,
6662+
/*EnteringContext=*/false,
6663+
/*MayBePseudoDestructor=*/nullptr,
6664+
/*IsTypename=*/false, /*LastII=*/nullptr,
6665+
/*OnlyNamespace=*/false,
6666+
/*InUsingDeclaration=*/false,
6667+
/*Disambiguation=*/EnteringContext) ||
6668+
6669+
SS.isEmpty() || SS.isInvalid() || !EnteringContext ||
6670+
Tok.is(tok::star)) {
6671+
TPA.Commit();
6672+
if (SS.isNotEmpty() && Tok.is(tok::star)) {
6673+
if (SS.isValid()) {
6674+
checkCompoundToken(SS.getEndLoc(), tok::coloncolon,
6675+
CompoundToken::MemberPtr);
6676+
}
66676677

6668-
if (DirectDeclParser)
6669-
(this->*DirectDeclParser)(D);
6678+
SourceLocation StarLoc = ConsumeToken();
6679+
D.SetRangeEnd(StarLoc);
6680+
DeclSpec DS(AttrFactory);
6681+
ParseTypeQualifierListOpt(DS);
6682+
D.ExtendWithDeclSpec(DS);
6683+
6684+
// Recurse to parse whatever is left.
6685+
Actions.runWithSufficientStackSpace(D.getBeginLoc(), [&] {
6686+
ParseDeclaratorInternal(D, DirectDeclParser);
6687+
});
6688+
6689+
// Sema will have to catch (syntactically invalid) pointers into global
6690+
// scope. It has to catch pointers into namespace scope anyway.
6691+
D.AddTypeInfo(DeclaratorChunk::getMemberPointer(
6692+
SS, DS.getTypeQualifiers(), StarLoc, DS.getEndLoc()),
6693+
std::move(DS.getAttributes()),
6694+
/*EndLoc=*/SourceLocation());
66706695
return;
66716696
}
6697+
} else {
6698+
TPA.Revert();
6699+
SS.clear();
6700+
ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
6701+
/*ObjectHasErrors=*/false,
6702+
/*EnteringContext=*/true);
6703+
}
66726704

6673-
if (SS.isValid()) {
6674-
checkCompoundToken(SS.getEndLoc(), tok::coloncolon,
6675-
CompoundToken::MemberPtr);
6676-
}
6705+
if (SS.isNotEmpty()) {
6706+
// The scope spec really belongs to the direct-declarator.
6707+
if (D.mayHaveIdentifier())
6708+
D.getCXXScopeSpec() = SS;
6709+
else
6710+
AnnotateScopeToken(SS, true);
66776711

6678-
SourceLocation StarLoc = ConsumeToken();
6679-
D.SetRangeEnd(StarLoc);
6680-
DeclSpec DS(AttrFactory);
6681-
ParseTypeQualifierListOpt(DS);
6682-
D.ExtendWithDeclSpec(DS);
6683-
6684-
// Recurse to parse whatever is left.
6685-
Actions.runWithSufficientStackSpace(D.getBeginLoc(), [&] {
6686-
ParseDeclaratorInternal(D, DirectDeclParser);
6687-
});
6688-
6689-
// Sema will have to catch (syntactically invalid) pointers into global
6690-
// scope. It has to catch pointers into namespace scope anyway.
6691-
D.AddTypeInfo(DeclaratorChunk::getMemberPointer(
6692-
SS, DS.getTypeQualifiers(), StarLoc, DS.getEndLoc()),
6693-
std::move(DS.getAttributes()),
6694-
/* Don't replace range end. */ SourceLocation());
6712+
if (DirectDeclParser)
6713+
(this->*DirectDeclParser)(D);
66956714
return;
66966715
}
66976716
}

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
159159
bool Parser::ParseOptionalCXXScopeSpecifier(
160160
CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
161161
bool EnteringContext, bool *MayBePseudoDestructor, bool IsTypename,
162-
const IdentifierInfo **LastII, bool OnlyNamespace,
163-
bool InUsingDeclaration) {
162+
const IdentifierInfo **LastII, bool OnlyNamespace, bool InUsingDeclaration,
163+
bool Disambiguation) {
164164
assert(getLangOpts().CPlusPlus &&
165165
"Call sites of this function should be guarded by checking for C++");
166166

@@ -528,13 +528,11 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
528528
UnqualifiedId TemplateName;
529529
TemplateName.setIdentifier(&II, Tok.getLocation());
530530
bool MemberOfUnknownSpecialization;
531-
if (TemplateNameKind TNK = Actions.isTemplateName(getCurScope(), SS,
532-
/*hasTemplateKeyword=*/false,
533-
TemplateName,
534-
ObjectType,
535-
EnteringContext,
536-
Template,
537-
MemberOfUnknownSpecialization)) {
531+
if (TemplateNameKind TNK = Actions.isTemplateName(
532+
getCurScope(), SS,
533+
/*hasTemplateKeyword=*/false, TemplateName, ObjectType,
534+
EnteringContext, Template, MemberOfUnknownSpecialization,
535+
Disambiguation)) {
538536
// If lookup didn't find anything, we treat the name as a template-name
539537
// anyway. C++20 requires this, and in prior language modes it improves
540538
// error recovery. But before we commit to this, check that we actually
@@ -557,7 +555,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
557555
continue;
558556
}
559557

560-
if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) &&
558+
if (MemberOfUnknownSpecialization && !Disambiguation &&
559+
(ObjectType || SS.isSet()) &&
561560
(IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
562561
// If we had errors before, ObjectType can be dependent even without any
563562
// templates. Do not report missing template keyword in that case.

0 commit comments

Comments
 (0)