Skip to content

Commit 9c5b4db

Browse files
Merge pull request #11027 from swiftlang/jepa-stable
[stable/21.x] Cherry-pick changes from previous stable branch
2 parents e0f7c8b + b64505a commit 9c5b4db

File tree

32 files changed

+481
-125
lines changed

32 files changed

+481
-125
lines changed

clang/include/clang/CodeGen/CodeGenABITypes.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,20 @@ llvm::Type *convertTypeForMemory(CodeGenModule &CGM, QualType T);
120120
unsigned getLLVMFieldNumber(CodeGenModule &CGM,
121121
const RecordDecl *RD, const FieldDecl *FD);
122122

123+
/// Return a declaration discriminator for the given global decl.
124+
uint16_t getPointerAuthDeclDiscriminator(CodeGenModule &CGM, GlobalDecl GD);
125+
126+
/// Return a type discriminator for the given function type.
127+
uint16_t getPointerAuthTypeDiscriminator(CodeGenModule &CGM,
128+
QualType FunctionType);
129+
130+
/// Return a signed constant pointer.
131+
llvm::Constant *getConstantSignedPointer(CodeGenModule &CGM,
132+
llvm::Constant *pointer,
133+
unsigned key,
134+
llvm::Constant *storageAddress,
135+
llvm::ConstantInt *otherDiscriminator);
136+
123137
/// Given the language and code-generation options that Clang was configured
124138
/// with, set the default LLVM IR attributes for a function definition.
125139
/// The attributes set here are mostly global target-configuration and

clang/lib/CodeGen/CGPointerAuth.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator(
5353
llvm_unreachable("bad discrimination kind");
5454
}
5555

56+
uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM,
57+
QualType FunctionType) {
58+
return CGM.getContext().getPointerAuthTypeDiscriminator(FunctionType);
59+
}
60+
61+
uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM,
62+
GlobalDecl Declaration) {
63+
return CGM.getPointerAuthDeclDiscriminator(Declaration);
64+
}
65+
5666
/// Return the "other" decl-specific discriminator for the given decl.
5767
uint16_t
5868
CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) {
@@ -588,6 +598,15 @@ llvm::Constant *CodeGenModule::getConstantSignedPointer(
588598
OtherDiscriminator);
589599
}
590600

