Skip to content

Commit 9709b0e

Browse files
authored
Merge pull request #3662 from trentxintong/unicodescalar
SE-0128 - Change unicodescalar initializer to failable
2 parents 14601ba + bbf8686 commit 9709b0e

18 files changed

+134
-95
lines changed

Diff for: stdlib/public/SDK/Foundation/CharacterSet.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public struct CharacterSet : ReferenceConvertible, Equatable, Hashable, SetAlgeb
103103
///
104104
/// It is the caller's responsibility to ensure that the values represent valid `UnicodeScalar` values, if that is what is desired.
105105
public init(charactersIn range: ClosedRange<UnicodeScalar>) {
106-
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)
106+
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)!
107107
_wrapped = _SwiftNSCharacterSet(immutableObject: NSCharacterSet(range: _utfRangeToNSRange(halfOpenRange)))
108108
}
109109

@@ -299,7 +299,7 @@ public struct CharacterSet : ReferenceConvertible, Equatable, Hashable, SetAlgeb
299299
///
300300
/// It is the caller's responsibility to ensure that the values represent valid `UnicodeScalar` values, if that is what is desired.
301301
public mutating func insert(charactersIn range: ClosedRange<UnicodeScalar>) {
302-
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)
302+
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)!
303303
let nsRange = _utfRangeToNSRange(halfOpenRange)
304304
_applyUnmanagedMutation {
305305
$0.addCharacters(in: nsRange)
@@ -316,7 +316,7 @@ public struct CharacterSet : ReferenceConvertible, Equatable, Hashable, SetAlgeb
316316

317317
/// Remove a closed range of integer values from the `CharacterSet`.
318318
public mutating func remove(charactersIn range: ClosedRange<UnicodeScalar>) {
319-
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)
319+
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)!
320320
let nsRange = _utfRangeToNSRange(halfOpenRange)
321321
_applyUnmanagedMutation {
322322
$0.removeCharacters(in: nsRange)

Diff for: stdlib/public/core/StaticString.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public struct StaticString
7676
_precondition(
7777
!hasPointerRepresentation,
7878
"StaticString should have Unicode scalar representation")
79-
return UnicodeScalar(UInt32(UInt(_startPtrOrData)))
79+
return UnicodeScalar(UInt32(UInt(_startPtrOrData)))!
8080
}
8181

8282
/// The length in bytes of the static string's ASCII or UTF-8 representation.

Diff for: stdlib/public/core/StringCore.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ public struct _StringCore {
339339
start: UnsafeMutablePointer<UTF8.CodeUnit>(_baseAddress!),
340340
count: count
341341
) {
342-
Encoding.encode(UnicodeScalar(UInt32(x)), into: processCodeUnit)
342+
Encoding.encode(UnicodeScalar(x), into: processCodeUnit)
343343
}
344344
}
345345
else {

Diff for: stdlib/public/core/StringUnicodeScalarView.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ extension String {
174174
case .emptyInput:
175175
_sanityCheckFailure("cannot subscript using an endIndex")
176176
case .error:
177-
return UnicodeScalar(0xfffd)
177+
return UnicodeScalar(0xfffd)!
178178
}
179179
}
180180

Diff for: stdlib/public/core/Unicode.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,7 @@ extension UTF16 {
10971097
return nil
10981098
}
10991099
isAscii = false
1100-
count += width(UnicodeScalar(0xfffd))
1100+
count += width(UnicodeScalar(0xfffd)!)
11011101
}
11021102
}
11031103
return (count, isAscii)

Diff for: stdlib/public/core/UnicodeScalar.swift

+64-26
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,28 @@ public struct UnicodeScalar :
6464

