Skip to content

Commit 488c6e8

Browse files
committed
[ABI] Initial draft of canonicalization and minimization of generic signatures
1 parent 5ad254b commit 488c6e8

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

docs/ABI/GenericSignature.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Generic Signatures
2+
3+
A generic signature describes a set of generic type parameters along with
4+
a set of constraints on those type parameters. Generic entities in Swift
5+
have a corresponding generic signature. For example, the following generic function:
6+
7+
```swift
8+
func foo<C1: Collection, C2: Collection>(c1: C1, c2: C2)
9+
where C1.Element: Equatable, C1.Element == C2.Element
10+
{ }
11+
```
12+
13+
has the generic signature:
14+
15+
```swift
16+
<C1, C2 where C1: Collection, C2: Collection, C1.Element: Equatable,
17+
C1.Element == C2.Element>
18+
```
19+
20+
Generic signatures are used in a few places within the ABI, including:
21+
22+
* The mangled names of generic entities include the generic signature
23+
* The generic type parameters and protocol-conformance constraints in a generic signature are mapped to type metadata and witness-table parameters in a generic function, respectively.
24+
25+
Whenever used in the ABI, a generic signature must be both *minimal* and *canonical*, as defined below.
26+
27+
## Minimization
28+
29+
A generic constraint is considered *redundant* if it can be proven true based on some combination of other constraints within the same generic signature. Redundant constraints can be removed from a generic signature without affecting the semantics of the signature. A generic signature is *minimal* when it does not contain any constraints that are redundant.
30+
31+
Consider the following generic signature:
32+
33+
```swift
34+
<C1, C2 where C1: Collection, C2: Collection, C1.Element: Equatable,
35+
C1.Element == C2.Element, C2.Element: Equatable>
36+
```
37+
38+
The constraint `C1.Element: Equatable` is redundant (because it can be proven based on `C1.Element == C2.Element` and `C2.Element: Equatable`). Similarly, `C2.Element: Equatable` is redundant (based on `C1.Element == C2.Element` and `C1.Element: Equatable`). Either one of these constraints can be removed without changing the semantics of the generic signature, and the resulting generic signature will be minimal (there are no redundant constraints that remain). As such, there are two minimal generic signatures that describe this set of constraints:
39+
40+
```swift
41+
<C1, C2 where C1: Collection, C2: Collection, C1.Element: Equatable,
42+
C1.Element == C2.Element>
43+
```
44+
45+
and
46+
47+
```swift
48+
<C1, C2 where C1: Collection, C2: Collection, C1.Element == C2.Element,
49+
C2.Element: Equatable>
50+
```
51+
52+
Removing both constraints would produce a semantically different generic signature. The following section on canonicalization details why the first generic signature is the signature used for ABI purposes.
53+
54+
## Canonicalization
55+
56+
A generic signature is *canonical* when each of its constraints is [canonical](#canonical-constraints) and the entries in the generic signature appear in canonical order.
57+
58+
1. Generic type parameters (that are not nested types) are listed first
59+
ordered by [type parameter ordering](#type-parameter-ordering)
60+
2. Constraints follow, ordered first by the
61+
[type parameter ordering](#type-parameter-ordering) of the left-hand
62+
operand and then by constraint kind. The left-hand side of a constraint
63+
is always a type parameter `T`, which can be a generic parameter of a
64+
nested type thereof (e.g., `T.SubSequence.Iterator.Element`).
65+
Constraints are ordered as follows:
66+
1. A superclass constraint `T: C`, where `C` is a class.
67+
2. A layout constraints (e.g., `T: some-layout`), where the right-hand
68+
side is `AnyObject` or one of the non-user-visible layout constraints
69+
like `_Trivial`.
70+
3. Conformance constraints `T: P`, where `P` is a protocol. The
71+
conformance constraints for a given type parameter `T` are further
72+
sorted using the [protocol ordering](#protocol-ordering).
73+
4. A same-type constraint `T == U`, where `U` is either a type parameter
74+
or a concrete type.
75+
76+
### Type parameter ordering
77+
78+
Given two type parameters `T1` and `T2`, `T1` precedes `T2` in the canonical ordering if:
79+
80+
* `T1` and `T2` are generic type parameters with depths `d1` and `d2`, and indices `i1` and `i2`, respectively, and either `d1 < d2` or `d1 == d2 && i1 < i2`;
81+
* `T1` is a generic type parameter and `T2` is a nested type `U2.A2`; or
82+
* `T1` is a nested type `U1.A1` and `T2` is a nested type `U2.A2`, where `A1` and `A2` name associated types of the protocols `P1` and `P2`, respectively, and either
83+
* `U1` precedes `U2` in the canonical ordering, or
84+
* `U1 == U2` and the name of `A1` lexicographically precedes the name of `A2`, or
85+
* `U1 == U2` and `P1` precedes `P2` in the canonical ordering defined by the following section on [protocol ordering](#protocol-ordering).
86+
87+
### Protocol ordering
88+
89+
Given two protocols `P1` and `P2`, protocol `P1` precedes `P2` in the canonical ordering if:
90+
91+
* `P1` is in a different module than `P2` and the module name of `P1` lexicographically precedes the module name of `P2`, or
92+
* `P1` and `P2` are in the same module and the name of `P1` lexicographically precedes the name of `P2`.
93+
94+
### Canonical constraints
95+
96+
A given constraint can be described in multiple ways. In our running example, the conformance constraint for the element type can be expressed as either `C1.Element: Equatable` or `C2.Element: Equatable`, because `C1.Element` and `C2.Element` name the same type. There might be an infinite number of ways to name the same type (e.g., `C1.SubSequence.SubSequence.Iterator.Element` is also equivalent to `C1.Element`). All of the spellings that refer to the same time comprise the *equivalence class* of that type.
97+
98+
Each equivalence class has a corresponding *anchor*, which is a type parameter that is the least type according to the [type parameter ordering](#type-parameter-ordering). Anchors are used to describe requirements canonically. A concrete type (i.e., a type that is not a type parameter) is canonical when each type parameters within is the anchor of its equivalence class.
99+
100+
A layout or conformance constraint is canonical when its left-hand side is the anchor of its equivalence class. A superclass constraint is canonical when its left-hand side is the anchor of its equivalence class and its right-hand side is a canonical concrete (class) type. Same-type constraint canonicalization is discussed in detail in the following section, but some basic rules apply: the left-hand side is always a type parameter, and the right-hand side is either a type parameter that follows the left-hand side (according to the [type parameter ordering](#type-parameter-ordering)) or is a canonical concrete type.
101+
102+
### Same-type constaints
103+
104+
The canonical form of superclass, layout, and conformance constraints are trivially canonicalized using the anchor of the appropriate equivalence class. Same-type constraints, on the other hand, are responsible for forming those equivalence classes. Let's expand our running example to include a third `Collection`:
105+
106+
```swift
107+
<C1, C2 where C1: Collection, C2: Collection, C3: Collection,
108+
C1.Element: Equatable, C1.Element == C2.Element, C1.Element == C3.Element>
109+
```
110+
111+
All of `C1.Element`, `C2.Element`, and `C3.Element` are in the same equivalence class, which can be formed by different sets of same-type constraints, e.g.,
112+
113+
```swift
114+
C1.Element == C2.Element, C1.Element == C3.Element
115+
```
116+
117+
or
118+
119+
```swift
120+
C1.Element == C2.Element, C2.Element == C3.Element
121+
```
122+
123+
or
124+
125+
```swift
126+
C1.Element == C3.Element, C2.Element == C3.Element
127+
```
128+
129+
All of these sets of constraints have the same effect (i.e., form the same equivalence class), but the second one happens to be the canonical form.
130+
131+
The canonical form is determined by first dividing all of the types into distinct components. Two types `T1` and `T2` are in the same component if the same type constraint `T1 == T2` can be proven true based on other known constraints in the generic signature (i.e., if `T1 == T2` would be redundant). For example, `C1.Element` and `C1.SubSequence.Elemenent` are in the same component, because `C1: Collection` and the `Collection` protocol contains the constraint `Element == SubSequence.Element`. However, `C1.Element` and `C2.Element` are in different components.
132+
133+
Each component has a *local anchor*, which is a type parameter that is the least type within that component, according to the [type parameter ordering](#type-parameter-ordering). The local anchors are then sorted (again, using [type parameter ordering](#type-parameter-ordering)); call the anchors `A1`, `A2`, ..., `An` where `Ai < Aj` for `i < j`. The canonical set of constraints depends on whether the equivalence class has been constrained to a concrete type:
134+
135+
* If there exists a same-type constraint `T == C`, where `T` is a member of the equivalence class and `C` is a concrete type, the set of same-type constraints for the equivalence class is `A1 == C`, `A2 == C`, ..., `An == C`.
136+
137+
* If there is no such same-type constraint, the set of canonical same-type constraints for the equivalence class is `A1 == A2`, `A2 == A3`, ..., `A(n-1) == An`.
138+
139+
The second case is illustrated above; note that it requires `n-1` same-type
140+
constraints to form an equivalence class with `n` separate components.
141+
142+
For the first case, consider a function that operates on `String` collections:
143+
144+
```swift
145+
func manyStrings<C1: Collection, C2: Collection, C3: Collection>(
146+
c1: C1, c2: C2, c3: C3)
147+
where C1.Element == String, C1.Element == C2.Element,
148+
C1.Element == C3.SubSequence.Element
149+
{ }
150+
```
151+
152+
The minimal canonical generic signature for this function is:
153+
154+
```swift
155+
<C1, C2, C3 where C1: Collection, C2: Collection, C3: Collection,
156+
C1.Element == String, C2.Element == String, C3.Element == String>
157+
```
158+
159+
Note that `C1.Element`, `C2.Element`, and `C3.SubSequence.Element` are all
160+
in the same equivalence class, but are in different components. The first two are the local anchors of their respective components, while the local anchor for the third component is `C3.Element`. Because the equivalence class is constrained to a concrete type (`String`), the canonical form includes a same-type constraint making each local anchor equivalent to that concrete type.
161+
162+
163+
164+

0 commit comments

Comments
 (0)