Inconsistent class type behaviour compared to Swift

The behaviour of Silver (Water: .2777) differs from Swift 5.7 when it comes to handling class types:

Example code:

class BasicClass {
    required init() { }
    func someFunction() { }
}

class AdvancedClass: BasicClass {
    override func someFunction() {
        print("In advanced someFunction()")
    }
}

var classes = [ BasicClass.self, AdvancedClass.self ]

for (i, c) in classes.enumerated() {
    let t = c.init()
    print("\(i):", type(of: t))
    print(type(of: t) === BasicClass.self, type(of: t) is BasicClass.Type)
    t.someFunction()
}

Running this code under Water in Windows gives this output:

0: ConsoleApplication.BasicClass
False False
1: ConsoleApplication.BasicClass
False False

In XCode on Mac, it will give this output:

0: BasicClass
true true
1: AdvancedClass
false true
In advanced someFunction()

Are there plans to make this behaviour similar or do I need to approach this differently with Silver?

Kind regards,
Heiko

What platform?

Trying .NET, this seems to atch what you posted, but it also seems correct, logically:

0: ConsoleApplication25.BasicClass
False False

t - ConsoleApplication25.BasicClass
c - ConsoleApplication25.AdvancedClass+MetaClass
type(of: t) - ConsoleApplication25.BasicClass
BasicClass.self - ConsoleApplication25.BasicClass+MetaClass

BasicClass.self is the metaclass, but type(of: t) is the .NET System.Type for the instance. These are distinct things.

On Mac I’m using Swift 5.7, Xcode 13.4.1 running on macOS 12.3.1

Windows System is RemObjects Water 11.0.0.2777 (develop) running on Win 10, 21H2, 19044.2130

The Swift behaviour is

type(of: t) === BasicClass.self 
// evaluate to true, if the class is identical, evaluates to false if t is a subclass of BasicClass

type(of: t) is BasicClass.Type
// evalutes to true if t is the same class as BasicClass OR if it is a subclass of BasicClass

So coming from a pure Swift perspective, I would wish this to evaluate to the same result in Silver.
I have virtually no experience with .NET, so I can’t comment whether it is consistent there.

Kind regards,
Heiko

I hear you, but unfortunately, there’s gray areas between what is Swift, the language, (which Silver supports), and what is Swift, the platform/class model (which, when you’re running on .NET is not the same, because these are CLR classes, not SwiftABI classes). And .NET Classes and their relationship to their meta classes is driven by how the object model works on .NET, not by the language.

That said, I’ll bring this up with the team for discussion as there’s at least one other inconsistency i’d like to have looked at — it seems that calling init isn’t virtual and calling init on ConsoleApplication25.AdvancedClass+MetaClass creates a BasicClass. which does seem wrong.

Logged as bugs://E26212: Swift: inconsistent result for {metaclass}.init()

let a = AdvancedClass.self
let x = a.init()
writeLn("x \(x)") // AdvancedClass, correct

for (i, c) in classes.enumerated() {
    writeLn("i \(i)")
    let t = c.init()
    writeLn("c \(c)") // proper metaclass from array for both cases
    writeLn("t \(t)") // BasicClass, both times
}

//x ConsoleApplication25.AdvancedClass
//i 0
//c ConsoleApplication25.BasicClass+MetaClass
//t ConsoleApplication25.BasicClass
//i 1
//c ConsoleApplication25.AdvancedClass+MetaClass
//t ConsoleApplication25.BasicClass    //// <<<< THIS ONE IS WRONG

Logged as bugs://E26213: Swift: inconsistency with typeof() and .self on .NET vs. Apple Swift

(low priority, and probably will be won’t fix)

let a = AdvancedClass.self
let x = a.init()
writeLn("x \(x)") // AdvancedClass, correct

writeLn("typeOf(x) \(typeOf(x))")
writeLn("type(of: x) \(type(of: x))")
writeLn("AdvancedClass.self \(AdvancedClass.self)")

//typeOf(x) ConsoleApplication25.AdvancedClass
//type(of: x) ConsoleApplication25.AdvancedClass
//AdvancedClass.self ConsoleApplication25.AdvancedClass+MetaClass

it seems that calling init isn’t virtual and calling init on ConsoleApplication25.AdvancedClass+MetaClass creates a BasicClass . which does seem wrong.

Would this explain also the observation, that AdvancesClass.someFunction() doesn’t print() if called in Silver?

Kind regards,
Heiko

Yes, in both cases, a BasicClass is instantiated. if you’d implement BasicClass.somefunction to also print something, you’d see that confirmed.

Understood. Thank you for looking into this!

1 Like