Skip to content

Commit 457b999

Browse files
Add checks that the endpoints of partial ranges are not-NaN. (#33378)
We can't actually check for NaN (because the notion doesn't exist for Comparable), but we can require that the endpoint is non-exceptional by checking if it equals itself.
1 parent 63875e9 commit 457b999

File tree

4 files changed

+59
-3
lines changed

4 files changed

+59
-3
lines changed

stdlib/public/core/ClosedRange.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,12 @@ extension Comparable {
330330
/// - Parameters:
331331
/// - minimum: The lower bound for the range.
332332
/// - maximum: The upper bound for the range.
333+
///
334+
/// - Precondition: `minimum <= maximum`.
333335
@_transparent
334336
public static func ... (minimum: Self, maximum: Self) -> ClosedRange<Self> {
335337
_precondition(
336-
minimum <= maximum, "Can't form Range with upperBound < lowerBound")
338+
minimum <= maximum, "Range requires lowerBound <= upperBound")
337339
return ClosedRange(uncheckedBounds: (lower: minimum, upper: maximum))
338340
}
339341
}

stdlib/public/core/Range.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -723,10 +723,12 @@ extension Comparable {
723723
/// - Parameters:
724724
/// - minimum: The lower bound for the range.
725725
/// - maximum: The upper bound for the range.
726+
///
727+
/// - Precondition: `minimum <= maximum`.
726728
@_transparent
727729
public static func ..< (minimum: Self, maximum: Self) -> Range<Self> {
728730
_precondition(minimum <= maximum,
729-
"Can't form Range with upperBound < lowerBound")
731+
"Range requires lowerBound <= upperBound")
730732
return Range(uncheckedBounds: (lower: minimum, upper: maximum))
731733
}
732734

@@ -752,8 +754,12 @@ extension Comparable {
752754
/// // Prints "[10, 20, 30]"
753755
///
754756
/// - Parameter maximum: The upper bound for the range.
757+
///
758+
/// - Precondition: `maximum` must compare equal to itself (i.e. cannot be NaN).
755759
@_transparent
756760
public static prefix func ..< (maximum: Self) -> PartialRangeUpTo<Self> {
761+
_precondition(maximum == maximum,
762+
"Range cannot have an unordered upper bound.")
757763
return PartialRangeUpTo(maximum)
758764
}
759765

@@ -779,8 +785,12 @@ extension Comparable {
779785
/// // Prints "[10, 20, 30, 40]"
780786
///
781787
/// - Parameter maximum: The upper bound for the range.
788+
///
789+
/// - Precondition: `maximum` must compare equal to itself (i.e. cannot be NaN).
782790
@_transparent
783791
public static prefix func ... (maximum: Self) -> PartialRangeThrough<Self> {
792+
_precondition(maximum == maximum,
793+
"Range cannot have an unordered upper bound.")
784794
return PartialRangeThrough(maximum)
785795
}
786796

@@ -806,8 +816,12 @@ extension Comparable {
806816
/// // Prints "[40, 50, 60, 70]"
807817
///
808818
/// - Parameter minimum: The lower bound for the range.
819+
///
820+
/// - Precondition: `minimum` must compare equal to itself (i.e. cannot be NaN).
809821
@_transparent
810822
public static postfix func ... (minimum: Self) -> PartialRangeFrom<Self> {
823+
_precondition(minimum == minimum,
824+
"Range cannot have an unordered lower bound.")
811825
return PartialRangeFrom(minimum)
812826
}
813827
}

test/stdlib/RangeTraps.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,47 @@ RangeTraps.test("CountablePartialRangeFrom")
7474
_ = it.next()
7575
}
7676

77+
RangeTraps.test("nanLowerBound")
78+
.code {
79+
expectCrashLater()
80+
_ = Double.nan ... 0
81+
}
82+
83+
RangeTraps.test("nanUpperBound")
84+
.code {
85+
expectCrashLater()
86+
_ = 0 ... Double.nan
87+
}
88+
89+
RangeTraps.test("nanLowerBoundPartial")
90+
.code {
91+
expectCrashLater()
92+
_ = Double.nan ..< 0
93+
}
94+
95+
RangeTraps.test("nanUpperBoundPartial")
96+
.code {
97+
expectCrashLater()
98+
_ = 0 ..< Double.nan
99+
}
77100

101+
RangeTraps.test("fromNaN")
102+
.code {
103+
expectCrashLater()
104+
_ = Double.nan...
105+
}
106+
107+
RangeTraps.test("toNaN")
108+
.code {
109+
expectCrashLater()
110+
_ = ..<Double.nan
111+
}
112+
113+
RangeTraps.test("throughNaN")
114+
.code {
115+
expectCrashLater()
116+
_ = ...Double.nan
117+
}
78118

79119
runAllTests()
80120

validation-test/stdlib/Range.swift.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ ${TestSuite}.test("Equatable") {
401401

402402
${TestSuite}.test("'${op}' traps when upperBound < lowerBound")
403403
.crashOutputMatches(_isDebugAssertConfiguration() ?
404-
"Can't form Range with upperBound < lowerBound" : "")
404+
"Range requires lowerBound <= upperBound" : "")
405405
.code {
406406
let _1 = ${Bound}(1)
407407
let _2 = ${Bound}(2)

0 commit comments

Comments
 (0)