Skip to content

Commit 8369ba9

Browse files
authored
Merge pull request #1429 from swiftlang/automerge/merge-main-2025-07-14_09-04
Merge `main` into `release/6.2`
2 parents 77b7cf4 + 64fff9d commit 8369ba9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1028
-1579
lines changed

Benchmarks/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ let usePackage: UsePackage
3737

3838
if let useLocalPackageEnv = Context.environment["SWIFTCI_USE_LOCAL_DEPS"], !useLocalPackageEnv.isEmpty {
3939
if useLocalPackageEnv == "1" {
40-
usePackage = .useLocalPackage("..")
40+
usePackage = .useLocalPackage("../..")
4141
} else {
4242
usePackage = .useLocalPackage(useLocalPackageEnv)
4343
}

CMakeLists.txt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ list(APPEND CMAKE_MODULE_PATH ${SwiftFoundation_SOURCE_DIR}/cmake/modules)
9494

9595
# Availability Macros (only applies to FoundationEssentials and FoundationInternationalization)
9696
set(_SwiftFoundation_BaseAvailability "macOS 15, iOS 18, tvOS 18, watchOS 11")
97-
set(_SwiftFoundation_macOS26Availability "macOS 26, iOS 26, tvOS 26, watchOS 26")
9897
set(_SwiftFoundation_FutureAvailability "macOS 10000, iOS 10000, tvOS 10000, watchOS 10000")
9998

10099
# All versions to define for each availability name
@@ -106,13 +105,11 @@ list(APPEND _SwiftFoundation_versions
106105

107106
# Each availability name to define
108107
list(APPEND _SwiftFoundation_availability_names
109-
"FoundationPreview"
110-
"FoundationSpan")
108+
"FoundationPreview")
111109

112110
# The aligned availability for each name (in the same order)
113111
list(APPEND _SwiftFoundation_availability_releases
114-
${_SwiftFoundation_BaseAvailability}
115-
${_SwiftFoundation_macOS26Availability})
112+
${_SwiftFoundation_BaseAvailability})
116113

117114
foreach(version ${_SwiftFoundation_versions})
118115
foreach(name release IN ZIP_LISTS _SwiftFoundation_availability_names _SwiftFoundation_availability_releases)

Package.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import CompilerPluginSupport
88

99
let availabilityTags: [_Availability] = [
1010
_Availability("FoundationPreview"), // Default FoundationPreview availability
11-
_Availability("FoundationSpan", availability: .macOS26), // Availability of Span types
1211
]
1312
let versionNumbers = ["6.0.2", "6.1", "6.2"]
1413

@@ -108,10 +107,7 @@ let package = Package(
108107
// TestSupport (Internal)
109108
.target(
110109
name: "TestSupport",
111-
dependencies: [
112-
"FoundationEssentials",
113-
"FoundationInternationalization",
114-
],
110+
path: "Tests/TestSupport",
115111
cSettings: wasiLibcCSettings,
116112
swiftSettings: availabilityMacros + featureSettings
117113
),

Proposals/0017-expanded-calendar-support.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Foundation will add constants to support the new calendar types.
2727

2828
Some calendars added by this proposal have a unique feature of "leap days", where two consecutive days can have the same numeric value. This new feature will have impact on APIs that search/match dates.
2929

30-
This property will be called isAdhikaDay, to indicate it is used in Hindu calendars which are the only calendars that has this feature. More details about the naming is provided in the section `Alternatives considered` at the end of the document.
30+
This property will be called `isRepeatedDay`. More details about the naming is provided in the section `Alternatives considered` at the end of the document.
3131

3232
## Detailed design
3333

@@ -86,7 +86,7 @@ Foundation will add new string constants for new calendar identifiers for NSCale
8686
}
8787
```
8888

89-
Following are the code changes required to support new calendar property isAdhikaDay
89+
Following are the code changes required to support new calendar property isRepeatedDay
9090

9191

9292
**Calendar.swift**
@@ -96,11 +96,20 @@ Following are the code changes required to support new calendar property isAdhik
9696
public enum Component : Sendable {
9797
// ...
9898
@available(FoundationPreview 6.2, *)
99-
case isAdhikaDay
99+
case isRepeatedDay
100100
// ....
101101
}
102102
```
103103

104+
**DateComponents.swift**
105+
106+
```swift
107+
extension DateComponents {
108+
@available(FoundationPreview 6.2, *)
109+
public var isRepeatedDay: Bool? { get set }
110+
}
111+
```
112+
104113
## Impact on existing code
105114

