Skip to content

[Clang][Sema] Don't build CXXDependentScopeMemberExprs for potentially implicit class member access expressions #92318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,8 @@ Bug Fixes to C++ Support
explicit object argument member functions. Fixes (#GH92188).
- Fix a C++11 crash when a non-const non-static member function is defined out-of-line with
the ``constexpr`` specifier. Fixes (#GH61004).
- Clang no longer transforms dependent qualified names into implicit class member access expressions
until it can be determined whether the name is that of a non-static member.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
11 changes: 5 additions & 6 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -5375,11 +5375,9 @@ class Sema final : public SemaBase {
bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R,
bool HasTrailingLParen);

ExprResult
BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
bool IsAddressOfOperand, const Scope *S,
TypeSourceInfo **RecoveryTSI = nullptr);
ExprResult BuildQualifiedDeclarationNameExpr(
CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI = nullptr);

ExprResult BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R,
bool NeedsADL,
Expand Down Expand Up @@ -8991,7 +8989,8 @@ class Sema final : public SemaBase {
ExprResult
BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs);
const TemplateArgumentListInfo *TemplateArgs,
bool IsAddressOfOperand);

TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Sema/SemaCXXScopeSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,14 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
Diag(IdInfo.IdentifierLoc,
diag::ext_undeclared_unqual_id_with_dependent_base)
<< IdInfo.Identifier << ContainingClass;
// Fake up a nested-name-specifier that starts with the
// injected-class-name of the enclosing class.
QualType T = Context.getTypeDeclType(ContainingClass);
TypeLocBuilder TLB;
TLB.pushTrivial(Context, T, IdInfo.IdentifierLoc);
SS.Extend(Context, /*TemplateKWLoc=*/SourceLocation(),
TLB.getTypeLocInContext(Context, T), IdInfo.IdentifierLoc);
// Add the identifier to form a dependent name.
SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc,
IdInfo.CCLoc);
return false;
Expand Down
65 changes: 12 additions & 53 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2718,34 +2718,6 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
return ExprError();
}

// C++ [temp.dep.expr]p3:
// An id-expression is type-dependent if it contains:
// -- an identifier that was declared with a dependent type,
// (note: handled after lookup)
// -- a template-id that is dependent,
// (note: handled in BuildTemplateIdExpr)
// -- a conversion-function-id that specifies a dependent type,
// -- a nested-name-specifier that contains a class-name that
// names a dependent type.
// Determine whether this is a member of an unknown specialization;
// we need to handle these differently.
bool DependentID = false;
if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName &&
Name.getCXXNameType()->isDependentType()) {
DependentID = true;
} else if (SS.isSet()) {
if (DeclContext *DC = computeDeclContext(SS, false)) {
if (RequireCompleteDeclContext(SS, DC))
return ExprError();
} else {
DependentID = true;
}
}

if (DependentID)
return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo,
IsAddressOfOperand, TemplateArgs);

// BoundsSafety: This specially handles arguments of bounds attributes
// appertains to a type of C struct field such that the name lookup
// within a struct finds the member name, which is not the case for other
Expand Down Expand Up @@ -2781,7 +2753,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
&AssumedTemplate))
return ExprError();

if (R.wasNotFoundInCurrentInstantiation())
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo,
IsAddressOfOperand, TemplateArgs);
} else {
Expand All @@ -2791,7 +2763,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,

// If the result might be in a dependent base class, this is a dependent
// id-expression.
if (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo,
IsAddressOfOperand, TemplateArgs);

Expand Down Expand Up @@ -2946,26 +2918,14 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
/// this path.
ExprResult Sema::BuildQualifiedDeclarationNameExpr(
CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
bool IsAddressOfOperand, const Scope *S, TypeSourceInfo **RecoveryTSI) {
if (NameInfo.getName().isDependentName())
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
NameInfo, /*TemplateArgs=*/nullptr);

DeclContext *DC = computeDeclContext(SS, false);
if (!DC)
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
NameInfo, /*TemplateArgs=*/nullptr);

if (RequireCompleteDeclContext(SS, DC))
return ExprError();

bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI) {
LookupResult R(*this, NameInfo, LookupOrdinaryName);
LookupQualifiedName(R, DC);
LookupParsedName(R, /*S=*/nullptr, &SS, /*ObjectType=*/QualType());

if (R.isAmbiguous())
return ExprError();

if (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
NameInfo, /*TemplateArgs=*/nullptr);

Expand All @@ -2974,6 +2934,7 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr(
// diagnostic during template instantiation is likely bogus, e.g. if a class
// is invalid because it's derived from an invalid base class, then missing
// members were likely supposed to be inherited.
DeclContext *DC = computeDeclContext(SS);
if (const auto *CD = dyn_cast<CXXRecordDecl>(DC))
if (CD->isInvalidDecl())
return ExprError();
Expand Down Expand Up @@ -3017,16 +2978,14 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr(
return ExprEmpty();
}

// Defend against this resolving to an implicit member access. We usually
// won't get here if this might be a legitimate a class member (we end up in
// BuildMemberReferenceExpr instead), but this can be valid if we're forming
// a pointer-to-member or in an unevaluated context in C++11.
if (!R.empty() && (*R.begin())->isCXXClassMember() && !IsAddressOfOperand)
// If necessary, build an implicit class member access.
if (isPotentialImplicitMemberAccess(SS, R, IsAddressOfOperand))
return BuildPossibleImplicitMemberExpr(SS,
/*TemplateKWLoc=*/SourceLocation(),
R, /*TemplateArgs=*/nullptr, S);
R, /*TemplateArgs=*/nullptr,
/*S=*/nullptr);

return BuildDeclarationNameExpr(SS, R, /* ADL */ false);
return BuildDeclarationNameExpr(SS, R, /*ADL=*/false);
}

/// Cast a base object to a member's actual type.
Expand Down Expand Up @@ -3190,7 +3149,7 @@ bool Sema::UseArgumentDependentLookup(const CXXScopeSpec &SS,
return false;

// Never if a scope specifier was provided.
if (SS.isSet())
if (SS.isNotEmpty())
return false;

// Only in C++ or ObjC++.
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/Sema/SemaLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2771,16 +2771,19 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS,
ObjectType->castAs<TagType>()->isBeingDefined()) &&
"Caller should have completed object type");
} else if (SS && SS->isNotEmpty()) {
if (NestedNameSpecifier *NNS = SS->getScopeRep();
NNS->getKind() == NestedNameSpecifier::Super)
return LookupInSuper(R, NNS->getAsRecordDecl());
// This nested-name-specifier occurs after another nested-name-specifier,
// so long into the context associated with the prior nested-name-specifier.
if ((DC = computeDeclContext(*SS, EnteringContext))) {
// The declaration context must be complete.
if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC))
return false;
R.setContextRange(SS->getRange());
// FIXME: '__super' lookup semantics could be implemented by a
// LookupResult::isSuperLookup flag which skips the initial search of
// the lookup context in LookupQualified.
if (NestedNameSpecifier *NNS = SS->getScopeRep();
NNS->getKind() == NestedNameSpecifier::Super)
return LookupInSuper(R, NNS->getAsRecordDecl());
}
IsDependent = !DC && isDependentScopeSpecifier(*SS);
} else {
Expand Down
105 changes: 35 additions & 70 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,44 +726,22 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
bool isAddressOfOperand,
const TemplateArgumentListInfo *TemplateArgs) {
DeclContext *DC = getFunctionLevelDeclContext();

// C++11 [expr.prim.general]p12:
// An id-expression that denotes a non-static data member or non-static
// member function of a class can only be used:
// (...)
// - if that id-expression denotes a non-static data member and it
// appears in an unevaluated operand.
//
// If this might be the case, form a DependentScopeDeclRefExpr instead of a
// CXXDependentScopeMemberExpr. The former can instantiate to either
// DeclRefExpr or MemberExpr depending on lookup results, while the latter is
// always a MemberExpr.
bool MightBeCxx11UnevalField =
getLangOpts().CPlusPlus11 && isUnevaluatedContext();

// Check if the nested name specifier is an enum type.
bool IsEnum = false;
if (NestedNameSpecifier *NNS = SS.getScopeRep())
IsEnum = isa_and_nonnull<EnumType>(NNS->getAsType());

if (!MightBeCxx11UnevalField && !isAddressOfOperand && !IsEnum &&
isa<CXXMethodDecl>(DC) &&
cast<CXXMethodDecl>(DC)->isImplicitObjectMemberFunction()) {
QualType ThisType =
cast<CXXMethodDecl>(DC)->getThisType().getNonReferenceType();

// Since the 'this' expression is synthesized, we don't need to
// perform the double-lookup check.
NamedDecl *FirstQualifierInScope = nullptr;
if (SS.isEmpty()) {
// FIXME: This codepath is only used by dependent unqualified names
// (e.g. a dependent conversion-function-id, or operator= once we support
// it). It doesn't quite do the right thing, and it will silently fail if
// getCurrentThisType() returns null.
QualType ThisType = getCurrentThisType();
if (ThisType.isNull())
return ExprError();

return CXXDependentScopeMemberExpr::Create(
Context, /*This=*/nullptr, ThisType,
Context, /*Base=*/nullptr, ThisType,
/*IsArrow=*/!Context.getLangOpts().HLSL,
/*Op=*/SourceLocation(), SS.getWithLocInContext(Context), TemplateKWLoc,
FirstQualifierInScope, NameInfo, TemplateArgs);
/*OperatorLoc=*/SourceLocation(),
/*QualifierLoc=*/NestedNameSpecifierLoc(), TemplateKWLoc,
/*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs);
}

return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
}

Expand All @@ -772,13 +750,15 @@ Sema::BuildDependentDeclRefExpr(const CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs) {
// DependentScopeDeclRefExpr::Create requires a valid QualifierLoc
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
if (!QualifierLoc)
return ExprError();
// DependentScopeDeclRefExpr::Create requires a valid NestedNameSpecifierLoc
if (!SS.isValid())
return CreateRecoveryExpr(
SS.getBeginLoc(),
TemplateArgs ? TemplateArgs->getRAngleLoc() : NameInfo.getEndLoc(), {});

return DependentScopeDeclRefExpr::Create(
Context, QualifierLoc, TemplateKWLoc, NameInfo, TemplateArgs);
Context, SS.getWithLocInContext(Context), TemplateKWLoc, NameInfo,
TemplateArgs);
}


Expand Down Expand Up @@ -5747,50 +5727,36 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
}

// We actually only call this from template instantiation.
ExprResult
Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs) {

ExprResult Sema::BuildQualifiedTemplateIdExpr(
CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs, bool IsAddressOfOperand) {
assert(TemplateArgs || TemplateKWLoc.isValid());
DeclContext *DC;
if (!(DC = computeDeclContext(SS, false)) ||
DC->isDependentContext() ||
RequireCompleteDeclContext(SS, DC))
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);

LookupResult R(*this, NameInfo, LookupOrdinaryName);
if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(),
/*Entering*/ false, TemplateKWLoc))
if (LookupTemplateName(R, /*S=*/nullptr, SS, /*ObjectType=*/QualType(),
/*EnteringContext=*/false, TemplateKWLoc))
return ExprError();

if (R.isAmbiguous())
return ExprError();

if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);

