Skip to content

Commit 0d1b252

Browse files
sdkrystianronlieb
authored andcommitted
[Clang][Sema] Don't build CXXDependentScopeMemberExprs for potentially implicit class member access expressions (llvm#92318)
According to [expr.prim.id.general] p2: > If an _id-expression_ `E` denotes a non-static non-type member of some class `C` at a point where the current class is `X` and > - `E` is potentially evaluated or `C` is `X` or a base class of `X`, and > - `E` is not the _id-expression_ of a class member access expression, and > - if `E` is a _qualified-id_, `E` is not the un-parenthesized operand of the unary `&` operator, > > the _id-expression_ is transformed into a class member access expression using `(*this)` as the object expression. Consider the following: ``` struct A { void f0(); template<typename T> void f1(); }; template<typename T> struct B : T { auto g0() -> decltype(T::f0()); // ok auto g1() -> decltype(T::template f1<int>()); // error: call to non-static member function without an object argument }; template struct B<A>; ``` Clang incorrectly rejects the call to `f1` in the _trailing-return-type_ of `g1`. Furthermore, the following snippet results in a crash during codegen: ``` struct A { void f(); }; template<typename T> struct B : T { template<typename U> static void g(); template<> void g<int>() { return T::f(); // crash here } }; template struct B<A>; ``` This happens because we unconditionally build a `CXXDependentScopeMemberExpr` (with an implicit object expression) for `T::f` when parsing the template definition, even though we don't know whether `g` is an implicit object member function yet. This patch fixes these issues by instead building `DependentScopeDeclRefExpr`s for such expressions, and only transforming them into implicit class member access expressions during instantiation. Since we implemented the MS "unqualified lookup into dependent bases" extension by building an implicit class member access (and relying on the first component name of the _nested-name-specifier_ to be looked up in the context of the object expression during instantiation), we instead pre-append a fake _nested-name-specifier_ that refers to the injected-class-name of the enclosing class. This patch also refactors `Sema::BuildQualifiedDeclarationNameExpr` and `Sema::BuildQualifiedTemplateIdExpr`, streamlining their implementation and removing any redundant checks. Change-Id: Ifaaa4aa9c49381e13029ad9266ee3785e2d791d1
1 parent af5e149 commit 0d1b252

File tree

10 files changed

+220
-117
lines changed

10 files changed

+220
-117
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6767,11 +6767,9 @@ class Sema final : public SemaBase {
67676767
bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R,
67686768
bool HasTrailingLParen);
67696769

6770-
ExprResult
6771-
BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS,
6772-
const DeclarationNameInfo &NameInfo,
6773-
bool IsAddressOfOperand, const Scope *S,
6774-
TypeSourceInfo **RecoveryTSI = nullptr);
6770+
ExprResult BuildQualifiedDeclarationNameExpr(
6771+
CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
6772+
bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI = nullptr);
67756773

67766774
ExprResult BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R,
67776775
bool NeedsADL,
@@ -11443,7 +11441,8 @@ class Sema final : public SemaBase {
1144311441
ExprResult
1144411442
BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
1144511443
const DeclarationNameInfo &NameInfo,
11446-
const TemplateArgumentListInfo *TemplateArgs);
11444+
const TemplateArgumentListInfo *TemplateArgs,
11445+
bool IsAddressOfOperand);
1144711446

1144811447
/// Form a template name from a name that is syntactically required to name a
1144911448
/// template, either due to use of the 'template' keyword or because a name in

clang/lib/Sema/SemaCXXScopeSpec.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,14 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
731731
Diag(IdInfo.IdentifierLoc,
732732
diag::ext_undeclared_unqual_id_with_dependent_base)
733733
<< IdInfo.Identifier << ContainingClass;
734+
// Fake up a nested-name-specifier that starts with the
735+
// injected-class-name of the enclosing class.
736+
QualType T = Context.getTypeDeclType(ContainingClass);
737+
TypeLocBuilder TLB;
738+
TLB.pushTrivial(Context, T, IdInfo.IdentifierLoc);
739+
SS.Extend(Context, /*TemplateKWLoc=*/SourceLocation(),
740+
TLB.getTypeLocInContext(Context, T), IdInfo.IdentifierLoc);
741+
// Add the identifier to form a dependent name.
734742
SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc,
735743
IdInfo.CCLoc);
736744
return false;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2729,7 +2729,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
27292729
&AssumedTemplate))
27302730
return ExprError();
27312731

