Skip to content

[stable/21.x] Cherry-pick changes from previous stable branch #11027

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b138afd
Target: Add target option for disabling `AArch64_ELFTargetObjectFile:…
AnthonyLatsis Jun 6, 2025
6c1b359
Revert "[clang] Remove unused CodeGen:: ptrauth helpers. NFCI."
AnthonyLatsis Jun 12, 2025
3ed40e9
Revert "[Hashing] get_execution_seed: use a non-vague linkage symbol"
bnbarham Aug 1, 2024
a1e7036
Revert "[Hashing] Use a non-deterministic seed if LLVM_ENABLE_ABI_BRE…
bnbarham Aug 1, 2024
06f1ce4
Revert "[Parse] Always end translation unit"
bnbarham Oct 4, 2023
5d5f0bd
Revert "[lldb] Fix the way we set up the lldb modules infrastructure."
bnbarham Oct 4, 2023
4013f32
Revert "[libc++] Remove dead implementation of is_nothrow_convertible…
AnthonyLatsis Jun 27, 2025
77f8d27
Revert "[libc++] Remove a few unused includes from <__bit/*> (#137934)"
AnthonyLatsis Jun 27, 2025
92a6898
Revert "[libc++] Replace __libcpp_{ctz, clz} with __builtin_{ctzg, cl…
AnthonyLatsis Jun 27, 2025
2c220d8
Revert "[libc++] Replace __libcpp_popcount by __builtin_popcountg (#1…
AnthonyLatsis Jun 27, 2025
1e1c701
Revert "[clang] Remove dead incremental Parser code (#102450)"
AnthonyLatsis Apr 25, 2025
58307d9
Revert "[Clang][Sema] Allow qualified type names in `swift_name` attr…
MaxDesiatov Jul 9, 2025
6a9347f
[JITLink] Truncate ELF symbol sizes to fit containing block.
lhames Aug 28, 2023
38e0c5e
[jitlink] Fix cherry-pick in 6267697a4c
benlangmuir Aug 22, 2024
ae0c681
[JITLink] Use the union of ELF section permissions rdar://114207428.
lhames Aug 29, 2023
b64505a
[Frontend] Destroy compiling compiler instance before read
bnbarham Jul 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions clang/include/clang/CodeGen/CodeGenABITypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T);
unsigned getLLVMFieldNumber(CodeGenModule &CGM,
const RecordDecl *RD, const FieldDecl *FD);

/// Return a declaration discriminator for the given global decl.
uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD);

/// Return a type discriminator for the given function type.
uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM,
QualType FunctionType);

/// Return a signed constant pointer.
llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM,
llvm::Constant *pointer,
unsigned key,
llvm::Constant *storageAddress,
llvm::ConstantInt *otherDiscriminator);

/// Given the language and code-generation options that Clang was configured
/// with, set the default LLVM IR attributes for a function definition.
/// The attributes set here are mostly global target-configuration and
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CodeGen/CGPointerAuth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator(
llvm_unreachable("bad discrimination kind");
}

uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM,
QualType FunctionType) {
return CGM.getContext().getPointerAuthTypeDiscriminator(FunctionType);
}

uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM,
GlobalDecl Declaration) {
return CGM.getPointerAuthDeclDiscriminator(Declaration);
}

/// Return the "other" decl-specific discriminator for the given decl.
uint16_t
CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) {
Expand Down Expand Up @@ -588,6 +598,15 @@ llvm::Constant *CodeGenModule::getConstantSignedPointer(
OtherDiscriminator);
}

llvm::Constant *
CodeGen::getConstantSignedPointer(CodeGenModule &CGM,
llvm::Constant *pointer, unsigned key,
llvm::Constant *storageAddress,
llvm::ConstantInt *otherDiscriminator) {
return CGM.getConstantSignedPointer(pointer, key, storageAddress,
otherDiscriminator);
}

void CodeGenModule::destroyConstantSignedPointerCaches() {
destroyCache<ByConstantCacheTy>(ConstantSignedPointersByConstant);
destroyCache<ByDeclCacheTy>(ConstantSignedPointersByDecl);
Expand Down
22 changes: 12 additions & 10 deletions clang/lib/Frontend/CompilerInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1655,16 +1655,18 @@ static bool compileModuleAndReadASTImpl(CompilerInstance &ImportingInstance,
SourceLocation ModuleNameLoc,
Module *Module,
StringRef ModuleFileName) {
auto Instance = ImportingInstance.cloneForModuleCompile(ModuleNameLoc, Module,
ModuleFileName);

if (!ImportingInstance.compileModule(ModuleNameLoc,
Module->getTopLevelModuleName(),
ModuleFileName, *Instance)) {
ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
diag::err_module_not_built)
<< Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
return false;
{
auto Instance = ImportingInstance.cloneForModuleCompile(ModuleNameLoc, Module,
ModuleFileName);

if (!ImportingInstance.compileModule(ModuleNameLoc,
Module->getTopLevelModuleName(),
ModuleFileName, *Instance)) {
ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
diag::err_module_not_built)
<< Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
return false;
}
}

return readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc,
Expand Down
8 changes: 7 additions & 1 deletion clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,11 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
Sema::ModuleImportState &ImportState) {
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);

// Skip over the EOF token, flagging end of previous input for incremental
// processing
if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::eof))
ConsumeToken();

Result = nullptr;
switch (Tok.getKind()) {
case tok::annot_pragma_unused:
Expand Down Expand Up @@ -722,7 +727,8 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,

// Late template parsing can begin.
Actions.SetLateTemplateParser(LateTemplateParserCallback, nullptr, this);
Actions.ActOnEndOfTranslationUnit();
if (!PP.isIncrementalProcessingEnabled())
Actions.ActOnEndOfTranslationUnit();
//else don't tell Sema that we ended parsing: more input might come.
return true;

Expand Down
17 changes: 4 additions & 13 deletions clang/lib/Sema/SemaSwift.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,6 @@ static bool isValidSwiftErrorResultType(QualType Ty) {
return isValidSwiftContextType(Ty);
}

static bool isValidSwiftContextName(StringRef ContextName) {
// ContextName might be qualified, e.g. 'MyNamespace.MyStruct'.
SmallVector<StringRef, 1> ContextNameComponents;
ContextName.split(ContextNameComponents, '.');
return all_of(ContextNameComponents, [&](StringRef Component) {
return isValidAsciiIdentifier(Component);
});
}

void SemaSwift::handleAttrAttr(Decl *D, const ParsedAttr &AL) {
if (AL.isInvalid() || AL.isUsedAsTypeAttr())
return;
Expand Down Expand Up @@ -365,11 +356,11 @@ static bool validateSwiftFunctionName(Sema &S, const ParsedAttr &AL,

// Split at the first '.', if it exists, which separates the context name
// from the base name.
std::tie(ContextName, BaseName) = BaseName.rsplit('.');
std::tie(ContextName, BaseName) = BaseName.split('.');
if (BaseName.empty()) {
BaseName = ContextName;
ContextName = StringRef();
} else if (ContextName.empty() || !isValidSwiftContextName(ContextName)) {
} else if (ContextName.empty() || !isValidAsciiIdentifier(ContextName)) {
S.Diag(Loc, diag::warn_attr_swift_name_invalid_identifier)
<< AL << /*context*/ 1;
return false;
Expand Down Expand Up @@ -593,11 +584,11 @@ bool SemaSwift::DiagnoseName(Decl *D, StringRef Name, SourceLocation Loc,
!IsAsync) {
StringRef ContextName, BaseName;

std::tie(ContextName, BaseName) = Name.rsplit('.');
std::tie(ContextName, BaseName) = Name.split('.');
if (BaseName.empty()) {
BaseName = ContextName;
ContextName = StringRef();
} else if (!isValidSwiftContextName(ContextName)) {
} else if (!isValidAsciiIdentifier(ContextName)) {
Diag(Loc, diag::warn_attr_swift_name_invalid_identifier)
<< AL << /*context*/ 1;
return false;
Expand Down
36 changes: 0 additions & 36 deletions clang/test/SemaObjCXX/attr-swift_name-cxx.mm
Original file line number Diff line number Diff line change
@@ -1,43 +1,7 @@
// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s

#define SWIFT_NAME(name) __attribute__((swift_name(name)))
#define SWIFT_ASYNC_NAME(name) __attribute__((__swift_async_name__(name)))

namespace MyNS {
struct NestedStruct {};
}

void nestedStruct_method(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct.method(self:)");
void nestedStruct_methodConstRef(const MyNS::NestedStruct&) SWIFT_NAME("MyNS.NestedStruct.methodConstRef(self:)");
void nestedStruct_invalidContext1(MyNS::NestedStruct) SWIFT_NAME(".MyNS.NestedStruct.invalidContext1(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
void nestedStruct_invalidContext2(MyNS::NestedStruct) SWIFT_NAME("MyNS::NestedStruct.invalidContext2(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
void nestedStruct_invalidContext3(MyNS::NestedStruct) SWIFT_NAME("::MyNS::NestedStruct.invalidContext3(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
void nestedStruct_invalidContext4(MyNS::NestedStruct) SWIFT_NAME("MyNS..NestedStruct.invalidContext4(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
void nestedStruct_invalidContext5(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct.invalidContext5.(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the base name}}
void nestedStruct_invalidContext6(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct::invalidContext6(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the base name}}

namespace MyNS {
namespace MyDeepNS {
struct DeepNestedStruct {};
}
}

void deepNestedStruct_method(MyNS::MyDeepNS::DeepNestedStruct) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.method(self:)");
void deepNestedStruct_methodConstRef(const MyNS::MyDeepNS::DeepNestedStruct&) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.methodConstRef(self:)");
void deepNestedStruct_invalidContext(const MyNS::MyDeepNS::DeepNestedStruct&) SWIFT_NAME("MyNS::MyDeepNS::DeepNestedStruct.methodConstRef(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}

typedef MyNS::MyDeepNS::DeepNestedStruct DeepNestedStructTypedef;

void deepNestedStructTypedef_method(DeepNestedStructTypedef) SWIFT_NAME("DeepNestedStructTypedef.method(self:)");
void deepNestedStructTypedef_methodQualName(MyNS::MyDeepNS::DeepNestedStruct) SWIFT_NAME("DeepNestedStructTypedef.method(self:)");

struct TopLevelStruct {
struct StructInStruct {};
};

void structInStruct_method(TopLevelStruct::StructInStruct) SWIFT_NAME("TopLevelStruct.StructInStruct.method(self:)");
void structInStruct_invalidContext(TopLevelStruct::StructInStruct) SWIFT_NAME("TopLevelStruct::StructInStruct.method(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}

typedef int (^CallbackTy)(void);

class CXXClass {
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,7 @@ set(files
__type_traits/is_member_pointer.h
__type_traits/is_nothrow_assignable.h
__type_traits/is_nothrow_constructible.h
__type_traits/is_nothrow_convertible.h
__type_traits/is_nothrow_destructible.h
__type_traits/is_null_pointer.h
__type_traits/is_object.h
Expand Down
12 changes: 6 additions & 6 deletions libcxx/include/__algorithm/sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,10 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos(
// Swap one pair on each iteration as long as both bitsets have at least one
// element for swapping.
while (__left_bitset != 0 && __right_bitset != 0) {
difference_type __tz_left = std::__countr_zero(__left_bitset);
__left_bitset = std::__libcpp_blsr(__left_bitset);
difference_type __tz_right = std::__countr_zero(__right_bitset);
__right_bitset = std::__libcpp_blsr(__right_bitset);
difference_type __tz_left = __libcpp_ctz(__left_bitset);
__left_bitset = __libcpp_blsr(__left_bitset);
difference_type __tz_right = __libcpp_ctz(__right_bitset);
__right_bitset = __libcpp_blsr(__right_bitset);
_Ops::iter_swap(__first + __tz_left, __last - __tz_right);
}
}
Expand Down Expand Up @@ -458,7 +458,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
// Swap within the left side. Need to find set positions in the reverse
// order.
while (__left_bitset != 0) {
difference_type __tz_left = __detail::__block_size - 1 - std::__countl_zero(__left_bitset);
difference_type __tz_left = __detail::__block_size - 1 - __libcpp_clz(__left_bitset);
__left_bitset &= (static_cast<uint64_t>(1) << __tz_left) - 1;
_RandomAccessIterator __it = __first + __tz_left;
if (__it != __lm1) {
Expand All @@ -471,7 +471,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
// Swap within the right side. Need to find set positions in the reverse
// order.
while (__right_bitset != 0) {
difference_type __tz_right = __detail::__block_size - 1 - std::__countl_zero(__right_bitset);
difference_type __tz_right = __detail::__block_size - 1 - __libcpp_clz(__right_bitset);
__right_bitset &= (static_cast<uint64_t>(1) << __tz_right) - 1;
_RandomAccessIterator __it = __lm1 - __tz_right;
if (__it != __first) {
Expand Down
63 changes: 63 additions & 0 deletions libcxx/include/__bit/countl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@
//
//===----------------------------------------------------------------------===//

// TODO: __builtin_clzg is available since Clang 19 and GCC 14. When support for older versions is dropped, we can
// refactor this code to exclusively use __builtin_clzg.

#ifndef _LIBCPP___BIT_COUNTL_H
#define _LIBCPP___BIT_COUNTL_H

#include <__bit/rotate.h>
#include <__config>
#include <__type_traits/integer_traits.h>
#include <limits>
Expand All @@ -22,10 +26,69 @@ _LIBCPP_PUSH_MACROS

_LIBCPP_BEGIN_NAMESPACE_STD

[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned __x) _NOEXCEPT {
return __builtin_clz(__x);
}

[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned long __x) _NOEXCEPT {
return __builtin_clzl(__x);
}

[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned long long __x) _NOEXCEPT {
return __builtin_clzll(__x);
}

#if _LIBCPP_HAS_INT128
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(__uint128_t __x) _NOEXCEPT {
# if __has_builtin(__builtin_clzg)
return __builtin_clzg(__x);
# else
// The function is written in this form due to C++ constexpr limitations.
// The algorithm:
// - Test whether any bit in the high 64-bits is set
// - No bits set:
// - The high 64-bits contain 64 leading zeros,
// - Add the result of the low 64-bits.
// - Any bits set:
// - The number of leading zeros of the input is the number of leading
// zeros in the high 64-bits.
return ((__x >> 64) == 0) ? (64 + __builtin_clzll(static_cast<unsigned long long>(__x)))
: __builtin_clzll(static_cast<unsigned long long>(__x >> 64));
# endif
}
#endif // _LIBCPP_HAS_INT128

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countl_zero(_Tp __t) _NOEXCEPT {
static_assert(__is_unsigned_integer_v<_Tp>, "__countl_zero requires an unsigned integer type");
#if __has_builtin(__builtin_clzg)
return __builtin_clzg(__t, numeric_limits<_Tp>::digits);
#else // __has_builtin(__builtin_clzg)
if (__t == 0)
return numeric_limits<_Tp>::digits;

if (sizeof(_Tp) <= sizeof(unsigned int))
return std::__libcpp_clz(static_cast<unsigned int>(__t)) -
(numeric_limits<unsigned int>::digits - numeric_limits<_Tp>::digits);
else if (sizeof(_Tp) <= sizeof(unsigned long))
return std::__libcpp_clz(static_cast<unsigned long>(__t)) -
(numeric_limits<unsigned long>::digits - numeric_limits<_Tp>::digits);
else if (sizeof(_Tp) <= sizeof(unsigned long long))
return std::__libcpp_clz(static_cast<unsigned long long>(__t)) -
(numeric_limits<unsigned long long>::digits - numeric_limits<_Tp>::digits);
else {
int __ret = 0;
int __iter = 0;
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
while (true) {
__t = std::__rotl(__t, __ulldigits);
if ((__iter = std::__countl_zero(static_cast<unsigned long long>(__t))) != __ulldigits)
break;
__ret += __iter;
}
return __ret + __iter;
}
#endif // __has_builtin(__builtin_clzg)
}

#if _LIBCPP_STD_VER >= 20
Expand Down
52 changes: 51 additions & 1 deletion libcxx/include/__bit/countr.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@
//
//===----------------------------------------------------------------------===//

// TODO: __builtin_ctzg is available since Clang 19 and GCC 14. When support for older versions is dropped, we can
// refactor this code to exclusively use __builtin_ctzg.

#ifndef _LIBCPP___BIT_COUNTR_H
#define _LIBCPP___BIT_COUNTR_H

#include <__bit/rotate.h>
#include <__config>
#include <__type_traits/integer_traits.h>
#include <__type_traits/is_unsigned.h>
#include <limits>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
Expand All @@ -22,10 +27,55 @@ _LIBCPP_PUSH_MACROS

_LIBCPP_BEGIN_NAMESPACE_STD

[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned __x) _NOEXCEPT {
return __builtin_ctz(__x);
}

[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned long __x) _NOEXCEPT {
return __builtin_ctzl(__x);
}

[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned long long __x) _NOEXCEPT {
return __builtin_ctzll(__x);
}

// A constexpr implementation for C++11 and later (using clang extensions for constexpr support)
// Precondition: __t != 0 (the caller __countr_zero handles __t == 0 as a special case)
template <class _Tp>
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__t != 0, "__countr_zero_impl called with zero value");
static_assert(is_unsigned<_Tp>::value, "__countr_zero_impl only works with unsigned types");
if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned int)) {
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
} else if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned long)) {
return std::__libcpp_ctz(static_cast<unsigned long>(__t));
} else if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned long long)) {
return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
} else {
#if _LIBCPP_STD_VER == 11
unsigned long long __ull = static_cast<unsigned long long>(__t);
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz(__ull);
#else
int __ret = 0;
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
while (static_cast<unsigned long long>(__t) == 0uLL) {
__ret += __ulldigits;
__t >>= __ulldigits;
}
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
#endif
}
}

template <class _Tp>
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
static_assert(__is_unsigned_integer_v<_Tp>, "__countr_zero only works with unsigned types");
static_assert(is_unsigned<_Tp>::value, "__countr_zero only works with unsigned types");
#if __has_builtin(__builtin_ctzg) // TODO (LLVM 21): This can be dropped once we only support Clang >= 19.
return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
#else
return __t != 0 ? std::__countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
#endif
}

#if _LIBCPP_STD_VER >= 20
Expand Down
Loading