mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
346 lines
11 KiB
C++
346 lines
11 KiB
C++
//===--- SILDefaultOverrideTable.h ------------------------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// TLDR: class : protocol :: SILDefaultOverrideTable : SILDefaultWitnessTable
|
|
//
|
|
// This file defines the SILDefaultOverrideTable class, which is used to
|
|
// provide default override implementations of class routines which have been
|
|
// come to implement the same semantic class member that was previously
|
|
// implemented by a different routine. As with SILDefaultWitnessTable, this
|
|
// type enables IRGen to generate metadata which in turn allows the runtime to
|
|
// instantiate vtables which contain these default overrides when they are
|
|
// needed: in the vtables of subclasses which were emitted prior to the
|
|
// replacement of the routine that implements the semantic member AND which
|
|
// already provided an override of the routine that previously implemented the
|
|
// semantic member.
|
|
//
|
|
// +--Example--------------------------------------------------------------{{ -+
|
|
//
|
|
// ResilientFramework v1:
|
|
// open class C {
|
|
// open var x: X {
|
|
// _read {
|
|
// ...
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// func useCOrDerived(_ c: C) {
|
|
// // calls C.x._read
|
|
// let x = C.x
|
|
// }
|
|
//
|
|
// ResilientFramework v2:
|
|
// open class C {
|
|
// open var: x: X {
|
|
// read {
|
|
// ...
|
|
// }
|
|
// // compiler generated thunk
|
|
// _read {
|
|
// yield x.read() // this isn't actually expressible in Swift
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// func useCOrDerived(_ c: C) {
|
|
// // calls C.x.read (!!!)
|
|
// let x = C.x
|
|
// pass(x)
|
|
// }
|
|
//
|
|
// The "semantic class member" here is the "reader" for C.x. In 1, this member
|
|
// was implemented by C.x._read. In 2.0, this was implemented by C.x.read. In
|
|
// other words, when C.x is read from some instance `Instance` of C or a
|
|
// subclass (e.g. in useCOrDerived), the C.x.read routine of `Instance`'s class
|
|
// is used. More concretely, the routine stored in the C.x.read slot from
|
|
// class(`Instance`)'s vtable is dispatched to.
|
|
//
|
|
// Without the solution provided SILDefaultOverrideTable, that slot could
|
|
// contain the wrong routine!, namely C's implementation of C.x.read when the
|
|
// subclass overrode C.x._read, as illustrated below.
|
|
//
|
|
// ClientBinary, built against version 1:
|
|
// class D : C {
|
|
// override var x: X {
|
|
// _read {
|
|
// ...
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// When C.x._read is called on an instance of D, D's override of that routine is
|
|
// called. When running ClientBinary against ResilientFramework v1, this is
|
|
// sufficient: useCOrDerived's reading of x dispatches to the routine in the
|
|
// x._read slot of the vtable, which D's vtable contains an override of.
|
|
//
|
|
// In detail, here's what happens at runtime: {{ // Runtime situation with v1
|
|
//
|
|
// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived.
|
|
// func useCOrDerived(_ c: C) {
|
|
// let vtable = typeof(c).VTable
|
|
// let reader = vtable[#slot(x._read)]
|
|
// let x = reader(c)
|
|
// pass(x)
|
|
// }
|
|
//
|
|
// +- C.VTable ----------+
|
|
// | slot | impl |
|
|
// +---------+-----------+
|
|
// | x._read | C.x._read |
|
|
// +---------+-----------+
|
|
//
|
|
// +- D.VTable ----------+
|
|
// | slot | impl |
|
|
// +---------+-----------+
|
|
// | x._read | D.x._read |
|
|
// +---------+-----------+
|
|
//
|
|
// When an instance d of D is passed, execution will proceed thus:
|
|
//
|
|
// func useCOrDerived(_ c: C = d: D) {
|
|
// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable
|
|
// let reader = vtable[#slot(x._read)] = D.VTable[#slot(x._read)] = D.x._read
|
|
// let x = reader(c) = D.x._read(d)
|
|
// pass (x)
|
|
// }
|
|
//
|
|
// In other words, D's override of x._read will be looked up in D's vtable and
|
|
// invoked.
|
|
//
|
|
// }} // Runtime situation with v1
|
|
//
|
|
// Now, suppose no additional machinery were added to the runtime, and consider
|
|
// the same situation when running against ResilientFramework v2. When running
|
|
// ClientBinary against ResilientFramework v2, useCOrDerived's reading of x
|
|
// dispatches to the routine in the x.read slot of the vtable (NOT the x._read
|
|
// slot). And D's vtable contains no override of that routine!
|
|
//
|
|
// Here's the v2 runtime situation WITHOUT additional machinery: {{ // BAD runtime situation with v2
|
|
//
|
|
// // Pseudocode describing dispatch in ResilientFramework.useCOrDerived.
|
|
// func useCOrDerived(_ c: C) {
|
|
// let vtable = typeof(c).VTable
|
|
// let reader = vtable[#slot(x.read)] // NOTE! This is now x.read NOT x._read!
|
|
// let x = reader(c)
|
|
// pass(x)
|
|
// }
|
|
//
|
|
// +- C.VTable ----------+
|
|
// | slot | impl |
|
|
// +---------+-----------+
|
|
// | x._read | C.x._read |
|
|
// | x.read | C.x.read |
|
|
// +---------+-----------+
|
|
//
|
|
// +- D.VTable ----------+
|
|
// | slot | impl |
|
|
// +---------+-----------+
|
|
// | x._read | D.x._read |
|
|
// | x.read | C.x.read | <- The bad entry!
|
|
// +---------+-----------+
|
|
//
|
|
// When an instance d of D is passed, execution will proceed thus:
|
|
//
|
|
// func useCOrDerived(_ c: C = d: D) {
|
|
// let vtable = typeof(c).VTable = typeof(d).VTable = D.VTable
|
|
// // The wrong implementation is looked up!
|
|
// let reader = vtable[#slot(x.read)] = D.VTable[#slot(x.read)] = C.x.read
|
|
// // The wrong implementation is called!
|
|
// let x = reader(c) = C.x._read(d)
|
|
// pass (x)
|
|
// }
|
|
//
|
|
// In other words, D's override of x._read is ignored, and C's implementation is
|
|
// used.
|
|
//
|
|
// }} // BAD runtime situation with v2
|
|
//
|
|
// As described so far, this problem has a few solutions. Those not taken are
|
|
// mentioned now with their reason for rejection.
|
|
//
|
|
// Rejected solutions:
|
|
//
|
|
// (a) Don't change the slot dispatched to by useCOrDerived.
|
|
//
|
|
// Rejection rationale: The new routine C.x.read has an improved ABI.
|
|
//
|
|
// (b) Reuse D.x._read in the x.read slot.
|
|
//
|
|
// Rejection rationale: D.x._read has the ABI appropriate for x._read and
|
|
// not the ABI appropriate for x.read.
|
|
//
|
|
// Accepted solution:
|
|
//
|
|
// Provide an implementation of x.read for use by D. The implementation is as
|
|
// follows, in pseudo-code that can't be written in Swift:
|
|
//
|
|
// ResilientFrameworkv2:
|
|
// func C_x_read_default_override(self: C) yields_once -> X {
|
|
// yield self.x._read // vtable dispatch
|
|
// }
|
|
//
|
|
// At runtime, when the VTable for D is assembled (which occurs at runtime
|
|
// because D's is a subclass of C which is defined in a resilient framework),
|
|
// this default override is slotted into D's VTable for x.read:
|
|
//
|
|
// +- D.VTable --------------------------+
|
|
// | slot | impl |
|
|
// +---------+---------------------------+
|
|
// | x._read | D.x._read |
|
|
// | x.read | C_x_read_default_override |
|
|
// +---------+---------------------------+
|
|
//
|
|
// +-}} Example ---------------------------------------------------------------+
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H
|
|
#define SWIFT_SIL_SILDEFAULTOVERRIDETABLE_H
|
|
|
|
#include "swift/SIL/SILAllocated.h"
|
|
#include "swift/SIL/SILDeclRef.h"
|
|
#include "llvm/ADT/ilist.h"
|
|
|
|
namespace swift {
|
|
|
|
struct PrintOptions;
|
|
|
|
/// Map the pair of overridable member and semantically equivalent overridable
|
|
/// member to the SIL-level entity used to override the former for subclasses
|
|
/// which do override the latter.
|
|
///
|
|
/// In the example above, an entry in the table would be
|
|
///
|
|
/// (C.x.read, C.x._read) -> C_x_read_default_override.
|
|
class SILDefaultOverrideTable
|
|
: public llvm::ilist_node<SILDefaultOverrideTable>,
|
|
public SILAllocated<SILDefaultOverrideTable> {
|
|
public:
|
|
struct Entry {
|
|
/// The method for which a default implementation may be needed by
|
|
/// subclasses in old clients.
|
|
SILDeclRef method;
|
|
/// The method the presence of whose override in subclasses in old clients
|
|
/// necessitates the provision of the default override.
|
|
SILDeclRef original;
|
|
/// The default override of `method` to be provided when `original` is
|
|
/// present.
|
|
SILFunction *impl;
|
|
|
|
/// Print the entry.
|
|
void print(llvm::raw_ostream &os, bool verbose = false) const;
|
|
|
|
/// Dump the entry to stderr.
|
|
void dump() const;
|
|
};
|
|
|
|
private:
|
|
friend class SILModule;
|
|
|
|
/// The containing module.
|
|
SILModule &module;
|
|
|
|
/// The linkage of the override table.
|
|
SILLinkage linkage;
|
|
|
|
/// The ClassDecl this table pertains to.
|
|
const ClassDecl *decl;
|
|
|
|
/// The contents of the table.
|
|
MutableArrayRef<Entry> entries;
|
|
|
|
enum class State {
|
|
Declared,
|
|
Defined,
|
|
};
|
|
/// The table's current state. This must eventually become ::Defined.
|
|
State state;
|
|
|
|
/// Private constructor for making SILDefaultOverrideTable declarations.
|
|
SILDefaultOverrideTable(SILModule &M, SILLinkage Linkage,
|
|
const ClassDecl *decl);
|
|
|
|
void registerWithModule();
|
|
|
|
public:
|
|
/// Declare a new table.
|
|
static SILDefaultOverrideTable *declare(SILModule &module, SILLinkage linkage,
|
|
const ClassDecl *decl);
|
|
|
|
/// Define a new table.
|
|
static SILDefaultOverrideTable *define(SILModule &module, SILLinkage linkage,
|
|
const ClassDecl *decl,
|
|
ArrayRef<Entry> entries);
|
|
|
|
/// A name unique among override tables but not symbols.
|
|
std::string getUniqueName() const;
|
|
|
|
/// Get the linkage of the default override table.
|
|
SILLinkage getLinkage() const { return linkage; }
|
|
|
|
/// Set the linkage of the default override table.
|
|
void setLinkage(SILLinkage linkage) { this->linkage = linkage; }
|
|
|
|
/// Promote the current table from a declaration to a definition consisting of
|
|
/// the specified entries.
|
|
void define(ArrayRef<Entry> entries);
|
|
|
|
~SILDefaultOverrideTable();
|
|
|
|
SILModule &getModule() const { return module; }
|
|
|
|
/// Return true if this is a declaration with no body.
|
|
bool isDeclaration() const { return state == State::Declared; }
|
|
|
|
/// Return the ClassDecl this table pertains to.
|
|
const ClassDecl *getClass() const { return decl; }
|
|
|
|
/// Return all of the default override table entries.
|
|
ArrayRef<Entry> getEntries() const { return entries; }
|
|
|
|
/// Verify that the default override table is well-formed.
|
|
void verify(const SILModule &M) const;
|
|
|
|
/// Print the default override table.
|
|
void print(llvm::raw_ostream &os, bool verbose = false) const;
|
|
|
|
/// Dump the default override table to stderr.
|
|
void dump() const;
|
|
};
|
|
|
|
} // namespace swift
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ilist_traits for SILDefaultOverrideTable
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace llvm {
|
|
|
|
template <>
|
|
struct ilist_traits<::swift::SILDefaultOverrideTable>
|
|
: public ilist_node_traits<::swift::SILDefaultOverrideTable> {
|
|
using SILDefaultOverrideTable = ::swift::SILDefaultOverrideTable;
|
|
|
|
public:
|
|
static void deleteNode(SILDefaultOverrideTable *WT) {
|
|
WT->~SILDefaultOverrideTable();
|
|
}
|
|
|
|
private:
|
|
void createNode(const SILDefaultOverrideTable &);
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
#endif
|