Skip to content

Commit afcb386

Browse files
committed
Add description for LUB Coercion
1 parent 30af091 commit afcb386

File tree

1 file changed

+105
-4
lines changed

1 file changed

+105
-4
lines changed

src/type-coercions.md

+105-4
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
Coercions are defined in [RFC 401]. [RFC 1558] then expanded on that.
44
A coercion is implicit and has no syntax.
55

6-
[RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
7-
[RFC 1558]: https://github.com/rust-lang/rfcs/blob/master/text/1558-closure-to-fn-coercion.md
8-
96
## Coercion sites
107

118
A coercion can only occur at certain coercion sites in a program; these are
@@ -182,5 +179,109 @@ unsized coercion to `Foo<U>`.
182179
> has been stabilized, the traits themselves are not yet stable and therefore
183180
> can't be used directly in stable Rust.
184181
185-
[`Unsize`]: ../std/marker/trait.Unsize.html
182+
## LUB Coercion
183+
184+
*Least upper bound coercion* (LUB coercion) is the type of coercion take place
185+
when several expressions of possibly different types need be unified to a
186+
single type. This happens in [match] arms, [if expressions] and [array literal
187+
expressions]. In LUB coercion, the compiler tries to find the least upper
188+
bound of given types. However, Least Upper Bound coercion is not described in
189+
any RFC.
190+
191+
For example:
192+
193+
```rust
194+
# let (a, b, c) = (0, 1, 2);
195+
let foo = if true {
196+
a
197+
} else if false {
198+
b
199+
} else {
200+
c
201+
};
202+
203+
let bar = match 42 {
204+
0 => a,
205+
1 => b,
206+
_ => c,
207+
};
208+
209+
let baz = [a, b, c];
210+
```
211+
In this example, both `foo` and `bar` get the type
212+
`LubCoerce(typeof(a), typeof(a), typeof(c))` and `baz` get the type
213+
`[LubCoerce(typeof(a), typeof(b), typeof(c)); 3]`.
214+
215+
Here is the pseudo code of `LubCoerce` in the rustc:
216+
217+
```txt
218+
Lub(a: Type, b: Type):
219+
if a == (FnDef | Closure)
220+
&& b == (FnDef | Closure)
221+
&& a != capturing Closure
222+
&& b != capturing Closure:
223+
return FnPtr
224+
225+
// Coerce(x, y) returns true when x can be coerced to y
226+
if Coerce(b, a):
227+
return a
228+
if Coerce(a, b):
229+
return b
230+
231+
// LubCoerce failed
232+
emits error
233+
234+
LubCoerce(vars):
235+
result = vars.get(0)
236+
for var in vars[1..]:
237+
result = Lub(result, var)
238+
return result
239+
```
240+
241+
LUB coercion has the following properties:
242+
1. Order independent: e.g. `LubCoerce(ty0, ty1, ty2, ty3)` equals to
243+
`LubCoerce(ty1, ty0, ty4, ty3)`.
244+
2. `LubCoerce(ty0, ty1, ty2, ...)` equals to
245+
`LubCoerce(LubCoerce(ty0, ty1), ty2, ...)`
246+
3. `LubCoerce(ty0, ty1) == Some(ty2)` means both `ty0` and `ty1` can be coerced to
247+
`ty2`.
248+
249+
Notice the feature No.3, it uses the word "means" rather than "if and only if".
250+
That's because currently if `ty0` and `ty1` can be coerced to `ty2` and
251+
unfortunately `ty2` equals to neither `ty0` nor `ty1`, there are only one
252+
special situation where we can get `LubCoerce(ty0, ty1) == Some(ty2)`:
253+
`LubCoerce((FnDef | Closure), (FnDef | Closure)) == Some(FnPtr)` (where Closure
254+
is non-capturing). You can check it with the pseudo code.
255+
256+
It also worth mentioning code below compiles if and only if
257+
`LubCoerce(Ty, typeof(a), typeof(b)).is_some()`:
258+
259+
```rust
260+
# #[derive(Clone, Copy)]
261+
# struct Ty;
262+
# let (a, b) = (Ty, Ty);
263+
let foo: Ty = if true {
264+
a
265+
} else {
266+
b
267+
};
268+
269+
let bar: Ty = match true {
270+
true => a,
271+
false => b,
272+
};
273+
274+
let baz: [Ty; 2] = [a, b];
275+
```
276+
277+
That's because with expected type, the compiler checks
278+
`LubCoerce(expected, ty0, ty1, ty2...).is_some()` rather than
279+
`LubCoerce(ty0, ty1, ty2...) == expected`.
280+
281+
[array literal expressions]: expressions/array-expr.md
282+
[if expressions]: expressions/if-expr.md
283+
[match]: expressions/match-expr.md
284+
[RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md
285+
[RFC 1558]: https://github.com/rust-lang/rfcs/blob/master/text/1558-closure-to-fn-coercion.md
186286
[`CoerceUnsized`]: ../std/ops/trait.CoerceUnsized.html
287+
[`Unsize`]: ../std/marker/trait.Unsize.html

0 commit comments

Comments
 (0)