struct Foo {
    let blah: String
}

class Bar {
    static let ala = "Me"
    let foo = Foo(blah: Self.ala)   // error: covariant 'Self' type cannot be referenced from a stored property initializer
    let foo2 = Foo(blah: Bar.ala)   // this is okay
}

What is "covariant 'Self' type"?

Why this error here? And change Self to Bar is fine? I always use Self instead of type name. But in a class type, this is an error, where as in a struct Self is fine.

1 Like

In a non-final class, Self is not the same as the type name because the type can be subclassed.

In Foo(blah: Self.ala), Self is covariant; in other words, if you have a subclass of Bar named Bar2, then Self here refers to Bar2. This use of covariant Self is not supported, for obvious reasons.

2 Likes

I've filed [SR-13892] Improve diagnostics involving "covariant 'Self'" · Issue #56289 · apple/swift · GitHub to improve the diagnostic.

1 Like

I was guessing it must be due to class can do inheritance but did not see why it should be a problem: even if Self now is Bar2, since Bar2 inherits from Bar, so now "Self.ala" is "Bar2.ala" and ala is in the base class, so it should be reachable from Bar2? It's just simple inheritance relationship, as I understand.

Edit: I tried

final class Bar { ... }

still cannot use Self, same error.

This is a bug/previously-proposed enhancement, SR-11176.

1 Like
class Bar {
    static let ala = "Me"
    let foo: Foo
    init() {
        foo = Foo(blah: Self.ala)
    }
}

you could init foo in this way

So Self works in init, I’m even more puzzle at the original compile error. Foo initialized in hand written init or just assign in property; it's the same Self: why the compiler not accept in one place and okay in another?

Why is Self is okay in #selector()?

class WhySelfIsOkaySometime {
    static let ala = "Me"
    let me = Self.ala   // error: covariant 'Self' type cannot be referenced from a stored property initializer
    var whatwhat = #selector(Self.someFunc) // Self is fine here!

    @objc
    func someFunc() { }
}
1 Like

That’s a great question! #selector is a compiler builtin for creating Objective-C selectors, which are ultimately just strings. Since the selector produced only depends on the method signature (and not on the actual type which ultimately provides the implementation for that signature), there’s no issue when generating a selector from a dynamic Self type.

I'm just wondering what the obvious reasons are?

Take the example in the original comment. If Bar was subclassed and an override of ala was provided in the subclass, what would be passed to Foo(blah: Self.ala)? How does that work with class initialization rules?

I skimmed through the page on initialization in the swift programming language, but could find no mention of any rules for static properties of classes. In fact, a search for "static" returned zero hits. Since the page also seems to implicitly only talk about instance properties in many places, I can only conclude that that's what it refers to everywhere. Did I miss it?

I seem to recall having tried to look up the same subject a few years ago without finding anything then either.

Anyway, let's say I put lazy in front of the static property in the example in the OP. Then the compiler will tell me 'lazy' cannot be used on an already-lazy global, so we can conclude that the property is an already lazy global. Then, intuitively, from the top of my head my answer to your question would be that a lazy global doesn't interact with the (to me unknown) class initialization rules for static properties, and that it will initialize and return the concrete type's ala property first time it's called. At this point I don't feel like the answer is becoming obvious.

If the issue is that Self implicitly references self by being equivalent to type(of: self) then I would have expected some drastically different error message, e.g. "implicit access of self through use of Self before self is available":

Writing Self.someStaticMember to access a member of the current type is the same as writing type(of: self).someStaticMember.

So is that it?

The foo and foo2 properties are instance properties.

Yes.

Well, I guess this is either obvious and you see it, or you don't.