if (R.empty()) {
DeclContext *DC = computeDeclContext(SS);
Diag(NameInfo.getLoc(), diag::err_no_member)
<< NameInfo.getName() << DC << SS.getRange();
return ExprError();
}

auto DiagnoseTypeTemplateDecl = [&](TemplateDecl *Temp,
bool isTypeAliasTemplateDecl) {
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
<< SS.getScopeRep() << NameInfo.getName().getAsString() << SS.getRange()
<< isTypeAliasTemplateDecl;
Diag(Temp->getLocation(), diag::note_referenced_type_template)
<< isTypeAliasTemplateDecl;
return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
};

if (ClassTemplateDecl *Temp = R.getAsSingle<ClassTemplateDecl>())
return DiagnoseTypeTemplateDecl(Temp, false);

if (TypeAliasTemplateDecl *Temp = R.getAsSingle<TypeAliasTemplateDecl>())
return DiagnoseTypeTemplateDecl(Temp, true);
// If necessary, build an implicit class member access.
if (isPotentialImplicitMemberAccess(SS, R, IsAddressOfOperand))
return BuildPossibleImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs,
/*S=*/nullptr);

return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL*/ false, TemplateArgs);
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL=*/false, TemplateArgs);
}

/// Form a template name from a name that is syntactically required to name a
Expand Down Expand Up @@ -5982,8 +5948,7 @@ bool Sema::CheckTemplateTypeArgument(
LookupParsedName(Result, CurScope, &SS, /*ObjectType=*/QualType());

if (Result.getAsSingle<TypeDecl>() ||
Result.getResultKind() ==
LookupResult::NotFoundInCurrentInstantiation) {
Result.wasNotFoundInCurrentInstantiation()) {
assert(SS.getScopeRep() && "dependent scope expr must has a scope!");
// Suggest that the user add 'typename' before the NNS.
SourceLocation Loc = AL.getSourceRange().getBegin();
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -3478,11 +3478,11 @@ class TreeTransform {
SS.Adopt(QualifierLoc);

if (TemplateArgs || TemplateKWLoc.isValid())
return getSema().BuildQualifiedTemplateIdExpr(SS, TemplateKWLoc, NameInfo,
TemplateArgs);
return getSema().BuildQualifiedTemplateIdExpr(
SS, TemplateKWLoc, NameInfo, TemplateArgs, IsAddressOfOperand);

return getSema().BuildQualifiedDeclarationNameExpr(
SS, NameInfo, IsAddressOfOperand, /*S*/nullptr, RecoveryTSI);
SS, NameInfo, IsAddressOfOperand, RecoveryTSI);
}

/// Build a new template-id expression.
Expand Down
Loading
Loading