Skip to content

Change unicodescalar initializer to failable #3662

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 1 commit into from
Jul 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions stdlib/public/SDK/Foundation/CharacterSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public struct CharacterSet : ReferenceConvertible, Equatable, Hashable, SetAlgeb
///
/// It is the caller's responsibility to ensure that the values represent valid `UnicodeScalar` values, if that is what is desired.
public init(charactersIn range: ClosedRange<UnicodeScalar>) {
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)!
_wrapped = _SwiftNSCharacterSet(immutableObject: NSCharacterSet(range: _utfRangeToNSRange(halfOpenRange)))
}

Expand Down Expand Up @@ -294,7 +294,7 @@ public struct CharacterSet : ReferenceConvertible, Equatable, Hashable, SetAlgeb
///
/// It is the caller's responsibility to ensure that the values represent valid `UnicodeScalar` values, if that is what is desired.
public mutating func insert(charactersIn range: ClosedRange<UnicodeScalar>) {
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)!
let nsRange = _utfRangeToNSRange(halfOpenRange)
_applyUnmanagedMutation {
$0.addCharacters(in: nsRange)
Expand All @@ -311,7 +311,7 @@ public struct CharacterSet : ReferenceConvertible, Equatable, Hashable, SetAlgeb

/// Remove a closed range of integer values from the `CharacterSet`.
public mutating func remove(charactersIn range: ClosedRange<UnicodeScalar>) {
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)
let halfOpenRange = range.lowerBound..<UnicodeScalar(range.upperBound.value + 1)!
let nsRange = _utfRangeToNSRange(halfOpenRange)
_applyUnmanagedMutation {
$0.removeCharacters(in: nsRange)
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StaticString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public struct StaticString
_precondition(
!hasPointerRepresentation,
"StaticString should have Unicode scalar representation")
return UnicodeScalar(UInt32(UInt(_startPtrOrData)))
return UnicodeScalar(UInt32(UInt(_startPtrOrData)))!
}

/// The length in bytes of the static string's ASCII or UTF-8 representation.
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StringCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ public struct _StringCore {
start: UnsafeMutablePointer<UTF8.CodeUnit>(_baseAddress!),
count: count
) {
Encoding.encode(UnicodeScalar(UInt32(x)), into: processCodeUnit)
Encoding.encode(UnicodeScalar(x), into: processCodeUnit)
}
}
else {
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StringUnicodeScalarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ extension String {
case .emptyInput:
_sanityCheckFailure("cannot subscript using an endIndex")
case .error:
return UnicodeScalar(0xfffd)
return UnicodeScalar(0xfffd)!
}
}

Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/Unicode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ extension UTF16 {
return nil
}
isAscii = false
count += width(UnicodeScalar(0xfffd))
count += width(UnicodeScalar(0xfffd)!)
}
}
return (count, isAscii)
Expand Down
90 changes: 64 additions & 26 deletions stdlib/public/core/UnicodeScalar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,28 @@ public struct UnicodeScalar :

/// Creates a Unicode scalar with the specified numeric value.
///
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
/// `0xE000...0x10FFFF`. In case of an invalid unicode scalar value, nil is
/// returned.
///
/// For example, the following code sample creates a `UnicodeScalar` instance
/// with a value of an emoji character:
///
/// let codepoint: UInt32 = 127881
/// let emoji = UnicodeScalar(codepoint)
/// print(emoji)
/// print(emoji!)
/// // Prints "🎉"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add an example of when it can return nil?

