From c4b47cb12e33a674d8c047612357c9c0b3b83876 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Tue, 21 May 2024 10:50:17 -0700 Subject: [PATCH 1/5] Test: implement `reverse` --- test/Inputs/Swiftskell.swift | 14 ++++++++++++-- test/Interpreter/moveonly_swiftskell.swift | 6 +++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/test/Inputs/Swiftskell.swift b/test/Inputs/Swiftskell.swift index 30bb79562b4a8..0e6045435add2 100644 --- a/test/Inputs/Swiftskell.swift +++ b/test/Inputs/Swiftskell.swift @@ -57,7 +57,7 @@ public protocol Generator: ~Copyable { // MARK: Tuples public enum Pair: ~Copyable { - case elms(L, R) + case pair(L, R) } extension Pair: Copyable where L: Copyable, R: Copyable {} @@ -244,7 +244,7 @@ extension List where Element: ~Copyable { public consuming func pop() -> Optional>> { switch consume self { case .empty: .none - case let .cons(elm, tail): .elms(elm, tail.take()) + case let .cons(elm, tail): .pair(elm, tail.take()) } } @@ -252,6 +252,16 @@ extension List where Element: ~Copyable { public consuming func push(_ newHead: consuming Element) -> List { return List(newHead, self) } + + /// Produces a new list that is the reverse of this list. + public consuming func reverse() -> List { + var new = List() + while case let .pair(head, tail) = pop() { + new = new.push(head) + self = tail + } + return new + } } extension List: Show where Element: Show & ~Copyable { diff --git a/test/Interpreter/moveonly_swiftskell.swift b/test/Interpreter/moveonly_swiftskell.swift index 7ed22656c7394..8ed69cb598f0b 100644 --- a/test/Interpreter/moveonly_swiftskell.swift +++ b/test/Interpreter/moveonly_swiftskell.swift @@ -53,6 +53,10 @@ func testListBasic() { check(items.length() == 5) check(!items.empty()) + items = items.reverse() + check(items.length() == 5) + print(items.show()) // CHECK: [4, 3, 2, 1, 0, ] + items = .empty check(items.length() == 0) check(items.empty()) @@ -60,5 +64,5 @@ func testListBasic() { let nums = List().push(7).push(7).push(3) print(nums.show()) // CHECK: [7, 7, 3, ] - + } From a74e7ce677cfb842f9de2cb6f53f0233a761af4d Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Tue, 21 May 2024 11:09:26 -0700 Subject: [PATCH 2/5] Test: promote `check` to library --- test/Inputs/Swiftskell.swift | 11 +++++++++++ test/Interpreter/moveonly_swiftskell.swift | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/test/Inputs/Swiftskell.swift b/test/Inputs/Swiftskell.swift index 0e6045435add2..db750f41980f7 100644 --- a/test/Inputs/Swiftskell.swift +++ b/test/Inputs/Swiftskell.swift @@ -55,6 +55,17 @@ public protocol Generator: ~Copyable { func next() -> Maybe } +/// 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: ~Copyable { case pair(L, R) diff --git a/test/Interpreter/moveonly_swiftskell.swift b/test/Interpreter/moveonly_swiftskell.swift index 8ed69cb598f0b..ebd2801890bf1 100644 --- a/test/Interpreter/moveonly_swiftskell.swift +++ b/test/Interpreter/moveonly_swiftskell.swift @@ -20,16 +20,6 @@ import Swiftskell -/// assertion function -func check(_ result: Bool, _ string: String? = nil, _ line: Int = #line) { - if result { return } - var msg = "assertion failure (line \(line))" - if let extra = string { - msg += ":\t" + extra - } - fatalError(msg) -} - /// Basic noncopyable type for testing. struct File: ~Copyable, Show { let id: Int From af9ce5f557fd3d3f5c613ce84c3dab01c671a9b0 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Tue, 21 May 2024 11:10:24 -0700 Subject: [PATCH 3/5] Test: convert empty method to borrowing getter it's cheap enough that it can be a computed property; and gives more test coverage. --- test/Inputs/Swiftskell.swift | 10 ++++++---- test/Interpreter/moveonly_swiftskell.swift | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/Inputs/Swiftskell.swift b/test/Inputs/Swiftskell.swift index db750f41980f7..017f8e57274fe 100644 --- a/test/Inputs/Swiftskell.swift +++ b/test/Inputs/Swiftskell.swift @@ -239,10 +239,12 @@ extension List where Element: ~Copyable { /// Basic utilities extension List where Element: ~Copyable { /// Is this list empty? - public borrowing func empty() -> Bool { - switch self { - case .empty: return true - case .cons(_, _): return false + public var isEmpty: Bool { + borrowing get { + switch self { + case .empty: true + case .cons(_, _): false + } } } diff --git a/test/Interpreter/moveonly_swiftskell.swift b/test/Interpreter/moveonly_swiftskell.swift index ebd2801890bf1..da71d04523bb8 100644 --- a/test/Interpreter/moveonly_swiftskell.swift +++ b/test/Interpreter/moveonly_swiftskell.swift @@ -41,7 +41,7 @@ func testListBasic() { var items = List(length: 5) { .init($0) } print(items.show()) // CHECK: [0, 1, 2, 3, 4, ] check(items.length() == 5) - check(!items.empty()) + check(!items.isEmpty) items = items.reverse() check(items.length() == 5) @@ -49,7 +49,7 @@ func testListBasic() { items = .empty check(items.length() == 0) - check(items.empty()) + check(items.isEmpty) let nums = List().push(7).push(7).push(3) print(nums.show()) // CHECK: [7, 7, 3, ] From a626c6b0898494f32ed9bd4b6260b0c6a7950600 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Tue, 21 May 2024 11:17:20 -0700 Subject: [PATCH 4/5] Test: more coverage for deinits --- test/Interpreter/moveonly_swiftskell.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/Interpreter/moveonly_swiftskell.swift b/test/Interpreter/moveonly_swiftskell.swift index da71d04523bb8..2b0659cede273 100644 --- a/test/Interpreter/moveonly_swiftskell.swift +++ b/test/Interpreter/moveonly_swiftskell.swift @@ -14,7 +14,8 @@ // RUN: -module-name E -o %t/E %target-rpath(%t) // RUN: %target-codesign %t/E // RUN: %target-codesign %t/%target-library-name(Swiftskell) -// RUN: %target-run %t/E %t/%target-library-name(Swiftskell) | %FileCheck %s +// RUN: %target-run %t/E %t/%target-library-name(Swiftskell) \ +// RUN: | %FileCheck %s --implicit-check-not destroy // REQUIRES: executable_test @@ -27,6 +28,8 @@ struct File: ~Copyable, Show { self.id = id } func show() -> String { return id.show() } + + deinit { print("destroying file \(id)") } } @@ -43,11 +46,24 @@ func testListBasic() { check(items.length() == 5) check(!items.isEmpty) + items = List(length: 5) { .init($0) } + // CHECK: destroying file 4 + // CHECK: destroying file 3 + // CHECK: destroying file 2 + // CHECK: destroying file 1 + // CHECK: destroying file 0 + items = items.reverse() check(items.length() == 5) print(items.show()) // CHECK: [4, 3, 2, 1, 0, ] items = .empty + // CHECK: destroying file 0 + // CHECK: destroying file 1 + // CHECK: destroying file 2 + // CHECK: destroying file 3 + // CHECK: destroying file 4 + check(items.length() == 0) check(items.isEmpty) From fb79bd244957f7656f702f8b510d789aa60e43d9 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Tue, 21 May 2024 11:49:21 -0700 Subject: [PATCH 5/5] Test: improve comments --- test/Inputs/Swiftskell.swift | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/Inputs/Swiftskell.swift b/test/Inputs/Swiftskell.swift index 017f8e57274fe..9e4b96bf3a8bc 100644 --- a/test/Inputs/Swiftskell.swift +++ b/test/Inputs/Swiftskell.swift @@ -164,6 +164,8 @@ public struct Box: ~Copyable { /// MARK: Data.List +/// +/// A singly-linked list public enum List: ~Copyable { case cons(Element, Box>) case empty @@ -179,7 +181,7 @@ public enum List: ~Copyable { /// 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. + /// Returns f(xn,...,f(x2, f(x1, init))...), or `init` if the list is empty. public borrowing func foldl( init initial: consuming Out, _ f: (borrowing Element, consuming Out) -> Out) -> Out @@ -196,7 +198,7 @@ extension List where Element: ~Copyable { } /// 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. + /// Returns f(x1, f(x2,...,f(xn, init)...)) or `init` if the list is empty. public borrowing func foldr( init initial: consuming Out, _ f: (borrowing Element, consuming Out) -> Out) -> Out @@ -239,6 +241,8 @@ extension List where Element: ~Copyable { /// Basic utilities extension List where Element: ~Copyable { /// Is this list empty? + /// + /// Complexity: O(1) public var isEmpty: Bool { borrowing get { switch self { @@ -249,11 +253,15 @@ extension List where Element: ~Copyable { } /// 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>> { switch consume self { case .empty: .none @@ -261,12 +269,16 @@ extension List where Element: ~Copyable { } } - /// Push an element onto the list. + /// Push an element onto the front of the list. + /// + /// Complexity: O(1) public consuming func push(_ newHead: consuming Element) -> List { return List(newHead, self) } /// Produces a new list that is the reverse of this list. + /// + /// Complexity: O(n) public consuming func reverse() -> List { var new = List() while case let .pair(head, tail) = pop() {