Skip to content

Commit

Permalink
PR43080: Do not build context-sensitive expressions during name class…
Browse files Browse the repository at this point in the history
…ification.

Summary:
We don't know what context to use until the classification result is
consumed by the parser, which could happen in a different semantic
context. So don't build the expression that results from name
classification until we get to that point and can handle it properly.

This covers everything except C++ implicit class member access, which
is a little awkward to handle properly in the face of the protected
member access check. But it at least fixes all the currently-filed
instances of PR43080.

Reviewers: efriedma

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D68896

llvm-svn: 374826
  • Loading branch information
zygoloid committed Oct 14, 2019
1 parent 9efbc56 commit 7e8fe67
Show file tree
Hide file tree
Showing 14 changed files with 417 additions and 178 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,11 @@ ANNOTATION(typename) // annotation for a C typedef name, a C++ (possibly
ANNOTATION(template_id) // annotation for a C++ template-id that names a
// function template specialization (not a type),
// e.g., "std::swap<int>"
ANNOTATION(non_type) // annotation for a single non-type declaration
ANNOTATION(non_type_undeclared) // annotation for an undeclared identifier that
// was assumed to be an ADL-only function name
ANNOTATION(non_type_dependent) // annotation for an assumed non-type member of
// a dependent base class
ANNOTATION(primary_expr) // annotation for a primary expression
ANNOTATION(decltype) // annotation for a decltype expression,
// e.g., "decltype(foo.bar())"
Expand Down
19 changes: 17 additions & 2 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,22 @@ class Parser : public CodeCompletionHandler {
Tok.setAnnotationValue(T.getAsOpaquePtr());
}

static NamedDecl *getNonTypeAnnotation(const Token &Tok) {
return static_cast<NamedDecl*>(Tok.getAnnotationValue());
}

static void setNonTypeAnnotation(Token &Tok, NamedDecl *ND) {
Tok.setAnnotationValue(ND);
}

static IdentifierInfo *getIdentifierAnnotation(const Token &Tok) {
return static_cast<IdentifierInfo*>(Tok.getAnnotationValue());
}

static void setIdentifierAnnotation(Token &Tok, IdentifierInfo *ND) {
Tok.setAnnotationValue(ND);
}

/// Read an already-translated primary expression out of an annotation
/// token.
static ExprResult getExprAnnotation(const Token &Tok) {
Expand Down Expand Up @@ -799,8 +815,7 @@ class Parser : public CodeCompletionHandler {
/// Annotation was successful.
ANK_Success
};
AnnotatedNameKind TryAnnotateName(bool IsAddressOfOperand,
CorrectionCandidateCallback *CCC = nullptr);
AnnotatedNameKind TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr);

