Closed
Description
When upgrading Rust in Servo, many usages of RefCell::borrow
that were previously fine now cause borrow-check errors like this:
components/script/dom/xmlhttprequest.rs:678:9: 678:13 error: `self` does not live long enough
components/script/dom/xmlhttprequest.rs:678 self.status_text.borrow().clone()
^~~~
components/script/dom/xmlhttprequest.rs:677:39: 679:6 note: reference must be valid for the destruction scope surrounding block at 677:38...
components/script/dom/xmlhttprequest.rs:677 fn StatusText(self) -> ByteString {
components/script/dom/xmlhttprequest.rs:678 self.status_text.borrow().clone()
components/script/dom/xmlhttprequest.rs:679 }
components/script/dom/xmlhttprequest.rs:677:39: 679:6 note: ...but borrowed value is only valid for the block at 677:38
components/script/dom/xmlhttprequest.rs:677 fn StatusText(self) -> ByteString {
components/script/dom/xmlhttprequest.rs:678 self.status_text.borrow().clone()
components/script/dom/xmlhttprequest.rs:679 }
This error message makes no sense to me, the two blocks it talks about are the same.
This can be worked around by binding the result of .borrow()
with let
before using it, enough though such a change looks like a no-op:
fn StatusText(self) -> ByteString {
- self.status_text.borrow().clone()
+ let status_text = self.status_text.borrow();
+ status_text.clone()
}
Activity
alexcrichton commentedon Mar 13, 2015
cc @pnkfelix
larsbergstrom commentedon Mar 13, 2015
@SimonSapin has also mentioned that there are some cases (e.g., in our "script" crate) where even this transformation does not work.
cc @nikomatsakis
SimonSapin commentedon Mar 13, 2015
I got stuck at some point but @jdm managed to find work arounds servo/servo@0c12dd9
pnkfelix commentedon Mar 17, 2015
This may be a consequence of the new scoping rules introduced in #21657. We may attempt to address coding patterns like this by implementing #22323; but implementing that is not a 1.0-blocking issue (i.e. it may wait until after 1.0 is released, depending on other priorities).
pnkfelix commentedon Mar 17, 2015
cc #22321
nikomatsakis commentedon Mar 17, 2015
Standalone test case:
pnkfelix commentedon Mar 19, 2015
leaving nominated tag to talk again next week, assigning to self to investigate.
pnkfelix commentedon Apr 2, 2015
Another work-around worth noting (if only because it provides a hint at the problem here):
I think this is an artifact of our temporary r-value rules, in particular the one that says that all temporaries for the tail expression in a block are assigned the lifetime of the parent of the block (that is, they can outlive the block), and thus the
Ref
returned byx.borrow()
is assigned a lifetime that is too long.pnkfelix commentedon Apr 2, 2015
(Having said that, I would like to see if we can still fix this in some way. But I no longer see this as a potential smoking gun pointing at some fundamental flaw in the system. So I've removed the I-nominated tag.)
More details for those interested: the thing that comes up is this:
borrow
has this signature:fn borrow<'a>(&'a self) -> Ref<'a>
, and theRef
has a destructor.dropck
wants the'a
to outlive the parent of the scope of the returnedRef
.Ref
, due to our tail expression rule for expression-blocks, is assigned the lifetime of the parent of the block.'a
in the call toborrow
ends up being assigned the lifetime of the destruction scope surrounding the function body.x
does not live that long.We could hack in special support for this case (i.e. some hack in terms of how
fn
bodies are treated).(but maybe that is acceptable...)
pnkfelix commentedon Apr 3, 2015
Actually, according to the current dynamic semantics, fn parameters already are torn down after the fn body is torn down.
So it should be entirely sound (perhaps even a legitimate bug fix, not sure) to make the code-extents reflect the fact that the temporaries from the tail expression for the fn-body are destroyed before the parameters are dropped (i.e., that the parameters strictly outlive all of the r-value temporaries of the fn body).
Here is some demo code illustrating this (note also that the behavior differs if one passes
--cfg nested
, but in either case the parameters still outlive the r-value temporaries).(The reason that its interesting that the behavior differs with and without
cfg nested
is that it even differs if you comment out thelet _b2 = ...;
line at the top ... i.e. the program transformation:to
is not semantics preserving; it changes the time at which the temporaries within
expr
are dropped with respect to the let-bindings withinstmts ...
)pnkfelix commentedon Apr 3, 2015
Okay, I'm pretty happy with the solution given in #24021.
Auto merge of #24021 - pnkfelix:fn-params-outlive-body, r=nikomatsakis
agrover commentedon Oct 6, 2016
@pnkfelix this still is present w.r.t expression blocks, though, which are pretty useful when attempting to control the length of borrows. Using workaround in #23338 (comment) for now but it would be great for this to work nicely at some point, it led me to think I was going crazy 😄