Skip to content

Commit fd87d76

Browse files
authored
[Clang][Sema] Don't build CXXDependentScopeMemberExprs for potentially implicit class member access expressions (#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.
1 parent e246105 commit fd87d76

File tree

11 files changed

+222
-145
lines changed

11 files changed

+222
-145
lines changed

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,8 @@ Bug Fixes to C++ Support
744744
explicit object argument member functions. Fixes (#GH92188).
745745
- Fix a C++11 crash when a non-const non-static member function is defined out-of-line with
746746
the ``constexpr`` specifier. Fixes (#GH61004).
747+
- Clang no longer transforms dependent qualified names into implicit class member access expressions
748+
until it can be determined whether the name is that of a non-static member.
747749

748750
Bug Fixes to AST Handling
749751
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

+5-6
Original file line numberDiff line numberDiff line change
@@ -5375,11 +5375,9 @@ class Sema final : public SemaBase {
53755375
bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R,
53765376
bool HasTrailingLParen);
53775377

5378-
ExprResult
5379-
BuildQualifiedDeclarationNameExpr(CXXScopeSpec &SS,
5380-
const DeclarationNameInfo &NameInfo,
5381-
bool IsAddressOfOperand, const Scope *S,
5382-
TypeSourceInfo **RecoveryTSI = nullptr);
5378+
ExprResult BuildQualifiedDeclarationNameExpr(
5379+
CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
5380+
bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI = nullptr);
53835381

53845382
ExprResult BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R,
53855383
bool NeedsADL,
@@ -8991,7 +8989,8 @@ class Sema final : public SemaBase {
89918989
ExprResult
89928990
BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
89938991
const DeclarationNameInfo &NameInfo,
8994-
const TemplateArgumentListInfo *TemplateArgs);
8992+
const TemplateArgumentListInfo *TemplateArgs,
8993+
bool IsAddressOfOperand);
89958994

89968995
TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS,
89978996
SourceLocation TemplateKWLoc,

clang/lib/Sema/SemaCXXScopeSpec.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,14 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
796796
Diag(IdInfo.IdentifierLoc,
797797
diag::ext_undeclared_unqual_id_with_dependent_base)
798798
<< IdInfo.Identifier << ContainingClass;
799+
// Fake up a nested-name-specifier that starts with the
800+
// injected-class-name of the enclosing class.
801+
QualType T = Context.getTypeDeclType(ContainingClass);
802+
TypeLocBuilder TLB;
803+
TLB.pushTrivial(Context, T, IdInfo.IdentifierLoc);
804+
SS.Extend(Context, /*TemplateKWLoc=*/SourceLocation(),
805+
TLB.getTypeLocInContext(Context, T), IdInfo.IdentifierLoc);
806+
// Add the identifier to form a dependent name.
799807
SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc,
800808
IdInfo.CCLoc);
801809
return false;

clang/lib/Sema/SemaExpr.cpp

+12-53
Original file line numberDiff line numberDiff line change
@@ -2718,34 +2718,6 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
27182718
return ExprError();
27192719
}
27202720

2721-
// C++ [temp.dep.expr]p3:
2722-
// An id-expression is type-dependent if it contains:
2723-
// -- an identifier that was declared with a dependent type,
2724-
// (note: handled after lookup)
2725-
// -- a template-id that is dependent,
2726-
// (note: handled in BuildTemplateIdExpr)
2727-
// -- a conversion-function-id that specifies a dependent type,
2728-
// -- a nested-name-specifier that contains a class-name that
2729-
// names a dependent type.
2730-
// Determine whether this is a member of an unknown specialization;
2731-
// we need to handle these differently.
2732-
bool DependentID = false;
2733-
if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName &&
2734-
Name.getCXXNameType()->isDependentType()) {
2735-
DependentID = true;
2736-
} else if (SS.isSet()) {
2737-
if (DeclContext *DC = computeDeclContext(SS, false)) {
2738-
if (RequireCompleteDeclContext(SS, DC))
2739-
return ExprError();
2740-
} else {
2741-
DependentID = true;
2742-
}
2743-
}
2744-
2745-
if (DependentID)
2746-
return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo,
2747-
IsAddressOfOperand, TemplateArgs);
2748-
27492721
// BoundsSafety: This specially handles arguments of bounds attributes
27502722
// appertains to a type of C struct field such that the name lookup
27512723
// within a struct finds the member name, which is not the case for other
@@ -2781,7 +2753,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
27812753
&AssumedTemplate))
27822754
return ExprError();
27832755

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

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

