|
3 | 3 | Coercions are defined in [RFC 401]. [RFC 1558] then expanded on that.
|
4 | 4 | A coercion is implicit and has no syntax.
|
5 | 5 |
|
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 |
| - |
9 | 6 | ## Coercion sites
|
10 | 7 |
|
11 | 8 | A coercion can only occur at certain coercion sites in a program; these are
|
@@ -182,5 +179,109 @@ unsized coercion to `Foo<U>`.
|
182 | 179 | > has been stabilized, the traits themselves are not yet stable and therefore
|
183 | 180 | > can't be used directly in stable Rust.
|
184 | 181 |
|
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 |
186 | 286 | [`CoerceUnsized`]: ../std/ops/trait.CoerceUnsized.html
|
| 287 | +[`Unsize`]: ../std/marker/trait.Unsize.html |
0 commit comments