///
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
/// `0xE000...0x10FFFF`.
public init(_ v: UInt32) {
/// In case of an invalid input value, nil is returned.
///
/// let codepoint: UInt32 = extValue // This might be an invalid value.
/// if let emoji = UnicodeScalar(codepoint) {
/// print(emoji)
/// } else {
/// // Do something else
/// }
public init?(_ v: UInt32) {
// Unicode 6.3.0:
//
// D9. Unicode codespace: A range of integers from 0 to 10FFFF.
Expand All @@ -85,29 +95,43 @@ public struct UnicodeScalar :
//
// * As a result of this definition, the set of Unicode scalar values
// consists of the ranges 0 to D7FF and E000 to 10FFFF, inclusive.

_precondition(v < 0xD800 || v > 0xDFFF,
"high- and low-surrogate code points are not valid Unicode scalar values")
_precondition(v <= 0x10FFFF, "value is outside of Unicode codespace")

self._value = v
if (v < 0xD800 || v > 0xDFFF) && v <= 0x10FFFF {
self._value = v
return
}
// Return nil in case of invalid unicode scalar value.
return nil
}

/// Creates a Unicode scalar with the specified numeric value.
///
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
/// `0xE000...0xFFFF`. In case of an invalid unicode scalar value, nil is
/// returned.
///
/// For example, the following code sample creates a `UnicodeScalar` instance
/// with a value of `밥`, the Korean word for rice:
///
/// let codepoint: UInt16 = 48165
/// let bap = UnicodeScalar(codepoint)
/// print(bap)
/// print(bap!)
/// // Prints "밥"
///
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
/// a valid Unicode scalar value, in the range `0...0xD7FF` or
/// `0xE000...0xFFFF`.
public init(_ v: UInt16) {
self = UnicodeScalar(UInt32(v))
/// In case an invalid input value, nil is returned.
///
/// let codepoint: UInt32 = extValue // This might be an invalid value.
/// if let bap = UnicodeScalar(codepoint) {
/// print(bap)
/// } else {
/// // Do something else
/// }
public init?(_ v: UInt16) {
if let us = UnicodeScalar(UInt32(v)) {
self = us
} else {
return nil
}
}

/// Creates a Unicode scalar with the specified numeric value.
Expand All @@ -117,12 +141,12 @@ public struct UnicodeScalar :
///
/// let codepoint: UInt8 = 55
/// let seven = UnicodeScalar(codepoint)
/// print(seven)
/// print(seven!)
/// // Prints "7"
///
/// - Parameter v: The code point to use for the scalar.
public init(_ v: UInt8) {
self = UnicodeScalar(UInt32(v))
self._value = UInt32(v)
}

/// Creates a duplicate of the given Unicode scalar.
Expand Down Expand Up @@ -161,11 +185,11 @@ public struct UnicodeScalar :
func lowNibbleAsHex(_ v: UInt32) -> String {
let nibble = v & 15
if nibble < 10 {
return String(UnicodeScalar(nibble+48)) // 48 = '0'
return String(UnicodeScalar(nibble+48)!) // 48 = '0'
} else {
// FIXME: was UnicodeScalar(nibble-10+65), which is now
// ambiguous. <rdar://problem/18506025>
return String(UnicodeScalar(nibble+65-10)) // 65 = 'A'
return String(UnicodeScalar(nibble+65-10)!) // 65 = 'A'
}
}

Expand Down Expand Up @@ -271,6 +295,11 @@ extension UnicodeScalar : Hashable {
extension UnicodeScalar {
/// Creates a Unicode scalar with the specified numeric value.
///
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
/// a valid Unicode scalar value, in the ranges `0...0xD7FF` or
/// `0xE000...0x10FFFF`. In case of an invalid unicode scalar value, nil is
/// returned.
///
/// For example, the following code sample creates a `UnicodeScalar` instance
/// with a value of an emoji character:
///
Expand All @@ -279,11 +308,20 @@ extension UnicodeScalar {
/// print(emoji)
/// // Prints "🎉"
///
/// - Parameter v: The Unicode code point to use for the scalar. `v` must be
/// a valid Unicode scalar value, in the ranges `0...0xD7FF` or
/// `0xE000...0x10FFFF`.
public init(_ v: Int) {
self = UnicodeScalar(UInt32(v))
/// In case an invalid input value, nil is returned.
///
/// let codepoint: UInt32 = extValue // This might be an invalid value.
/// if let emoji = UnicodeScalar(codepoint) {
/// print(emoji)
/// } else {
/// // Do something else
/// }
public init?(_ v: Int) {
if let us = UnicodeScalar(UInt32(v)) {
self = us
} else {
return nil
}
}
}

Expand Down
16 changes: 8 additions & 8 deletions test/1_stdlib/Character.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ CharacterTests.test("RoundTripping/Random") {
CharacterTests.test("forall x: ASCII . String(Character(x)) == String(x)") {
// For all ASCII chars, constructing a Character then a String should be the
// same as constructing a String directly.
let asciiDomain = Array(0..<128).map({ UnicodeScalar(Int($0)) })
let asciiDomain = (0..<128).map({ UnicodeScalar(Int($0))! })
expectEqualFunctionsForDomain(asciiDomain,
{ String($0) },
{ String(Character($0)) })
Expand All @@ -233,8 +233,8 @@ CharacterTests.test(
// For all ASCII chars, constructing a Character then a String should ordered
// the same as constructing a String directly.
let asciiDomain = Array(0..<127)
let ascii0to126 = asciiDomain.map({ UnicodeScalar(Int($0)) })
let ascii1to127 = asciiDomain.map({ UnicodeScalar(Int($0 + 1)) })
let ascii0to126 = asciiDomain.map({ UnicodeScalar(Int($0))! })
let ascii1to127 = asciiDomain.map({ UnicodeScalar(Int($0 + 1))! })
typealias PredicateFn = (UnicodeScalar) -> (UnicodeScalar) -> Bool
expectEqualMethodsForDomain(
ascii0to126,
Expand All @@ -258,7 +258,7 @@ var UnicodeScalarTests = TestSuite("UnicodeScalar")

UnicodeScalarTests.test("UInt8(ascii: UnicodeScalar)") {
for i in 0..<0x7f {
let us = UnicodeScalar(i)
let us = UnicodeScalar(i)!
expectEqual(UInt8(i), UInt8(ascii: us))
}
}
Expand All @@ -281,11 +281,11 @@ UnicodeScalarTests.test("UInt32(_: UnicodeScalar),UInt64(_: UnicodeScalar)") {
}

UnicodeScalarTests.test("isASCII()") {
expectTrue(UnicodeScalar(0).isASCII)
expectTrue(UnicodeScalar(0)!.isASCII)
expectTrue(("A" as UnicodeScalar).isASCII)
expectTrue(UnicodeScalar(127).isASCII)
expectFalse(UnicodeScalar(128).isASCII)
expectFalse(UnicodeScalar(256).isASCII)
expectTrue(UnicodeScalar(127)!.isASCII)
expectFalse(UnicodeScalar(128)!.isASCII)
expectFalse(UnicodeScalar(256)!.isASCII)
}

UnicodeScalarTests.test("Comparable") {
Expand Down
12 changes: 6 additions & 6 deletions test/1_stdlib/PrintInteger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ PrintTests.test("CustomStringConvertible") {
hasDescription(CInt(42))
hasDescription(CLong(42))
hasDescription(CLongLong(42))
hasDescription(CWideChar(42))
hasDescription(CWideChar(42)!)
Copy link
Contributor

@gribozavr gribozavr Jul 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this infer '42' to be a UInt8, and then not even need the unwrapping? (Similarly for other cases below.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will treat it as Int. I had to do hasDescription(CWideChar(UInt8(42))) to get rid of the unwrapping.

hasDescription(CChar16(42))
hasDescription(CChar32(42))
hasDescription(CChar32(42)!)
}

PrintTests.test("Printable") {
Expand All @@ -50,9 +50,9 @@ PrintTests.test("Printable") {
expectPrinted("42", CInt(42))
expectPrinted("42", CLong(42))
expectPrinted("42", CLongLong(42))
expectPrinted("*", CWideChar(42))
expectPrinted("*", CWideChar(42)!)
expectPrinted("42", CChar16(42))
expectPrinted("*", CChar32(42))
expectPrinted("*", CChar32(42)!)

if (UInt64(Int.max) > 0x1_0000_0000 as UInt64) {
expectPrinted("-9223372036854775808", Int.min)
Expand Down Expand Up @@ -141,9 +141,9 @@ PrintTests.test("Printable") {
expectPrinted("42", CLong(42))
expectPrinted("42", CLongLong(42))

expectPrinted("*", CWideChar(42))
expectPrinted("*", CWideChar(42)!)
expectPrinted("42", CChar16(42))
expectPrinted("*", CChar32(42))
expectPrinted("*", CChar32(42)!)
}

runAllTests()
44 changes: 22 additions & 22 deletions test/1_stdlib/TestCharacterSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ class TestCharacterSetSuper { }
#endif

class TestCharacterSet : TestCharacterSetSuper {
let capitalA = UnicodeScalar(0x0041) // LATIN CAPITAL LETTER A
let capitalB = UnicodeScalar(0x0042) // LATIN CAPITAL LETTER B
let capitalC = UnicodeScalar(0x0043) // LATIN CAPITAL LETTER C
let capitalA = UnicodeScalar(0x0041)! // LATIN CAPITAL LETTER A
let capitalB = UnicodeScalar(0x0042)! // LATIN CAPITAL LETTER B
let capitalC = UnicodeScalar(0x0043)! // LATIN CAPITAL LETTER C

func testBasicConstruction() {
// Create a character set
Expand Down Expand Up @@ -87,14 +87,14 @@ class TestCharacterSet : TestCharacterSetSuper {

func testRanges() {
// Simple range check
let asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)...UnicodeScalar(0x5A))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5B)))
let asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!)
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5B)!))

// Some string filtering tests
let asciiLowercase = CharacterSet(charactersIn: UnicodeScalar(0x61)...UnicodeScalar(0x7B))
let asciiLowercase = CharacterSet(charactersIn: UnicodeScalar(0x61)!...UnicodeScalar(0x7B)!)
let testString = "helloHELLOhello"
let expected = "HELLO"

Expand All @@ -103,23 +103,23 @@ class TestCharacterSet : TestCharacterSetSuper {
}

func testInsertAndRemove() {
var asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)...UnicodeScalar(0x5A))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))

asciiUppercase.remove(UnicodeScalar(0x49))
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x49)))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))
var asciiUppercase = CharacterSet(charactersIn: UnicodeScalar(0x41)!...UnicodeScalar(0x5A)!)
expectTrue(asciiUppercase.contains(UnicodeScalar(0x49)!))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))

