Skip to content

Commit 3cdb30e

Browse files
authored
[Clang][Sema] Use the correct lookup context when building overloaded 'operator->' in the current instantiation (#104458)
Currently, clang erroneously rejects the following: ``` struct A { template<typename T> void f(); }; template<typename T> struct B { void g() { (*this)->template f<int>(); // error: no member named 'f' in 'B<T>' } A* operator->(); }; ``` This happens because `Sema::ActOnStartCXXMemberReference` does not adjust the `ObjectType` parameter when `ObjectType` is a dependent type (except when the type is a `PointerType` and the class member access is the `->` form). Since the (possibly adjusted) `ObjectType` parameter (`B<T>` in the above example) is passed to `Parser::ParseOptionalCXXScopeSpecifier`, we end up looking up `f` in `B` rather than `A`. This patch fixes the issue by identifying cases where the type of the object expression `T` is a dependent, non-pointer type and: - `T` is the current instantiation and lookup for `operator->` finds a member of the current instantiation, or - `T` has at least one dependent base case, and `operator->` is not found in the current instantiation and using `ASTContext::DependentTy` as the type of the object expression when the optional _nested-name-specifier_ is parsed. Fixes #104268.
1 parent eba6160 commit 3cdb30e

File tree

5 files changed

+62
-37
lines changed

5 files changed

+62
-37
lines changed

clang/include/clang/Sema/Sema.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -10639,9 +10639,9 @@ class Sema final : public SemaBase {
1063910639
/// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
1064010640
/// (if one exists), where @c Base is an expression of class type and
1064110641
/// @c Member is the name of the member we're trying to find.
10642-
ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base,
10643-
SourceLocation OpLoc,
10644-
bool *NoArrowOperatorFound = nullptr);
10642+
ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
10643+
bool *NoArrowOperatorFound,
10644+
bool &IsDependent);
1064510645

1064610646
ExprResult BuildCXXMemberCallExpr(Expr *Exp, NamedDecl *FoundDecl,
1064710647
CXXConversionDecl *Method,

clang/lib/Sema/SemaExprCXX.cpp

+21-16
Original file line numberDiff line numberDiff line change
@@ -7966,18 +7966,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
79667966

79677967
QualType BaseType = Base->getType();
79687968
MayBePseudoDestructor = false;
7969-
if (BaseType->isDependentType()) {
7970-
// If we have a pointer to a dependent type and are using the -> operator,
7971-
// the object type is the type that the pointer points to. We might still
7972-
// have enough information about that type to do something useful.
7973-
if (OpKind == tok::arrow)
7974-
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
7975-
BaseType = Ptr->getPointeeType();
7976-
7977-
ObjectType = ParsedType::make(BaseType);
7978-
MayBePseudoDestructor = true;
7979-
return Base;
7980-
}
79817969

79827970
// C++ [over.match.oper]p8:
79837971
// [...] When operator->returns, the operator-> is applied to the value
@@ -7992,7 +7980,8 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
79927980
SmallVector<FunctionDecl*, 8> OperatorArrows;
79937981
CTypes.insert(Context.getCanonicalType(BaseType));
79947982

7995-
while (BaseType->isRecordType()) {
7983+
while (
7984+
isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) {
79967985
if (OperatorArrows.size() >= getLangOpts().ArrowDepth) {
79977986
Diag(OpLoc, diag::err_operator_arrow_depth_exceeded)
79987987
<< StartingType << getLangOpts().ArrowDepth << Base->getSourceRange();
@@ -8002,15 +7991,26 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
80027991
return ExprError();
80037992
}
80047993

7994+
bool IsDependent;
80057995
Result = BuildOverloadedArrowExpr(
8006-
S, Base, OpLoc,
7996+
Base, OpLoc,
80077997
// When in a template specialization and on the first loop iteration,
80087998
// potentially give the default diagnostic (with the fixit in a
80097999
// separate note) instead of having the error reported back to here
80108000
// and giving a diagnostic with a fixit attached to the error itself.
80118001
(FirstIteration && CurFD && CurFD->isFunctionTemplateSpecialization())
80128002
? nullptr
8013-
: &NoArrowOperatorFound);
8003+
: &NoArrowOperatorFound,
8004+
IsDependent);
8005+
8006+
if (IsDependent) {
8007+
// BuildOverloadedArrowExpr sets IsDependent to indicate that we need
8008+
// to build a dependent overloaded arrow expression.
8009+
assert(BaseType->isDependentType());
8010+
BaseType = Context.DependentTy;
8011+
break;
8012+
}
8013+
80148014
if (Result.isInvalid()) {
80158015
if (NoArrowOperatorFound) {
80168016
if (FirstIteration) {
@@ -8030,6 +8030,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
80308030
}
80318031
return ExprError();
80328032
}
8033+
80338034
Base = Result.get();
80348035
if (CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(Base))
80358036
OperatorArrows.push_back(OpCall->getDirectCallee());
@@ -8067,7 +8068,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
80678068
// it's legal for the type to be incomplete if this is a pseudo-destructor
80688069
// call. We'll do more incomplete-type checks later in the lookup process,
80698070
// so just skip this check for ObjC types.
8070-
if (!BaseType->isRecordType()) {
8071+
if (!isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) {
80718072
ObjectType = ParsedType::make(BaseType);
80728073
MayBePseudoDestructor = true;
80738074
return Base;
@@ -8085,6 +8086,10 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
80858086
return CreateRecoveryExpr(Base->getBeginLoc(), Base->getEndLoc(), {Base});
80868087
}
80878088

8089+
// We can't implicitly declare the destructor for a templated class.
8090+
if (BaseType->isDependentType())
8091+
MayBePseudoDestructor = true;
8092+
80888093
// C++ [basic.lookup.classref]p2:
80898094
// If the id-expression in a class member access (5.2.5) is an
80908095
// unqualified-id, and the type of the object expression is of a class

clang/lib/Sema/SemaOverload.cpp

+19-5
Original file line numberDiff line numberDiff line change
@@ -15878,12 +15878,14 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
1587815878
return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), Method);
1587915879
}
1588015880

15881-
ExprResult
15882-
Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
15883-
bool *NoArrowOperatorFound) {
15884-
assert(Base->getType()->isRecordType() &&
15881+
ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
15882+
bool *NoArrowOperatorFound,
15883+
bool &IsDependent) {
15884+
assert(Base->getType()->getAsRecordDecl() &&
1588515885
"left-hand side must have class type");
1588615886

15887+
IsDependent = false;
15888+
1588715889
if (checkPlaceholderForOverload(*this, Base))
1588815890
return ExprError();
1588915891

@@ -15904,7 +15906,19 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
1590415906
return ExprError();
1590515907

1590615908
LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName);
15907-
LookupQualifiedName(R, Base->getType()->castAs<RecordType>()->getDecl());
15909+
LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType());
15910+
15911+
// If the expression is dependent and we either:
15912+
// - found a member of the current instantiation named 'operator->', or
15913+
// - found nothing, and the lookup context has no dependent base classes
15914+
//
15915+
// then we should build a dependent class member access expression.
15916+
if (R.wasNotFoundInCurrentInstantiation() ||
15917+
(Base->isTypeDependent() && !R.empty())) {
15918+
IsDependent = true;
15919+
return Base;
15920+
}
15921+
1590815922
R.suppressAccessDiagnostics();
1590915923

