[clang] [Sema] Fast-path simple plain auto deduction in DeduceAutoType (PR #188196)

via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 24 01:39:08 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: heturing (heturing)

<details>
<summary>Changes</summary>

Fixes #<!-- -->186274

Add a fast path in `Sema::DeduceAutoType` for a subset of plain, 
unconstrained `auto` deduction.

For non-init-list cases where the declared type is plain `auto` and the
initializer type is canonical, non-overload, and not an Objective-C object
pointer type, compute the deduced type directly by:
- removing references,
- applying array/function decay, and
- dropping top-level cv-qualifiers.

All other cases continue to use the existing template-deduction-based path.

This avoids building the temporary template deduction machinery for simple
`auto` cases while preserving the existing behavior for more complex forms.

On the reproducer from the issue, the patched version reduces compile time from
an average of 2.679s to 1.528s over 5 runs (about 1.75x faster, or a 42.9%
reduction). For reference, replacing all `auto` with `int` in the same test
case gives an average compile time of 1.345s.

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


2 Files Affected:

- (modified) clang/lib/Sema/SemaTemplateDeduction.cpp (+18-1) 
- (added) clang/test/SemaCXX/special-case-auto-deduction.cpp (+105) 


``````````diff
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 477af31def50e..2bde052c9ddc3 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5256,6 +5256,24 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
 
     DeducedType = getDecltypeForExpr(Init);
     assert(!DeducedType.isNull());
+  } else if (!InitList && !AT->isGNUAutoType() && !AT->isConstrained() &&
+             Context.hasSameType(Type.getType(), Context.AutoDeductTy) &&
+             !Init->getType()->isSpecificBuiltinType(BuiltinType::Overload) &&
+             Init->getType().isCanonical() &&
+             !Init->getType()->isObjCObjectPointerType()) {
+    // Fast-path a subset of plain unconstrained `auto` deduction for
+    // non-init-list cases with canonical initializer types. For these cases,
+    // the deduced type can be computed directly from the initializer type by
+    // removing references, applying array/function decay, and dropping
+    // top-level cv-qualifiers.
+    QualType Ty = Init->getType();
+    Ty = Ty.getNonReferenceType();
+
+    if (Ty->isArrayType() || Ty->isFunctionType())
+      Ty = Context.getDecayedType(Ty);
+
+    Ty = Ty.getLocalUnqualifiedType();
+    DeducedType = Ty;
   } else {
     LocalInstantiationScope InstScope(*this);
 
@@ -5319,7 +5337,6 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
           TDK != TemplateDeductionResult::Success)
         return TDK;
     }
-
     // Could be null if somehow 'auto' appears in a non-deduced context.
     if (Deduced[0].getKind() != TemplateArgument::Type)
       return TemplateDeductionResult::Incomplete;
diff --git a/clang/test/SemaCXX/special-case-auto-deduction.cpp b/clang/test/SemaCXX/special-case-auto-deduction.cpp
new file mode 100644
index 0000000000000..ece3b4a9f8c80
--- /dev/null
+++ b/clang/test/SemaCXX/special-case-auto-deduction.cpp
@@ -0,0 +1,105 @@
+// RUN: %clangxx -std=c++20 -fsyntax-only %s
+
+#include <initializer_list>
+
+// Plain `auto` cases covered by the fast path.
+int i = 0;
+int &r = i;
+auto a = r;
+static_assert(__is_same(decltype(a), int));
+
+const int ci = 0;
+auto b = ci;
+static_assert(__is_same(decltype(b), int));
+
+const int &cr = ci;
+auto c = cr;
+static_assert(__is_same(decltype(c), int));
+
+int arr[3];
+auto arr_decay = arr;
+static_assert(__is_same(decltype(arr_decay), int *));
+
+void foo();
+auto func_decay = foo;
+static_assert(__is_same(decltype(func_decay), void (*)(void)));
+
+int *const p = nullptr;
+auto ptr_top_const_removed = p;
+static_assert(__is_same(decltype(ptr_top_const_removed), int *));
+
+const int *q = nullptr;
+auto ptr_pointee_const_preserved = q;
+static_assert(__is_same(decltype(ptr_pointee_const_preserved), const int *));
+
+int arr2[3] = {0, 1, 2};
+int (&rarr)[3] = arr2;
+auto array_ref_decay = rarr;
+static_assert(__is_same(decltype(array_ref_decay), int *));
+
+auto str_decay = "abc";
+static_assert(__is_same(decltype(str_decay), const char *));
+
+int arr3[2][3] = {{1, 2, 3}, {4, 5, 6}};
+auto multi_arr_decay = arr3;
+static_assert(__is_same(decltype(multi_arr_decay), int (*)[3]));
+
+volatile int vi = 0;
+auto volatile_value = vi;
+static_assert(__is_same(decltype(volatile_value), int));
+
+volatile int *vp = nullptr;
+auto volatile_ptr = vp;
+static_assert(__is_same(decltype(volatile_ptr), volatile int *));
+
+// Non-fast-path init-list case should remain unchanged.
+auto ilist = {1, 2, 3};
+static_assert(__is_same(decltype(ilist), std::initializer_list<int>));
+
+// Reference-valued initializers.
+int j = 1;
+int &lr = j;
+int &&rr = 2;
+
+auto ref_lvalue = lr;
+static_assert(__is_same(decltype(ref_lvalue), int));
+
+// A named rvalue reference is still an lvalue expression.
+auto ref_named_rvalue = rr;
+static_assert(__is_same(decltype(ref_named_rvalue), int));
+
+const int cj = 3;
+const int &clr = cj;
+auto ref_const_lvalue = clr;
+static_assert(__is_same(decltype(ref_const_lvalue), int));
+
+const int &&crr = 4;
+auto ref_const_rvalue = crr;
+static_assert(__is_same(decltype(ref_const_rvalue), int));
+
+void (&func_ref)() = foo;
+auto func_ref_decay = func_ref;
+static_assert(__is_same(decltype(func_ref_decay), void (*)(void)));
+
+// Adjacent `const auto` cases should remain unchanged.
+const auto ca1 = 0;
+static_assert(__is_same(decltype(ca1), const int));
+
+int i2 = 1;
+int &lr2 = i2;
+const auto ca2 = lr2;
+static_assert(__is_same(decltype(ca2), const int));
+
+const int ci2 = 2;
+const auto ca3 = ci2;
+static_assert(__is_same(decltype(ca3), const int));
+
+int arr4[3] = {1, 2, 3};
+const auto ca4 = arr4;
+static_assert(__is_same(decltype(ca4), int *const));
+
+void qux();
+const auto ca5 = qux;
+static_assert(__is_same(decltype(ca5), void (*const)(void)));
+
+

``````````

</details>


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


More information about the cfe-commits mailing list