Skip to content

Commit 47e78c0

Browse files
authored
Merge pull request #11044 from apple/rationalize-bitshifting
[stdlib] Rationalize bitshifting in protocols
2 parents 2ba6cc9 + a86d9ae commit 47e78c0

File tree

3 files changed

+78
-98
lines changed

3 files changed

+78
-98
lines changed

stdlib/public/core/DoubleWidth.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public struct DoubleWidth<Base : FixedWidthInteger> :
8989
// At this point we know source's bitWidth > Base.bitWidth, or else
9090
// we would've taken the first branch.
9191
let lowInT = source & T(~0 as Low)
92-
let highInT = source &>> numericCast(High.bitWidth)
92+
let highInT = source >> High.bitWidth
9393

9494
let low = Low(lowInT.magnitude)
9595
guard let high = High(exactly: highInT) else { return nil }

stdlib/public/core/Integers.swift.gyb

Lines changed: 64 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,14 +1489,25 @@ ${assignmentOperatorComment(x.operator, False)}
14891489
/// - Complexity: O(1).
14901490
static prefix func ~ (_ x: Self) -> Self
14911491

1492-
% for x in binaryBitwise + maskingShifts:
1492+
% for x in binaryBitwise:
14931493
${operatorComment(x.operator, False)}
14941494
static func ${x.operator}(_ lhs: Self, _ rhs: Self) -> Self
14951495

14961496
${assignmentOperatorComment(x.operator, False)}
14971497
static func ${x.operator}=(_ lhs: inout Self, _ rhs: Self)
14981498
% end
14991499

1500+
% for x in maskingShifts:
1501+
${operatorComment(x.nonMaskingOperator, False)}
1502+
static func ${x.nonMaskingOperator}<RHS: BinaryInteger>(
1503+
_ lhs: Self, _ rhs: RHS
1504+
) -> Self
1505+
1506+
${assignmentOperatorComment(x.nonMaskingOperator, False)}
1507+
static func ${x.nonMaskingOperator}=<RHS: BinaryInteger>(
1508+
_ lhs: inout Self, _ rhs: RHS)
1509+
% end
1510+
15001511
/// Returns the quotient and remainder of this value divided by the given
15011512
/// value.
15021513
///
@@ -1549,8 +1560,9 @@ extension BinaryInteger {
15491560
return (self / rhs, self % rhs)
15501561
}
15511562

1552-
% for x in binaryBitwise + maskingShifts:
1563+
% for x in binaryBitwise:
15531564

1565+
// Homogeneous
15541566
${operatorComment(x.operator, False)}
15551567
@_transparent
15561568
public static func ${x.operator} (lhs: Self, rhs: Self) -> Self {
@@ -1561,24 +1573,18 @@ ${operatorComment(x.operator, False)}
15611573

15621574
% end
15631575

1564-
% for x in maskingShifts:
1565-
1566-
${operatorComment(x.operator, False)}
1567-
public static func ${x.operator} <
1568-
Other : BinaryInteger
1569-
>(lhs: Self, rhs: Other) -> Self {
1570-
return lhs ${x.operator} Self(extendingOrTruncating: rhs)
1571-
}
1572-
1573-
${assignmentOperatorComment(x.operator, False)}
1574-
@_transparent
1575-
public static func ${x.operator}= <
1576-
Other : BinaryInteger
1577-
>(lhs: inout Self, rhs: Other) {
1578-
lhs = lhs ${x.operator} rhs
1576+
% for x in maskingShifts:
1577+
// Heterogeneous non-masking shift in terms of shift-assignment
1578+
${operatorComment(x.nonMaskingOperator, False)}
1579+
public static func ${x.nonMaskingOperator}<RHS: BinaryInteger>(
1580+
_ lhs: Self, _ rhs: RHS
1581+
) -> Self {
1582+
var r = lhs
1583+
r ${x.nonMaskingOperator}= rhs
1584+
return r
15791585
}
1586+
% end
15801587

1581-
% end
15821588
}
15831589

15841590
// Strideable conformance
@@ -1835,50 +1841,6 @@ extension BinaryInteger {
18351841
}
18361842
}
18371843

