Repository: hylo-lang/hylo (swift)

Issue:
Fatal error in `Emitter.emitMove` in example with conformances
Please see example:
```
public type LocalFuture<R> {

  let r: R

  public fun await() -> R {
    r
  }
}

```

Compiling this will yield an error:
```
IR/Emitter.swift:2661: Fatal error: Unexpectedly found nil while unwrapping an Optional value
```

This comes from `emitMove` function at the following portion of the code:
```
    // Other types must be movable.
    let movable = program.conformance(
      of: t, to: program.ast.core.movable.type, exposedTo: insertionScope!)!
```

NOTE: for my longer-term type, I don't want it to be movable.

(adding `.copy()` when returning `r` makes the crash go away)

Pull request:
Fixes #1236

Patch:
diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift
index 81897220e..047801950 100644
--- a/Sources/IR/Emitter.swift
+++ b/Sources/IR/Emitter.swift
@@ -2778,8 +2778,6 @@ struct Emitter {
 
   /// Inserts IR for move-initializing/assigning `storage` with `value` 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]` emits move-initialization.
   /// - `[.inout]` emits move-assignment.
@@ -2799,6 +2797,13 @@ struct Emitter {
       return
     }
 
+    // Other types must be movable.
+    let m = program.ast.core.movable.type
+    guard let movable = program.conformance(of: model, to: m, exposedTo: insertionScope!) else {
+      report(.error(model, doesNotConformTo: m, at: site))
+      return
+    }
+
     // Use memcpy of `source` is trivially movable.
     if program.isTriviallyMovable(model, in: insertionScope!) {
       let x0 = insert(module.makeAccess(.sink, from: value, at: site))!
@@ -2810,12 +2815,6 @@ struct Emitter {
       return
     }
 
-    // Other types must be movable.
-    guard
-      let movable = program.conformance(
-        of: model, to: program.ast.core.movable.type, exposedTo: insertionScope!)
-    else { preconditionFailure("expected '\(model)' to be 'Movable'") }
-
     // 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 {
diff --git a/StandardLibrary/Sources/Array.hylo b/StandardLibrary/Sources/Array.hylo
index b272bcd23..4ccfb7fa1 100644
--- a/StandardLibrary/Sources/Array.hylo
+++ b/StandardLibrary/Sources/Array.hylo
@@ -77,7 +77,7 @@ public type Array<Element: Movable & Deinitializable>: Deinitializable {
   ///
   /// The projected pointer is valid only for the duration of the projection and can be advanced up
   /// to `count()`. It may be null if `self` is empty.
-  public fun with_mutable_contiguous_storage<E, T>(
+  public fun with_mutable_contiguous_storage<E, T: Movable>(
     _ action: inout [E](PointerToMutable<Element>) inout -> T
   ) inout -> T {
     if capacity() == 0 { &action(.null()) } else { &action(pointer_to_element[at: 0]) }
diff --git a/StandardLibrary/Sources/Core/CollectionOfOne.hylo b/StandardLibrary/Sources/Core/CollectionOfOne.hylo
index a7b9eb84f..364d843a0 100644
--- a/StandardLibrary/Sources/Core/CollectionOfOne.hylo
+++ b/StandardLibrary/Sources/Core/CollectionOfOne.hylo
@@ -1,5 +1,5 @@
 /// A collection containing a single element.
-public type CollectionOfOne<Element: Movable & Deinitializable>: Deinitializable {
+public type CollectionOfOne<Element: Movable & Deinitializable>: Movable, Deinitializable {
 
   /// The element contained in `self`.
   var contents: Element


Test patch:
diff --git a/Tests/HyloTests/TestCases/Lowering/IllegalMove.hylo b/Tests/HyloTests/TestCases/Lowering/IllegalMove.hylo
new file mode 100644
index 000000000..fcb96f36b
--- /dev/null
+++ b/Tests/HyloTests/TestCases/Lowering/IllegalMove.hylo
@@ -0,0 +1,17 @@
+//- lowerToFinishedIR expecting: failure
+
+// Note that some missing conformances to `Movable` can't be caught before we've emitted refined IR
+// because must determine whether values can be constructed in place, which typically can't be done
+// on syntax alone.
+
+public type Container<R> {
+
+  let r: R
+
+  //! @+1 diagnostic type 'R' does not conform to trait 'Movable'
+  public fun f0() -> R { r }
+
+  //! @+1 diagnostic type 'R' does not conform to trait 'Movable'
+  public fun f1() -> R { return r }
+
+}