[clang] [Sema] Fast-path simple plain auto deduction in DeduceAutoType (PR #188196)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 29 21:45:37 PDT 2026
https://github.com/heturing updated https://github.com/llvm/llvm-project/pull/188196
>From 3fa4d53eabc50af0328a5960c8c15ffdaae4f5b9 Mon Sep 17 00:00:00 2001
From: heturing <32864610+heturing at users.noreply.github.com>
Date: Mon, 23 Mar 2026 22:00:32 -0600
Subject: [PATCH 1/3] [Sema] Add a fast path for simple plain auto deduction
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 19 +++-
.../SemaCXX/special-case-auto-deduction.cpp | 105 ++++++++++++++++++
2 files changed, 123 insertions(+), 1 deletion(-)
create mode 100644 clang/test/SemaCXX/special-case-auto-deduction.cpp
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f18a34415ba43..f5c672d9d697c 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5262,6 +5262,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);
@@ -5325,7 +5343,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)));
+
+
>From ab5a971a7f51882be1498d79791afa78925e0579 Mon Sep 17 00:00:00 2001
From: Jiaqi He <heturing at gmail.com>
Date: Wed, 25 Mar 2026 00:30:22 -0600
Subject: [PATCH 2/3] [Sema] Extend fast path for auto deduction
Extend the Sema::DeduceAutoType fast path to cover more simple `auto`
deduction cases.
In particular, this supports non-canonical initializer types,
top-level-cv-qualified `auto`, and single-level `auto*` declarators,
while keeping ObjC and OpenCL cases on the existing slow path.
Also add tests for class, union, enum, and `__restrict__` pointer cases.
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 62 +++--
.../SemaCXX/special-case-auto-deduction.cpp | 247 ++++++++++++++----
2 files changed, 239 insertions(+), 70 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f5c672d9d697c..bb8ddc6ad1c4b 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5249,9 +5249,13 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
// Deduce type of TemplParam in Func(Init)
SmallVector<DeducedTemplateArgument, 1> Deduced;
Deduced.resize(1);
-
SmallVector<OriginalCallArg, 4> OriginalCallArgs;
+ bool CanTryFastPath =
+ !InitList &&
+ !Init->getType()->isSpecificBuiltinType(BuiltinType::Overload) &&
+ !getLangOpts().ObjC && !getLangOpts().OpenCL;
+
QualType DeducedType;
// If this is a 'decltype(auto)' specifier, do the decltype dance.
if (AT->isDecltypeAuto()) {
@@ -5262,25 +5266,41 @@ 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 {
+ } else if (CanTryFastPath) {
+ // Fast-path a subset of `auto` deduction for non-init-list cases in
+ // non-ObjC and non-OpenCL language modes. 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. For single-level `auto*` declarators, the deduced type
+ // is the pointee type of the processed initializer type.
+
+ QualType TypeTy = Type.getType();
+ bool IsPlainOrTopLevelCvAuto = Context.hasSameType(
+ TypeTy.getLocalUnqualifiedType(), Context.getAutoDeductType());
+ bool IsSimpleAutoStar =
+ TypeTy->isPointerType() &&
+ Context.hasSameType(TypeTy->getPointeeType().getLocalUnqualifiedType(),
+ Context.getAutoDeductType());
+
+ QualType ProcessedInitTy = Init->getType().getNonReferenceType();
+ if (ProcessedInitTy->isArrayType() || ProcessedInitTy->isFunctionType())
+ ProcessedInitTy = Context.getDecayedType(ProcessedInitTy);
+
+ ProcessedInitTy = ProcessedInitTy.getUnqualifiedType();
+ bool CanUsePointerFastPath =
+ IsSimpleAutoStar && ProcessedInitTy->isPointerType();
+
+ if (IsPlainOrTopLevelCvAuto) {
+ DeducedType = ProcessedInitTy;
+ assert(!DeducedType.isNull());
+ } else if (CanUsePointerFastPath) {
+ DeducedType = ProcessedInitTy->getPointeeType();
+ assert(!DeducedType.isNull());
+ }
+ }
+
+ // Auto deduction with template
+ if (DeducedType.isNull()) {
LocalInstantiationScope InstScope(*this);
// Build template<class TemplParam> void Func(FuncParam);
@@ -5342,6 +5362,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
/*Decomposed=*/false, /*ArgIdx=*/0, /*TDF=*/0, FailedTSC);
TDK != TemplateDeductionResult::Success)
return TDK;
+
}
// Could be null if somehow 'auto' appears in a non-deduced context.
if (Deduced[0].getKind() != TemplateArgument::Type)
@@ -5363,6 +5384,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
}
DeducedType = Context.getCommonSugaredType(Result, DeducedType);
}
+
if (AT->isConstrained() && !IgnoreConstraints &&
CheckDeducedPlaceholderConstraints(
diff --git a/clang/test/SemaCXX/special-case-auto-deduction.cpp b/clang/test/SemaCXX/special-case-auto-deduction.cpp
index ece3b4a9f8c80..96c5f2f06adc1 100644
--- a/clang/test/SemaCXX/special-case-auto-deduction.cpp
+++ b/clang/test/SemaCXX/special-case-auto-deduction.cpp
@@ -1,87 +1,142 @@
-// RUN: %clangxx -std=c++20 -fsyntax-only %s
+// RUN: %clangxx -std=c++20 -fsyntax-only -Xclang -verify %s
#include <initializer_list>
-// Plain `auto` cases covered by the fast path.
+
+// Plain auto tests
int i = 0;
-int &r = i;
-auto a = r;
+auto a = i;
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;
+volatile int vi = 0;
+auto c = vi;
static_assert(__is_same(decltype(c), int));
+int &r = i;
+auto d = r;
+static_assert(__is_same(decltype(d), int));
+
+const int &cr = ci;
+auto e = cr;
+static_assert(__is_same(decltype(e), int));
+
+int &&rr = 1;
+auto f = rr;
+static_assert(__is_same(decltype(f), int));
+
+
+// Plain auto with array / function
+
int arr[3];
auto arr_decay = arr;
static_assert(__is_same(decltype(arr_decay), int *));
+int arr2[2][3];
+auto arr2_decay = arr2;
+static_assert(__is_same(decltype(arr2_decay), int (*)[3]));
+
+int (&rarr)[3] = arr;
+auto rarr_decay = rarr;
+static_assert(__is_same(decltype(rarr_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 *));
+void (&func_ref)() = foo;
+auto func_ref_decay = func_ref;
+static_assert(__is_same(decltype(func_ref_decay), void (*)(void)));
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));
+// pointer qualifier
-volatile int *vp = nullptr;
-auto volatile_ptr = vp;
-static_assert(__is_same(decltype(volatile_ptr), volatile int *));
+int *ip = nullptr;
+auto p1 = ip;
+static_assert(__is_same(decltype(p1), 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>));
+int *const ipc = nullptr;
+auto p2 = ipc;
+static_assert(__is_same(decltype(p2), int *));
-// Reference-valued initializers.
-int j = 1;
-int &lr = j;
-int &&rr = 2;
+const int *cip = nullptr;
+auto p3 = cip;
+static_assert(__is_same(decltype(p3), const int *));
-auto ref_lvalue = lr;
-static_assert(__is_same(decltype(ref_lvalue), int));
+volatile int *vip = nullptr;
+auto p4 = vip;
+static_assert(__is_same(decltype(p4), volatile int *));
-// A named rvalue reference is still an lvalue expression.
-auto ref_named_rvalue = rr;
-static_assert(__is_same(decltype(ref_named_rvalue), int));
+int * __restrict__ rp = nullptr;
+auto p5 = rp;
+static_assert(__is_same(decltype(p5), 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 * __restrict__ crp = nullptr;
+auto p6 = crp;
+static_assert(__is_same(decltype(p6), const int *));
-const int &&crr = 4;
-auto ref_const_rvalue = crr;
-static_assert(__is_same(decltype(ref_const_rvalue), int));
+// non-canonical type
+using Animal = int;
+Animal animal = 0;
+auto t1 = animal;
+static_assert(__is_same(decltype(t1), Animal));
-void (&func_ref)() = foo;
-auto func_ref_decay = func_ref;
-static_assert(__is_same(decltype(func_ref_decay), void (*)(void)));
+using AnimalPtr = int *;
+AnimalPtr ap = nullptr;
+auto t2 = ap;
+static_assert(__is_same(decltype(t2), AnimalPtr));
+
+using ConstInt = const int;
+ConstInt cx = 0;
+auto t3 = cx;
+static_assert(__is_same(decltype(t3), int));
+
+using IntArray3 = int[3];
+IntArray3 ta = {1, 2, 3};
+auto t4 = ta;
+static_assert(__is_same(decltype(t4), int *));
+
+using FuncTy = void();
+FuncTy &fr = foo;
+auto t5 = fr;
+static_assert(__is_same(decltype(t5), void (*)(void)));
+
+// class / union / enum
+
+struct MyStruct {
+ int x;
+};
+MyStruct sv{1};
+auto class_value = sv;
+static_assert(__is_same(decltype(class_value), MyStruct));
+
+union MyUnion {
+ int i;
+ float f;
+};
+MyUnion uv{};
+auto union_value = uv;
+static_assert(__is_same(decltype(union_value), MyUnion));
+
+enum MyEnum { EnumA, EnumB };
+MyEnum ev = EnumA;
+auto enum_value = ev;
+static_assert(__is_same(decltype(enum_value), MyEnum));
+
+enum class MyScopedEnum : unsigned long { X, Y };
+MyScopedEnum sev = MyScopedEnum::X;
+auto scoped_enum_value = sev;
+static_assert(__is_same(decltype(scoped_enum_value), MyScopedEnum));
+
+
+// const auto
-// Adjacent `const auto` cases should remain unchanged.
const auto ca1 = 0;
static_assert(__is_same(decltype(ca1), const int));
@@ -102,4 +157,96 @@ void qux();
const auto ca5 = qux;
static_assert(__is_same(decltype(ca5), void (*const)(void)));
+using AliasConstInt = const int;
+AliasConstInt aci = 3;
+const auto ca6 = aci;
+static_assert(__is_same(decltype(ca6), const int));
+
+// auto *
+
+int *ip1 = nullptr;
+auto *ap1 = ip1;
+static_assert(__is_same(decltype(ap1), int *));
+
+const int *cip1 = nullptr;
+auto *ap2 = cip1;
+static_assert(__is_same(decltype(ap2), const int *));
+
+int arr5[3];
+auto *ap3 = arr5;
+static_assert(__is_same(decltype(ap3), int *));
+
+void f1();
+auto *ap4 = f1;
+static_assert(__is_same(decltype(ap4), void (*)(void)));
+
+using Animal2 = int;
+Animal2 *ap5 = nullptr;
+auto *ap6 = ap5;
+static_assert(__is_same(decltype(ap6), Animal2 *));
+
+
+// const auto *
+
+int *ip2 = nullptr;
+const auto *cp1 = ip2;
+static_assert(__is_same(decltype(cp1), const int *));
+
+const int *cip2 = nullptr;
+const auto *cp2 = cip2;
+static_assert(__is_same(decltype(cp2), const int *));
+
+int arr6[3];
+const auto *cp3 = arr6;
+static_assert(__is_same(decltype(cp3), const int *));
+
+using Animal3 = int;
+Animal3 *ap7 = nullptr;
+const auto *cp4 = ap7;
+static_assert(__is_same(decltype(cp4), const Animal3 *));
+
+
+// auto ** / const auto **
+
+int **pp1 = nullptr;
+auto **dp1 = pp1;
+static_assert(__is_same(decltype(dp1), int **));
+
+const int **pp2 = nullptr;
+auto **dp2 = pp2;
+static_assert(__is_same(decltype(dp2), const int **));
+
+const int *pbase = nullptr;
+const int **pp3 = &pbase;
+const auto **dp3 = pp3;
+static_assert(__is_same(decltype(dp3), const int **));
+
+using Animal4 = int;
+Animal4 **pp4 = nullptr;
+auto **dp4 = pp4;
+static_assert(__is_same(decltype(dp4), Animal4 **));
+
+
+// init-list
+
+auto ilist = {1, 2, 3};
+static_assert(__is_same(decltype(ilist), std::initializer_list<int>));
+
+// untouched case
+
+
+int x = 0;
+auto *bad = x; // expected-error {{variable 'bad' with type 'auto *' has incompatible initializer of type 'int'}}
+
+int y = 0;
+auto &ref = y;
+static_assert(__is_same(decltype(ref), int &));
+
+
+int *p = nullptr;
+auto *const pc = p;
+static_assert(__is_same(decltype(pc), int * const));
+
+const auto *const cpc = p;
+static_assert(__is_same(decltype(cpc), const int * const));
>From 5787baf4fabce03930510e59d5af8fef3e38fb58 Mon Sep 17 00:00:00 2001
From: Jiaqi He <heturing at gmail.com>
Date: Sun, 29 Mar 2026 19:41:42 -0600
Subject: [PATCH 3/3] [Sema] Add a fast path for additional simple auto
deduction cases
---
clang/docs/ReleaseNotes.rst | 5 ++
clang/lib/Sema/SemaTemplateDeduction.cpp | 58 ++++++++++++++++---
.../SemaCXX/special-case-auto-deduction.cpp | 19 +++++-
3 files changed, 72 insertions(+), 10 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6a2632543d337..82be304da7abc 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -169,6 +169,11 @@ Non-comprehensive list of changes in this release
- Added header ``endian.h`` which contains byte order helpers specified in POSIX
+- Improved the performance of ``auto`` deduction by adding the fast path
+ for additional simple cases, including non-canonical initializer types,
+ top-level-cv-qualified ``auto``, and single-level ``auto*`` declarators in
+ supported language modes.
+
New Compiler Flags
------------------
- New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index bb8ddc6ad1c4b..69dc5656f855f 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5256,6 +5256,10 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
!Init->getType()->isSpecificBuiltinType(BuiltinType::Overload) &&
!getLangOpts().ObjC && !getLangOpts().OpenCL;
+#ifndef NDEBUG
+ bool FastPathUsed = false;
+#endif
+
QualType DeducedType;
// If this is a 'decltype(auto)' specifier, do the decltype dance.
if (AT->isDecltypeAuto()) {
@@ -5274,13 +5278,14 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
// cv-qualifiers. For single-level `auto*` declarators, the deduced type
// is the pointee type of the processed initializer type.
- QualType TypeTy = Type.getType();
+ QualType DeclaredTy = Type.getType();
bool IsPlainOrTopLevelCvAuto = Context.hasSameType(
- TypeTy.getLocalUnqualifiedType(), Context.getAutoDeductType());
+ DeclaredTy.getLocalUnqualifiedType(), Context.getAutoDeductType());
bool IsSimpleAutoStar =
- TypeTy->isPointerType() &&
- Context.hasSameType(TypeTy->getPointeeType().getLocalUnqualifiedType(),
- Context.getAutoDeductType());
+ DeclaredTy->isPointerType() &&
+ Context.hasSameType(
+ DeclaredTy->getPointeeType().getLocalUnqualifiedType(),
+ Context.getAutoDeductType());
QualType ProcessedInitTy = Init->getType().getNonReferenceType();
if (ProcessedInitTy->isArrayType() || ProcessedInitTy->isFunctionType())
@@ -5292,15 +5297,32 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
if (IsPlainOrTopLevelCvAuto) {
DeducedType = ProcessedInitTy;
- assert(!DeducedType.isNull());
+#ifndef NDEBUG
+ FastPathUsed = true;
+#endif
} else if (CanUsePointerFastPath) {
DeducedType = ProcessedInitTy->getPointeeType();
- assert(!DeducedType.isNull());
+ Qualifiers ProcessedInitQuals = DeducedType.getLocalQualifiers();
+ Qualifiers TypeQuals = DeclaredTy->getPointeeType().getLocalQualifiers();
+ ProcessedInitQuals.removeCVRQualifiers(TypeQuals.getCVRQualifiers());
+ DeducedType = Context.getQualifiedType(
+ DeducedType.getLocalUnqualifiedType(), ProcessedInitQuals);
+#ifndef NDEBUG
+ FastPathUsed = true;
+#endif
}
}
// Auto deduction with template
+ // When assertions are enabled, check that the fast path deduces the same
+ // canonical type as the slow path.
+#ifndef NDEBUG
+ if (DeducedType.isNull() || FastPathUsed) {
+ QualType FastPathDeducedType = DeducedType;
+#else
if (DeducedType.isNull()) {
+#endif
+
LocalInstantiationScope InstScope(*this);
// Build template<class TemplParam> void Func(FuncParam);
@@ -5369,6 +5391,28 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result,
return TemplateDeductionResult::Incomplete;
DeducedType = Deduced[0].getAsType();
+#ifndef NDEBUG
+ if (FastPathUsed) {
+ // Ignore differences due only to QualType sugar.
+ if (FastPathDeducedType.getCanonicalType() !=
+ DeducedType.getCanonicalType()) {
+ llvm::errs() << "Deducing: ";
+ Type.dump();
+ Init->dump();
+ llvm::errs() << "Fast-path deduced type: ";
+ FastPathDeducedType.dump();
+
+ llvm::errs() << "Slow-path deduced type: ";
+ DeducedType.dump();
+ }
+
+ assert(FastPathDeducedType.getCanonicalType() ==
+ DeducedType.getCanonicalType() &&
+ "fast path auto deduction produced a different deduced type than "
+ "the template-deduction path");
+ }
+#endif
+
if (InitList) {
DeducedType = BuildStdInitializerList(DeducedType, Loc);
if (DeducedType.isNull())
diff --git a/clang/test/SemaCXX/special-case-auto-deduction.cpp b/clang/test/SemaCXX/special-case-auto-deduction.cpp
index 96c5f2f06adc1..59c6d3103a7a8 100644
--- a/clang/test/SemaCXX/special-case-auto-deduction.cpp
+++ b/clang/test/SemaCXX/special-case-auto-deduction.cpp
@@ -1,6 +1,12 @@
-// RUN: %clangxx -std=c++20 -fsyntax-only -Xclang -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
-#include <initializer_list>
+namespace std {
+template <typename T>
+struct initializer_list {
+ const T *begin;
+ const T *end;
+};
+}
// Plain auto tests
@@ -185,6 +191,14 @@ Animal2 *ap5 = nullptr;
auto *ap6 = ap5;
static_assert(__is_same(decltype(ap6), Animal2 *));
+volatile int *vp2 = nullptr;
+const auto *cp5 = vp2;
+static_assert(__is_same(decltype(cp5), const volatile int *));
+
+const volatile int *cvp = nullptr;
+const auto *cp6 = cvp;
+static_assert(__is_same(decltype(cp6), const volatile int *));
+
// const auto *
@@ -234,7 +248,6 @@ static_assert(__is_same(decltype(ilist), std::initializer_list<int>));
// untouched case
-
int x = 0;
auto *bad = x; // expected-error {{variable 'bad' with type 'auto *' has incompatible initializer of type 'int'}}
More information about the cfe-commits
mailing list