1591015924
for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end();

clang/lib/Sema/TreeTransform.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -16583,10 +16583,14 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
1658316583
} else if (Op == OO_Arrow) {
1658416584
// It is possible that the type refers to a RecoveryExpr created earlier
1658516585
// in the tree transformation.
16586-
if (First->getType()->isDependentType())
16586+
if (First->containsErrors())
1658716587
return ExprError();
16588+
bool IsDependent;
1658816589
// -> is never a builtin operation.
16589-
return SemaRef.BuildOverloadedArrowExpr(nullptr, First, OpLoc);
16590+
ExprResult Result = SemaRef.BuildOverloadedArrowExpr(
16591+
First, OpLoc, /*NoArrowOperatorFound=*/nullptr, IsDependent);
16592+
assert(!IsDependent);
16593+
return Result;
1659016594
} else if (Second == nullptr || isPostIncDec) {
1659116595
if (!First->getType()->isOverloadableType() ||
1659216596
(Op == OO_Amp && getSema().isQualifiedMemberAccess(First))) {

clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp

+13-11
Original file line numberDiff line numberDiff line change
@@ -484,16 +484,19 @@ namespace N4 {
484484
template<typename T>
485485
struct A {
486486
void not_instantiated(A a, A<T> b, T c) {
487-
a->x;
488-
b->x;
487+
a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
488+
b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
489489
c->x;
490490
}
491491

492492
void instantiated(A a, A<T> b, T c) {
493-
a->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
494-
// expected-error@-1 {{no member named 'x' in 'N4::A<int>'}}
495-
b->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
496-
// expected-error@-1 {{no member named 'x' in 'N4::A<int>'}}
493+
// FIXME: We should only emit a single diagnostic suggesting to use '.'!
494+
a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
495+
// expected-error@-1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
496+
// expected-error@-2 {{no member named 'x' in 'N4::A<int>'}}
497+
b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
498+
// expected-error@-1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
499+
// expected-error@-2 {{no member named 'x' in 'N4::A<int>'}}
497500
c->x; // expected-error {{member reference type 'int' is not a pointer}}
498501
}
499502
};
@@ -540,11 +543,10 @@ namespace N4 {
540543
a->T::f();
541544
a->T::g();
542545

543-
// FIXME: 'U' should be a dependent name, and its lookup context should be 'a.operator->()'!
544-
a->U::x; // expected-error {{use of undeclared identifier 'U'}}
545-
a->U::y; // expected-error {{use of undeclared identifier 'U'}}
546-
a->U::f(); // expected-error {{use of undeclared identifier 'U'}}
547-
a->U::g(); // expected-error {{use of undeclared identifier 'U'}}
546+
a->U::x;
547+
a->U::y;
548+
a->U::f();
549+
a->U::g();
548550
}
549551

550552
void instantiated(D a) {

0 commit comments

Comments
 (0)