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

kadir çetinkaya via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 11 00:12:39 PST 2025


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

>From e5a3d7aa5362357acd30ef5d1bafaa814ac2c89f Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Tue, 11 Feb 2025 01:56:50 -0500
Subject: [PATCH 1/2] [clang][HeuristicResolver] Track the expression whose
 type is being simplified after each step in simplifyType()

---
 clang/lib/Sema/HeuristicResolver.cpp          | 47 ++++++++++++-------
 .../unittests/Sema/HeuristicResolverTest.cpp  | 16 +++++++
 2 files changed, 45 insertions(+), 18 deletions(-)

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>

>From 780894689ff741c761457eec1c925679309336a3 Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Tue, 11 Feb 2025 03:12:30 -0500
Subject: [PATCH 2/2] [clang][HeuristicResolver] Additional hardening against
 an infinite loop in simplifyType() (#126690)

---
 clang/lib/Sema/HeuristicResolver.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 3cbf33dcdced383..adce403412f6898 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -258,7 +258,11 @@ QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E,
     }
     return T;
   };
-  while (!Current.Type.isNull()) {
+  // As an additional protection against infinite loops, bound the number of
+  // simplification steps.
+  size_t StepCount = 0;
+  const size_t MaxSteps = 64;
+  while (!Current.Type.isNull() && StepCount++ < MaxSteps) {
     TypeExprPair New = SimplifyOneStep(Current);
     if (New.Type == Current.Type)
       break;



More information about the cfe-commits mailing list