Metatype generic method overload in embedded Swift

I have this code:

let sheet = UnsafeTGAPointer(SHEET_TGA)
    .flatten(into: Image.self)
    .grid(itemWidth: 16, itemHeight: 16)

but flatten errors with "Cannot use metatype of type 'Image' in embedded Swift"

This is flatten:

public extension Drawable {
    /// Shorthand for flattening a nested structure of lazy drawables into a trivial image, for
    /// cases where using memory and losing information is preferable to repeatedly recomputing all layers.
    ///
    /// This method is a convenience and overloads the generic version of this function in cases where
    /// an explicit type is not provided, since an `Image` is the most primitive of drawables.
    /// It is the equivalent of calling `flatten(into: Image.self)`.
    @_disfavoredOverload func flatten() -> Image { .init(self) }
    /// Shorthand for flattening a nested structure of lazy drawables into a primitive drawable, for
    /// cases where using memory and losing information is preferable to repeatedly recomputing all layers.
    ///
    /// This overload allows specifying the type in the middle of a method chain.
    func flatten<T>(into type: T.Type) -> T where T: PrimitiveDrawable { .init(self) }
    /// Shorthand for flattening a nested structure of lazy drawables into a primitive drawable, for
    /// cases where using memory and losing information is preferable to repeatedly recomputing all layers.
    ///
    /// This overload is used when inferring the return type.
    func flatten<T>() -> T where T: PrimitiveDrawable { .init(self) }
}

There's the other two overloads, one that defaults to Image and one that infers it from the return type, but neither is possible in a longer chain of methods, or even the simple one shown in the beginning of my post.

In this case I wanted to flatten into an image anyway, which has the default disfavored overload, but if I needed something else there's nothing I can do because methods can't be specialized.

Can I work around this somehow or is my only option to wait for Embedded Swift to improve this?

Curious, have you tried adding the Image specialization to drawable so you don’t have to specify the meta type (Image.self)? It’s been a while so not sure if possible.

Do you mean specializing the flatten<T> method with <> syntax? That's not supported by Swift unfortunately which is why I need the unused argument.

In this case the limitation could be lifted, but it would require more subtle analysis to make sure the metatype value is ignored inside the method, and not used for its pointer identity.

However, you can also encode this with a phantom type (Phantom type - HaskellWiki):

struct G<T> {
  init() {}
}

Now a G<Image>() can serve the same purpose as Image.Type, allowing you to infer T := Image here:

func flatten<T>(into type: G<T>) -> T where T: PrimitiveDrawable { ... }

You don't need an instance of T to construct a G<T>, because G<T>'s storage does not actually depend on T.

Unlike a metatype, a G<T> value doesn't have any identity or runtime representation, so it is safely specialized away.

5 Likes