From ecce35088908b1a63a01d51ef3ab9d86170c97e9 Mon Sep 17 00:00:00 2001 From: David Sankel Date: Tue, 12 Sep 2023 17:15:23 -0400 Subject: [PATCH 01/85] Add Array to Hylo standard library - This is a partial implementation of only some functionality - DynamicBuffer was made Movable to work around a compiler defect --- Library/Hylo/Array.hylo | 83 ++++++++++++++++++++ Library/Hylo/DynamicBuffer.hylo | 12 ++- Tests/LibraryTests/TestCases/ArrayTests.hylo | 10 +++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 Library/Hylo/Array.hylo create mode 100644 Tests/LibraryTests/TestCases/ArrayTests.hylo diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo new file mode 100644 index 000000000..59722782d --- /dev/null +++ b/Library/Hylo/Array.hylo @@ -0,0 +1,83 @@ +/// An ordered, random-access collection. +public type Array : Regular { + + // TODO: Change from `Int` header to `Void` once #1000 is fixed + var _storage: DynamicBuffer + var _count: Int + + /// Creates a new, empty array + public init() { + &_storage = .new() + &_count = 0 + } + + /// The number of elements in the array + public fun count() -> Int { + return _count.copy() + } + public fun capacity() -> Int { + return _storage.capacity() + } + + /// Reserves enough space to store `x` elements + public fun reserveCapacity( _ x : Int) { + if( x >= capacity() ) { + return + } + var new_capacity : Int; + if( capacity() == 0 ) { + &new_capacity = 1; + } else { + &new_capacity = capacity(); + } + while (new_capacity < x) { + &new_capacity = capacity() + capacity() + } + var new_storage = DynamicBuffer( + capacity: new_capacity, + initializing_header_with: fun(_ h: set Int) -> Void { &h = 0 } + ) + + var i = 0 + var e = _storage.first_element_address() + var f = new_storage.first_element_address() + while( i < count() ) { + f.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { + &x = &e.unsafe[] + } ) + + &e = e.advance( by: 1 ) + &f = f.advance( by: 1 ) + } + + &_storage = new_storage + } + + /// Adds a new element at the end of the array. + public fun append(from source: sink Element) { + reserveCapacity( count() + 1 ) + var p = _storage.first_element_address().advance(by: count()) + + p.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { + &x = source + } ) + } +} + +public conformance Array: Equatable { + + /// Returns `true` iff `other` has an equivalent value. + public fun infix== (_ other: Self) -> Bool { + // TODO + return true + } + +} + +public conformance Array: Copyable { + /// Returns an equivalent instance. + public fun copy() -> Self { + // TODO + .new() + } +} diff --git a/Library/Hylo/DynamicBuffer.hylo b/Library/Hylo/DynamicBuffer.hylo index bc83f0f56..b3f6c832c 100644 --- a/Library/Hylo/DynamicBuffer.hylo +++ b/Library/Hylo/DynamicBuffer.hylo @@ -4,7 +4,7 @@ /// - Warning: The deinitializer of `DynamicBuffer` does not deinitialize the elements that may /// be stored in its payload. You must ensure that they are properly deinitialized before /// `deinit` is called. -public type DynamicBuffer: Deinitializable { +public type DynamicBuffer: Deinitializable, Movable { /// The description of payloads in `DynamicBuffer`s. typealias BufferHeader = { @@ -74,3 +74,13 @@ public type DynamicBuffer: Deinitializable { } } + +public conformance DynamicBuffer: Movable { + + fun take_value(from source: sink Self) -> {self: Self, Void} { + // TODO: Fix defects preventing the following line from working + // &storage.base = source.storage.base + (self: source, ()) + } + +} diff --git a/Tests/LibraryTests/TestCases/ArrayTests.hylo b/Tests/LibraryTests/TestCases/ArrayTests.hylo new file mode 100644 index 000000000..6d474ca23 --- /dev/null +++ b/Tests/LibraryTests/TestCases/ArrayTests.hylo @@ -0,0 +1,10 @@ +//- compileAndRun expecting: success + +fun test_init_empty() { + var d = Array() + precondition(d.count() == 0) +} + +public fun main() { + test_init_empty() +} From 81fabb3a1991578470dd28cbd8ad865bb5adc3ec Mon Sep 17 00:00:00 2001 From: David Sankel Date: Tue, 12 Sep 2023 17:32:53 -0400 Subject: [PATCH 02/85] Add explaination for DynamicBuffer's Movable conformance --- Library/Hylo/DynamicBuffer.hylo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Hylo/DynamicBuffer.hylo b/Library/Hylo/DynamicBuffer.hylo index b3f6c832c..faa1b4ddf 100644 --- a/Library/Hylo/DynamicBuffer.hylo +++ b/Library/Hylo/DynamicBuffer.hylo @@ -75,6 +75,8 @@ public type DynamicBuffer: Deinitializable, Mo } +// TODO: This trait was added as a workaround for #1002 since DynamicBuffer +// is initialized in the Array constructor public conformance DynamicBuffer: Movable { fun take_value(from source: sink Self) -> {self: Self, Void} { From 040ab7532e11d9f6295b105733d5564d77a53350 Mon Sep 17 00:00:00 2001 From: David Sankel Date: Tue, 12 Sep 2023 17:41:12 -0400 Subject: [PATCH 03/85] Add explanation for Array not conforming to Regular. --- Library/Hylo/Array.hylo | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 59722782d..4a97d7993 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -1,5 +1,5 @@ /// An ordered, random-access collection. -public type Array : Regular { +public type Array : Deinitializable { // TODO: Change from `Int` header to `Void` once #1000 is fixed var _storage: DynamicBuffer @@ -64,6 +64,13 @@ public type Array : Regular { } } +/* + +// TODO: Make Array conform to Regular instead of Deinitializable once #1002 is fixed. +// Currently that issue prevents the copy() function below from compiling. +// +// Error is "type 'Element' does not conform to trait 'Movable'" + public conformance Array: Equatable { /// Returns `true` iff `other` has an equivalent value. @@ -81,3 +88,5 @@ public conformance Array: Copyable { .new() } } + +*/ From 64d50651353d8121115049935e1ad1d7d69650e9 Mon Sep 17 00:00:00 2001 From: David Sankel Date: Tue, 12 Sep 2023 17:43:53 -0400 Subject: [PATCH 04/85] Minor fix. --- Library/Hylo/Array.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 4a97d7993..4a137c57b 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -43,7 +43,7 @@ public type Array : Deinitializable { var f = new_storage.first_element_address() while( i < count() ) { f.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { - &x = &e.unsafe[] + &x = e.unsafe[] } ) &e = e.advance( by: 1 ) From 40c01c046062d0cb107aca3f5a3b8727f948f963 Mon Sep 17 00:00:00 2001 From: David Sankel Date: Tue, 12 Sep 2023 17:52:06 -0400 Subject: [PATCH 05/85] Commented out code blocked by #1003. --- Library/Hylo/Array.hylo | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 4a137c57b..7d751942a 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -42,9 +42,10 @@ public type Array : Deinitializable { var e = _storage.first_element_address() var f = new_storage.first_element_address() while( i < count() ) { - f.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { - &x = e.unsafe[] - } ) + // TODO: This is currently blocked by #1003 + // f.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { + // &x = e.unsafe[] + // } ) &e = e.advance( by: 1 ) &f = f.advance( by: 1 ) @@ -57,10 +58,10 @@ public type Array : Deinitializable { public fun append(from source: sink Element) { reserveCapacity( count() + 1 ) var p = _storage.first_element_address().advance(by: count()) - - p.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { - &x = source - } ) + // TODO: Currently blocked by #1003 + // p.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { + // &x = source + // } ) } } From 267e6227b132def7ebde802380e6460b08c9ed6d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 13 Sep 2023 16:51:13 +0200 Subject: [PATCH 06/85] Use 'Void' as the header of arrays' out of line storage --- Library/Hylo/Array.hylo | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 7d751942a..83ada6679 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -1,8 +1,7 @@ /// An ordered, random-access collection. public type Array : Deinitializable { - // TODO: Change from `Int` header to `Void` once #1000 is fixed - var _storage: DynamicBuffer + var _storage: DynamicBuffer var _count: Int /// Creates a new, empty array @@ -33,10 +32,10 @@ public type Array : Deinitializable { while (new_capacity < x) { &new_capacity = capacity() + capacity() } - var new_storage = DynamicBuffer( + + var new_storage = DynamicBuffer( capacity: new_capacity, - initializing_header_with: fun(_ h: set Int) -> Void { &h = 0 } - ) + initializing_header_with: fun (_ h: set Void) -> Void { &h = () }) var i = 0 var e = _storage.first_element_address() From d8234d42e6585668040e12e44ed0531903ea2adc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 13 Sep 2023 16:52:10 +0200 Subject: [PATCH 07/85] Document 'Array''s properties --- Library/Hylo/Array.hylo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 83ada6679..e1f805355 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -1,7 +1,10 @@ /// An ordered, random-access collection. public type Array : Deinitializable { + /// The out-of-line storage of the array. var _storage: DynamicBuffer + + /// The number of elements in the array. var _count: Int /// Creates a new, empty array From 92506588bd1471a2bb5844ad9d029cf8db0aa501 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 13 Sep 2023 16:57:59 +0200 Subject: [PATCH 08/85] Reformat code --- Library/Hylo/Array.hylo | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index e1f805355..b7a72f8b4 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -7,32 +7,33 @@ public type Array : Deinitializable { /// The number of elements in the array. var _count: Int - /// Creates a new, empty array + /// Creates a new, empty array. public init() { &_storage = .new() &_count = 0 } - /// The number of elements in the array + /// The number of elements in the array. public fun count() -> Int { return _count.copy() } + + /// The number of elements that can be stored in the array before new storage must be allocated. public fun capacity() -> Int { return _storage.capacity() } /// Reserves enough space to store `x` elements - public fun reserveCapacity( _ x : Int) { - if( x >= capacity() ) { - return - } - var new_capacity : Int; - if( capacity() == 0 ) { - &new_capacity = 1; + public fun reserveCapacity(_ x : Int) { + if x >= capacity() { return } + + var new_capacity: Int + if capacity() == 0 { + &new_capacity = 1 } else { - &new_capacity = capacity(); + &new_capacity = capacity() } - while (new_capacity < x) { + while new_capacity < x { &new_capacity = capacity() + capacity() } @@ -43,14 +44,14 @@ public type Array : Deinitializable { var i = 0 var e = _storage.first_element_address() var f = new_storage.first_element_address() - while( i < count() ) { + while i < count() { // TODO: This is currently blocked by #1003 // f.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { // &x = e.unsafe[] // } ) - &e = e.advance( by: 1 ) - &f = f.advance( by: 1 ) + &e = e.advance(by: 1) + &f = f.advance(by: 1) } &_storage = new_storage @@ -58,7 +59,7 @@ public type Array : Deinitializable { /// Adds a new element at the end of the array. public fun append(from source: sink Element) { - reserveCapacity( count() + 1 ) + reserveCapacity(count() + 1) var p = _storage.first_element_address().advance(by: count()) // TODO: Currently blocked by #1003 // p.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { From 704a81937453737dc95804971bc1ccce1cc5780d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 13 Sep 2023 18:21:44 +0200 Subject: [PATCH 09/85] Simplify the computation of minimum capacity --- Library/Hylo/Array.hylo | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index b7a72f8b4..9130099dc 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -27,12 +27,7 @@ public type Array : Deinitializable { public fun reserveCapacity(_ x : Int) { if x >= capacity() { return } - var new_capacity: Int - if capacity() == 0 { - &new_capacity = 1 - } else { - &new_capacity = capacity() - } + var new_capacity = max[1, capacity()].copy() while new_capacity < x { &new_capacity = capacity() + capacity() } From 984b3440d00dbcaaee26e60be078978b1cda6340 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 13 Sep 2023 18:30:47 +0200 Subject: [PATCH 10/85] Rename 'Array.reserveCapacity' to 'reserve_capacity' --- Library/Hylo/Array.hylo | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 9130099dc..c52afcd22 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -23,12 +23,12 @@ public type Array : Deinitializable { return _storage.capacity() } - /// Reserves enough space to store `x` elements - public fun reserveCapacity(_ x : Int) { - if x >= capacity() { return } + /// Reserves enough space to store `n` elements + public fun reserve_capacity(_ n: Int) { + if n >= capacity() { return } var new_capacity = max[1, capacity()].copy() - while new_capacity < x { + while new_capacity < n { &new_capacity = capacity() + capacity() } @@ -54,7 +54,7 @@ public type Array : Deinitializable { /// Adds a new element at the end of the array. public fun append(from source: sink Element) { - reserveCapacity(count() + 1) + reserve_capacity(count() + 1) var p = _storage.first_element_address().advance(by: count()) // TODO: Currently blocked by #1003 // p.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { From 7bf129646b29ffa228699710a007049a0f2fb6d1 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 13 Sep 2023 18:31:12 +0200 Subject: [PATCH 11/85] Add missing explicit capture (wip) --- Library/Hylo/Array.hylo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index c52afcd22..d331a7f6f 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -56,10 +56,10 @@ public type Array : Deinitializable { public fun append(from source: sink Element) { reserve_capacity(count() + 1) var p = _storage.first_element_address().advance(by: count()) - // TODO: Currently blocked by #1003 - // p.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { - // &x = source - // } ) + // TODO: This is currently blocked by #1006 + // p.unsafe_initialize_pointee(fun[sink let s = source] (_ x: set Element) -> Void { + // &x = s + // }) } } From 68b42bd90c6b19df6228d8e208494bd2534e6522 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 11:37:18 +0200 Subject: [PATCH 12/85] Refactor the representation of bundle references --- Sources/IR/Emitter.swift | 8 ++--- .../Operands/Constant/BundleReference.swift | 30 +++++++++++++++++++ .../Constant/SubscriptBundleReference.swift | 30 ------------------- .../Operands/Instruction/ProjectBundle.swift | 6 ++-- 4 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 Sources/IR/Operands/Constant/BundleReference.swift delete mode 100644 Sources/IR/Operands/Constant/SubscriptBundleReference.swift diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index bf746b5eb..8805a3b33 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1655,7 +1655,7 @@ struct Emitter { private mutating func emit( subscriptCallee callee: AnyExprID - ) -> (callee: SubscriptBundleReference, captures: [Operand]) { + ) -> (callee: BundleReference, captures: [Operand]) { // TODO: Handle captures switch callee.kind { case NameExpr.self: @@ -1672,7 +1672,7 @@ struct Emitter { private mutating func emit( namedSubscriptCallee callee: NameExpr.ID - ) -> (callee: SubscriptBundleReference, captures: [Operand]) { + ) -> (callee: BundleReference, captures: [Operand]) { switch program[callee].referredDecl { case .direct(let d, let a) where d.kind == SubscriptDecl.self: // Callee is a direct reference to a subscript declaration. @@ -1681,12 +1681,12 @@ struct Emitter { UNIMPLEMENTED() } - let b = SubscriptBundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) return (b, []) case .member(let d, let a, let s) where d.kind == SubscriptDecl.self: // Callee is a member reference to a subscript declaration. - let b = SubscriptBundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) // The callee's receiver is the sole capture. let receiver = emitLValue(receiver: s, at: ast[callee].site) diff --git a/Sources/IR/Operands/Constant/BundleReference.swift b/Sources/IR/Operands/Constant/BundleReference.swift new file mode 100644 index 000000000..18fd31cbb --- /dev/null +++ b/Sources/IR/Operands/Constant/BundleReference.swift @@ -0,0 +1,30 @@ +import Core + +/// A Hylo IR reference to a method or subscript bundle. +public struct BundleReference: Hashable { + + /// The ID of the referred bundle. + public let bundle: T.ID + + /// If `bundle` is generic, the arguments to its generic parameter. + public let arguments: GenericArguments + + /// Creates a reference to `s` parameterized by `a`. + public init(to s: T.ID, parameterizedBy a: GenericArguments) { + self.bundle = s + self.arguments = a + } + +} + +extension BundleReference: CustomStringConvertible { + + public var description: String { + if arguments.isEmpty { + return "@\(bundle)" + } else { + return "@\(bundle)<\(list: arguments.values)>" + } + } + +} diff --git a/Sources/IR/Operands/Constant/SubscriptBundleReference.swift b/Sources/IR/Operands/Constant/SubscriptBundleReference.swift deleted file mode 100644 index edfb15bc2..000000000 --- a/Sources/IR/Operands/Constant/SubscriptBundleReference.swift +++ /dev/null @@ -1,30 +0,0 @@ -import Core - -/// A Hylo IR reference to a user subscript bundle. -public struct SubscriptBundleReference: Hashable { - - /// The ID of the referred IR subscript bundle. - public let bundle: SubscriptDecl.ID - - /// If `function` is generic, the arguments to its generic parameter. - public let arguments: GenericArguments - - /// Creates a reference to `s` parameterized by `a`. - public init(to s: SubscriptDecl.ID, parameterizedBy a: GenericArguments) { - self.bundle = s - self.arguments = a - } - -} - -extension SubscriptBundleReference: CustomStringConvertible { - - public var description: String { - if arguments.isEmpty { - return "@\(bundle)" - } else { - return "@\(bundle)<\(list: arguments.values)>" - } - } - -} diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index e7786e1f2..ebe53262b 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -4,7 +4,7 @@ import Core public struct ProjectBundle: Instruction { /// The subscript bundle implementing the projections. - public let bundle: SubscriptBundleReference + public let bundle: BundleReference /// The pure functional type of the callee. public let pureCalleeType: LambdaType @@ -23,7 +23,7 @@ public struct ProjectBundle: Instruction { /// Creates an instance with the given properties. fileprivate init( - bundle: SubscriptBundleReference, + bundle: BundleReference, pureCalleeType: LambdaType, variants: [AccessEffect: Function.ID], operands: [Operand], @@ -83,7 +83,7 @@ extension Module { /// - Requires: `bundleType` is canonical and `variants` is not empty. func makeProjectBundle( applying variants: [AccessEffect: Function.ID], - of bundle: SubscriptBundleReference, + of bundle: BundleReference, typed bundleType: SubscriptType, to arguments: [Operand], at site: SourceRange From 5d4432f0896562c4402fb8bbedfc99edc0a6c3ce Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 11:56:08 +0200 Subject: [PATCH 13/85] Make 'Module.isBorrowSet(_:)' internal --- Sources/IR/Module.swift | 9 +++++++++ Sources/IR/Operands/Instruction/Call.swift | 9 --------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index f99bfe9f3..1178a517b 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -791,4 +791,13 @@ public struct Module { } } + /// Returns `true` iff `o` is an `access [set]` instruction. + func isBorrowSet(_ o: Operand) -> Bool { + guard + let i = o.instruction, + let s = self[i] as? Access + else { return false } + return s.capabilities == [.set] + } + } diff --git a/Sources/IR/Operands/Instruction/Call.swift b/Sources/IR/Operands/Instruction/Call.swift index 2e572481e..f998aa06b 100644 --- a/Sources/IR/Operands/Instruction/Call.swift +++ b/Sources/IR/Operands/Instruction/Call.swift @@ -77,13 +77,4 @@ extension Module { return .init(callee: callee, output: output, arguments: arguments, site: site) } - /// Returns `true` iff `o` is an `access [set]` instruction. - fileprivate func isBorrowSet(_ o: Operand) -> Bool { - guard - let i = o.instruction, - let s = self[i] as? Access - else { return false } - return s.capabilities == [.set] - } - } From 9d16eacff02fd2f3b4e25c854ae070c3191a8189 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:17:00 +0200 Subject: [PATCH 14/85] Simplify 'Module.makeProjectBundle' --- Sources/FrontEnd/TypedProgram.swift | 8 +++++ Sources/IR/Emitter.swift | 23 +++---------- .../Operands/Instruction/ProjectBundle.swift | 32 +++++++++---------- 3 files changed, 29 insertions(+), 34 deletions(-) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index 5e4c2fc15..adb362dff 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -428,6 +428,14 @@ public struct TypedProgram { } } + /// Returns the type of `d` specialized by `specialization` in `scopeOfUse`. + public func canonicalType( + of d: T.ID, specializedBy specialization: GenericArguments, in scopeOfUse: AnyScopeID + ) -> AnyType { + let t = specialize(self[d].type, for: specialization, in: scopeOfUse) + return canonical(t, in: scopeOfUse) + } + /// Returns the declarations of `d`' captures. /// /// If `d` is a member, its receiver is its only capture. Otherwise, this method returns diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 8805a3b33..a227e1996 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2034,17 +2034,9 @@ struct Emitter { let (callee, captures) = emit(subscriptCallee: ast[e].callee) let arguments = captures + explicitArguments - var variants: [AccessEffect: Function.ID] = [:] - for v in ast[callee.bundle].impls { - variants[ast[v].introducer.value] = module.demandDeclaration(lowering: v) - } - - // Projection is evaluated last. - let t = canonicalType(of: callee.bundle, specializedBy: callee.arguments) - return insert( - module.makeProjectBundle( - applying: variants, of: callee, typed: SubscriptType(t)!, - to: arguments, at: ast[e].site))! + let s = module.makeProjectBundle( + applying: callee, to: arguments, in: insertionScope!, at: ast[e].site) + return insert(s)! } /// Inserts the IR for lvalue `e`. @@ -2148,14 +2140,9 @@ struct Emitter { let t = SubscriptType(canonicalType(of: d, specializedBy: specialization))! let r = insert(module.makeAccess(t.capabilities, from: receiver, at: site))! - var variants: [AccessEffect: Function.ID] = [:] - for v in ast[d].impls { - variants[ast[v].introducer.value] = module.demandDeclaration(lowering: v) - } - let s = module.makeProjectBundle( - applying: variants, of: .init(to: d, parameterizedBy: specialization), typed: t, - to: [r], at: site) + applying: .init(to: d, parameterizedBy: specialization), to: [r], + in: insertionScope!, at: site) return insert(s)! } diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index ebe53262b..d53c8fcf5 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -14,8 +14,8 @@ public struct ProjectBundle: Instruction { /// The arguments of the call. /// - /// Operands to non-`sink` inputs must be the result of an `access` instruction requesting a - /// capability for each variant in `callee` and having no use before `project`. + /// Operands to must be the result of an `access` instruction requesting a capability for each + /// variant in `callee` and having no use before `project_bundle`. public private(set) var operands: [Operand] /// The site of the code corresponding to that instruction. @@ -77,24 +77,24 @@ extension ProjectBundle: CustomStringConvertible { extension Module { /// Creates a `project_bundle` anchored at `site` that projects a value by applying one of the - /// given `variants` on `arguments`. The variants are defined in `bundle`, which is has type - /// `bundleType`. - /// - /// - Requires: `bundleType` is canonical and `variants` is not empty. - func makeProjectBundle( - applying variants: [AccessEffect: Function.ID], - of bundle: BundleReference, - typed bundleType: SubscriptType, + /// variants in `bundle` on `arguments`, canonicalizing types in `scopeOfUse`. + mutating func makeProjectBundle( + applying bundle: BundleReference, to arguments: [Operand], + in scopeOfUse: AnyScopeID, at site: SourceRange ) -> ProjectBundle { - precondition(bundleType[.isCanonical]) + var variants: [AccessEffect: Function.ID] = [:] + for v in program[bundle.bundle].impls { + variants[program[v].introducer.value] = demandDeclaration(lowering: v) + } + + let bundleType = program.canonicalType( + of: bundle.bundle, specializedBy: bundle.arguments, in: scopeOfUse) + let t = SubscriptType(bundleType)!.pure + return .init( - bundle: bundle, - pureCalleeType: bundleType.pure, - variants: variants, - operands: arguments, - site: site) + bundle: bundle, pureCalleeType: t, variants: variants, operands: arguments, site: site) } } From abbae56d164e2a5938a789d04dc1ba3e074cce5c Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:28:54 +0200 Subject: [PATCH 15/85] Refactor the representation of 'project_bundle' --- .../Analysis/Module+AccessReification.swift | 2 +- .../Operands/Instruction/ProjectBundle.swift | 30 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Sources/IR/Analysis/Module+AccessReification.swift b/Sources/IR/Analysis/Module+AccessReification.swift index 2408cb41b..665468380 100644 --- a/Sources/IR/Analysis/Module+AccessReification.swift +++ b/Sources/IR/Analysis/Module+AccessReification.swift @@ -145,7 +145,7 @@ extension Module { arguments[a] = .register(insert(b, before: i)) } - let o = RemoteType(k, s.projection.bareType) + let o = RemoteType(k, s.projection) let reified = makeProject( o, applying: s.variants[k]!, specializedBy: s.bundle.arguments, to: arguments, at: s.site) replace(i, with: reified) diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index d53c8fcf5..e7b540b28 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -6,8 +6,11 @@ public struct ProjectBundle: Instruction { /// The subscript bundle implementing the projections. public let bundle: BundleReference - /// The pure functional type of the callee. - public let pureCalleeType: LambdaType + /// The parameters of the subscript. + public let parameters: [ParameterType] + + /// The type of the projected value. + public let projection: AnyType /// The subscripts implementing the projection. public let variants: [AccessEffect: Function.ID] @@ -24,14 +27,16 @@ public struct ProjectBundle: Instruction { /// Creates an instance with the given properties. fileprivate init( bundle: BundleReference, - pureCalleeType: LambdaType, variants: [AccessEffect: Function.ID], + parameters: [ParameterType], + projection: AnyType, operands: [Operand], site: SourceRange ) { self.bundle = bundle - self.pureCalleeType = pureCalleeType self.variants = variants + self.parameters = parameters + self.projection = projection self.operands = operands self.site = site } @@ -41,19 +46,9 @@ public struct ProjectBundle: Instruction { .init(variants.keys) } - /// The type of the projected value. - public var projection: RemoteType { - RemoteType(pureCalleeType.output)! - } - - /// The parameters of the projection. - public var parameters: LazyMapSequence<[CallableTypeParameter], ParameterType> { - pureCalleeType.inputs.lazy.map({ ParameterType($0.type)! }) - } - /// The types of the instruction's results. public var result: IR.`Type`? { - .address(projection.bareType) + .address(projection) } public mutating func replaceOperand(at i: Int, with new: Operand) { @@ -94,7 +89,10 @@ extension Module { let t = SubscriptType(bundleType)!.pure return .init( - bundle: bundle, pureCalleeType: t, variants: variants, operands: arguments, site: site) + bundle: bundle, variants: variants, + parameters: t.inputs.lazy.map({ ParameterType($0.type)! }), + projection: RemoteType(t.output)!.bareType, + operands: arguments, site: site) } } From bc772ccc0a51fe3b60b8d914384674cf9263582f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:39:46 +0200 Subject: [PATCH 16/85] Remove needless label --- Sources/IR/FunctionID.swift | 2 +- Sources/IR/Module.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index 41e121451..492d7cc4c 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -34,7 +34,7 @@ extension Function { } /// Creates the identity of the lowered form of `f` used as an initializer. - public init(initializer f: InitializerDecl.ID) { + public init(_ f: InitializerDecl.ID) { self.value = .lowered(AnyDeclID(f)) } diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 1178a517b..09323e371 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -307,7 +307,7 @@ public struct Module { mutating func demandDeclaration(lowering d: InitializerDecl.ID) -> Function.ID { precondition(!program.ast[d].isMemberwise) - let f = Function.ID(initializer: d) + let f = Function.ID(d) if functions[f] != nil { return f } let parameters = program.accumulatedGenericParameters(in: d) From 3f265e2221f1897178ea31bbe92bcb120cba2f88 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:46:49 +0200 Subject: [PATCH 17/85] Refactor the initializers of 'Function.ID' --- Sources/IR/FunctionID.swift | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index 492d7cc4c..626819a15 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -1,4 +1,5 @@ import Core +import Utils extension Function { @@ -28,19 +29,17 @@ extension Function { /// The value of this identity. public let value: Value - /// Creates the identity of the lowered form of `f`. - public init(_ f: FunctionDecl.ID) { - self.value = .lowered(AnyDeclID(f)) - } - - /// Creates the identity of the lowered form of `f` used as an initializer. - public init(_ f: InitializerDecl.ID) { - self.value = .lowered(AnyDeclID(f)) - } - - /// Creates the identity of the lowered form of `s`. - public init(_ s: SubscriptImpl.ID) { - self.value = .loweredSubscript(s) + /// Creates the identity of the lowered form of `d`, which is the declaration of a function, + /// initializer or subscript implementation.s + public init(_ d: T) { + switch d.kind { + case FunctionDecl.self, InitializerDecl.self: + self.value = .lowered(AnyDeclID(d)) + case SubscriptImpl.self: + self.value = .loweredSubscript(SubscriptImpl.ID(d)!) + default: + unreachable() + } } /// Creates the identity of the lowered form of `s`. From a2dbe17be72cc120f50fd83101116218a96c87c3 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 15:47:53 +0200 Subject: [PATCH 18/85] Improve encapsulation --- Sources/IR/FunctionID.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index 626819a15..eebd5d193 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -30,8 +30,8 @@ extension Function { public let value: Value /// Creates the identity of the lowered form of `d`, which is the declaration of a function, - /// initializer or subscript implementation.s - public init(_ d: T) { + /// initializer, or subscript implementation.s + init(_ d: T) { switch d.kind { case FunctionDecl.self, InitializerDecl.self: self.value = .lowered(AnyDeclID(d)) @@ -43,7 +43,7 @@ extension Function { } /// Creates the identity of the lowered form of `s`. - public init(_ s: SynthesizedFunctionDecl) { + init(_ s: SynthesizedFunctionDecl) { self.value = .synthesized(s) } From d188d66be37c57907150c7d4c974496a4f880c5b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 10:52:36 +0200 Subject: [PATCH 19/85] Move 'BundleReference.swift' to 'Sources/IR/' --- Sources/IR/{Operands/Constant => }/BundleReference.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Sources/IR/{Operands/Constant => }/BundleReference.swift (100%) diff --git a/Sources/IR/Operands/Constant/BundleReference.swift b/Sources/IR/BundleReference.swift similarity index 100% rename from Sources/IR/Operands/Constant/BundleReference.swift rename to Sources/IR/BundleReference.swift From 0c4698135cf847809374a944e7da0d8d25e7ebc8 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:03:14 +0200 Subject: [PATCH 20/85] Add an abstraction representing callees in IR --- Sources/IR/Callee.swift | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Sources/IR/Callee.swift diff --git a/Sources/IR/Callee.swift b/Sources/IR/Callee.swift new file mode 100644 index 000000000..39b2851f4 --- /dev/null +++ b/Sources/IR/Callee.swift @@ -0,0 +1,30 @@ +import Core + +/// The callee of a function call in the IR. +enum Callee { + + /// A direct reference to a function, initializer, or method implementation. + case direct(FunctionReference) + + /// A reference to a variant in a method bundle. + case bundle(BundleReference) + + /// A lambda. + case lambda(Operand) + + /// Creates an instance representing a reference to the lowered form of `d` in `module`, + /// specialized by`a` in `scopeOfUse`. + init( + _ d: AnyDeclID, specializedBy a: GenericArguments, + in module: inout Module, usedIn scopeOfUse: AnyScopeID + ) { + if let m = MethodDecl.ID(d) { + self = .bundle(BundleReference(to: m, specializedBy: a)) + } else { + let r = FunctionReference( + to: FunctionDecl.ID(d)!, in: &module, specializedBy: a, in: scopeOfUse) + self = .direct(r) + } + } + +} From 9b9bf162f769d99619083b03a4adbc88cb5ee0b5 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:05:47 +0200 Subject: [PATCH 21/85] Improve API and docs --- Sources/IR/BundleReference.swift | 4 ++-- Sources/IR/Emitter.swift | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Sources/IR/BundleReference.swift b/Sources/IR/BundleReference.swift index 18fd31cbb..8a2cdd2d5 100644 --- a/Sources/IR/BundleReference.swift +++ b/Sources/IR/BundleReference.swift @@ -1,6 +1,6 @@ import Core -/// A Hylo IR reference to a method or subscript bundle. +/// A reference to a method or subscript bundle. public struct BundleReference: Hashable { /// The ID of the referred bundle. @@ -10,7 +10,7 @@ public struct BundleReference: Hashable { public let arguments: GenericArguments /// Creates a reference to `s` parameterized by `a`. - public init(to s: T.ID, parameterizedBy a: GenericArguments) { + public init(to s: T.ID, specializedBy a: GenericArguments) { self.bundle = s self.arguments = a } diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index a227e1996..b41647ef1 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1681,12 +1681,12 @@ struct Emitter { UNIMPLEMENTED() } - let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, specializedBy: a) return (b, []) case .member(let d, let a, let s) where d.kind == SubscriptDecl.self: // Callee is a member reference to a subscript declaration. - let b = BundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) + let b = BundleReference(to: SubscriptDecl.ID(d)!, specializedBy: a) // The callee's receiver is the sole capture. let receiver = emitLValue(receiver: s, at: ast[callee].site) @@ -2137,6 +2137,7 @@ struct Emitter { boundTo: receiver, declaredBy: i, specializedBy: specialization, at: site) } + let callee = BundleReference(to: d, specializedBy: specialization) let t = SubscriptType(canonicalType(of: d, specializedBy: specialization))! let r = insert(module.makeAccess(t.capabilities, from: receiver, at: site))! From c08f2bffd6f3580885a541651c320bb8d4f5039b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:06:46 +0200 Subject: [PATCH 22/85] Improve API and docs --- Sources/IR/Emitter.swift | 5 ++--- Sources/IR/Operands/Instruction/ProjectBundle.swift | 11 +++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index b41647ef1..c9f4f4f7c 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2035,7 +2035,7 @@ struct Emitter { let arguments = captures + explicitArguments let s = module.makeProjectBundle( - applying: callee, to: arguments, in: insertionScope!, at: ast[e].site) + applying: .init(callee, in: insertionScope!), to: arguments, at: ast[e].site) return insert(s)! } @@ -2142,8 +2142,7 @@ struct Emitter { let r = insert(module.makeAccess(t.capabilities, from: receiver, at: site))! let s = module.makeProjectBundle( - applying: .init(to: d, parameterizedBy: specialization), to: [r], - in: insertionScope!, at: site) + applying: .init(callee, in: insertionScope!), to: [r], at: site) return insert(s)! } diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index e7b540b28..3cff64b72 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -72,24 +72,23 @@ extension ProjectBundle: CustomStringConvertible { extension Module { /// Creates a `project_bundle` anchored at `site` that projects a value by applying one of the - /// variants in `bundle` on `arguments`, canonicalizing types in `scopeOfUse`. + /// variants in `bundle` on `arguments`. mutating func makeProjectBundle( - applying bundle: BundleReference, + applying bundle: ScopedValue>, to arguments: [Operand], - in scopeOfUse: AnyScopeID, at site: SourceRange ) -> ProjectBundle { var variants: [AccessEffect: Function.ID] = [:] - for v in program[bundle.bundle].impls { + for v in program[bundle.value.bundle].impls { variants[program[v].introducer.value] = demandDeclaration(lowering: v) } let bundleType = program.canonicalType( - of: bundle.bundle, specializedBy: bundle.arguments, in: scopeOfUse) + of: bundle.value.bundle, specializedBy: bundle.value.arguments, in: bundle.scope) let t = SubscriptType(bundleType)!.pure return .init( - bundle: bundle, variants: variants, + bundle: bundle.value, variants: variants, parameters: t.inputs.lazy.map({ ParameterType($0.type)! }), projection: RemoteType(t.output)!.bareType, operands: arguments, site: site) From 8b712e25a9945e68857cad9f3ee3a2f7f1f692fb Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:07:08 +0200 Subject: [PATCH 23/85] Fix comments --- Sources/FrontEnd/TypedProgram.swift | 4 ++-- Sources/IR/Operands/Instruction/Call.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index adb362dff..3a10e8d46 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -436,7 +436,7 @@ public struct TypedProgram { return canonical(t, in: scopeOfUse) } - /// Returns the declarations of `d`' captures. + /// Returns the declarations of `d`'s captures. /// /// If `d` is a member, its receiver is its only capture. Otherwise, this method returns /// `nonMemberCaptures(d)`. @@ -447,7 +447,7 @@ public struct TypedProgram { return nonMemberCaptures(of: d) } - /// Returns the declarations of `d`' captures. + /// Returns the declarations of `d`'s captures. /// /// If `d` is a member, its receiver is its only capture. Otherwise, this method returns /// `nonMemberCaptures(d)`. diff --git a/Sources/IR/Operands/Instruction/Call.swift b/Sources/IR/Operands/Instruction/Call.swift index f998aa06b..92c7f3f57 100644 --- a/Sources/IR/Operands/Instruction/Call.swift +++ b/Sources/IR/Operands/Instruction/Call.swift @@ -7,7 +7,7 @@ import Utils /// of the callee. `operands` must contain as many operands as the callee's type. public struct Call: Instruction { - /// The callee and arguments of the call. + /// The callee, the return storage, and arguments of the call. public private(set) var operands: [Operand] /// The site of the code corresponding to that instruction. From 398ee2d3ff72e86a1377038bf0e39478fb73528f Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:07:47 +0200 Subject: [PATCH 24/85] Generate IR for calls to method bundles --- Sources/IR/Emitter.swift | 79 +++++++++++----- Sources/IR/FunctionID.swift | 4 +- Sources/IR/Module.swift | 32 +++++++ .../IR/Operands/Instruction/CallBundle.swift | 91 +++++++++++++++++++ 4 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 Sources/IR/Operands/Instruction/CallBundle.swift diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index c9f4f4f7c..6bfca5f9a 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1164,8 +1164,14 @@ struct Emitter { let arguments = captures + explicitArguments // Call is evaluated last. - let o = insert(module.makeAccess(.set, from: storage, at: ast[e].site))! - insert(module.makeCall(applying: callee, to: arguments, writingResultTo: o, at: ast[e].site)) + switch callee { + case .direct(let r): + emitApply(.constant(r), to: arguments, writingResultTo: storage, at: ast[e].site) + case .lambda(let r): + emitApply(r, to: arguments, writingResultTo: storage, at: ast[e].site) + case .bundle(let r): + emitApply(r, to: arguments, writingResultTo: storage, at: ast[e].site) + } } /// Inserts the IR for storing the value of `e` to `storage`. @@ -1404,6 +1410,27 @@ struct Emitter { frames.top.setMayHoldCaptures(s) } + /// Inserts the IR for calling `callee` on `arguments`, storing the result to `storage`. + private mutating func emitApply( + _ callee: Operand, to arguments: [Operand], + writingResultTo storage: Operand, at site: SourceRange + ) { + let o = insert(module.makeAccess(.set, from: storage, at: site))! + insert(module.makeCall(applying: callee, to: arguments, writingResultTo: o, at: site)) + insert(module.makeEndAccess(o, at: site)) + } + + /// Inserts the IR for calling `callee` on `arguments`, storing the result to `storage`. + private mutating func emitApply( + _ callee: BundleReference, to arguments: [Operand], + writingResultTo storage: Operand, at site: SourceRange + ) { + let o = insert(module.makeAccess(.set, from: storage, at: site))! + insert(module.makeCallBundle( + applying: .init(callee, in: insertionScope!), to: arguments, writingResultTo: o, at: site)) + insert(module.makeEndAccess(o, at: site)) + } + /// Inserts the IR for given constructor `call`, which initializes storage `r` by applying /// initializer `d` parameterized by `a`. /// @@ -1579,7 +1606,7 @@ struct Emitter { /// - Requires: `callee` has a lambda type. private mutating func emit( functionCallee callee: AnyExprID - ) -> (callee: Operand, captures: [Operand]) { + ) -> (callee: Callee, captures: [Operand]) { switch callee.kind { case NameExpr.self: return emit(namedFunctionCallee: .init(callee)!) @@ -1589,41 +1616,36 @@ struct Emitter { return emit(functionCallee: ast[InoutExpr.ID(callee)!].subject) default: - return (emit(lambdaCallee: callee), []) + let f = emit(lambdaCallee: callee) + return (.lambda(f), []) } } /// Inserts the IR for given `callee` and returns `(c, a)`, where `c` is the callee's value and /// `a` are arguments to lifted parameters. - /// - /// - Requires: `callee` has a lambda type. private mutating func emit( namedFunctionCallee callee: NameExpr.ID - ) -> (callee: Operand, captures: [Operand]) { - let calleeType = LambdaType(canonical(program[callee].type))! - + ) -> (callee: Callee, captures: [Operand]) { switch program[callee].referredDecl { case .direct(let d, let a) where d.kind == FunctionDecl.self: // Callee is a direct reference to a function declaration. - guard calleeType.environment == .void else { + guard LambdaType(canonical(program[callee].type))!.environment == .void else { UNIMPLEMENTED() } let specialization = module.specialization(in: insertionFunction!).merging(a) - let r = FunctionReference( + let f = FunctionReference( to: FunctionDecl.ID(d)!, in: &module, specializedBy: specialization, in: insertionScope!) - return (.constant(r), []) + return (.direct(f), []) - case .member(let d, let a, let s) where d.kind == FunctionDecl.self: - // Callee is a member reference to a function or method. - let r = FunctionReference( - to: FunctionDecl.ID(d)!, in: &module, specializedBy: a, in: insertionScope!) - - // The callee's receiver is the sole capture. + case .member(let d, let a, let s): + // Callee is a member reference to a function or method. Its receiver is the only capture. + let specialization = module.specialization(in: insertionFunction!).merging(a) let receiver = emitLValue(receiver: s, at: ast[callee].site) - let k = RemoteType(calleeType.captures[0].type)?.access ?? .sink - let i = insert(module.makeAccess(k, from: receiver, at: ast[callee].site))! - return (Operand.constant(r), [i]) + let k = receiverCapabilities(program[callee].type) + let c = insert(module.makeAccess(k, from: receiver, at: ast[callee].site))! + let f = Callee(d, specializedBy: specialization, in: &module, usedIn: insertionScope!) + return (f, [c]) case .builtinFunction, .builtinType: // Calls to built-ins should have been handled already. @@ -1632,7 +1654,7 @@ struct Emitter { default: // Callee is a lambda. let f = emit(lambdaCallee: .init(callee)) - return (f, []) + return (.lambda(f), []) } } @@ -2394,6 +2416,19 @@ struct Emitter { return canonical(t) } + /// Returns the capabilities required by the receiver of `callee`, which is the type of a member + /// function or method bundle. + private func receiverCapabilities(_ callee: AnyType) -> AccessEffectSet { + switch canonical(callee).base { + case let t as LambdaType: + return [RemoteType(t.captures[0].type)?.access ?? .sink] + case let t as MethodType: + return t.capabilities + default: + unreachable() + } + } + /// Inserts a stack allocation for an object of type `t`. private mutating func emitAllocStack( for t: AnyType, at site: SourceRange diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index eebd5d193..22977970c 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -30,10 +30,10 @@ extension Function { public let value: Value /// Creates the identity of the lowered form of `d`, which is the declaration of a function, - /// initializer, or subscript implementation.s + /// initializer, method implementation, or subscript implementation.s init(_ d: T) { switch d.kind { - case FunctionDecl.self, InitializerDecl.self: + case FunctionDecl.self, InitializerDecl.self, MethodImpl.self: self.value = .lowered(AnyDeclID(d)) case SubscriptImpl.self: self.value = .loweredSubscript(SubscriptImpl.ID(d)!) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 09323e371..2865a9953 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -280,6 +280,30 @@ public struct Module { return f } + /// Returns the identity of the IR function corresponding to `d`. + mutating func demandDeclaration(lowering d: MethodImpl.ID) -> Function.ID { + let f = Function.ID(d) + if functions[f] != nil { return f } + + let parameters = program.accumulatedGenericParameters(in: d) + let output = program.canonical( + (program[d].type.base as! CallableType).output, in: program[d].scope) + + let inputs = loweredParameters(of: d) + + let entity = Function( + isSubscript: false, + site: program.ast[d].site, + linkage: program.isExported(d) ? .external : .module, + genericParameters: Array(parameters), + inputs: inputs, + output: output, + blocks: []) + addFunction(entity, for: f) + + return f + } + /// Returns the identity of the IR function corresponding to `d`. mutating func demandDeclaration(lowering d: SubscriptImpl.ID) -> Function.ID { let f = Function.ID(d) @@ -407,6 +431,14 @@ public struct Module { return result } + /// Returns the lowered declarations of `d`'s parameters. + private func loweredParameters(of d: MethodImpl.ID) -> [Parameter] { + let bundle = MethodDecl.ID(program[d].scope)! + let inputs = program.ast[bundle].parameters + let r = Parameter(AnyDeclID(program[d].receiver), capturedAs: program[d].receiver.type) + return [r] + inputs.map(pairedWithLoweredType(parameter:)) + } + /// Returns the lowered declarations of `d`'s parameters. /// /// `d`'s receiver comes first and is followed by `d`'s formal parameters, from left to right. diff --git a/Sources/IR/Operands/Instruction/CallBundle.swift b/Sources/IR/Operands/Instruction/CallBundle.swift new file mode 100644 index 000000000..8f642dbe6 --- /dev/null +++ b/Sources/IR/Operands/Instruction/CallBundle.swift @@ -0,0 +1,91 @@ +import Core +import Utils + +/// Invokes one variant of `bundle` with `arguments` and writes its result to `output`. +public struct CallBundle: Instruction { + + /// The method bundle implementing the variant to call. + public let bundle: BundleReference + + /// The type of the bundle. + public let bundleType: MethodType + + /// The variants of the bundle. + public let variants: [AccessEffect: Function.ID] + + /// The return storage and arguments of the call. + public private(set) var operands: [Operand] + + /// The site of the code corresponding to that instruction. + public let site: SourceRange + + /// Creates an instance with the given properties. + fileprivate init( + bundle: BundleReference, + bundleType: MethodType, + variants: [AccessEffect: Function.ID], + output: Operand, + arguments: [Operand], + site: SourceRange + ) { + self.bundle = bundle + self.bundleType = bundleType + self.variants = variants + self.operands = [output] + arguments + self.site = site + } + + /// The capabilities possibly requested on the receiver. + public var capabilities: AccessEffectSet { + .init(variants.keys) + } + + /// The location at which the result of `callee` is stored. + public var output: Operand { operands[0] } + + /// The arguments of the call. + public var arguments: ArraySlice { operands[1...] } + + public mutating func replaceOperand(at i: Int, with new: Operand) { + operands[i] = new + } + +} + +extension CallBundle: CustomStringConvertible { + + public var description: String { + "call_bundle \(capabilities) \(bundle)(\(list: arguments)) to \(output)" + } + +} + +extension Module { + + /// Creates a `call_bundle` anchored at `site` that calls one of the variants in `bundle` on + /// `arguments`. + mutating func makeCallBundle( + applying bundle: ScopedValue>, to arguments: [Operand], + writingResultTo output: Operand, + at site: SourceRange + ) -> CallBundle { + var variants: [AccessEffect: Function.ID] = [:] + for v in program[bundle.value.bundle].impls { + variants[program[v].introducer.value] = demandDeclaration(lowering: v) + } + + let bundleType = program.canonicalType( + of: bundle.value.bundle, specializedBy: bundle.value.arguments, in: bundle.scope) + let t = MethodType(bundleType)! + + precondition((t.inputs.count + 1) == arguments.count) + precondition(arguments.allSatisfy({ self[$0] is Access })) + precondition(isBorrowSet(output)) + + return .init( + bundle: bundle.value, bundleType: t, variants: variants, + output: output, arguments: arguments, + site: site) + } + +} From 4d24d70560fcd36e68794256e641739c3f5f7740 Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Thu, 14 Sep 2023 13:06:58 +0200 Subject: [PATCH 25/85] Add test case for exhausive branch returns Since the current behaviour is not the desired behaviour I add a case enforcing the current behaviour. On the commit that changes the behaviour we change this test aswel. That way we maintain the invariant that all commits pass the tests. --- .../EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo diff --git a/Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo b/Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo new file mode 100644 index 000000000..e7fdd7c11 --- /dev/null +++ b/Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo @@ -0,0 +1,9 @@ +//- compileAndRun expecting: failure + +public fun main() { + let _ = f() +} + +public fun f() -> Int { + if true { return 0 } else { return 1 } +} //! diagnostic missing return in function expected to return 'Int' From c1885342269f6ea113a1b6b689690f633bdf2f7b Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Fri, 15 Sep 2023 11:50:23 +0200 Subject: [PATCH 26/85] Add special case for return type missing in DI --- .../Analysis/Module+NormalizeObjectStates.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift index 16bbc2986..156cf4520 100644 --- a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift +++ b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift @@ -534,6 +534,16 @@ extension Module { if o.value == .full(.initialized) { return } if k == .set { + // If the parameter is a return value (index == 0) we emit specialised diagnostics + if case .parameter(_, let index) = p, index == 0 { + let t = self[f].output + if !t.isVoidOrNever { + diagnostics.insert( + .missingFunctionReturn(expectedReturnType: t, at: site) + ) + return + } + } diagnostics.insert( .uninitializedSetParameter(beforeReturningFrom: f, in: self, at: site)) return @@ -991,4 +1001,11 @@ extension Diagnostic { .error("use of uninitialized object", at: site) } + fileprivate static func missingFunctionReturn( + expectedReturnType: AnyType, + at site: SourceRange + ) -> Diagnostic { + .error("missing return in function expected to return '\(expectedReturnType)'", at: site) + } + } From 23180200e7332fee7bb30658ad71d8b4ebaa565f Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Fri, 1 Sep 2023 09:17:59 +0200 Subject: [PATCH 27/85] Remove blanket failure for void/never return This also inverts the negative test --- Sources/IR/Emitter.swift | 10 +--------- .../TestCases/ExhaustiveBranchReturns.hylo | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 6bfca5f9a..f4f4e6fcc 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -233,9 +233,7 @@ struct Emitter { ) -> SourceRange { switch emit(braceStmt: b) { case .next: - if !canonical(returnType).isVoidOrNever { - report(.error(missingReturn: returnType, at: .empty(atEndOf: ast[b].site))) - } else { + if canonical(returnType).isVoidOrNever { emitStore(value: .void, to: returnValue!, at: .empty(atEndOf: ast[b].site)) } return ast[b].site @@ -2609,12 +2607,6 @@ extension Diagnostic { .error("integer literal '\(s)' overflows when stored into '\(t)'", at: site) } - fileprivate static func error( - missingReturn returnType: AnyType, at site: SourceRange - ) -> Diagnostic { - .error("missing return in function expected to return '\(returnType)'", at: site) - } - fileprivate static func warning(unreachableStatement s: AnyStmtID, in ast: AST) -> Diagnostic { .error("statement will never be executed", at: .empty(at: ast[s].site.first())) } diff --git a/Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo b/Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo index e7fdd7c11..4775ef85d 100644 --- a/Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo +++ b/Tests/EndToEndTests/TestCases/ExhaustiveBranchReturns.hylo @@ -1,4 +1,4 @@ -//- compileAndRun expecting: failure +//- compileAndRun expecting: success public fun main() { let _ = f() @@ -6,4 +6,4 @@ public fun main() { public fun f() -> Int { if true { return 0 } else { return 1 } -} //! diagnostic missing return in function expected to return 'Int' +} From 874e58c8dffb48bd068ed3cea282099e449ee17e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 13:14:28 +0200 Subject: [PATCH 28/85] Update Sources/IR/Analysis/Module+NormalizeObjectStates.swift --- Sources/IR/Analysis/Module+NormalizeObjectStates.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift index 156cf4520..9858540fe 100644 --- a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift +++ b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift @@ -534,8 +534,8 @@ extension Module { if o.value == .full(.initialized) { return } if k == .set { - // If the parameter is a return value (index == 0) we emit specialised diagnostics - if case .parameter(_, let index) = p, index == 0 { + // If the parameter is a return value (index == 0) we emit specialized diagnostics. + if case .parameter(_, 0) = p { let t = self[f].output if !t.isVoidOrNever { diagnostics.insert( From ac8c7067dbdc2c62b3f55260d8488e6844366619 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 14:39:38 +0200 Subject: [PATCH 29/85] Request sink access on move sources during access reification --- Sources/IR/Analysis/Module+AccessReification.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/IR/Analysis/Module+AccessReification.swift b/Sources/IR/Analysis/Module+AccessReification.swift index 665468380..3a7caa68d 100644 --- a/Sources/IR/Analysis/Module+AccessReification.swift +++ b/Sources/IR/Analysis/Module+AccessReification.swift @@ -83,7 +83,7 @@ extension Module { case is Load: return [.sink] case is Move: - return [.inout] + return u.index == 0 ? [.sink] : [.inout] case is ProjectBundle: return requests(projectBundle: u) default: From 2eb970839149ff43a3a20969b4a550d395c1dfe1 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 16:35:37 +0200 Subject: [PATCH 30/85] Mangle method bundle declarations --- Sources/IR/Mangling/Demangler.swift | 6 ++++ Sources/IR/Mangling/Mangler.swift | 35 ++++++++++++++++++++++ Sources/IR/Mangling/ManglingOperator.swift | 6 ++++ 3 files changed, 47 insertions(+) diff --git a/Sources/IR/Mangling/Demangler.swift b/Sources/IR/Mangling/Demangler.swift index 142967e2c..a563d6654 100644 --- a/Sources/IR/Mangling/Demangler.swift +++ b/Sources/IR/Mangling/Demangler.swift @@ -113,6 +113,12 @@ struct Demangler { return nil case .monomorphizedFunctionDecl: return nil + case .methodDecl: + return nil + case .methodImpl: + return nil + case .methodType: + return nil case .synthesizedFunctionDecl: return nil case .conformanceConstraint, .equalityConstraint, .valueConstraint, .whereClause: diff --git a/Sources/IR/Mangling/Mangler.swift b/Sources/IR/Mangling/Mangler.swift index 874cb0964..43d7917c8 100644 --- a/Sources/IR/Mangling/Mangler.swift +++ b/Sources/IR/Mangling/Mangler.swift @@ -145,6 +145,10 @@ struct Mangler { write(initializer: InitializerDecl.ID(symbol)!, to: &output) case MatchCase.self: write(anonymousScope: symbol, to: &output) + case MethodDecl.self: + write(methodDecl: MethodDecl.ID(symbol)!, to: &output) + case MethodImpl.self: + write(methodImpl: MethodImpl.ID(symbol)!, to: &output) case ModuleDecl.self: write(entity: ModuleDecl.ID(symbol)!, to: &output) case NamespaceDecl.self: @@ -238,6 +242,19 @@ struct Mangler { mangle(type: program[d].type, to: &output) } + /// Writes the mangled representation of `d` to `output`. + private mutating func write(methodDecl d: MethodDecl.ID, to output: inout Output) { + write(operator: .methodDecl, to: &output) + write(string: program.ast[d].identifier.value, to: &output) + mangle(type: program[d].type, to: &output) + } + + /// Writes the mangled representation of `d` to `output`. + private mutating func write(methodImpl d: MethodImpl.ID, to output: inout Output) { + write(operator: .methodImpl, to: &output) + write(base64Digit: program.ast[d].introducer.value, to: &output) + } + /// Writes the mangled representation of `d` to `output`. private mutating func write(subscriptDecl d: SubscriptDecl.ID, to output: inout Output) { if program.ast[d].isProperty { @@ -383,6 +400,9 @@ struct Mangler { case let t as LambdaType: write(lambda: t, to: &output) + case let t as MethodType: + write(method: t, to: &output) + case let t as MetatypeType: write(operator: .metatypeType, to: &output) mangle(type: t.instance, to: &output) @@ -499,6 +519,21 @@ struct Mangler { mangle(type: t.output, to: &output) } + /// Writes the mangled representation of `symbol` to `output`. + private mutating func write(method t: MethodType, to output: inout Output) { + write(operator: .methodType, to: &output) + write(base64Digit: t.capabilities, to: &output) + mangle(type: t.receiver, to: &output) + + write(integer: t.inputs.count, to: &output) + for i in t.inputs { + write(string: i.label ?? "", to: &output) + mangle(type: i.type, to: &output) + } + + mangle(type: t.output, to: &output) + } + /// Writes the mangled representation of `symbol` to `output`. private mutating func write(subscriptType t: SubscriptType, to output: inout Output) { write(operator: .subscriptType, to: &output) diff --git a/Sources/IR/Mangling/ManglingOperator.swift b/Sources/IR/Mangling/ManglingOperator.swift index cd6bb5206..835805489 100644 --- a/Sources/IR/Mangling/ManglingOperator.swift +++ b/Sources/IR/Mangling/ManglingOperator.swift @@ -22,6 +22,10 @@ public enum ManglingOperator: String { case existentializedFunctionDecl = "eF" + case methodDecl = "hF" + + case methodImpl = "iF" + case monomorphizedFunctionDecl = "mF" case staticFunctionDecl = "sF" @@ -76,6 +80,8 @@ public enum ManglingOperator: String { case genericTypeParameterType = "gT" + case methodType = "hT" + case lambdaType = "lT" case metatypeType = "mT" From 5d55f92e68069832e6a071873e4d4463af3bc040 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 16:37:05 +0200 Subject: [PATCH 31/85] Stand up the call bundle reification pass --- .../Module+CallBundleReification.swift | 32 +++++++++++++++++++ Sources/IR/Module.swift | 1 + 2 files changed, 33 insertions(+) create mode 100644 Sources/IR/Analysis/Module+CallBundleReification.swift diff --git a/Sources/IR/Analysis/Module+CallBundleReification.swift b/Sources/IR/Analysis/Module+CallBundleReification.swift new file mode 100644 index 000000000..3dc82a4d1 --- /dev/null +++ b/Sources/IR/Analysis/Module+CallBundleReification.swift @@ -0,0 +1,32 @@ +import Core + +extension Module { + + /// Replace occurrences of `call_bundle` by `call` depending on the uses of their first operands, + /// reporting errors and warnings to `diagnostics`. + /// + /// - Requires: `f` is in `self`. + public mutating func reifyCallsToBundles(in f: Function.ID, diagnostics: inout DiagnosticSet) { + for i in blocks(in: f).map(instructions(in:)).joined() where self[i] is CallBundle { + reify(callBundle: i, for: .let) + } + } + + private mutating func reify(callBundle i: InstructionID, for k: AccessEffect) { + let s = self[i] as! CallBundle + assert(s.capabilities.contains(k)) + + var arguments = Array(s.arguments) + let r = makeAccess(k, from: arguments[0], at: s.site) + arguments[0] = .register(insert(r, before: i)) + + let b = Block.ID(containing: i) + let f = FunctionReference( + to: s.variants[k]!, in: self, specializedBy: s.bundle.arguments, in: self[b].scope) + + let reified = makeCall( + applying: .constant(f), to: arguments, writingResultTo: s.output, at: s.site) + replace(i, with: reified) + } + +} diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 2865a9953..162c0a621 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -202,6 +202,7 @@ public struct Module { } try run({ removeDeadCode(in: $0, diagnostics: &log) }) + try run({ reifyCallsToBundles(in: $0, diagnostics: &log) }) try run({ reifyAccesses(in: $0, diagnostics: &log) }) try run({ closeBorrows(in: $0, diagnostics: &log) }) try run({ normalizeObjectStates(in: $0, diagnostics: &log) }) From 3fc7e329a96f855f1da8056b1a76e36cc7be34bf Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 16:48:30 +0200 Subject: [PATCH 32/85] Generate IR lowering method declarations --- Sources/IR/Emitter.swift | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index f4f4e6fcc..48d220adf 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -317,6 +317,46 @@ struct Emitter { insert(module.makeReturn(at: returnSite)) } + /// Inserts the IR for `d`. + private mutating func lower(method d: MethodDecl.ID) { + for i in ast[d].impls { + lower(methodImpl: i) + } + } + + /// Inserts the IR for `d`. + private mutating func lower(methodImpl d: MethodImpl.ID) { + let f = module.demandDeclaration(lowering: d) + guard let b = ast[d].body else { return } + + // Create the function entry. + let entry = module.appendEntry(in: program.scopeContainingBody(of: d)!, to: f) + + // Configure the locals. + var locals = DeclProperty() + locals[ast[d].receiver] = .parameter(entry, 0) + + let bundle = MethodDecl.ID(program[d].scope)! + for (i, p) in ast[bundle].parameters.enumerated() { + locals[p] = .parameter(entry, i + 1) + } + + let bodyFrame = Frame(locals: locals) + + // Emit the body. + self.insertionPoint = .end(of: entry) + switch b { + case .block(let s): + let returnType = LambdaType(program[d].type)!.output + let returnSite = pushing(bodyFrame, { $0.lowerStatements(s, expecting: returnType) }) + insert(module.makeReturn(at: returnSite)) + + case .expr(let e): + pushing(bodyFrame, { $0.emitStore(value: e, to: $0.returnValue!) }) + insert(module.makeReturn(at: ast[e].site)) + } + } + /// Inserts the IR for `d`. private mutating func lower(subscript d: SubscriptDecl.ID) { for i in ast[d].impls { @@ -416,6 +456,8 @@ struct Emitter { lower(function: .init(m)!) case InitializerDecl.self: lower(initializer: .init(m)!) + case MethodDecl.self: + lower(method: .init(m)!) case SubscriptDecl.self: lower(subscript: .init(m)!) default: From 5d5008455ff55cd9c904a9bcae8fb37191cff366 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 16:50:41 +0200 Subject: [PATCH 33/85] Test call to method bundle --- .../EndToEndTests/TestCases/MethodBundle.hylo | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Tests/EndToEndTests/TestCases/MethodBundle.hylo diff --git a/Tests/EndToEndTests/TestCases/MethodBundle.hylo b/Tests/EndToEndTests/TestCases/MethodBundle.hylo new file mode 100644 index 000000000..96959d646 --- /dev/null +++ b/Tests/EndToEndTests/TestCases/MethodBundle.hylo @@ -0,0 +1,20 @@ +//- compileAndRun expecting: success + +type A: Deinitializable { + + public var x: Int + + public memberwise init + + public fun foo() -> Int { + let { x.copy() } + sink { x } + } + +} + +public fun main() { + var a = A(x: 42) + precondition(a.foo() == 42) + precondition(a.foo() == 42) +} From d6dbc43957beeca238d264c2ef966f9ba0db0a2c Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 15 Sep 2023 16:56:28 +0200 Subject: [PATCH 34/85] Apply swift-format --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 2 +- Sources/IR/Emitter.swift | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index d11758bf7..a3e0e43d9 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -152,7 +152,7 @@ struct TypeChecker { return [ program.ast.movableTrait, program.ast.deinitializableTrait, - program.ast.foreignConvertibleTrait + program.ast.foreignConvertibleTrait, ] } diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 48d220adf..9f4fdc056 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1466,8 +1466,9 @@ struct Emitter { writingResultTo storage: Operand, at site: SourceRange ) { let o = insert(module.makeAccess(.set, from: storage, at: site))! - insert(module.makeCallBundle( - applying: .init(callee, in: insertionScope!), to: arguments, writingResultTo: o, at: site)) + insert( + module.makeCallBundle( + applying: .init(callee, in: insertionScope!), to: arguments, writingResultTo: o, at: site)) insert(module.makeEndAccess(o, at: site)) } From 38e8db8e6cecb98b55caa3f27a5da3c00329e199 Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Fri, 15 Sep 2023 13:26:10 +0200 Subject: [PATCH 35/85] Add hylo library product --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index a998a1be7..0f9c1966a 100644 --- a/Package.swift +++ b/Package.swift @@ -16,6 +16,7 @@ let package = Package( products: [ .executable(name: "hc", targets: ["hc"]), .executable(name: "hylo-demangle", targets: ["hylo-demangle"]), + .library(name: "hc-lib", targets: ["Driver"]) ], dependencies: [ From 62e9d666a7e1eab5c765521ac9cdf6c74e5b3c96 Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Fri, 15 Sep 2023 13:26:16 +0200 Subject: [PATCH 36/85] Add public init to SourcePosition --- Sources/Core/SourcePosition.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Core/SourcePosition.swift b/Sources/Core/SourcePosition.swift index a82fbcbf2..fae17641c 100644 --- a/Sources/Core/SourcePosition.swift +++ b/Sources/Core/SourcePosition.swift @@ -8,7 +8,7 @@ public struct SourcePosition: Hashable { public let index: String.Index /// Creates an instance with the given properties. - init(_ index: String.Index, in file: SourceFile) { + public init(_ index: String.Index, in file: SourceFile) { self.file = file self.index = index } @@ -16,7 +16,7 @@ public struct SourcePosition: Hashable { /// Creates an instance referring to the given 1-based line and column numbers in `source`. /// /// - Precondition: `line` and `column` denote a valid position in `source`. - init(line: Int, column: Int, in file: SourceFile) { + public init(line: Int, column: Int, in file: SourceFile) { self.file = file self.index = file.index(line: line, column: column) } From 2fee5ceb9036295d6f2a59e69978718460f4db5a Mon Sep 17 00:00:00 2001 From: Nils Hjelte Date: Fri, 15 Sep 2023 16:06:20 +0200 Subject: [PATCH 37/85] Refactor Hylo library target name --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 0f9c1966a..730df497f 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( products: [ .executable(name: "hc", targets: ["hc"]), .executable(name: "hylo-demangle", targets: ["hylo-demangle"]), - .library(name: "hc-lib", targets: ["Driver"]) + .library(name: "Hylo", targets: ["Driver"]) ], dependencies: [ From f68d3e2fa537567c71704c3dbef44823a75513cc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 11:46:27 +0200 Subject: [PATCH 38/85] Add a helper to get the lowered form of a conformance implementation --- Sources/IR/Module.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 162c0a621..4bbf49677 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -237,6 +237,16 @@ public struct Module { functions[identity] = function } + /// Returns the identity of the IR function corresponding to `i`. + mutating func demandDeclaration(lowering i: Core.Conformance.Implementation) -> Function.ID { + switch i { + case .concrete(let d): + return demandDeclaration(lowering: d)! + case .synthetic(let d): + return demandDeclaration(lowering: d) + } + } + /// Returns the identity of the IR function corresponding to `d`, or `nil` if `d` can't be /// lowered to an IR function. mutating func demandDeclaration(lowering d: AnyDeclID) -> Function.ID? { From 202506ce9810601507f9e623d64b08301b3e47c6 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 11:46:57 +0200 Subject: [PATCH 39/85] Add missing case to handle method implementations --- Sources/IR/Module.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 4bbf49677..c9ef9794e 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -253,6 +253,8 @@ public struct Module { switch d.kind { case FunctionDecl.self: return demandDeclaration(lowering: FunctionDecl.ID(d)!) + case MethodImpl.self: + return demandDeclaration(lowering: MethodImpl.ID(d)!) case InitializerDecl.self: return demandDeclaration(lowering: InitializerDecl.ID(d)!) case SubscriptImpl.self: From 71036cd483336ecd74aa157f1c0d15e2b5197a14 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 11:47:50 +0200 Subject: [PATCH 40/85] Implement IR support for custom conformances to 'Movable' --- Sources/IR/Module.swift | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index c9ef9794e..879af2aa4 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -415,21 +415,15 @@ public struct Module { } } - /// Returns the identity of the IR function implementing the `k` variant move-operator defined in - /// conformance `c`. + /// Returns the identity of the IR function implementing the `k` variant move-operation defined + /// in conformance `c`. /// /// - Requires: `k` is either `.set` or `.inout` mutating func demandMoveOperatorDeclaration( _ k: AccessEffect, from c: Core.Conformance ) -> Function.ID { let d = program.ast.moveRequirement(k) - switch c.implementations[d]! { - case .concrete: - UNIMPLEMENTED() - - case .synthetic(let s): - return demandDeclaration(lowering: s) - } + return demandDeclaration(lowering: c.implementations[d]!) } /// Returns the lowered declarations of `d`'s parameters. From 41dc0653989d1ff457881fbfba1689dabfdaebbe Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 11:48:11 +0200 Subject: [PATCH 41/85] Refactor 'Module.demandDeinitDeclaration' --- Sources/IR/Module.swift | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index 879af2aa4..5d7515dd3 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -405,14 +405,11 @@ public struct Module { /// Returns the identity of the IR function implementing the deinitializer defined in /// conformance `c`. - mutating func demandDeinitDeclaration(from c: Core.Conformance) -> Function.ID { + mutating func demandDeinitDeclaration( + from c: Core.Conformance + ) -> Function.ID { let d = program.ast.deinitRequirement() - switch c.implementations[d]! { - case .concrete(let s): - return demandDeclaration(lowering: FunctionDecl.ID(s)!) - case .synthetic(let s): - return demandDeclaration(lowering: s) - } + return demandDeclaration(lowering: c.implementations[d]!) } /// Returns the identity of the IR function implementing the `k` variant move-operation defined From b7937c2b7da4b8a82f5c296a03b3abd92fa080dc Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 11:53:18 +0200 Subject: [PATCH 42/85] Test custom conformance to 'Movable' --- Tests/EndToEndTests/TestCases/CustomMove.hylo | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Tests/EndToEndTests/TestCases/CustomMove.hylo diff --git a/Tests/EndToEndTests/TestCases/CustomMove.hylo b/Tests/EndToEndTests/TestCases/CustomMove.hylo new file mode 100644 index 000000000..0ce246712 --- /dev/null +++ b/Tests/EndToEndTests/TestCases/CustomMove.hylo @@ -0,0 +1,32 @@ +//- compileAndRun expecting: success + +type A: Deinitializable { + var witness: Int + public var x: Int + public init(x: sink Int) { + &self.x = x + &self.witness = 0 + } +} + +conformance A: Movable { + public fun take_value(from source: sink Self) -> {self: Self, Void} { + set { + &self.x = source.x + &self.witness = 0 + } + inout { + &self.x = source.x + &self.witness += 1 + } + } +} + +public fun main() { + var s = A(x: 1) + &s = A(x: 2) + &s = A(x: 2) + + precondition(s.x == 2) + precondition(s.witness == 2) +} From 9fbb80a50211d8fe1c21615c72ebe46390499e1f Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Thu, 14 Sep 2023 13:47:15 +0200 Subject: [PATCH 43/85] Add test showing empty init behaviour --- Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo diff --git a/Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo b/Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo new file mode 100644 index 000000000..d89be6830 --- /dev/null +++ b/Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo @@ -0,0 +1,5 @@ +//- lowerToFinishedIR expecting: failure + +public type Foo { + public init() { } //! diagnostic set parameter not initialized before function returns +} From 95df4f7a0df720a982731dee22e8e38cc08a4fb3 Mon Sep 17 00:00:00 2001 From: Walter Smuts Date: Sun, 17 Sep 2023 15:28:53 +0200 Subject: [PATCH 44/85] Insert init directive for empty product type Fixes: https://github.com/hylo-lang/hylo/issues/997 --- Sources/IR/Emitter.swift | 8 ++++++++ Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 9f4fdc056..7fdbc45ab 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -314,6 +314,14 @@ struct Emitter { insertionPoint = .end(of: entry) let bodyFrame = Frame(locals: locals) let returnSite = pushing(bodyFrame, { $0.lowerStatements($0.ast[d].body!, expecting: .void) }) + + let receiverLayout = AbstractTypeLayout(of: program[d].receiver.type, definedIn: program) + + // If the object is empty, simply mark it initialized. + if receiverLayout.properties.isEmpty { + let receiver = entry.parameter(0) + insert(module.makeMarkState(receiver, initialized: true, at: ast[d].site)) + } insert(module.makeReturn(at: returnSite)) } diff --git a/Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo b/Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo index d89be6830..73d82fb9c 100644 --- a/Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo +++ b/Tests/HyloTests/TestCases/Lowering/EmptyStruct.hylo @@ -1,5 +1,5 @@ -//- lowerToFinishedIR expecting: failure +//- lowerToFinishedIR expecting: success public type Foo { - public init() { } //! diagnostic set parameter not initialized before function returns + public init() { } } From a0a61456b7c997971289492ae0f89ede5e767541 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 09:47:20 +0200 Subject: [PATCH 45/85] Refactor the gathering of traits bounds declared in generic environments --- .../FrontEnd/TypeChecking/TypeChecker.swift | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index a3e0e43d9..0e34806a2 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -165,13 +165,7 @@ struct TypeChecker { return conformedTraits(of: TraitType(d, ast: program.ast), in: scopeOfUse) } - // Conformances of other generic parameters are stored in generic environments. - var result = Set() - for s in program.scopes(from: scopeOfUse) where s.kind.value is GenericScope.Type { - let e = environment(of: s)! - result.formUnion(e.conformedTraits(of: ^t)) - } - + var result = conformedTraits(declaredInEnvironmentIntroducing: ^t, exposedTo: scopeOfUse) result.formUnion(conformedTraits(declaredInExtensionsOf: ^t, exposedTo: scopeOfUse)) return result } @@ -240,6 +234,22 @@ struct TypeChecker { return result } + /// Returns the traits to which `t` is declared conforming in its generic environment. + /// + /// `t` is a generic type parameter or an associated type introduced by a generic environment + /// logically containing `scopeOfUse`. The return value is the set of traits used as bounds of + /// `t` in that environment. + mutating func conformedTraits( + declaredInEnvironmentIntroducing t: AnyType, exposedTo scopeOfUse: AnyScopeID + ) -> Set { + var result = Set() + for s in program.scopes(from: scopeOfUse) where s.kind.value is GenericScope.Type { + let e = environment(of: s)! + result.formUnion(e.conformedTraits(of: ^t)) + } + return result + } + // MARK: Type transformations /// Returns `generic` with occurrences of parameters keying `specialization` replaced by their From 266602aedb69f3525c12ed622121b2ac1568e8a6 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 09:48:42 +0200 Subject: [PATCH 46/85] Rename 'TypedProgram.declaredConformances' to 'explicitConformance' --- Sources/FrontEnd/TypedProgram.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index 3a10e8d46..0323eab35 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -329,7 +329,7 @@ public struct TypedProgram { of model: AnyType, to concept: TraitType, exposedTo scopeOfUse: AnyScopeID ) -> Conformance? { let m = canonical(model, in: scopeOfUse) - if let c = declaredConformance(of: m, to: concept, exposedTo: scopeOfUse) { + if let c = explicitConformance(of: m, to: concept, exposedTo: scopeOfUse) { return c } else { return structuralConformance(of: m, to: concept, exposedTo: scopeOfUse) @@ -340,8 +340,9 @@ public struct TypedProgram { /// `scopeOfUse`, or `nil` if such a conformance doesn't exist. /// /// This method returns `nil` if the conformance of `model` to `concept` is structural (e.g., a - /// tuple's synthesized conformance to `Movable`). - private func declaredConformance( + /// tuple's synthesized conformance to `Movable`) or if the conformance is implied by a trait + /// bound (e.g., `T: P` in `fun f() {}`). + private func explicitConformance( of model: AnyType, to concept: TraitType, exposedTo scopeOfUse: AnyScopeID ) -> Conformance? { assert(model[.isCanonical]) From 3fc6a0c254bab667969bd234c0aba6f3eec67bf1 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Thu, 14 Sep 2023 09:49:17 +0200 Subject: [PATCH 47/85] Compute conformances implied by constraints in generic environments --- Sources/FrontEnd/TypedProgram.swift | 32 +++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index 0323eab35..fabafcc90 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -329,11 +329,17 @@ public struct TypedProgram { of model: AnyType, to concept: TraitType, exposedTo scopeOfUse: AnyScopeID ) -> Conformance? { let m = canonical(model, in: scopeOfUse) + if let c = explicitConformance(of: m, to: concept, exposedTo: scopeOfUse) { return c - } else { - return structuralConformance(of: m, to: concept, exposedTo: scopeOfUse) } + + if let c = impliedConformance(of: m, to: concept, exposedTo: scopeOfUse) { + return c + } + + // Last resort; maybe the conformance is structural. + return structuralConformance(of: m, to: concept, exposedTo: scopeOfUse) } /// Returns the explicitly declared conformance of `model` to `concept` that is exposed to @@ -377,6 +383,28 @@ public struct TypedProgram { } } + /// Returns the conformance of `model` to `concept` that is implied by the generic environment + /// introducing `model` in `scopeOfUse`, or `nil` if such a conformance doesn't exist. + private func impliedConformance( + of model: AnyType, to concept: TraitType, exposedTo scopeOfUse: AnyScopeID + ) -> Conformance? { + var checker = TypeChecker(asContextFor: self) + let bounds = checker.conformedTraits( + declaredInEnvironmentIntroducing: model, + exposedTo: scopeOfUse) + guard bounds.contains(concept) else { return nil } + + var implementations = Conformance.ImplementationMap() + for requirement in ast.requirements(of: concept.decl) { + implementations[requirement] = .concrete(requirement) + } + + return .init( + model: model, concept: concept, arguments: [:], conditions: [], scope: scopeOfUse, + implementations: implementations, isStructural: true, + site: .empty(at: ast[scopeOfUse].site.first())) + } + /// Returns the implicit structural conformance of `model` to `concept` that is exposed to /// `scopeOfUse`, or `nil` if such a conformance doesn't exist. private func structuralConformance( From 945ba3c93c605d3b46cbedfe09cd5a27a77e7618 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 16:50:38 +0200 Subject: [PATCH 48/85] Refactor 'Emitter.emitMove(_:_:to:at:)' --- Sources/IR/Emitter.swift | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 7fdbc45ab..9314e16a5 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2258,31 +2258,25 @@ struct Emitter { } } - /// Appends the IR for a call to move-initialize/assign `storage` with `value`, anchoring new - /// instructions at `site`. + /// Inserts the IR to move-initialize/assign `storage` with `value`, anchoring new instructions + /// at `site`. /// /// The type of `value` must a built-in or conform to `Movable` in `insertionScope`. /// /// The value of `semantics` defines the type of move to emit: /// - `[.set]` unconditionally emits move-initialization. /// - `[.inout]` unconditionally emits move-assignment. - /// - `[.inout, .set]` (default) emits a `move` instruction that will is later replaced during - /// definite initialization analysis by move-assignment if `storage` is found initialized or - /// move-initialization otherwise. after + /// - `[.inout, .set]` emits a `move` instruction that will is later replaced during definite + /// initialization analysis by either move-assignment if `storage` is found initialized or + /// by move-initialization otherwise. private mutating func emitMove( _ semantics: AccessEffectSet, _ value: Operand, to storage: Operand, at site: SourceRange ) { precondition(!semantics.isEmpty && semantics.isSubset(of: [.set, .inout])) - // Built-in are always stored. let t = module.type(of: storage).ast if t.isBuiltin { - let x0 = insert(module.makeAccess(.set, from: storage, at: site))! - let x1 = insert(module.makeAccess(.sink, from: value, at: site))! - let x2 = insert(module.makeLoad(x1, at: site))! - insert(module.makeStore(x2, at: x0, at: site)) - insert(module.makeEndAccess(x1, at: site)) - insert(module.makeEndAccess(x0, at: site)) + emitMoveBuiltIn(value, to: storage, at: site) return } @@ -2313,6 +2307,19 @@ struct Emitter { insert(module.makeMove(value, to: storage, usingConformance: movable, at: site)) } + /// Implements `emitMove` for built-in types. + private mutating func emitMoveBuiltIn( + _ value: Operand, to storage: Operand, at site: SourceRange + ) { + // Built-in are always stored. + let x0 = insert(module.makeAccess(.set, from: storage, at: site))! + let x1 = insert(module.makeAccess(.sink, from: value, at: site))! + let x2 = insert(module.makeLoad(x1, at: site))! + insert(module.makeStore(x2, at: x0, at: site)) + insert(module.makeEndAccess(x1, at: site)) + insert(module.makeEndAccess(x0, at: site)) + } + // MARK: Deinitialization /// If `storage` is deinitializable in `self.insertionScope`, inserts the IR for deinitializing From e79b94dde66f9470910c0779f20e6ad5125314c3 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 22:18:35 +0200 Subject: [PATCH 49/85] Refactor 'Emitter.emitMove' to keep track of specialization arguments --- Sources/IR/Emitter.swift | 59 +++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 9314e16a5..aa7b96e5d 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2247,7 +2247,7 @@ struct Emitter { let predecessor = module.instruction(before: i) insertionPoint = .before(i) - emitMove([semantics], s.object, to: s.target, at: s.site) + emitMove(semantics, s.object, to: s.target, usingConformance: s.movable, at: s.site) module.removeInstruction(i) if let p = predecessor { @@ -2264,8 +2264,8 @@ struct Emitter { /// The type of `value` must a built-in or conform to `Movable` in `insertionScope`. /// /// The value of `semantics` defines the type of move to emit: - /// - `[.set]` unconditionally emits move-initialization. - /// - `[.inout]` unconditionally emits move-assignment. + /// - `[.set]` emits move-initialization. + /// - `[.inout]` emits move-assignment. /// - `[.inout, .set]` emits a `move` instruction that will is later replaced during definite /// initialization analysis by either move-assignment if `storage` is found initialized or /// by move-initialization otherwise. @@ -2275,6 +2275,8 @@ struct Emitter { precondition(!semantics.isEmpty && semantics.isSubset(of: [.set, .inout])) let t = module.type(of: storage).ast + + // Built-in types are handled as a special case. if t.isBuiltin { emitMoveBuiltIn(value, to: storage, at: site) return @@ -2284,27 +2286,13 @@ struct Emitter { let movable = program.conformance( of: t, to: program.ast.movableTrait, exposedTo: insertionScope!)! - // If the semantics of the move is known, emit a call to the corresponding move method. + // Insert a call to the approriate move implementation if its semantics is unambiguous. + // Otherwise, insert a call to the method bundle. if let k = semantics.uniqueElement { - let d = module.demandMoveOperatorDeclaration(k, from: movable) - let r = FunctionReference( - to: d, in: module, specializedBy: movable.arguments, in: insertionScope!) - let f = Operand.constant(r) - - let x0 = insert(module.makeAllocStack(.void, at: site))! - let x1 = insert(module.makeAccess(.set, from: x0, at: site))! - let x2 = insert(module.makeAccess(k, from: storage, at: site))! - let x3 = insert(module.makeAccess(.sink, from: value, at: site))! - insert(module.makeCall(applying: f, to: [x2, x3], writingResultTo: x1, at: site)) - insert(module.makeEndAccess(x3, at: site)) - insert(module.makeEndAccess(x2, at: site)) - insert(module.makeEndAccess(x1, at: site)) - insert(module.makeDeallocStack(for: x0, at: site)) - return + emitMove(k, value, to: storage, usingConformance: movable, at: site) + } else { + insert(module.makeMove(value, to: storage, usingConformance: movable, at: site)) } - - // Otherwise, emit a move. - insert(module.makeMove(value, to: storage, usingConformance: movable, at: site)) } /// Implements `emitMove` for built-in types. @@ -2320,6 +2308,33 @@ struct Emitter { insert(module.makeEndAccess(x0, at: site)) } + /// Inserts IR for move-initializing/assigning `storage` with `value` using `movable` to locate + /// the implementations of these operations. + /// + /// The value of `semantics` defines the type of move to emit: + /// - `.set` emits move-initialization. + /// - `.inout` emits move-assignment. + /// + /// - Requires: `storage` does not have a built-in type. + private mutating func emitMove( + _ semantics: AccessEffect, _ value: Operand, to storage: Operand, + usingConformance movable: Core.Conformance, at site: SourceRange + ) { + let d = module.demandMoveOperatorDeclaration(semantics, from: movable) + let a = module.specialization(in: insertionFunction!).merging(movable.arguments) + let f = FunctionReference(to: d, in: module, specializedBy: a, in: insertionScope!) + + let x0 = insert(module.makeAllocStack(.void, at: site))! + let x1 = insert(module.makeAccess(.set, from: x0, at: site))! + let x2 = insert(module.makeAccess(semantics, from: storage, at: site))! + let x3 = insert(module.makeAccess(.sink, from: value, at: site))! + insert(module.makeCall(applying: .constant(f), to: [x2, x3], writingResultTo: x1, at: site)) + insert(module.makeEndAccess(x3, at: site)) + insert(module.makeEndAccess(x2, at: site)) + insert(module.makeEndAccess(x1, at: site)) + insert(module.makeDeallocStack(for: x0, at: site)) + } + // MARK: Deinitialization /// If `storage` is deinitializable in `self.insertionScope`, inserts the IR for deinitializing From 89f0867989ddbdff8efdfe72798e3e4165ed4ed2 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 22:21:44 +0200 Subject: [PATCH 50/85] Simplify the representation of function identifiers --- Sources/IR/Analysis/Module+Depolymorphize.swift | 4 ---- Sources/IR/FunctionID.swift | 11 ++--------- Sources/IR/Mangling/Mangler.swift | 2 -- Sources/IR/Module+Description.swift | 2 -- Sources/IR/Program.swift | 2 -- 5 files changed, 2 insertions(+), 19 deletions(-) diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index 296593ec1..33d0b7e4a 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -565,10 +565,6 @@ extension TypedProgram { guard let t = trait(defining: d) else { return nil } return (declaration: d, trait: TraitType(t, ast: ast)) - case .loweredSubscript(let d): - guard let t = trait(defining: d) else { return nil } - return (declaration: AnyDeclID(d), trait: TraitType(t, ast: ast)) - default: return nil } diff --git a/Sources/IR/FunctionID.swift b/Sources/IR/FunctionID.swift index 22977970c..e2f5676cb 100644 --- a/Sources/IR/FunctionID.swift +++ b/Sources/IR/FunctionID.swift @@ -9,12 +9,9 @@ extension Function { /// The value of a function IR identity. public enum Value: Hashable { - /// The identity of a lowered Hylo function, initializer, or method variant. + /// The identity of a lowered function, initializer, method variant, or subscript variant. case lowered(AnyDeclID) - /// The identity of a lowered subscript variant. - case loweredSubscript(SubscriptImpl.ID) - /// The identity of a synthesized declaration. case synthesized(SynthesizedFunctionDecl) @@ -33,10 +30,8 @@ extension Function { /// initializer, method implementation, or subscript implementation.s init(_ d: T) { switch d.kind { - case FunctionDecl.self, InitializerDecl.self, MethodImpl.self: + case FunctionDecl.self, InitializerDecl.self, MethodImpl.self, SubscriptImpl.self: self.value = .lowered(AnyDeclID(d)) - case SubscriptImpl.self: - self.value = .loweredSubscript(SubscriptImpl.ID(d)!) default: unreachable() } @@ -80,8 +75,6 @@ extension Function.ID: CustomStringConvertible { switch value { case .lowered(let d): return d.description - case .loweredSubscript(let d): - return d.description case .synthesized(let d): return d.description case .existentialized(let b): diff --git a/Sources/IR/Mangling/Mangler.swift b/Sources/IR/Mangling/Mangler.swift index 43d7917c8..11fccc73b 100644 --- a/Sources/IR/Mangling/Mangler.swift +++ b/Sources/IR/Mangling/Mangler.swift @@ -310,8 +310,6 @@ struct Mangler { switch symbol.value { case .lowered(let d): mangle(decl: d, to: &output) - case .loweredSubscript(let d): - mangle(decl: d, to: &output) case .monomorphized(let f, let a): write(monomorphized: f, for: a, to: &output) case .synthesized(let d): diff --git a/Sources/IR/Module+Description.swift b/Sources/IR/Module+Description.swift index 078ad3b77..bfa9e9fda 100644 --- a/Sources/IR/Module+Description.swift +++ b/Sources/IR/Module+Description.swift @@ -82,8 +82,6 @@ extension Module: TextOutputStreamable { return "Existentialized form of '\(debugDescription(base))'" case .lowered(let d): return program.debugDescription(d) - case .loweredSubscript(let d): - return program.debugDescription(d) case .monomorphized(let base, let arguments): return "Monomorphized form of '\(debugDescription(base))' for <\(list: arguments.values)>" case .synthesized(let d): diff --git a/Sources/IR/Program.swift b/Sources/IR/Program.swift index d85bdb2d0..86a920424 100644 --- a/Sources/IR/Program.swift +++ b/Sources/IR/Program.swift @@ -42,8 +42,6 @@ public struct Program: Core.Program { switch f.value { case .lowered(let d): return base.module(containing: base[d].scope) - case .loweredSubscript(let d): - return base.module(containing: base[d].scope) case .monomorphized: UNIMPLEMENTED() case .existentialized(let i): From c2beb52168864a9af09d2ac162e1f0eb9fc49a98 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 22:50:38 +0200 Subject: [PATCH 51/85] Rename 'TypedProgram.requirement(referredBy:)' to 'traitMember(referredBy:)' --- .../IR/Analysis/Module+Depolymorphize.swift | 23 ++----------------- Sources/IR/TypedProgram+Extensions.swift | 21 +++++++++++++++++ 2 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 Sources/IR/TypedProgram+Extensions.swift diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index 33d0b7e4a..6ebfa5b79 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -481,8 +481,8 @@ extension Module { let p = program.specialize(c.specialization, for: specialization, in: scopeOfUse) let f: Function.ID - if let r = program.requirement(referredBy: c) { - f = monomorphize(requirement: r.declaration, of: r.trait, in: ir, for: p, in: scopeOfUse) + if let m = program.traitMember(referredBy: c.function) { + f = monomorphize(requirement: m.declaration, of: m.trait, in: ir, for: p, in: scopeOfUse) } else { f = monomorphize(c.function, in: ir, for: p, in: scopeOfUse) } @@ -552,22 +552,3 @@ extension Module { } } - -extension TypedProgram { - - /// If `r` refers to a requirement, returns the declaration of that requirement along with the - /// trait that defines it. Otherwise, returns `nil`. - fileprivate func requirement( - referredBy r: FunctionReference - ) -> (declaration: AnyDeclID, trait: TraitType)? { - switch r.function.value { - case .lowered(let d): - guard let t = trait(defining: d) else { return nil } - return (declaration: d, trait: TraitType(t, ast: ast)) - - default: - return nil - } - } - -} diff --git a/Sources/IR/TypedProgram+Extensions.swift b/Sources/IR/TypedProgram+Extensions.swift new file mode 100644 index 000000000..6afcb5d18 --- /dev/null +++ b/Sources/IR/TypedProgram+Extensions.swift @@ -0,0 +1,21 @@ +import Core +import FrontEnd + +extension TypedProgram { + + /// If `f` refers to a trait member, returns the declaration of that member along with the trait + /// in which it is defined. Otherwise, returns `nil`. + func traitMember( + referredBy f: Function.ID + ) -> (declaration: AnyDeclID, trait: TraitType)? { + switch f.value { + case .lowered(let d): + guard let t = trait(defining: d) else { return nil } + return (declaration: d, trait: TraitType(t, ast: ast)) + + default: + return nil + } + } + +} From a2236dc234a70d3bf2060f4ec7a8de9d01879196 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 22:51:20 +0200 Subject: [PATCH 52/85] Refine comments --- Sources/Core/Program.swift | 2 +- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Core/Program.swift b/Sources/Core/Program.swift index 46feb23c0..a4d2e169c 100644 --- a/Sources/Core/Program.swift +++ b/Sources/Core/Program.swift @@ -311,7 +311,7 @@ extension Program { scopes(from: scope).first(TranslationUnit.self)! } - /// Returns the trait defining `d` iff `d` is a requirement. Otherwise, returns nil. + /// Returns the trait of which `d` is a member, or `nil` if `d` isn't member of a trait. public func trait(defining d: T) -> TraitDecl.ID? { switch d.kind { case AssociatedTypeDecl.self, AssociatedValueDecl.self: diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 0e34806a2..cf3f5f472 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -2949,7 +2949,7 @@ struct TypeChecker { // The specialization of the match includes that of context in which it was looked up. var specialization = context?.arguments ?? [:] - // If the match is a trait requirement, specialize its receiver as necessary. + // If the match is a trait member, specialize its receiver as necessary. if let t = program.trait(defining: m) { assert(specialization[program[t].receiver] == nil) specialization[program[t].receiver] = context?.type From 3c2cfbeeb96357b24d4474030ac33010b2537d74 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Sun, 17 Sep 2023 22:52:40 +0200 Subject: [PATCH 53/85] Keep track of specialization arguments when generating moves --- Sources/IR/Emitter.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index aa7b96e5d..c566c060d 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2321,7 +2321,13 @@ struct Emitter { usingConformance movable: Core.Conformance, at site: SourceRange ) { let d = module.demandMoveOperatorDeclaration(semantics, from: movable) - let a = module.specialization(in: insertionFunction!).merging(movable.arguments) + + var a = module.specialization(in: insertionFunction!).merging(movable.arguments) + if let m = program.traitMember(referredBy: d) { + let t = module.type(of: storage).ast + a = a.merging([program[m.trait.decl].receiver: t]) + } + let f = FunctionReference(to: d, in: module, specializedBy: a, in: insertionScope!) let x0 = insert(module.makeAllocStack(.void, at: site))! From 7d513172501d188ba4e4718689579ff6cf6276ae Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 18 Sep 2023 09:12:50 +0200 Subject: [PATCH 54/85] Test use of conformance to 'Movable' in generic contexts --- Tests/HyloTests/TestCases/Lowering/Identity.hylo | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Tests/HyloTests/TestCases/Lowering/Identity.hylo diff --git a/Tests/HyloTests/TestCases/Lowering/Identity.hylo b/Tests/HyloTests/TestCases/Lowering/Identity.hylo new file mode 100644 index 000000000..54c0871bf --- /dev/null +++ b/Tests/HyloTests/TestCases/Lowering/Identity.hylo @@ -0,0 +1,9 @@ +//- lowerToFinishedIR expecting: success + +fun identity(_ x: sink T) -> T { + return x +} + +public fun main() { + _ = identity(42) +} From 4a9101fe8367d9475b61b0bf0b2a802263585938 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 18 Sep 2023 09:46:25 +0200 Subject: [PATCH 55/85] Keep track of specialization arguments when generating deinit calls --- Sources/IR/Emitter.swift | 41 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index c566c060d..4508220fc 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2247,7 +2247,7 @@ struct Emitter { let predecessor = module.instruction(before: i) insertionPoint = .before(i) - emitMove(semantics, s.object, to: s.target, usingConformance: s.movable, at: s.site) + emitMove(semantics, s.object, to: s.target, withMovableConformance: s.movable, at: s.site) module.removeInstruction(i) if let p = predecessor { @@ -2289,7 +2289,7 @@ struct Emitter { // Insert a call to the approriate move implementation if its semantics is unambiguous. // Otherwise, insert a call to the method bundle. if let k = semantics.uniqueElement { - emitMove(k, value, to: storage, usingConformance: movable, at: site) + emitMove(k, value, to: storage, withMovableConformance: movable, at: site) } else { insert(module.makeMove(value, to: storage, usingConformance: movable, at: site)) } @@ -2318,17 +2318,10 @@ struct Emitter { /// - Requires: `storage` does not have a built-in type. private mutating func emitMove( _ semantics: AccessEffect, _ value: Operand, to storage: Operand, - usingConformance movable: Core.Conformance, at site: SourceRange + withMovableConformance movable: Core.Conformance, at site: SourceRange ) { let d = module.demandMoveOperatorDeclaration(semantics, from: movable) - - var a = module.specialization(in: insertionFunction!).merging(movable.arguments) - if let m = program.traitMember(referredBy: d) { - let t = module.type(of: storage).ast - a = a.merging([program[m.trait.decl].receiver: t]) - } - - let f = FunctionReference(to: d, in: module, specializedBy: a, in: insertionScope!) + let f = reference(to: d, implementedFor: movable) let x0 = insert(module.makeAllocStack(.void, at: site))! let x1 = insert(module.makeAccess(.set, from: x0, at: site))! @@ -2360,7 +2353,7 @@ struct Emitter { // Use custom conformance to `Deinitializable` if possible. let concept = program.ast.deinitializableTrait if let c = module.program.conformance(of: model, to: concept, exposedTo: insertionScope!) { - emitDeinit(storage, withConformanceToDeinitializable: c, at: site) + emitDeinit(storage, withDeinitializableConformance: c, at: site) return } @@ -2368,20 +2361,19 @@ struct Emitter { report(.error(module.type(of: storage).ast, doesNotConformTo: concept, at: site)) } - /// Inserts the IR for deinitializing `storage`, using `c` to identify the deinitializer to apply - /// and anchoring new instructions at `site`. + /// Inserts the IR for deinitializing `storage`, using `deinitializable` to identify the locate + /// the deinitializer to apply. private mutating func emitDeinit( - _ storage: Operand, withConformanceToDeinitializable c: Core.Conformance, + _ storage: Operand, withDeinitializableConformance deinitializable: Core.Conformance, at site: SourceRange ) { - let d = module.demandDeinitDeclaration(from: c) - let f = Operand.constant( - FunctionReference(to: d, in: module, specializedBy: c.arguments, in: insertionScope!)) + let d = module.demandDeinitDeclaration(from: deinitializable) + let f = reference(to: d, implementedFor: deinitializable) let x0 = insert(module.makeAllocStack(.void, at: site))! let x1 = insert(module.makeAccess(.set, from: x0, at: site))! let x2 = insert(module.makeAccess(.sink, from: storage, at: site))! - insert(module.makeCall(applying: f, to: [x2], writingResultTo: x1, at: site)) + insert(module.makeCall(applying: .constant(f), to: [x2], writingResultTo: x1, at: site)) insert(module.makeEndAccess(x2, at: site)) insert(module.makeEndAccess(x1, at: site)) insert(module.makeMarkState(x0, initialized: false, at: site)) @@ -2480,6 +2472,17 @@ struct Emitter { // MARK: Helpers + /// Returns a function reference to `d`, which is an implementation that's part of `c`. + private func reference( + to d: Function.ID, implementedFor c: Core.Conformance + ) -> FunctionReference { + var a = module.specialization(in: insertionFunction!).merging(c.arguments) + if let m = program.traitMember(referredBy: d) { + a = a.merging([program[m.trait.decl].receiver: c.model]) + } + return FunctionReference(to: d, in: module, specializedBy: a, in: insertionScope!) + } + /// Returns the canonical form of `t` in the current insertion scope. private func canonical(_ t: AnyType) -> AnyType { program.canonical(t, in: insertionScope!) From d7b856ea0d08588e08eb507d0ecf819c0a3a41ea Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 18 Sep 2023 09:46:59 +0200 Subject: [PATCH 56/85] Test use of conformance to 'Deinitializable' in generic contexts --- Tests/HyloTests/TestCases/Lowering/Sink.hylo | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Tests/HyloTests/TestCases/Lowering/Sink.hylo diff --git a/Tests/HyloTests/TestCases/Lowering/Sink.hylo b/Tests/HyloTests/TestCases/Lowering/Sink.hylo new file mode 100644 index 000000000..3cb497a11 --- /dev/null +++ b/Tests/HyloTests/TestCases/Lowering/Sink.hylo @@ -0,0 +1,9 @@ +//- lowerToFinishedIR expecting: success + +fun eat(_ x: sink T) { + _ = x +} + +public fun main() { + eat(42) +} From cd141eac04ef4436b342989369e28ae2356a9d98 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 18 Sep 2023 09:47:56 +0200 Subject: [PATCH 57/85] Apply swift-format --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 730df497f..d02d2d675 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( products: [ .executable(name: "hc", targets: ["hc"]), .executable(name: "hylo-demangle", targets: ["hylo-demangle"]), - .library(name: "Hylo", targets: ["Driver"]) + .library(name: "Hylo", targets: ["Driver"]), ], dependencies: [ From ac652e6fc3489a473e93274fce5bdce6a3a37637 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Mon, 18 Sep 2023 11:07:08 +0200 Subject: [PATCH 58/85] Write more concise docs --- Sources/IR/Emitter.swift | 56 +++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 4508220fc..11f4552fd 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -20,7 +20,9 @@ import Utils /// /// Other entry points may be used during IR passes (e.g., `emitDeinit`). /// -/// - Note: Unless documented otherwise, methods insert IR in `self.module` at `insertionPoint`. +/// - Note: Unless documented otherwise, the methods of `Emitter` type insert IR in `self.module` +/// at `self.insertionPoint`, anchoring new instructions at the given source range, named `site` +/// in their parameter lists. struct Emitter { /// The diagnostics of lowering errors. @@ -834,7 +836,7 @@ struct Emitter { } - /// Inserts IR for returning from current function, anchoring instructions to `s`. + /// Inserts IR for returning from current function, anchoring instructions at `s`. private mutating func emitControlFlow(return s: ReturnStmt.ID) { for f in frames.elements.reversed() { emitDeallocs(for: f, at: ast[s].site) @@ -1410,8 +1412,7 @@ struct Emitter { insert(module.makeStore(x2, at: x1, at: syntax.site)) } - /// Writes an instance of `Hylo.Int` with value `v` to `storage`, anchoring new instruction at - /// `site`. + /// Writes an instance of `Hylo.Int` with value `v` to `storage`. /// /// - Requires: `storage` is the address of uninitialized memory of type `Hylo.Int`. private mutating func emitStore(int v: Int, to storage: Operand, at site: SourceRange) { @@ -1420,8 +1421,7 @@ struct Emitter { insert(module.makeStore(.word(v), at: x1, at: site)) } - /// Writes an instance of `Hylo.String` with value `v` to `storage`, anchoring new instruction at - /// `site`. + /// Writes an instance of `Hylo.String` with value `v` to `storage`. /// /// - Requires: `storage` is the address of uninitialized memory of type `Hylo.String`. private mutating func emitStore(string v: String, to storage: Operand, at site: SourceRange) { @@ -2174,8 +2174,8 @@ struct Emitter { UNIMPLEMENTED() } - /// Returns the address of the member declared by `d`, specialized with `specialization` and - /// bound to `receiver`, inserting IR anchored at `site`. + /// Inserts IR to return the address of the member declared by `d`, bound to `receiver`, and + /// specialized by `specialization`. private mutating func emitProperty( boundTo receiver: Operand, declaredBy d: AnyDeclID, specializedBy specialization: GenericArguments, @@ -2196,8 +2196,8 @@ struct Emitter { } } - /// Returns the projection the property declared by `d`, specialized with `specialization` and - /// bound to `receiver`, inserting IR anchored at `site`. + /// Returns the projection the property declared by `d`, bound to `receiver`, and specialized by + /// `specialization`. private mutating func emitComputedProperty( boundTo receiver: Operand, declaredByBundle d: SubscriptDecl.ID, specializedBy specialization: GenericArguments, @@ -2217,8 +2217,8 @@ struct Emitter { return insert(s)! } - /// Returns the projection of the property declared by `d`, specialized with `specialization` and - /// bound to `receiver`, inserting IR anchored at `site`. + /// Returns the projection of the property declared by `d`, bound to `receiver`, and specialized + /// by `specialization`. private mutating func emitComputedProperty( boundTo receiver: Operand, declaredBy d: SubscriptImpl.ID, specializedBy specialization: GenericArguments, @@ -2258,8 +2258,7 @@ struct Emitter { } } - /// Inserts the IR to move-initialize/assign `storage` with `value`, anchoring new instructions - /// at `site`. + /// Inserts IR for move-initializing/assigning `storage` with `value`. /// /// The type of `value` must a built-in or conform to `Movable` in `insertionScope`. /// @@ -2337,7 +2336,7 @@ struct Emitter { // MARK: Deinitialization /// If `storage` is deinitializable in `self.insertionScope`, inserts the IR for deinitializing - /// it, anchoring new instructions at `site`. Otherwise, reports a diagnostic. + /// it. Otherwise, reports a diagnostic. /// /// Let `T` be the type of `storage`, `storage` is deinitializable iff `T` has a deinitializer /// exposed to `self.insertionScope`. @@ -2381,8 +2380,7 @@ struct Emitter { } /// If `storage` is deinitializable in `self.insertionScope`, inserts the IR for deinitializing - /// it, anchoring new instructions at `site`. Otherwise, reports a diagnostic for each part that - /// isn't deinitializable. + /// it. Otherwise, reports a diagnostic for each part that isn't deinitializable. private mutating func emitDeinitParts(of storage: Operand, at site: SourceRange) { let t = module.type(of: storage).ast @@ -2396,8 +2394,8 @@ struct Emitter { } /// If `storage`, which stores a record, is deinitializable in `self.insertionScope`, inserts - /// the IR for deinitializing it, anchoring new instructions at `site`. Otherwise, reports a - /// diagnostic for each part that isn't deinitializable. + /// the IR for deinitializing it. Otherwise, reports a diagnostic for each part that isn't + /// deinitializable. /// /// - Requires: the type of `storage` has a record layout. private mutating func emitDeinitRecordParts(of storage: Operand, at site: SourceRange) { @@ -2420,8 +2418,8 @@ struct Emitter { } /// If `storage`, which stores a union. is deinitializable in `self.insertionScope`, inserts - /// the IR for deinitializing it, anchoring new instructions at `site`. Otherwise, reports a - /// diagnostic for each part that isn't deinitializable. + /// the IR for deinitializing it. Otherwise, reports a diagnostic for each part that isn't + /// deinitializable. /// /// - Requires: the type of `storage` is a union. private mutating func emitDeinitUnionPayload(of storage: Operand, at site: SourceRange) { @@ -2460,8 +2458,8 @@ struct Emitter { } /// If `storage`, which stores a union container holding a `payload`, is deinitializable in - /// `self.insertionScope`, inserts the IR for deinitializing it, anchoring new instructions at - /// `site`. Otherwise, reports a diagnostic for each part that isn't deinitializable. + /// `self.insertionScope`, inserts the IR for deinitializing it. Otherwise, reports a diagnostic + /// for each part that isn't deinitializable. private mutating func emitDeinitUnionPayload( of storage: Operand, containing payload: AnyType, at site: SourceRange ) { @@ -2518,14 +2516,13 @@ struct Emitter { return s } - /// Inserts the IR for deallocating each allocation in the top frame of `self.frames`, anchoring - /// new instructions at `site`. + /// Inserts the IR for deallocating each allocation in the top frame of `self.frames`. private mutating func emitDeallocTopFrame(at site: SourceRange) { emitDeallocs(for: frames.top, at: site) frames.top.allocs.removeAll() } - /// Inserts the IR for deallocating each allocation in `f`, anchoring new instructions at `site`. + /// Inserts the IR for deallocating each allocation in `f`. private mutating func emitDeallocs(for f: Frame, at site: SourceRange) { for a in f.allocs.reversed() { if a.mayHoldCaptures { @@ -2536,7 +2533,7 @@ struct Emitter { } /// Appends the IR for computing the address of the given `subfield` of the record at - /// `recordAddress` and returns the resulting address, anchoring new instructions at `site`. + /// `recordAddress` and returns the resulting address. mutating func emitSubfieldView( _ recordAddress: Operand, at subfield: RecordPath, at site: SourceRange ) -> Operand { @@ -2545,8 +2542,7 @@ struct Emitter { return insert(s)! } - /// Emits the IR trapping iff `predicate`, which is an object of type `i1`, anchoring new - /// instructions at `site`. + /// Emits the IR trapping iff `predicate`, which is an object of type `i1`. private mutating func emitGuard(_ predicate: Operand, at site: SourceRange) { let failure = appendBlock() let success = appendBlock() @@ -2558,7 +2554,7 @@ struct Emitter { } /// Emits the IR for copying the union discriminator of `container`, which is the address of - /// a union container, anchoring new instructions at `site`. + /// a union container. private mutating func emitUnionDiscriminator( _ container: Operand, at site: SourceRange ) -> Operand { From ed13d9a11256ba273e922ee3c17d3f75cba59e42 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 19 Sep 2023 11:01:53 +0200 Subject: [PATCH 59/85] Make 'AccessEffectSet.Elements' conform to 'Collection' A conformance to 'Collection' provides more algorithms than one to 'Sequence' without neagtively impacting performance. --- Sources/Core/AccessEffectSet.swift | 45 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Sources/Core/AccessEffectSet.swift b/Sources/Core/AccessEffectSet.swift index b5ac9883f..494e72494 100644 --- a/Sources/Core/AccessEffectSet.swift +++ b/Sources/Core/AccessEffectSet.swift @@ -30,8 +30,7 @@ public struct AccessEffectSet: OptionSet, Hashable { /// The weakest capability in `self`, or `nil` if `self` is empty. public var weakest: AccessEffect? { - var s = elements - return s.next() + elements.first } /// Returns the strongest capability in `self` including `k`. @@ -74,33 +73,39 @@ public struct AccessEffectSet: OptionSet, Hashable { extension AccessEffectSet { - /// A sequence with the elements of an access effect set. - public struct Elements: Sequence, IteratorProtocol { + /// A collection with the elements of an access effect set. + public struct Elements: Collection { + + public typealias Index = UInt8 + + public typealias Element = AccessEffect /// The contents of the set being iterated over. private let base: AccessEffectSet - /// The next effect returned by the iterator. - private var position: UInt8 - /// Creates an iterator over `s`. public init(_ s: AccessEffectSet) { self.base = s - self.position = s.rawValue & (~s.rawValue + 1) - if self.position == 0 { - self.position = 1 << 7 - } } - /// Returns the next element in the sequence. - public mutating func next() -> AccessEffect? { - if position == (1 << 7) { return nil } - defer { - repeat { - position = position << 1 - } while (position != (1 << 7)) && ((position & base.rawValue) != position) - } - return .init(rawValue: position) + public var startIndex: UInt8 { + base.rawValue & (~base.rawValue + 1) + } + + public var endIndex: UInt8 { + 1 << 7 + } + + public func index(after position: UInt8) -> UInt8 { + var next = position + repeat { + next = next << 1 + } while (next != endIndex) && ((next & base.rawValue) != next) + return next + } + + public subscript(position: UInt8) -> AccessEffect { + AccessEffect(rawValue: position)! } } From 44e5135c956e16fa8d40e94699f828e1e01cdf28 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Tue, 19 Sep 2023 11:04:24 +0200 Subject: [PATCH 60/85] Fix invalid application of parameter attributes --- Sources/CodeGen/LLVM/Transpilation.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 7b48ba67e..cd3633efa 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -406,7 +406,7 @@ extension LLVM.Module { let transpilation = declareFunction(ir.base.mangled(f), .init(from: parameters, in: &self)) configureAttributes(transpilation, transpiledFrom: f, of: m) - configureInputAttributes(transpilation.parameters.dropFirst(), transpiledFrom: f, in: m) + configureInputAttributes(transpilation.parameters.dropLast(), transpiledFrom: f, in: m) return transpilation } From a2c152467338bfa59c75c4da9e1e91bd18e2d579 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 00:34:28 +0200 Subject: [PATCH 61/85] Generate IR for pointer conversions to sink addresses --- Sources/IR/Emitter.swift | 36 ++++++++++++++----- .../Instruction/PointerToAddress.swift | 8 ++--- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 11f4552fd..045cf12e2 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -1123,7 +1123,7 @@ struct Emitter { case .down: emitStore(downcast: e, to: storage) case .pointerConversion: - unreachable("pointer to address conversion evaluates to a lvalue") + emitStore(pointerConversion: e, to: storage) } } @@ -1161,6 +1161,21 @@ struct Emitter { UNIMPLEMENTED() } + /// Inserts the IR for storing the value of `e` to `storage`. + private mutating func emitStore(pointerConversion e: CastExpr.ID, to storage: Operand) { + let x0 = emitLValue(pointerConversion: e) + + // Consuming a pointee requires a conformance to `Movable`. + let target = RemoteType(canonical(program[e].type))! + let movable = program.ast.movableTrait + if !program.conforms(target.bareType, to: movable, in: insertionScope!) { + report(.error(module.type(of: x0).ast, doesNotConformTo: movable, at: ast[e].site)) + return + } + + emitMove([.inout, .set], x0, to: storage, at: ast[e].site) + } + /// Inserts the IR for storing the value of `e` to `storage`. private mutating func emitStore(_ e: ConditionalExpr.ID, to storage: Operand) { let (success, failure) = emitTest(condition: ast[e].condition, in: AnyScopeID(e)) @@ -2072,18 +2087,23 @@ struct Emitter { private mutating func emitLValue(_ e: CastExpr.ID) -> Operand { switch ast[e].direction { case .pointerConversion: - let x0 = emitLValue(ast[e].left) - let x1 = insert(module.makeAccess(.sink, from: x0, at: ast[e].site))! - let x2 = insert(module.makeLoad(x1, at: ast[e].site))! - insert(module.makeEndAccess(x1, at: ast[e].site)) - let target = RemoteType(canonical(program[e].type))! - return insert(module.makePointerToAddress(x2, to: target, at: ast[e].site))! - + return emitLValue(pointerConversion: e) default: UNIMPLEMENTED() } } + /// Inserts the IR for lvalue `e`. + private mutating func emitLValue(pointerConversion e: CastExpr.ID) -> Operand { + let x0 = emitLValue(ast[e].left) + let x1 = insert(module.makeAccess(.sink, from: x0, at: ast[e].site))! + let x2 = insert(module.makeLoad(x1, at: ast[e].site))! + insert(module.makeEndAccess(x1, at: ast[e].site)) + + let target = RemoteType(canonical(program[e].type))! + return insert(module.makePointerToAddress(x2, to: target, at: ast[e].site))! + } + /// Inserts the IR for lvalue `e`. private mutating func emitLValue(_ e: InoutExpr.ID) -> Operand { emitLValue(ast[e].subject) diff --git a/Sources/IR/Operands/Instruction/PointerToAddress.swift b/Sources/IR/Operands/Instruction/PointerToAddress.swift index 34f0d0dbd..f7e1d684c 100644 --- a/Sources/IR/Operands/Instruction/PointerToAddress.swift +++ b/Sources/IR/Operands/Instruction/PointerToAddress.swift @@ -49,14 +49,10 @@ extension Module { /// Creates a `pointer_to_address` anchored at `site` that converts `source`, which is a /// built-in pointer value, to an address of type `target`. - /// - /// - Requires: `target.access` is `.let`, `.inout`, or `.set` func makePointerToAddress( - _ source: Operand, - to target: RemoteType, - at site: SourceRange + _ source: Operand, to target: RemoteType, at site: SourceRange ) -> PointerToAddress { - precondition(AccessEffectSet([.let, .inout, .set]).contains(target.access)) + precondition(target.access != .yielded) return .init(source: source, target: target, site: site) } From 588b60e177183f859d05441cb82e0c4620382b0a Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 00:34:51 +0200 Subject: [PATCH 62/85] Implement mangling for conformance constraints --- Sources/IR/Mangling/Mangler.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/IR/Mangling/Mangler.swift b/Sources/IR/Mangling/Mangler.swift index 11fccc73b..dfadc6881 100644 --- a/Sources/IR/Mangling/Mangler.swift +++ b/Sources/IR/Mangling/Mangler.swift @@ -295,9 +295,17 @@ struct Mangler { /// Writes the mangled representation of `c` to `output`. private mutating func write(constraint c: WhereClause.ConstraintExpr, to output: inout Output) { switch c { - case .conformance, .value: + case .value: UNIMPLEMENTED() + case .conformance(let lhs, let rhs): + write(operator: .conformanceConstraint, to: &output) + mangle(type: program[lhs].type, to: &output) + write(integer: rhs.count, to: &output) + for t in rhs { + mangle(type: program[t].type, to: &output) + } + case .equality(let lhs, let rhs): write(operator: .equalityConstraint, to: &output) mangle(type: program[lhs].type, to: &output) From 5b6eada86d8226294966520e55fbad90aab5fb47 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 00:35:23 +0200 Subject: [PATCH 63/85] Implement a method to consume the pointee of a 'PointerToMutable' --- Library/Hylo/Core/PointerToMutable.hylo | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index 32161b46c..107f6c44c 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -82,3 +82,13 @@ public conformance PointerToMutable: Equatable { } } + +public extension PointerToMutable where Pointee: Movable { + + /// Returns the value at the address represented by `self`, leaving the storage at this address + /// uninitialized. + public fun unsafe_pointee() -> Pointee { + return base as* (remote sink Pointee) + } + +} From 722c306049785802b52f7b10c9cb2178749c5242 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 00:40:04 +0200 Subject: [PATCH 64/85] Test 'PointerToMutable.unsafe_pointee()' --- Tests/LibraryTests/TestCases/PointerToMutableTests.hylo | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo b/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo index a3f7613a8..32198a365 100644 --- a/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo +++ b/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo @@ -24,7 +24,16 @@ fun test_advance() { precondition(!(f == e)) } +fun test_pointee() { + let p = PointerToMutable.allocate(count: 1) + p.unsafe_initialize_pointee(fun (_ i: set Int) -> Void { &i = 42 }) + let y = p.unsafe_pointee() + precondition(42 == y) + p.deallocate() +} + public fun main() { test_advance_by_bytes() test_advance() + test_pointee() } From 28553c0b2a4421c83ea72353fd717d95c1a22e2d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 10:38:39 +0200 Subject: [PATCH 65/85] Add a note for future deprecation --- Library/Hylo/Core/PointerToMutable.hylo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index 107f6c44c..d973d098a 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -87,6 +87,8 @@ public extension PointerToMutable where Pointee: Movable { /// Returns the value at the address represented by `self`, leaving the storage at this address /// uninitialized. + /// + /// Note: This method should be deprecated once nonmutating subscripts are implemented. public fun unsafe_pointee() -> Pointee { return base as* (remote sink Pointee) } From ab28738cf01ac41a12f2f28d8e5ac1da9b47e998 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 11:10:19 +0200 Subject: [PATCH 66/85] Implement a pointee initialization method that doesn't use lambdas --- Library/Hylo/Core/PointerToMutable.hylo | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index d973d098a..c901f0e6e 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -93,4 +93,19 @@ public extension PointerToMutable where Pointee: Movable { return base as* (remote sink Pointee) } + /// Initialize the value at the address represented by `self` to `value`. + /// + /// - Requires: the `MemoryLayout.size()` bytes starting at the address are uninitialized + /// and suitably aligned for `Pointee`. + public fun unsafe_initialize_pointee(_ value: sink Pointee) { + initialize(&(base as* (remote set Pointee)), to: value) + } + +} + +/// Initializes `x` to `y`. +/// +/// - Note: This function is a workaround for the lack of `set` bindings (see #925). +fun initialize(_ x: set T, to y: sink T) { + &x = y } From 01f292cafdb1eb6c08dbaf322d28a2dcfbdc219b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 11:19:09 +0200 Subject: [PATCH 67/85] Test pointee initialization without lambdas --- .../TestCases/PointerToMutableTests.hylo | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo b/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo index 32198a365..844afa613 100644 --- a/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo +++ b/Tests/LibraryTests/TestCases/PointerToMutableTests.hylo @@ -24,16 +24,25 @@ fun test_advance() { precondition(!(f == e)) } -fun test_pointee() { +fun test_initialize_pointee_lambda() { let p = PointerToMutable.allocate(count: 1) p.unsafe_initialize_pointee(fun (_ i: set Int) -> Void { &i = 42 }) let y = p.unsafe_pointee() - precondition(42 == y) + precondition(y == 42) + p.deallocate() +} + +fun test_initialize_pointee_direct() { + let p = PointerToMutable.allocate(count: 1) + p.unsafe_initialize_pointee(42) + let y = p.unsafe_pointee() + precondition(y == 42) p.deallocate() } public fun main() { test_advance_by_bytes() test_advance() - test_pointee() + test_initialize_pointee_lambda() + test_initialize_pointee_direct() } From 0732966659a910a39d29ffb97d281db143bb304d Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 11:53:51 +0200 Subject: [PATCH 68/85] Apply code formatting conventions --- Library/Hylo/Array.hylo | 3 ++- Library/Hylo/DynamicBuffer.hylo | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index d331a7f6f..3c571479b 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -1,5 +1,5 @@ /// An ordered, random-access collection. -public type Array : Deinitializable { +public type Array: Deinitializable { /// The out-of-line storage of the array. var _storage: DynamicBuffer @@ -61,6 +61,7 @@ public type Array : Deinitializable { // &x = s // }) } + } /* diff --git a/Library/Hylo/DynamicBuffer.hylo b/Library/Hylo/DynamicBuffer.hylo index faa1b4ddf..cd6f8fc2a 100644 --- a/Library/Hylo/DynamicBuffer.hylo +++ b/Library/Hylo/DynamicBuffer.hylo @@ -49,11 +49,12 @@ public type DynamicBuffer: Deinitializable, Mo } /// Returns the address of the initial element. - /// - Precondition: `capacity != 0` + /// + /// - Requires: `capacity > 0`. public fun first_element_address() -> PointerToMutable { // TODO: Uncomment the following line when #995 is fixed - // precondition(self.capacity() == 0) - return PointerToMutable(type_punning: storage.advance(by_bytes: payload_offset())) + // precondition(self.capacity() > 0) + PointerToMutable(type_punning: storage.advance(by_bytes: payload_offset())) } /// Deinitializes `self`. From fc925dfd1bda3eccdc34fa49852a5d6b56a2c7a6 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 11:54:50 +0200 Subject: [PATCH 69/85] Fix condition to exit 'Array.reserve_capacity' early --- Library/Hylo/Array.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 3c571479b..8d378b9d4 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -25,7 +25,7 @@ public type Array: Deinitializable { /// Reserves enough space to store `n` elements public fun reserve_capacity(_ n: Int) { - if n >= capacity() { return } + if n < capacity() { return } var new_capacity = max[1, capacity()].copy() while new_capacity < n { From 24e1750bd74b61cda3dba1dc83ac9bb86e05e845 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 11:56:31 +0200 Subject: [PATCH 70/85] Implement previously blocked methods of 'Array' --- Library/Hylo/Array.hylo | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 8d378b9d4..592f2ac65 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -40,11 +40,7 @@ public type Array: Deinitializable { var e = _storage.first_element_address() var f = new_storage.first_element_address() while i < count() { - // TODO: This is currently blocked by #1003 - // f.unsafe_initialize_pointee( fun(_ x:set Element) -> Void { - // &x = e.unsafe[] - // } ) - + f.unsafe_initialize_pointee(e.unsafe_pointee()) &e = e.advance(by: 1) &f = f.advance(by: 1) } @@ -56,10 +52,7 @@ public type Array: Deinitializable { public fun append(from source: sink Element) { reserve_capacity(count() + 1) var p = _storage.first_element_address().advance(by: count()) - // TODO: This is currently blocked by #1006 - // p.unsafe_initialize_pointee(fun[sink let s = source] (_ x: set Element) -> Void { - // &x = s - // }) + p.unsafe_initialize_pointee(source) } } From b545f6f10ccd1405dcda6c214785287e961c5c4e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 11:56:44 +0200 Subject: [PATCH 71/85] Remove needless label --- Library/Hylo/Array.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 592f2ac65..bb679eec2 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -49,7 +49,7 @@ public type Array: Deinitializable { } /// Adds a new element at the end of the array. - public fun append(from source: sink Element) { + public fun append(_ source: sink Element) { reserve_capacity(count() + 1) var p = _storage.first_element_address().advance(by: count()) p.unsafe_initialize_pointee(source) From 627a00af3c1023890d4493dd4b7c7e0ae7c3d42c Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 12:00:33 +0200 Subject: [PATCH 72/85] Remove needless leading '_' --- Library/Hylo/Array.hylo | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index bb679eec2..f100baa83 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -2,14 +2,14 @@ public type Array: Deinitializable { /// The out-of-line storage of the array. - var _storage: DynamicBuffer + var storage: DynamicBuffer /// The number of elements in the array. var _count: Int /// Creates a new, empty array. public init() { - &_storage = .new() + &storage = .new() &_count = 0 } @@ -20,7 +20,7 @@ public type Array: Deinitializable { /// The number of elements that can be stored in the array before new storage must be allocated. public fun capacity() -> Int { - return _storage.capacity() + return storage.capacity() } /// Reserves enough space to store `n` elements @@ -37,7 +37,7 @@ public type Array: Deinitializable { initializing_header_with: fun (_ h: set Void) -> Void { &h = () }) var i = 0 - var e = _storage.first_element_address() + var e = storage.first_element_address() var f = new_storage.first_element_address() while i < count() { f.unsafe_initialize_pointee(e.unsafe_pointee()) @@ -45,13 +45,13 @@ public type Array: Deinitializable { &f = f.advance(by: 1) } - &_storage = new_storage + &storage = new_storage } /// Adds a new element at the end of the array. public fun append(_ source: sink Element) { reserve_capacity(count() + 1) - var p = _storage.first_element_address().advance(by: count()) + var p = storage.first_element_address().advance(by: count()) p.unsafe_initialize_pointee(source) } From e7fdf1c854745c8de9894d50fe0240e8c0d2e4de Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 12:01:23 +0200 Subject: [PATCH 73/85] Add a comment about the correctness of 'reserve_capacity' --- Library/Hylo/Array.hylo | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index f100baa83..86c9a99a3 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -45,6 +45,8 @@ public type Array: Deinitializable { &f = f.advance(by: 1) } + // Deinitializing the `self.storage` is safe at this point because all its elements must have + // been moved to `new_storage`. &storage = new_storage } From 1da36890f9aae059cecfa340cc79bb7246137600 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 12:14:18 +0200 Subject: [PATCH 74/85] Implement a subscript to access the elements of 'Array' --- Library/Hylo/Array.hylo | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 86c9a99a3..de11f1c55 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -57,6 +57,20 @@ public type Array: Deinitializable { p.unsafe_initialize_pointee(source) } + /// Accesses the element at `position`. + /// + /// - Requires: `position` is in the range `0 ..< count()`. + public subscript(_ position: Int): Element { + let { + // precondition(position >= 0 && position < count()) + storage.first_element_address().advance(by: count()).unsafe[] + } + inout { + // precondition(position >= 0 && position < count()) + &storage.first_element_address().advance(by: count()).unsafe[] + } + } + } /* From 68d62ebdf69ab8a532d23f82089745576e07764c Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 14:07:24 +0200 Subject: [PATCH 75/85] Fix the payload offset computation in 'DynamicBuffer' --- Library/Hylo/DynamicBuffer.hylo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Hylo/DynamicBuffer.hylo b/Library/Hylo/DynamicBuffer.hylo index cd6f8fc2a..b620b16a9 100644 --- a/Library/Hylo/DynamicBuffer.hylo +++ b/Library/Hylo/DynamicBuffer.hylo @@ -16,7 +16,7 @@ public type DynamicBuffer: Deinitializable, Mo static fun payload_offset() -> Int { let a = MemoryLayout.alignment() let s = MemoryLayout.size() - return s + (a - s % a) + return s + (a - s) % a } /// A pointer to the base of the buffer's out-of-line storage. From 23195b4447cdd5cb4ac395490425bf486a2d8498 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 14:10:05 +0200 Subject: [PATCH 76/85] Mark mutating methods 'inout' --- Library/Hylo/Array.hylo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index de11f1c55..327d90d31 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -24,7 +24,7 @@ public type Array: Deinitializable { } /// Reserves enough space to store `n` elements - public fun reserve_capacity(_ n: Int) { + public fun reserve_capacity(_ n: Int) inout { if n < capacity() { return } var new_capacity = max[1, capacity()].copy() @@ -51,7 +51,7 @@ public type Array: Deinitializable { } /// Adds a new element at the end of the array. - public fun append(_ source: sink Element) { + public fun append(_ source: sink Element) inout { reserve_capacity(count() + 1) var p = storage.first_element_address().advance(by: count()) p.unsafe_initialize_pointee(source) From b49e5d7c3a75607d506a185d7b7cb029495b85ba Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 14:10:35 +0200 Subject: [PATCH 77/85] Fix 'Array.reserve_capacity(_:)' --- Library/Hylo/Array.hylo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index 327d90d31..e66c8e7e3 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -29,7 +29,7 @@ public type Array: Deinitializable { var new_capacity = max[1, capacity()].copy() while new_capacity < n { - &new_capacity = capacity() + capacity() + &new_capacity += new_capacity.copy() } var new_storage = DynamicBuffer( @@ -43,6 +43,7 @@ public type Array: Deinitializable { f.unsafe_initialize_pointee(e.unsafe_pointee()) &e = e.advance(by: 1) &f = f.advance(by: 1) + &i += 1 } // Deinitializing the `self.storage` is safe at this point because all its elements must have From 9500d32434668abea05c692a97be280ddd391c3b Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 14:11:08 +0200 Subject: [PATCH 78/85] Factorize Array element address computation --- Library/Hylo/Array.hylo | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index e66c8e7e3..e4c6bd55e 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -53,9 +53,9 @@ public type Array: Deinitializable { /// Adds a new element at the end of the array. public fun append(_ source: sink Element) inout { - reserve_capacity(count() + 1) - var p = storage.first_element_address().advance(by: count()) - p.unsafe_initialize_pointee(source) + &reserve_capacity(count() + 1) + pointer_to_element(at: count()).unsafe_initialize_pointee(source) + &_count += 1 } /// Accesses the element at `position`. @@ -64,14 +64,21 @@ public type Array: Deinitializable { public subscript(_ position: Int): Element { let { // precondition(position >= 0 && position < count()) - storage.first_element_address().advance(by: count()).unsafe[] + pointer_to_element(at: position).unsafe[] } inout { // precondition(position >= 0 && position < count()) - &storage.first_element_address().advance(by: count()).unsafe[] + pointer_to_element(at: position).unsafe[] } } + /// Returns the address of the element at `position`. + /// + /// - Requires: `position` is in the range `0 ..< capacity()`. + fun pointer_to_element(at position: Int) -> PointerToMutable { + storage.first_element_address().advance(by: position) + } + } /* From 79c1d5ad87eef4189e2d96510614cc0ddbc9a5b2 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 14:16:03 +0200 Subject: [PATCH 79/85] Implement 'Array: Deinitializable' --- Library/Hylo/Array.hylo | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index e4c6bd55e..a361fd07d 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -1,5 +1,5 @@ /// An ordered, random-access collection. -public type Array: Deinitializable { +public type Array: Deinitializable { /// The out-of-line storage of the array. var storage: DynamicBuffer @@ -13,6 +13,15 @@ public type Array: Deinitializable { &_count = 0 } + /// Deinitializes `self`. + public fun deinit() sink { + var i = 0 + while i < count() { + &pointer_to_element(at: i).unsafe_pointee().deinit() + &i += 1 + } + } + /// The number of elements in the array. public fun count() -> Int { return _count.copy() From cc35dbf904e06ce9488baa0a96526fc5e715b731 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 14:27:22 +0200 Subject: [PATCH 80/85] Test 'Array.append(_:)' --- Tests/LibraryTests/TestCases/ArrayTests.hylo | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/LibraryTests/TestCases/ArrayTests.hylo b/Tests/LibraryTests/TestCases/ArrayTests.hylo index 6d474ca23..a975a83b8 100644 --- a/Tests/LibraryTests/TestCases/ArrayTests.hylo +++ b/Tests/LibraryTests/TestCases/ArrayTests.hylo @@ -5,6 +5,18 @@ fun test_init_empty() { precondition(d.count() == 0) } +fun test_append() { + var d = Array() + &d.append(21) + &d.append(42) + &d.append(84) + + precondition(d[0] == 21) + precondition(d[1] == 42) + precondition(d[2] == 84) +} + public fun main() { test_init_empty() + test_append() } From 290ce6773ea72dab0e283e306cb7dc9a11b08926 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 16:35:14 +0200 Subject: [PATCH 81/85] Fix improperly named binding --- Sources/Core/NativeInstruction.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Core/NativeInstruction.swift b/Sources/Core/NativeInstruction.swift index cf348bf1f..33d5d10c4 100644 --- a/Sources/Core/NativeInstruction.swift +++ b/Sources/Core/NativeInstruction.swift @@ -311,8 +311,8 @@ extension NativeInstruction: CustomStringConvertible { return "fptosi_\(l)_\(r)" case .zeroinitializer(let t): return "zeroinitializer_\(t)" - case .advancedByBytes(let byteOffset): - return "advanced_by_bytes_\(byteOffset)" + case .advancedByBytes(let t): + return "advanced_by_bytes_\(t)" } } From 1613b36759cd88b14dc098b7aafc5b1d6e332bb0 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 16:35:58 +0200 Subject: [PATCH 82/85] Fix the transpilation of 'Builtin.advanced_by_bytes' --- Sources/CodeGen/LLVM/Transpilation.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index cd3633efa..e9b43a468 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -903,10 +903,7 @@ extension LLVM.Module { let base = llvm(s.operands[0]) let byteOffset = llvm(s.operands[1]) register[.register(i)] = insertGetElementPointerInBounds( - of: base, - typed: ptr, - indices: [byteOffset], - at: insertionPoint) + of: base, typed: i8, indices: [byteOffset], at: insertionPoint) default: unreachable("unexpected LLVM instruction '\(s.instruction)'") From 8535a958ec6cd318a12207e1141ce399ad2d270e Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 16:36:39 +0200 Subject: [PATCH 83/85] Remove needless spaces --- Library/Hylo/Core/PointerToMutable.hylo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo index c901f0e6e..17a5bd82f 100644 --- a/Library/Hylo/Core/PointerToMutable.hylo +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -34,10 +34,10 @@ public type PointerToMutable: Regular { } /// Returns `self` offset forward by `n` array elements of `Pointee` type. - public fun advance( by n: Int ) -> Self { + public fun advance(by n: Int) -> Self { let offset_in_bytes = MemoryLayout.stride() * n return PointerToMutable.new( - base: Builtin.advanced_by_bytes_word( base, offset_in_bytes.value ) ) + base: Builtin.advanced_by_bytes_word(base, offset_in_bytes.value)) } /// Creates an instance that does not address any usable storage. From 5bfa32b536b610ab30e29f6eae8d9aab1e3df194 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 16:37:09 +0200 Subject: [PATCH 84/85] Implement a property to access the header of a 'DynamicBuffer' --- Library/Hylo/DynamicBuffer.hylo | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Library/Hylo/DynamicBuffer.hylo b/Library/Hylo/DynamicBuffer.hylo index b620b16a9..415cb0cf4 100644 --- a/Library/Hylo/DynamicBuffer.hylo +++ b/Library/Hylo/DynamicBuffer.hylo @@ -50,7 +50,7 @@ public type DynamicBuffer: Deinitializable, Mo /// Returns the address of the initial element. /// - /// - Requires: `capacity > 0`. + /// - Requires: `capacity() > 0`. public fun first_element_address() -> PointerToMutable { // TODO: Uncomment the following line when #995 is fixed // precondition(self.capacity() > 0) @@ -74,6 +74,19 @@ public type DynamicBuffer: Deinitializable, Mo return p.unsafe[].0.copy() } + /// Accesses the header of `self`. + /// + /// - Requires: `capacity() > 0`. + public property header: Header { + let { buffer_header().unsafe[].1 } + inout { &buffer_header().unsafe[].1 } + } + + /// Returns the address of the `self`'s header. + fun buffer_header() -> PointerToMutable { + PointerToMutable(type_punning: storage) + } + } // TODO: This trait was added as a workaround for #1002 since DynamicBuffer From 6a90e54a6fcc5cd768800ce78ea445ce656fe856 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Wed, 20 Sep 2023 16:37:32 +0200 Subject: [PATCH 85/85] Store the count of the array in the out-of-line storage --- Library/Hylo/Array.hylo | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Library/Hylo/Array.hylo b/Library/Hylo/Array.hylo index a361fd07d..577455ca1 100644 --- a/Library/Hylo/Array.hylo +++ b/Library/Hylo/Array.hylo @@ -2,15 +2,13 @@ public type Array: Deinitializable { /// The out-of-line storage of the array. - var storage: DynamicBuffer - - /// The number of elements in the array. - var _count: Int + /// + /// The header of the buffer indicates the number of elements contained in the array. + var storage: DynamicBuffer /// Creates a new, empty array. public init() { &storage = .new() - &_count = 0 } /// Deinitializes `self`. @@ -24,7 +22,7 @@ public type Array: Deinitializable { /// The number of elements in the array. public fun count() -> Int { - return _count.copy() + if storage.capacity() == 0 { 0 } else { storage.header.copy() } } /// The number of elements that can be stored in the array before new storage must be allocated. @@ -41,9 +39,11 @@ public type Array: Deinitializable { &new_capacity += new_capacity.copy() } - var new_storage = DynamicBuffer( + // TODO: Call `self.copy()` directly in the lambda. + let c = count() + var new_storage = DynamicBuffer( capacity: new_capacity, - initializing_header_with: fun (_ h: set Void) -> Void { &h = () }) + initializing_header_with: fun (_ h: set Int) -> Void { &h = c.copy() }) var i = 0 var e = storage.first_element_address() @@ -64,7 +64,7 @@ public type Array: Deinitializable { public fun append(_ source: sink Element) inout { &reserve_capacity(count() + 1) pointer_to_element(at: count()).unsafe_initialize_pointee(source) - &_count += 1 + &storage.header += 1 } /// Accesses the element at `position`.