|
| 1 | +//===--- SILDefaultOverrideTable.h ------------------------------*- C++ -*-===// |
| 2 | +// |
| 3 | +// This source file is part of the Swift.org open source project |
| 4 | +// |
| 5 | +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors |
| 6 | +// Licensed under Apache License v2.0 with Runtime Library Exception |
| 7 | +// |
| 8 | +// See https://swift.org/LICENSE.txt for license information |
| 9 | +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| 10 | +// |
| 11 | +//===----------------------------------------------------------------------===// |
| 12 | +// |
| 13 | +// TLDR: class : protocol :: SILDefaultOverrideTable : SILDefaultWitnessTable |
| 14 | +// |
| 15 | +// This file defines the SILDefaultOverrideTable class, which is used to |
| 16 | +// provide default override implementations of class routines which have been |
| 17 | +// come to implement the same semantic class member that was previously |
| 18 | +// implemented by a different routine. As with SILDefaultWitnessTable, this |
| 19 | +// type enables IRGen to generate metadata which in turn allows the runtime to |
| 20 | +// instantiate vtables which contain these default overrides when they are |
| 21 | +// needed: in the vtables of subclasses which were emitted prior to the |
| 22 | +// replacement of the routine that implements the semantic member AND which |
| 23 | +// already provided an override of the routine that previously implemented the |
| 24 | +// semantic member. |
| 25 | +// |
| 26 | +// +--Example--------------------------------------------------------------{{ -+ |
| 27 | +// |
| 28 | +// ResilientFramework v1: |
| 29 | +// open class C { |
| 30 | +// open var x: X { |
| 31 | +// _read { |
| 32 | +// ... |
| 33 | +// } |
| 34 | +// } |
| 35 | +// } |
| 36 | +// |
| 37 | +// func useCOrDerived(_ c: C) { |
| 38 | +// // calls C.x._read |
| 39 | +// let x = C.x |
| 40 | +// } |
| 41 | +// |
| 42 | +// ResilientFramework v2: |
| 43 | +// open class C { |
| 44 | +// open var: x: X { |
| 45 | +// read { |
| 46 | +// ... |
| 47 | +// } |
| 48 | +// // compiler generated thunk |
| 49 | +// _read { |
| 50 | +// yield x.read() // this isn't actually expressible in Swift |
| 51 | +// } |
| 52 | +// } |
| 53 | +// } |
| 54 | +// |
| 55 | +// func useCOrDerived(_ c: C) { |
| 56 | +// // calls C.x.read (!!!) |
| 57 | +// let x = C.x |
| 58 | +// pass(x) |
| 59 | +// } |
| 60 | +// |
| 61 | +// The "semantic class member" here is the "reader" for C.x. In 1, this member |
| 62 | +// was implemented by C.x._read. In 2.0, this was implemented by C.x.read. In |
| 63 | +// other words, when C.x is read from some instance `Instance` of C or a |
| 64 | +// subclass (e.g. in useCOrDerived), the C.x.read routine of `Instance`'s class |
| 65 | +// is used. More concretely, the routine stored in the C.x.read slot from |
| 66 | +// class(`Instance`)'s vtable is dispatched to. |
| 67 | +// |
| 68 | +// Without the solution provided SILDefaultOverrideTable, that slot could |
| 69 | +// contain the wrong routine!, namely C's implementation of C.x.read when the |
| 70 | +// subclass overrode C.x._read, as illustrated below. |
| 71 | +// |
| 72 | +// ClientBinary, built against version 1: |
| 73 | +// class D : C { |
| 74 | +// override var x: X { |
| 75 | +// _read { |
| 76 | +// ... |
| 77 | +// } |
| 78 | +// } |
| 79 | +// } |
| 80 | +// |
| 81 | +// When C.x._read is called on an instance of D, D's override of that routine is |
| 82 | +// called. When running ClientBinary against ResilientFramework v1, this is |
| 83 | +// sufficient: useCOrDerived's reading of x dispatches to the routine in the |
| 84 | +// x._read slot of the vtable, which D's vtable contains an override of. |
| 85 | +// |
| 86 | +// In detail, here's what happens at runtime: {{ // Runtime situation with v1 |
| 87 | +// |
| 88 | +// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived. |
| 89 | +// func useCOrDerived(_ c: C) { |
| 90 | +// let vtable = typeof(c).VTable |
| 91 | +// let reader = vtable[#slot(x._read)] |
| 92 | +// let x = reader(c) |
| 93 | +// pass(x) |
| 94 | +// } |
| 95 | +// |
| 96 | +// +- C.VTable ----------+ |
| 97 | +// | slot | impl | |
| 98 | +// +---------+-----------+ |
| 99 | +// | x._read | C.x._read | |
| 100 | +// +---------+-----------+ |
| 101 | +// |
| 102 | +// +- D.VTable ----------+ |
| 103 | +// | slot | impl | |
| 104 | +// +---------+-----------+ |
| 105 | +// | x._read | D.x._read | |
| 106 | +// +---------+-----------+ |
| 107 | +// |
| 108 | +// When an instance d of D is passed, execution will proceed thus: |
| 109 | +// |
| 110 | +// func useCOrDerived(_ c: C = d: D) { |
| 111 | +// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable |
| 112 | +// let reader = vtable[#slot(x._read)] = D.VTable[#slot(x._read)] = D.x._read |
| 113 | +// let x = reader(c) = D.x._read(d) |
| 114 | +// pass (x) |
| 115 | +// } |
| 116 | +// |
| 117 | +// In other words, D's override of x._read will be looked up in D's vtable and |
| 118 | +// invoked. |
| 119 | +// |
| 120 | +// }} // Runtime situation with v1 |
| 121 | +// |
| 122 | +// Now, suppose no additional machinery were added to the runtime, and consider |
| 123 | +// the same situation when running against ResilientFramework v2. When running |
| 124 | +// ClientBinary against ResilientFramework v2, useCOrDerived's reading of x |
| 125 | +// dispatches to the routine in the x.read slot of the vtable (NOT the x._read |
| 126 | +// slot). And D's vtable contains no override of that routine! |
| 127 | +// |
| 128 | +// Here's the v2 runtime situation WITHOUT additional machinery: {{ // BAD runtime situation with v2 |
| 129 | +// |
| 130 | +// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived. |
| 131 | +// func useCOrDerived(_ c: C) { |
| 132 | +// let vtable = typeof(c).VTable |
| 133 | +// let reader = vtable[#slot(x.read)] // NOTE! This is now x.read NOT x._read! |
| 134 | +// let x = reader(c) |
| 135 | +// pass(x) |
| 136 | +// } |
| 137 | +// |
| 138 | +// +- C.VTable ----------+ |
| 139 | +// | slot | impl | |
| 140 | +// +---------+-----------+ |
| 141 | +// | x._read | C.x._read | |
| 142 | +// | x.read | C.x.read | |
| 143 | +// +---------+-----------+ |
| 144 | +// |
| 145 | +// +- D.VTable ----------+ |
| 146 | +// | slot | impl | |
| 147 | +// +---------+-----------+ |
| 148 | +// | x._read | D.x._read | |
| 149 | +// | x.read | C.x.read | <- The bad entry! |
| 150 | +// +---------+-----------+ |
| 151 | +// |
| 152 | +// When an instance d of D is passed, execution will proceed thus: |
| 153 | +// |
| 154 | +// func useCOrDerived(_ c: C = d: D) { |
| 155 | +// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable |
| 156 | +// // The wrong implementation is looked up! |
| 157 | +// let reader = vtable[#slot(x.read)] = D.VTable[#slot(x.read)] = C.x.read |
| 158 | +// // The wrong implementation is called! |
| 159 | +// let x = reader(c) = C.x._read(d) |
| 160 | +// pass (x) |
| 161 | +// } |
| 162 | +// |
| 163 | +// In other words, D's override of x._read is ignored, and C's implementation is |
| 164 | +// used. |
| 165 | +// |
| 166 | +// }} // BAD runtime situation with v2 |
| 167 | +// |
| 168 | +// As described so far, this problem has a few solutions. Those not taken are |
| 169 | +// mentioned now with their reason for rejection. |
| 170 | +// |
| 171 | +// Rejected solutions: |
| 172 | +// |
| 173 | +// (a) Don't change the slot dispatched to by useCOrDerived. |
| 174 | +// |
| 175 | +// Rejection rationale: The new routine C.x.read has an improved ABI. |
| 176 | +// |
| 177 | +// (b) Reuse D.x._read in the x.read slot. |
| 178 | +// |
| 179 | +// Rejection rationale: D.x._read has the ABI appropriate for x._read and |
| 180 | +// not the ABI appropriate for x.read. |
| 181 | +// |
| 182 | +// Accepted solution: |
| 183 | +// |
| 184 | +// Provide an implementation of x.read for use by D. The implementation is as |
| 185 | +// follows, in pseudo-code that can't be written in Swift: |
| 186 | +// |
| 187 | +// ResilientFrameworkv2: |
| 188 | +// func C_x_read_default_override(self: C) yields_once -> X { |
| 189 | +// yield self.x._read // vtable dispatch |
| 190 | +// } |
| 191 | +// |
| 192 | +// At runtime, when the VTable for D is assembled (which occurs at runtime |
| 193 | +// because D's is a subclass of C which is defined in a resilient framework), |
| 194 | +// this default override is slotted into D's VTable for x.read: |
| 195 | +// |
| 196 | +// +- D.VTable --------------------------+ |
| 197 | +// | slot | impl | |
| 198 | +// +---------+---------------------------+ |
| 199 | +// | x._read | D.x._read | |
| 200 | +// | x.read | C_x_read_default_override | |
| 201 | +// +---------+---------------------------+ |
| 202 | +// |
| 203 | +// +-}} Example ---------------------------------------------------------------+ |
| 204 | +// |
| 205 | +//===----------------------------------------------------------------------===// |
| 206 | + |
| 207 | +#ifndef SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H |
| 208 | +#define SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H |
| 209 | + |
| 210 | +#include "swift/SIL/SILAllocated.h" |
| 211 | +#include "swift/SIL/SILDeclRef.h" |
| 212 | +#include "llvm/ADT/ilist.h" |
| 213 | + |
| 214 | +namespace swift { |
| 215 | + |
| 216 | +struct PrintOptions; |
| 217 | + |
| 218 | +/// Map the pair of overridable member and semantically equivalent overridable |
| 219 | +/// member to the SIL-level entity used to override the former for subclasses |
| 220 | +/// which do override the latter. |
| 221 | +/// |
| 222 | +/// In the example above, an entry in the table would be |
| 223 | +/// |
| 224 | +/// (C.x.read, C.x._read) -> C_x_read_default_override. |
| 225 | +class SILDefaultOverrideTable |
| 226 | + : public llvm::ilist_node<SILDefaultOverrideTable>, |
| 227 | + public SILAllocated<SILDefaultOverrideTable> { |
| 228 | +public: |
| 229 | + struct Entry { |
| 230 | + /// The method for which a default implementation may be needed by |
| 231 | + /// subclasses in old clients. |
| 232 | + SILDeclRef method; |
| 233 | + /// The method the presence of whose override in subclasses in old clients |
| 234 | + /// necessitates the provision of the default override. |
| 235 | + SILDeclRef original; |
| 236 | + /// The default override of `method` to be provided when `original` is |
| 237 | + /// present. |
| 238 | + SILFunction *impl; |
| 239 | + }; |
| 240 | + |
| 241 | +private: |
| 242 | + friend class SILModule; |
| 243 | + |
| 244 | + /// The containing module. |
| 245 | + SILModule &module; |
| 246 | + |
| 247 | + /// The linkage of the override table. |
| 248 | + SILLinkage linkage; |
| 249 | + |
| 250 | + /// The ClassDecl this table pertains to. |
| 251 | + const ClassDecl *decl; |
| 252 | + |
| 253 | + /// The contents of the table. |
| 254 | + MutableArrayRef<Entry> entries; |
| 255 | + |
| 256 | + enum class State { |
| 257 | + Declared, |
| 258 | + Defined, |
| 259 | + }; |
| 260 | + /// The table's current state. This must eventually become ::Defined. |
| 261 | + State state; |
| 262 | + |
| 263 | + /// Private constructor for making SILDefaultOverrideTable declarations. |
| 264 | + SILDefaultOverrideTable(SILModule &M, SILLinkage Linkage, |
| 265 | + const ClassDecl *decl); |
| 266 | + |
| 267 | + void registerWithModule(); |
| 268 | + |
| 269 | +public: |
| 270 | + /// Declare a new table. |
| 271 | + static SILDefaultOverrideTable *declare(SILModule &module, SILLinkage linkage, |
| 272 | + const ClassDecl *decl); |
| 273 | + |
| 274 | + /// Define a new table. |
| 275 | + static SILDefaultOverrideTable *define(SILModule &module, SILLinkage linkage, |
| 276 | + const ClassDecl *decl, |
| 277 | + ArrayRef<Entry> entries); |
| 278 | + |
| 279 | + /// A name unique among override tables but not symbols. |
| 280 | + std::string getUniqueName() const; |
| 281 | + |
| 282 | + /// Get the linkage of the default override table. |
| 283 | + SILLinkage getLinkage() const { return linkage; } |
| 284 | + |
| 285 | + /// Set the linkage of the default override table. |
| 286 | + void setLinkage(SILLinkage linkage) { this->linkage = linkage; } |
| 287 | + |
| 288 | + /// Promote the current table from a declaration to a definition consisting of |
| 289 | + /// the specified entries. |
| 290 | + void define(ArrayRef<Entry> entries); |
| 291 | + |
| 292 | + ~SILDefaultOverrideTable(); |
| 293 | + |
| 294 | + SILModule &getModule() const { return module; } |
| 295 | + |
| 296 | + /// Return true if this is a declaration with no body. |
| 297 | + bool isDeclaration() const { return state == State::Declared; } |
| 298 | + |
| 299 | + /// Return the ClassDecl this table pertains to. |
| 300 | + const ClassDecl *getClass() const { return decl; } |
| 301 | + |
| 302 | + /// Return all of the default override table entries. |
| 303 | + ArrayRef<Entry> getEntries() const { return entries; } |
| 304 | + |
| 305 | + /// Verify that the default override table is well-formed. |
| 306 | + void verify(const SILModule &M) const; |
| 307 | +}; |
| 308 | + |
| 309 | +} // namespace swift |
| 310 | + |
| 311 | +//===----------------------------------------------------------------------===// |
| 312 | +// ilist_traits for SILDefaultOverrideTable |
| 313 | +//===----------------------------------------------------------------------===// |
| 314 | + |
| 315 | +namespace llvm { |
| 316 | + |
| 317 | +template <> |
| 318 | +struct ilist_traits<::swift::SILDefaultOverrideTable> |
| 319 | + : public ilist_node_traits<::swift::SILDefaultOverrideTable> { |
| 320 | + using SILDefaultOverrideTable = ::swift::SILDefaultOverrideTable; |
| 321 | + |
| 322 | +public: |
| 323 | + static void deleteNode(SILDefaultOverrideTable *WT) { |
| 324 | + WT->~SILDefaultOverrideTable(); |
| 325 | + } |
| 326 | + |
| 327 | +private: |
| 328 | + void createNode(const SILDefaultOverrideTable &); |
| 329 | +}; |
| 330 | + |
| 331 | +} // namespace llvm |
| 332 | + |
| 333 | +#endif |
0 commit comments