https://github.com/heturing updated https://github.com/llvm/llvm-project/pull/188196
>From 3fa4d53eabc50af0328a5960c8c15ffdaae4f5b9 Mon Sep 17 00:00:00 2001 From: heturing <[email protected]> Date: Mon, 23 Mar 2026 22:00:32 -0600 Subject: [PATCH 1/4] [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 <[email protected]> Date: Wed, 25 Mar 2026 00:30:22 -0600 Subject: [PATCH 2/4] [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 <[email protected]> Date: Sun, 29 Mar 2026 19:41:42 -0600 Subject: [PATCH 3/4] [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'}} >From 0f3c951556bd8a048068da9ef8d13ff533816c5f Mon Sep 17 00:00:00 2001 From: Jiaqi He <[email protected]> Date: Tue, 31 Mar 2026 23:44:45 -0600 Subject: [PATCH 4/4] [Sema] Add a fast path for simple plain auto deduction --- clang/docs/ReleaseNotes.rst | 6 ++---- clang/lib/Sema/SemaTemplateDeduction.cpp | 21 ++++++++----------- .../SemaCXX/special-case-auto-deduction.cpp | 19 +++++++++++++++++ 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 72b85131e4f54..0f35b0976bca8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -183,10 +183,8 @@ 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. +- Improved the performance of ``auto`` deduction by adding a fast path + for simple cases, including ``auto``, ``const auto``, and ``auto*``. (#GH186274) - Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 69dc5656f855f..27482c6f5d4a6 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5285,7 +5285,8 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, DeclaredTy->isPointerType() && Context.hasSameType( DeclaredTy->getPointeeType().getLocalUnqualifiedType(), - Context.getAutoDeductType()); + Context.getAutoDeductType()) && + !DeclaredTy->getPointeeType().getQualifiers().hasCVRQualifiers(); QualType ProcessedInitTy = Init->getType().getNonReferenceType(); if (ProcessedInitTy->isArrayType() || ProcessedInitTy->isFunctionType()) @@ -5302,11 +5303,11 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, #endif } else if (CanUsePointerFastPath) { DeducedType = ProcessedInitTy->getPointeeType(); - Qualifiers ProcessedInitQuals = DeducedType.getLocalQualifiers(); - Qualifiers TypeQuals = DeclaredTy->getPointeeType().getLocalQualifiers(); - ProcessedInitQuals.removeCVRQualifiers(TypeQuals.getCVRQualifiers()); - DeducedType = Context.getQualifiedType( - DeducedType.getLocalUnqualifiedType(), ProcessedInitQuals); + // Normalize qualifiers on array pointee types so the fast path builds the + // same deduced type shape as the slow path. + Qualifiers DeducedQuals; + DeducedType = Context.getUnqualifiedArrayType(DeducedType, DeducedQuals); + DeducedType = Context.getQualifiedType(DeducedType, DeducedQuals); #ifndef NDEBUG FastPathUsed = true; #endif @@ -5384,7 +5385,6 @@ 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) @@ -5394,8 +5394,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, #ifndef NDEBUG if (FastPathUsed) { // Ignore differences due only to QualType sugar. - if (FastPathDeducedType.getCanonicalType() != - DeducedType.getCanonicalType()) { + if (FastPathDeducedType != DeducedType) { llvm::errs() << "Deducing: "; Type.dump(); Init->dump(); @@ -5406,8 +5405,7 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *Init, QualType &Result, DeducedType.dump(); } - assert(FastPathDeducedType.getCanonicalType() == - DeducedType.getCanonicalType() && + assert(FastPathDeducedType == DeducedType && "fast path auto deduction produced a different deduced type than " "the template-deduction path"); } @@ -5428,7 +5426,6 @@ 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 59c6d3103a7a8..5c1e7b8027361 100644 --- a/clang/test/SemaCXX/special-case-auto-deduction.cpp +++ b/clang/test/SemaCXX/special-case-auto-deduction.cpp @@ -263,3 +263,22 @@ static_assert(__is_same(decltype(pc), int * const)); const auto *const cpc = p; static_assert(__is_same(decltype(cpc), const int * const)); + +// cvr test +const int *qp1 = nullptr; +auto *__restrict qx1 = qp1; +static_assert(__is_same(decltype(qx1), const int *__restrict)); + +volatile int *qp2 = nullptr; +auto *const qx2 = qp2; +static_assert(__is_same(decltype(qx2), volatile int *const)); + +volatile int *__restrict qp3 = nullptr; +const auto *const qx3 = qp3; +static_assert(__is_same(decltype(qx3), const volatile int *const)); + +// type alias + +using PtrAlias = const int *; +PtrAlias qp4 = nullptr; +auto *const qx4 = &qp4; \ No newline at end of file _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
