Skip to content

Commit e64fc41

Browse files
sdkrystianllvmbot
authored andcommitted
[Clang][Sema] Use the correct lookup context when building overloaded 'operator->' in the current instantiation (llvm#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 llvm#104268. (cherry picked from commit 3cdb30e)
1 parent 0c64156 commit e64fc41

File tree

5 files changed

+62
-37
lines changed

5 files changed

+62
-37
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10615,9 +10615,9 @@ class Sema final : public SemaBase {
1061510615
/// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
1061610616
/// (if one exists), where @c Base is an expression of class type and
1061710617
/// @c Member is the name of the member we're trying to find.
10618-
ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base,
10619-
SourceLocation OpLoc,
10620-
bool *NoArrowOperatorFound = nullptr);
10618+
ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
10619+
bool *NoArrowOperatorFound,
10620+
bool &IsDependent);
1062110621

1062210622
ExprResult BuildCXXMemberCallExpr(Expr *Exp, NamedDecl *FoundDecl,
1062310623
CXXConversionDecl *Method,

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7850,18 +7850,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
78507850

78517851
QualType BaseType = Base->getType();
78527852
MayBePseudoDestructor = false;
7853-
if (BaseType->isDependentType()) {
7854-
// If we have a pointer to a dependent type and are using the -> operator,
7855-
// the object type is the type that the pointer points to. We might still
7856-
// have enough information about that type to do something useful.
7857-
if (OpKind == tok::arrow)
7858-
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
7859-
BaseType = Ptr->getPointeeType();
7860-
7861-
ObjectType = ParsedType::make(BaseType);
7862-
MayBePseudoDestructor = true;
7863-
return Base;
7864-
}
78657853

78667854
// C++ [over.match.oper]p8:
78677855
// [...] When operator->returns, the operator-> is applied to the value
@@ -7876,7 +7864,8 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
78767864
SmallVector<FunctionDecl*, 8> OperatorArrows;
78777865
CTypes.insert(Context.getCanonicalType(BaseType));
78787866

7879-
while (BaseType->isRecordType()) {
7867+
while (
7868+
isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) {
78807869
if (OperatorArrows.size() >= getLangOpts().ArrowDepth) {
78817870
Diag(OpLoc, diag::err_operator_arrow_depth_exceeded)
78827871
<< StartingType << getLangOpts().ArrowDepth << Base->getSourceRange();
@@ -7886,15 +7875,26 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
78867875
return ExprError();
78877876
}
78887877

7878+
bool IsDependent;
78897879
Result = BuildOverloadedArrowExpr(
7890-
S, Base, OpLoc,
7880+
Base, OpLoc,
78917881
// When in a template specialization and on the first loop iteration,
78927882
// potentially give the default diagnostic (with the fixit in a
78937883
// separate note) instead of having the error reported back to here
78947884
// and giving a diagnostic with a fixit attached to the error itself.
78957885
(FirstIteration && CurFD && CurFD->isFunctionTemplateSpecialization())
78967886
? nullptr
7897-
: &NoArrowOperatorFound);
7887+
: &NoArrowOperatorFound,
7888+
IsDependent);
7889+
7890+
if (IsDependent) {
7891+
// BuildOverloadedArrowExpr sets IsDependent to indicate that we need
7892+
// to build a dependent overloaded arrow expression.
7893+
assert(BaseType->isDependentType());
7894+
BaseType = Context.DependentTy;
7895+
break;
7896+
}
7897+
78987898
if (Result.isInvalid()) {
78997899
if (NoArrowOperatorFound) {
79007900
if (FirstIteration) {
@@ -7914,6 +7914,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
79147914
}
79157915
return ExprError();
79167916
}
7917+
79177918
Base = Result.get();
79187919
if (CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(Base))
79197920
OperatorArrows.push_back(OpCall->getDirectCallee());
@@ -7951,7 +7952,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
79517952
// it's legal for the type to be incomplete if this is a pseudo-destructor
79527953
// call. We'll do more incomplete-type checks later in the lookup process,
79537954
// so just skip this check for ObjC types.
7954-
if (!BaseType->isRecordType()) {
7955+
if (!isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) {
79557956
ObjectType = ParsedType::make(BaseType);
79567957
MayBePseudoDestructor = true;
79577958
return Base;
@@ -7969,6 +7970,10 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
79697970
return CreateRecoveryExpr(Base->getBeginLoc(), Base->getEndLoc(), {Base});
79707971
}
79717972

7973+
// We can't implicitly declare the destructor for a templated class.
7974+
if (BaseType->isDependentType())
7975+
MayBePseudoDestructor = true;
7976+
79727977
// C++ [basic.lookup.classref]p2:
79737978
// If the id-expression in a class member access (5.2.5) is an
79747979
// unqualified-id, and the type of the object expression is of a class

clang/lib/Sema/SemaOverload.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15805,12 +15805,14 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
1580515805
return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), Method);
1580615806
}
1580715807

15808-
ExprResult
15809-
Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
15810-
bool *NoArrowOperatorFound) {
15811-
assert(Base->getType()->isRecordType() &&
15808+
ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
15809+
bool *NoArrowOperatorFound,
15810+
bool &IsDependent) {
15811+
assert(Base->getType()->getAsRecordDecl() &&
1581215812
"left-hand side must have class type");
1581315813

15814+
IsDependent = false;
15815+
1581415816
if (checkPlaceholderForOverload(*this, Base))
1581515817
return ExprError();
1581615818

@@ -15831,7 +15833,19 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
1583115833
return ExprError();
1583215834

1583315835
LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName);
15834-
LookupQualifiedName(R, Base->getType()->castAs<RecordType>()->getDecl());
15836+
LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType());
15837+
15838+
// If the expression is dependent and we either:
15839+
// - found a member of the current instantiation named 'operator->', or
15840+
// - found nothing, and the lookup context has no dependent base classes
15841+
//
15842+
// then we should build a dependent class member access expression.
15843+
if (R.wasNotFoundInCurrentInstantiation() ||
15844+
(Base->isTypeDependent() && !R.empty())) {
15845+
IsDependent = true;
15846+
return Base;
15847+
}
15848+
1583515849
R.suppressAccessDiagnostics();
1583615850

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

clang/lib/Sema/TreeTransform.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16376,10 +16376,14 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
1637616376
} else if (Op == OO_Arrow) {
1637716377
// It is possible that the type refers to a RecoveryExpr created earlier
1637816378
// in the tree transformation.
16379-
if (First->getType()->isDependentType())
16379+
if (First->containsErrors())
1638016380
return ExprError();
16381+
bool IsDependent;
1638116382
// -> is never a builtin operation.
16382-
return SemaRef.BuildOverloadedArrowExpr(nullptr, First, OpLoc);
16383+
ExprResult Result = SemaRef.BuildOverloadedArrowExpr(
16384+
First, OpLoc, /*NoArrowOperatorFound=*/nullptr, IsDependent);
16385+
assert(!IsDependent);
16386+
return Result;
1638316387
} else if (Second == nullptr || isPostIncDec) {
1638416388
if (!First->getType()->isOverloadableType() ||
1638516389
(Op == OO_Amp && getSema().isQualifiedMemberAccess(First))) {

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

Lines changed: 13 additions & 11 deletions
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)