Change:
I've been playing a bit around and the associated sugar. I found a case where it may cause a segfault. I tried to reproduce the issue in a simplified code and got the following one (also available on ) I guess this code should not compile since on the last line, points to a dropped value.
Hm, this shouldn't compile because is returning a temporary which can't live as long as . Seems odd that it does though! I've confirmed that this compiles on all of stable/beta/nightly. cc perhaps this is a dupe of an existing bug?
It doesn't actually have to do with the Index sugar: It seems like it has to do with copying the lifetime bound from the associated type into the function return type: if you manually substitute in for on line 19, then it doesn't compile.
I would've guessed it's similar to .
triage: P-high
Why does it not compile on nightly in playpen?
Could be , or some other recent fix.
So this seems like it can be closed?
Unfortunately it seems like we didn't have nightly builds from 2015-12-15 through 2015-12-18. I can confirm that this was broken in rust nightly-2015-12-14 and is rejected (correctly) by rust nightly-2015-12-19. I'll do a quick bisect just to double check what fixed it.
Bisection reveals that the fix was due to PR . While there is a test attached to PR , to me the test there (which is a reduction of the code from ) seems different enough from the code here that I'm going to make an independent regression test for the code given here.
Hmm. While writing the test and then "simplifying" it, I actually found a related program that continues to segfault:
(apparently the switch from explicit to implicit return of in the body of is what is causing the regression to sneak in again. I'll investigate.)
what?
There's some really funky interactions going on here with associated types and with lifetime bounds on the struct. For example, this version of refined example is (correctly) rejected by the compiler: while this alternative is incorrectly accepted: but the only different between the two is that in the version that is rejected, we have a maximally parameteric blanket impl: while for the version that is accepted, we have instead a more narrowly focused impl (that happens to be lifetime parameterized):
So far I think that something is funky in the region inference. Looking at the output from as well as what the debug output reports, I see evidence that the region graph is (properly) encoding a constraint of the form: ConstrainRegSubVar(ReFree(CodeExtent(7/CallSiteScope { fnid: 15, bodyid: 32 }), BrNamed(DefId { krate: 0, node: DefIndex(11) =silly::'y }, 'y(76))), 'r)) in other words, is an existentially-bound region that we know must live at least as long as the CallSiteScope for the fn, and we are encoding that this (or if you prefer, "the region variable outlives ") Yet despite the above constraint, the region variable is eventually "solved" and resolves as follows: resolvevar('r) = ReScope(CodeExtent(18/Misc(38))) But this does not make sense; the node id 38 is contained within the function that is being called, so that ReScope cannot possibly be long-lived enough to satisfy the above constraint (right?) cc Update: for some reason, during expansion when we encounter the above quoted ConstrainRegSubVar, it falls into the early check if a relationship is implied by the givens. I'll keep looking; I'd like to know what given is being employed here to deduce that, but such info is not yet included in the instrumentation. BTW, here is the region constraint graph as generated by : ; you can pass that into something like to see the picture here (the immediately relevant part being the region variable) relevant lines from current output (prefixed by their line numbers, for my own future reference)
you might want to wrap some of that up in code fences, it's getting reinterpreted as markdown.
yeah I was debating; the lines are really long so that's why I didn't do code fences at first, since I figured it was better to let the lines wrap. I'll manually wrap the lines and add fences.
Okay I think I found the bug. The actual region-graph representation (as opposed to what the code works to present via graphviz) allocates one node for every region-variable, and then allocates just one more node, at the so-called , to represent all of the concrete (non inference variable) regions. (Presumably the logic here is that all such concrete regions are existentially bound and thus we can abstract them into a single node, in terms of the reasoning that will be employed for the purposes of finding solutions to the constraint sets.) Also, for future code archaeologists: the use of a dummy-idx dates from back when niko first switched the region-inference code to use a standalone graph abstraction, back in (July 2013!). (This does not predate the introduction of explicit lifetime variables, the syntax for which was being discussed on as late as February 2013.) The problem is that other code in the regioninference module assumes that we aren't playing such games. In particular, the method is very simple code, because it is just calling directly out to and looping over the resulting iterator. But that depth-first traversal will then see all the edges that are going both into and out of the single dummy-idx node! So it ends up over-expanding the set to include way_ more assumed relationships than it should.

Discussion:
for _ in 0..num_vars { graph.add_node(()); } <del> let dummy_idx = graph.add_node(()); </del> <ins> // Issue #30438: two distinct dummy nodes, one for incoming // edges (dummy_source) and another for outgoing edges // (dummy_sink). In `dummy -> a -> b -> dummy`, using one // dummy node leads one to think (erroneously) there exists a // path from `b` to `a`. Two dummy nodes sidesteps the issue. let dummy_source = graph.add_node(()); let dummy_sink = graph.add_node(()); </ins> for (constraint, _) in constraints.iter() { match *constraint {

*constraint); } ConstrainRegSubVar(_, b_id) => { <del> graph.add_edge(dummy_idx, NodeIndex(b_id.index as usize), *constraint); </del> <ins> graph.add_edge(dummy_source, NodeIndex(b_id.index as usize), *constraint); </ins> } ConstrainVarSubReg(a_id, _) => { <del> graph.add_edge(NodeIndex(a_id.index as usize), dummy_idx, *constraint); </del> <ins> graph.add_edge(NodeIndex(a_id.index as usize), dummy_sink, *constraint); </ins> } } }

<ins> // Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // Original regression test for Issue #30438. use std::ops::Index; struct Test<'a> { s: &'a String } impl <'a> Index<usize> for Test<'a> { type Output = Test<'a>; fn index(&self, _: usize) -> &Self::Output { return &Test { s: &self.s}; //~^ ERROR: borrowed value does not live long enough } } fn main() { let s = "Hello World".to_string(); let test = Test{s: &s}; let r = &test[0]; println!("{}", test.s); // OK since test is valid println!("{}", r.s); // Segfault since value pointed by r has already been dropped } </ins>

<ins> // Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // Modified regression test for Issue #30438 that exposed an // independent issue (see discussion on ticket). use std::ops::Index; struct Test<'a> { s: &'a String } impl <'a> Index<usize> for Test<'a> { type Output = Test<'a>; fn index(&self, _: usize) -> &Self::Output { &Test { s: &self.s} //~^ ERROR: borrowed value does not live long enough } } fn main() { let s = "Hello World".to_string(); let test = Test{s: &s}; let r = &test[0]; println!("{}", test.s); // OK since test is valid println!("{}", r.s); // Segfault since value pointed by r has already been dropped } </ins>

<ins> // Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // Simplfied regression test for #30438, inspired by arielb1. trait Trait { type Out; } struct Test<'a> { s: &'a str } fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static { let x = Test { s: "this cannot last" }; &x //~^ ERROR: `x` does not live long enough } impl<'b> Trait for Test<'b> { type Out = Test<'b>; } fn main() { let orig = Test { s: "Hello World" }; let r = silly(&orig); println!("{}", orig.s); // OK since `orig` is valid println!("{}", r.s); // Segfault (method does not return a sane value) } </ins>