@@ -2946,26 +2918,14 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
29462918
/// this path.
29472919
ExprResult Sema::BuildQualifiedDeclarationNameExpr(
29482920
CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
2949-
bool IsAddressOfOperand, const Scope *S, TypeSourceInfo **RecoveryTSI) {
2950-
if (NameInfo.getName().isDependentName())
2951-
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
2952-
NameInfo, /*TemplateArgs=*/nullptr);
2953-
2954-
DeclContext *DC = computeDeclContext(SS, false);
2955-
if (!DC)
2956-
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
2957-
NameInfo, /*TemplateArgs=*/nullptr);
2958-
2959-
if (RequireCompleteDeclContext(SS, DC))
2960-
return ExprError();
2961-
2921+
bool IsAddressOfOperand, TypeSourceInfo **RecoveryTSI) {
29622922
LookupResult R(*this, NameInfo, LookupOrdinaryName);
2963-
LookupQualifiedName(R, DC);
2923+
LookupParsedName(R, /*S=*/nullptr, &SS, /*ObjectType=*/QualType());
29642924

29652925
if (R.isAmbiguous())
29662926
return ExprError();
29672927

2968-
if (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)
2928+
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
29692929
return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(),
29702930
NameInfo, /*TemplateArgs=*/nullptr);
29712931

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

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

3029-
return BuildDeclarationNameExpr(SS, R, /* ADL */ false);
2988+
return BuildDeclarationNameExpr(SS, R, /*ADL=*/false);
30302989
}
30312990

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

31923151
// Never if a scope specifier was provided.
3193-
if (SS.isSet())
3152+
if (SS.isNotEmpty())
31943153
return false;
31953154

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

clang/lib/Sema/SemaLookup.cpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -2771,16 +2771,19 @@ bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS,
27712771
ObjectType->castAs<TagType>()->isBeingDefined()) &&
27722772
"Caller should have completed object type");
27732773
} else if (SS && SS->isNotEmpty()) {
2774-
if (NestedNameSpecifier *NNS = SS->getScopeRep();
2775-
NNS->getKind() == NestedNameSpecifier::Super)
2776-
return LookupInSuper(R, NNS->getAsRecordDecl());
27772774
// This nested-name-specifier occurs after another nested-name-specifier,
27782775
// so long into the context associated with the prior nested-name-specifier.
27792776
if ((DC = computeDeclContext(*SS, EnteringContext))) {
27802777
// The declaration context must be complete.
27812778
if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC))
27822779
return false;
27832780
R.setContextRange(SS->getRange());
2781+
// FIXME: '__super' lookup semantics could be implemented by a
2782+
// LookupResult::isSuperLookup flag which skips the initial search of
2783+
// the lookup context in LookupQualified.
2784+
if (NestedNameSpecifier *NNS = SS->getScopeRep();
2785+
NNS->getKind() == NestedNameSpecifier::Super)
2786+
return LookupInSuper(R, NNS->getAsRecordDecl());
27842787
}
27852788
IsDependent = !DC && isDependentScopeSpecifier(*SS);
27862789
} else {

clang/lib/Sema/SemaTemplate.cpp

+35-70
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

@@ -5747,50 +5727,36 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
57475727
}
57485728

