Skip to content

Simple operators for character value comparisons. #71749

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

Closed
wants to merge 11 commits into from
Closed
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
70 changes: 70 additions & 0 deletions stdlib/public/core/UnicodeScalar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,15 @@ extension UInt8 {
"Code point value does not fit into ASCII")
self = UInt8(v.value)
}
/// Initializes a UInt8 with the ASCII value of the provided Unicode scalar if possible.
///
/// - Parameter unicode: The Unicode scalar to initialize from.
/// - Note: Construct with value `v.value`.
@inlinable @_alwaysEmitIntoClient
public init?(ifASCII v: Unicode.Scalar) {
guard v.value < 128 else { return nil }
self = Self(v.value)
}
}
extension UInt32 {
/// Construct with value `v.value`.
Expand All @@ -380,6 +389,67 @@ extension UInt64 {
}
}

/// Extends `Optional<UInt8>` to allow direct comparisons with double quoted literals.
extension UInt8? {
/// Returns a Boolean value indicating whether the optional `UInt8` is equal to the provided Unicode scalar.
///
/// - Parameters:
/// - i: The optional `UInt8` value to compare.
/// - s: The Unicode scalar to compare against.
/// - Returns: `true` if the optional `UInt8` is equal to the provided Unicode scalar; otherwise, `false`.
@_transparent @_alwaysEmitIntoClient
public static func == (i: Self, s: Unicode.Scalar) -> Bool {
return i == UInt8(ifASCII: s)
}

/// Returns a Boolean value indicating whether the optional `UInt8` is not equal to the provided Unicode scalar.
@_transparent @_alwaysEmitIntoClient
public static func != (i: Self, s: Unicode.Scalar) -> Bool {
return i != UInt8(ifASCII: s)
}
/// Returns a Boolean value indicating whether the optional `UInt8` is equal to the provided Unicode scalar.
///
/// - Parameters:
/// - s: The Unicode scalar to compare against.
/// - i: The optional `UInt8` value to compare.
/// - Returns: `true` if the optional `UInt8` is equal to the provided Unicode scalar; otherwise, `false`.
@_transparent @_alwaysEmitIntoClient
public static func == (s: Unicode.Scalar, i: Self) -> Bool {
return i == UInt8(ifASCII: s)
}

/// Returns a Boolean value indicating whether the optional `UInt8` is not equal to the provided Unicode scalar.
@_transparent @_alwaysEmitIntoClient
public static func != (s: Unicode.Scalar, i: Self) -> Bool {
return i != UInt8(ifASCII: s)
}

/// Allows pattern matching of Unicode scalars in switch statements.
@_transparent @_alwaysEmitIntoClient
public static func ~= (s: Unicode.Scalar, i: Self) -> Bool {
return i == UInt8(ifASCII: s)
}
}

/// Extends `FixedWidthInteger` providing initialization from a Unicode scalar.
extension FixedWidthInteger {
/// Initializes a FixedWidthInteger with the value of the provided Unicode scalar.
///
/// - Parameter unicode: The Unicode scalar to initialize from.
/// - Note: Construct with value `v.value`.
@inlinable @_alwaysEmitIntoClient
public init?(unicode v: Unicode.Scalar) {
guard v.value <= Self.max else { return nil }
self = Self(v.value)
}
/// Converts an ASCII value into a UnicodeScalar
@inlinable @_alwaysEmitIntoClient
public var asciiScalar: Unicode.Scalar? {
guard self < 128 && self >= 0 else { return nil }
return Unicode.Scalar(UInt8(self))
}
}