2732-
if (R.wasNotFoundInCurrentInstantiation())
2732+
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
27332733
return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo,
27342734
IsAddressOfOperand, TemplateArgs);
27352735
} else {
@@ -2739,7 +2739,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
27392739

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

@@ -2890,26 +2890,14 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
28902890

28912891
ExprResult Sema::BuildQualifiedDeclarationNameExpr(
28922892
CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
2893-
bool IsAddressOfOperand, const Scope *S, TypeSourceInfo **RecoveryTSI) {
2894-
if (NameInfo.getName().isDependentName())
2895-
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
2896-
NameInfo, /*TemplateArgs=*/nullptr);
2897-
2898-
DeclContext *DC = computeDeclContext(SS, false);
2899-
if (!DC)
2900-
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
2901-
NameInfo, /*TemplateArgs=*/nullptr);
2902-
2903-
if (RequireCompleteDeclContext(SS, DC))
2904-
return ExprError();
2905-
2893+
bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI) {
29062894
LookupResult R(*this, NameInfo, LookupOrdinaryName);
2907-
LookupQualifiedName(R, DC);
2895+
LookupParsedName(R, /*S=*/nullptr, &SS, /*ObjectType=*/QualType());
29082896

29092897
if (R.isAmbiguous())
29102898
return ExprError();
29112899

2912-
if (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)
2900+
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
29132901
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
29142902
NameInfo, /*TemplateArgs=*/nullptr);
29152903

@@ -2918,6 +2906,7 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr(
29182906
// diagnostic during template instantiation is likely bogus, e.g. if a class
29192907
// is invalid because it's derived from an invalid base class, then missing
29202908
// members were likely supposed to be inherited.
2909+
DeclContext *DC = computeDeclContext(SS);
29212910
if (const auto *CD = dyn_cast<CXXRecordDecl>(DC))
29222911
if (CD->isInvalidDecl())
29232912
return ExprError();
@@ -2961,16 +2950,14 @@ ExprResult Sema::BuildQualifiedDeclarationNameExpr(
29612950
return ExprEmpty();
29622951
}
29632952

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

2973-
return BuildDeclarationNameExpr(SS, R, /* ADL */ false);
2960+
return BuildDeclarationNameExpr(SS, R, /*ADL=*/false);
29742961
}
29752962

