Skip to content

Commit c25bf40

Browse files
committed
[stdlib] Various documentation improvements
* Revise type(of:) * Revise withoutActuallyEscaping(_:do:) * Add slicing / index sharing to Collection * Other cleanups
1 parent d98268c commit c25bf40

8 files changed

+287
-106
lines changed

stdlib/public/core/Bool.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ public struct Bool {
7878
@_transparent
7979
internal init(_ v: Builtin.Int1) { self._value = v }
8080

81+
/// Creates an instance equal to the given Boolean value.
82+
///
83+
/// - Parameter value: The Boolean value to copy.
8184
public init(_ value: Bool) {
8285
self = value
8386
}
@@ -155,6 +158,12 @@ extension Bool : Equatable, Hashable {
155158
}
156159

157160
extension Bool : LosslessStringConvertible {
161+
/// Creates a new Boolean value from the given string.
162+
///
163+
/// If `description` is any string other than `"true"` or `"false"`, the
164+
/// result is `nil`. This initializer is case sensitive.
165+
///
166+
/// - Parameter description: A string representation of the Boolean value.
158167
public init?(_ description: String) {
159168
if description == "true" {
160169
self = true

stdlib/public/core/Builtin.swift

Lines changed: 179 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -623,36 +623,109 @@ func _trueAfterDiagnostics() -> Builtin.Int1 {
623623

624624
/// Returns the dynamic type of a value.
625625
///
626-
/// - Parameter of: The value to take the dynamic type of.
627-
/// - Returns: The dynamic type, which will be a value of metatype type.
628-
///
629-
/// - Remark: If the parameter is statically of a protocol or protocol
630-
/// composition type, the result will be an *existential metatype*
631-
/// (`P.Type` for a protocol `P`), and will represent the type of the value
632-
/// inside the existential container with the same protocol conformances
633-
/// as the value. Otherwise, the result will be a *concrete metatype*
634-
/// (`T.Type` for a non-protocol type `T`, or `P.Protocol` for a protocol
635-
/// `P`). Normally, this will do what you mean, but one wart to be aware
636-
/// of is when you use `type(of:)` in a generic context with a type
637-
/// parameter bound to a protocol type:
638-
///
639-
/// ```
640-
/// func foo<T>(x: T) -> T.Type {
641-
/// return type(of: x)
642-
/// }
643-
/// protocol P {}
644-
/// func bar(x: P) {
645-
/// foo(x: x) // Call foo with T == P
646-
/// }
647-
/// ```
648-
///
649-
/// since the call to `type(of:)` inside `foo` only sees `T` as a concrete
650-
/// type, foo will end up returning `P.self` instead of the dynamic type
651-
/// inside `x`. This can be worked around by writing `type(of: x as Any)`
652-
/// to get the dynamic type inside `x` as an `Any.Type`.
626+
/// You can use the `type(of:)` function to find the dynamic type of a value,
627+
/// particularly when the dynamic type is different from the static type. The
628+
/// *static type* of a value is the known, compile-time type of the value. The
629+
/// *dynamic type* of a value is the value's actual type at run-time, which
630+
/// may be nested inside its concrete type.
631+
///
632+
/// In the following code, the `count` variable has the same static and dynamic
633+
/// type: `Int`. When `count` is passed to the `printInfo(_:)` function,
634+
/// however, the `value` parameter has a static type of `Any`, the type
635+
/// declared for the parameter, and a dynamic type of `Int`.
636+
///
637+
/// func printInfo(_ value: Any) {
638+
/// let type = type(of: value)
639+
/// print("'\(value)' of type '\(type)'")
640+
/// }
641+
///
642+
/// let count: Int = 5
643+
/// printInfo(count)
644+
/// // '5' of type 'Int'
645+
///
646+
/// The dynamic type returned from `type(of:)` is a *concrete metatype*
647+
/// (`T.Type`) for a class, structure, enumeration, or other non-protocol type
648+
/// `T`, or an *existential metatype* (`P.Type`) for a protocol or protocol
649+
/// composition `P`. When the static type of the value passed to `type(of:)`
650+
/// is constrained to a class or protocol, you can use that metatype to access
651+
/// initializers or other static members of the class or protocol.
652+
///
653+
/// For example, the parameter passed as `value` to the `printSmileyInfo(_:)`
654+
/// function in the example below is an instance of the `Smiley` class or one
655+
/// of its subclasses. The function uses `type(of:)` to find the dynamic type
656+
/// of `value`, which itself is an instance of the `Smiley.Type` metatype.
657+
///
658+
/// class Smiley {
659+
/// class var text: String {
660+
/// return ":)"
661+
/// }
662+
/// }
663+
///
664+
/// class EmojiSmiley : Smiley {
665+
/// override class var text: String {
666+
/// return "😀"
667+
/// }
668+
/// }
669+
///
670+
/// func printSmileyInfo(_ value: Smiley) {
671+
/// let smileyType = type(of: value)
672+
/// print("Smile!", smileyType.text)
673+
/// }
674+
///
675+
/// let emojiSmiley = EmojiSmiley()
676+
/// printSmileyInfo(emojiSmiley)
677+
/// // Smile! 😀
678+
///
679+
/// In this example, accessing the `text` property of the `smileyType` metatype
680+
/// retrieves the overriden value from the `EmojiSmiley` subclass, instead of
681+
/// the `Smiley` class's original definition.
682+
///
683+
/// Normally, you don't need to be aware of the difference between concrete and
684+
/// existential metatypes, but calling `type(of:)` can yield unexpected
685+
/// results in a generic context with a type parameter bound to a protocol. In
686+
/// a case like this, where a generic parameter `T` is bound to a protocol
687+
/// `P`, the type parameter is not statically known to be a protocol type in
688+
/// the body of the generic function, so `type(of:)` can only produce the
689+
/// concrete metatype `P.Protocol`.
690+
///
691+
/// The following example defines a `printGenericInfo(_:)` function that takes
692+
/// a generic parameter and declares the `String` type's conformance to a new
693+
/// protocol `P`. When `printGenericInfo(_:)` is called with a string that has
694+
/// `P` as its static type, the call to `type(of:)` returns `P.self` instead
695+
/// of the dynamic type inside the parameter, `String.self`.
696+
///
697+
/// func printGenericInfo<T>(_ value: T) {
698+
/// let type = type(of: value)
699+
/// print("'\(value)' of type '\(type)'")
700+
/// }
701+
///
702+
/// protocol P {}
703+
/// extension String: P {}
704+
///
705+
/// let stringAsP: P = "Hello!"
706+
/// printGenericInfo(stringAsP)
707+
/// // 'Hello!' of type 'P'
708+
///
709+
/// This unexpected result occurs because the call to `type(of: value)` inside
710+
/// `printGenericInfo(_:)` must return a metatype that is an instance of
711+
/// `T.Type`, but `String.self` (the expected dynamic type) is not an instance
712+
/// of `P.Type` (the concrete metatype of `value`. To get the dynamic type
713+
/// inside `value` in this generic context, cast the parameter to `Any` when
714+
/// calling `type(of:)`.
715+
///
716+
/// func betterPrintGenericInfo<T>(_ value: T) {
717+
/// let type = type(of: value as Any)
718+
/// print("'\(value)' of type '\(type)'")
719+
/// }
720+
///
721+
/// betterPrintGenericInfo(stringAsP)
722+
/// // 'Hello!' of type 'String'
723+
///
724+
/// - Parameter value: The value to find the dynamic type of.
725+
/// - Returns: The dynamic type, which is a value of metatype type.
653726
@_transparent
654727
@_semantics("typechecker.type(of:)")
655-
public func type<Type, Metatype>(of: Type) -> Metatype {
728+
public func type<T, Metatype>(of value: T) -> Metatype {
656729
// This implementation is never used, since calls to `Swift.type(of:)` are
657730
// resolved as a special case by the type checker.
658731
Builtin.staticReport(_trueAfterDiagnostics(), true._value,
@@ -661,73 +734,91 @@ public func type<Type, Metatype>(of: Type) -> Metatype {
661734
Builtin.unreachable()
662735
}
663736

664-
/// Allows a nonescaping closure to temporarily be used as if it were
665-
/// allowed to escape.
666-
///
667-
/// This is useful when you need to pass a closure to an API that can't
668-
/// statically guarantee the closure won't escape when used in a way that
669-
/// won't allow it to escape in practice, such as in a lazy collection
670-
/// view:
671-
///
672-
/// ```
673-
/// func allValues(in array: [Int], matchPredicate: (Int) -> Bool) -> Bool {
674-
/// // Error because `lazy.filter` may escape the closure if the `lazy`
675-
/// // collection is persisted; however, in this case, we discard the
676-
/// // lazy collection immediately before returning.
677-
/// return array.lazy.filter { !matchPredicate($0) }.isEmpty
678-
/// }
679-
/// ```
680-
///
681-
/// or with `async`:
682-
///
683-
/// ```
684-
/// func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void,
685-
/// on queue: DispatchQueue) {
686-
/// // Error: `async` normally escapes the closure, but in this case
687-
/// // we explicitly barrier before the closure would escape
688-
/// queue.async(f)
689-
/// queue.async(g)
690-
/// queue.sync(flags: .barrier) {}
691-
/// }
692-
/// ```
693-
///
694-
/// `withoutActuallyEscaping` provides a temporarily-escapable copy of the
695-
/// closure that can be used in these situations:
696-
///
697-
/// ```
698-
/// func allValues(in array: [Int], matchPredicate: (Int) -> Bool) -> Bool {
699-
/// return withoutActuallyEscaping(matchPredicate) { escapablePredicate in
700-
/// array.lazy.filter { !escapableMatchPredicate($0) }.isEmpty
701-
/// }
702-
/// }
703-
///
704-
/// func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void,
705-
/// on queue: DispatchQueue) {
706-
/// withoutActuallyEscaping(f) { escapableF in
707-
/// withoutActuallyEscaping(g) { escapableG in
708-
/// queue.async(escapableF)
709-
/// queue.async(escapableG)
710-
/// queue.sync(flags: .barrier) {}
737+
/// Allows a nonescaping closure to temporarily be used as if it were allowed
738+
/// to escape.
739+
///
740+
/// You can use this function to call an API that takes an escaping closure in
741+
/// a way that doesn't allow the closure to escape in practice. The examples
742+
/// below demonstrate how to use `withoutActuallyEscaping(_:do:)` in
743+
/// conjunction with two common APIs that use escaping closures: lazy
744+
/// collection views and asynchronous operations.
745+
///
746+
/// The following code declares an `allValues(in:match:)` function that checks
747+
/// whether all the elements in an array match a predicate. The function won't
748+
/// compile as written, because a lazy collection's `filter(_:)` method
749+
/// requires an escaping closure. The lazy collection isn't persisted, so the
750+
/// `predicate` closure won't actually escape the body of the function, but
751+
/// even so it can't be used in this way.
752+
///
753+
/// func allValues(in array: [Int], match predicate: (Int) -> Bool) -> Bool {
754+
/// return array.lazy.filter { !predicate($0) }.isEmpty
711755
/// }
712-
/// }
713-
/// }
714-
/// ```
756+
/// // error: closure use of non-escaping parameter 'predicate'...
757+
///
758+
/// `withoutActuallyEscaping(_:do:)` provides a temporarily-escapable copy of
759+
/// `predicate` that _can_ be used in a call to the lazy view's `filter(_:)`
760+
/// method. The second version of `allValues(in:match:)` compiles without
761+
/// error, with the compiler guaranteeing that the `escapablePredicate`
762+
/// closure doesn't last beyond the call to `withoutActuallyEscaping(_:do:)`.
763+
///
764+
/// func allValues(in array: [Int], match predicate: (Int) -> Bool) -> Bool {
765+
/// return withoutActuallyEscaping(predicate) { escapablePredicate in
766+
/// array.lazy.filter { !escapablePredicate($0) }.isEmpty
767+
/// }
768+
/// }
769+
///
770+
/// Asynchronous calls are another type of API that typically escape their
771+
/// closure arguments. The following code declares a
772+
/// `perform(_:simultaneouslyWith:)` function that uses a dispatch queue to
773+
/// execute two closures concurrently.
774+
///
775+
/// func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void) {
776+
/// let queue = DispatchQueue(label: "perform", attributes: .concurrent)
777+
/// queue.async(execute: f)
778+
/// queue.async(execute: g)
779+
/// queue.sync(flags: .barrier) {}
780+
/// }
781+
/// // error: passing non-escaping parameter 'f'...
782+
/// // error: passing non-escaping parameter 'g'...
783+
///
784+
/// The `perform(_:simultaneouslyWith:)` function ends with a call to the
785+
/// `sync(flags:execute:)` method using the `.barrier` flag, which forces the
786+
/// function to wait until both closures have completed running before
787+
/// returning. Even though the barrier guarantees that neither closure will
788+
/// escape the function, the `async(execute:)` method still requires that the
789+
/// closures passed be marked as `@escaping`, so the first version of the
790+
/// function does not compile. To resolve this, you can use
791+
/// `withoutActuallyEscaping(_:do:)` to get copies of `f` and `g` that can be
792+
/// passed to `async(execute:)`.
793+
///
794+
/// func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void) {
795+
/// withoutActuallyEscaping(f) { escapableF in
796+
/// withoutActuallyEscaping(g) { escapableG in
797+
/// let queue = DispatchQueue(label: "perform", attributes: .concurrent)
798+
/// queue.async(execute: escapableF)
799+
/// queue.async(execute: escapableG)
800+
/// queue.sync(flags: .barrier) {}
801+
/// }
802+
/// }
803+
/// }
804+
///
805+
/// - Important: The escapable copy of `closure` passed as `body` is only valid
806+
/// during the call to `withoutActuallyEscaping(_:do:)`. It is undefined
807+
/// behavior for the escapable closure to be stored, referenced, or executed
808+
/// after the function returns.
715809
///
716810
/// - Parameter closure: A non-escaping closure value that will be made
717-
/// escapable for the duration of the execution of the `do` block.
718-
/// - Parameter do: A code block that will be immediately executed, receiving
719-
/// an escapable copy of `closure` as an argument.
720-
/// - Returns: the forwarded return value from the `do` block.
721-
/// - Remark: It is undefined behavior for the escapable closure to be stored,
722-
/// referenced, or executed after `withoutActuallyEscaping` returns. A
723-
/// future version of Swift will introduce a dynamic check to trap if
724-
/// the escapable closure is still referenced at the point
725-
/// `withoutActuallyEscaping` returns.
811+
/// escapable for the duration of the execution of the `body` closure. If
812+
/// `body` has a return value, it is used as the return value for the
813+
/// `withoutActuallyEscaping(_:do:)` function.
814+
/// - Parameter body: A closure that will be immediately executed, receiving an
815+
/// escapable copy of `closure` as an argument.
816+
/// - Returns: The return value of the `body` closure, if any.
726817
@_transparent
727818
@_semantics("typechecker.withoutActuallyEscaping(_:do:)")
728819
public func withoutActuallyEscaping<ClosureType, ResultType>(
729820
_ closure: ClosureType,
730-
do: (_ escapingClosure: ClosureType) throws -> ResultType
821+
do body: (_ escapingClosure: ClosureType) throws -> ResultType
731822
) rethrows -> ResultType {
732823
// This implementation is never used, since calls to
733824
// `Swift.withoutActuallyEscaping(_:do:)` are resolved as a special case by

0 commit comments

Comments
 (0)