Assigning a subclass to a variable defined as the base class:

I have been experimenting a bit with C++ interoperability and have come across a snag that I thought was fixed. It is related to issue:
https://github.com/apple/swift/issues/66323 - ‘Finalize Swift 5.9 rules that determine when inherited members that shadow other members can be introduced to the Swift type that represents the C++ structure or class #66323.’

I downloaded the latest toolchain that has the fix and created the following C++ class ( as mentioned in the link ):

class Plant {
public:
  void water(float amount) { moisture += amount; }
private:
  float moisture = 0.0;
};

class Fern: public Plant {
public:
  void water(float amount);
};

What I would like to do is create a variable of type Plant but then later, use the same variable to hold a Fern. However, I can’t get the following to compile:

var plant: Plant = Plant()

…. later

plant = Fern() <— ‘Cannot assign value of type 'Fern' to type 'Plant’’

I also tried:

var plant: Plant = Plant()
var fern: Fern = Fern()
    
plant = fern as! Plant       <———  ‘Cast from 'Fern' to unrelated type 'Plant' always fails’

Could someone let me know what I might be doing wrong? Thanks!

Well, there’s two levels of problem here.

The first is that we don’t yet provide implicit conversions in Swift for what would be derived-to-base conversions in C++. I do think this is something we could generally support for types we import as reference types, although multiple inheritance does make it tricky.

The second is that, because these are not being imported as reference types, but instead are value types, you’re actually requesting something called an “object slice”. The Plant class, taken as a value type, is not polymorphic: variables of that type always just store the fields associated with Plant and not anything associated with its subclasses. If you did this assignment in C++, it would just copy the portion of the source object that belongs to Plant and ignore the rest of the object; this is generally considered to be bad. You need some kind of indirection / reference semantics (in C++ terms, a pointer or reference) to get polymorphism like this in C++.

Swift’s C++ interop is naturally going to duplicate that conception of the C++ types in Swift by default. If you want Plant to be a polymorphic type, you need to tell Swift to import it as a reference type. And then someday we’ll support derived-to-base conversions on it in Swift.

I don’t think we would ever want to do derived-to-base conversions on value types.

3 Likes

Ok, thanks for the explanation. Since I’m not that great with C++, it looks like I might have to stick with the working Objective C++ wrapper route I already use.

Just out of interest, what I am trying to do is basically like in the example I showed. The user has an option to select what exact type of C++ subclass they want to do some work. There is no multiple inheritance. Basically like the Swift equivalent.

I had a bit of a closer look at the docs regarding Mapping C++ Types to Swift Reference Types and it looks like it could be possible, but my C++ isn’t up to it. So until there is a lot more simple examples available I’ll have to stick with the Objective C++ wrapper.