Skip to content

[Foundation] add additional conformances and functionality to NSRange #9654

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
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: 4 additions & 2 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6089,13 +6089,15 @@ void TypeChecker::checkConformancesInContext(DeclContext *dc,
auto extendedNominal =
diag.ExistingDC->getAsNominalTypeOrNominalTypeExtensionContext();
if (existingModule != dc->getParentModule() &&
(existingModule == extendedNominal->getParentModule() ||
(existingModule->getName() ==
extendedNominal->getParentModule()->getName() ||
existingModule == diag.Protocol->getParentModule())) {
// Warn about the conformance.
diagnose(diag.Loc, diag::redundant_conformance_adhoc,
dc->getDeclaredInterfaceType(),
diag.Protocol->getName(),
existingModule == extendedNominal->getParentModule(),
existingModule->getName() ==
extendedNominal->getParentModule()->getName(),
existingModule->getName());

// Complain about any declarations in this extension whose names match
Expand Down
124 changes: 106 additions & 18 deletions stdlib/public/SDK/Foundation/NSRange.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,121 @@

@_exported import Foundation // Clang module

extension NSRange : Hashable {
public var hashValue: Int {
#if arch(i386) || arch(arm)
return Int(bitPattern: (UInt(bitPattern: location) | (UInt(bitPattern: length) << 16)))
#elseif arch(x86_64) || arch(arm64)
return Int(bitPattern: (UInt(bitPattern: location) | (UInt(bitPattern: length) << 32)))
#endif
}

public static func==(_ lhs: NSRange, _ rhs: NSRange) -> Bool {
return lhs.location == rhs.location && rhs.length == rhs.length
}
}

extension NSRange : CustomStringConvertible, CustomDebugStringConvertible {
public var description: String { return "{\(location), \(length)}" }
public var debugDescription: String { return "{\(location), \(length)}" }
}

extension NSRange {
public init?(_ string: String) {
if string.isEmpty {
// fail early if the string is empty
return nil
}
let scanner = Scanner(string: string)
let digitSet = CharacterSet.decimalDigits
let _ = scanner.scanUpToCharacters(from: digitSet, into: nil)
if scanner.isAtEnd {
// fail early if there are no decimal digits
return nil
}
var location = 0
guard scanner.scanInt(&location) else {
return nil
}
if scanner.isAtEnd {
// return early if there are no more characters after the first int in the string
return nil
}
let _ = scanner.scanUpToCharacters(from: digitSet, into: nil)
if scanner.isAtEnd {
// return early if there are no integer characters after the first int in the string
return nil
}
var length = 0
guard scanner.scanInt(&length) else {
return nil
}

self.location = location
self.length = length
}
}

extension NSRange {
public var lowerBound: Int { return location }

public var upperBound: Int { return location + length }

public func contains(_ index: Int) -> Bool { return (!(index < location) && (index - location) < length) }

public mutating func formUnion(_ other: NSRange) {
self = union(other)
}

public func union(_ other: NSRange) -> NSRange {
let max1 = location + length
let max2 = other.location + other.length
let maxend = (max1 < max2) ? max2 : max1
let minloc = location < other.location ? location : other.location
return NSRange(location: minloc, length: maxend - minloc)
}

public func intersection(_ other: NSRange) -> NSRange? {
let max1 = location + length
let max2 = other.location + other.length
let minend = (max1 < max2) ? max1 : max2
if other.location <= location && location < max2 {
return NSRange(location: location, length: minend - location)
} else if location <= other.location && other.location < max1 {
return NSRange(location: other.location, length: minend - other.location);
}
return nil
}
}


//===----------------------------------------------------------------------===//
// Ranges
//===----------------------------------------------------------------------===//

extension NSRange {
public init(_ x: Range<Int>) {
location = x.lowerBound
length = x.count
}

// FIXME(ABI)#75 (Conditional Conformance): this API should be an extension on Range.
// Can't express it now because the compiler does not support conditional
// extensions with type equality constraints.
public func toRange() -> Range<Int>? {
if location == NSNotFound { return nil }
return location..<(location+length)
}
public init(_ x: Range<Int>) {
location = x.lowerBound
length = x.count
}

// FIXME(ABI)#75 (Conditional Conformance): this API should be an extension on Range.
// Can't express it now because the compiler does not support conditional
// extensions with type equality constraints.
public func toRange() -> Range<Int>? {
Copy link
Collaborator

Choose a reason for hiding this comment

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

While you're at it, might it be appropriate to address this FIXME?

extension Range where Bound == Int {
  public init?(_ x: NSRange) {
    if x.location == NSNotFound { return nil }
    self.init(uncheckedBounds: (x.location, x.location + x.length))
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just a cherry-pick, we can't remove this API without breaking source compatibility.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, sorry, I didn't notice that this is a cherry pick, and I guess I must have missed the initial commit to master.

Not suggesting that you remove the API, just asking whether now's the time when this can be deprecated (on master) and the originally intended initializer added.

Copy link
Member

Choose a reason for hiding this comment

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

I'm going to address that FIXME shortly in another PR...

if location == NSNotFound { return nil }
return location..<(location+length)
}
}

extension NSRange : CustomReflectable {
public var customMirror: Mirror {
return Mirror(self, children: ["location": location, "length": length])
}
public var customMirror: Mirror {
return Mirror(self, children: ["location": location, "length": length])
}
}

extension NSRange : CustomPlaygroundQuickLookable {
public var customPlaygroundQuickLook: PlaygroundQuickLook {
return .range(Int64(location), Int64(length))
}
public var customPlaygroundQuickLook: PlaygroundQuickLook {
return .range(Int64(location), Int64(length))
}
}
1 change: 0 additions & 1 deletion test/Compatibility/bridging-nsnumber-and-nsvalue.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ extension Equatable {
}
}
extension Hashable { public var hashValue: Int { fatalError("trill hiphy") } }
extension NSRange: Hashable {}
extension CGSize: Hashable {}
extension CGPoint: Hashable {}
extension CGRect: Hashable {}
Expand Down
9 changes: 9 additions & 0 deletions test/Compatibility/range_hashable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %target-typecheck-verify-swift -swift-version 3

// REQUIRES: objc_interop

import Foundation

extension NSRange : Hashable { // expected-warning{{conformance of '_NSRange' to protocol 'Hashable' was already stated in the type's module 'Foundation'}}
var hashValue: Int { return 0 } // expected-note{{var 'hashValue' will not be used to satisfy the conformance to 'Hashable'}}
}
2 changes: 0 additions & 2 deletions test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ coercionTypes = {
'CGRect',
'CGPoint',
'CGSize',
'NSRange',
],
}
}%
Expand All @@ -41,7 +40,6 @@ extension Equatable {
}
}
extension Hashable { public var hashValue: Int { fatalError("trill hiphy") } }
extension NSRange: Hashable {}
extension CGSize: Hashable {}
extension CGPoint: Hashable {}
extension CGRect: Hashable {}
Expand Down