29762963
ExprResult
@@ -3114,7 +3101,7 @@ bool Sema::UseArgumentDependentLookup(const CXXScopeSpec &SS,
31143101
return false;
31153102

31163103
// Never if a scope specifier was provided.
3117-
if (SS.isSet())
3104+
if (SS.isNotEmpty())
31183105
return false;
31193106

31203107
// Only in C++ or ObjC++.

clang/lib/Sema/SemaLookup.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,16 +2720,19 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS,
27202720
ObjectType->castAs<TagType>()->isBeingDefined()) &&
27212721
"Caller should have completed object type");
27222722
} else if (SS && SS->isNotEmpty()) {
2723-
if (NestedNameSpecifier *NNS = SS->getScopeRep();
2724-
NNS->getKind() == NestedNameSpecifier::Super)
2725-
return LookupInSuper(R, NNS->getAsRecordDecl());
27262723
// This nested-name-specifier occurs after another nested-name-specifier,
27272724
// so long into the context associated with the prior nested-name-specifier.
27282725
if ((DC = computeDeclContext(*SS, EnteringContext))) {
27292726
// The declaration context must be complete.
27302727
if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC))
27312728
return false;
27322729
R.setContextRange(SS->getRange());
2730+
// FIXME: '__super' lookup semantics could be implemented by a
2731+
// LookupResult::isSuperLookup flag which skips the initial search of
2732+
// the lookup context in LookupQualified.
2733+
if (NestedNameSpecifier *NNS = SS->getScopeRep();
2734+
NNS->getKind() == NestedNameSpecifier::Super)
2735+
return LookupInSuper(R, NNS->getAsRecordDecl());
27332736
}
27342737
IsDependent = !DC && isDependentScopeSpecifier(*SS);
27352738
} else {

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 35 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -726,44 +726,22 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
726726
const DeclarationNameInfo &NameInfo,
727727
bool isAddressOfOperand,
728728
const TemplateArgumentListInfo *TemplateArgs) {
729-
DeclContext *DC = getFunctionLevelDeclContext();
730-
731-
// C++11 [expr.prim.general]p12:
732-
// An id-expression that denotes a non-static data member or non-static
733-
// member function of a class can only be used:
734-
// (...)
735-
// - if that id-expression denotes a non-static data member and it
736-
// appears in an unevaluated operand.
737-
//
738-
// If this might be the case, form a DependentScopeDeclRefExpr instead of a
739-
// CXXDependentScopeMemberExpr. The former can instantiate to either
740-
// DeclRefExpr or MemberExpr depending on lookup results, while the latter is
741-
// always a MemberExpr.
742-
bool MightBeCxx11UnevalField =
743-
getLangOpts().CPlusPlus11 && isUnevaluatedContext();
744-
745-
// Check if the nested name specifier is an enum type.
746-
bool IsEnum = false;
747-
if (NestedNameSpecifier *NNS = SS.getScopeRep())
748-
IsEnum = isa_and_nonnull<EnumType>(NNS->getAsType());
749-
750-
if (!MightBeCxx11UnevalField && !isAddressOfOperand && !IsEnum &&
751-
isa<CXXMethodDecl>(DC) &&
752-
cast<CXXMethodDecl>(DC)->isImplicitObjectMemberFunction()) {
753-
QualType ThisType =
754-
cast<CXXMethodDecl>(DC)->getThisType().getNonReferenceType();
755-
756-
// Since the 'this' expression is synthesized, we don't need to
757-
// perform the double-lookup check.
758-
NamedDecl *FirstQualifierInScope = nullptr;
729+
if (SS.isEmpty()) {
730+
// FIXME: This codepath is only used by dependent unqualified names
731+
// (e.g. a dependent conversion-function-id, or operator= once we support
732+
// it). It doesn't quite do the right thing, and it will silently fail if
733+
// getCurrentThisType() returns null.
734+
QualType ThisType = getCurrentThisType();
735+
if (ThisType.isNull())
736+
return ExprError();
759737

760738
return CXXDependentScopeMemberExpr::Create(
761-
Context, /*This=*/nullptr, ThisType,
739+
Context, /*Base=*/nullptr, ThisType,
762740
/*IsArrow=*/!Context.getLangOpts().HLSL,
763-
/*Op=*/SourceLocation(), SS.getWithLocInContext(Context), TemplateKWLoc,
764-
FirstQualifierInScope, NameInfo, TemplateArgs);
741+
/*OperatorLoc=*/SourceLocation(),
742+
/*QualifierLoc=*/NestedNameSpecifierLoc(), TemplateKWLoc,
743+
/*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs);
765744
}
766-
767745
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
768746
}
769747

@@ -772,13 +750,15 @@ Sema::BuildDependentDeclRefExpr(const CXXScopeSpec &SS,
772750
SourceLocation TemplateKWLoc,
773751
const DeclarationNameInfo &NameInfo,
774752
const TemplateArgumentListInfo *TemplateArgs) {
775-
// DependentScopeDeclRefExpr::Create requires a valid QualifierLoc
776-
NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context);
777-
if (!QualifierLoc)
778-
return ExprError();
753+
// DependentScopeDeclRefExpr::Create requires a valid NestedNameSpecifierLoc
754+
if (!SS.isValid())
755+
return CreateRecoveryExpr(
756+
SS.getBeginLoc(),
757+
TemplateArgs ? TemplateArgs->getRAngleLoc() : NameInfo.getEndLoc(), {});
779758

780759
return DependentScopeDeclRefExpr::Create(
781-
Context, QualifierLoc, TemplateKWLoc, NameInfo, TemplateArgs);
760+
Context, SS.getWithLocInContext(Context), TemplateKWLoc, NameInfo,
761+
TemplateArgs);
782762
}
783763

784764
bool Sema::DiagnoseUninstantiableTemplate(SourceLocation PointOfInstantiation,
@@ -5822,50 +5802,36 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
58225802
}
58235803

58245804
// We actually only call this from template instantiation.
5825-
ExprResult
5826-
Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
5827-
SourceLocation TemplateKWLoc,
5828-
const DeclarationNameInfo &NameInfo,
5829-
const TemplateArgumentListInfo *TemplateArgs) {
5830-
5805+
ExprResult Sema::BuildQualifiedTemplateIdExpr(
5806+
CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
5807+
const DeclarationNameInfo &NameInfo,
5808+
const TemplateArgumentListInfo *TemplateArgs, bool IsAddressOfOperand) {
58315809
assert(TemplateArgs || TemplateKWLoc.isValid());
5832-
DeclContext *DC;
5833-
if (!(DC = computeDeclContext(SS, false)) ||
5834-
DC->isDependentContext() ||
5835-
RequireCompleteDeclContext(SS, DC))
5836-
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
58375810

58385811
LookupResult R(*this, NameInfo, LookupOrdinaryName);
5839-
if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(),
5840-
/*Entering*/ false, TemplateKWLoc))
5812+
if (LookupTemplateName(R, /*S=*/nullptr, SS, /*ObjectType=*/QualType(),
5813+
/*EnteringContext=*/false, TemplateKWLoc))
58415814
return ExprError();
58425815

58435816
if (R.isAmbiguous())
58445817
return ExprError();
58455818

5819+
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
5820+
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
5821+
58465822
if (R.empty()) {
5823+
DeclContext *DC = computeDeclContext(SS);
58475824
Diag(NameInfo.getLoc(), diag::err_no_member)
58485825
<< NameInfo.getName() << DC << SS.getRange();
58495826
return ExprError();
58505827
}
58515828

5852-
auto DiagnoseTypeTemplateDecl = [&](TemplateDecl *Temp,
5853-
bool isTypeAliasTemplateDecl) {
5854-
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
5855-
<< SS.getScopeRep() << NameInfo.getName().getAsString() << SS.getRange()
5856-
<< isTypeAliasTemplateDecl;
5857-
Diag(Temp->getLocation(), diag::note_referenced_type_template)
5858-
<< isTypeAliasTemplateDecl;
5859-
return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
5860-
};
5861-
5862-
if (ClassTemplateDecl *Temp = R.getAsSingle<ClassTemplateDecl>())
5863-
return DiagnoseTypeTemplateDecl(Temp, false);
5864-
5865-
if (TypeAliasTemplateDecl *Temp = R.getAsSingle<TypeAliasTemplateDecl>())
5866-
return DiagnoseTypeTemplateDecl(Temp, true);
5829+
// If necessary, build an implicit class member access.
5830+
if (isPotentialImplicitMemberAccess(SS, R, IsAddressOfOperand))
5831+
return BuildPossibleImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs,
5832+
/*S=*/nullptr);
58675833

