Skip to content

[6.0🍒] NFC: Update Changelog for more porposals, and add more testing. #73990

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 14 commits into from
Jun 1, 2024
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
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@

## Swift 6.0

* [SE-0432][]:
Noncopyable enums can be pattern-matched with switches without consuming the
value you switch over:

```swift
enum Lunch: ~Copyable {
case soup
case salad
case sandwich
}

func isSoup(_ lunch: borrowing Lunch) -> Bool {
switch lunch {
case .soup: true
default: false
}
}
```


* [SE-0428][]:
Distributed actors now have the ability to support complete split server /
client systems, thanks to the new `@Resolvable` macro and runtime changes.
Expand Down Expand Up @@ -238,6 +258,24 @@ And the module structure to support such applications looks like this:
}
```

* [SE-0429][]:
The noncopyable fields of certain types can now be consumed individually:

```swift
struct Token: ~Copyable {}

struct Authentication: ~Copyable {
let id: Token
let name: String

mutating func exchange(_ new: consuming Token) -> Token {
let old = self.id // <- partial consumption of 'self'
self = .init(id: new, name: self.name)
return old
}
}
```

* [SE-0427][]:
You can now suppress `Copyable` on protocols, generic parameters,
and existentials:
Expand Down Expand Up @@ -10517,6 +10555,8 @@ using the `.dynamicType` member to retrieve the type of an expression should mig
[SE-0411]: https://github.com/apple/swift-evolution/blob/main/proposals/0411-isolated-default-values.md
[SE-0412]: https://github.com/apple/swift-evolution/blob/main/proposals/0412-strict-concurrency-for-global-variables.md
[SE-0413]: https://github.com/apple/swift-evolution/blob/main/proposals/0413-typed-throws.md
[SE-0429]: https://github.com/apple/swift-evolution/blob/main/proposals/0429-partial-consumption.md
[SE-0432]: https://github.com/apple/swift-evolution/blob/main/proposals/0432-noncopyable-switch.md
[SE-0414]: https://github.com/apple/swift-evolution/blob/main/proposals/0414-region-based-isolation.md
[SE-0424]: https://github.com/apple/swift-evolution/blob/main/proposals/0424-custom-isolation-checking-for-serialexecutor.md
[SE-0428]: https://github.com/apple/swift-evolution/blob/main/proposals/0428-resolve-distributed-actor-protocols.md
Expand Down
8 changes: 8 additions & 0 deletions test/Generics/inverse_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -500,3 +500,11 @@ struct TestResolution3 {
var dictNC: [String: NC] = [:] // expected-error {{type 'NC' does not conform to protocol 'Copyable'}}
var exampleNC: Example<NC> = Example() // expected-error {{type 'NC' does not conform to protocol 'Copyable'}}
}

public struct Box<Wrapped: ~Copyable>: ~Copyable {}
// Box is never copyable, so we can't support this conditional conformance.
public enum List<Element: ~Copyable>: ~Copyable {
case cons(Element, Box<List<Element>>) // expected-error {{associated value 'cons' of 'Copyable'-conforming generic enum 'List' has non-Copyable type '(Element, Box<List<Element>>)'}}
case empty
}
extension List: Copyable where Element: Copyable {}
213 changes: 190 additions & 23 deletions test/Inputs/Swiftskell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ public protocol Show: ~Copyable {
borrowing func show() -> String
}

extension CustomStringConvertible {
public func show() -> String { return description }
}
extension Int: Show {}

public func print(_ s: borrowing some Show & ~Copyable) {
print(s.show())
}
Expand Down Expand Up @@ -50,37 +55,47 @@ public protocol Generator: ~Copyable {
func next() -> Maybe<Element>
}

/// Eager assertion function, to avoid autoclosures.
public func check(_ result: Bool, _ string: String? = nil,
_ file: String = #file, _ line: Int = #line) {
if result { return }
var msg = "assertion failure (\(file):\(line))"
if let extra = string {
msg += ":\t" + extra
}
fatalError(msg)
}

// MARK: Tuples
public enum Pair<L: ~Copyable, R: ~Copyable>: ~Copyable {
case elms(L, R)
case pair(L, R)
}
extension Pair: Copyable where L: Copyable, R: Copyable {}

/// MARK: Data.Maybe

public enum Maybe<Value: ~Copyable>: ~Copyable {
case just(Value)
public enum Maybe<Wrapped: ~Copyable>: ~Copyable {
case just(Wrapped)
case nothing
}

extension Maybe: Copyable {}

extension Maybe: Show where Value: Show & ~Copyable {
extension Maybe: Show where Wrapped: Show & ~Copyable {
public borrowing func show() -> String {
switch self {
case let .just(borrowing elm):
case let .just(elm):
return elm.show()
case .nothing:
return "<nothing>"
}
}
}

extension Maybe: Eq where Value: Eq, Value: ~Copyable {
extension Maybe: Eq where Wrapped: Eq, Wrapped: ~Copyable {
public static func ==(_ a: borrowing Self, _ b: borrowing Self) -> Bool {
switch a {
case let .just(borrowing a1):
case let .just(a1):
switch b {
case let .just(borrowing b1):
case let .just(b1):
return a1 == b1
case .nothing:
return false
Expand All @@ -96,19 +111,6 @@ extension Maybe: Eq where Value: Eq, Value: ~Copyable {
}
}


// FIXME: triggers crash!
// @inlinable
// public func fromMaybe<A: ~Copyable>(_ defaultVal: consuming A,
// _ mayb: consuming Maybe<A>) -> A {
// switch mayb {
// case let .just(payload):
// return payload
// case .nothing:
// return defaultVal
// }
// }

public func isJust<A: ~Copyable>(_ m: borrowing Maybe<A>) -> Bool {
switch m {
case .just:
Expand All @@ -127,3 +129,168 @@ public struct UnownedRef<Instance: AnyObject> {
@usableFromInline
internal unowned(unsafe) var _value: Instance
}

/// Provides underlying support so that you can create recursive enums, because
/// noncopyable enums do not yet support indirect cases.
public struct Box<Wrapped: ~Copyable>: ~Copyable {
private let _pointer: UnsafeMutablePointer<Wrapped>

init(_ wrapped: consuming Wrapped) {
_pointer = .allocate(capacity: 1)
_pointer.initialize(to: wrapped)
}

deinit {
_pointer.deinitialize(count: 1)
_pointer.deallocate()
}

consuming func take() -> Wrapped {
let wrapped = _pointer.move()
_pointer.deallocate()
discard self
return wrapped
}

var borrow: Wrapped {
_read { yield _pointer.pointee }
}

consuming func map(_ transform: (consuming Wrapped) -> Wrapped) -> Self {
_pointer.initialize(to: transform(_pointer.move()))
return self
}
}


/// MARK: Data.List
///
/// A singly-linked list
public enum List<Element: ~Copyable>: ~Copyable {
case cons(Element, Box<List<Element>>)
case empty

public init(_ head: consuming Element,
_ tail: consuming List<Element>) {
self = .cons(head, Box(tail))
}

public init() { self = .empty }
}

/// Pure Iteration
extension List where Element: ~Copyable {
/// Performs forward iteration through the list, accumulating a result value.
/// Returns f(xn,...,f(x2, f(x1, init))...), or `init` if the list is empty.
public borrowing func foldl<Out>(
init initial: consuming Out,
_ f: (borrowing Element, consuming Out) -> Out) -> Out
where Out: ~Copyable {
func loop(_ acc: consuming Out, _ lst: borrowing Self) -> Out {
switch lst {
case .empty:
return acc
case let .cons(elm, tail):
return loop(f(elm, acc), tail.borrow)
}
}
return loop(initial, self)
}

/// Performs reverse iteration through the list, accumulating a result value.
/// Returns f(x1, f(x2,...,f(xn, init)...)) or `init` if the list is empty.
public borrowing func foldr<Out>(
init initial: consuming Out,
_ f: (borrowing Element, consuming Out) -> Out) -> Out
where Out: ~Copyable {
switch self {
case .empty:
return initial
case let .cons(elm, tail):
return f(elm, tail.borrow.foldr(init: initial, f))
}
}

// Forward iteration without accumulating a result.
public borrowing func forEach(_ f: (borrowing Element) -> Void) -> Void {
switch self {
case .empty: return
case let .cons(elm, tail):
f(elm)
return tail.borrow.forEach(f)
}
}
}

/// Initialization
extension List where Element: ~Copyable {
// Generates a list of elements [f(0), f(1), ..., f(n-1)] from left to right.
// For n < 0, the empty list is created.
public init(length n: Int, _ f: (Int) -> Element) {
guard n > 0 else {
self = .empty
return
}

let cur = n-1
let elm = f(cur)
self = List(elm, List(length: cur, f))
}
}

/// Basic utilities
extension List where Element: ~Copyable {
/// Is this list empty?
///
/// Complexity: O(1)
public var isEmpty: Bool {
borrowing get {
switch self {
case .empty: true
case .cons(_, _): false
}
}
}

/// How many elements are in this list?
///
/// Complexity: O(n)
public borrowing func length() -> Int {
return foldl(init: 0) { $1 + 1 }
}

/// Pop the first element off the list, if present.
///
/// Complexity: O(1)
public consuming func pop() -> Optional<Pair<Element, List<Element>>> {
switch consume self {
case .empty: .none
case let .cons(elm, tail): .pair(elm, tail.take())
}
}

/// Push an element onto the front of the list.
///
/// Complexity: O(1)
public consuming func push(_ newHead: consuming Element) -> List<Element> {
return List(newHead, self)
}

/// Produces a new list that is the reverse of this list.
///
/// Complexity: O(n)
public consuming func reverse() -> List<Element> {
var new = List<Element>()
while case let .pair(head, tail) = pop() {
new = new.push(head)
self = tail
}
return new
}
}

extension List: Show where Element: Show & ~Copyable {
public borrowing func show() -> String {
return "[" + foldl(init: "]", { $0.show() + ", " + $1 })
}
}
Loading