|
| 1 | +Swift classes in detail |
| 2 | +======================= |
| 3 | + |
| 4 | +Syntax overview |
| 5 | +--------------- |
| 6 | + |
| 7 | +:: |
| 8 | + |
| 9 | + class MyClass { |
| 10 | + var MyVar : Int |
| 11 | + func f(x : Int) -> Int { |
| 12 | + return MyVar + x |
| 13 | + } |
| 14 | + static fund getAMyclass() -> MyClass { |
| 15 | + return new MyClass |
| 16 | + } |
| 17 | + constructor() { |
| 18 | + MyVar = 10 |
| 19 | + } |
| 20 | + destructor { |
| 21 | + // Misc finalization |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + extension MyClass { |
| 26 | + func g() -> Int { |
| 27 | + return 4 |
| 28 | + } |
| 29 | + } |
| 30 | + |
| 31 | + func f() { |
| 32 | + var x = new MyClass(10) |
| 33 | + } |
| 34 | + |
| 35 | + class MyDerived : MyClass { |
| 36 | + func h() {} |
| 37 | + } |
| 38 | + |
| 39 | + |
| 40 | +Constructing an instance of a class |
| 41 | +----------------------------------- |
| 42 | +:: |
| 43 | + |
| 44 | + var x = new MyClass(10) |
| 45 | + |
| 46 | +This syntax basically just calls the constructor found by overload resolution |
| 47 | +on the given class. |
| 48 | + |
| 49 | +FIXME: There's been some debate whether we should allow default construction |
| 50 | +of classes, or constructing a class with ``MyClass(10)`` on an opt-in basis. |
| 51 | +We might want to take another look here later. |
| 52 | + |
| 53 | +Inheritance |
| 54 | +----------- |
| 55 | + |
| 56 | +A class inherits from at most one other class. The base class is named in |
| 57 | +the same place protocols are named. All member functions/properties of the |
| 58 | +base class can be accessed through an instance of the derived class |
| 59 | +(ignoring access control and shadowing). |
| 60 | + |
| 61 | +Constructors |
| 62 | +------------ |
| 63 | +:: |
| 64 | + |
| 65 | + constructor() { |
| 66 | + MyVar = 10 |
| 67 | + } |
| 68 | + |
| 69 | +This syntax defines a constructor, which is called to build an instance of |
| 70 | +the object. |
| 71 | + |
| 72 | +If no constructors are defined, an implicit constructor is defined which |
| 73 | +takes no arguments and implicitly initializes all ivars and the base class. |
| 74 | + |
| 75 | +A constructor in the derived class must delegate to a constructor in the |
| 76 | +base class. |
| 77 | + |
| 78 | +The exact syntax for initialization of ivars/delegation to the base class |
| 79 | +is still sort of an open question. There are a few possible models here: |
| 80 | + |
| 81 | +1. All ivars are implicitly initialized before the body of the constructor. |
| 82 | + Initializers can be provided on the ivar directly if necessary. This is the |
| 83 | + closest to the current model. This was discarded sucks because it doesn't |
| 84 | + allow initialization based on |
| 85 | + arguments. |
| 86 | +2. Some sort of C++-style list of initializers; this was discarded quickly |
| 87 | + during discussion as being both ugly and not as flexible as we would like. |
| 88 | +3. Add some sort of init block to constructors, like:: |
| 89 | + |
| 90 | + constructor (x : Int) { |
| 91 | + init { |
| 92 | + myIvar = x |
| 93 | + } |
| 94 | + print(myIvar) |
| 95 | + } |
| 96 | + |
| 97 | + Capturing self in the init block would be banned. If an ivar is accessed |
| 98 | + before initialization, or not initialized in the init block, it would be |
| 99 | + automatically "default"-initialized (as-if with an empty argument list) if |
| 100 | + such construction is possible, or an error otherwise. The ivar rules would |
| 101 | + be enforced using CFG analysis. Delegation to the base class would be in |
| 102 | + the init block; the syntax here is also an open question. Proposals |
| 103 | + included ``super(1,2)``, ``constructor(1,2)``, ``This(1,2)``, |
| 104 | + ``MyBaseClass(1,2)``. In the delegation case, the init block would not |
| 105 | + access ``self`` at all, and would perform a delegation call at the end |
| 106 | + (syntax also undecided). |
| 107 | + |
| 108 | + This approach sucks simply because it's ugly. There are a couple of |
| 109 | + alternative ways to express this, including some sort of explicit |
| 110 | + ``constructed`` marker, but I'm not sure we can come up with a variant |
| 111 | + which isn't ugly. |
| 112 | + |
| 113 | +4. Implicitly deduce an init block using the CFG:: |
| 114 | + |
| 115 | + constructor (x : Int) { |
| 116 | + // init implicitly begins here |
| 117 | + myIvar = x |
| 118 | + // init implicitly ends here |
| 119 | + myMethod() |
| 120 | + } |
| 121 | + |
| 122 | + Essentially the same semantic rules as (2), but less ugly (and possibly |
| 123 | + slightly more flexible in the presence of control flow because there |
| 124 | + could be multiple end-points). The downside here is that it isn't obvious |
| 125 | + at a glance where exactly the split occurs, which could lead to unexpected |
| 126 | + default construction. |
| 127 | + |
| 128 | +Destructors |
| 129 | +----------- |
| 130 | + |
| 131 | +A destructor is defined using just the keyword destructor followed by a |
| 132 | +brace-stmt. Destructors can only be defined in classes, and only in the |
| 133 | +class declaration itself. It's a runtime error if the body resurrects the |
| 134 | +object (i.e. if there are live reference to the object after the body of |
| 135 | +the destructor runs). Member ivars are destroyed after the body of the |
| 136 | +destructor runs. FIXME: Where exactly do we run the base class destructor? |
| 137 | + |
| 138 | +Member functions and properties |
| 139 | +------------------------------- |
| 140 | + |
| 141 | +Like structs, classes have member functions, properties, and ivars. |
| 142 | +Unlike structs, member functions and properties are overridable (and use |
| 143 | +dynamic dispatch) by default. Overriding can be disabled with the "final" |
| 144 | +attribute. |
| 145 | + |
| 146 | +In a derived class, if you define a member with the same name as a member |
| 147 | +of its base class, the base class member is overridden by default. If the |
| 148 | +type doesn't match (what exactly "match" means TBD), it's an error. This |
| 149 | +implies that it's impossible to hide a base class member without explicit |
| 150 | +markings. |
| 151 | + |
| 152 | +The override-by-default model requires two attributes to control when it |
| 153 | +isn't doing the right thing: "shadow" and "overload". "overload" means |
| 154 | +that the member of the derived class is an overload of the base class member; |
| 155 | +all the members from the base class and the derived class are part of overload |
| 156 | +resolution. "shadow" means that the derived class is intentionally shadowing |
| 157 | +the base class name; the name from the base class is never found by name lookup |
| 158 | +on the derived class. |
| 159 | + |
| 160 | +Accessing overridden members of the base class |
| 161 | +---------------------------------------------- |
| 162 | + |
| 163 | +Tentatively, ``super.foo()`` accesses foo from the base class, bypassing |
| 164 | +dynamic dispatch. |
| 165 | + |
| 166 | +Extensions |
| 167 | +---------- |
| 168 | + |
| 169 | +Extensions for structs can only contain methods, properties, |
| 170 | +and constructors. They always use static dispatch. |
| 171 | + |
| 172 | +Extensions for classes are more flexible in two respects: |
| 173 | + |
| 174 | +1. They can contains ivars: these are essentially baking in language support |
| 175 | + for a side-table lookup. They must be either default-initializable or have |
| 176 | + an explicit initializer on the variable definition. The initializer is run |
| 177 | + lazily. |
| 178 | +2. Members of extensions of classes can be overridden (?). Per our discussion |
| 179 | + in the meeting, I thought this model could work, but in retrospect it might |
| 180 | + be way too confusing; if you have a base class X and a derived class Y, |
| 181 | + overriding an extension of X in an extension of Y leads to strange behavior |
| 182 | + depending on whether the extension of Y is loaded (essentially, the same |
| 183 | + weirdness of ObjC categories and linking). |
| 184 | + |
| 185 | +Name lookup for extensions works much the same way that it does for a derived |
| 186 | +class, except that rather than base class vs. derived class, it's names from |
| 187 | +current extension vs. names from other sources (or something similar to this). |
| 188 | +If there's multiple declarations with the same name, it's an error, and the |
| 189 | +user has to resolve it with "shadow", and "overload" (where "shadow" only works |
| 190 | +for names from other modules; we'll want some other mechanism for name remapping |
| 191 | +for protocol implementations). |
| 192 | + |
| 193 | +Constructors in extensions are required to delegate to another constructor. This |
| 194 | +is necessary because of access-control etc. (FIXME: implicit delegation?) |
0 commit comments