From f2c1829b695c0ab8a40efeb1df43360c4ffa02fd Mon Sep 17 00:00:00 2001 From: Max Moiseev Date: Wed, 13 Jul 2016 16:01:44 -0700 Subject: [PATCH] [stdlib] More new integer protocols --- stdlib/public/core/Integers.swift.gyb | 607 +++++++++++++++++++++++++- stdlib/public/core/Unicode.swift | 9 +- 2 files changed, 608 insertions(+), 8 deletions(-) diff --git a/stdlib/public/core/Integers.swift.gyb b/stdlib/public/core/Integers.swift.gyb index 2fe72d4daa08b..4f002d7e737b9 100644 --- a/stdlib/public/core/Integers.swift.gyb +++ b/stdlib/public/core/Integers.swift.gyb @@ -14,6 +14,8 @@ # Utility code for later in this template # +from string import maketrans, capitalize + # Number of bits in the Builtin.Word type word_bits = int(CMAKE_SIZEOF_VOID_P) * 8 @@ -34,14 +36,38 @@ binaryArithmetic = { struct(operator='*', name='multiplied', mutatingName='multiply', firstArg='by', llvmName='mul', kind='*'), struct(operator='/', name='divided', mutatingName='divide', firstArg='by', llvmName='div', kind='/'), ], + 'BinaryInteger' : [ + struct(operator='%', name='remainder', mutatingName='formRemainder', firstArg='dividingBy', llvmName='rem', kind='/'), + ], } +binaryBitwise = [ + struct(operator='&', name='bitwiseAnd', llvmName='and'), + struct(operator='|', name='bitwiseOr', llvmName='or'), + struct(operator='^', name='bitwiseXor', llvmName='xor'), +] + +maskingShifts = [ + struct( + operator='&>>', nonMaskingOperator='>>', description='right shift', + name='maskingShiftRight', llvmName=lambda s:['lshr','ashr'][s]), + struct( + operator='&<<', nonMaskingOperator='<<', description='left shift', + name='maskingShiftLeft', llvmName=lambda _: 'shl'), +] }% +infix operator &<< { associativity none precedence 160 } +infix operator &<<= { associativity right precedence 90 assignment } +infix operator &>> { associativity none precedence 160 } +infix operator &>>= { associativity right precedence 90 assignment } + +//===----------------------------------------------------------------------===// //===--- Bits for the Stdlib ----------------------------------------------===// +//===----------------------------------------------------------------------===// -//FIXME: This should go in the stdlib separately, probably. +//FIXME(integers): This should go in the stdlib separately, probably. extension ExpressibleByIntegerLiteral where Self : _ExpressibleByBuiltinIntegerLiteral { /// Create an instance initialized to `value`. @@ -76,8 +102,8 @@ extension ExpressibleByIntegerLiteral /// be preferred to the `abs(_)` function, whose return value is of the same /// type as its argument. public protocol Arithmetic : Equatable, ExpressibleByIntegerLiteral { - // FIXME: implement - //init?(exactly source: T) + // FIXME(integers): implement + //init?(exactly source: T) // FIXME(ABI): should be just Arithmetic associatedtype Magnitude : Equatable, ExpressibleByIntegerLiteral @@ -102,7 +128,7 @@ extension Arithmetic { } } -% for Protocol in ['Arithmetic']: +% for Protocol in binaryArithmetic: extension ${Protocol} { % for x in binaryArithmetic[Protocol]: % callLabel = x.firstArg + ': ' if not x.firstArg == '_' else '' @@ -128,3 +154,576 @@ extension SignedArithmetic { self = negated() } } + +//===----------------------------------------------------------------------===// +//===--- Arithmetic operators ---------------------------------------------===// +//===----------------------------------------------------------------------===// + +% for Protocol in binaryArithmetic: +% for x in binaryArithmetic[Protocol]: +% callLabel = x.firstArg + ': ' if not x.firstArg == '_' else '' +@_transparent +public func ${x.operator} (lhs: T, rhs: T) -> T { + return lhs.${x.name}(${callLabel}rhs) +} + +@_transparent +public func ${x.operator}= (lhs: inout T, rhs: T) { + lhs.${x.mutatingName}(${callLabel}rhs) +} +% end +% end + +@_transparent +public prefix func -(x: T) -> T { + return x.negated() +} + +//===----------------------------------------------------------------------===// +//===--- BinaryInteger ----------------------------------------------------===// +//===----------------------------------------------------------------------===// + +public protocol BinaryInteger : + Comparable, Arithmetic, CustomStringConvertible { + + static var isSigned: Bool { get } + + // Dispatching through these puts less stress on the user reading + // the interface and error messages (and on the type checker) than + // does having many operator overloads. + func isEqual(to rhs: Self) -> Bool + func isLess(than rhs: Self) -> Bool + + init?(exactly source: T) + + init(_ source: T) + + init(_ source: T) + + init(extendingOrTruncating source: T) + + init(clamping source: T) + + func word(at n: Int) -> UInt + + var bitWidth : Int { get } + + var minimumSignedRepresentationBitWidth: Int { get } + +% for x in binaryArithmetic['BinaryInteger']: + // defaulted using an in-place counterpart, but can be used as an + // optimization hook + func ${x.name}(${x.firstArg} rhs: Self) -> Self + + // implementation hook + mutating func ${x.mutatingName}(${x.firstArg} rhs: Self) +% end + + func quotientAndRemainder(dividingBy rhs: Self) -> (Self, Self) + + /// Returns `-1` if the value of `self` is negative, `1` if it's positive, + /// `0` otherwise. + func signum() -> Self +} + +extension BinaryInteger { + public init?(exactly source: T) { + // FIXME(integers): implement + fatalError() + } + + public init(_ source: T) { + // FIXME(integers): implement + fatalError() + } + + public func signum() -> Self { + // FIXME(integers): implement + fatalError() + } + + public var countRepresentedWords: Int { + return (self.bitWidth + ${word_bits} - 1) / ${word_bits} + } + + public func quotientAndRemainder(dividingBy rhs: Self) -> (Self, Self) { + return (self.divided(by: rhs), self.remainder(dividingBy: rhs)) + } +} + +//===----------------------------------------------------------------------===// +//===--- Homogeneous comparison -------------------------------------------===// +//===----------------------------------------------------------------------===// + +@_transparent +public func == (lhs: T, rhs: T) -> Bool { + return lhs.isEqual(to: rhs) +} + +@_transparent +public func < (lhs: T, rhs: T) -> Bool { + return lhs.isLess(than: rhs) +} + +//===----------------------------------------------------------------------===// +//===--- Heterogeneous comparison -----------------------------------------===// +//===----------------------------------------------------------------------===// + +@_transparent +public func == (lhs: T, rhs: U) -> Bool { + return (lhs > 0) == (rhs > 0) + && T(extendingOrTruncating: rhs) == lhs + && U(extendingOrTruncating: lhs) == rhs +} + +@_transparent +public func != (lhs:T, rhs: U) -> Bool { + return !(lhs == rhs) +} + +@_transparent +public func < (lhs: T, rhs: U) -> Bool { + let lhsSign = lhs < 0 ? -1 : lhs > 0 ? 1 : 0 + let rhsSign = rhs < 0 ? -1 : rhs > 0 ? 1 : 0 + if lhsSign != rhsSign { return lhsSign < rhsSign } + + // if we get here, lhs and rhs have the same sign. If they're + // negative, then T and U are both signed types, and one of them can + // represent values of the other type. Otherwise, lhs and rhs are + // positive, and one of T, U may be signed and the other unsigned. + // In this case, we can conceptually subtract 1 from the bitWidth of + // any signed type, and either the resulting bitWidths are the same + // or one can represent every value of the other. + + let rT = T(extendingOrTruncating: rhs) + + // Can we round-trip rhs through T? + if U(extendingOrTruncating: rT) == rhs { + return lhs < rT + } + + return U(extendingOrTruncating: lhs) < rhs +} + +@inline(__always) +public func <= (lhs: T, rhs: U) -> Bool { + return !(rhs < lhs) +} + +@inline(__always) +public func >= (lhs: T, rhs: U) -> Bool { + return !(lhs < rhs) +} + +@inline(__always) +public func > (lhs: T, rhs: U) -> Bool { + return rhs < lhs +} + +//===----------------------------------------------------------------------===// +//===--- Ambiguity breakers -----------------------------------------------===// +// These two versions of the operators are not ordered with respect to +// one another: +// +// (T, T) -> Bool +// (T, U) -> Bool +// +// so we define: +// +// (T, T) -> Bool +//===----------------------------------------------------------------------===// + +@_transparent +public func != (lhs: T, rhs: T) -> Bool { + return !(lhs == rhs) +} + +@inline(__always) +public func <= (lhs: T, rhs: T) -> Bool { + return !(rhs < lhs) +} + +@inline(__always) +public func >= (lhs: T, rhs: T) -> Bool { + return !(lhs < rhs) +} + +@inline(__always) +public func > (lhs: T, rhs: T) -> Bool { + return rhs < lhs +} + +//===----------------------------------------------------------------------===// +//===--- FixedWidthInteger ------------------------------------------------===// +//===----------------------------------------------------------------------===// + +public enum ArithmeticOverflow { + @_transparent + public init(_ overflow: Bool) { self = overflow ? .overflow : .none } + case none, overflow +} + +public protocol FixedWidthInteger : BinaryInteger { + static var bitWidth : Int { get } + + static var max: Self { get } + static var min: Self { get } + +% for x in binaryArithmetic['Arithmetic']: +%{ +comment = ''' + /// Return a pair consisting of `self` {} `rhs`, + /// truncated to fit if necessary, and a flag indicating whether an + /// arithmetic overflow occurred.'''.format(x.operator) + (''' + /// + /// - Precondition: `rhs != 0`''' if x.kind == '/' else '') +}% +${comment} + func ${x.name}WithOverflow( + ${x.firstArg} rhs: Self + ) -> (partialValue: Self, overflow: ArithmeticOverflow) +% end + +% for x in binaryBitwise + maskingShifts: + func ${x.name}(_ rhs: Self) -> Self +% end + + static func doubleWidthMultiply(_ lhs: Self, _ rhs: Self) + -> (high: Self, low: Magnitude) + static func doubleWidthDivide( + _ lhs: (high: Self, low: Magnitude), _ rhs: Self) + -> (quotient: Self, remainder: Self) + + init(_truncatingBits bits: UInt) + + var popcount: Int { get } + var leadingZeros: Int { get } +} + +//===----------------------------------------------------------------------===// +//===--- Operators on FixedWidthInteger -----------------------------------===// +//===----------------------------------------------------------------------===// + +@inline(__always) +public prefix func ~ (x: T) -> T { + return 0 &- x &- 1 +} + +% for x in binaryBitwise: +@_transparent +public func ${x.operator} (lhs: T, rhs: T) -> T { + return lhs.${x.name}(rhs) +} + +@_transparent +public func ${x.operator}= (lhs: inout T, rhs: T) { + lhs = lhs.${x.name}(rhs) +} +% end + +% for x in maskingShifts: + +@_transparent +public func ${x.operator} (lhs: T, rhs: T) -> T { + return lhs.${x.name}(rhs) +} + +@_transparent +public func ${x.operator}= (lhs: inout T, rhs: T) { + lhs = lhs ${x.operator} rhs +} + +@_transparent +public func ${x.operator} < + T: FixedWidthInteger, U: BinaryInteger +>(lhs: T, rhs: U) -> T { + return lhs.${x.name}(T(extendingOrTruncating: rhs)) +} + +@_transparent +public func ${x.operator}= < + T: FixedWidthInteger, U: BinaryInteger +>(lhs: inout T, rhs: U) { + lhs = lhs ${x.operator} rhs +} + +@_transparent +public func ${x.nonMaskingOperator} < + T: FixedWidthInteger, U: BinaryInteger +>(lhs: T, rhs: U) -> T { + // FIXME(integers): uncomment once Int conforms to BinaryInteger + //let shift = rhs < -T.bitWidth ? -T.bitWidth + //: rhs > T.bitWidth ? T.bitWidth + //: Int(rhs) + //return lhs ${x.nonMaskingOperator} shift + fatalError() +} + +//===----------------------------------------------------------------------===// +//=== "Smart ${x.description}", supporting overshifts and negative shifts -===// +//===----------------------------------------------------------------------===// + +@_transparent +public func ${x.nonMaskingOperator} < + T: FixedWidthInteger +>(lhs: T, rhs: Int) -> T { + // FIXME(integers): uncomment once Int conforms to BinaryInteger + fatalError() + //let overshiftR = T.isSigned ? lhs &>> (T.bitWidth - 1) : 0 + //let overshiftL: T = 0 + //if _fastPath(rhs >= 0) { + //if _fastPath(rhs < T.bitWidth) { + //return lhs.${x.name}(T(extendingOrTruncating: rhs)) + //} + //return overshift${'LR'['R' in x.name]} + //} + + //if _slowPath(rhs <= -T.bitWidth) { + //return overshift${'RL'['R' in x.name]} + //} + //return lhs ${x.operator.translate(maketrans('<>', '><'))} -rhs +} + +@_transparent +public func ${x.nonMaskingOperator}= < + T: FixedWidthInteger +>(lhs: inout T, rhs: T) { + lhs = lhs ${x.nonMaskingOperator} rhs +} + +@_transparent +public func ${x.nonMaskingOperator}= < + T: FixedWidthInteger, U: BinaryInteger +>(lhs: inout T, rhs: U) { + lhs = lhs ${x.nonMaskingOperator} rhs +} + +% end # maskingShifts + + +extension FixedWidthInteger { + public init(clamping source: Other) { + if _slowPath(source < Self.min) { + self = Self.min + } + else if _slowPath(source > Self.max) { + self = Self.max + } + else { self = Self(extendingOrTruncating: source) } + } + +% for x in binaryArithmetic['Arithmetic']: +% callLabel = x.firstArg + ': ' if not x.firstArg == '_' else '' + @_transparent + // FIXME(integers): rename `rhs` to `other` + public mutating func ${x.mutatingName}(${x.firstArg} rhs: Self) { + let (result, _ /*overflow*/) = self.${x.name}WithOverflow(${callLabel}rhs) + // FIXME(integers): overflow check + //_assertCond(overflow == .none, "overflow in ${x.name}") + self = result + } + + /// Return `self ${x.operator} rhs`. If an arithmetic overflow + /// occurs, the behavior is undefined. + /// + /// Note: use this function to avoid the cost of overflow checking + /// when you are sure that the operation won't overflow. + @_transparent + public func unsafe${capitalize(x.name)}(rhs: Self) -> Self { + let (result, overflow) = self.${x.name}WithOverflow(${callLabel}rhs) + + if (overflow != .none) { + if (_isDebugAssertConfiguration()) { + _preconditionFailure("overflow in unsafe${capitalize(x.name)}") + } + else { + Builtin.conditionallyUnreachable() + } + } + return result + } +% end + + @_transparent + public mutating func formRemainder(dividingBy other: Self) { + // FIXME(integers): implement + fatalError() + } + + @_transparent + public init(extendingOrTruncating source: T) { + if Self.bitWidth <= ${word_bits} { + self = Self.init(_truncatingBits: source.word(at: 0)) + } + else { + var result: Self = source < 0 ? ~0 : 0 + // start with the most significant word + var n = source.countRepresentedWords + while n >= 0 { + // masking is OK here because this we have already ensured + // that Self.bitWidth > ${word_bits}. Not masking results in + // infinite recursion. + result &<<= ${word_bits} + result |= Self(_truncatingBits: source.word(at: n)) + n -= 1 + } + + self = result + } + } + + @_transparent + // FIXME(integers): give it a better name. _highBitIndex is **NOT** what it is + public // transparent + static var _highBitIndex: Self { + // FIXME(integers): uncomment once UInt conforms to BinaryInteger + //return Self.init(_truncatingBits: UInt(Self.bitWidth._storage) &- 1) + fatalError() + } + + public var popcount: Int { + fatalError() + } + + public static func doubleWidthDivide( + _ lhs: (high: Self, low: Magnitude), _ rhs: Self) + -> (quotient: Self, remainder: Self) { + fatalError() + } +} + +% for x in [op for ops in binaryArithmetic.values() for op in ops]: +% callLabel = x.firstArg + ': ' if not x.firstArg == '_' else '' +% if x.kind != '/': +public func &${x.operator} (lhs: T, rhs: T) -> T { + return lhs.${x.name}WithOverflow(${callLabel}rhs).partialValue +} +% end +% end + +//===----------------------------------------------------------------------===// +//===--- UnsignedInteger --------------------------------------------------===// +//===----------------------------------------------------------------------===// + +public protocol UnsignedInteger_ : BinaryInteger { + associatedtype Magnitude : BinaryInteger +} + +extension UnsignedInteger_ { + @_transparent + public var magnitude: Self { return self } + + @_transparent + public static var isSigned: Bool { return false } + + public var description: String { + // FIXME(integers): uncomment once Int conforms to BinaryInteger + fatalError() + //if self == 0 { + //return "0" + //} + + //let ascii0 = 48 + //var buf: [UnicodeScalar] = [] + + //var x = self + //repeat { + //let r = x % 10 + //x /= 10 + //buf.append( + //UnicodeScalar( + //ascii0 + Int(Word(extendingOrTruncating: r)._storage))) + //} + //while x != 0 + //return String(buf.reversed().lazy.map { Character($0) }) + } +} + +extension UnsignedInteger_ where Self : FixedWidthInteger { + @_transparent + public init(_ source: T) { + // FIXME(integers): uncomment checks + //_assertCond( + //source >= 0, "negative value \(source) not representable by \(Self.self)") + //let requiredBits = source.minimumSignedRepresentationBitWidth - 1 + //_assertCond( + //requiredBits <= Self.bitWidth, + //"\(Self.self) cannot store all \(requiredBits) bits " + //+ "needed for unsigned representation of \(source)") + self.init(extendingOrTruncating: source) + } + + @_transparent + public init?(exactly source: T) { + // FIXME(integers): uncomment checks + //_assertCond( + //source >= 0, "negative value \(source) not representable by \(Self.self)") + let requiredBits = source.minimumSignedRepresentationBitWidth - 1 + if requiredBits > Self.bitWidth { + return nil + } + self.init(extendingOrTruncating: source) + } + + @_transparent + public static var max: Self { + return ~0 + } + + @_transparent + public static var min: Self { + return 0 + } + +} + + +//===----------------------------------------------------------------------===// +//===--- SignedInteger ----------------------------------------------------===// +//===----------------------------------------------------------------------===// + +public protocol SignedInteger_ : BinaryInteger, SignedArithmetic { + associatedtype Magnitude : BinaryInteger +} + +extension SignedInteger_ { + public var description: String { + let base = String(magnitude) + return self < 0 ? "-" + base : base + } + + @_transparent + public static var isSigned: Bool { return true } +} + +extension SignedInteger_ where Self : FixedWidthInteger { + @_transparent + public init(_ source: T) { + // FIXME(integers): uncomment checks + //let requiredBits = source.minimumSignedRepresentationBitWidth + //_assertCond( + //requiredBits <= Self.bitWidth, + //"\(Self.self) cannot store all \(requiredBits) bits " + //+ "needed for signed representation of \(source)") + self.init(extendingOrTruncating: source) + } + + @_transparent + public init?(exactly source: T) { + let requiredBits = source.minimumSignedRepresentationBitWidth + if requiredBits > Self.bitWidth { + return nil + } + self.init(extendingOrTruncating: source) + } + + @_transparent + public static var max: Self { + return ~min + } + + @_transparent + public static var min: Self { + return -1 &<< Self._highBitIndex + } +} diff --git a/stdlib/public/core/Unicode.swift b/stdlib/public/core/Unicode.swift index 3b30b63e41276..d7e6b06f64515 100644 --- a/stdlib/public/core/Unicode.swift +++ b/stdlib/public/core/Unicode.swift @@ -344,10 +344,11 @@ public struct UTF8 : UnicodeCodec { return (nil, 3) } // Extract data bits. - let value = (buffer & 0x3f000000) >> 24 - | (buffer & 0x003f0000) >> 10 - | (buffer & 0x00003f00) << 4 - | (buffer & 0x00000007) << 18 + // FIXME(integers): remove extra type casts + let value = (buffer & 0x3f000000) >> (24 as UInt32) + | (buffer & 0x003f0000) >> (10 as UInt32) + | (buffer & 0x00003f00) << (4 as UInt32) + | (buffer & 0x00000007) << (18 as UInt32) return (value, 4) default: // Invalid sequence (CU0 invalid).