Skip to content

[stdlib][SE-0089] Finish off Lossless String Conversion #3761

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 3 commits into from
Jul 29, 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
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public func run_ObjectiveCBridgeFromNSStringForced(_ N: Int) {

@inline(never)
func testObjectiveCBridgeToNSString() {
let nativeString = String("Native")
let nativeString = "Native"

var s: NSString?
for _ in 0 ..< 10_000 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ extension TestSuite {
return makeCollectionOfEquatable(elements.map(wrapValueIntoEquatable))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===------------------------------------------------------------------===//
// generate()
Expand Down Expand Up @@ -1209,7 +1209,7 @@ extension TestSuite {
return makeCollection(elements.map(wrapValue))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

// FIXME: swift-3-indexing-model - add tests for the follow?
// index(before: of i: Index) -> Index
Expand Down Expand Up @@ -1522,7 +1522,7 @@ extension TestSuite {

addBidirectionalCollectionTests(${forwardTestArgs})

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

func makeWrappedCollection(_ elements: [OpaqueValue<Int>]) -> C {
return makeCollection(elements.map(wrapValue))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ extension TestSuite {
return makeCollectionOfComparable(elements.map(wrapValueIntoComparable))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===----------------------------------------------------------------------===//
// subscript(_: Index)
Expand Down Expand Up @@ -749,7 +749,7 @@ self.test("\(testNamePrefix).partition/InvalidOrderings") {
return makeCollection(elements.map(wrapValue))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===----------------------------------------------------------------------===//
// subscript(_: Index)
Expand Down Expand Up @@ -906,7 +906,7 @@ self.test("\(testNamePrefix).partition/DispatchesThrough_withUnsafeMutableBuffer
return makeCollectionOfComparable(elements.map(wrapValueIntoComparable))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===----------------------------------------------------------------------===//
// sort()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ extension TestSuite {
return makeCollection(elements.map(wrapValue))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===----------------------------------------------------------------------===//
// init()
Expand Down Expand Up @@ -1218,7 +1218,7 @@ self.test("\(testNamePrefix).OperatorPlus") {
return makeCollection(elements.map(wrapValue))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===----------------------------------------------------------------------===//
// removeLast()
Expand Down Expand Up @@ -1340,7 +1340,7 @@ self.test("\(testNamePrefix).removeLast(n: Int)/whereIndexIsBidirectional/remove
resiliencyChecks: resiliencyChecks,
outOfBoundsIndexOffset: outOfBoundsIndexOffset)

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

// No extra checks for collections with random access traversal so far.
} // addRangeReplaceableRandomAccessCollectionTests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extension TestSuite {
return makeCollection(elements.map(wrapValue))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===------------------------------------------------------------------===//
// removeFirst()
Expand Down Expand Up @@ -210,7 +210,7 @@ extension TestSuite {
return makeCollection(elements.map(wrapValue))
}

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

//===------------------------------------------------------------------===//
// removeLast()
Expand Down Expand Up @@ -351,7 +351,7 @@ extension TestSuite {
resiliencyChecks: resiliencyChecks,
outOfBoundsIndexOffset: outOfBoundsIndexOffset)

testNamePrefix += String(C.Type.self)
testNamePrefix += String(describing: C.Type.self)

// No tests yet.
} // addRangeReplaceableRandomAccessSliceTests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,7 @@ extension TestSuite {
return makeSequenceOfEquatable(elements.map(wrapValueIntoEquatable))
}

testNamePrefix += String(S.Type.self)
testNamePrefix += String(describing: S.Type.self)

let isMultiPass = makeSequence([])
._preprocessingPass { true } ?? false
Expand Down
2 changes: 1 addition & 1 deletion stdlib/private/StdlibUnittest/StdlibCoreExtras.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public func == (lhs: TypeIdentifier, rhs: TypeIdentifier) -> Bool {
extension TypeIdentifier
: CustomStringConvertible, CustomDebugStringConvertible {
public var description: String {
return String(value)
return String(describing: value)
}
public var debugDescription: String {
return "TypeIdentifier(\(description))"
Expand Down
6 changes: 6 additions & 0 deletions stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -2215,6 +2215,12 @@ public func checkStrideable<Instances : Collection, Strides : Collection>(
}
}

public func checkLosslessStringConvertible<Instance>(
_ instances: [Instance]
) where Instance : LosslessStringConvertible & Equatable {
expectEqualFunctionsForDomain(instances, { $0 }, { Instance(String($0))! })
}

public func nthIndex<C: Collection>(_ x: C, _ n: Int) -> C.Index {
return x.index(x.startIndex, offsetBy: numericCast(n))
}
Expand Down
2 changes: 1 addition & 1 deletion stdlib/private/StdlibUnittest/StringConvertible.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ extension CustomPrintableValue : CustomDebugStringConvertible {
public func expectPrinted<T>(
expectedOneOf patterns: [String], _ object: T, ${TRACE}
) {
let actual = String(object)
let actual = String(describing: object)
if !patterns.contains(actual) {
expectationFailure(
"expected: any of \(String(reflecting: patterns))\n"
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/AnyHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ extension AnyHashable : Hashable {

extension AnyHashable : CustomStringConvertible {
public var description: String {
return String(base)
return String(describing: base)
}
}

Expand Down
12 changes: 12 additions & 0 deletions stdlib/public/core/Bool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@ extension Bool : Equatable, Hashable {
}
}

extension Bool : LosslessStringConvertible {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please make sure we have tests for this. It would be best if you could add a utility to StdlibUnittest that could test any LosslessStringConvertible and Equatable type, by accepting a list of instances.

public init?(_ description: String) {
if description == "true" {
self = true
} else if description == "false" {
self = false
} else {
return nil
}
}
}

//===----------------------------------------------------------------------===//
// Operators
//===----------------------------------------------------------------------===//
Expand Down
8 changes: 8 additions & 0 deletions stdlib/public/core/Character.swift
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@ public struct Character :
internal var _representation: Representation
}

extension Character : CustomStringConvertible {
public var description: String {
return String(describing: self)
}
}

extension Character : LosslessStringConvertible {}

extension Character : CustomDebugStringConvertible {
/// A textual representation of the character, suitable for debugging.
public var debugDescription: String {
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/FloatingPointParsing.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ internal func _isspace_clocale(_ u: UTF16.CodeUnit) -> Bool {
% end

//===--- Parsing ----------------------------------------------------------===//
extension ${Self} {
extension ${Self} : LosslessStringConvertible {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have tests for every new conformance to LosslessStringConvertible? We should.

/// Construct from an ASCII representation.
///
/// Returns the result of calling the POSIX function
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/ImplicitlyUnwrappedOptional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension ImplicitlyUnwrappedOptional : CustomStringConvertible {
public var description: String {
switch self {
case .some(let value):
return String(value)
return String(describing: value)
case .none:
return "nil"
}
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/Integers.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ public protocol SignedInteger_ : BinaryInteger, SignedArithmetic {

extension SignedInteger_ {
public var description: String {
let base = String(magnitude)
let base = String(describing: magnitude)
return self < 0 ? "-" + base : base
}

Expand Down
6 changes: 3 additions & 3 deletions stdlib/public/core/Mirror.swift
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ extension String {
/// }
///
/// let p = Point(x: 21, y: 30)
/// print(String(p))
/// print(String(describing: p))
/// // Prints "Point(x: 21, y: 30)"
///
/// After adding `CustomStringConvertible` conformance by implementing the
Expand All @@ -867,11 +867,11 @@ extension String {
/// }
/// }
///
/// print(String(p))
/// print(String(describing: p))
/// // Prints "(21, 30)"
///
/// - SeeAlso: `String.init<Subject>(reflecting: Subject)`
public init<Subject>(_ instance: Subject) {
public init<Subject>(describing instance: Subject) {
self.init()
_print_unlocked(instance, &self)
}
Expand Down
14 changes: 14 additions & 0 deletions stdlib/public/core/OutputStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,20 @@ public protocol CustomStringConvertible {
var description: String { get }
}

/// A type that can be represented as a string in a lossless, unambiguous way.
///
/// For example, the integer value 1050 can be represented in its entirety as
/// the string "1050".
///
/// The description property of a conforming type must be a value-preserving
/// representation of the original value. As such, it should be possible to
/// re-create an instance from its string representation.
public protocol LosslessStringConvertible : CustomStringConvertible {
/// Instantiates an instance of the conforming type from a string
/// representation.
init?(_ description: String)
}

/// A type with a customized textual representation suitable for debugging
/// purposes.
///
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 @@ -255,7 +255,7 @@ public struct StaticString

extension StaticString {
public var customMirror: Mirror {
return Mirror(reflecting: String(self))
return Mirror(reflecting: description)
}
}

Expand Down
18 changes: 18 additions & 0 deletions stdlib/public/core/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,24 @@ extension String {
return _nativeUnicodeUppercaseString(self)
#endif
}

/// Creates an instance from the description of a given
/// `LosslessStringConvertible` instance.
public init<T : LosslessStringConvertible>(_ value: T) {
self = value.description
}
}

extension String : CustomStringConvertible {
public var description: String {
return self
}
}

extension String : LosslessStringConvertible {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please make sure we have tests for this.

public init?(_ description: String) {
self = description
}
}

extension String {
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StringInterpolation.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ extension String : ExpressibleByStringInterpolation {
///
/// - SeeAlso: `ExpressibleByStringInterpolation`
public init<T>(stringInterpolationSegment expr: T) {
self = String(expr)
self = String(describing: expr)
}

% for Type in StreamableTypes:
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StringUTF16.swift
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ extension String {
return UTF16View(_core)
}
set {
self = String(newValue)
self = String(describing: newValue)
}
}

Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StringUTF8.swift
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ extension String {
return UTF8View(self._core)
}
set {
self = String(newValue)
self = String(describing: newValue)
}
}

Expand Down
17 changes: 15 additions & 2 deletions stdlib/public/core/UnicodeScalar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -271,17 +271,30 @@ public struct UnicodeScalar :
}

extension UnicodeScalar : CustomStringConvertible, CustomDebugStringConvertible {
/// An escaped textual representation of the Unicode scalar.
/// A textual representation of the Unicode scalar.
public var description: String {
return "\"\(escaped(asASCII: false))\""
return String._fromWellFormedCodeUnitSequence(
UTF32.self,
input: repeatElement(self.value, count: 1))
}

/// An escaped textual representation of the Unicode scalar, suitable for
/// debugging.
public var debugDescription: String {
return "\"\(escaped(asASCII: true))\""
}
}

extension UnicodeScalar : LosslessStringConvertible {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In test/1_stdlib/Character.swift.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. It was not obvious which API it tests, could you add the string UnicodeScalar.init?(_: String) in the test name somewhere? We usually name the tests after specific APIs, if that's what we are targeting.

public init?(_ description: String) {
let scalars = description.unicodeScalars
guard let v = scalars.first, scalars.count == 1 else {
return nil
}
self = v
}
}

extension UnicodeScalar : Hashable {
/// The Unicode scalar's hash value.
///
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/UnsafeBufferPointer.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ extension Unsafe${Mutable}BufferPointer : CustomDebugStringConvertible {
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String {
return "Unsafe${Mutable}BufferPointer"
+ "(start: \(_position.map(String.init(_:)) ?? "nil"), count: \(count))"
+ "(start: \(_position.map(String.init(describing:)) ?? "nil"), count: \(count))"
}
}
%end
Expand Down
Loading