6565
/// Creates a Unicode scalar with the specified numeric value.
6666
///
67+
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
68+
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
69+
/// `0xE000...0x10FFFF`. In case of an invalid unicode scalar value, nil is
70+
/// returned.
71+
///
6772
/// For example, the following code sample creates a `UnicodeScalar` instance
6873
/// with a value of an emoji character:
6974
///
7075
/// let codepoint: UInt32 = 127881
7176
/// let emoji = UnicodeScalar(codepoint)
72-
/// print(emoji)
77+
/// print(emoji!)
7378
/// // Prints "🎉"
7479
///
75-
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
76-
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
77-
/// `0xE000...0x10FFFF`.
78-
public init(_ v: UInt32) {
80+
/// In case of an invalid input value, nil is returned.
81+
///
82+
/// let codepoint: UInt32 = extValue // This might be an invalid value.
83+
/// if let emoji = UnicodeScalar(codepoint) {
84+
/// print(emoji)
85+
/// } else {
86+
/// // Do something else
87+
/// }
88+
public init?(_ v: UInt32) {
7989
// Unicode 6.3.0:
8090
//
8191
// D9. Unicode codespace: A range of integers from 0 to 10FFFF.
@@ -85,29 +95,43 @@ public struct UnicodeScalar :
8595
//
8696
// * As a result of this definition, the set of Unicode scalar values
8797
// consists of the ranges 0 to D7FF and E000 to 10FFFF, inclusive.
88-
89-
_precondition(v < 0xD800 || v > 0xDFFF,
90-
"high- and low-surrogate code points are not valid Unicode scalar values")
91-
_precondition(v <= 0x10FFFF, "value is outside of Unicode codespace")
92-
93-
self._value = v
98+
if (v < 0xD800 || v > 0xDFFF) && v <= 0x10FFFF {
99+
self._value = v
100+
return
101+
}
102+
// Return nil in case of invalid unicode scalar value.
103+
return nil
94104
}
95105

96106
/// Creates a Unicode scalar with the specified numeric value.
97107
///
108+
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
109+
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
110+
/// `0xE000...0xFFFF`. In case of an invalid unicode scalar value, nil is
111+
/// returned.
112+
///
98113
/// For example, the following code sample creates a `UnicodeScalar` instance
99114
/// with a value of `밥`, the Korean word for rice:
100115
///
101116
/// let codepoint: UInt16 = 48165
102117
/// let bap = UnicodeScalar(codepoint)
103-
/// print(bap)
118+
/// print(bap!)
104119
/// // Prints "밥"
105120
///
106-
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
107-
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
108-
/// `0xE000...0xFFFF`.
109-
public init(_ v: UInt16) {
110-
self = UnicodeScalar(UInt32(v))
121+
/// In case an invalid input value, nil is returned.
122+
///
123+
/// let codepoint: UInt32 = extValue // This might be an invalid value.
124+
/// if let bap = UnicodeScalar(codepoint) {
125+
/// print(bap)
126+
/// } else {
127+
/// // Do something else
128+
/// }
129+
public init?(_ v: UInt16) {
130+
if let us = UnicodeScalar(UInt32(v)) {
131+
self = us
132+
} else {
133+
return nil
134+
}
111135
}
112136

113137
/// Creates a Unicode scalar with the specified numeric value.
@@ -117,12 +141,12 @@ public struct UnicodeScalar :
117141
///
118142
/// let codepoint: UInt8 = 55
119143
/// let seven = UnicodeScalar(codepoint)
120-
/// print(seven)
144+
/// print(seven!)
121145
/// // Prints "7"
122146
///
123147
/// - Parameter v: The code point to use for the scalar.
124148
public init(_ v: UInt8) {
125-
self = UnicodeScalar(UInt32(v))
149+
self._value = UInt32(v)
126150
}
127151

128152
/// Creates a duplicate of the given Unicode scalar.
@@ -161,11 +185,11 @@ public struct UnicodeScalar :
161185
func lowNibbleAsHex(_ v: UInt32) -> String {
162186
let nibble = v & 15
163187
if nibble < 10 {
164-
return String(UnicodeScalar(nibble+48)) // 48 = '0'
188+
return String(UnicodeScalar(nibble+48)!) // 48 = '0'
165189
} else {
166190
// FIXME: was UnicodeScalar(nibble-10+65), which is now
167191
// ambiguous. <rdar://problem/18506025>
168-
return String(UnicodeScalar(nibble+65-10)) // 65 = 'A'
192+
return String(UnicodeScalar(nibble+65-10)!) // 65 = 'A'
169193
}
170194
}
171195

@@ -271,6 +295,11 @@ extension UnicodeScalar : Hashable {
271295
extension UnicodeScalar {
272296
/// Creates a Unicode scalar with the specified numeric value.
273297
///
298+
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
299+
/// a valid Unicode scalar value, in the ranges `0...0xD7FF` or
300+
/// `0xE000...0x10FFFF`. In case of an invalid unicode scalar value, nil is
301+
/// returned.
302+
///
274303
/// For example, the following code sample creates a `UnicodeScalar` instance
275304
/// with a value of an emoji character:
276305
///
@@ -279,11 +308,20 @@ extension UnicodeScalar {
279308
/// print(emoji)
280309
/// // Prints "🎉"
281310
///
282-
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
283-
/// a valid Unicode scalar value, in the ranges `0...0xD7FF` or
284-
/// `0xE000...0x10FFFF`.
285-
public init(_ v: Int) {
286-
self = UnicodeScalar(UInt32(v))
311+
/// In case an invalid input value, nil is returned.
312+
///
313+
/// let codepoint: UInt32 = extValue // This might be an invalid value.
314+
/// if let emoji = UnicodeScalar(codepoint) {
315+
/// print(emoji)
316+
/// } else {
317+
/// // Do something else
318+
/// }
319+
public init?(_ v: Int) {
320+
if let us = UnicodeScalar(UInt32(v)) {
321+
self = us
322+
} else {
323+
return nil
324+
}
287325
}
288326
}
289327

Diff for: test/1_stdlib/Character.swift

+8-8
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ CharacterTests.test("RoundTripping/Random") {
222222
CharacterTests.test("forall x: ASCII . String(Character(x)) == String(x)") {
223223
// For all ASCII chars, constructing a Character then a String should be the
224224
// same as constructing a String directly.
225-
let asciiDomain = Array(0..<128).map({ UnicodeScalar(Int($0)) })
225+
let asciiDomain = (0..<128).map({ UnicodeScalar(Int($0))! })
226226
expectEqualFunctionsForDomain(asciiDomain,
227227
{ String($0) },
228228
{ String(Character($0)) })
@@ -233,8 +233,8 @@ CharacterTests.test(
233233
// For all ASCII chars, constructing a Character then a String should ordered
234234
// the same as constructing a String directly.
235235
let asciiDomain = Array(0..<127)
236-
let ascii0to126 = asciiDomain.map({ UnicodeScalar(Int($0)) })
237-
let ascii1to127 = asciiDomain.map({ UnicodeScalar(Int($0 + 1)) })
236+
let ascii0to126 = asciiDomain.map({ UnicodeScalar(Int($0))! })
237+
let ascii1to127 = asciiDomain.map({ UnicodeScalar(Int($0 + 1))! })
238238
typealias PredicateFn = (UnicodeScalar) -> (UnicodeScalar) -> Bool
239239
expectEqualMethodsForDomain(
240240
ascii0to126,
@@ -258,7 +258,7 @@ var UnicodeScalarTests = TestSuite("UnicodeScalar")
258258

259259
UnicodeScalarTests.test("UInt8(ascii: UnicodeScalar)") {
260260
for i in 0..<0x7f {
261-
let us = UnicodeScalar(i)
261+
let us = UnicodeScalar(i)!
262262
expectEqual(UInt8(i), UInt8(ascii: us))
263263
}
264264
}
@@ -281,11 +281,11 @@ UnicodeScalarTests.test("UInt32(_: UnicodeScalar),UInt64(_: UnicodeScalar)") {
281281
}
282282

283283
UnicodeScalarTests.test("isASCII()") {
284-
expectTrue(UnicodeScalar(0).isASCII)
284+
expectTrue(UnicodeScalar(0)!.isASCII)
285285
expectTrue(("A" as UnicodeScalar).isASCII)
286-
expectTrue(UnicodeScalar(127).isASCII)
287-
expectFalse(UnicodeScalar(128).isASCII)
288-
expectFalse(UnicodeScalar(256).isASCII)
286+
expectTrue(UnicodeScalar(127)!.isASCII)
287+
expectFalse(UnicodeScalar(128)!.isASCII)
288+
expectFalse(UnicodeScalar(256)!.isASCII)
289289
}
290290

291291
UnicodeScalarTests.test("Comparable") {

Diff for: test/1_stdlib/PrintInteger.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ PrintTests.test("CustomStringConvertible") {
3333
hasDescription(CInt(42))
3434
hasDescription(CLong(42))
3535
hasDescription(CLongLong(42))
36-
hasDescription(CWideChar(42))
36+
hasDescription(CWideChar(42)!)
3737
hasDescription(CChar16(42))
38-
hasDescription(CChar32(42))
38+
hasDescription(CChar32(42)!)
3939
}
4040

4141
PrintTests.test("Printable") {
@@ -50,9 +50,9 @@ PrintTests.test("Printable") {
5050
expectPrinted("42", CInt(42))
5151
expectPrinted("42", CLong(42))
5252
expectPrinted("42", CLongLong(42))
53-
expectPrinted("*", CWideChar(42))
53+
expectPrinted("*", CWideChar(42)!)
5454
expectPrinted("42", CChar16(42))
55-
expectPrinted("*", CChar32(42))
55+
expectPrinted("*", CChar32(42)!)
5656

5757
if (UInt64(Int.max) > 0x1_0000_0000 as UInt64) {
5858
expectPrinted("-9223372036854775808", Int.min)
@@ -141,9 +141,9 @@ PrintTests.test("Printable") {
141141
expectPrinted("42", CLong(42))
142142
expectPrinted("42", CLongLong(42))
143143

144-
expectPrinted("*", CWideChar(42))
144+
expectPrinted("*", CWideChar(42)!)
145145
expectPrinted("42", CChar16(42))
146-
expectPrinted("*", CChar32(42))
146+
expectPrinted("*", CChar32(42)!)
147147
}
148148

149149
runAllTests()

Diff for: test/1_stdlib/TestCharacterSet.swift

+22-22
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ class TestCharacterSetSuper { }
2121
#endif
2222

2323
class TestCharacterSet : TestCharacterSetSuper {
24-
let capitalA = UnicodeScalar(0x0041) // LATIN CAPITAL LETTER A
25-
let capitalB = UnicodeScalar(0x0042) // LATIN CAPITAL LETTER B
26-
let capitalC = UnicodeScalar(0x0043) // LATIN CAPITAL LETTER C
24+
let capitalA = UnicodeScalar(0x0041)! // LATIN CAPITAL LETTER A
25+
let capitalB = UnicodeScalar(0x0042)! // LATIN CAPITAL LETTER B
26+
let capitalC = UnicodeScalar(0x0043)! // LATIN CAPITAL LETTER C
2727

2828
func testBasicConstruction() {
2929
// Create a character set
@@ -87,14 +87,14 @@ class TestCharacterSet : TestCharacterSetSuper {
8787

8888
func testRanges() {
8989
// Simple range check
90-
let asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)...UnicodeScalar(0x5A))
91-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)))
92-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)))
93-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))
94-
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5B)))
90+
let asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!)
91+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!))
92+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
93+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
94+
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5B)!))
9595

