Animatable protocol: no compiler checks for constraints on associatedtype

The following code builds and runs without errors, however I would expect the compiler to fail due to a protocol requirement not being met:

struct MyAnimatable: Animatable {
    var animatableData: String {
        get { "TEST" }
        set { }
    }
}

The Animatable protocol has a requirement for an associated type AnimatableData, which is constrained to conform to VectorArithmetic. String does not conform to VectorArithmetic.

Outside of SwiftUI, the compiler does fail with an error, as expected. The following code demonstrates this:

struct MyAnotherAnimatable: AnotherAnimatable {
    var animatableData: String {
        get { "TEST" }
        set { }
    }
}

/* Mimicking protocols from SwiftUI */
// NOTE: Simplified not to inherit from AdditiveArithmetic intentionally
protocol AnotherVectorArithmetic {
    mutating func scale(by rhs: Double)
    var magnitudeSquared: Double { get }
}

protocol AnotherAnimatable {
    associatedtype AnimatableData: AnotherVectorArithmetic
    var animatableData: AnimatableData { get set }
}

The error is:

Type 'MyAnotherAnimatable' does not conform to protocol 'AnotherAnimatable'

Unable to infer associated type 'AnimatableData' for protocol 'AnotherAnimatable'

Candidate would match and infer 'AnimatableData' = 'String' if 'String' conformed to 'AnotherVectorArithmetic'

Why does the build succeed for Animatable from SwiftUI? Is there something special about these types that I'm missing? As far as I understand, the default implementation returning an instance of EmptyAnimatableData is not used since my type provides its own implementation. Could this be a compiler issue?

For reference, I've copied the relevant interfaces below:

/// A type that describes how to animate a property of a view.
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol Animatable {

    /// The type defining the data to animate.
    associatedtype AnimatableData : VectorArithmetic

    /// The data to animate.
    var animatableData: Self.AnimatableData { get set }
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Animatable where Self : VectorArithmetic {

    /// The data to animate.
    public var animatableData: Self
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Animatable where Self.AnimatableData == EmptyAnimatableData {

    /// The data to animate.
    public var animatableData: EmptyAnimatableData
}

Replies

I found an answer on the Swift community forums, here: https://forums.swift.org/t/checking-constraints-on-associated-types-within-a-protocol/66263/7

In summary, the default implementation of animatableData still exists on the MyAnimatable type, even when that type implements its own animatableData property which does not meet the constraint (does not conform to VectorArithmetic). When trying to read/write to that property, the implementation you access is based on the context from where you perform the operation. I find this counterintuitive, but its good to know.