Skip to content

Commit 7fd8426

Browse files
[clang][Sema] Handle pointer and reference type more robustly in HeuristicResolver::resolveMemberExpr() (llvm#124451)
Fixes llvm#124450
1 parent 928cad4 commit 7fd8426

File tree

2 files changed

+103
-30
lines changed

2 files changed

+103
-30
lines changed

clang/lib/Sema/HeuristicResolver.cpp

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ class HeuristicResolverImpl {
7474
// resolves it to a TagDecl in which we can try name lookup.
7575
TagDecl *resolveTypeToTagDecl(const Type *T);
7676

77+
// Helper function for simplifying a type.
78+
// `Type` is the type to simplify.
79+
// `E` is the expression whose type `Type` is, if known. This sometimes
80+
// contains information relevant to the type that's not stored in `Type`
81+
// itself.
82+
// If `UnwrapPointer` is true, exactly only pointer type will be unwrapped
83+
// during simplification, and the operation fails if no pointer type is found.
84+
QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer);
85+
7786
// This is a reimplementation of CXXRecordDecl::lookupDependentName()
7887
// so that the implementation can call into other HeuristicResolver helpers.
7988
// FIXME: Once HeuristicResolver is upstreamed to the clang libraries
@@ -198,6 +207,57 @@ QualType HeuristicResolverImpl::getPointeeType(QualType T) {
198207
return FirstArg.getAsType();
199208
}
200209

210+
QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E,
211+
bool UnwrapPointer) {
212+
bool DidUnwrapPointer = false;
213+
auto SimplifyOneStep = [&](QualType T) {
214+
if (UnwrapPointer) {
215+
if (QualType Pointee = getPointeeType(T); !Pointee.isNull()) {
216+
DidUnwrapPointer = true;
217+
return Pointee;
218+
}
219+
}
220+
if (const auto *RT = T->getAs<ReferenceType>()) {
221+
// Does not count as "unwrap pointer".
222+
return RT->getPointeeType();
223+
}
224+
if (const auto *BT = T->getAs<BuiltinType>()) {
225+
// If BaseType is the type of a dependent expression, it's just
226+
// represented as BuiltinType::Dependent which gives us no information. We
227+
// can get further by analyzing the dependent expression.
228+
if (E && BT->getKind() == BuiltinType::Dependent) {
229+
return resolveExprToType(E);
230+
}
231+
}
232+
if (const auto *AT = T->getContainedAutoType()) {
233+
// If T contains a dependent `auto` type, deduction will not have
234+
// been performed on it yet. In simple cases (e.g. `auto` variable with
235+
// initializer), get the approximate type that would result from
236+
// deduction.
237+
// FIXME: A more accurate implementation would propagate things like the
238+
// `const` in `const auto`.
239+
if (E && AT->isUndeducedAutoType()) {
240+
if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
241+
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
242+
if (VD->hasInit())
243+
return resolveExprToType(VD->getInit());
244+
}
245+
}
246+
}
247+
}
248+
return T;
249+
};
250+
while (!Type.isNull()) {
251+
QualType New = SimplifyOneStep(Type);
252+
if (New == Type)
253+
break;
254+
Type = New;
255+
}
256+
if (UnwrapPointer && !DidUnwrapPointer)
257+
return QualType();
258+
return Type;
259+
}
260+
201261
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
202262
const CXXDependentScopeMemberExpr *ME) {
203263
// If the expression has a qualifier, try resolving the member inside the
@@ -230,36 +290,7 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
230290
// Try resolving the member inside the expression's base type.
231291
Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
232292
QualType BaseType = ME->getBaseType();
233-
if (ME->isArrow()) {
234-
BaseType = getPointeeType(BaseType);
235-
if (BaseType.isNull())
236-
return {};
237-
}
238-
if (const auto *BT = BaseType->getAs<BuiltinType>()) {
239-
// If BaseType is the type of a dependent expression, it's just
240-
// represented as BuiltinType::Dependent which gives us no information. We
241-
// can get further by analyzing the dependent expression.
242-
if (Base && BT->getKind() == BuiltinType::Dependent) {
243-
BaseType = resolveExprToType(Base);
244-
if (BaseType.isNull())
245-
return {};
246-
}
247-
}
248-
if (const auto *AT = BaseType->getContainedAutoType()) {
249-
// If BaseType contains a dependent `auto` type, deduction will not have
250-
// been performed on it yet. In simple cases (e.g. `auto` variable with
251-
// initializer), get the approximate type that would result from deduction.
252-
// FIXME: A more accurate implementation would propagate things like the
253-
// `const` in `const auto`.
254-
if (AT->isUndeducedAutoType()) {
255-
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
256-
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
257-
if (VD->hasInit())
258-
BaseType = resolveExprToType(VD->getInit());
259-
}
260-
}
261-
}
262-
}
293+
BaseType = simplifyType(BaseType, Base, ME->isArrow());
263294
return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
264295
}
265296

clang/unittests/Sema/HeuristicResolverTest.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,48 @@ TEST(HeuristicResolver, MemberExpr_Chained) {
213213
cxxMethodDecl(hasName("foo")).bind("output"));
214214
}
215215

216+
TEST(HeuristicResolver, MemberExpr_ReferenceType) {
217+
std::string Code = R"cpp(
218+
struct B {
219+
int waldo;
220+
};
221+
template <typename T>
222+
struct A {
223+
B &b;
224+
};
225+
template <typename T>
226+
void foo(A<T> &a) {
227+
a.b.waldo;
228+
}
229+
)cpp";
230+
// Test resolution of "waldo" in "a.b.waldo".
231+
expectResolution(
232+
Code, &HeuristicResolver::resolveMemberExpr,
233+
cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
234+
fieldDecl(hasName("waldo")).bind("output"));
235+
}
236+
237+
TEST(HeuristicResolver, MemberExpr_PointerType) {
238+
std::string Code = R"cpp(
239+
struct B {
240+
int waldo;
241+
};
242+
template <typename T>
243+
struct A {
244+
B *b;
245+
};
246+
template <typename T>
247+
void foo(A<T> &a) {
248+
a.b->waldo;
249+
}
250+
)cpp";
251+
// Test resolution of "waldo" in "a.b->waldo".
252+
expectResolution(
253+
Code, &HeuristicResolver::resolveMemberExpr,
254+
cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"),
255+
fieldDecl(hasName("waldo")).bind("output"));
256+
}
257+
216258
TEST(HeuristicResolver, MemberExpr_TemplateArgs) {
217259
std::string Code = R"cpp(
218260
struct Foo {

0 commit comments

Comments
 (0)