From fa91980d2d686d3f426a2cae1d0e8fd6825d2d94 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Fri, 19 Aug 2022 06:27:31 -0400 Subject: [PATCH 01/14] Add long description and test for E0311 Adds a long description and unit test for the E0311 compiler error. --- compiler/rustc_error_codes/src/error_codes.rs | 2 +- .../src/error_codes/E0311.md | 49 +++++++++++++++++++ src/test/ui/error-codes/E0311.rs | 18 +++++++ src/test/ui/error-codes/E0311.stderr | 45 +++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0311.md create mode 100644 src/test/ui/error-codes/E0311.rs create mode 100644 src/test/ui/error-codes/E0311.stderr diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 854625579ee7b..1e86d159668ff 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -159,6 +159,7 @@ E0307: include_str!("./error_codes/E0307.md"), E0308: include_str!("./error_codes/E0308.md"), E0309: include_str!("./error_codes/E0309.md"), E0310: include_str!("./error_codes/E0310.md"), +E0311: include_str!("./error_codes/E0311.md"), E0312: include_str!("./error_codes/E0312.md"), E0316: include_str!("./error_codes/E0316.md"), E0317: include_str!("./error_codes/E0317.md"), @@ -568,7 +569,6 @@ E0790: include_str!("./error_codes/E0790.md"), // E0300, // unexpanded macro // E0304, // expected signed integer constant // E0305, // expected constant - E0311, // thing may not live long enough E0313, // lifetime of borrowed pointer outlives lifetime of captured // variable // E0314, // closure outlives stack frame diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md new file mode 100644 index 0000000000000..8b5daaaa17865 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -0,0 +1,49 @@ +E0311 occurs when there is insufficient information for the rust compiler to +prove that some time has a long enough lifetime. + +Erroneous code example: + +```compile_fail, E0311 +use std::borrow::BorrowMut; + +trait NestedBorrowMut { + fn nested_borrow_mut(&mut self) -> &mut V; +} + +impl NestedBorrowMut for T +where + T: BorrowMut, + U: BorrowMut, // missing lifetime specifier here --> compile fail +{ + fn nested_borrow_mut(&mut self) -> &mut V { + self.borrow_mut().borrow_mut() + } +} +``` + +In this example we have a trait that borrows some inner data element of type V +from an outer type T, through an intermediate type U. The compiler is unable to +prove that the livetime of U is long enough to support the reference, so it +throws E0311. To fix the issue we can explicitly add lifetime specifiers to the +trait, which link the lifetimes of the various data types and allow the code +to compile. + +Working implementation of the `NestedBorrowMut` trait: + +``` +use std::borrow::BorrowMut; + +trait NestedBorrowMut<'a, U, V> { + fn nested_borrow_mut(& 'a mut self) -> &'a mut V; +} + +impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T +where + T: BorrowMut, + U: BorrowMut + 'a, +{ + fn nested_borrow_mut(&'a mut self) -> &'a mut V { + self.borrow_mut().borrow_mut() + } +} +``` diff --git a/src/test/ui/error-codes/E0311.rs b/src/test/ui/error-codes/E0311.rs new file mode 100644 index 0000000000000..eb9a473e9a2d1 --- /dev/null +++ b/src/test/ui/error-codes/E0311.rs @@ -0,0 +1,18 @@ +use std::borrow::BorrowMut; + +trait NestedBorrowMut { + fn nested_borrow_mut(&mut self) -> &mut V; +} + +impl NestedBorrowMut for T +where + T: BorrowMut, + U: BorrowMut, // Error is caused by missing lifetime here +{ + fn nested_borrow_mut(&mut self) -> &mut V { + let u_ref = self.borrow_mut(); //~ ERROR E0311 + u_ref.borrow_mut() //~ ERROR E0311 + } +} + +fn main() {} diff --git a/src/test/ui/error-codes/E0311.stderr b/src/test/ui/error-codes/E0311.stderr new file mode 100644 index 0000000000000..a219a6352adc8 --- /dev/null +++ b/src/test/ui/error-codes/E0311.stderr @@ -0,0 +1,45 @@ +error[E0311]: the parameter type `U` may not live long enough + --> $DIR/E0311.rs:13:21 + | +LL | let u_ref = self.borrow_mut(); + | ^^^^^^^^^^^^^^^^^ + | +note: the parameter type `U` must be valid for the anonymous lifetime defined here... + --> $DIR/E0311.rs:12:26 + | +LL | fn nested_borrow_mut(&mut self) -> &mut V { + | ^^^^^^^^^ +note: ...so that the type `U` will meet its required lifetime bounds + --> $DIR/E0311.rs:13:21 + | +LL | let u_ref = self.borrow_mut(); + | ^^^^^^^^^^^^^^^^^ +help: consider adding an explicit lifetime bound... + | +LL | U: BorrowMut + 'a, // Error is caused by missing lifetime here + | ++++ + +error[E0311]: the parameter type `U` may not live long enough + --> $DIR/E0311.rs:14:9 + | +LL | u_ref.borrow_mut() + | ^^^^^^^^^^^^^^^^^^ + | +note: the parameter type `U` must be valid for the anonymous lifetime defined here... + --> $DIR/E0311.rs:12:26 + | +LL | fn nested_borrow_mut(&mut self) -> &mut V { + | ^^^^^^^^^ +note: ...so that the type `U` will meet its required lifetime bounds + --> $DIR/E0311.rs:14:9 + | +LL | u_ref.borrow_mut() + | ^^^^^^^^^^^^^^^^^^ +help: consider adding an explicit lifetime bound... + | +LL | U: BorrowMut + 'a, // Error is caused by missing lifetime here + | ++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0311`. From 08fa70e5c5beb8d84c528d8effb481c4f830b28d Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Fri, 19 Aug 2022 07:06:47 -0400 Subject: [PATCH 02/14] fix updated stderr outputs --- .../lifetimes/missing-lifetimes-in-signature-2.stderr | 1 + .../suggestions/lifetimes/missing-lifetimes-in-signature.stderr | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr index 0212c2d712cb3..5dcbb86cbdf79 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature-2.stderr @@ -27,3 +27,4 @@ LL | fn func(foo: &Foo, t: T) { error: aborting due to previous error +For more information about this error, try `rustc --explain E0311`. diff --git a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr index 0d749f04bea77..bd5b3b5340ab3 100644 --- a/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr +++ b/src/test/ui/suggestions/lifetimes/missing-lifetimes-in-signature.stderr @@ -162,5 +162,5 @@ LL | G: Get + 'a, error: aborting due to 8 previous errors -Some errors have detailed explanations: E0261, E0309, E0621, E0700. +Some errors have detailed explanations: E0261, E0309, E0311, E0621, E0700. For more information about an error, try `rustc --explain E0261`. From 63de1ec0706ddbb8e4d3aacba71ad47308d8dfa9 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Fri, 19 Aug 2022 09:34:20 -0400 Subject: [PATCH 03/14] Apply suggestions from code review Co-authored-by: Guillaume Gomez --- .../src/error_codes/E0311.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index 8b5daaaa17865..a5975c4f472be 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -1,9 +1,9 @@ -E0311 occurs when there is insufficient information for the rust compiler to +This error occurs when there is insufficient information for the rust compiler to prove that some time has a long enough lifetime. Erroneous code example: -```compile_fail, E0311 +```compile_fail,E0311 use std::borrow::BorrowMut; trait NestedBorrowMut { @@ -13,7 +13,7 @@ trait NestedBorrowMut { impl NestedBorrowMut for T where T: BorrowMut, - U: BorrowMut, // missing lifetime specifier here --> compile fail + U: BorrowMut, // error: missing lifetime specifier { fn nested_borrow_mut(&mut self) -> &mut V { self.borrow_mut().borrow_mut() @@ -21,12 +21,11 @@ where } ``` -In this example we have a trait that borrows some inner data element of type V -from an outer type T, through an intermediate type U. The compiler is unable to -prove that the livetime of U is long enough to support the reference, so it -throws E0311. To fix the issue we can explicitly add lifetime specifiers to the -trait, which link the lifetimes of the various data types and allow the code -to compile. +In this example we have a trait that borrows some inner data element of type `V` +from an outer type `T`, through an intermediate type `U`. The compiler is unable to +prove that the livetime of `U` is long enough to support the reference. To fix the +issue we can explicitly add lifetime specifiers to the `NestedBorrowMut` trait, which +link the lifetimes of the various data types and allow the code to compile. Working implementation of the `NestedBorrowMut` trait: @@ -40,7 +39,7 @@ trait NestedBorrowMut<'a, U, V> { impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T where T: BorrowMut, - U: BorrowMut + 'a, + U: BorrowMut + 'a, // Adding lifetime specifier { fn nested_borrow_mut(&'a mut self) -> &'a mut V { self.borrow_mut().borrow_mut() From a9cefd04411611e72417ee2762979e300aac8749 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Fri, 19 Aug 2022 10:53:14 -0400 Subject: [PATCH 04/14] fix line lengths --- compiler/rustc_error_codes/src/error_codes/E0311.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index a5975c4f472be..9477a0b1fb76a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -1,5 +1,5 @@ -This error occurs when there is insufficient information for the rust compiler to -prove that some time has a long enough lifetime. +This error occurs when there is insufficient information for the rust compiler +to prove that some time has a long enough lifetime. Erroneous code example: @@ -22,10 +22,11 @@ where ``` In this example we have a trait that borrows some inner data element of type `V` -from an outer type `T`, through an intermediate type `U`. The compiler is unable to -prove that the livetime of `U` is long enough to support the reference. To fix the -issue we can explicitly add lifetime specifiers to the `NestedBorrowMut` trait, which -link the lifetimes of the various data types and allow the code to compile. +from an outer type `T`, through an intermediate type `U`. The compiler is unable +to prove that the livetime of `U` is long enough to support the reference. To +fix the issue we can explicitly add lifetime specifiers to the `NestedBorrowMut` +trait, which link the lifetimes of the various data types and allow the code to +compile. Working implementation of the `NestedBorrowMut` trait: From dbcc4095560a3ddcbdfe3359641b4da00634dcde Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Tue, 23 Aug 2022 05:19:04 -0400 Subject: [PATCH 05/14] Improve E0311.md description --- .../src/error_codes/E0311.md | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index 9477a0b1fb76a..00b23c420529f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -21,14 +21,35 @@ where } ``` -In this example we have a trait that borrows some inner data element of type `V` -from an outer type `T`, through an intermediate type `U`. The compiler is unable -to prove that the livetime of `U` is long enough to support the reference. To -fix the issue we can explicitly add lifetime specifiers to the `NestedBorrowMut` -trait, which link the lifetimes of the various data types and allow the code to -compile. - -Working implementation of the `NestedBorrowMut` trait: +Why doesn't this code compile? The problem has to do with Rust's rules for +lifetime elision in functions (Chapter 10.3 in the Rust book). One of the +inputs is a reference to `self`, so the compiler attempts to assign the +the same lifetime to the `&mut self` input and `&mut V` output to the +`nested_borrow_mut()` function. The problem is that there is no way for the +compiler to directly figure out how these two lifetimes are related in the +implementation of the function. We're implementing the `NextedBorrowMut` +trait for a type `T`, so the `&mut self` reference has the lifetime of `T`. +We know that `T` implements the `BorrowMut` trait returning a reference to `U`, +and that `U` implements the `BorrowMut` trait returning a reference to `V`. +The key is that we have not told the compiler that those two `U` lifetimes +are the same: for all it knows, we could be that the first `BorrowMut` trait +on `T` works with a lifetime `'a` and the second `BorrowMut` trait on `U` +works on a lifetime `'b`. + +The fix here is to add explicit lifetime annotations that tell the compiler +that the lifetime of the output is in fact the same as the lifetime of the +input (`self`). There are three references involved, to objects of type `T` +(`self`), `U` (the intermediate type), and `V` (the return type). In the +working code below, we see that all have been given the same lifetime `'a`: +- `&'a mut self` in the function argument list for `T` +- `U: BorrowMut + 'a` in the trait bounds for `U` +- `&'a mut V` in the function return for `V`. + +The compiler can the check that the implementation of the +`nested_borrow_mut()` function satisfies these lifetimes. There are two +functions being called inside of `nested_borrow_mut()`, both of which are +the `borrow_mut()` function, which promises that the output lifetime is +the same as the input lifetime (see lifetime elision rules), which checks out. ``` use std::borrow::BorrowMut; From 231e3a041512027b13c87b07fe2a22c89b290adb Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Tue, 23 Aug 2022 05:26:47 -0400 Subject: [PATCH 06/14] actually fix typo this time --- compiler/rustc_error_codes/src/error_codes/E0311.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index 00b23c420529f..e73d5f16f9bf3 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -1,5 +1,5 @@ This error occurs when there is insufficient information for the rust compiler -to prove that some time has a long enough lifetime. +to prove that a type has a long enough lifetime. Erroneous code example: From dd7c48e52937dade956379f3faf7a1bff1b95a5f Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Wed, 24 Aug 2022 20:44:09 -0400 Subject: [PATCH 07/14] Improve description again -- update summary based on review -- rewrite explanation to be more clear and correct --- .../src/error_codes/E0311.md | 73 ++++++++++--------- src/test/ui/error-codes/E0311.rs | 3 +- src/test/ui/error-codes/E0311.stderr | 12 +-- 3 files changed, 47 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index e73d5f16f9bf3..a9d44dcdcee82 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -1,5 +1,5 @@ -This error occurs when there is insufficient information for the rust compiler -to prove that a type has a long enough lifetime. +This error occurs when there is an unsatisfied outlives bound on a generic +type parameter or associated type. Erroneous code example: @@ -13,58 +13,63 @@ trait NestedBorrowMut { impl NestedBorrowMut for T where T: BorrowMut, - U: BorrowMut, // error: missing lifetime specifier + U: BorrowMut, { fn nested_borrow_mut(&mut self) -> &mut V { - self.borrow_mut().borrow_mut() + let u_ref = self.borrow_mut(); + let v_ref = u_ref.borrow_mut(); + v_ref } } ``` -Why doesn't this code compile? The problem has to do with Rust's rules for -lifetime elision in functions (Chapter 10.3 in the Rust book). One of the -inputs is a reference to `self`, so the compiler attempts to assign the -the same lifetime to the `&mut self` input and `&mut V` output to the -`nested_borrow_mut()` function. The problem is that there is no way for the -compiler to directly figure out how these two lifetimes are related in the -implementation of the function. We're implementing the `NextedBorrowMut` -trait for a type `T`, so the `&mut self` reference has the lifetime of `T`. -We know that `T` implements the `BorrowMut` trait returning a reference to `U`, -and that `U` implements the `BorrowMut` trait returning a reference to `V`. -The key is that we have not told the compiler that those two `U` lifetimes -are the same: for all it knows, we could be that the first `BorrowMut` trait -on `T` works with a lifetime `'a` and the second `BorrowMut` trait on `U` -works on a lifetime `'b`. +Why doesn't this code compile? It helps to look at the lifetime bounds that +the compiler is automatically adding ("Lifetime Ellision", Chapter 10.3 in the +Rust book) to the `nested_borrow_mut` and `borrow_mut` functions. In both cases +the input is a reference to `self`, so the compiler attempts to assign the +the same lifetime to the input and output. -The fix here is to add explicit lifetime annotations that tell the compiler -that the lifetime of the output is in fact the same as the lifetime of the -input (`self`). There are three references involved, to objects of type `T` -(`self`), `U` (the intermediate type), and `V` (the return type). In the -working code below, we see that all have been given the same lifetime `'a`: -- `&'a mut self` in the function argument list for `T` -- `U: BorrowMut + 'a` in the trait bounds for `U` -- `&'a mut V` in the function return for `V`. +Looking specifically at `nested_borrow_mut`, +we see that there are three object references to keep track of, +along with their associated lifetimes: +- `self` (which is a `&mut T`) +- `u_ref` (which is a `&mut U`) +- `v_ref` (which is a `&mut V`) -The compiler can the check that the implementation of the -`nested_borrow_mut()` function satisfies these lifetimes. There are two -functions being called inside of `nested_borrow_mut()`, both of which are -the `borrow_mut()` function, which promises that the output lifetime is -the same as the input lifetime (see lifetime elision rules), which checks out. +The `borrow_mut()` method implicitly requires that that the input and output +have the same lifetime bounds. Thus: +```rust + let u_ref = self.borrow_mut(); + let v_ref = u_ref.borrow_mut(); ``` + +Imply that `u_ref` and `self` must share a lifetime bound, and also that +`v_ref` and `u_ref` share a lifetime bound. The problem is that the function +signature for `nested_borrow_mut` only gives the compiler information about the +lifetimes of `self` and `v_ref` -- nothing about `u_ref`. + +The way to fix this error is then to explicitly tell the compiler that the +lifetime of `u_ref` is the same as `self` and `v_ref`, which then allows it +to satisfy the two lifetime bound requirements described above. + +Here is the working version of the code: +```rust use std::borrow::BorrowMut; trait NestedBorrowMut<'a, U, V> { - fn nested_borrow_mut(& 'a mut self) -> &'a mut V; + fn nested_borrow_mut(&'a mut self) -> &'a mut V; } impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T where T: BorrowMut, - U: BorrowMut + 'a, // Adding lifetime specifier + U: BorrowMut + 'a, { fn nested_borrow_mut(&'a mut self) -> &'a mut V { - self.borrow_mut().borrow_mut() + let u_ref = self.borrow_mut(); + let v_ref = u_ref.borrow_mut(); + v_ref } } ``` diff --git a/src/test/ui/error-codes/E0311.rs b/src/test/ui/error-codes/E0311.rs index eb9a473e9a2d1..95f8602306cde 100644 --- a/src/test/ui/error-codes/E0311.rs +++ b/src/test/ui/error-codes/E0311.rs @@ -11,7 +11,8 @@ where { fn nested_borrow_mut(&mut self) -> &mut V { let u_ref = self.borrow_mut(); //~ ERROR E0311 - u_ref.borrow_mut() //~ ERROR E0311 + let v_ref = u_ref.borrow_mut(); //~ ERROR E0311 + v_ref } } diff --git a/src/test/ui/error-codes/E0311.stderr b/src/test/ui/error-codes/E0311.stderr index a219a6352adc8..2cf6404f2f1c0 100644 --- a/src/test/ui/error-codes/E0311.stderr +++ b/src/test/ui/error-codes/E0311.stderr @@ -20,10 +20,10 @@ LL | U: BorrowMut + 'a, // Error is caused by missing lifetime here | ++++ error[E0311]: the parameter type `U` may not live long enough - --> $DIR/E0311.rs:14:9 + --> $DIR/E0311.rs:14:21 | -LL | u_ref.borrow_mut() - | ^^^^^^^^^^^^^^^^^^ +LL | let v_ref = u_ref.borrow_mut(); + | ^^^^^^^^^^^^^^^^^^ | note: the parameter type `U` must be valid for the anonymous lifetime defined here... --> $DIR/E0311.rs:12:26 @@ -31,10 +31,10 @@ note: the parameter type `U` must be valid for the anonymous lifetime defined he LL | fn nested_borrow_mut(&mut self) -> &mut V { | ^^^^^^^^^ note: ...so that the type `U` will meet its required lifetime bounds - --> $DIR/E0311.rs:14:9 + --> $DIR/E0311.rs:14:21 | -LL | u_ref.borrow_mut() - | ^^^^^^^^^^^^^^^^^^ +LL | let v_ref = u_ref.borrow_mut(); + | ^^^^^^^^^^^^^^^^^^ help: consider adding an explicit lifetime bound... | LL | U: BorrowMut + 'a, // Error is caused by missing lifetime here From fc02eee8f6b433c8485086d3e97ae499b53b240b Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Wed, 24 Aug 2022 20:50:02 -0400 Subject: [PATCH 08/14] fix wrapping --- compiler/rustc_error_codes/src/error_codes/E0311.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index a9d44dcdcee82..9521adc252225 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -29,22 +29,21 @@ Rust book) to the `nested_borrow_mut` and `borrow_mut` functions. In both cases the input is a reference to `self`, so the compiler attempts to assign the the same lifetime to the input and output. -Looking specifically at `nested_borrow_mut`, -we see that there are three object references to keep track of, -along with their associated lifetimes: +Looking specifically at `nested_borrow_mut`, we see that there are three object +references to keep track of, along with their associated lifetimes: - `self` (which is a `&mut T`) - `u_ref` (which is a `&mut U`) - `v_ref` (which is a `&mut V`) The `borrow_mut()` method implicitly requires that that the input and output -have the same lifetime bounds. Thus: +have the same lifetime bounds. Thus the lines: ```rust let u_ref = self.borrow_mut(); let v_ref = u_ref.borrow_mut(); ``` -Imply that `u_ref` and `self` must share a lifetime bound, and also that +imply that `u_ref` and `self` must share a lifetime bound, and also that `v_ref` and `u_ref` share a lifetime bound. The problem is that the function signature for `nested_borrow_mut` only gives the compiler information about the lifetimes of `self` and `v_ref` -- nothing about `u_ref`. From 4f194a76c8482ffcd16526a08b1dc489a5a75e96 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Thu, 25 Aug 2022 05:43:59 -0400 Subject: [PATCH 09/14] Fix rust-doc error There was a partial rust code block in the readme that was invalid because of a missing line. I inlined the code snippet into the text to fix the error. This also improves readability a bit. --- .../src/error_codes/E0311.md | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index 9521adc252225..f8c04d54db70a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -25,35 +25,30 @@ where Why doesn't this code compile? It helps to look at the lifetime bounds that the compiler is automatically adding ("Lifetime Ellision", Chapter 10.3 in the -Rust book) to the `nested_borrow_mut` and `borrow_mut` functions. In both cases -the input is a reference to `self`, so the compiler attempts to assign the +Rust book) to the `nested_borrow_mut()` and `borrow_mut()` functions. In both +cases the input is a reference to `self`, so the compiler attempts to assign the same lifetime to the input and output. -Looking specifically at `nested_borrow_mut`, we see that there are three object -references to keep track of, along with their associated lifetimes: +Looking specifically at `nested_borrow_mut()`, we see that there are three +object references to keep track of, along with their associated lifetimes: - `self` (which is a `&mut T`) - `u_ref` (which is a `&mut U`) - `v_ref` (which is a `&mut V`) The `borrow_mut()` method implicitly requires that that the input and output -have the same lifetime bounds. Thus the lines: - -```rust - let u_ref = self.borrow_mut(); - let v_ref = u_ref.borrow_mut(); -``` - -imply that `u_ref` and `self` must share a lifetime bound, and also that -`v_ref` and `u_ref` share a lifetime bound. The problem is that the function -signature for `nested_borrow_mut` only gives the compiler information about the -lifetimes of `self` and `v_ref` -- nothing about `u_ref`. +have the same lifetime bounds. Thus the lines: `let u_ref = self.borrow_mut();` +and `let v_ref = u_ref.borrow_mut();` in `nested_borrow_mut()` above imply that +`u_ref` and `self` must share a lifetime bound, and also that `v_ref` and +`u_ref` share a lifetime bound. The problem is that the function signature for +`nested_borrow_mut()` only gives the compiler information about the lifetimes +of `self` and `v_ref` -- nothing about `u_ref`. The way to fix this error is then to explicitly tell the compiler that the lifetime of `u_ref` is the same as `self` and `v_ref`, which then allows it to satisfy the two lifetime bound requirements described above. Here is the working version of the code: -```rust +``` use std::borrow::BorrowMut; trait NestedBorrowMut<'a, U, V> { From de3e95b56c3c1af339a6c13dc8875599e553aa98 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Fri, 26 Aug 2022 15:01:49 -0400 Subject: [PATCH 10/14] Review updates: simpler MWE and docs - use the simpler minimum working example from the review - add an alterate "fix" that helps make the cause of the error more clear - attempt to add an improved description of what is going on --- .../src/error_codes/E0311.md | 83 ++++++++----------- src/test/ui/error-codes/E0311.rs | 18 +--- src/test/ui/error-codes/E0311.stderr | 51 ++++-------- 3 files changed, 52 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index f8c04d54db70a..b1fa61d365c73 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -1,69 +1,52 @@ -This error occurs when there is an unsatisfied outlives bound on a generic -type parameter or associated type. +This error occurs when there is an unsatisfied outlives bound involving an +elided region on a generic type parameter or associated type. Erroneous code example: ```compile_fail,E0311 -use std::borrow::BorrowMut; - -trait NestedBorrowMut { - fn nested_borrow_mut(&mut self) -> &mut V; +fn no_restriction(x: &()) -> &() { + with_restriction::(x) } -impl NestedBorrowMut for T -where - T: BorrowMut, - U: BorrowMut, -{ - fn nested_borrow_mut(&mut self) -> &mut V { - let u_ref = self.borrow_mut(); - let v_ref = u_ref.borrow_mut(); - v_ref - } +fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () { + x } ``` -Why doesn't this code compile? It helps to look at the lifetime bounds that -the compiler is automatically adding ("Lifetime Ellision", Chapter 10.3 in the -Rust book) to the `nested_borrow_mut()` and `borrow_mut()` functions. In both -cases the input is a reference to `self`, so the compiler attempts to assign -the same lifetime to the input and output. +Why doesn't this code compile? It helps to look at the lifetime bounds that are +automatically adding by the compiler. For more details see the Rust +Documentation for Lifetime Elision: +https://doc.rust-lang.org/reference/lifetime-elision.html] -Looking specifically at `nested_borrow_mut()`, we see that there are three -object references to keep track of, along with their associated lifetimes: -- `self` (which is a `&mut T`) -- `u_ref` (which is a `&mut U`) -- `v_ref` (which is a `&mut V`) +There are two lifetimes being passed into the `no_restriction()` function: one +associated with the generic type `T` parameter and the other with the input +argument `x`. The compiler does not know which of these lifetimes can be +assigned to the output reference, so we get an error. -The `borrow_mut()` method implicitly requires that that the input and output -have the same lifetime bounds. Thus the lines: `let u_ref = self.borrow_mut();` -and `let v_ref = u_ref.borrow_mut();` in `nested_borrow_mut()` above imply that -`u_ref` and `self` must share a lifetime bound, and also that `v_ref` and -`u_ref` share a lifetime bound. The problem is that the function signature for -`nested_borrow_mut()` only gives the compiler information about the lifetimes -of `self` and `v_ref` -- nothing about `u_ref`. +One way to "fix" this code would be to remove the generic type argument `T`. +In this case, the lifetime elision works because there is a single input +lifetime, which is associated with `x`. -The way to fix this error is then to explicitly tell the compiler that the -lifetime of `u_ref` is the same as `self` and `v_ref`, which then allows it -to satisfy the two lifetime bound requirements described above. +``` +fn no_restriction(x: &()) -> &() { + with_restriction(x) +} -Here is the working version of the code: +fn with_restriction<'a>(x: &'a ()) -> &'a () { + x +} ``` -use std::borrow::BorrowMut; -trait NestedBorrowMut<'a, U, V> { - fn nested_borrow_mut(&'a mut self) -> &'a mut V; +The "correct" way to resolve this error is to explicitly tell the compiler +which input lifetime should be assigned to the output. In this case we give +both the generic type `T` parameter and the argument `x` the same lifetime +requirement as the output reference, producing a working version of the code: +``` +fn no_restriction<'a, T: 'a>(x: &'a ()) -> &'a () { + with_restriction::(x) } -impl<'a, T, U, V> NestedBorrowMut<'a, U, V> for T -where - T: BorrowMut, - U: BorrowMut + 'a, -{ - fn nested_borrow_mut(&'a mut self) -> &'a mut V { - let u_ref = self.borrow_mut(); - let v_ref = u_ref.borrow_mut(); - v_ref - } +fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () { + x } ``` diff --git a/src/test/ui/error-codes/E0311.rs b/src/test/ui/error-codes/E0311.rs index 95f8602306cde..566b518b4331d 100644 --- a/src/test/ui/error-codes/E0311.rs +++ b/src/test/ui/error-codes/E0311.rs @@ -1,19 +1,9 @@ -use std::borrow::BorrowMut; - -trait NestedBorrowMut { - fn nested_borrow_mut(&mut self) -> &mut V; +fn no_restriction(x: &()) -> &() { + with_restriction::(x) //~ ERROR E0311 } -impl NestedBorrowMut for T -where - T: BorrowMut, - U: BorrowMut, // Error is caused by missing lifetime here -{ - fn nested_borrow_mut(&mut self) -> &mut V { - let u_ref = self.borrow_mut(); //~ ERROR E0311 - let v_ref = u_ref.borrow_mut(); //~ ERROR E0311 - v_ref - } +fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () { + x } fn main() {} diff --git a/src/test/ui/error-codes/E0311.stderr b/src/test/ui/error-codes/E0311.stderr index 2cf6404f2f1c0..bc0182555af86 100644 --- a/src/test/ui/error-codes/E0311.stderr +++ b/src/test/ui/error-codes/E0311.stderr @@ -1,45 +1,24 @@ -error[E0311]: the parameter type `U` may not live long enough - --> $DIR/E0311.rs:13:21 +error[E0311]: the parameter type `T` may not live long enough + --> $DIR/E0311.rs:2:5 | -LL | let u_ref = self.borrow_mut(); - | ^^^^^^^^^^^^^^^^^ +LL | with_restriction::(x) + | ^^^^^^^^^^^^^^^^^^^^^ | -note: the parameter type `U` must be valid for the anonymous lifetime defined here... - --> $DIR/E0311.rs:12:26 +note: the parameter type `T` must be valid for the anonymous lifetime defined here... + --> $DIR/E0311.rs:1:25 | -LL | fn nested_borrow_mut(&mut self) -> &mut V { - | ^^^^^^^^^ -note: ...so that the type `U` will meet its required lifetime bounds - --> $DIR/E0311.rs:13:21 +LL | fn no_restriction(x: &()) -> &() { + | ^^^ +note: ...so that the type `T` will meet its required lifetime bounds + --> $DIR/E0311.rs:2:5 | -LL | let u_ref = self.borrow_mut(); - | ^^^^^^^^^^^^^^^^^ +LL | with_restriction::(x) + | ^^^^^^^^^^^^^^^^^^^^^ help: consider adding an explicit lifetime bound... | -LL | U: BorrowMut + 'a, // Error is caused by missing lifetime here - | ++++ +LL | fn no_restriction(x: &()) -> &() { + | ++++ -error[E0311]: the parameter type `U` may not live long enough - --> $DIR/E0311.rs:14:21 - | -LL | let v_ref = u_ref.borrow_mut(); - | ^^^^^^^^^^^^^^^^^^ - | -note: the parameter type `U` must be valid for the anonymous lifetime defined here... - --> $DIR/E0311.rs:12:26 - | -LL | fn nested_borrow_mut(&mut self) -> &mut V { - | ^^^^^^^^^ -note: ...so that the type `U` will meet its required lifetime bounds - --> $DIR/E0311.rs:14:21 - | -LL | let v_ref = u_ref.borrow_mut(); - | ^^^^^^^^^^^^^^^^^^ -help: consider adding an explicit lifetime bound... - | -LL | U: BorrowMut + 'a, // Error is caused by missing lifetime here - | ++++ - -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0311`. From deadf071edf4b397523739e41b6006ee278d5341 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Sat, 27 Aug 2022 14:36:17 -0400 Subject: [PATCH 11/14] fix trailing `]` --- compiler/rustc_error_codes/src/error_codes/E0311.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index b1fa61d365c73..bc75a1f7f5e6a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -16,7 +16,7 @@ fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () { Why doesn't this code compile? It helps to look at the lifetime bounds that are automatically adding by the compiler. For more details see the Rust Documentation for Lifetime Elision: -https://doc.rust-lang.org/reference/lifetime-elision.html] +https://doc.rust-lang.org/reference/lifetime-elision.html. There are two lifetimes being passed into the `no_restriction()` function: one associated with the generic type `T` parameter and the other with the input From 4a443dfb8227d407ff3f0542cb6e688833708ba9 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Mon, 29 Aug 2022 06:05:01 -0400 Subject: [PATCH 12/14] review updates to E0311 description --- .../src/error_codes/E0311.md | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index bc75a1f7f5e6a..638c3e0a4373f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -14,33 +14,25 @@ fn with_restriction<'a, T: 'a>(x: &'a ()) -> &'a () { ``` Why doesn't this code compile? It helps to look at the lifetime bounds that are -automatically adding by the compiler. For more details see the Rust -Documentation for Lifetime Elision: -https://doc.rust-lang.org/reference/lifetime-elision.html. +automatically added by the compiler. For more details see the documentation for +[lifetime elision]( https://doc.rust-lang.org/reference/lifetime-elision.html). -There are two lifetimes being passed into the `no_restriction()` function: one -associated with the generic type `T` parameter and the other with the input -argument `x`. The compiler does not know which of these lifetimes can be -assigned to the output reference, so we get an error. +The compiler elides the lifetime of `x` and the return type to some arbitrary +lifetime `'anon` in `no_restriction()`. The only information available to the +compiler is that `'anon` is valid for the duration of the function. When +calling `with_restriction()`, the compiler requires the completely unrelated +type parameter `T` to outlive `'anon` because of the `T: 'a bound` in +`with_restriction()`. This causes an error because `T` is not required to +outlive `'anon` in `no_restriction()`. -One way to "fix" this code would be to remove the generic type argument `T`. -In this case, the lifetime elision works because there is a single input -lifetime, which is associated with `x`. +If `no_restriction()` were to use `&T` instead of `&()` as an argument, the +compiler would have added an implied bound [implied +bound](https://rust-lang.github.io/rfcs/2089-implied-bounds.html), causing this +to compile. -``` -fn no_restriction(x: &()) -> &() { - with_restriction(x) -} - -fn with_restriction<'a>(x: &'a ()) -> &'a () { - x -} -``` +This error can be resolved by explicitly naming the elided lifetime for `x` and +then explicily requiring that the generic parameter `T` outlives that lifetime: -The "correct" way to resolve this error is to explicitly tell the compiler -which input lifetime should be assigned to the output. In this case we give -both the generic type `T` parameter and the argument `x` the same lifetime -requirement as the output reference, producing a working version of the code: ``` fn no_restriction<'a, T: 'a>(x: &'a ()) -> &'a () { with_restriction::(x) From 0d9c01480b9142e9c0aea24500a9461c2bee5a68 Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Mon, 26 Sep 2022 20:50:33 -0400 Subject: [PATCH 13/14] remove implied link bound per review also update .stderr outputs --- compiler/rustc_error_codes/src/error_codes/E0311.md | 4 +--- src/test/ui/error-codes/E0311.stderr | 4 ++-- .../suggest-introducing-and-adding-missing-lifetime.stderr | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index 638c3e0a4373f..768b849817db5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -26,9 +26,7 @@ type parameter `T` to outlive `'anon` because of the `T: 'a bound` in outlive `'anon` in `no_restriction()`. If `no_restriction()` were to use `&T` instead of `&()` as an argument, the -compiler would have added an implied bound [implied -bound](https://rust-lang.github.io/rfcs/2089-implied-bounds.html), causing this -to compile. +compiler would have added an implied bound, causing this to compile. This error can be resolved by explicitly naming the elided lifetime for `x` and then explicily requiring that the generic parameter `T` outlives that lifetime: diff --git a/src/test/ui/error-codes/E0311.stderr b/src/test/ui/error-codes/E0311.stderr index bc0182555af86..9873b5ae6ff13 100644 --- a/src/test/ui/error-codes/E0311.stderr +++ b/src/test/ui/error-codes/E0311.stderr @@ -16,8 +16,8 @@ LL | with_restriction::(x) | ^^^^^^^^^^^^^^^^^^^^^ help: consider adding an explicit lifetime bound... | -LL | fn no_restriction(x: &()) -> &() { - | ++++ +LL | fn no_restriction<'a, T: 'a>(x: &()) -> &() { + | +++ ++++ error: aborting due to previous error diff --git a/src/test/ui/lifetimes/suggest-introducing-and-adding-missing-lifetime.stderr b/src/test/ui/lifetimes/suggest-introducing-and-adding-missing-lifetime.stderr index a8b0996d8b0c7..31fd8a4d633e9 100644 --- a/src/test/ui/lifetimes/suggest-introducing-and-adding-missing-lifetime.stderr +++ b/src/test/ui/lifetimes/suggest-introducing-and-adding-missing-lifetime.stderr @@ -21,3 +21,4 @@ LL | fn no_restriction<'a, T: 'a>(x: &()) -> &() { error: aborting due to previous error +For more information about this error, try `rustc --explain E0311`. From c0d32fd2cc4eafe4f621490e7c44b7af04d1f2be Mon Sep 17 00:00:00 2001 From: Matthew Kelly Date: Tue, 27 Sep 2022 19:23:59 -0400 Subject: [PATCH 14/14] review updates --- compiler/rustc_error_codes/src/error_codes/E0311.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0311.md b/compiler/rustc_error_codes/src/error_codes/E0311.md index 768b849817db5..08159d3f469ac 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0311.md +++ b/compiler/rustc_error_codes/src/error_codes/E0311.md @@ -1,5 +1,5 @@ This error occurs when there is an unsatisfied outlives bound involving an -elided region on a generic type parameter or associated type. +elided region and a generic type parameter or associated type. Erroneous code example: @@ -21,7 +21,7 @@ The compiler elides the lifetime of `x` and the return type to some arbitrary lifetime `'anon` in `no_restriction()`. The only information available to the compiler is that `'anon` is valid for the duration of the function. When calling `with_restriction()`, the compiler requires the completely unrelated -type parameter `T` to outlive `'anon` because of the `T: 'a bound` in +type parameter `T` to outlive `'anon` because of the `T: 'a` bound in `with_restriction()`. This causes an error because `T` is not required to outlive `'anon` in `no_restriction()`.