1838-
//===----------------------------------------------------------------------===//
1839-
//===--- BinaryInteger smart shifts ---------------------------------------===//
1840-
//===----------------------------------------------------------------------===//
1841-
// FIXME(integers): uncomment once <rdar://problem/29643515> gets fixed
1842-
#if false
1843-
extension BinaryInteger {
1844-
% for x in maskingShifts:
1845-
@_transparent
1846-
public static func ${x.nonMaskingOperator} <
1847-
Other : BinaryInteger
1848-
>(lhs: Self, rhs: Other) -> Self {
1849-
var lhs = lhs
1850-
lhs ${x.nonMaskingOperator}= rhs
1851-
return lhs
1852-
}
1853-
1854-
// It is hard to imagine overshift to the left in an arbitrarily sized
1855-
// integer, but shifting too far to the right and negative shift cases are
1856-
// supported.
1857-
% reversedOperator = x.operator.translate(maketrans('<>', '><'))
1858-
% isRightShift = '>' in x.operator
1859-
@_transparent
1860-
public static func ${x.nonMaskingOperator}= <
1861-
Other : BinaryInteger
1862-
>(lhs: inout Self, rhs: Other) {
1863-
if rhs < (0 as Other) {
1864-
lhs ${reversedOperator}= (0 - rhs)
1865-
return
1866-
}
1867-
% if isRightShift:
1868-
let overshift = Self.isSigned
1869-
? (lhs < (0 as Self) ? ~(0 as Self) : 0 )
1870-
: 0
1871-
if rhs >= lhs.bitWidth {
1872-
lhs = overshift
1873-
return
1874-
}
1875-
% end
1876-
lhs ${x.operator}= Self(extendingOrTruncating: rhs)
1877-
}
1878-
% end
1879-
}
1880-
#endif
1881-
18821844
//===----------------------------------------------------------------------===//
18831845
//===--- FixedWidthInteger ------------------------------------------------===//
18841846
//===----------------------------------------------------------------------===//
@@ -2105,6 +2067,15 @@ ${overflowOperationComment(x.operator)}
21052067

21062068
/// A representation of this integer with the byte order swapped.
21072069
var byteSwapped: Self { get }
2070+
2071+
% for x in maskingShifts:
2072+
${operatorComment(x.operator, False)}
2073+
static func ${x.operator}(_ lhs: Self, _ rhs: Self) -> Self
2074+
2075+
${assignmentOperatorComment(x.operator, False)}
2076+
static func ${x.operator}=(_ lhs: inout Self, _ rhs: Self)
2077+
% end
2078+
21082079
}
21092080

21102081
extension FixedWidthInteger {
@@ -2162,6 +2133,37 @@ extension FixedWidthInteger {
21622133
return byteSwapped
21632134
#endif
21642135
}
2136+
2137+
% for x in maskingShifts:
2138+
2139+
// Homogeneous masking shift
2140+
${operatorComment(x.operator, False)}
2141+
@_transparent
2142+
public static func ${x.operator} (lhs: Self, rhs: Self) -> Self {
2143+
var lhs = lhs
2144+
lhs ${x.operator}= rhs
2145+
return lhs
2146+
}
2147+
2148+
2149+
// Heterogeneous masking shift
2150+
${operatorComment(x.operator, False)}
2151+
public static func ${x.operator} <
2152+
Other : BinaryInteger
2153+
>(lhs: Self, rhs: Other) -> Self {
2154+
return lhs ${x.operator} Self(extendingOrTruncating: rhs)
2155+
}
2156+
2157+
// Heterogeneous masking shift assignment
2158+
${assignmentOperatorComment(x.operator, False)}
2159+
@_transparent
2160+
public static func ${x.operator}= <
2161+
Other : BinaryInteger
2162+
>(lhs: inout Self, rhs: Other) {
2163+
lhs = lhs ${x.operator} rhs
2164+
}
2165+
2166+
% end
21652167
}
21662168

21672169
//===----------------------------------------------------------------------===//
@@ -2190,20 +2192,6 @@ ${operatorComment(x.nonMaskingOperator, True)}
21902192
return lhs
21912193
}
21922194

2193-
// FIXME(integers): uncommenting this overload results in a compiler not being
2194-
// able to typecheck expression like `(int64 >> 8) & 0xFF`.
2195-
#if false
2196-
@_transparent
2197-
public static func ${x.nonMaskingOperator} (_ lhs: Self, _ rhs: Int) -> Self {
2198-
return ${x.helper}(lhs, rhs)
2199-
}
2200-
2201-
@_transparent
2202-
public static func ${x.nonMaskingOperator}= (_ lhs: inout Self, _ rhs: Int) {
2203-
lhs = ${x.helper}(lhs, rhs)
2204-
}
2205-
#endif
2206-
22072195
@_transparent
22082196
public static func ${x.nonMaskingOperator}= <
22092197
Other : BinaryInteger

test/Prototypes/BigInt.swift

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,24 +1090,26 @@ public struct _BigInt<Word: FixedWidthInteger & UnsignedInteger> :
10901090
data.removeFirst(Swift.min(data.count, words))
10911091
}
10921092

