support deeply nested coercions
It would be nice if this code were to compile:
struct Foo {
v: Vec<i32>
}
fn bar(f: &Foo, g: &[i32]) {
let x = match true {
true => {
match f {
&Foo { ref v } => &v,
}
}
false => {
g
}
};
println!("{:?}", x);
}
fn main() { }
At present, it does not. The reason is that we apply a &x[..] coercion to the inner match expression, but the result of the arm is not coerced, and hence has type &&Vec<i32>. This then fails the borrow-check because the borrow of v outlives the arm. If you change the arm to &Foo { ref v } => v or &Foo { ref v } => &v[..], it will compile fine.
The following very similar example using an if does work, or at least did, but somewhat accidentally. Pull request https://github.com/rust-lang/rust/pull/40224 winds up breaking it as part of a refactoring; hopefully nobody will notice. =)
struct Foo {
v: Vec<i32>
}
fn bar(f: &Foo, g: &[i32]) {
let x = if true {
match f {
&Foo { ref v } => &v,
}
} else {
g
};
println!("{:?}", x);
}
fn main() { }
I'll note that the
ifexample only works because match scoping is terrible: the localvis destroyed "at the end of the match expression", but because the match expression is the if's arm - where the coercion occurs - the local is destroyed after the coercion.Coercions getting "between" a match expression and the local's destruction looks rather like a bug to me.
For example, adding a block causes the error to occur again:
struct Foo { v: Vec<i32> } fn bar(c: bool, f: &Foo, g: &[i32]) { let x = if true { { match f { &Foo { ref v } => &v, } } } else { g }; println!("{:?}", x); } fn main() { }Ariel Ben-Yehuda at 2017-03-30 12:09:43
@arielb1 hmm, interesting. Yes that does explain why it was working before (I was a bit surprised that it did). Basically because the coercion was being applied not to block but directly to the tail expression of the block (because of how the if handling was written). This still seems to fit the general rubric of nested coercions, though.
My general idea is that if we plan to coerce the result of some expr E, we might consider examining the expression and seeing whether it makes sense to apply the coercion more deeply -- ie., if
E = { ...; TE }, then we could coerceTEinstead, and so forth. But in general this gets pretty tricky -- e.g., we now supportbreakin blocks. It seems like to do it right we'd need to lift coercions into a more general data structure, as we've often talked about.Another interesting example (which works) is this one, where I removed block from the match arm:
struct Foo { v: Vec<i32> } fn bar(f: &Foo, g: &[i32]) { let x = match true { true => match f { &Foo { ref v } => &v, }, false => g, }; println!("{:?}", x); } fn main() { }Niko Matsakis at 2017-03-30 12:42:17
we now support
breakin blocksWhat do you mean by that?
Ariel Ben-Yehuda at 2017-03-30 13:02:13
@arielb1 I mean that, in HIR (but not in Rust surface syntax), we permit
breakfrom blocks that carry a value. This is how thecatchblock code is implemented.Niko Matsakis at 2017-03-30 13:06:21
This still does not compile. There's been no comments in two years. @nikomatsakis do you think this is worth keeping open? Are we going to be changing any of this any time soon?
Steve Klabnik at 2018-09-24 15:53:55