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

via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 10 23:00:39 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Nathan Ridge (HighCommander4)

<details>
<summary>Changes</summary>

Fixes https://github.com/llvm/llvm-project/issues/126536

---
Full diff: https://github.com/llvm/llvm-project/pull/126689.diff


2 Files Affected:

- (modified) clang/lib/Sema/HeuristicResolver.cpp (+29-18) 
- (modified) clang/unittests/Sema/HeuristicResolverTest.cpp (+16) 


``````````diff
diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 36e5b44b8b12cc1..3cbf33dcdced383 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -210,52 +210,63 @@ 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 (VD->hasInit()) {
+              auto *Init = VD->getInit();
+              return {resolveExprToType(Init), Init};
+            }
           }
         }
       }
     }
     return T;
   };
-  while (!Type.isNull()) {
-    QualType New = SimplifyOneStep(Type);
-    if (New == Type)
+  while (!Current.Type.isNull()) {
+    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(
diff --git a/clang/unittests/Sema/HeuristicResolverTest.cpp b/clang/unittests/Sema/HeuristicResolverTest.cpp
index 5c3459dbeb1018b..c7cfe7917c532e6 100644
--- a/clang/unittests/Sema/HeuristicResolverTest.cpp
+++ b/clang/unittests/Sema/HeuristicResolverTest.cpp
@@ -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>

``````````

</details>


https://github.com/llvm/llvm-project/pull/126689


More information about the cfe-commits mailing list