106115
Vietnamese and Korean calendars have no special considerations.
@@ -113,19 +122,17 @@ Calendars used in India are introducing the new field for the leap day. Clients
113122
var components = DateComponents()
114123
```
115124

116-
**Second**, the comparison for calendar dates will account for this field. Two dates differs if they don't have the same value for isAdhikaDay
125+
**Second**, the comparison for calendar dates will account for this field. Two dates differs if they don't have the same value for isRepeatedDay
117126

118127
For example, the clients may currently use following check
119128

120129
```swift
121130
cal.compare(d1, d2)
122131
```
123132

124-
For Hindu calendars, this would only compare equal if they're both Adhika day or if they are both not. For non-Hindu calendars, isAdhikaDay property will be ignored.
125-
126-
**Third**, the calendar date arithmetic will be updated to correctly calculate the dates regarding `adhikaDay`. When working with Hindu calendars, as the leap days can occur at any position, looking for next day would have to involve recalculation.
133+
For Hindu calendars, this would only compare equal if their year, month, day, and repeated day values are all the same. For non-Hindu calendars, isRepeatedDay property will be ignored.
127134

128-
As a general rule, behavior of `isAdhikaDay` will replicate `isLeapMonth` in APIs doing matching and searching.
135+
As a general rule, behavior of `isRepeatedDay` will replicate `isLeapMonth` in APIs doing matching and searching.
129136

130137
For example, let's explain what is the expected behavior in this API that enumerate the next date
131138

@@ -144,17 +151,17 @@ Clients using this function to enumerate the next date after `date`, matching th
144151

145152
* If start is on a leap day
146153

147-
* `strict`: If components.isAdhikaDay is true, this gives you the next date that is also a leap day that shares the same day, month, year number (i.e. all those specified with the comps argument) as start. If only a subset of `DateComponents` is specified, for example only the month, this gives you the next date which is a leap day with same month. If no arguments are given for `DateComponents`, this gives the next date that is also a leap day. If components.isAdhikaDay is false, this function gives you the next date that matches the day/month/year number but one that is not a leap day.
154+
* `strict`: If components.isRepeatedDay is true, this gives you the next date that is also a leap day that shares the same day, month, year number (i.e. all those specified with the comps argument) as start. If only a subset of `DateComponents` is specified, for example only the month, this gives you the next date which is a leap day with same month. If no arguments are given for `DateComponents`, this gives the next date that is also a leap day. If components.isRepeatedDay is false, this function gives you the next date that matches the day/month/year number but one that is not a leap day.
148155

149156
* If start is not on a leap day
150157

151-
* `strict`: If components.isAdhikaDay is true, this gives you the next date that is a leap day that shares the same day, month, year number (i.e. all those specified with the comps argument) as start but a leap day. If components.isAdhikaDay is false, this function gives you the next date that matches the day/month/year number that is not a leap day.
158+
* `strict`: If components.isRepeatedDay is true, this gives you the next date that is a leap day that shares the same day, month, year number (i.e. all those specified with the comps argument) as start but a leap day. If components.isRepeatedDay is false, this function gives you the next date that matches the day/month/year number that is not a leap day.
152159

153160
For the `direction` and `repeatedTimePolicy`:
154161

155162
* `backward`: This flag does not affect how leap day search is handled, but merely changes the search direction so that it finds the match before start rather than after start.
156163

157-
* `first`: If there are two or more matching dates, and all their components are the same, including isAdhikaDay, the function returns the first date.
164+
* `first`: If there are two or more matching dates, and all their components are the same, including isRepeatedDay, the function returns the first date.
158165

159166
* `last`: Similar to `first`, but the function returns the last match.
160167

@@ -178,6 +185,6 @@ public func dates(byAdding components: DateComponents,
178185

179186
As mentioned before, the leap day is a unique feature of some of new calendars. It appears when a certain astronomical position of Sun and Moon happens which means it can appear on any date. That differs from Gregorian leap day of February 29th, which happens every four years (approximately) and it is always on the same day.
180187

181-
This property will be called isAdhikaDay to clearly indicate it is related to Hindu lunisolar calendars. The alternative was to call it isLeapDay, but that may lead to confusion with Gregorian calendar
188+
This property will be called isRepeatedDay to clearly indicate it is a day that repeats. The alternatives were to call it isLeapDay which might lead to confusion with Gregorian calendar, or isAdhikaDay which would be unclear for developers unfamiliar with Hindu calendar terminology.
182189

183190
Another point of discussion was whether to use Bengali vs Bangla and Oriya vs Odia. There has been an effort to stop using the older colloquial names Bengali and Oriya and to switch to the now-accepted names Bangla and Odia. While most of the attention has gone to the language names, the same naming should also be used for the calendar names, despite them receiving less attention.

Proposals/0024-CurrentBundle.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Introduce `#bundle`
22

33

