Really hard to do basic things with noncopyable types

sure, i’m probably holding them wrong, noncopyable types are just… not fun.

here’s a noncopyable struct:

@frozen public
struct DiagnosticContext<Symbolicator>:~Copyable
{
    @usableFromInline internal
    var diagnostics:[Diagnostic]
}

and then here’s an intermediate abstraction that contains a stored property of that noncopyable struct:

import Diagnostics 

extension StaticLinker
{
    struct Tables:~Copyable
    {
        var diagnostics:DiagnosticContext<StaticSymbolicator>
        ...
    }
}

and now here’s a toplevel type that contains the intermediate type

struct StaticLinker:~Copyable
{
    private
    var tables:Tables
    ....
}

now i’m trying to do

extension StaticLinker
{
    consuming
    func diagnostics() -> DiagnosticContext<StaticSymbolicator>
    {
        self.tables.diagnostics
    }
}

but that doesn’t work

error: cannot partially consume 'self'

nor does

        (consume self).tables.diagnostics
error: cannot partially consume 'unknown'

this doesn’t parse

        (consume (consume self).tables).diagnostics

this crashes the compiler

    var diagnostics:DiagnosticContext<StaticSymbolicator>
    {
        _read
        {
            yield self.tables.diagnostics
        }
    }

COW-proofing is a lot harder than i thought…

Partial consumption is coming but the read coroutine is probably closer to what you want. That ought to work, I'll look into a fix.

1 Like

oof, just ran into a compiler segfault when building in release mode only. details here:

argh! i’m running into this again, except this time i actually do need to partially consume the field.

i have a type Table and a second type TableWrapper:

struct Table:~Copyable {}

struct TableWrapper:~Copyable
{
    var table:Table
}

i can create the TableWrapper by transferring to it an instance of Table, but i am totally stumped as to how to get the Table back out of the TableWrapper when i am done with it. surely there is something obvious i am missing?

Can't you add a consuming get function to the wrapper?

no, the access in the body of the getter is still considered a partial consumption

struct Table: ~Copyable {}

struct TableWrapper: ~Copyable {
    var table: Table

    consuming func intoTable() -> Table {
        table
    }
}

this works fine using the latest nightly :+1:

Just to make sure I'm understanding properly, this does not compile?

struct TableWrapper: ~Copyable {
    var table: Table
    consuming func get() -> Table {
        return table
    }
}


var table = Table()
let wrapper = TableWrapper(table: table)
// Do stuff with wrapper
table = wrapper.get()

no it does not, which is why @Alejandro was careful to specify latest nightly where it does compile.

Swift version 5.10 (swift-5.10-RELEASE)
Target: x86_64-unknown-linux-gnu
<stdin>:5:16: error: cannot partially consume 'self'
        return table
               ^
1 Like

This should just work now in Swift 6.0 nightlies. In earlier versions, the best you could do is swap in a dummy value to replace the one you want to get out, like:

struct Table:~Copyable {
  init(dummy: ()) {}

  mutating func swapWithDummy() -> Table {
    let value = self
    self = .init(dummy: ())
    return value
  }
}

struct TableWrapper:~Copyable
{
    var table:Table

    mutating func takeTable() -> Table {
      return self.table.swapWithDummy()
    }
}
2 Likes

that works! i had forgotten that mutating also lets you temporarily consume noncopyable self