Skip to content

[clang][HeuristicResolver] Track the expression whose type is being simplified after each step in simplifyType() #126689

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 1 commit into from
Feb 11, 2025
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
45 changes: 27 additions & 18 deletions clang/lib/Sema/HeuristicResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,37 +210,46 @@ QualType HeuristicResolverImpl::getPointeeType(QualType T) {
QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E,
bool UnwrapPointer) {
bool DidUnwrapPointer = false;
auto SimplifyOneStep = [&](QualType T) {
// A type, together with an optional expression whose type it represents
// which may have additional information about the expression's type
// not stored in the QualType itself.
struct TypeExprPair {
QualType Type;
const Expr *E = nullptr;
};
TypeExprPair Current{Type, E};
auto SimplifyOneStep = [UnwrapPointer, &DidUnwrapPointer,
this](TypeExprPair T) -> TypeExprPair {
if (UnwrapPointer) {
if (QualType Pointee = getPointeeType(T); !Pointee.isNull()) {
if (QualType Pointee = getPointeeType(T.Type); !Pointee.isNull()) {
DidUnwrapPointer = true;
return Pointee;
return {Pointee};
}
}
if (const auto *RT = T->getAs<ReferenceType>()) {
if (const auto *RT = T.Type->getAs<ReferenceType>()) {
// Does not count as "unwrap pointer".
return RT->getPointeeType();
return {RT->getPointeeType()};
}
if (const auto *BT = T->getAs<BuiltinType>()) {
if (const auto *BT = T.Type->getAs<BuiltinType>()) {
// If BaseType is the type of a dependent expression, it's just
// represented as BuiltinType::Dependent which gives us no information. We
// can get further by analyzing the dependent expression.
if (E && BT->getKind() == BuiltinType::Dependent) {
return resolveExprToType(E);
if (T.E && BT->getKind() == BuiltinType::Dependent) {
return {resolveExprToType(T.E), T.E};
}
}
if (const auto *AT = T->getContainedAutoType()) {
if (const auto *AT = T.Type->getContainedAutoType()) {
// If T contains a dependent `auto` type, deduction will not have
// been performed on it yet. In simple cases (e.g. `auto` variable with
// initializer), get the approximate type that would result from
// deduction.
// FIXME: A more accurate implementation would propagate things like the
// `const` in `const auto`.
if (E && AT->isUndeducedAutoType()) {
if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
if (T.E && AT->isUndeducedAutoType()) {
if (const auto *DRE = dyn_cast<DeclRefExpr>(T.E)) {
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
if (VD->hasInit())
return resolveExprToType(VD->getInit());
if (auto *Init = VD->getInit())
return {resolveExprToType(Init), Init};
}
}
}
Expand All @@ -251,15 +260,15 @@ QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E,
// simplification steps.
size_t StepCount = 0;
const size_t MaxSteps = 64;
while (!Type.isNull() && StepCount++ < MaxSteps) {
QualType New = SimplifyOneStep(Type);
if (New == Type)
while (!Current.Type.isNull() && StepCount++ < MaxSteps) {
TypeExprPair New = SimplifyOneStep(Current);
if (New.Type == Current.Type)
break;
Type = New;
Current = New;
}
if (UnwrapPointer && !DidUnwrapPointer)
return QualType();
return Type;
return Current.Type;
}

std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
Expand Down
16 changes: 16 additions & 0 deletions clang/unittests/Sema/HeuristicResolverTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,22 @@ TEST(HeuristicResolver, MemberExpr_DeducedNonTypeTemplateParameter) {
fieldDecl(hasName("found")).bind("output"));
}

TEST(HeuristicResolver, MemberExpr_HangIssue126536) {
std::string Code = R"cpp(
template <class T>
void foo() {
T bar;
auto baz = (bar, bar);
baz.foo();
}
)cpp";
// Test resolution of "foo" in "baz.foo()".
// Here, we are testing that we do not get into an infinite loop.
expectResolution(
Code, &HeuristicResolver::resolveMemberExpr,
cxxDependentScopeMemberExpr(hasMemberName("foo")).bind("input"));
}

TEST(HeuristicResolver, DeclRefExpr_StaticMethod) {
std::string Code = R"cpp(
template <typename T>
Expand Down