1093-
public static func <<=(lhs: inout _BigInt, rhs: Int) {
1093+
public static func <<= <RHS : BinaryInteger>(lhs: inout _BigInt, rhs: RHS) {
10941094
defer { lhs._checkInvariants() }
10951095
guard rhs != 0 else { return }
10961096
guard rhs > 0 else {
1097-
lhs >>= -rhs
1097+
lhs >>= 0 - rhs
10981098
return
10991099
}
11001100

1101+
let wordWidth = RHS(Word.bitWidth)
1102+
11011103
// We can add `rhs / bits` extra words full of zero at the low end.
1102-
let extraWords = rhs / Word.bitWidth
1104+
let extraWords = Int(rhs / wordWidth)
11031105
lhs._data.reserveCapacity(lhs._data.count + extraWords + 1)
11041106
_BigInt._shiftLeft(&lhs._data, byWords: extraWords)
11051107

11061108
// Each existing word will need to be shifted left by `rhs % bits`.
11071109
// For each pair of words, we'll use the high `offset` bits of the
11081110
// lower word and the low `Word.bitWidth - offset` bits of the higher
11091111
// word.
1110-
let highOffset = rhs % Word.bitWidth
1112+
let highOffset = Int(rhs % wordWidth)
11111113
let lowOffset = Word.bitWidth - highOffset
11121114

11131115
// If there's no offset, we're finished, as `rhs` was a multiple of
@@ -1126,19 +1128,20 @@ public struct _BigInt<Word: FixedWidthInteger & UnsignedInteger> :
11261128
lhs._standardize()
11271129
}
11281130

1129-
public static func >>=(lhs: inout _BigInt, rhs: Int) {
1131+
public static func >>= <RHS : BinaryInteger>(lhs: inout _BigInt, rhs: RHS) {
11301132
defer { lhs._checkInvariants() }
11311133
guard rhs != 0 else { return }
11321134
guard rhs > 0 else {
1133-
lhs <<= -rhs
1135+
lhs <<= 0 - rhs
11341136
return
11351137
}
11361138

11371139
var tempData = lhs._dataAsTwosComplement()
11381140

1141+
let wordWidth = RHS(Word.bitWidth)
11391142
// We can remove `rhs / bits` full words at the low end.
11401143
// If that removes the entirety of `_data`, we're done.
1141-
let wordsToRemove = rhs / Word.bitWidth
1144+
let wordsToRemove = Int(rhs / wordWidth)
11421145
_BigInt._shiftRight(&tempData, byWords: wordsToRemove)
11431146
guard tempData.count != 0 else {
11441147
lhs = lhs.isNegative ? -1 : 0
@@ -1149,7 +1152,7 @@ public struct _BigInt<Word: FixedWidthInteger & UnsignedInteger> :
11491152
// For each pair of words, we'll use the low `offset` bits of the
11501153
// higher word and the high `_BigInt.Word.bitWidth - offset` bits of
11511154
// the lower word.
1152-
let lowOffset = rhs % Word.bitWidth
1155+
let lowOffset = Int(rhs % wordWidth)
11531156
let highOffset = Word.bitWidth - lowOffset
11541157

11551158
// If there's no offset, we're finished, as `rhs` was a multiple of
@@ -1169,18 +1172,6 @@ public struct _BigInt<Word: FixedWidthInteger & UnsignedInteger> :
11691172
tempData[tempData.count - 1] >>= lowOffset
11701173
lhs = _BigInt(_twosComplementData: tempData)
11711174
}
1172-
1173-
public static func <<(lhs: _BigInt, rhs: Int) -> _BigInt {
1174-
var lhs = lhs
1175-
lhs <<= rhs
1176-
return lhs
1177-
}
1178-
1179-
public static func >>(lhs: _BigInt, rhs: Int) -> _BigInt {
1180-
var lhs = lhs
1181-
lhs >>= rhs
1182-
return lhs
1183-
}
11841175
}
11851176

11861177
//===--- Bit --------------------------------------------------------------===//
@@ -1788,7 +1779,8 @@ BigInt8Tests.test("Bitshift") {
17881779

17891780
(x, y) = (BigInt(UInt.max), UInt.max)
17901781
for i in 0...64 { // test 64-bit shift, should both be zero
1791-
expectTrue(x >> i == y >> i)
1782+
expectTrue(x >> i == y >> i,
1783+
"\(x) as \(type(of:x)) >> \(i) => \(x >> i) != \(y) as \(type(of:y)) >> \(i) => \(y >> i)")
17921784
}
17931785

17941786
x = BigInt(-1)

0 commit comments

Comments
 (0)