Skip to content

Commit

Permalink
[FOLD] Fix arrow operator for expressions with non-pointer type that …
Browse files Browse the repository at this point in the history
…is the current instantiation
  • Loading branch information
sdkrystian committed Apr 26, 2024
1 parent 99d48cf commit b8fda81
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 34 deletions.
67 changes: 35 additions & 32 deletions clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,12 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
bool SuppressQualifierCheck,
ActOnMemberAccessExtraArgs *ExtraArgs) {
assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid");
if (R.wasNotFoundInCurrentInstantiation())
// If the member wasn't found in the current instantiation, or if the
// arrow operator was used with a dependent non-pointer object expression,
// build a CXXDependentScopeMemberExpr.
if (R.wasNotFoundInCurrentInstantiation() ||
(IsArrow && !BaseExprType->isPointerType() &&
BaseExprType->isDependentType()))
return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS,
TemplateKWLoc, FirstQualifierInScope,
R.getLookupNameInfo(), TemplateArgs);
Expand Down Expand Up @@ -1036,41 +1041,39 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
<< isa<CXXDestructorDecl>(FD);

if (R.empty()) {
// Rederive where we looked up.
DeclContext *DC =
(SS.isSet() ? computeDeclContext(SS) : computeDeclContext(BaseType));
if (ExtraArgs) {
ExprResult RetryExpr;
if (!IsArrow && BaseExpr && !BaseExpr->isTypeDependent()) {
SFINAETrap Trap(*this, true);
ParsedType ObjectType;
bool MayBePseudoDestructor = false;
RetryExpr = ActOnStartCXXMemberReference(getCurScope(), BaseExpr,
OpLoc, tok::arrow, ObjectType,
MayBePseudoDestructor);
if (RetryExpr.isUsable() && !Trap.hasErrorOccurred()) {
CXXScopeSpec TempSS(SS);
RetryExpr = ActOnMemberAccessExpr(
ExtraArgs->S, RetryExpr.get(), OpLoc, tok::arrow, TempSS,
TemplateKWLoc, ExtraArgs->Id, ExtraArgs->ObjCImpDecl);
}
if (Trap.hasErrorOccurred())
RetryExpr = ExprError();
}
if (RetryExpr.isUsable()) {
Diag(OpLoc, diag::err_no_member_overloaded_arrow)
<< MemberName << DC << FixItHint::CreateReplacement(OpLoc, "->");
return RetryExpr;
ExprResult RetryExpr = ExprError();
if (ExtraArgs && !IsArrow && BaseExpr && !BaseExpr->isTypeDependent()) {
SFINAETrap Trap(*this, true);
ParsedType ObjectType;
bool MayBePseudoDestructor = false;
RetryExpr = ActOnStartCXXMemberReference(getCurScope(), BaseExpr, OpLoc,
tok::arrow, ObjectType,
MayBePseudoDestructor);
if (RetryExpr.isUsable() && !Trap.hasErrorOccurred()) {
CXXScopeSpec TempSS(SS);
RetryExpr = ActOnMemberAccessExpr(
ExtraArgs->S, RetryExpr.get(), OpLoc, tok::arrow, TempSS,
TemplateKWLoc, ExtraArgs->Id, ExtraArgs->ObjCImpDecl);
}
if (Trap.hasErrorOccurred())
RetryExpr = ExprError();
}

// Rederive where we looked up.
DeclContext *DC =
(SS.isSet() ? computeDeclContext(SS) : computeDeclContext(BaseType));
assert(DC);
Diag(R.getNameLoc(), diag::err_no_member)
<< MemberName << DC
<< (SS.isSet()
? SS.getRange()
: (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()));
return ExprError();

if (RetryExpr.isUsable())
Diag(OpLoc, diag::err_no_member_overloaded_arrow)
<< MemberName << DC << FixItHint::CreateReplacement(OpLoc, "->");
else
Diag(R.getNameLoc(), diag::err_no_member)
<< MemberName << DC
<< (SS.isSet()
? SS.getRange()
: (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()));
return RetryExpr;
}

// Diagnose lookups that find only declarations from a non-base
Expand Down
71 changes: 69 additions & 2 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 @@ -439,7 +439,7 @@ namespace N2 {
a->B::C::x;
}
};
}
} // namespace N2

namespace N3 {
struct A { };
Expand All @@ -453,4 +453,71 @@ namespace N3 {
this->A::operator=(*this);
}
};
}
} // namespace N3

namespace N4 {
template<typename T>
struct A {
void not_instantiated(A a, A<T> b, T c) {
a->x;
b->x;
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>'}}
c->x; // expected-error {{member reference type 'int' is not a pointer}}
}
};

template void A<int>::instantiated(A<int>, A<int>, int); // expected-note {{in instantiation of}}

struct B {
int x;

void f();
};

template<typename T>
struct C {
B *operator->();

void not_instantiated(C a, C<T> b, T c) {
a->x;
b->x;
c->x;
}

void instantiated(C a, C<T> b, T c) {
a->x;
b->x;
c->x; // expected-error {{member reference type 'int' is not a pointer}}
}
};

template void C<int>::instantiated(C, C, int); // expected-note {{in instantiation of}}

template<typename T>
struct D {
T *operator->();

void not_instantiated(D a) {
a->x;
a->y;
a->f();
a->g();
}

void instantiated(D a) {
a->x;
a->y; // expected-error {{no member named 'y' in 'N4::B'}}
a->f();
a->g(); // expected-error {{no member named 'g' in 'N4::B'}}
}
};

template void D<B>::instantiated(D); // expected-note {{in instantiation of}}
} // namespace N4

0 comments on commit b8fda81

Please sign in to comment.