Skip to content

Adopt Swift 6 and audit for Sendable #5000

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 15 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Use noncopyable State type for DateFormatter and NumberFormatter
  • Loading branch information
parkera committed Jul 23, 2024
commit c2bc79a8d35e9ea3c14b36030662a40785697071
118 changes: 63 additions & 55 deletions Sources/Foundation/DateFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@ open class DateFormatter : Formatter, @unchecked Sendable {
super.init()
}

// Consumes state
private convenience init(state: State) {
private convenience init(state: consuming sending State) {
self.init()
nonisolated(unsafe) let consumedState = state
_lock.withLock { $0 = consumedState }

// work around issue that state needs to be reinitialized after consuming
struct Wrapper : ~Copyable, @unchecked Sendable {
var value: State? = nil
}
var w = Wrapper(value: consume state)

_lock.withLock {
$0 = w.value.take()!
}
}

open override func copy(with zone: NSZone? = nil) -> Any {
Expand All @@ -37,56 +44,57 @@ open class DateFormatter : Formatter, @unchecked Sendable {
super.init(coder: coder)
}

final class State {
private var _formatter : CFDateFormatter? = nil
struct State : ~Copyable {
class Box {
var formatter: CFDateFormatter?
init() {}
}

private var _formatter = Box()

func copy(with zone: NSZone? = nil) -> State {
let copied = State()

func __copy<T>(_ keyPath: ReferenceWritableKeyPath<State, T>) {
copied[keyPath: keyPath] = self[keyPath: keyPath]
}

__copy(\.formattingContext)
__copy(\.dateStyle)
__copy(\.timeStyle)
__copy(\._locale)
__copy(\.generatesCalendarDates)
__copy(\._timeZone)
__copy(\._calendar)
__copy(\.isLenient)
__copy(\._twoDigitStartDate)
__copy(\._eraSymbols)
__copy(\._monthSymbols)
__copy(\._shortMonthSymbols)
__copy(\._weekdaySymbols)
__copy(\._shortWeekdaySymbols)
__copy(\._amSymbol)
__copy(\._pmSymbol)
__copy(\._longEraSymbols)
__copy(\._veryShortMonthSymbols)
__copy(\._standaloneMonthSymbols)
__copy(\._shortStandaloneMonthSymbols)
__copy(\._veryShortStandaloneMonthSymbols)
__copy(\._veryShortWeekdaySymbols)
__copy(\._standaloneWeekdaySymbols)
__copy(\._shortStandaloneWeekdaySymbols)
__copy(\._veryShortStandaloneWeekdaySymbols)
__copy(\._quarterSymbols)
__copy(\._shortQuarterSymbols)
__copy(\._standaloneQuarterSymbols)
__copy(\._shortStandaloneQuarterSymbols)
__copy(\._gregorianStartDate)
__copy(\.doesRelativeDateFormatting)
func copy(with zone: NSZone? = nil) -> sending State {
var copied = State()

copied.formattingContext = formattingContext
copied.dateStyle = dateStyle
copied.timeStyle = timeStyle
copied._locale = _locale
copied.generatesCalendarDates = generatesCalendarDates
copied._timeZone = _timeZone
copied._calendar = _calendar
copied.isLenient = isLenient
copied._twoDigitStartDate = _twoDigitStartDate
copied._eraSymbols = _eraSymbols
copied._monthSymbols = _monthSymbols
copied._shortMonthSymbols = _shortMonthSymbols
copied._weekdaySymbols = _weekdaySymbols
copied._shortWeekdaySymbols = _shortWeekdaySymbols
copied._amSymbol = _amSymbol
copied._pmSymbol = _pmSymbol
copied._longEraSymbols = _longEraSymbols
copied._veryShortMonthSymbols = _veryShortMonthSymbols
copied._standaloneMonthSymbols = _standaloneMonthSymbols
copied._shortStandaloneMonthSymbols = _shortStandaloneMonthSymbols
copied._veryShortStandaloneMonthSymbols = _veryShortStandaloneMonthSymbols
copied._veryShortWeekdaySymbols = _veryShortWeekdaySymbols
copied._standaloneWeekdaySymbols = _standaloneWeekdaySymbols
copied._shortStandaloneWeekdaySymbols = _shortStandaloneWeekdaySymbols
copied._veryShortStandaloneWeekdaySymbols = _veryShortStandaloneWeekdaySymbols
copied._quarterSymbols = _quarterSymbols
copied._shortQuarterSymbols = _shortQuarterSymbols
copied._standaloneQuarterSymbols = _standaloneQuarterSymbols
copied._shortStandaloneQuarterSymbols = _shortStandaloneQuarterSymbols
copied._gregorianStartDate = _gregorianStartDate
copied.doesRelativeDateFormatting = doesRelativeDateFormatting

// The last is `_dateFormat` because setting `dateStyle` and `timeStyle` make it `nil`.
__copy(\._dateFormat)
copied._dateFormat = _dateFormat

return copied
}

func formatter() -> CFDateFormatter {
guard let obj = _formatter else {
guard let obj = _formatter.formatter else {
let dateStyle = CFDateFormatterStyle(rawValue: CFIndex(dateStyle.rawValue))!
let timeStyle = CFDateFormatterStyle(rawValue: CFIndex(timeStyle.rawValue))!

Expand All @@ -95,14 +103,14 @@ open class DateFormatter : Formatter, @unchecked Sendable {
if let dateFormat = _dateFormat {
CFDateFormatterSetFormat(obj, dateFormat._cfObject)
}
_formatter = obj
_formatter.formatter = obj
return obj
}
return obj
}

private func _reset() {
_formatter = nil
private mutating func _reset() {
_formatter.formatter = nil
}

// MARK: -
Expand Down Expand Up @@ -143,7 +151,7 @@ open class DateFormatter : Formatter, @unchecked Sendable {
_setFormatterAttribute(formatter, attributeName: kCFDateFormatterGregorianStartDate, value: _gregorianStartDate?._cfObject)
}

internal final func _setFormatterAttribute(_ formatter: CFDateFormatter, attributeName: CFString, value: AnyObject?) {
internal func _setFormatterAttribute(_ formatter: CFDateFormatter, attributeName: CFString, value: AnyObject?) {
if let value = value {
CFDateFormatterSetProperty(formatter, attributeName, value)
}
Expand Down Expand Up @@ -174,7 +182,7 @@ open class DateFormatter : Formatter, @unchecked Sendable {
}
}

/*@NSCopying*/ internal var _locale: Locale? { willSet { _reset() } }
internal var _locale: Locale? { willSet { _reset() } }
var locale: Locale! {
get {
guard let locale = _locale else { return .current }
Expand All @@ -187,7 +195,7 @@ open class DateFormatter : Formatter, @unchecked Sendable {

var generatesCalendarDates = false { willSet { _reset() } }

/*@NSCopying*/ internal var _timeZone: TimeZone? { willSet { _reset() } }
internal var _timeZone: TimeZone? { willSet { _reset() } }
var timeZone: TimeZone! {
get {
guard let tz = _timeZone else {
Expand All @@ -203,7 +211,7 @@ open class DateFormatter : Formatter, @unchecked Sendable {
}
}

/*@NSCopying*/ internal var _calendar: Calendar! { willSet { _reset() } }
internal var _calendar: Calendar! { willSet { _reset() } }
var calendar: Calendar! {
get {
guard let calendar = _calendar else {
Expand All @@ -221,7 +229,7 @@ open class DateFormatter : Formatter, @unchecked Sendable {

var isLenient = false { willSet { _reset() } }

/*@NSCopying*/ internal var _twoDigitStartDate: Date? { willSet { _reset() } }
internal var _twoDigitStartDate: Date? { willSet { _reset() } }
var twoDigitStartDate: Date? {
get {
guard let startDate = _twoDigitStartDate else {
Expand All @@ -234,7 +242,7 @@ open class DateFormatter : Formatter, @unchecked Sendable {
}
}

/*@NSCopying*/ var defaultDate: Date? { willSet { _reset() } }
var defaultDate: Date? { willSet { _reset() } }

internal var _eraSymbols: [String]? { willSet { _reset() } }
var eraSymbols: [String] {
Expand Down
166 changes: 83 additions & 83 deletions Sources/Foundation/NumberFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,18 @@ open class NumberFormatter : Formatter, @unchecked Sendable {
super.init(coder: coder)
}

// Consumes state
private convenience init(state: State) {
private convenience init(state: consuming sending State) {
self.init()
nonisolated(unsafe) let consumedState = state
_lock.withLock { $0 = consumedState }

// work around issue that state needs to be reinitialized after consuming
struct Wrapper : ~Copyable, @unchecked Sendable {
var value: State? = nil
}
var w = Wrapper(value: consume state)

_lock.withLock {
$0 = w.value.take()!
}
}

open override func copy(with zone: NSZone? = nil) -> Any {
Expand All @@ -74,98 +81,91 @@ open class NumberFormatter : Formatter, @unchecked Sendable {
return numberFormatter.string(for: num)!
}

final class State {
private var _formatter: CFNumberFormatter? = nil
struct State : ~Copyable {
class Box {
var formatter: CFNumberFormatter?
init() {}
}

private var _formatter = Box()

// MARK: -

func copy(with zone: NSZone? = nil) -> State {
let copied = State()

func __copy<T>(_ keyPath: ReferenceWritableKeyPath<State, T>) {
copied[keyPath: keyPath] = self[keyPath: keyPath]
}

func __copy<T>(_ keyPath: ReferenceWritableKeyPath<State, T>) where T: NSCopying {
copied[keyPath: keyPath] = self[keyPath: keyPath].copy(with: zone) as! T
}

func __copy<T>(_ keyPath: ReferenceWritableKeyPath<State, T?>) where T: NSCopying {
copied[keyPath: keyPath] = self[keyPath: keyPath]?.copy(with: zone) as! T?
}

__copy(\.formattingContext)
__copy(\._numberStyle)
__copy(\._locale)
__copy(\._generatesDecimalNumbers)
__copy(\._textAttributesForNegativeValues)
__copy(\._textAttributesForPositiveValues)
__copy(\._allowsFloats)
__copy(\._decimalSeparator)
__copy(\._alwaysShowsDecimalSeparator)
__copy(\._currencyDecimalSeparator)
__copy(\._usesGroupingSeparator)
__copy(\._groupingSeparator)
__copy(\._zeroSymbol)
__copy(\._textAttributesForZero)
__copy(\._nilSymbol)
__copy(\._textAttributesForNil)
__copy(\._notANumberSymbol)
__copy(\._textAttributesForNotANumber)
__copy(\._positiveInfinitySymbol)
__copy(\._textAttributesForPositiveInfinity)
__copy(\._negativeInfinitySymbol)
__copy(\._textAttributesForNegativeInfinity)
__copy(\._positivePrefix)
__copy(\._positiveSuffix)
__copy(\._negativePrefix)
__copy(\._negativeSuffix)
__copy(\._currencyCode)
__copy(\._currencySymbol)
__copy(\._internationalCurrencySymbol)
__copy(\._percentSymbol)
__copy(\._perMillSymbol)
__copy(\._minusSign)
__copy(\._plusSign)
__copy(\._exponentSymbol)
__copy(\._groupingSize)
__copy(\._secondaryGroupingSize)
__copy(\._multiplier)
__copy(\._formatWidth)
__copy(\._paddingCharacter)
__copy(\._paddingPosition)
__copy(\._roundingMode)
__copy(\._roundingIncrement)
__copy(\._minimumIntegerDigits)
__copy(\._maximumIntegerDigits)
__copy(\._minimumFractionDigits)
__copy(\._maximumFractionDigits)
__copy(\._minimum)
__copy(\._maximum)
__copy(\._currencyGroupingSeparator)
__copy(\._lenient)
__copy(\._usesSignificantDigits)
__copy(\._minimumSignificantDigits)
__copy(\._maximumSignificantDigits)
__copy(\._partialStringValidationEnabled)
__copy(\._hasThousandSeparators)
__copy(\._thousandSeparator)
__copy(\._localizesFormat)
__copy(\._positiveFormat)
__copy(\._negativeFormat)
__copy(\._roundingBehavior)
var copied = State()

copied.formattingContext = formattingContext
copied._numberStyle = _numberStyle
copied._locale = _locale
copied._generatesDecimalNumbers = _generatesDecimalNumbers
copied._textAttributesForNegativeValues = _textAttributesForNegativeValues
copied._textAttributesForPositiveValues = _textAttributesForPositiveValues
copied._allowsFloats = _allowsFloats
copied._decimalSeparator = _decimalSeparator
copied._alwaysShowsDecimalSeparator = _alwaysShowsDecimalSeparator
copied._currencyDecimalSeparator = _currencyDecimalSeparator
copied._usesGroupingSeparator = _usesGroupingSeparator
copied._groupingSeparator = _groupingSeparator
copied._zeroSymbol = _zeroSymbol
copied._textAttributesForZero = _textAttributesForZero
copied._nilSymbol = _nilSymbol
copied._textAttributesForNil = _textAttributesForNil
copied._notANumberSymbol = _notANumberSymbol
copied._textAttributesForNotANumber = _textAttributesForNotANumber
copied._positiveInfinitySymbol = _positiveInfinitySymbol
copied._textAttributesForPositiveInfinity = _textAttributesForPositiveInfinity
copied._negativeInfinitySymbol = _negativeInfinitySymbol
copied._textAttributesForNegativeInfinity = _textAttributesForNegativeInfinity
copied._positivePrefix = _positivePrefix
copied._positiveSuffix = _positiveSuffix
copied._negativePrefix = _negativePrefix
copied._negativeSuffix = _negativeSuffix
copied._currencyCode = _currencyCode
copied._currencySymbol = _currencySymbol
copied._internationalCurrencySymbol = _internationalCurrencySymbol
copied._percentSymbol = _percentSymbol
copied._perMillSymbol = _perMillSymbol
copied._minusSign = _minusSign
copied._plusSign = _plusSign
copied._exponentSymbol = _exponentSymbol
copied._groupingSize = _groupingSize
copied._secondaryGroupingSize = _secondaryGroupingSize
copied._multiplier = _multiplier
copied._formatWidth = _formatWidth
copied._paddingCharacter = _paddingCharacter
copied._paddingPosition = _paddingPosition
copied._roundingMode = _roundingMode
copied._roundingIncrement = _roundingIncrement
copied._minimumIntegerDigits = _minimumIntegerDigits
copied._maximumIntegerDigits = _maximumIntegerDigits
copied._minimumFractionDigits = _minimumFractionDigits
copied._maximumFractionDigits = _maximumFractionDigits
copied._minimum = _minimum
copied._maximum = _maximum
copied._currencyGroupingSeparator = _currencyGroupingSeparator
copied._lenient = _lenient
copied._usesSignificantDigits = _usesSignificantDigits
copied._minimumSignificantDigits = _minimumSignificantDigits
copied._maximumSignificantDigits = _maximumSignificantDigits
copied._partialStringValidationEnabled = _partialStringValidationEnabled
copied._hasThousandSeparators = _hasThousandSeparators
copied._thousandSeparator = _thousandSeparator
copied._localizesFormat = _localizesFormat
copied._positiveFormat = _positiveFormat
copied._negativeFormat = _negativeFormat
copied._roundingBehavior = _roundingBehavior

return copied
}

// MARK: -

func _reset() {
_formatter = nil
_formatter.formatter = nil
}

func formatter() -> CFNumberFormatter {
if let obj = _formatter {
if let obj = _formatter.formatter {
return obj
} else {
let numberStyle = CFNumberFormatterStyle(rawValue: CFIndex(_numberStyle.rawValue))!
Expand All @@ -180,7 +180,7 @@ open class NumberFormatter : Formatter, @unchecked Sendable {
}
CFNumberFormatterSetFormat(obj, format._cfObject)
}
_formatter = obj
_formatter.formatter = obj
return obj
}
}
Expand Down