Default values for generics are accepted but don't actually work

I have this method on Color:

func blend(with other: Color, _ mode: some BlendMode = .binary) -> Color {
    mode.blend(self, with: other)
}

This syntax doesn't appear to cause any errors in itself, but Swift doesn't actually seem to be able to infer the generic parameter from a default value, causing a confusing compile error when trying to use this method:

public subscript(x: Int, y: Int) -> Color {
    background[x, y].blend(with: foreground[x, y]) // ERROR: Generic parameter 'some BlendMode' could not be inferred
}

Compiling the code crashes the compiler:

Assertion failed: ((HadError || !M.is<SourceFile*>() || M.get<SourceFile*>()->ASTStage < SourceFile::TypeChecked) && "UnresolvedDot" "in wrong phase"), function walkToExprPre, file ExprNodes.def, line 104.

Is this a bug with the feature or is the feature not supposed to exist in the first place?

Can you share a self-contained example? The following works for me:

protocol BlendMode {}

struct Color: BlendMode {}

extension BlendMode where Self == Color {
  static var binary: Color { fatalError() }
}

func blend(_ mode: some BlendMode = .binary) {}

blend()

Color is a struct of var r, g, b, a: UInt8, and the static var binary was on the protocol itself, just without the Self == Color.

Color isn't really a blend mode, rather the idea was to implement blend modes as structs instead of (Color, Color) -> Color or an enum so that Swift could infer the modes with . syntax while still leaving in the ability to add more modes from outside of the module.

binary would just be a static variable containing one of the implementations of BlendMode, in this case treating opacity as a boolean for testing purposes

so it's

public extension BlendMode {
    static var binary: BlendBinary { .init() }
}

and

public protocol BlendMode {
    func blend(_ color: Color, with other: Color) -> Color
}

This gives me:

q.swift:11:48: error: contextual member reference to static property 'binary' requires 'Self' constraint in the protocol extension
func blend(_: Color, _ mode: some BlendMode = .binary) {}
                                               ^
q.swift:7:1: note: missing same-type requirement on 'Self'
extension BlendMode {
^
1 Like

I see, that makes sense. I guess my compiler, instead of telling me the error, gets confused and crashes.

Well, that's not good—the compiler itself shouldn't crash even if code is ill-formed. Can you give a self-contained example that crashes the compiler?

5 Likes

I think that might be impossible, there was a lot of code (it takes 14 seconds to compile on an M2 max) and sometimes this error was actually caught. I was working really quickly and I can't figure out what exactly combined to cause this to happen now