asciiUppercase.remove(UnicodeScalar(0x49)!)
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x49)!))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x5A)!))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))


// Zero-length range
asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)..<UnicodeScalar(0x41))
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)))
asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)!..<UnicodeScalar(0x41)!)
expectTrue(asciiUppercase.contains(UnicodeScalar(0x41)!))

asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)..<UnicodeScalar(0x42))
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x41)))
asciiUppercase.remove(charactersIn: UnicodeScalar(0x41)!..<UnicodeScalar(0x42)!)
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x41)!))

asciiUppercase.remove(charactersIn: "Z")
expectTrue(!asciiUppercase.contains(UnicodeScalar(0x5A)))
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/default_literals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var d = 3.5
genericFloatingLiteral(d)

extension UInt32 {
func asChar() -> UnicodeScalar { return UnicodeScalar(self) }
func asChar() -> UnicodeScalar { return UnicodeScalar(self)! }
}
var ch = UInt32(65).asChar()

Expand Down
2 changes: 1 addition & 1 deletion test/Interpreter/SDK/Accelerate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extension vU1024: ExpressibleByIntegerLiteral, CustomStringConvertible, Equatabl
var digit: vU1024 = 0
repeat {
(intermediate, digit) = quorem(intermediate, 10)
digits.append(Character(UnicodeScalar(Int(digit) + 48)))
digits.append(Character(UnicodeScalar(Int(digit) + 48)!))
} while intermediate != 0
return String(digits.reversed())
}
Expand Down
Loading