/// Push a tok::annot_cxxscope token onto the token stream.
void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation);
Expand Down
95 changes: 78 additions & 17 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1855,29 +1855,52 @@ class Sema {
/// Describes the result of the name lookup and resolution performed
/// by \c ClassifyName().
enum NameClassificationKind {
/// This name is not a type or template in this context, but might be
/// something else.
NC_Unknown,
/// Classification failed; an error has been produced.
NC_Error,
/// The name has been typo-corrected to a keyword.
NC_Keyword,
/// The name was classified as a type.
NC_Type,
NC_Expression,
NC_NestedNameSpecifier,
/// The name was classified as a specific non-type, non-template
/// declaration. ActOnNameClassifiedAsNonType should be called to
/// convert the declaration to an expression.
NC_NonType,
/// The name was classified as an ADL-only function name.
/// ActOnNameClassifiedAsUndeclaredNonType should be called to convert the
/// result to an expression.
NC_UndeclaredNonType,
/// The name denotes a member of a dependent type that could not be
/// resolved. ActOnNameClassifiedAsDependentNonType should be called to
/// convert the result to an expression.
NC_DependentNonType,
/// The name was classified as a non-type, and an expression representing
/// that name has been formed.
NC_ContextIndependentExpr,
/// The name was classified as a template whose specializations are types.
NC_TypeTemplate,
/// The name was classified as a variable template name.
NC_VarTemplate,
/// The name was classified as a function template name.
NC_FunctionTemplate,
/// The name was classified as an ADL-only function template name.
NC_UndeclaredTemplate,
};

class NameClassification {
NameClassificationKind Kind;
ExprResult Expr;
TemplateName Template;
ParsedType Type;
union {
ExprResult Expr;
NamedDecl *NonTypeDecl;
TemplateName Template;
ParsedType Type;
};

explicit NameClassification(NameClassificationKind Kind) : Kind(Kind) {}

public:
NameClassification(ExprResult Expr) : Kind(NC_Expression), Expr(Expr) {}

NameClassification(ParsedType Type) : Kind(NC_Type), Type(Type) {}

NameClassification(const IdentifierInfo *Keyword) : Kind(NC_Keyword) {}
Expand All @@ -1890,8 +1913,24 @@ class Sema {
return NameClassification(NC_Unknown);
}

static NameClassification NestedNameSpecifier() {
return NameClassification(NC_NestedNameSpecifier);
static NameClassification ContextIndependentExpr(ExprResult E) {
NameClassification Result(NC_ContextIndependentExpr);
Result.Expr = E;
return Result;
}

static NameClassification NonType(NamedDecl *D) {
NameClassification Result(NC_NonType);
Result.NonTypeDecl = D;
return Result;
}

static NameClassification UndeclaredNonType() {
return NameClassification(NC_UndeclaredNonType);
}

static NameClassification DependentNonType() {
return NameClassification(NC_DependentNonType);
}

static NameClassification TypeTemplate(TemplateName Name) {
Expand Down Expand Up @@ -1920,14 +1959,19 @@ class Sema {

NameClassificationKind getKind() const { return Kind; }

ExprResult getExpression() const {
assert(Kind == NC_ContextIndependentExpr);
return Expr;
}

ParsedType getType() const {
assert(Kind == NC_Type);
return Type;
}

ExprResult getExpression() const {
assert(Kind == NC_Expression);
return Expr;
NamedDecl *getNonTypeDecl() const {
assert(Kind == NC_NonType);
return NonTypeDecl;
}

TemplateName getTemplateName() const {
Expand Down Expand Up @@ -1971,17 +2015,29 @@ class Sema {
/// \param NextToken The token following the identifier. Used to help
/// disambiguate the name.
///
/// \param IsAddressOfOperand True if this name is the operand of a unary
/// address of ('&') expression, assuming it is classified as an
/// expression.
///
/// \param CCC The correction callback, if typo correction is desired.
NameClassification ClassifyName(Scope *S, CXXScopeSpec &SS,
IdentifierInfo *&Name, SourceLocation NameLoc,
const Token &NextToken,
bool IsAddressOfOperand,
CorrectionCandidateCallback *CCC = nullptr);

/// Act on the result of classifying a name as an undeclared (ADL-only)
/// non-type declaration.
ExprResult ActOnNameClassifiedAsUndeclaredNonType(IdentifierInfo *Name,
SourceLocation NameLoc);
/// Act on the result of classifying a name as an undeclared member of a
/// dependent base class.
ExprResult ActOnNameClassifiedAsDependentNonType(const CXXScopeSpec &SS,
IdentifierInfo *Name,
SourceLocation NameLoc,
bool IsAddressOfOperand);
/// Act on the result of classifying a name as a specific non-type
/// declaration.
ExprResult ActOnNameClassifiedAsNonType(Scope *S, const CXXScopeSpec &SS,
NamedDecl *Found,
SourceLocation NameLoc,
const Token &NextToken);

/// Describes the detailed kind of a template name. Used in diagnostics.
enum class TemplateNameKindForDiagnostics {
ClassTemplate,
Expand Down Expand Up @@ -3407,6 +3463,7 @@ class Sema {
LookupNameKind NameKind,
RedeclarationKind Redecl
= NotForRedeclaration);
bool LookupBuiltin(LookupResult &R);
bool LookupName(LookupResult &R, Scope *S,
bool AllowBuiltinCreation = false);
bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
Expand Down Expand Up @@ -4389,6 +4446,10 @@ class Sema {
TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr,
ArrayRef<Expr *> Args = None, TypoExpr **Out = nullptr);

DeclResult LookupIvarInObjCMethod(LookupResult &Lookup, Scope *S,
IdentifierInfo *II);
ExprResult BuildIvarRefExpr(Scope *S, SourceLocation Loc, ObjCIvarDecl *IV);

ExprResult LookupInObjCMethod(LookupResult &LookUp, Scope *S,
IdentifierInfo *II,
bool AllowBuiltinCreation=false);
Expand Down
13 changes: 7 additions & 6 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2929,28 +2929,29 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
IdentifierInfo *Name = AfterScope.getIdentifierInfo();
Sema::NameClassification Classification = Actions.ClassifyName(
getCurScope(), SS, Name, AfterScope.getLocation(), Next,
/*IsAddressOfOperand=*/false, /*CCC=*/nullptr);
/*CCC=*/nullptr);
switch (Classification.getKind()) {
case Sema::NC_Error:
SkipMalformedDecl();
return true;

case Sema::NC_Keyword:
case Sema::NC_NestedNameSpecifier:
llvm_unreachable("typo correction and nested name specifiers not "
"possible here");
llvm_unreachable("typo correction is not possible here");

case Sema::NC_Type:
case Sema::NC_TypeTemplate:
case Sema::NC_UndeclaredNonType:
case Sema::NC_UndeclaredTemplate:
// Not a previously-declared non-type entity.
MightBeDeclarator = false;
break;

case Sema::NC_Unknown:
case Sema::NC_Expression:
case Sema::NC_NonType:
case Sema::NC_DependentNonType:
case Sema::NC_ContextIndependentExpr:
case Sema::NC_VarTemplate:
case Sema::NC_FunctionTemplate:
case Sema::NC_UndeclaredTemplate:
// Might be a redeclaration of a prior entity.
break;
}
Expand Down
12 changes: 11 additions & 1 deletion clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -840,13 +840,23 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
return Actions.ActOnCXXNullPtrLiteral(ConsumeToken());

case tok::annot_primary_expr:
assert(Res.get() == nullptr && "Stray primary-expression annotation?");
Res = getExprAnnotation(Tok);
ConsumeAnnotationToken();
if (!Res.isInvalid() && Tok.is(tok::less))
checkPotentialAngleBracket(Res);
break;

case tok::annot_non_type:
case tok::annot_non_type_dependent:
case tok::annot_non_type_undeclared: {
CXXScopeSpec SS;
Token Replacement;
Res = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
assert(!Res.isUnset() &&
"should not perform typo correction on annotation token");
break;
}

case tok::kw___super:
case tok::kw_decltype:
// Annotate the token and tail recurse.
Expand Down
75 changes: 57 additions & 18 deletions clang/lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,27 +555,66 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
return false;
}

ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand,
ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
bool isAddressOfOperand,
Token &Replacement) {
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
if (ParseUnqualifiedId(SS,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false,
/*ObjectType=*/nullptr, &TemplateKWLoc, Name))
return ExprError();
ExprResult E;

// We may have already annotated this id-expression.
switch (Tok.getKind()) {
case tok::annot_non_type: {
NamedDecl *ND = getNonTypeAnnotation(Tok);
SourceLocation Loc = ConsumeAnnotationToken();
E = Actions.ActOnNameClassifiedAsNonType(getCurScope(), SS, ND, Loc, Tok);
break;
}

case tok::annot_non_type_dependent: {
IdentifierInfo *II = getIdentifierAnnotation(Tok);
SourceLocation Loc = ConsumeAnnotationToken();

// This is only the direct operand of an & operator if it is not
// followed by a postfix-expression suffix.
if (isAddressOfOperand && isPostfixExpressionSuffixStart())
isAddressOfOperand = false;

E = Actions.ActOnNameClassifiedAsDependentNonType(SS, II, Loc,
isAddressOfOperand);
break;
}

// This is only the direct operand of an & operator if it is not
// followed by a postfix-expression suffix.
if (isAddressOfOperand && isPostfixExpressionSuffixStart())
isAddressOfOperand = false;
case tok::annot_non_type_undeclared: {
assert(SS.isEmpty() &&
"undeclared non-type annotation should be unqualified");
IdentifierInfo *II = getIdentifierAnnotation(Tok);
SourceLocation Loc = ConsumeAnnotationToken();
E = Actions.ActOnNameClassifiedAsUndeclaredNonType(II, Loc);
break;
}

default:
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
if (ParseUnqualifiedId(SS,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
/*AllowConstructorName=*/false,
/*AllowDeductionGuide=*/false,
/*ObjectType=*/nullptr, &TemplateKWLoc, Name))
return ExprError();

// This is only the direct operand of an & operator if it is not
// followed by a postfix-expression suffix.
if (isAddressOfOperand && isPostfixExpressionSuffixStart())
isAddressOfOperand = false;

E = Actions.ActOnIdExpression(
getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false,
&Replacement);
break;
}

ExprResult E = Actions.ActOnIdExpression(
getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false,
&Replacement);
if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less))
checkPotentialAngleBracket(E);
return E;
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
// Try to limit which sets of keywords should be included in typo
// correction based on what the next token is.
StatementFilterCCC CCC(Next);
if (TryAnnotateName(/*IsAddressOfOperand*/ false, &CCC) == ANK_Error) {
if (TryAnnotateName(&CCC) == ANK_Error) {
// Handle errors here by skipping up to the next semicolon or '}', and
// eat the semicolon if that's what stopped us.
SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
// this is ambiguous. Typo-correct to type and expression keywords and
// to types and identifiers, in order to try to recover from errors.
TentativeParseCCC CCC(Next);
switch (TryAnnotateName(false /* no nested name specifier */, &CCC)) {
switch (TryAnnotateName(&CCC)) {
case ANK_Error:
return TPResult::Error;
case ANK_TentativeDecl:
Expand Down Expand Up @@ -1569,7 +1569,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
} else {
// Try to resolve the name. If it doesn't exist, assume it was
// intended to name a type and keep disambiguating.
switch (TryAnnotateName(false /* SS is not dependent */)) {
switch (TryAnnotateName()) {
case ANK_Error:
return TPResult::Error;
case ANK_TentativeDecl:
Expand Down
Loading

0 comments on commit 7e8fe67

Please sign in to comment.