Skip to content

[Clang][Sema] Use the correct lookup context when building overloaded 'operator->' in the current instantiation #104458

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
Sep 9, 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
6 changes: 3 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10639,9 +10639,9 @@ class Sema final : public SemaBase {
/// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
/// (if one exists), where @c Base is an expression of class type and
/// @c Member is the name of the member we're trying to find.
ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base,
SourceLocation OpLoc,
bool *NoArrowOperatorFound = nullptr);
ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
bool *NoArrowOperatorFound,
bool &IsDependent);

ExprResult BuildCXXMemberCallExpr(Expr *Exp, NamedDecl *FoundDecl,
CXXConversionDecl *Method,
Expand Down
37 changes: 21 additions & 16 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7966,18 +7966,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,

QualType BaseType = Base->getType();
MayBePseudoDestructor = false;
if (BaseType->isDependentType()) {
// If we have a pointer to a dependent type and are using the -> operator,
// the object type is the type that the pointer points to. We might still
// have enough information about that type to do something useful.
if (OpKind == tok::arrow)
if (const PointerType *Ptr = BaseType->getAs<PointerType>())
BaseType = Ptr->getPointeeType();

ObjectType = ParsedType::make(BaseType);
MayBePseudoDestructor = true;
return Base;
}

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

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

bool IsDependent;
Result = BuildOverloadedArrowExpr(
S, Base, OpLoc,
Base, OpLoc,
// When in a template specialization and on the first loop iteration,
// potentially give the default diagnostic (with the fixit in a
// separate note) instead of having the error reported back to here
// and giving a diagnostic with a fixit attached to the error itself.
(FirstIteration && CurFD && CurFD->isFunctionTemplateSpecialization())
? nullptr
: &NoArrowOperatorFound);
: &NoArrowOperatorFound,
IsDependent);

if (IsDependent) {
// BuildOverloadedArrowExpr sets IsDependent to indicate that we need
// to build a dependent overloaded arrow expression.
assert(BaseType->isDependentType());
BaseType = Context.DependentTy;
break;
}

if (Result.isInvalid()) {
if (NoArrowOperatorFound) {
if (FirstIteration) {
Expand All @@ -8030,6 +8030,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
}
return ExprError();
}

Base = Result.get();
if (CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(Base))
OperatorArrows.push_back(OpCall->getDirectCallee());
Expand Down Expand Up @@ -8067,7 +8068,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
// it's legal for the type to be incomplete if this is a pseudo-destructor
// call. We'll do more incomplete-type checks later in the lookup process,
// so just skip this check for ObjC types.
if (!BaseType->isRecordType()) {
if (!isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) {
ObjectType = ParsedType::make(BaseType);
MayBePseudoDestructor = true;
return Base;
Expand All @@ -8085,6 +8086,10 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
return CreateRecoveryExpr(Base->getBeginLoc(), Base->getEndLoc(), {Base});
}

// We can't implicitly declare the destructor for a templated class.
if (BaseType->isDependentType())
MayBePseudoDestructor = true;

// C++ [basic.lookup.classref]p2:
// If the id-expression in a class member access (5.2.5) is an
// unqualified-id, and the type of the object expression is of a class
Expand Down
24 changes: 19 additions & 5 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15878,12 +15878,14 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), Method);
}

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

IsDependent = false;

if (checkPlaceholderForOverload(*this, Base))
return ExprError();

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

LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName);
LookupQualifiedName(R, Base->getType()->castAs<RecordType>()->getDecl());
LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType());

// If the expression is dependent and we either:
// - found a member of the current instantiation named 'operator->', or
// - found nothing, and the lookup context has no dependent base classes
//
// then we should build a dependent class member access expression.
if (R.wasNotFoundInCurrentInstantiation() ||
(Base->isTypeDependent() && !R.empty())) {
IsDependent = true;
return Base;
}

R.suppressAccessDiagnostics();

for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end();
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -16583,10 +16583,14 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
} else if (Op == OO_Arrow) {
// It is possible that the type refers to a RecoveryExpr created earlier
// in the tree transformation.
if (First->getType()->isDependentType())
if (First->containsErrors())
return ExprError();
bool IsDependent;
// -> is never a builtin operation.
return SemaRef.BuildOverloadedArrowExpr(nullptr, First, OpLoc);
ExprResult Result = SemaRef.BuildOverloadedArrowExpr(
First, OpLoc, /*NoArrowOperatorFound=*/nullptr, IsDependent);
assert(!IsDependent);
return Result;
} else if (Second == nullptr || isPostIncDec) {
if (!First->getType()->isOverloadableType() ||
(Op == OO_Amp && getSema().isQualifiedMemberAccess(First))) {
Expand Down
24 changes: 13 additions & 11 deletions clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,16 +484,19 @@ namespace N4 {
template<typename T>
struct A {
void not_instantiated(A a, A<T> b, T c) {
a->x;
b->x;
a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
c->x;
}

void instantiated(A a, A<T> b, T c) {
a->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
// expected-error@-1 {{no member named 'x' in 'N4::A<int>'}}
b->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
// expected-error@-1 {{no member named 'x' in 'N4::A<int>'}}
// FIXME: We should only emit a single diagnostic suggesting to use '.'!
a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
// expected-error@-1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
// expected-error@-2 {{no member named 'x' in 'N4::A<int>'}}
b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
// expected-error@-1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
// expected-error@-2 {{no member named 'x' in 'N4::A<int>'}}
c->x; // expected-error {{member reference type 'int' is not a pointer}}
}
};
Expand Down Expand Up @@ -540,11 +543,10 @@ namespace N4 {
a->T::f();
a->T::g();

// FIXME: 'U' should be a dependent name, and its lookup context should be 'a.operator->()'!
a->U::x; // expected-error {{use of undeclared identifier 'U'}}
a->U::y; // expected-error {{use of undeclared identifier 'U'}}
a->U::f(); // expected-error {{use of undeclared identifier 'U'}}
a->U::g(); // expected-error {{use of undeclared identifier 'U'}}
a->U::x;
a->U::y;
a->U::f();
a->U::g();
}

void instantiated(D a) {
Expand Down
Loading