From b7feb9f7423874b0a9aeb83f926b00352a576e37 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Nov 2018 12:35:38 -0500 Subject: [PATCH 1/6] introduce the idea of "type must be valid for scope" Unlike "type must outlive" `T: r`, this relation is between a type and a **program point** `P`. It will eventually be strengthened so that `T valid for P` implies `T: P` but not the reverse (presently, they are equivalent). --- src/librustc_typeck/check/dropck.rs | 6 +- src/librustc_typeck/check/regionck.rs | 94 ++++++++++++++++----------- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index b8544177bbbbe..7201e26edc9f6 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -314,7 +314,7 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( // which cannot be outlived. None => return Ok(()), }; - let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope)); + let parent_scope_region = rcx.tcx.mk_region(ty::ReScope(parent_scope)); let origin = || infer::SubregionOrigin::SafeDestructor(span); let cause = &ObligationCause::misc(span, body_id); let infer_ok = rcx.infcx.at(cause, rcx.fcx.param_env).dropck_outlives(ty); @@ -322,8 +322,8 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( let kinds = rcx.fcx.register_infer_ok_obligations(infer_ok); for kind in kinds { match kind.unpack() { - UnpackedKind::Lifetime(r) => rcx.sub_regions(origin(), parent_scope, r), - UnpackedKind::Type(ty) => rcx.type_must_outlive(origin(), ty, parent_scope), + UnpackedKind::Lifetime(r) => rcx.sub_regions(origin(), parent_scope_region, r), + UnpackedKind::Type(ty) => rcx.type_must_be_valid_for_scope(origin(), ty, parent_scope), } } Ok(()) diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 212ee2698e012..375242e73b46b 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -373,10 +373,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { body.id(), call_site_scope ); - let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope)); let body_hir_id = self.tcx.hir.node_to_hir_id(body_id.node_id); - self.type_of_node_must_outlive(infer::CallReturn(span), body_hir_id, call_site_region); + self.type_of_node_must_be_valid_for_scope(infer::CallReturn(span), body_hir_id, call_site_scope); self.constrain_opaque_types( &self.fcx.opaque_types.borrow(), @@ -434,10 +433,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // that the lifetime of any regions that appear in a // variable's type enclose at least the variable's scope. let var_scope = self.region_scope_tree.var_scope(hir_id.local_id); - let var_region = self.tcx.mk_region(ty::ReScope(var_scope)); let origin = infer::BindingTypeIsNotValidAtDecl(span); - self.type_of_node_must_outlive(origin, hir_id, var_region); + self.type_of_node_must_be_valid_for_scope(origin, hir_id, var_scope); let typ = self.resolve_node_type(hir_id); let body_id = self.body_id; @@ -520,14 +518,15 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { // scope of that expression. This also guarantees basic WF. let expr_ty = self.resolve_node_type(expr.hir_id); // the region corresponding to this expression - let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope { + let expr_scope = region::Scope { id: expr.hir_id.local_id, data: region::ScopeData::Node, - })); - self.type_must_outlive( + }; + let expr_region = self.tcx.mk_region(ty::ReScope(expr_scope)); + self.type_must_be_valid_for_scope( infer::ExprTypeIsNotInScope(expr_ty, expr.span), expr_ty, - expr_region, + expr_scope, ); let is_method_call = self.tables.borrow().is_method_call(expr); @@ -546,7 +545,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { }; let substs = self.tables.borrow().node_substs(expr.hir_id); - self.substs_wf_in_scope(origin, substs, expr.span, expr_region); + self.substs_wf_in_scope(origin, substs, expr.span, expr_scope); // Arguments (sub-expressions) are checked via `constrain_call`, below. } @@ -572,7 +571,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { hir::ExprKind::Path(_) => { let substs = self.tables.borrow().node_substs(expr.hir_id); let origin = infer::ParameterOrigin::Path; - self.substs_wf_in_scope(origin, substs, expr.span, expr_region); + self.substs_wf_in_scope(origin, substs, expr.span, expr_scope); } hir::ExprKind::Call(ref callee, ref args) => { @@ -619,7 +618,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { let lhs_ty = self.resolve_expr_type_adjusted(&lhs); let rhs_ty = self.resolve_expr_type_adjusted(&rhs); for &ty in &[lhs_ty, rhs_ty] { - self.type_must_outlive(infer::Operand(expr.span), ty, expr_region); + self.type_must_be_valid_for_scope(infer::Operand(expr.span), ty, expr_scope); } intravisit::walk_expr(self, expr); } @@ -674,7 +673,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { // FIXME(https://github.com/rust-lang/rfcs/issues/811) // nested method calls requires that this rule change let ty0 = self.resolve_node_type(expr.hir_id); - self.type_must_outlive(infer::AddrOf(expr.span), ty0, expr_region); + self.type_must_be_valid_for_scope(infer::AddrOf(expr.span), ty0, expr_scope); intravisit::walk_expr(self, expr); } @@ -705,16 +704,15 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { } hir::ExprKind::Ret(Some(ref ret_expr)) => { - let call_site_scope = self.call_site_scope; + let call_site_scope = self.call_site_scope.unwrap(); debug!( "visit_expr ExprKind::Ret ret_expr.id {} call_site_scope: {:?}", ret_expr.id, call_site_scope ); - let call_site_region = self.tcx.mk_region(ty::ReScope(call_site_scope.unwrap())); - self.type_of_node_must_outlive( + self.type_of_node_must_be_valid_for_scope( infer::CallReturn(ret_expr.span), ret_expr.hir_id, - call_site_region, + call_site_scope, ); intravisit::walk_expr(self, expr); } @@ -813,26 +811,25 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { id: call_expr.hir_id.local_id, data: region::ScopeData::Node, }; - let callee_region = self.tcx.mk_region(ty::ReScope(callee_scope)); - debug!("callee_region={:?}", callee_region); + debug!("callee_scope={:?}", callee_scope); for arg_expr in arg_exprs { debug!("Argument: {:?}", arg_expr); // ensure that any regions appearing in the argument type are // valid for at least the lifetime of the function: - self.type_of_node_must_outlive( + self.type_of_node_must_be_valid_for_scope( infer::CallArg(arg_expr.span), arg_expr.hir_id, - callee_region, + callee_scope, ); } // as loop above, but for receiver if let Some(r) = receiver { debug!("receiver: {:?}", r); - self.type_of_node_must_outlive(infer::CallRcvr(r.span), r.hir_id, callee_region); + self.type_of_node_must_be_valid_for_scope(infer::CallRcvr(r.span), r.hir_id, callee_scope); } } @@ -867,10 +864,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // expression. self.check_safety_of_rvalue_destructor_if_necessary(&cmt, expr.span); - let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope { + let expr_scope = region::Scope { id: expr.hir_id.local_id, data: region::ScopeData::Node, - })); + }; + let expr_region = self.tcx.mk_region(ty::ReScope(expr_scope)); for adjustment in adjustments { debug!( "constrain_adjustments: adjustment={:?}, cmt={:?}", @@ -905,8 +903,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ); // Specialized version of constrain_call. - self.type_must_outlive(infer::CallRcvr(expr.span), input, expr_region); - self.type_must_outlive(infer::CallReturn(expr.span), output, expr_region); + self.type_must_be_valid_for_scope(infer::CallRcvr(expr.span), input, expr_scope); + self.type_must_be_valid_for_scope(infer::CallReturn(expr.span), output, expr_scope); } if let adjustment::Adjust::Borrow(ref autoref) = adjustment.kind { @@ -916,10 +914,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // the current node. // // FIXME(#6268) remove to support nested method calls - self.type_of_node_must_outlive( + self.type_of_node_must_be_valid_for_scope( infer::AutoBorrow(expr.span), expr.hir_id, - expr_region, + expr_scope, ); } @@ -999,13 +997,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } } - /// Guarantees that any lifetimes which appear in the type of the node `id` (after applying - /// adjustments) are valid for at least `minimum_lifetime` - fn type_of_node_must_outlive( + /// Requires that all regions in the (fully adjusted) type of the + /// node `hir_id` are **valid** during the scope. This means that + /// they have to outlive `scope`. This implies `T: scope` but is + /// actually stronger than that in the case of projections. This + /// helps overcome some weakenesses in our inference (see #55756). + fn type_of_node_must_be_valid_for_scope( &mut self, origin: infer::SubregionOrigin<'tcx>, hir_id: hir::HirId, - minimum_lifetime: ty::Region<'tcx>, + scope: region::Scope, ) { // Try to resolve the type. If we encounter an error, then typeck // is going to fail anyway, so just stop here and let typeck @@ -1021,10 +1022,26 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let ty = self.resolve_type(ty); debug!( "constrain_regions_in_type_of_node(\ - ty={}, ty0={}, id={:?}, minimum_lifetime={:?})", - ty, ty0, hir_id, minimum_lifetime + ty={}, ty0={}, id={:?}, scope={:?})", + ty, ty0, hir_id, scope ); - self.type_must_outlive(origin, ty, minimum_lifetime); + + self.type_must_be_valid_for_scope(origin, ty, scope) + } + + /// Requires that all regions in the type `T` are **valid** during + /// the scope. This means that they have to outlive `scope`. This + /// implies `T: scope` but is actually stronger than that in the + /// case of projections. This helps overcome some weakenesses in + /// our inference (see #55756). + pub fn type_must_be_valid_for_scope( + &self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + scope: region::Scope, + ) { + let region = self.tcx.mk_region(ty::ReScope(scope)); + self.type_must_outlive(origin, ty, region) } /// Adds constraints to inference such that `T: 'a` holds (or @@ -1408,25 +1425,26 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { origin: infer::ParameterOrigin, substs: &Substs<'tcx>, expr_span: Span, - expr_region: ty::Region<'tcx>, + expr_scope: region::Scope, ) { debug!( "substs_wf_in_scope(substs={:?}, \ - expr_region={:?}, \ + expr_scope={:?}, \ origin={:?}, \ expr_span={:?})", - substs, expr_region, origin, expr_span + substs, expr_scope, origin, expr_span ); let origin = infer::ParameterInScope(origin, expr_span); + let expr_region = self.tcx.mk_region(ty::ReScope(expr_scope)); for region in substs.regions() { self.sub_regions(origin.clone(), expr_region, region); } for ty in substs.types() { let ty = self.resolve_type(ty); - self.type_must_outlive(origin.clone(), ty, expr_region); + self.type_must_be_valid_for_scope(origin.clone(), ty, expr_scope); } } } From 4b5995e3fc7cdaa952b421fe31dd69280f5447cc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 15 Nov 2018 13:58:41 -0500 Subject: [PATCH 2/6] make `type_must_be_valid_for_scope` require each region to outlive S This is compatible with the liveness rules for NLL; it is also stricter than the old requirement. It gives enough guidance to get inference passing. --- src/librustc_typeck/check/regionck.rs | 9 +++-- src/test/ui/issues/issue-55756.rs | 47 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/issues/issue-55756.rs diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 375242e73b46b..a0e4ad9131f21 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -1034,14 +1034,19 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// implies `T: scope` but is actually stronger than that in the /// case of projections. This helps overcome some weakenesses in /// our inference (see #55756). + /// + /// (This is the lexical equivalent of NLL's "liveness rules", + /// which require that any region which appears at the point P + /// must contain the point P.) pub fn type_must_be_valid_for_scope( &self, origin: infer::SubregionOrigin<'tcx>, ty: Ty<'tcx>, scope: region::Scope, ) { - let region = self.tcx.mk_region(ty::ReScope(scope)); - self.type_must_outlive(origin, ty, region) + let scope_region = self.tcx.mk_region(ty::ReScope(scope)); + let ty = self.resolve_type_vars_if_possible(&ty); + self.tcx.for_each_free_region(&ty, |ty_region| self.sub_regions(origin.clone(), scope_region, ty_region)); } /// Adds constraints to inference such that `T: 'a` holds (or diff --git a/src/test/ui/issues/issue-55756.rs b/src/test/ui/issues/issue-55756.rs new file mode 100644 index 0000000000000..4ed533bd4c107 --- /dev/null +++ b/src/test/ui/issues/issue-55756.rs @@ -0,0 +1,47 @@ +// Regression test for #55756. +// +// In this test, the result of `self.callee` is a projection `>::Guard`. As it may contain a destructor, the dropck +// rules require that this type outlivess the scope of `state`. Unfortunately, +// our region inference is not smart enough to figure out how to +// translate a requirement like +// +// >::guard: 'r +// +// into a requirement that `'0: 'r` -- in particular, it fails to do +// so because it *also* knows that `>::Guard: 'a` +// from the trait definition. Faced with so many choices, the current +// solver opts to do nothing. +// +// The problem we were having was that we would observe two +// potentially applicable rules when trying to find bounds for `>::Guard`: +// +// ``` +// >::Guard: 'a // from the where clauses +// for<'b> { >::Guard: 'b } // from the trait definition +// ``` +// +// Because of this, we decided to take no action to influence +// inference, which means that `'0` winds up unconstrained, leading to +// the ultimate error. +// +// compile-pass + +#![crate_type="lib"] + +pub trait Database<'a> { + type Guard: 'a; +} + +pub struct Stateful<'a, D: 'a>(&'a D); + +impl<'b, D: for <'a> Database<'a>> Stateful<'b, D> { + pub fn callee<'a>(&'a self) -> >::Guard { + unimplemented!() + } + pub fn caller<'a>(&'a self) -> >::Guard { + let state = self.callee(); + self.callee() + } +} From 925a081b7c738fe323cd7d40f143ec451b6cb870 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 16 Nov 2018 09:39:05 -0500 Subject: [PATCH 3/6] pacify the mercilous tidy --- src/librustc_typeck/check/regionck.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index a0e4ad9131f21..fdd7dabc89902 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -375,7 +375,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ); let body_hir_id = self.tcx.hir.node_to_hir_id(body_id.node_id); - self.type_of_node_must_be_valid_for_scope(infer::CallReturn(span), body_hir_id, call_site_scope); + self.type_of_node_must_be_valid_for_scope( + infer::CallReturn(span), + body_hir_id, + call_site_scope, + ); self.constrain_opaque_types( &self.fcx.opaque_types.borrow(), @@ -829,7 +833,11 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // as loop above, but for receiver if let Some(r) = receiver { debug!("receiver: {:?}", r); - self.type_of_node_must_be_valid_for_scope(infer::CallRcvr(r.span), r.hir_id, callee_scope); + self.type_of_node_must_be_valid_for_scope( + infer::CallRcvr(r.span), + r.hir_id, + callee_scope, + ); } } @@ -1046,7 +1054,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ) { let scope_region = self.tcx.mk_region(ty::ReScope(scope)); let ty = self.resolve_type_vars_if_possible(&ty); - self.tcx.for_each_free_region(&ty, |ty_region| self.sub_regions(origin.clone(), scope_region, ty_region)); + self.tcx.for_each_free_region( + &ty, + |ty_region| self.sub_regions(origin.clone(), scope_region, ty_region), + ); } /// Adds constraints to inference such that `T: 'a` holds (or From 2888a98e93b85196485bf6ab8ef22dfe2ad18e77 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 16 Nov 2018 13:33:18 -0500 Subject: [PATCH 4/6] update tests Summary: - A few tests that used to compile, but which were highly questionable, now fail to compile. These are tests that also fail under NLL -- they have to do with closures that are returning invalid values (e.g., other closures that contain values that would be out of scope if used), but our regionck rules permitted them for some reason. - One test for #55608 now compiles successfully -- looking at it, I can't I see any reason that it should *not* compile. - Some errors messages changed in small ways, in some cases improving quite a bit, but often just "different". --- .../borrowck-describe-lvalue.ast.stderr | 31 ++++++++++++++--- .../borrowck-describe-lvalue.mir.stderr | 8 ++--- .../ui/borrowck/borrowck-describe-lvalue.rs | 1 + .../issue-55608-captures-empty-region.rs | 30 +++++++++++----- .../issue-55608-captures-empty-region.stderr | 13 +++---- src/test/ui/issues/issue-40510-1.rs | 6 ++-- src/test/ui/issues/issue-40510-1.stderr | 34 +++++++++++++++++++ src/test/ui/issues/issue-40510-3.rs | 6 ++-- src/test/ui/issues/issue-40510-3.stderr | 24 +++++++++++++ src/test/ui/issues/issue-49824.rs | 5 +-- src/test/ui/issues/issue-49824.stderr | 30 ++++++++++------ ...n-borrow-params-issue-29793-big.ast.stderr | 14 ++++---- ...ion-borrow-params-issue-29793-small.stderr | 28 ++++++++------- .../regions-escape-via-trait-or-not.stderr | 10 +++--- ...regions-return-ref-to-upvar-issue-17403.rs | 2 +- ...ons-return-ref-to-upvar-issue-17403.stderr | 31 ++++------------- 16 files changed, 184 insertions(+), 89 deletions(-) create mode 100644 src/test/ui/issues/issue-40510-1.stderr create mode 100644 src/test/ui/issues/issue-40510-3.stderr diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr index bc6385ffd920b..dcc7c724c4a8e 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr @@ -219,8 +219,31 @@ LL | &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than LL | }; | - first borrow ends here +error[E0598]: lifetime of `x` is too short to guarantee its contents can be safely reborrowed + --> $DIR/borrowck-describe-lvalue.rs:305:16 + | +LL | || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body + | ^^ + | +note: `x` would have to be valid for the expression at 304:12... + --> $DIR/borrowck-describe-lvalue.rs:304:12 + | +LL | / || { +LL | | || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body +LL | | //[ast]~^ ERROR lifetime of `x` is too short +LL | | let y = &mut x; +... | +LL | | } +LL | | }; + | |____________^ +note: ...but `x` is only valid for the lifetime as defined on the body at 304:12 + --> $DIR/borrowck-describe-lvalue.rs:304:12 + | +LL | || { + | ^^ + error[E0499]: cannot borrow `**x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:307:25 + --> $DIR/borrowck-describe-lvalue.rs:308:25 | LL | let y = &mut x; | - first mutable borrow occurs here @@ -231,7 +254,7 @@ LL | } | - first borrow ends here error[E0382]: use of moved value: `x` - --> $DIR/borrowck-describe-lvalue.rs:318:22 + --> $DIR/borrowck-describe-lvalue.rs:319:22 | LL | drop(x); | - value moved here @@ -240,7 +263,7 @@ LL | drop(x); //[ast]~ ERROR use of moved value: `x` | = note: move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait -error: aborting due to 26 previous errors +error: aborting due to 27 previous errors -Some errors occurred: E0382, E0499, E0502, E0503. +Some errors occurred: E0382, E0499, E0502, E0503, E0598. For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr index 5721c52ba2172..89a63b8cbd7c4 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr @@ -10,7 +10,7 @@ LL | *y = 1; | ------ first borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:307:20 + --> $DIR/borrowck-describe-lvalue.rs:308:20 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -26,10 +26,10 @@ error: captured variable cannot escape `FnMut` closure body LL | || { | - inferred to be a `FnMut` closure LL | / || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body +LL | | //[ast]~^ ERROR lifetime of `x` is too short LL | | let y = &mut x; LL | | &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than once at a time -LL | | //[mir]~^ ERROR cannot borrow `x` as mutable more than once at a time -LL | | *y = 1; +... | LL | | drop(y); LL | | } | |_________________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body @@ -362,7 +362,7 @@ LL | drop(x); | - mutable borrow later used here error[E0382]: use of moved value: `x` - --> $DIR/borrowck-describe-lvalue.rs:318:22 + --> $DIR/borrowck-describe-lvalue.rs:319:22 | LL | drop(x); | - value moved here diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.rs b/src/test/ui/borrowck/borrowck-describe-lvalue.rs index 649de888ab0a2..5dce6f68560fc 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.rs +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.rs @@ -303,6 +303,7 @@ fn main() { let mut x = 0; || { || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body + //[ast]~^ ERROR lifetime of `x` is too short let y = &mut x; &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than once at a time //[mir]~^ ERROR cannot borrow `x` as mutable more than once at a time diff --git a/src/test/ui/impl-trait/issue-55608-captures-empty-region.rs b/src/test/ui/impl-trait/issue-55608-captures-empty-region.rs index 7ebc348996f5e..891696a4b6210 100644 --- a/src/test/ui/impl-trait/issue-55608-captures-empty-region.rs +++ b/src/test/ui/impl-trait/issue-55608-captures-empty-region.rs @@ -1,22 +1,36 @@ // This used to ICE because it creates an `impl Trait` that captures a // hidden empty region. +// +// compile-pass #![feature(conservative_impl_trait)] -fn server() -> impl FilterBase2 { //~ ERROR [E0700] - segment2(|| { loop { } }).map2(|| "") +fn server() -> impl FilterBase2 { + segment2(|| loop {}).map2(|| "") } trait FilterBase2 { - fn map2(self, _fn: F) -> Map2 where Self: Sized { loop { } } + fn map2(self, _fn: F) -> Map2 + where + Self: Sized, + { + loop {} + } } -struct Map2 { _func: F } +struct Map2 { + _func: F, +} -impl FilterBase2 for Map2 { } +impl FilterBase2 for Map2 {} -fn segment2(_fn: F) -> Map2 where F: Fn() -> Result<(), ()> { - loop { } +fn segment2(_fn: F) -> Map2 +where + F: Fn() -> Result<(), ()>, +{ + loop {} } -fn main() { server(); } +fn main() { + server(); +} diff --git a/src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr b/src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr index d1f147834d2ef..9b8a697242465 100644 --- a/src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr +++ b/src/test/ui/impl-trait/issue-55608-captures-empty-region.stderr @@ -1,11 +1,8 @@ -error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/issue-55608-captures-empty-region.rs:6:16 +warning: the feature `conservative_impl_trait` has been stable since 1.26.0 and no longer requires an attribute to enable + --> $DIR/issue-55608-captures-empty-region.rs:6:12 | -LL | fn server() -> impl FilterBase2 { //~ ERROR [E0700] - | ^^^^^^^^^^^^^^^^ +LL | #![feature(conservative_impl_trait)] + | ^^^^^^^^^^^^^^^^^^^^^^^ | - = note: hidden type `Map2<[closure@$DIR/issue-55608-captures-empty-region.rs:7:36: 7:41]>` captures an empty lifetime + = note: #[warn(stable_features)] on by default -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/issues/issue-40510-1.rs b/src/test/ui/issues/issue-40510-1.rs index b053f8e7d807a..04e87a03ef80c 100644 --- a/src/test/ui/issues/issue-40510-1.rs +++ b/src/test/ui/issues/issue-40510-1.rs @@ -8,14 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-pass +// (This regression test *used* to pass, but started to fail once +// #55756 was fixed.) + #![allow(unused)] fn f() { let mut x: Box<()> = Box::new(()); || { - &mut x + &mut x //~ ERROR cannot infer }; } diff --git a/src/test/ui/issues/issue-40510-1.stderr b/src/test/ui/issues/issue-40510-1.stderr new file mode 100644 index 0000000000000..68bd9f999b659 --- /dev/null +++ b/src/test/ui/issues/issue-40510-1.stderr @@ -0,0 +1,34 @@ +error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements + --> $DIR/issue-40510-1.rs:20:9 + | +LL | &mut x //~ ERROR cannot infer + | ^^^^^^ + | +note: first, the lifetime cannot outlive the lifetime as defined on the body at 19:5... + --> $DIR/issue-40510-1.rs:19:5 + | +LL | || { + | ^^ +note: ...so that closure can access `x` + --> $DIR/issue-40510-1.rs:20:9 + | +LL | &mut x //~ ERROR cannot infer + | ^^^^^^ +note: but, the lifetime must be valid for the expression at 19:5... + --> $DIR/issue-40510-1.rs:19:5 + | +LL | / || { +LL | | &mut x //~ ERROR cannot infer +LL | | }; + | |_____^ +note: ...so type `[closure@$DIR/issue-40510-1.rs:19:5: 21:6 x:&mut std::boxed::Box<()>]` of expression is valid during the expression + --> $DIR/issue-40510-1.rs:19:5 + | +LL | / || { +LL | | &mut x //~ ERROR cannot infer +LL | | }; + | |_____^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/issues/issue-40510-3.rs b/src/test/ui/issues/issue-40510-3.rs index d4d1d28a1376a..26f89f0c9eeef 100644 --- a/src/test/ui/issues/issue-40510-3.rs +++ b/src/test/ui/issues/issue-40510-3.rs @@ -8,14 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-pass +// (This regression test *used* to pass, but started to fail once +// #55756 was fixed.) + #![allow(unused)] fn f() { let mut x: Vec<()> = Vec::new(); || { - || { + || { //~ ERROR too short x.push(()) } }; diff --git a/src/test/ui/issues/issue-40510-3.stderr b/src/test/ui/issues/issue-40510-3.stderr new file mode 100644 index 0000000000000..e393352ff8c44 --- /dev/null +++ b/src/test/ui/issues/issue-40510-3.stderr @@ -0,0 +1,24 @@ +error[E0598]: lifetime of `x` is too short to guarantee its contents can be safely reborrowed + --> $DIR/issue-40510-3.rs:20:9 + | +LL | || { //~ ERROR too short + | ^^ + | +note: `x` would have to be valid for the expression at 19:5... + --> $DIR/issue-40510-3.rs:19:5 + | +LL | / || { +LL | | || { //~ ERROR too short +LL | | x.push(()) +LL | | } +LL | | }; + | |_____^ +note: ...but `x` is only valid for the lifetime as defined on the body at 19:5 + --> $DIR/issue-40510-3.rs:19:5 + | +LL | || { + | ^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0598`. diff --git a/src/test/ui/issues/issue-49824.rs b/src/test/ui/issues/issue-49824.rs index 1f3e575288427..985f7bb59dc71 100644 --- a/src/test/ui/issues/issue-49824.rs +++ b/src/test/ui/issues/issue-49824.rs @@ -13,13 +13,14 @@ // This test checks that a failure occurs with NLL but does not fail with the // legacy AST output. Check issue-49824.nll.stderr for expected compilation error // output under NLL and #49824 for more information. +// +// UPDATE: Once #55756 was fixed, this test started to fail in both modes. #[rustc_error] fn main() { - //~^ compilation successful let mut x = 0; || { - || { + || { //~ ERROR too short let _y = &mut x; } }; diff --git a/src/test/ui/issues/issue-49824.stderr b/src/test/ui/issues/issue-49824.stderr index b6cafe5e9e994..d4e0b2f47990c 100644 --- a/src/test/ui/issues/issue-49824.stderr +++ b/src/test/ui/issues/issue-49824.stderr @@ -1,14 +1,24 @@ -error: compilation successful - --> $DIR/issue-49824.rs:18:1 - | -LL | / fn main() { -LL | | //~^ compilation successful -LL | | let mut x = 0; -LL | | || { -... | +error[E0598]: lifetime of `x` is too short to guarantee its contents can be safely reborrowed + --> $DIR/issue-49824.rs:23:9 + | +LL | || { //~ ERROR too short + | ^^ + | +note: `x` would have to be valid for the expression at 22:5... + --> $DIR/issue-49824.rs:22:5 + | +LL | / || { +LL | | || { //~ ERROR too short +LL | | let _y = &mut x; +LL | | } LL | | }; -LL | | } - | |_^ + | |_____^ +note: ...but `x` is only valid for the lifetime as defined on the body at 22:5 + --> $DIR/issue-49824.rs:22:5 + | +LL | || { + | ^^ error: aborting due to previous error +For more information about this error, try `rustc --explain E0598`. diff --git a/src/test/ui/regions/region-borrow-params-issue-29793-big.ast.stderr b/src/test/ui/regions/region-borrow-params-issue-29793-big.ast.stderr index 23dc39db129ee..94f08a4a1a7f2 100644 --- a/src/test/ui/regions/region-borrow-params-issue-29793-big.ast.stderr +++ b/src/test/ui/regions/region-borrow-params-issue-29793-big.ast.stderr @@ -7,9 +7,10 @@ LL | WrapB::new().set(|t: bool| if t { x } else { y }) // (separate erro | capture occurs here ... LL | }); - | - borrowed value dropped before borrower - | - = note: values in a scope are dropped in the opposite order they are created + | - borrowed value only lives until here +... +LL | } + | - borrowed value needs to live until here error[E0597]: `y` does not live long enough --> $DIR/region-borrow-params-issue-29793-big.rs:81:54 @@ -20,9 +21,10 @@ LL | WrapB::new().set(|t: bool| if t { x } else { y }) // (separate erro | capture occurs here ... LL | }); - | - borrowed value dropped before borrower - | - = note: values in a scope are dropped in the opposite order they are created + | - borrowed value only lives until here +... +LL | } + | - borrowed value needs to live until here error: aborting due to 2 previous errors diff --git a/src/test/ui/regions/region-borrow-params-issue-29793-small.stderr b/src/test/ui/regions/region-borrow-params-issue-29793-small.stderr index cfe38fe20040c..3f44ef34a60bc 100644 --- a/src/test/ui/regions/region-borrow-params-issue-29793-small.stderr +++ b/src/test/ui/regions/region-borrow-params-issue-29793-small.stderr @@ -7,9 +7,10 @@ LL | let f = |t: bool| if t { x } else { y }; // (separate errors for `x | capture occurs here ... LL | }; - | - borrowed value dropped before borrower - | - = note: values in a scope are dropped in the opposite order they are created + | - borrowed value only lives until here +... +LL | } + | - borrowed value needs to live until here error[E0597]: `y` does not live long enough --> $DIR/region-borrow-params-issue-29793-small.rs:19:45 @@ -20,9 +21,10 @@ LL | let f = |t: bool| if t { x } else { y }; // (separate errors for `x | capture occurs here ... LL | }; - | - borrowed value dropped before borrower - | - = note: values in a scope are dropped in the opposite order they are created + | - borrowed value only lives until here +... +LL | } + | - borrowed value needs to live until here error[E0597]: `x` does not live long enough --> $DIR/region-borrow-params-issue-29793-small.rs:34:34 @@ -33,9 +35,10 @@ LL | let f = |t: bool| if t { x } else { y }; // (separate errors for `x | capture occurs here ... LL | }; - | - borrowed value dropped before borrower - | - = note: values in a scope are dropped in the opposite order they are created + | - borrowed value only lives until here +... +LL | } + | - borrowed value needs to live until here error[E0597]: `y` does not live long enough --> $DIR/region-borrow-params-issue-29793-small.rs:34:45 @@ -46,9 +49,10 @@ LL | let f = |t: bool| if t { x } else { y }; // (separate errors for `x | capture occurs here ... LL | }; - | - borrowed value dropped before borrower - | - = note: values in a scope are dropped in the opposite order they are created + | - borrowed value only lives until here +... +LL | } + | - borrowed value needs to live until here error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function --> $DIR/region-borrow-params-issue-29793-small.rs:65:17 diff --git a/src/test/ui/regions/regions-escape-via-trait-or-not.stderr b/src/test/ui/regions/regions-escape-via-trait-or-not.stderr index 60bd14ba4d79c..c13996cd2b08f 100644 --- a/src/test/ui/regions/regions-escape-via-trait-or-not.stderr +++ b/src/test/ui/regions/regions-escape-via-trait-or-not.stderr @@ -12,16 +12,16 @@ LL | with(|o| o) //~ ERROR cannot infer = note: ...so that the expression is assignable: expected &isize found &isize -note: but, the lifetime must be valid for the expression at 28:5... +note: but, the lifetime must be valid for the call at 28:5... --> $DIR/regions-escape-via-trait-or-not.rs:28:5 | LL | with(|o| o) //~ ERROR cannot infer - | ^^^^ -note: ...so type `fn([closure@$DIR/regions-escape-via-trait-or-not.rs:28:10: 28:15]) -> isize {with::<&isize, [closure@$DIR/regions-escape-via-trait-or-not.rs:28:10: 28:15]>}` of expression is valid during the expression - --> $DIR/regions-escape-via-trait-or-not.rs:28:5 + | ^^^^^^^^^^^ +note: ...so that argument is valid for the call + --> $DIR/regions-escape-via-trait-or-not.rs:28:10 | LL | with(|o| o) //~ ERROR cannot infer - | ^^^^ + | ^^^^^ error: aborting due to previous error diff --git a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs index 99e5cc0315383..a2000fa4db86b 100644 --- a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs +++ b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.rs @@ -14,7 +14,7 @@ fn main() { // Unboxed closure case { let mut x = 0; - let mut f = || &mut x; //~ ERROR cannot infer + let mut f = || &mut x; //~ ERROR borrowed data cannot be stored outside of its closure let x = f(); let y = f(); } diff --git a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr index c841c9605bd5f..13c1f69467fe5 100644 --- a/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr +++ b/src/test/ui/regions/regions-return-ref-to-upvar-issue-17403.stderr @@ -1,30 +1,11 @@ -error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements +error: borrowed data cannot be stored outside of its closure --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:17:24 | -LL | let mut f = || &mut x; //~ ERROR cannot infer - | ^^^^^^ - | -note: first, the lifetime cannot outlive the lifetime as defined on the body at 17:21... - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:17:21 - | -LL | let mut f = || &mut x; //~ ERROR cannot infer - | ^^^^^^^^^ -note: ...so that closure can access `x` - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:17:24 - | -LL | let mut f = || &mut x; //~ ERROR cannot infer - | ^^^^^^ -note: but, the lifetime must be valid for the call at 19:17... - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:19:17 - | -LL | let y = f(); - | ^^^ -note: ...so type `&mut i32` of expression is valid during the expression - --> $DIR/regions-return-ref-to-upvar-issue-17403.rs:19:17 - | -LL | let y = f(); - | ^^^ +LL | let mut f = || &mut x; //~ ERROR borrowed data cannot be stored outside of its closure + | ----- -- ^^^^^^ cannot be stored outside of its closure + | | | + | | ...because it cannot outlive this closure + | borrowed data cannot be stored into here... error: aborting due to previous error -For more information about this error, try `rustc --explain E0495`. From c56b1225f8623307c5ec52bc6ad177fc052ea2ed Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 16 Nov 2018 14:47:26 -0500 Subject: [PATCH 5/6] bless more tests --- .../borrowck-describe-lvalue.ast.nll.stderr | 8 +++---- src/test/ui/issues/issue-40510-1.nll.stderr | 14 ------------- src/test/ui/issues/issue-40510-3.nll.stderr | 11 +++++----- src/test/ui/issues/issue-44056.rs | 16 -------------- src/test/ui/issues/issue-49824.nll.stderr | 21 +++---------------- 5 files changed, 12 insertions(+), 58 deletions(-) delete mode 100644 src/test/ui/issues/issue-40510-1.nll.stderr delete mode 100644 src/test/ui/issues/issue-44056.rs diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr index c3aba793f190e..a1824c698aad6 100644 --- a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr +++ b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr @@ -10,7 +10,7 @@ LL | *y = 1; | ------ first borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:307:20 + --> $DIR/borrowck-describe-lvalue.rs:308:20 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -26,10 +26,10 @@ error: captured variable cannot escape `FnMut` closure body LL | || { | - inferred to be a `FnMut` closure LL | / || { //[mir]~ ERROR captured variable cannot escape `FnMut` closure body +LL | | //[ast]~^ ERROR lifetime of `x` is too short LL | | let y = &mut x; LL | | &mut x; //[ast]~ ERROR cannot borrow `**x` as mutable more than once at a time -LL | | //[mir]~^ ERROR cannot borrow `x` as mutable more than once at a time -LL | | *y = 1; +... | LL | | drop(y); LL | | } | |_________________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body @@ -370,7 +370,7 @@ LL | drop(x); This warning will become a hard error in the future. error[E0382]: use of moved value: `x` - --> $DIR/borrowck-describe-lvalue.rs:318:22 + --> $DIR/borrowck-describe-lvalue.rs:319:22 | LL | drop(x); | - value moved here diff --git a/src/test/ui/issues/issue-40510-1.nll.stderr b/src/test/ui/issues/issue-40510-1.nll.stderr deleted file mode 100644 index 723b6f3111c62..0000000000000 --- a/src/test/ui/issues/issue-40510-1.nll.stderr +++ /dev/null @@ -1,14 +0,0 @@ -warning: captured variable cannot escape `FnMut` closure body - --> $DIR/issue-40510-1.rs:18:9 - | -LL | || { - | - inferred to be a `FnMut` closure -LL | &mut x - | ^^^^^^ returns a reference to a captured variable which escapes the closure body - | - = note: `FnMut` closures only have access to their captured variables while they are executing... - = note: ...therefore, they cannot allow references to captured variables to escape - = warning: This error has been downgraded to a warning for backwards compatibility with previous releases. - It represents potential unsoundness in your code. - This warning will become a hard error in the future. - diff --git a/src/test/ui/issues/issue-40510-3.nll.stderr b/src/test/ui/issues/issue-40510-3.nll.stderr index e8e82ee8fdae2..54b342c92722a 100644 --- a/src/test/ui/issues/issue-40510-3.nll.stderr +++ b/src/test/ui/issues/issue-40510-3.nll.stderr @@ -1,16 +1,15 @@ -warning: captured variable cannot escape `FnMut` closure body - --> $DIR/issue-40510-3.rs:18:9 +error: captured variable cannot escape `FnMut` closure body + --> $DIR/issue-40510-3.rs:20:9 | LL | || { | - inferred to be a `FnMut` closure -LL | / || { +LL | / || { //~ ERROR too short LL | | x.push(()) LL | | } | |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body | = note: `FnMut` closures only have access to their captured variables while they are executing... = note: ...therefore, they cannot allow references to captured variables to escape - = warning: This error has been downgraded to a warning for backwards compatibility with previous releases. - It represents potential unsoundness in your code. - This warning will become a hard error in the future. + +error: aborting due to previous error diff --git a/src/test/ui/issues/issue-44056.rs b/src/test/ui/issues/issue-44056.rs deleted file mode 100644 index b2f0e917749d5..0000000000000 --- a/src/test/ui/issues/issue-44056.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-pass -// only-x86_64 -// no-prefer-dynamic -// compile-flags: -Ctarget-feature=+avx -Clto - -fn main() {} diff --git a/src/test/ui/issues/issue-49824.nll.stderr b/src/test/ui/issues/issue-49824.nll.stderr index 555558c99d933..3fd6854322ab1 100644 --- a/src/test/ui/issues/issue-49824.nll.stderr +++ b/src/test/ui/issues/issue-49824.nll.stderr @@ -1,30 +1,15 @@ -warning: captured variable cannot escape `FnMut` closure body - --> $DIR/issue-49824.rs:22:9 +error: captured variable cannot escape `FnMut` closure body + --> $DIR/issue-49824.rs:23:9 | LL | || { | - inferred to be a `FnMut` closure -LL | / || { +LL | / || { //~ ERROR too short LL | | let _y = &mut x; LL | | } | |_________^ returns a closure that contains a reference to a captured variable, which then escapes the closure body | = note: `FnMut` closures only have access to their captured variables while they are executing... = note: ...therefore, they cannot allow references to captured variables to escape - = warning: This error has been downgraded to a warning for backwards compatibility with previous releases. - It represents potential unsoundness in your code. - This warning will become a hard error in the future. - -error: compilation successful - --> $DIR/issue-49824.rs:18:1 - | -LL | / fn main() { -LL | | //~^ compilation successful -LL | | let mut x = 0; -LL | | || { -... | -LL | | }; -LL | | } - | |_^ error: aborting due to previous error From 29ec15ab4ad1dc0cd249bec489a76e35721077e4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 16 Nov 2018 17:11:29 -0500 Subject: [PATCH 6/6] update incremental test -- returning that reference was illegal Also, changing the pattern in the closure in this way doesn't affect the closure *creator* in particular. --- src/test/incremental/hashes/closure_expressions.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/incremental/hashes/closure_expressions.rs b/src/test/incremental/hashes/closure_expressions.rs index 85d1f3df05b1d..90feed214408e 100644 --- a/src/test/incremental/hashes/closure_expressions.rs +++ b/src/test/incremental/hashes/closure_expressions.rs @@ -60,14 +60,14 @@ pub fn add_parameter() { // Change parameter pattern ---------------------------------------------------- #[cfg(cfail1)] pub fn change_parameter_pattern() { - let _ = |x: &u32| x; + let _ = |x: &u32| { println!("{}", x); }; } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, TypeckTables")] +#[rustc_clean(cfg="cfail2", except="HirBody, TypeckTables")] #[rustc_clean(cfg="cfail3")] pub fn change_parameter_pattern() { - let _ = |&x: &u32| x; + let _ = |&x: &u32| { println!("{}", x); }; }