4-
* Proposal: [SF-0024](0024-filename.md)
4+
* Proposal: [SF-0024](0024-CurrentBundle.md)
55
* Authors:[Matt Seaman](https://github.com/matthewseaman), [Andreas Neusuess](https://github.com/Tantalum73)
66
* Review Manager: [Tina L](https://github.com/itingliu)
77
* Implementation: https://github.com/swiftlang/swift-foundation/pull/1274

Sources/FoundationEssentials/AttributedString/AttributedString+Runs.swift

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -209,26 +209,24 @@ extension AttributedString.Runs.Index: Comparable {
209209
}
210210
}
211211

212-
#if !FOUNDATION_FRAMEWORK
213-
@available(macOS, deprecated: 10000, introduced: 12, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
214-
@available(iOS, deprecated: 10000, introduced: 15, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
215-
@available(tvOS, deprecated: 10000, introduced: 15, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
216-
@available(watchOS, deprecated: 10000, introduced: 8, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
217-
@available(visionOS, deprecated: 10000, introduced: 1, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
212+
@available(macOS, deprecated: 26, introduced: 12, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
213+
@available(iOS, deprecated: 26, introduced: 15, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
214+
@available(tvOS, deprecated: 26, introduced: 15, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
215+
@available(watchOS, deprecated: 26, introduced: 8, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
216+
@available(visionOS, deprecated: 26, introduced: 1, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
218217
@available(*, deprecated, message: "AttributedString.Runs.Index should not be used as a Strideable and should instead be offset using the API provided by AttributedString.Runs")
219218
extension AttributedString.Runs.Index: Strideable {
220219
public func distance(to other: Self) -> Int {
221220
// This isn't perfect (since two non-sliced indices might have other sliced runs between them) but checking is better than nothing
222221
precondition(!self._withinDiscontiguous && !other._withinDiscontiguous, "AttributedString.Runs.Index's Strideable conformance may not be used with discontiguous sliced runs")
223222
return other._runOffset - self._runOffset
224223
}
225-
224+
226225
public func advanced(by n: Int) -> Self {
227226
precondition(!self._withinDiscontiguous, "AttributedString.Runs.Index's Strideable conformance may not be used with discontiguous sliced runs")
228227
return Self(_runOffset: self._runOffset + n, withinDiscontiguous: false)
229228
}
230229
}
231-
#endif
232230

233231
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
234232
extension Range<AttributedString.Runs.Index> {
@@ -332,12 +330,18 @@ extension AttributedString.Runs: BidirectionalCollection {
332330
}
333331
}
334332

335-
#if !FOUNDATION_FRAMEWORK
336333
@_alwaysEmitIntoClient
337334
public func distance(from start: Index, to end: Index) -> Int {
335+
#if FOUNDATION_FRAMEWORK
336+
if #available(macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26, *) {
337+
_distance(from: start, to: end)
338+
} else {
339+
start.distance(to: end)
340+
}
341+
#else
338342
_distance(from: start, to: end)
343+
#endif
339344
}
340-
#endif
341345

342346
@available(FoundationPreview 6.2, *)
343347
@usableFromInline

Sources/FoundationEssentials/Calendar/Calendar.swift

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
248248
package static let timeZone = ComponentSet(rawValue: 1 << 15)
249249
package static let isLeapMonth = ComponentSet(rawValue: 1 << 16)
250250
package static let dayOfYear = ComponentSet(rawValue: 1 << 18)
251+
package static let isRepeatedDay = ComponentSet(rawValue: 1 << 19)
251252

252253
package var count: Int {
253254
rawValue.nonzeroBitCount
@@ -272,6 +273,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
272273
if contains(.calendar) { result.insert(.calendar) }
273274
if contains(.timeZone) { result.insert(.timeZone) }
274275
if contains(.isLeapMonth) { result.insert(.isLeapMonth) }
276+
if contains(.isRepeatedDay) { result.insert(.isRepeatedDay) }
275277
if contains(.dayOfYear) { result.insert(.dayOfYear) }
276278
return result
277279
}
@@ -293,8 +295,9 @@ public struct Calendar : Hashable, Equatable, Sendable {
293295
if self.contains(.yearForWeekOfYear) { return .yearForWeekOfYear }
294296
if self.contains(.nanosecond) { return .nanosecond }
295297

296-
// The algorithms that call this function assume that isLeapMonth counts as a 'highest unit set', but the order is after nanosecond.
298+
// The algorithms that call this function assume that isLeapMonth and isRepeatedDay can count as 'highest unit set', but they are ordered after nanosecond.
297299
if self.contains(.isLeapMonth) { return .isLeapMonth }
300+
if self.contains(.isRepeatedDay) { return .isRepeatedDay }
298301

299302
// The calendar and timeZone properties do not count as a 'highest unit set', since they are not ordered in time like the others are.
300303
return nil
@@ -325,7 +328,8 @@ public struct Calendar : Hashable, Equatable, Sendable {
325328
case timeZone
326329
@available(macOS 14, iOS 17, tvOS 17, watchOS 10, *)
327330
case isLeapMonth
328-
331+
@available(FoundationPreview 6.2, *)
332+
case isRepeatedDay
329333
@available(macOS 15, iOS 18, tvOS 18, watchOS 11, *)
330334
case dayOfYear
331335

@@ -349,6 +353,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
349353
case .calendar: return ComponentSet.calendar.rawValue
350354
case .timeZone: return ComponentSet.timeZone.rawValue
351355
case .isLeapMonth: return ComponentSet.isLeapMonth.rawValue
356+
case .isRepeatedDay: return ComponentSet.isRepeatedDay.rawValue
352357
}
353358
}
354359

@@ -372,6 +377,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
372377
case .calendar: "calendar"
373378
case .timeZone: "timeZone"
374379
case .isLeapMonth: "isLeapMonth"
380+
case .isRepeatedDay: "isRepeatedDay"
375381
}
376382
}
377383
}
@@ -694,6 +700,11 @@ public struct Calendar : Hashable, Equatable, Sendable {
694700
return dc
695701
}
696702

703+
/// True if this is a lunisolar calendar that repeats the month number for a leap month, false otherwise.
704+
var hasRepeatingMonths: Bool {
705+
return identifier == .chinese || identifier == .dangi || identifier == .gujarati || identifier == .kannada || identifier == .marathi || identifier == .telugu || identifier == .vietnamese || identifier == .vikram
706+
}
707+
697708
/// Returns all the date components of a date, as if in a given time zone (instead of the `Calendar` time zone).
698709
///
699710
/// The time zone overrides the time zone of the `Calendar` for the purposes of this calculation.
@@ -809,7 +820,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
809820
}
810821

811822
switch component {
812-
case .calendar, .timeZone, .isLeapMonth:
823+
case .calendar, .timeZone, .isLeapMonth, .isRepeatedDay:
813824
return .orderedSame
814825
case .day, .hour:
815826
// Day here so we don't assume that time zone fall back situations don't fall back into a previous day
@@ -902,6 +913,9 @@ public struct Calendar : Hashable, Equatable, Sendable {
902913
let comp1 = self.dateComponents(Set(units), from: date1)
903914
let comp2 = self.dateComponents(Set(units), from: date2)
904915

916+
// check if this is a lunisolar calendar that repeats the day number for a leap day
917+
let hasRepeatingDays = identifier == .gujarati || identifier == .kannada || identifier == .marathi || identifier == .telugu || identifier == .vikram
918+
905919
for c in units {
906920
guard let value1 = comp1.value(for: c), let value2 = comp2.value(for: c) else {
907921
return fallback
@@ -913,7 +927,7 @@ public struct Calendar : Hashable, Equatable, Sendable {
913927
return .orderedAscending
914928
}
915929

916-
if c == .month && identifier == .chinese {
930+
if c == .month && hasRepeatingMonths {
917931
let leap1 = comp1.isLeapMonth ?? false
918932
let leap2 = comp2.isLeapMonth ?? false
919933

@@ -924,6 +938,17 @@ public struct Calendar : Hashable, Equatable, Sendable {
924938
}
925939
}
926940

941+
if c == .day && hasRepeatingDays {
942+
let repeated1 = comp1.isRepeatedDay ?? false
943+
let repeated2 = comp2.isRepeatedDay ?? false
944+
945+
if !repeated1 && repeated2 {
946+
return .orderedAscending
947+
} else if repeated1 && !repeated2 {
948+
return .orderedDescending
949+
}
950+
}
951+
927952
if component == c {
928953
return .orderedSame
929954
}
@@ -1341,6 +1366,12 @@ public struct Calendar : Hashable, Equatable, Sendable {
13411366
comp.isLeapMonth = _dateComponents(.month, from: date).isLeapMonth
13421367
}
13431368

1369+
if components.isRepeatedDay != nil {
1370+
// `isRepeatedDay` isn't part of `actualUnits`, so we have to retrieve
1371+
// it separately
1372+
comp.isRepeatedDay = _dateComponents(.isRepeatedDay, from: date).isRepeatedDay
1373+
}
1374+
13441375
// Apply an epsilon to comparison of nanosecond values
13451376
if let nanosecond = comp.nanosecond, let tempNanosecond = tempComp.nanosecond {
13461377
if labs(CLong(nanosecond - tempNanosecond)) > 500 {

0 commit comments

Comments
 (0)