5868-
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL*/ false, TemplateArgs);
5834+
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL=*/false, TemplateArgs);
58695835
}
58705836

58715837
TemplateNameKind Sema::ActOnTemplateName(Scope *S,
@@ -6043,8 +6009,7 @@ bool Sema::CheckTemplateTypeArgument(
60436009
LookupParsedName(Result, CurScope, &SS, /*ObjectType=*/QualType());
60446010

60456011
if (Result.getAsSingle<TypeDecl>() ||
6046-
Result.getResultKind() ==
6047-
LookupResult::NotFoundInCurrentInstantiation) {
6012+
Result.wasNotFoundInCurrentInstantiation()) {
60486013
assert(SS.getScopeRep() && "dependent scope expr must has a scope!");
60496014
// Suggest that the user add 'typename' before the NNS.
60506015
SourceLocation Loc = AL.getSourceRange().getBegin();

clang/lib/Sema/TreeTransform.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3482,11 +3482,11 @@ class TreeTransform {
34823482
SS.Adopt(QualifierLoc);
34833483

34843484
if (TemplateArgs || TemplateKWLoc.isValid())
3485-
return getSema().BuildQualifiedTemplateIdExpr(SS, TemplateKWLoc, NameInfo,
3486-
TemplateArgs);
3485+
return getSema().BuildQualifiedTemplateIdExpr(
3486+
SS, TemplateKWLoc, NameInfo, TemplateArgs, IsAddressOfOperand);
34873487

34883488
return getSema().BuildQualifiedDeclarationNameExpr(
3489-
SS, NameInfo, IsAddressOfOperand, /*S*/nullptr, RecoveryTSI);
3489+
SS, NameInfo, IsAddressOfOperand, RecoveryTSI);
34903490
}
34913491

34923492
/// Build a new template-id expression.

0 commit comments

Comments
 (0)