Skip to content

Commit c3c9b1b

Browse files
authored
Merge pull request #42047 from rjmccall/extended-existential-type-metadata
Add type metadata for extended existential types
2 parents 06a0462 + 148357f commit c3c9b1b

18 files changed

+2428
-19
lines changed

include/swift/ABI/GenericContext.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ class TargetGenericRequirementDescriptor {
184184
using GenericRequirementDescriptor =
185185
TargetGenericRequirementDescriptor<InProcess>;
186186

187+
/// An array of generic parameter descriptors, all
188+
/// GenericParamDescriptor::implicit(), which is by far
189+
/// the most common case. Some generic context storage can
190+
/// avoid storing descriptors when they all match this pattern.
191+
extern const GenericParamDescriptor
192+
ImplicitGenericParamDescriptors[MaxNumImplicitGenericParamDescriptors];
193+
187194
/// A runtime description of a generic signature.
188195
class RuntimeGenericSignature {
189196
GenericContextDescriptorHeader Header;

include/swift/ABI/Metadata.h

Lines changed: 363 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ template <class T> struct FullMetadata : T::HeaderType, T {
140140
FullMetadata() = default;
141141
constexpr FullMetadata(const HeaderType &header, const T &metadata)
142142
: HeaderType(header), T(metadata) {}
143+
144+
template <class... Args>
145+
constexpr FullMetadata(const HeaderType &header, Args &&...metadataArgs)
146+
: HeaderType(header), T(std::forward<Args>(metadataArgs)...) {}
143147
};
144148

145149
/// Given a canonical metadata pointer, produce the adjusted metadata pointer.
@@ -1571,7 +1575,14 @@ enum class ExistentialTypeRepresentation {
15711575
Error,
15721576
};
15731577

1574-
/// The structure of existential type metadata.
1578+
/// The structure of type metadata for simple existential types which
1579+
/// don't require an extended existential descriptor:
1580+
///
1581+
/// - They are existential over a single type T.
1582+
/// - Their head type is that type T.
1583+
/// - Their existential constraints are a composition of protocol
1584+
/// requirements, superclass constraints, and possibly a class
1585+
/// layout constraint on T.
15751586
template <typename Runtime>
15761587
struct TargetExistentialTypeMetadata
15771588
: TargetMetadata<Runtime>,
@@ -1689,6 +1700,357 @@ struct TargetExistentialTypeMetadata
16891700
using ExistentialTypeMetadata
16901701
= TargetExistentialTypeMetadata<InProcess>;
16911702

1703+
/// A description of the shape of an existential type.
1704+
///
1705+
/// An existential type has the general form:
1706+
/// \exists <signature> . <type>
1707+
///
1708+
/// The signature is called the requirement signature. In the most
1709+
/// common case (the only one Swift currently supports), it has exactly
1710+
/// one type parameter. The types and conformances required by this
1711+
/// signature must be stored in the existential container at runtime.
1712+
///
1713+
/// The type is called the type (sub)expression, and it is expressed
1714+
/// in terms of the type parameters introduced by the requirement
1715+
/// signature. It is required to name each of the type parameters in
1716+
/// an identity position (outside of the base type of a member type
1717+
/// expression). In the most common case, it is the unique type
1718+
/// parameter type. Swift currently only supports existential
1719+
/// subexpressions that are either the (unique) type parameter or
1720+
/// a metatype thereof (possibly multiply-derived).
1721+
///
1722+
/// In order to efficiently support generic substitution of the
1723+
/// constraints of existential types, extended existential type
1724+
/// descriptors represent a generalizable form of the existential:
1725+
/// \forall <gen sig> . \exists <req sig> . <type>
1726+
///
1727+
/// The new signature is called the generalization signature of the
1728+
/// shape. All concrete references to types within the requirement
1729+
/// signature and head type which are not expressed in terms of
1730+
/// the requirement signature are replaced with type parameters freshly
1731+
/// added to the generalization signature. Any given existential type
1732+
/// is then a specialization of this generalization.
1733+
///
1734+
/// For example, the existential type
1735+
/// \exists <T: Collection where T.Element == Int> . T
1736+
/// is generalized as
1737+
/// \forall <U> . \exists <T: Collection where T.Element == U> . T
1738+
/// and is the application of this generalization at <Int>.
1739+
///
1740+
/// The soundness condition on this is that the generalization
1741+
/// signatures, requirement signatures, and head types must be
1742+
/// identical for any two existential types for which a generic
1743+
/// substitution can carry one to the other.
1744+
///
1745+
/// Only particularly complex existential types are represented with
1746+
/// an explicit existential shape. Types which are a simple composition
1747+
/// of layout, superclass, and unconstrained protocol constraints on
1748+
/// an opaque or (metatype-of)+-opaque are represented with
1749+
/// ExistentialTypeMetadata or ExistentialMetatypeMetadata.
1750+
/// Types which *can* be represented with those classes *must*
1751+
/// be represented with them.
1752+
template <typename Runtime>
1753+
struct TargetExtendedExistentialTypeShape
1754+
: swift::ABI::TrailingObjects<
1755+
TargetExtendedExistentialTypeShape<Runtime>,
1756+
// Optional generalization signature header
1757+
TargetGenericContextDescriptorHeader<Runtime>,
1758+
// Optional type subexpression
1759+
TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false>,
1760+
// Optional suggested value witnesses
1761+
TargetRelativeIndirectablePointer<Runtime, const TargetValueWitnessTable<Runtime>,
1762+
/*nullable*/ false>,
1763+
// Parameters for requirement signature, followed by parameters
1764+
// for generalization signature
1765+
GenericParamDescriptor,
1766+
// Requirements for requirement signature, followed by requirements
1767+
// for generalization signature
1768+
TargetGenericRequirementDescriptor<Runtime>> {
1769+
private:
1770+
using RelativeStringPointer =
1771+
TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false>;
1772+
using RelativeValueWitnessTablePointer =
1773+
TargetRelativeIndirectablePointer<Runtime,
1774+
const TargetValueWitnessTable<Runtime>,
1775+
/*nullable*/ false>;
1776+
using TrailingObjects =
1777+
swift::ABI::TrailingObjects<
1778+
TargetExtendedExistentialTypeShape<Runtime>,
1779+
TargetGenericContextDescriptorHeader<Runtime>,
1780+
RelativeStringPointer,
1781+
RelativeValueWitnessTablePointer,
1782+
GenericParamDescriptor,
1783+
TargetGenericRequirementDescriptor<Runtime>>;
1784+
friend TrailingObjects;
1785+
1786+
template<typename T>
1787+
using OverloadToken = typename TrailingObjects::template OverloadToken<T>;
1788+
1789+
size_t numTrailingObjects(OverloadToken<TargetGenericContextDescriptorHeader<Runtime>>) const {
1790+
return Flags.hasGeneralizationSignature();
1791+
}
1792+
1793+
size_t numTrailingObjects(OverloadToken<RelativeStringPointer>) const {
1794+
return Flags.hasTypeExpression();
1795+
}
1796+
1797+
size_t numTrailingObjects(OverloadToken<RelativeValueWitnessTablePointer>) const {
1798+
return Flags.hasSuggestedValueWitnesses();
1799+
}
1800+
1801+
size_t numTrailingObjects(OverloadToken<GenericParamDescriptor>) const {
1802+
return (Flags.hasImplicitReqSigParams() ? 0 : getNumReqSigParams())
1803+
+ (Flags.hasImplicitGenSigParams() ? 0 : getNumGenSigParams());
1804+
}
1805+
1806+
size_t numTrailingObjects(OverloadToken<GenericRequirementDescriptor>) const {
1807+
return getNumGenSigRequirements() + getNumReqSigRequirements();
1808+
}
1809+
1810+
const TargetGenericContextDescriptorHeader<Runtime> *
1811+
getGenSigHeader() const {
1812+
assert(hasGeneralizationSignature());
1813+
return this->template getTrailingObjects<
1814+
TargetGenericContextDescriptorHeader<Runtime>>();
1815+
}
1816+
1817+
public:
1818+
using SpecialKind = ExtendedExistentialTypeShapeFlags::SpecialKind;
1819+
1820+
/// Flags for the existential shape.
1821+
ExtendedExistentialTypeShapeFlags Flags;
1822+
1823+
/// The header describing the requirement signature of the existential.
1824+
TargetGenericContextDescriptorHeader<Runtime> ReqSigHeader;
1825+
1826+
RuntimeGenericSignature getRequirementSignature() const {
1827+
return {ReqSigHeader, getReqSigParams(), getReqSigRequirements()};
1828+
}
1829+
1830+
unsigned getNumReqSigParams() const {
1831+
return ReqSigHeader.NumParams;
1832+
}
1833+
1834+
const GenericParamDescriptor *getReqSigParams() const {
1835+
return Flags.hasImplicitReqSigParams()
1836+
? ImplicitGenericParamDescriptors
1837+
: this->template getTrailingObjects<GenericParamDescriptor>();
1838+
}
1839+
1840+
unsigned getNumReqSigRequirements() const {
1841+
return ReqSigHeader.NumRequirements;
1842+
}
1843+
1844+
const TargetGenericRequirementDescriptor<Runtime> *
1845+
getReqSigRequirements() const {
1846+
return this->template getTrailingObjects<
1847+
TargetGenericRequirementDescriptor<Runtime>>();
1848+
}
1849+
1850+
/// The type expression of the existential, as a symbolic mangled type
1851+
/// string. Must be null if the header is just the (single)
1852+
/// requirement type parameter.
1853+
TargetPointer<Runtime, const char> getTypeExpression() const {
1854+
return Flags.hasTypeExpression()
1855+
? this->template getTrailingObjects<RelativeStringPointer>()->get()
1856+
: nullptr;
1857+
}
1858+
1859+
bool isTypeExpressionOpaque() const {
1860+
return !Flags.hasTypeExpression();
1861+
}
1862+
1863+
/// The suggested value witness table for the existential.
1864+
/// Required if:
1865+
/// - the special kind is SpecialKind::ExplicitLayout
1866+
/// - the special kind is SpecialKind::None and the type head
1867+
/// isn't opaque
1868+
TargetPointer<Runtime, const TargetValueWitnessTable<Runtime>>
1869+
getSuggestedValueWitnesses() const {
1870+
return Flags.hasSuggestedValueWitnesses()
1871+
? this->template getTrailingObjects<
1872+
RelativeValueWitnessTablePointer>()->get()
1873+
: nullptr;
1874+
}
1875+
1876+
/// Return the amount of space used in the existential container
1877+
/// for storing the existential arguments (including both the
1878+
/// type metadata and the conformances).
1879+
unsigned getContainerSignatureLayoutSizeInWords() const {
1880+
unsigned rawSize = ReqSigHeader.getArgumentLayoutSizeInWords();
1881+
switch (Flags.getSpecialKind()) {
1882+
// The default and explicitly-sized-value-layout cases don't optimize
1883+
// the storage of the signature.
1884+
case SpecialKind::None:
1885+
case SpecialKind::ExplicitLayout:
1886+
return rawSize;
1887+
1888+
// The class and metadata cases don't store type metadata.
1889+
case SpecialKind::Class:
1890+
case SpecialKind::Metatype:
1891+
// Requirement signatures won't have non-key parameters.
1892+
return rawSize - ReqSigHeader.NumParams;
1893+
}
1894+
1895+
// Assume any future cases don't optimize metadata storage.
1896+
return rawSize;
1897+
}
1898+
1899+
bool hasGeneralizationSignature() const {
1900+
return Flags.hasGeneralizationSignature();
1901+
}
1902+
1903+
RuntimeGenericSignature getGeneralizationSignature() const {
1904+
if (!hasGeneralizationSignature()) return RuntimeGenericSignature();
1905+
return {*getGenSigHeader(), getGenSigParams(), getGenSigRequirements()};
1906+
}
1907+
1908+
unsigned getNumGenSigParams() const {
1909+
return hasGeneralizationSignature()
1910+
? getGenSigHeader()->NumParams : 0;
1911+
}
1912+
1913+
const GenericParamDescriptor *getGenSigParams() const {
1914+
assert(hasGeneralizationSignature());
1915+
if (Flags.hasImplicitGenSigParams())
1916+
return ImplicitGenericParamDescriptors;
1917+
auto base = this->template getTrailingObjects<GenericParamDescriptor>();
1918+
if (!Flags.hasImplicitReqSigParams())
1919+
base += getNumReqSigParams();
1920+
return base;
1921+
}
1922+
1923+
unsigned getNumGenSigRequirements() const {
1924+
return hasGeneralizationSignature()
1925+
? getGenSigHeader()->NumRequirements : 0;
1926+
}
1927+
1928+
const TargetGenericRequirementDescriptor<Runtime> *
1929+
getGenSigRequirements() const {
1930+
assert(hasGeneralizationSignature());
1931+
return getReqSigRequirements() + ReqSigHeader.NumRequirements;
1932+
}
1933+
1934+
/// Return the amount of space used in ExtendedExistentialTypeMetadata
1935+
/// for this shape to store the generalization arguments.
1936+
unsigned getGenSigArgumentLayoutSizeInWords() const {
1937+
if (!hasGeneralizationSignature()) return 0;
1938+
return getGenSigHeader()->getArgumentLayoutSizeInWords();
1939+
}
1940+
};
1941+
using ExtendedExistentialTypeShape
1942+
= TargetExtendedExistentialTypeShape<InProcess>;
1943+
1944+
/// A hash which is guaranteed (ignoring a weakness in the
1945+
/// selected cryptographic hash algorithm) to be unique for the
1946+
/// source string. Cryptographic hashing is reasonable to use
1947+
/// for certain kinds of complex uniquing performed by the
1948+
/// runtime when the representation being uniqued would otherwise
1949+
/// be prohibitively difficult to hash and compare, such as a
1950+
/// generic signature.
1951+
///
1952+
/// The hash is expected to be computed at compile time and simply
1953+
/// trusted at runtime. We are therefore not concerned about
1954+
/// malicious collisions: an attacker would have to control the
1955+
/// program text, and there is nothing they can accomplish from a
1956+
/// hash collision that they couldn't do more easily by just changing
1957+
/// the program to do what they want. We just want a hash with
1958+
/// minimal chance of a birthday-problem collision. As long as
1959+
/// we use a cryptographic hash algorithm, even a 64-bit hash would
1960+
/// probably do the trick just fine, since the number of objects
1961+
/// being uniqued will be far less than 2^32. Still, to stay on the
1962+
/// safe side, we use a 128-bit hash; the additional 8 bytes is
1963+
/// fairly marginal compared to the size of (e.g.) even the smallest
1964+
/// existential shape.
1965+
///
1966+
/// We'd like to use BLAKE3 for this, but pending acceptance of
1967+
/// that into LLVM, we're using SHA-256. We simply truncaate the
1968+
/// hash to the desired length.
1969+
struct UniqueHash {
1970+
static_assert(NumBytes_UniqueHash % sizeof(uint32_t) == 0,
1971+
"NumBytes_UniqueHash not a multiple of 4");
1972+
enum { NumChunks = NumBytes_UniqueHash / sizeof(uint32_t) };
1973+
1974+
uint32_t Data[NumChunks];
1975+
1976+
friend bool operator==(const UniqueHash &lhs, const UniqueHash &rhs) {
1977+
for (unsigned i = 0; i != NumChunks; ++i)
1978+
if (lhs.Data[i] != rhs.Data[i])
1979+
return false;
1980+
return true;
1981+
}
1982+
1983+
friend uint32_t hash_value(const UniqueHash &hash) {
1984+
// It's a cryptographic hash, so there's no point in merging
1985+
// hash data from multiple chunks.
1986+
return hash.Data[0];
1987+
}
1988+
};
1989+
1990+
/// A descriptor for an extended existential type descriptor which
1991+
/// needs to be uniqued at runtime.
1992+
template <typename Runtime>
1993+
struct TargetNonUniqueExtendedExistentialTypeShape {
1994+
/// A reference to memory that can be used to cache a globally-unique
1995+
/// descriptor for this existential shape.
1996+
TargetRelativeDirectPointer<Runtime,
1997+
std::atomic<ConstTargetMetadataPointer<Runtime,
1998+
TargetExtendedExistentialTypeShape>>> UniqueCache;
1999+
2000+
/// A hash of the mangling of the existential shape.
2001+
///
2002+
/// TODO: describe that mangling here
2003+
UniqueHash Hash;
2004+
2005+
/// The local copy of the existential shape descriptor.
2006+
TargetExtendedExistentialTypeShape<Runtime> LocalCopy;
2007+
};
2008+
using NonUniqueExtendedExistentialTypeShape
2009+
= TargetNonUniqueExtendedExistentialTypeShape<InProcess>;
2010+
2011+
/// The structure of type metadata for existential types which require
2012+
/// an extended existential descriptor.
2013+
///
2014+
/// An extended existential type metadata is a concrete application of
2015+
/// an extended existential descriptor to its generalization arguments,
2016+
/// which there may be none of. See ExtendedExistentialDescriptor.
2017+
template <typename Runtime>
2018+
struct TargetExtendedExistentialTypeMetadata
2019+
: TargetMetadata<Runtime>,
2020+
swift::ABI::TrailingObjects<
2021+
TargetExtendedExistentialTypeMetadata<Runtime>,
2022+
ConstTargetPointer<Runtime, void>> {
2023+
private:
2024+
using TrailingObjects =
2025+
swift::ABI::TrailingObjects<
2026+
TargetExtendedExistentialTypeMetadata<Runtime>,
2027+
ConstTargetPointer<Runtime, void>>;
2028+
friend TrailingObjects;
2029+
2030+
template<typename T>
2031+
using OverloadToken = typename TrailingObjects::template OverloadToken<T>;
2032+
2033+
size_t numTrailingObjects(OverloadToken<ConstTargetPointer<Runtime, void>>) const {
2034+
return Shape->getGenSigLayoutSizeInWords();
2035+
}
2036+
2037+
public:
2038+
explicit constexpr
2039+
TargetExtendedExistentialTypeMetadata(const ExtendedExistentialTypeShape *shape)
2040+
: TargetMetadata<Runtime>(MetadataKind::ExtendedExistential),
2041+
Shape(shape) {}
2042+
2043+
TargetSignedPointer<Runtime, const ExtendedExistentialTypeShape *
2044+
__ptrauth_swift_nonunique_extended_existential_type_shape>
2045+
Shape;
2046+
2047+
ConstTargetPointer<Runtime, void> const *getGeneralizationArguments() const {
2048+
return this->template getTrailingObjects<ConstTargetPointer<Runtime, void>>();
2049+
}
2050+
};
2051+
using ExtendedExistentialTypeMetadata
2052+
= TargetExtendedExistentialTypeMetadata<InProcess>;
2053+
16922054
/// The basic layout of an existential metatype type.
16932055
template <typename Runtime>
16942056
struct TargetExistentialMetatypeContainer {

0 commit comments

Comments
 (0)