9696
// Some string filtering tests
97-
let asciiLowercase = CharacterSet(charactersIn: UnicodeScalar(0x61)...UnicodeScalar(0x7B))
97+
let asciiLowercase = CharacterSet(charactersIn: UnicodeScalar(0x61)!...UnicodeScalar(0x7B)!)
9898
let testString = "helloHELLOhello"
9999
let expected = "HELLO"
100100

@@ -103,23 +103,23 @@ class TestCharacterSet : TestCharacterSetSuper {
103103
}
104104

105105
func testInsertAndRemove() {
106-
var asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)...UnicodeScalar(0x5A))
107-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)))
108-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)))
109-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))
110-
111-
asciiUppercase.remove(UnicodeScalar(0x49))
112-
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x49)))
113-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)))
114-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))
106+
var asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!)
107+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!))
108+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
109+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
110+
111+
asciiUppercase.remove(UnicodeScalar(0x49)!)
112+
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x49)!))
113+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
114+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
115115

116116

117117
// Zero-length range
118-
asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)..<UnicodeScalar(0x41))
119-
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))
118+
asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)!..<UnicodeScalar(0x41)!)
119+
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
120120

121-
asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)..<UnicodeScalar(0x42))
122-
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x41)))
121+
asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)!..<UnicodeScalar(0x42)!)
122+
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x41)!))
123123

124124
asciiUppercase.remove(charactersIn: "Z")
125125
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5A)))

Diff for: test/Constraints/default_literals.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var d = 3.5
2727
genericFloatingLiteral(d)
2828

2929
extension UInt32 {
30-
func asChar() -> UnicodeScalar { return UnicodeScalar(self) }
30+
func asChar() -> UnicodeScalar { return UnicodeScalar(self)! }
3131
}
3232
var ch = UInt32(65).asChar()
3333

Diff for: test/Interpreter/SDK/Accelerate.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension vU1024: ExpressibleByIntegerLiteral, CustomStringConvertible, Equatabl
2626
var digit: vU1024 = 0
2727
repeat {
2828
(intermediate, digit) = quorem(intermediate, 10)
29-
digits.append(Character(UnicodeScalar(Int(digit) + 48)))
29+
digits.append(Character(UnicodeScalar(Int(digit) + 48)!))
3030
} while intermediate != 0
3131
return String(digits.reversed())
3232
}

0 commit comments

Comments
 (0)