601+
llvm::Constant *
602+
CodeGen::getConstantSignedPointer(CodeGenModule &CGM,
603+
llvm::Constant *pointer, unsigned key,
604+
llvm::Constant *storageAddress,
605+
llvm::ConstantInt *otherDiscriminator) {
606+
return CGM.getConstantSignedPointer(pointer, key, storageAddress,
607+
otherDiscriminator);
608+
}
609+
591610
void CodeGenModule::destroyConstantSignedPointerCaches() {
592611
destroyCache<ByConstantCacheTy>(ConstantSignedPointersByConstant);
593612
destroyCache<ByDeclCacheTy>(ConstantSignedPointersByDecl);

clang/lib/Frontend/CompilerInstance.cpp

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,16 +1655,18 @@ static bool compileModuleAndReadASTImpl(CompilerInstance &ImportingInstance,
16551655
SourceLocation ModuleNameLoc,
16561656
Module *Module,
16571657
StringRef ModuleFileName) {
1658-
auto Instance = ImportingInstance.cloneForModuleCompile(ModuleNameLoc, Module,
1659-
ModuleFileName);
1660-
1661-
if (!ImportingInstance.compileModule(ModuleNameLoc,
1662-
Module->getTopLevelModuleName(),
1663-
ModuleFileName, *Instance)) {
1664-
ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
1665-
diag::err_module_not_built)
1666-
<< Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
1667-
return false;
1658+
{
1659+
auto Instance = ImportingInstance.cloneForModuleCompile(ModuleNameLoc, Module,
1660+
ModuleFileName);
1661+
1662+
if (!ImportingInstance.compileModule(ModuleNameLoc,
1663+
Module->getTopLevelModuleName(),
1664+
ModuleFileName, *Instance)) {
1665+
ImportingInstance.getDiagnostics().Report(ModuleNameLoc,
1666+
diag::err_module_not_built)
1667+
<< Module->Name << SourceRange(ImportLoc, ModuleNameLoc);
1668+
return false;
1669+
}
16681670
}
16691671

16701672
return readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc,

clang/lib/Parse/Parser.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,11 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
629629
Sema::ModuleImportState &ImportState) {
630630
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this);
631631

632+
// Skip over the EOF token, flagging end of previous input for incremental
633+
// processing
634+
if (PP.isIncrementalProcessingEnabled() && Tok.is(tok::eof))
635+
ConsumeToken();
636+
632637
Result = nullptr;
633638
switch (Tok.getKind()) {
634639
case tok::annot_pragma_unused:
@@ -722,7 +727,8 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result,
722727

723728
// Late template parsing can begin.
724729
Actions.SetLateTemplateParser(LateTemplateParserCallback, nullptr, this);
725-
Actions.ActOnEndOfTranslationUnit();
730+
if (!PP.isIncrementalProcessingEnabled())
731+
Actions.ActOnEndOfTranslationUnit();
726732
//else don't tell Sema that we ended parsing: more input might come.
727733
return true;
728734

clang/lib/Sema/SemaSwift.cpp

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,6 @@ static bool isValidSwiftErrorResultType(QualType Ty) {
7272
return isValidSwiftContextType(Ty);
7373
}
7474

75-
static bool isValidSwiftContextName(StringRef ContextName) {
76-
// ContextName might be qualified, e.g. 'MyNamespace.MyStruct'.
77-
SmallVector<StringRef, 1> ContextNameComponents;
78-
ContextName.split(ContextNameComponents, '.');
79-
return all_of(ContextNameComponents, [&](StringRef Component) {
80-
return isValidAsciiIdentifier(Component);
81-
});
82-
}
83-
8475
void SemaSwift::handleAttrAttr(Decl *D, const ParsedAttr &AL) {
8576
if (AL.isInvalid() || AL.isUsedAsTypeAttr())
8677
return;
@@ -365,11 +356,11 @@ static bool validateSwiftFunctionName(Sema &S, const ParsedAttr &AL,
365356

366357
// Split at the first '.', if it exists, which separates the context name
367358
// from the base name.
368-
std::tie(ContextName, BaseName) = BaseName.rsplit('.');
359+
std::tie(ContextName, BaseName) = BaseName.split('.');
369360
if (BaseName.empty()) {
370361
BaseName = ContextName;
371362
ContextName = StringRef();
372-
} else if (ContextName.empty() || !isValidSwiftContextName(ContextName)) {
363+
} else if (ContextName.empty() || !isValidAsciiIdentifier(ContextName)) {
373364
S.Diag(Loc, diag::warn_attr_swift_name_invalid_identifier)
374365
<< AL << /*context*/ 1;
375366
return false;
@@ -593,11 +584,11 @@ bool SemaSwift::DiagnoseName(Decl *D, StringRef Name, SourceLocation Loc,
593584
!IsAsync) {
594585
StringRef ContextName, BaseName;
595586

596-
std::tie(ContextName, BaseName) = Name.rsplit('.');
587+
std::tie(ContextName, BaseName) = Name.split('.');
597588
if (BaseName.empty()) {
598589
BaseName = ContextName;
599590
ContextName = StringRef();
600-
} else if (!isValidSwiftContextName(ContextName)) {
591+
} else if (!isValidAsciiIdentifier(ContextName)) {
601592
Diag(Loc, diag::warn_attr_swift_name_invalid_identifier)
602593
<< AL << /*context*/ 1;
603594
return false;

clang/test/SemaObjCXX/attr-swift_name-cxx.mm

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,7 @@
11
// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s
22

3-
#define SWIFT_NAME(name) __attribute__((swift_name(name)))
43
#define SWIFT_ASYNC_NAME(name) __attribute__((__swift_async_name__(name)))
54

6-
namespace MyNS {
7-
struct NestedStruct {};
8-
}
9-
10-
void nestedStruct_method(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct.method(self:)");
11-
void nestedStruct_methodConstRef(const MyNS::NestedStruct&) SWIFT_NAME("MyNS.NestedStruct.methodConstRef(self:)");
12-
void nestedStruct_invalidContext1(MyNS::NestedStruct) SWIFT_NAME(".MyNS.NestedStruct.invalidContext1(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
13-
void nestedStruct_invalidContext2(MyNS::NestedStruct) SWIFT_NAME("MyNS::NestedStruct.invalidContext2(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
14-
void nestedStruct_invalidContext3(MyNS::NestedStruct) SWIFT_NAME("::MyNS::NestedStruct.invalidContext3(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
15-
void nestedStruct_invalidContext4(MyNS::NestedStruct) SWIFT_NAME("MyNS..NestedStruct.invalidContext4(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
16-
void nestedStruct_invalidContext5(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct.invalidContext5.(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the base name}}
17-
void nestedStruct_invalidContext6(MyNS::NestedStruct) SWIFT_NAME("MyNS.NestedStruct::invalidContext6(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the base name}}
18-
19-
namespace MyNS {
20-
namespace MyDeepNS {
21-
struct DeepNestedStruct {};
22-
}
23-
}
24-
25-
void deepNestedStruct_method(MyNS::MyDeepNS::DeepNestedStruct) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.method(self:)");
26-
void deepNestedStruct_methodConstRef(const MyNS::MyDeepNS::DeepNestedStruct&) SWIFT_NAME("MyNS.MyDeepNS.DeepNestedStruct.methodConstRef(self:)");
27-
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}}
28-
29-
typedef MyNS::MyDeepNS::DeepNestedStruct DeepNestedStructTypedef;
30-
31-
void deepNestedStructTypedef_method(DeepNestedStructTypedef) SWIFT_NAME("DeepNestedStructTypedef.method(self:)");
32-
void deepNestedStructTypedef_methodQualName(MyNS::MyDeepNS::DeepNestedStruct) SWIFT_NAME("DeepNestedStructTypedef.method(self:)");
33-
34-
struct TopLevelStruct {
35-
struct StructInStruct {};
36-
};
37-
38-
void structInStruct_method(TopLevelStruct::StructInStruct) SWIFT_NAME("TopLevelStruct.StructInStruct.method(self:)");
39-
void structInStruct_invalidContext(TopLevelStruct::StructInStruct) SWIFT_NAME("TopLevelStruct::StructInStruct.method(self:)"); // expected-warning {{'swift_name' attribute has invalid identifier for the context name}}
40-
415
typedef int (^CallbackTy)(void);
426

437
class CXXClass {

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@ set(files
847847
__type_traits/is_member_pointer.h
848848
__type_traits/is_nothrow_assignable.h
849849
__type_traits/is_nothrow_constructible.h
850+
__type_traits/is_nothrow_convertible.h
850851
__type_traits/is_nothrow_destructible.h
851852
__type_traits/is_null_pointer.h
852853
__type_traits/is_object.h

libcxx/include/__algorithm/sort.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,10 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos(
359359
// Swap one pair on each iteration as long as both bitsets have at least one
360360
// element for swapping.
361361
while (__left_bitset != 0 && __right_bitset != 0) {
362-
difference_type __tz_left = std::__countr_zero(__left_bitset);
363-
__left_bitset = std::__libcpp_blsr(__left_bitset);
364-
difference_type __tz_right = std::__countr_zero(__right_bitset);
365-
__right_bitset = std::__libcpp_blsr(__right_bitset);
362+
difference_type __tz_left = __libcpp_ctz(__left_bitset);
363+
__left_bitset = __libcpp_blsr(__left_bitset);
364+
difference_type __tz_right = __libcpp_ctz(__right_bitset);
365+
__right_bitset = __libcpp_blsr(__right_bitset);
366366
_Ops::iter_swap(__first + __tz_left, __last - __tz_right);
367367
}
368368
}
@@ -458,7 +458,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
458458
// Swap within the left side. Need to find set positions in the reverse
459459
// order.
460460
while (__left_bitset != 0) {
461-
difference_type __tz_left = __detail::__block_size - 1 - std::__countl_zero(__left_bitset);
461+
difference_type __tz_left = __detail::__block_size - 1 - __libcpp_clz(__left_bitset);
462462
__left_bitset &= (static_cast<uint64_t>(1) << __tz_left) - 1;
463463
_RandomAccessIterator __it = __first + __tz_left;
464464
if (__it != __lm1) {
@@ -471,7 +471,7 @@ inline _LIBCPP_HIDE_FROM_ABI void __swap_bitmap_pos_within(
471471
// Swap within the right side. Need to find set positions in the reverse
472472
// order.
473473
while (__right_bitset != 0) {
474-
difference_type __tz_right = __detail::__block_size - 1 - std::__countl_zero(__right_bitset);
474+
difference_type __tz_right = __detail::__block_size - 1 - __libcpp_clz(__right_bitset);
475475
__right_bitset &= (static_cast<uint64_t>(1) << __tz_right) - 1;
476476
_RandomAccessIterator __it = __lm1 - __tz_right;
477477
if (__it != __first) {

libcxx/include/__bit/countl.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
// TODO: __builtin_clzg is available since Clang 19 and GCC 14. When support for older versions is dropped, we can
10+
// refactor this code to exclusively use __builtin_clzg.
11+
912
#ifndef _LIBCPP___BIT_COUNTL_H
1013
#define _LIBCPP___BIT_COUNTL_H
1114

15+
#include <__bit/rotate.h>
1216
#include <__config>
1317
#include <__type_traits/integer_traits.h>
1418
#include <limits>
@@ -22,10 +26,69 @@ _LIBCPP_PUSH_MACROS
2226

2327
_LIBCPP_BEGIN_NAMESPACE_STD
2428

29+
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned __x) _NOEXCEPT {
30+
return __builtin_clz(__x);
31+
}
32+
33+
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned long __x) _NOEXCEPT {
34+
return __builtin_clzl(__x);
35+
}
36+
37+
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(unsigned long long __x) _NOEXCEPT {
38+
return __builtin_clzll(__x);
39+
}
40+
41+
#if _LIBCPP_HAS_INT128
42+
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_clz(__uint128_t __x) _NOEXCEPT {
43+
# if __has_builtin(__builtin_clzg)
44+
return __builtin_clzg(__x);
45+
# else
46+
// The function is written in this form due to C++ constexpr limitations.
47+
// The algorithm:
48+
// - Test whether any bit in the high 64-bits is set
49+
// - No bits set:
50+
// - The high 64-bits contain 64 leading zeros,
51+
// - Add the result of the low 64-bits.
52+
// - Any bits set:
53+
// - The number of leading zeros of the input is the number of leading
54+
// zeros in the high 64-bits.
55+
return ((__x >> 64) == 0) ? (64 + __builtin_clzll(static_cast<unsigned long long>(__x)))
56+
: __builtin_clzll(static_cast<unsigned long long>(__x >> 64));
57+
# endif
58+
}
59+
#endif // _LIBCPP_HAS_INT128
60+
2561
template <class _Tp>
2662
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countl_zero(_Tp __t) _NOEXCEPT {
2763
static_assert(__is_unsigned_integer_v<_Tp>, "__countl_zero requires an unsigned integer type");
64+
#if __has_builtin(__builtin_clzg)
2865
return __builtin_clzg(__t, numeric_limits<_Tp>::digits);
66+
#else // __has_builtin(__builtin_clzg)
67+
if (__t == 0)
68+
return numeric_limits<_Tp>::digits;
69+
70+
if (sizeof(_Tp) <= sizeof(unsigned int))
71+
return std::__libcpp_clz(static_cast<unsigned int>(__t)) -
72+
(numeric_limits<unsigned int>::digits - numeric_limits<_Tp>::digits);
73+
else if (sizeof(_Tp) <= sizeof(unsigned long))
74+
return std::__libcpp_clz(static_cast<unsigned long>(__t)) -
75+
(numeric_limits<unsigned long>::digits - numeric_limits<_Tp>::digits);
76+
else if (sizeof(_Tp) <= sizeof(unsigned long long))
77+
return std::__libcpp_clz(static_cast<unsigned long long>(__t)) -
78+
(numeric_limits<unsigned long long>::digits - numeric_limits<_Tp>::digits);
79+
else {
80+
int __ret = 0;
81+
int __iter = 0;
82+
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
83+
while (true) {
84+
__t = std::__rotl(__t, __ulldigits);
85+
if ((__iter = std::__countl_zero(static_cast<unsigned long long>(__t))) != __ulldigits)
86+
break;
87+
__ret += __iter;
88+
}
89+
return __ret + __iter;
90+
}
91+
#endif // __has_builtin(__builtin_clzg)
2992
}
3093

3194
#if _LIBCPP_STD_VER >= 20

libcxx/include/__bit/countr.h

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
// TODO: __builtin_ctzg is available since Clang 19 and GCC 14. When support for older versions is dropped, we can
10+
// refactor this code to exclusively use __builtin_ctzg.
11+
912
#ifndef _LIBCPP___BIT_COUNTR_H
1013
#define _LIBCPP___BIT_COUNTR_H
1114

15+
#include <__bit/rotate.h>
1216
#include <__config>
1317
#include <__type_traits/integer_traits.h>
18+
#include <__type_traits/is_unsigned.h>
1419
#include <limits>
1520

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

2328
_LIBCPP_BEGIN_NAMESPACE_STD
2429

30+
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned __x) _NOEXCEPT {
31+
return __builtin_ctz(__x);
32+
}
33+
34+
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned long __x) _NOEXCEPT {
35+
return __builtin_ctzl(__x);
36+
}
37+
38+
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __libcpp_ctz(unsigned long long __x) _NOEXCEPT {
39+
return __builtin_ctzll(__x);
40+
}
41+
42+
// A constexpr implementation for C++11 and later (using clang extensions for constexpr support)
43+
// Precondition: __t != 0 (the caller __countr_zero handles __t == 0 as a special case)
44+
template <class _Tp>
45+
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
46+
_LIBCPP_ASSERT_INTERNAL(__t != 0, "__countr_zero_impl called with zero value");
47+
static_assert(is_unsigned<_Tp>::value, "__countr_zero_impl only works with unsigned types");
48+
if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned int)) {
49+
return std::__libcpp_ctz(static_cast<unsigned int>(__t));
50+
} else if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned long)) {
51+
return std::__libcpp_ctz(static_cast<unsigned long>(__t));
52+
} else if _LIBCPP_CONSTEXPR (sizeof(_Tp) <= sizeof(unsigned long long)) {
53+
return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
54+
} else {
55+
#if _LIBCPP_STD_VER == 11
56+
unsigned long long __ull = static_cast<unsigned long long>(__t);
57+
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
58+
return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz(__ull);
59+
#else
60+
int __ret = 0;
61+
const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
62+
while (static_cast<unsigned long long>(__t) == 0uLL) {
63+
__ret += __ulldigits;
64+
__t >>= __ulldigits;
65+
}
66+
return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
67+
#endif
68+
}
69+
}
70+
2571
template <class _Tp>
2672
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
27-
static_assert(__is_unsigned_integer_v<_Tp>, "__countr_zero only works with unsigned types");
73+
static_assert(is_unsigned<_Tp>::value, "__countr_zero only works with unsigned types");
74+
#if __has_builtin(__builtin_ctzg) // TODO (LLVM 21): This can be dropped once we only support Clang >= 19.
2875
return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
76+
#else
77+
return __t != 0 ? std::__countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
78+
#endif
2979
}
3080

3181
#if _LIBCPP_STD_VER >= 20

0 commit comments

Comments
 (0)