extension Unicode.Scalar: Equatable {
@inlinable
public static func == (lhs: Unicode.Scalar, rhs: Unicode.Scalar) -> Bool {
Expand Down
6 changes: 2 additions & 4 deletions test/Constraints/patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -631,9 +631,7 @@ func testExprPatternIsolation() {
}
for case 0 in nil {} // expected-error {{'nil' requires a contextual type}}
for case 0 in [nil] {}
// expected-error@-1 {{type 'Any' cannot conform to 'Equatable'}}
// expected-note@-2 {{only concrete types such as structs, enums and classes can conform to protocols}}
// expected-note@-3 {{requirement from conditional conformance of 'Any?' to 'Equatable'}}
// expected-error@-1 {{expression pattern of type 'Int' cannot match values of type 'Any?'}}

// Though we will try Double for an integer literal...
let d: Double = 0
Expand Down Expand Up @@ -679,7 +677,7 @@ func testExprPatternIsolation() {

// FIXME: Bad error (https://github.com/apple/swift/issues/64279)
if case .e(nil, 0) = produceMultiPayload() {}
// expected-error@-1 {{expression pattern of type 'String' cannot match values of type 'Substring'}}
// expected-error@-1 {{expression pattern of type 'Unicode.Scalar' cannot match values of type 'UInt8'}}
// expected-note@-2 {{overloads for '~=' exist with these partially matching parameter lists}}

if case .e(5, nil) = produceMultiPayload() as MultiPayload<Int?> {}
Expand Down
4 changes: 2 additions & 2 deletions test/Constraints/rdar105782480.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ func foo(value: MyEnum) {
takeClosure {
switch value {
case .second(let drag).invalid:
// expected-error@-1 {{value of type 'MyEnum' has no member 'invalid'}}
// expected-error@-2 {{'let' binding pattern cannot appear in an expression}}
// expected-error@-1 {{expression pattern of type 'Unicode.Scalar' cannot match values of type 'MyEnum'}}
// expected-error@-2 {{type 'Unicode.Scalar' has no member 'second'}}
break
}
}
Expand Down
64 changes: 64 additions & 0 deletions test/Misc/character_ops.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// RUN: %target-run-simple-swift | %FileCheck %s
// REQUIRES: executable_test

let c = UInt8(ascii: "a")

// CHECK: OK
if c == "a" {
print("OK")
}

// CHECK: OK
if c != "b" {
print("OK")
}

// CHECK: OK
switch c {
case "a":
print("OK")
case "b":
fallthrough
default:
print("FAIL")
}

let d: UInt8? = UInt8(ascii: "a")

// CHECK: OK
if d == "a" {
print("OK")
}

// CHECK: OK
if d != "b" {
print("OK")
}

// CHECK: OK
switch d {
case "a":
print("OK")
case "b":
fallthrough
default:
print("FAIL")
}

// CHECK: OK
switch d?.asciiScalar {
case "a":
print("OK")
case "b":
fallthrough
default:
print("FAIL")
}

// CHECK: OK
if UInt8(unicode: "a") == "a" {
print("OK")
}

// CHECK: 128512
print(UInt64(unicode: "😀"))
1 change: 1 addition & 0 deletions test/abi/Inputs/macOS/arm64/stdlib/baseline-asserts
Original file line number Diff line number Diff line change
Expand Up @@ -7042,6 +7042,7 @@ _$ss17FixedWidthIntegerPSzTb
_$ss17FixedWidthIntegerPs25LosslessStringConvertibleTb
_$ss17FixedWidthIntegerPsE03bitB0Sivg
_$ss17FixedWidthIntegerPsE03bitB0SivpMV
_$ss17FixedWidthIntegerPsE11asciiScalars7UnicodeO0E0VSgvpMV
_$ss17FixedWidthIntegerPsE12littleEndianxvg
_$ss17FixedWidthIntegerPsE12littleEndianxvpMV
_$ss17FixedWidthIntegerPsE12littleEndianxx_tcfC
Expand Down
4 changes: 2 additions & 2 deletions test/stmt/typed_throws.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ func testDoCatchMultiErrorType() {
do {
try doSomething()
try doHomework()
} catch .failed { // expected-error{{type 'any Error' has no member 'failed'}}

} catch .failed { // expected-error{{expression pattern of type 'Unicode.Scalar' cannot match values of type 'any Error}}
// expected-error@-1{{type 'Unicode.Scalar' has no member 'failed'}}
} catch {
let _: Int = error // expected-error{{cannot convert value of type 'any Error' to specified type 'Int'}}
}
Expand Down