From ee06263f9227c75a3e0df6997bb0bb203c2efa65 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 8 May 2015 14:48:26 +0200 Subject: [PATCH] Fallout from fixing Issue 25199. There are two interesting kinds of breakage illustrated here: 1. `Box` in many contexts is treated as `Box`, due to [RFC 599]. However, in a type like `&'a Box`, the `Box` type will be expanded to `Box`, again due to [RFC 599]. This, combined with the fix to Issue 25199, leads to a borrowck problem due the combination of this function signature (in src/libstd/net/parser.rs): ```rust fn read_or(&mut self, parsers: &mut [Box Option>]) -> Option; ``` with this call site (again in src/libstd/net/parser.rs): ```rust fn read_ip_addr(&mut self) -> Option { let ipv4_addr = |p: &mut Parser| p.read_ipv4_addr().map(|v4| IpAddr::V4(v4)); let ipv6_addr = |p: &mut Parser| p.read_ipv6_addr().map(|v6| IpAddr::V6(v6)); self.read_or(&mut [Box::new(ipv4_addr), Box::new(ipv6_addr)]) } ``` yielding borrowck errors like: ``` parser.rs:265:27: 265:69 error: borrowed value does not live long enough parser.rs:265 self.read_or(&mut [Box::new(ipv4_addr), Box::new(ipv6_addr)]) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` (full log at: https://gist.github.com/pnkfelix/e2e80f1a71580f5d3103 ) The issue here is perhaps subtle: the `parsers` argument is inferred to be taking a slice of boxed objects with the implicit lifetime bound attached to the `self` parameter to `read_or`. Meanwhile, the fix to Issue 25199 (added in a forth-coming commit) is forcing us to assume that each boxed object may have a destructor that could refer to state of that lifetime, and *therefore* that inferred lifetime is required to outlive the boxed object itself. In this case, the relevant boxed object here is not going to make any such references; I believe it is just an artifact of how the expression was built that it is not assigned type: `Box Option + 'static>`. (i.e., mucking with the expression is probably one way to fix this problem). But the other way to fix it, adopted here, is to change the `read_or` method type to force make the (presumably-intended) `'static` bound explicit on the boxed `FnMut` object. (Note: this is still just the *first* example of breakage.) 2. In `macro_rules.rs`, the `TTMacroExpander` trait defines a method with signature: ```rust fn expand<'cx>(&self, cx: &'cx mut ExtCtxt, ...) -> Box; ``` taking a `&'cx mut ExtCtxt` as an argument and returning a `Box`. The fix to Issue 25199 (added in aforementioned forth-coming commit) assumes that a value of type `Box` may, in its destructor, refer to a reference of lifetime `'cx`; thus the `'cx` lifetime is forced to outlive the returned value. Meanwhile, within `expand.rs`, the old code was doing: ```rust match expander.expand(fld.cx, ...).make_pat() { ... => immutable borrow of fld.cx ... } ``` The problem is that the `'cx` lifetime, inferred for the `expander.expand` call, has now been extended so that it has to outlive the temporary R-value returned by `expanded.expand`. But call is also reborrowing `fld.cx` *mutably*, which means that this reborrow must end before any immutable borrow of `fld.cx`; but there is one of those within the match body. (Note that the temporary R-values for the input expression to `match` all live as long as the whole `match` expression itself (see Issue #3511 and PR #11585). To address this, I moved the construction of the pat value into its own `let`-statement, so that the `Box` will only live for as long as the initializing expression for the `let`-statement, and thus allow the subsequent immutable borrow within the `match`. [RFC 599]: https://github.com/rust-lang/rfcs/blob/master/text/0599-default-object-bound.md --- src/libstd/net/parser.rs | 2 +- src/libsyntax/ext/expand.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libstd/net/parser.rs b/src/libstd/net/parser.rs index 88cfc5a7b2d..69f40d7e7be 100644 --- a/src/libstd/net/parser.rs +++ b/src/libstd/net/parser.rs @@ -61,7 +61,7 @@ impl<'a> Parser<'a> { } // Return result of first successful parser - fn read_or(&mut self, parsers: &mut [Box Option>]) + fn read_or(&mut self, parsers: &mut [Box Option + 'static>]) -> Option { for pf in parsers.iter_mut() { match self.read_atomically(|p: &mut Parser| pf(p)) { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4ea2d4e5c68..c3f1b974815 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -986,9 +986,10 @@ fn expand_pat(p: P, fld: &mut MacroExpander) -> P { let fm = fresh_mark(); let marked_before = mark_tts(&tts[..], fm); let mac_span = fld.cx.original_span(); - let expanded = match expander.expand(fld.cx, - mac_span, - &marked_before[..]).make_pat() { + let pat = expander.expand(fld.cx, + mac_span, + &marked_before[..]).make_pat(); + let expanded = match pat { Some(e) => e, None => { fld.cx.span_err(