57495729
// We actually only call this from template instantiation.
5750-
ExprResult
5751-
Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS,
5752-
SourceLocation TemplateKWLoc,
5753-
const DeclarationNameInfo &NameInfo,
5754-
const TemplateArgumentListInfo *TemplateArgs) {
5755-
5730+
ExprResult Sema::BuildQualifiedTemplateIdExpr(
5731+
CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
5732+
const DeclarationNameInfo &NameInfo,
5733+
const TemplateArgumentListInfo *TemplateArgs, bool IsAddressOfOperand) {
57565734
assert(TemplateArgs || TemplateKWLoc.isValid());
5757-
DeclContext *DC;
5758-
if (!(DC = computeDeclContext(SS, false)) ||
5759-
DC->isDependentContext() ||
5760-
RequireCompleteDeclContext(SS, DC))
5761-
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
57625735

57635736
LookupResult R(*this, NameInfo, LookupOrdinaryName);
5764-
if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(),
5765-
/*Entering*/ false, TemplateKWLoc))
5737+
if (LookupTemplateName(R, /*S=*/nullptr, SS, /*ObjectType=*/QualType(),
5738+
/*EnteringContext=*/false, TemplateKWLoc))
57665739
return ExprError();
57675740

57685741
if (R.isAmbiguous())
57695742
return ExprError();
57705743

5744+
if (R.wasNotFoundInCurrentInstantiation() || SS.isInvalid())
5745+
return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
5746+
57715747
if (R.empty()) {
5748+
DeclContext *DC = computeDeclContext(SS);
57725749
Diag(NameInfo.getLoc(), diag::err_no_member)
57735750
<< NameInfo.getName() << DC << SS.getRange();
57745751
return ExprError();
57755752
}
57765753

5777-
auto DiagnoseTypeTemplateDecl = [&](TemplateDecl *Temp,
5778-
bool isTypeAliasTemplateDecl) {
5779-
Diag(NameInfo.getLoc(), diag::err_template_kw_refers_to_type_template)
5780-
<< SS.getScopeRep() << NameInfo.getName().getAsString() << SS.getRange()
5781-
<< isTypeAliasTemplateDecl;
5782-
Diag(Temp->getLocation(), diag::note_referenced_type_template)
5783-
<< isTypeAliasTemplateDecl;
5784-
return CreateRecoveryExpr(NameInfo.getBeginLoc(), NameInfo.getEndLoc(), {});
5785-
};
5786-
5787-
if (ClassTemplateDecl *Temp = R.getAsSingle<ClassTemplateDecl>())
5788-
return DiagnoseTypeTemplateDecl(Temp, false);
5789-
5790-
if (TypeAliasTemplateDecl *Temp = R.getAsSingle<TypeAliasTemplateDecl>())
5791-
return DiagnoseTypeTemplateDecl(Temp, true);
5754+
// If necessary, build an implicit class member access.
5755+
if (isPotentialImplicitMemberAccess(SS, R, IsAddressOfOperand))
5756+
return BuildPossibleImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs,
5757+
/*S=*/nullptr);
57925758

5793-
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL*/ false, TemplateArgs);
5759+
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL=*/false, TemplateArgs);
57945760
}
57955761

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

59845950
if (Result.getAsSingle<TypeDecl>() ||
5985-
Result.getResultKind() ==
5986-
LookupResult::NotFoundInCurrentInstantiation) {
5951+
Result.wasNotFoundInCurrentInstantiation()) {
59875952
assert(SS.getScopeRep() && "dependent scope expr must has a scope!");
59885953
// Suggest that the user add 'typename' before the NNS.
59895954
SourceLocation Loc = AL.getSourceRange().getBegin();

clang/lib/Sema/TreeTransform.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -3478,11 +3478,11 @@ class TreeTransform {
34783478
SS.Adopt(QualifierLoc);
34793479

34803480
if (TemplateArgs || TemplateKWLoc.isValid())
3481-
return getSema().BuildQualifiedTemplateIdExpr(SS, TemplateKWLoc, NameInfo,
3482-
TemplateArgs);
3481+
return getSema().BuildQualifiedTemplateIdExpr(
3482+
SS, TemplateKWLoc, NameInfo, TemplateArgs, IsAddressOfOperand);
34833483

34843484
return getSema().BuildQualifiedDeclarationNameExpr(
3485-
SS, NameInfo, IsAddressOfOperand, /*S*/nullptr, RecoveryTSI);
3485+
SS, NameInfo, IsAddressOfOperand, RecoveryTSI);
34863486
}
34873487

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

0 commit comments

Comments
 (0)