Skip to content

Commit 148357f

Browse files
committed
Implement extended existential shapes and type metadata.
The immediate use case is only concretely-constrained existential types, which could use a much simpler representation, but I've future-proofed the representation as much as I can; thus, the requirement signature can have arbitrary parameters and requirements, and the type can have an arbitrary type as the sub-expression. The latter is also necessary for existential metatypes. The chief implementation complexity here is that we must be able to agree on the identity of an existential type that might be produced by substitution. Thus, for example, `any P<T>` when `T == Int` must resolve to the same type metadata as `any P<Int>`. To handle this, we identify the "shape" of the existential type, consisting of those parts which cannot possibly be the result of substitution, and then abstract the substitutable "holes" as an application of a generalization signature. That algorithm will come in a later patch; this patch just represents it. Uniquing existential shapes from the requirements would be quite complex because of all the symbolic mangled names they use. This is particularly true because it's not reasonable to require translation units to agree about what portions they mangle vs. reference symbolically. Instead, we expect the compiler to do a cryptographic hash of a mangling of the shape, then use that as the unique key identifying the shape. This is just the core representation and runtime interface; other parts of the runtime, such as dynamic casting and demangling support, will come later.
1 parent a986d74 commit 148357f

File tree

12 files changed

+1362
-7
lines changed

12 files changed

+1362
-7
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)