From 80ed62eea77ecac142d241e22c4e048da78d0434 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 14:47:57 -0400 Subject: [PATCH 01/34] regionck: rustfmt --- src/librustc_typeck/check/regionck.rs | 695 ++++++++++++++------------ 1 file changed, 388 insertions(+), 307 deletions(-) diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index fbf8afc3be234..e3f9d6cd97ca8 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -88,27 +88,32 @@ use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; -use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty}; use rustc::infer; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::ty::adjustment; +use rustc::ty::subst::Substs; +use rustc::ty::{self, Ty}; +use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc::hir::{self, PatKind}; +use rustc_data_structures::sync::Lrc; use std::mem; use std::ops::Deref; use std::rc::Rc; -use rustc_data_structures::sync::Lrc; use syntax::ast; use syntax_pos::Span; -use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; -use rustc::hir::{self, PatKind}; // a variation on try that just returns unit macro_rules! ignore_err { - ($e:expr) => (match $e { Ok(e) => e, Err(_) => { - debug!("ignoring mem-categorization error!"); - return () - }}) + ($e:expr) => { + match $e { + Ok(e) => e, + Err(_) => { + debug!("ignoring mem-categorization error!"); + return (); + } + } + }; } /////////////////////////////////////////////////////////////////////////// @@ -118,11 +123,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn regionck_expr(&self, body: &'gcx hir::Body) { let subject = self.tcx.hir.body_owner_def_id(body.id()); let id = body.value.id; - let mut rcx = RegionCtxt::new(self, - RepeatingScope(id), - id, - Subject(subject), - self.param_env); + let mut rcx = RegionCtxt::new( + self, + RepeatingScope(id), + id, + Subject(subject), + self.param_env, + ); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_body(body); @@ -136,18 +143,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// Region checking during the WF phase for items. `wf_tys` are the /// types from which we should derive implied bounds, if any. - pub fn regionck_item(&self, - item_id: ast::NodeId, - span: Span, - wf_tys: &[Ty<'tcx>]) { + pub fn regionck_item(&self, item_id: ast::NodeId, span: Span, wf_tys: &[Ty<'tcx>]) { debug!("regionck_item(item.id={:?}, wf_tys={:?})", item_id, wf_tys); let subject = self.tcx.hir.local_def_id(item_id); - let mut rcx = RegionCtxt::new(self, - RepeatingScope(item_id), - item_id, - Subject(subject), - self.param_env); - rcx.outlives_environment.add_implied_bounds(self, wf_tys, item_id, span); + let mut rcx = RegionCtxt::new( + self, + RepeatingScope(item_id), + item_id, + Subject(subject), + self.param_env, + ); + rcx.outlives_environment + .add_implied_bounds(self, wf_tys, item_id, span); rcx.visit_region_obligations(item_id); rcx.resolve_regions_and_report_errors(); } @@ -160,17 +167,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// rest of type check and because sometimes we need type /// inference to have completed before we can determine which /// constraints to add. - pub fn regionck_fn(&self, - fn_id: ast::NodeId, - body: &'gcx hir::Body) { + pub fn regionck_fn(&self, fn_id: ast::NodeId, body: &'gcx hir::Body) { debug!("regionck_fn(id={})", fn_id); let subject = self.tcx.hir.body_owner_def_id(body.id()); let node_id = body.value.id; - let mut rcx = RegionCtxt::new(self, - RepeatingScope(node_id), - node_id, - Subject(subject), - self.param_env); + let mut rcx = RegionCtxt::new( + self, + RepeatingScope(node_id), + node_id, + Subject(subject), + self.param_env, + ); if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded @@ -190,7 +197,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // INTERNALS -pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { +pub struct RegionCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { pub fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, pub region_scope_tree: Lrc, @@ -208,7 +215,6 @@ pub struct RegionCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { // id of AST node being analyzed (the subject of the analysis). subject_def_id: DefId, - } impl<'a, 'gcx, 'tcx> Deref for RegionCtxt<'a, 'gcx, 'tcx> { @@ -222,12 +228,13 @@ pub struct RepeatingScope(ast::NodeId); pub struct Subject(DefId); impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { - pub fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, - RepeatingScope(initial_repeating_scope): RepeatingScope, - initial_body_id: ast::NodeId, - Subject(subject): Subject, - param_env: ty::ParamEnv<'tcx>) - -> RegionCtxt<'a, 'gcx, 'tcx> { + pub fn new( + fcx: &'a FnCtxt<'a, 'gcx, 'tcx>, + RepeatingScope(initial_repeating_scope): RepeatingScope, + initial_body_id: ast::NodeId, + Subject(subject): Subject, + param_env: ty::ParamEnv<'tcx>, + ) -> RegionCtxt<'a, 'gcx, 'tcx> { let region_scope_tree = fcx.tcx.region_scope_tree(subject); let outlives_environment = OutlivesEnvironment::new(param_env); RegionCtxt { @@ -296,11 +303,12 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// closures, however, we save and restore any "scoped state" /// before we invoke this function. (See `visit_fn` in the /// `intravisit::Visitor` impl below.) - fn visit_fn_body(&mut self, - id: ast::NodeId, // the id of the fn itself - body: &'gcx hir::Body, - span: Span) - { + fn visit_fn_body( + &mut self, + id: ast::NodeId, // the id of the fn itself + body: &'gcx hir::Body, + span: Span, + ) { // When we enter a function, we can derive debug!("visit_fn_body(id={})", id); @@ -309,7 +317,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let call_site = region::Scope { id: body.value.hir_id.local_id, - data: region::ScopeData::CallSite + data: region::ScopeData::CallSite, }; self.call_site_scope = Some(call_site); @@ -328,32 +336,39 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // because it will have no effect. // // FIXME(#27579) return types should not be implied bounds - let fn_sig_tys: Vec<_> = - fn_sig.inputs().iter().cloned().chain(Some(fn_sig.output())).collect(); + let fn_sig_tys: Vec<_> = fn_sig + .inputs() + .iter() + .cloned() + .chain(Some(fn_sig.output())) + .collect(); self.outlives_environment.add_implied_bounds( self.fcx, &fn_sig_tys[..], body_id.node_id, - span); + span, + ); self.link_fn_args( region::Scope { id: body.value.hir_id.local_id, - data: region::ScopeData::Node + data: region::ScopeData::Node, }, - &body.arguments); + &body.arguments, + ); self.visit_body(body); self.visit_region_obligations(body_id.node_id); let call_site_scope = self.call_site_scope.unwrap(); - debug!("visit_fn_body body.id {:?} call_site_scope: {:?}", - body.id(), call_site_scope); + debug!( + "visit_fn_body body.id {:?} call_site_scope: {:?}", + 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_outlive(infer::CallReturn(span), body_hir_id, call_site_region); self.constrain_opaque_types( &self.fcx.opaque_types.borrow(), @@ -361,8 +376,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ); } - fn visit_region_obligations(&mut self, node_id: ast::NodeId) - { + fn visit_region_obligations(&mut self, node_id: ast::NodeId) { debug!("visit_region_obligations: node_id={}", node_id); // region checking can introduce new pending obligations @@ -374,19 +388,24 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.outlives_environment.region_bound_pairs(), self.implicit_region_bound, self.param_env, - self.body_id); + self.body_id, + ); } fn resolve_regions_and_report_errors(&self) { - self.fcx.resolve_regions_and_report_errors(self.subject_def_id, - &self.region_scope_tree, - &self.outlives_environment); + self.fcx.resolve_regions_and_report_errors( + self.subject_def_id, + &self.region_scope_tree, + &self.outlives_environment, + ); } fn resolve_regions_and_report_errors_unless_nll(&self) { - self.fcx.resolve_regions_and_report_errors_unless_nll(self.subject_def_id, - &self.region_scope_tree, - &self.outlives_environment); + self.fcx.resolve_regions_and_report_errors_unless_nll( + self.subject_def_id, + &self.region_scope_tree, + &self.outlives_environment, + ); } fn constrain_bindings_in_pat(&mut self, pat: &hir::Pat) { @@ -423,7 +442,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let typ = self.resolve_node_type(hir_id); let body_id = self.body_id; let _ = dropck::check_safety_of_destructor_if_necessary( - self, typ, span, body_id, var_scope); + self, typ, span, body_id, var_scope, + ); }) } } @@ -441,14 +461,21 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { NestedVisitorMap::None } - fn visit_fn(&mut self, - fk: intravisit::FnKind<'gcx>, - _: &'gcx hir::FnDecl, - body_id: hir::BodyId, - span: Span, - id: ast::NodeId) { - assert!(match fk { intravisit::FnKind::Closure(..) => true, _ => false }, - "visit_fn invoked for something other than a closure"); + fn visit_fn( + &mut self, + fk: intravisit::FnKind<'gcx>, + _: &'gcx hir::FnDecl, + body_id: hir::BodyId, + span: Span, + id: ast::NodeId, + ) { + assert!( + match fk { + intravisit::FnKind::Closure(..) => true, + _ => false, + }, + "visit_fn invoked for something other than a closure" + ); // Save state of current function before invoking // `visit_fn_body`. We will restore afterwards. @@ -460,7 +487,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { self.visit_fn_body(id, body, span); // Restore state from previous function. - self.outlives_environment.pop_snapshot_post_closure(env_snapshot); + self.outlives_environment + .pop_snapshot_post_closure(env_snapshot); self.call_site_scope = old_call_site_scope; self.body_id = old_body_id; } @@ -483,20 +511,24 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { } fn visit_expr(&mut self, expr: &'gcx hir::Expr) { - debug!("regionck::visit_expr(e={:?}, repeating_scope={})", - expr, self.repeating_scope); + debug!( + "regionck::visit_expr(e={:?}, repeating_scope={})", + expr, self.repeating_scope + ); // No matter what, the type of each expression must outlive the // 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 { - id: expr.hir_id.local_id, - data: region::ScopeData::Node - })); - self.type_must_outlive(infer::ExprTypeIsNotInScope(expr_ty, expr.span), - expr_ty, expr_region); + let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope { + id: expr.hir_id.local_id, + data: region::ScopeData::Node, + })); + self.type_must_outlive( + infer::ExprTypeIsNotInScope(expr_ty, expr.span), + expr_ty, + expr_region, + ); let is_method_call = self.tables.borrow().is_method_call(expr); @@ -506,12 +538,11 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { // provided as arguments outlive the call. if is_method_call { let origin = match expr.node { - hir::ExprKind::MethodCall(..) => - infer::ParameterOrigin::MethodCall, - hir::ExprKind::Unary(op, _) if op == hir::UnDeref => - infer::ParameterOrigin::OverloadedDeref, - _ => - infer::ParameterOrigin::OverloadedOperator + hir::ExprKind::MethodCall(..) => infer::ParameterOrigin::MethodCall, + hir::ExprKind::Unary(op, _) if op == hir::UnDeref => { + infer::ParameterOrigin::OverloadedDeref + } + _ => infer::ParameterOrigin::OverloadedOperator, }; let substs = self.tables.borrow().node_substs(expr.hir_id); @@ -533,8 +564,10 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { } } - debug!("regionck::visit_expr(e={:?}, repeating_scope={}) - visiting subexprs", - expr, self.repeating_scope); + debug!( + "regionck::visit_expr(e={:?}, repeating_scope={}) - visiting subexprs", + expr, self.repeating_scope + ); match expr.node { hir::ExprKind::Path(_) => { let substs = self.tables.borrow().node_substs(expr.hir_id); @@ -571,7 +604,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { self.constrain_call(expr, Some(&lhs), Some(&**rhs).into_iter()); intravisit::walk_expr(self, expr); - }, + } hir::ExprKind::Binary(_, ref lhs, ref rhs) if is_method_call => { // As `ExprKind::MethodCall`, but the call is via an overloaded op. @@ -586,8 +619,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_outlive(infer::Operand(expr.span), ty, expr_region); } intravisit::walk_expr(self, expr); } @@ -674,12 +706,16 @@ 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; - debug!("visit_expr ExprKind::Ret ret_expr.id {} call_site_scope: {:?}", - ret_expr.id, call_site_scope); + 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(infer::CallReturn(ret_expr.span), - ret_expr.hir_id, - call_site_region); + self.type_of_node_must_outlive( + infer::CallReturn(ret_expr.span), + ret_expr.hir_id, + call_site_region, + ); intravisit::walk_expr(self, expr); } @@ -691,13 +727,11 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for RegionCtxt<'a, 'gcx, 'tcx> { } impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { - fn constrain_cast(&mut self, - cast_expr: &hir::Expr, - source_expr: &hir::Expr) - { - debug!("constrain_cast(cast_expr={:?}, source_expr={:?})", - cast_expr, - source_expr); + fn constrain_cast(&mut self, cast_expr: &hir::Expr, source_expr: &hir::Expr) { + debug!( + "constrain_cast(cast_expr={:?}, source_expr={:?})", + cast_expr, source_expr + ); let source_ty = self.resolve_node_type(source_expr.hir_id); let target_ty = self.resolve_node_type(cast_expr.hir_id); @@ -705,40 +739,35 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.walk_cast(cast_expr, source_ty, target_ty); } - fn walk_cast(&mut self, - cast_expr: &hir::Expr, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>) { - debug!("walk_cast(from_ty={:?}, to_ty={:?})", - from_ty, - to_ty); + fn walk_cast(&mut self, cast_expr: &hir::Expr, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) { + debug!("walk_cast(from_ty={:?}, to_ty={:?})", from_ty, to_ty); match (&from_ty.sty, &to_ty.sty) { - /*From:*/ (&ty::Ref(from_r, from_ty, _), - /*To: */ &ty::Ref(to_r, to_ty, _)) => { + /*From:*/ + (&ty::Ref(from_r, from_ty, _), /*To: */ &ty::Ref(to_r, to_ty, _)) => { // Target cannot outlive source, naturally. self.sub_regions(infer::Reborrow(cast_expr.span), to_r, from_r); self.walk_cast(cast_expr, from_ty, to_ty); } - /*From:*/ (_, - /*To: */ &ty::Dynamic(.., r)) => { + /*From:*/ + (_, /*To: */ &ty::Dynamic(.., r)) => { // When T is existentially quantified as a trait // `Foo+'to`, it must outlive the region bound `'to`. self.type_must_outlive(infer::RelateObjectBound(cast_expr.span), from_ty, r); } - /*From:*/ (&ty::Adt(from_def, _), - /*To: */ &ty::Adt(to_def, _)) if from_def.is_box() && to_def.is_box() => { + /*From:*/ + (&ty::Adt(from_def, _), /*To: */ &ty::Adt(to_def, _)) + if from_def.is_box() && to_def.is_box() => + { self.walk_cast(cast_expr, from_ty.boxed_ty(), to_ty.boxed_ty()); } - _ => { } + _ => {} } } - fn check_expr_fn_block(&mut self, - expr: &'gcx hir::Expr, - body_id: hir::BodyId) { + fn check_expr_fn_block(&mut self, expr: &'gcx hir::Expr, body_id: hir::BodyId) { let repeating_scope = self.set_repeating_scope(body_id.node_id); intravisit::walk_expr(self, expr); self.set_repeating_scope(repeating_scope); @@ -747,7 +776,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { fn constrain_callee(&mut self, callee_expr: &hir::Expr) { let callee_ty = self.resolve_node_type(callee_expr.hir_id); match callee_ty.sty { - ty::FnDef(..) | ty::FnPtr(_) => { } + ty::FnDef(..) | ty::FnPtr(_) => {} _ => { // this should not happen, but it does if the program is // erroneous @@ -760,18 +789,21 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } } - fn constrain_call<'b, I: Iterator>(&mut self, - call_expr: &hir::Expr, - receiver: Option<&hir::Expr>, - arg_exprs: I) { + fn constrain_call<'b, I: Iterator>( + &mut self, + call_expr: &hir::Expr, + receiver: Option<&hir::Expr>, + arg_exprs: I, + ) { //! Invoked on every call site (i.e., normal calls, method calls, //! and overloaded operators). Constrains the regions which appear //! in the type of the function. Also constrains the regions that //! appear in the arguments appropriately. - debug!("constrain_call(call_expr={:?}, receiver={:?})", - call_expr, - receiver); + debug!( + "constrain_call(call_expr={:?}, receiver={:?})", + call_expr, receiver + ); // `callee_region` is the scope representing the time in which the // call occurs. @@ -779,7 +811,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // FIXME(#6268) to support nested method calls, should be callee_id let callee_scope = region::Scope { id: call_expr.hir_id.local_id, - data: region::ScopeData::Node + data: region::ScopeData::Node, }; let callee_region = self.tcx.mk_region(ty::ReScope(callee_scope)); @@ -790,27 +822,30 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // 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(infer::CallArg(arg_expr.span), - arg_expr.hir_id, - callee_region); + self.type_of_node_must_outlive( + infer::CallArg(arg_expr.span), + arg_expr.hir_id, + callee_region, + ); } // 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_outlive(infer::CallRcvr(r.span), r.hir_id, callee_region); } } /// Create a temporary `MemCategorizationContext` and pass it to the closure. fn with_mc(&self, f: F) -> R - where F: for<'b> FnOnce(mc::MemCategorizationContext<'b, 'gcx, 'tcx>) -> R + where + F: for<'b> FnOnce(mc::MemCategorizationContext<'b, 'gcx, 'tcx>) -> R, { - f(mc::MemCategorizationContext::with_infer(&self.infcx, - &self.region_scope_tree, - &self.tables.borrow())) + f(mc::MemCategorizationContext::with_infer( + &self.infcx, + &self.region_scope_tree, + &self.tables.borrow(), + )) } /// Invoked on any adjustments that occur. Checks that if this is a region pointer being @@ -832,37 +867,46 @@ 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 { - id: expr.hir_id.local_id, - data: region::ScopeData::Node - })); + let expr_region = self.tcx.mk_region(ty::ReScope(region::Scope { + id: expr.hir_id.local_id, + data: region::ScopeData::Node, + })); for adjustment in adjustments { - debug!("constrain_adjustments: adjustment={:?}, cmt={:?}", - adjustment, cmt); + debug!( + "constrain_adjustments: adjustment={:?}, cmt={:?}", + adjustment, cmt + ); if let adjustment::Adjust::Deref(Some(deref)) = adjustment.kind { debug!("constrain_adjustments: overloaded deref: {:?}", deref); // Treat overloaded autoderefs as if an AutoBorrow adjustment // was applied on the base type, as that is always the case. - let input = self.tcx.mk_ref(deref.region, ty::TypeAndMut { - ty: cmt.ty, - mutbl: deref.mutbl, - }); - let output = self.tcx.mk_ref(deref.region, ty::TypeAndMut { - ty: adjustment.target, - mutbl: deref.mutbl, - }); - - self.link_region(expr.span, deref.region, - ty::BorrowKind::from_mutbl(deref.mutbl), &cmt); + let input = self.tcx.mk_ref( + deref.region, + ty::TypeAndMut { + ty: cmt.ty, + mutbl: deref.mutbl, + }, + ); + let output = self.tcx.mk_ref( + deref.region, + ty::TypeAndMut { + ty: adjustment.target, + mutbl: deref.mutbl, + }, + ); + + self.link_region( + expr.span, + deref.region, + ty::BorrowKind::from_mutbl(deref.mutbl), + &cmt, + ); // 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_outlive(infer::CallRcvr(expr.span), input, expr_region); + self.type_must_outlive(infer::CallReturn(expr.span), output, expr_region); } if let adjustment::Adjust::Borrow(ref autoref) = adjustment.kind { @@ -872,73 +916,84 @@ 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(infer::AutoBorrow(expr.span), - expr.hir_id, - expr_region); + self.type_of_node_must_outlive( + infer::AutoBorrow(expr.span), + expr.hir_id, + expr_region, + ); } cmt = self.with_mc(|mc| mc.cat_expr_adjusted(expr, cmt, &adjustment))?; if let Categorization::Deref(_, mc::BorrowedPtr(_, r_ptr)) = cmt.cat { - self.mk_subregion_due_to_dereference(expr.span, - expr_region, r_ptr); + self.mk_subregion_due_to_dereference(expr.span, expr_region, r_ptr); } } Ok(cmt) } - pub fn mk_subregion_due_to_dereference(&mut self, - deref_span: Span, - minimum_lifetime: ty::Region<'tcx>, - maximum_lifetime: ty::Region<'tcx>) { - self.sub_regions(infer::DerefPointer(deref_span), - minimum_lifetime, maximum_lifetime) + pub fn mk_subregion_due_to_dereference( + &mut self, + deref_span: Span, + minimum_lifetime: ty::Region<'tcx>, + maximum_lifetime: ty::Region<'tcx>, + ) { + self.sub_regions( + infer::DerefPointer(deref_span), + minimum_lifetime, + maximum_lifetime, + ) } - fn check_safety_of_rvalue_destructor_if_necessary(&mut self, - cmt: &mc::cmt_<'tcx>, - span: Span) { + fn check_safety_of_rvalue_destructor_if_necessary(&mut self, cmt: &mc::cmt_<'tcx>, span: Span) { match cmt.cat { - Categorization::Rvalue(region) => { - match *region { - ty::ReScope(rvalue_scope) => { - let typ = self.resolve_type(cmt.ty); - let body_id = self.body_id; - let _ = dropck::check_safety_of_destructor_if_necessary( - self, typ, span, body_id, rvalue_scope); - } - ty::ReStatic => {} - _ => { - span_bug!(span, - "unexpected rvalue region in rvalue \ - destructor safety checking: `{:?}`", - region); - } + Categorization::Rvalue(region) => match *region { + ty::ReScope(rvalue_scope) => { + let typ = self.resolve_type(cmt.ty); + let body_id = self.body_id; + let _ = dropck::check_safety_of_destructor_if_necessary( + self, + typ, + span, + body_id, + rvalue_scope, + ); } - } + ty::ReStatic => {} + _ => { + span_bug!( + span, + "unexpected rvalue region in rvalue \ + destructor safety checking: `{:?}`", + region + ); + } + }, _ => {} } } /// Invoked on any index expression that occurs. Checks that if this is a slice /// being indexed, the lifetime of the pointer includes the deref expr. - fn constrain_index(&mut self, - index_expr: &hir::Expr, - indexed_ty: Ty<'tcx>) - { - debug!("constrain_index(index_expr=?, indexed_ty={}", - self.ty_to_string(indexed_ty)); + fn constrain_index(&mut self, index_expr: &hir::Expr, indexed_ty: Ty<'tcx>) { + debug!( + "constrain_index(index_expr=?, indexed_ty={}", + self.ty_to_string(indexed_ty) + ); let r_index_expr = ty::ReScope(region::Scope { id: index_expr.hir_id.local_id, - data: region::ScopeData::Node + data: region::ScopeData::Node, }); if let ty::Ref(r_ptr, r_ty, _) = indexed_ty.sty { match r_ty.sty { ty::Slice(_) | ty::Str => { - self.sub_regions(infer::IndexSlice(index_expr.span), - self.tcx.mk_region(r_index_expr), r_ptr); + self.sub_regions( + infer::IndexSlice(index_expr.span), + self.tcx.mk_region(r_index_expr), + r_ptr, + ); } _ => {} } @@ -947,27 +1002,29 @@ 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(&mut self, + fn type_of_node_must_outlive( + &mut self, origin: infer::SubregionOrigin<'tcx>, hir_id: hir::HirId, - minimum_lifetime: ty::Region<'tcx>) - { + minimum_lifetime: ty::Region<'tcx>, + ) { // Try to resolve the type. If we encounter an error, then typeck // is going to fail anyway, so just stop here and let typeck // report errors later on in the writeback phase. let ty0 = self.resolve_node_type(hir_id); let ty = self.tables - .borrow() - .adjustments() - .get(hir_id) - .and_then(|adj| adj.last()) - .map_or(ty0, |adj| adj.target); + .borrow() + .adjustments() + .get(hir_id) + .and_then(|adj| adj.last()) + .map_or(ty0, |adj| adj.target); let ty = self.resolve_type(ty); - debug!("constrain_regions_in_type_of_node(\ - ty={}, ty0={}, id={:?}, minimum_lifetime={:?})", - ty, ty0, - hir_id, minimum_lifetime); + debug!( + "constrain_regions_in_type_of_node(\ + ty={}, ty0={}, id={:?}, minimum_lifetime={:?})", + ty, ty0, hir_id, minimum_lifetime + ); self.type_must_outlive(origin, ty, minimum_lifetime); } @@ -979,23 +1036,25 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// - `origin`, the reason we need this constraint /// - `ty`, the type `T` /// - `region`, the region `'a` - pub fn type_must_outlive(&self, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>) - { - self.infcx.type_must_outlive(self.outlives_environment.region_bound_pairs(), - self.implicit_region_bound, - self.param_env, - origin, - ty, - region); + pub fn type_must_outlive( + &self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + self.infcx.type_must_outlive( + self.outlives_environment.region_bound_pairs(), + self.implicit_region_bound, + self.param_env, + origin, + ty, + region, + ); } /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the /// resulting pointer is linked to the lifetime of its guarantor (if any). - fn link_addr_of(&mut self, expr: &hir::Expr, - mutability: hir::Mutability, base: &hir::Expr) { + fn link_addr_of(&mut self, expr: &hir::Expr, mutability: hir::Mutability, base: &hir::Expr) { debug!("link_addr_of(expr={:?}, base={:?})", expr, base); let cmt = ignore_err!(self.with_mc(|mc| mc.cat_expr(base))); @@ -1011,7 +1070,9 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { fn link_local(&self, local: &hir::Local) { debug!("regionck::for_local()"); let init_expr = match local.init { - None => { return; } + None => { + return; + } Some(ref expr) => &**expr, }; let discr_cmt = Rc::new(ignore_err!(self.with_mc(|mc| mc.cat_expr(init_expr)))); @@ -1043,10 +1104,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let arg_cmt = self.with_mc(|mc| { Rc::new(mc.cat_rvalue(arg.hir_id, arg.pat.span, re_scope, arg_ty)) }); - debug!("arg_ty={:?} arg_cmt={:?} arg={:?}", - arg_ty, - arg_cmt, - arg); + debug!("arg_ty={:?} arg_cmt={:?} arg={:?}", arg_ty, arg_cmt, arg); self.link_pattern(arg_cmt, &arg.pat); } } @@ -1054,9 +1112,10 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// Link lifetimes of any ref bindings in `root_pat` to the pointers found /// in the discriminant, if needed. fn link_pattern(&self, discr_cmt: mc::cmt<'tcx>, root_pat: &hir::Pat) { - debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", - discr_cmt, - root_pat); + debug!( + "link_pattern(discr_cmt={:?}, root_pat={:?})", + discr_cmt, root_pat + ); ignore_err!(self.with_mc(|mc| { mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, sub_pat| { match sub_pat.node { @@ -1064,11 +1123,17 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { PatKind::Binding(..) => { if let Some(&bm) = mc.tables.pat_binding_modes().get(sub_pat.hir_id) { if let ty::BindByReference(mutbl) = bm { - self.link_region_from_node_type(sub_pat.span, sub_pat.hir_id, - mutbl, &sub_cmt); + self.link_region_from_node_type( + sub_pat.span, + sub_pat.hir_id, + mutbl, + &sub_cmt, + ); } } else { - self.tcx.sess.delay_span_bug(sub_pat.span, "missing binding mode"); + self.tcx + .sess + .delay_span_bug(sub_pat.span, "missing binding mode"); } } _ => {} @@ -1079,12 +1144,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// Link lifetime of borrowed pointer resulting from autoref to lifetimes in the value being /// autoref'd. - fn link_autoref(&self, - expr: &hir::Expr, - expr_cmt: &mc::cmt_<'tcx>, - autoref: &adjustment::AutoBorrow<'tcx>) - { - debug!("link_autoref(autoref={:?}, expr_cmt={:?})", autoref, expr_cmt); + fn link_autoref( + &self, + expr: &hir::Expr, + expr_cmt: &mc::cmt_<'tcx>, + autoref: &adjustment::AutoBorrow<'tcx>, + ) { + debug!( + "link_autoref(autoref={:?}, expr_cmt={:?})", + autoref, expr_cmt + ); match *autoref { adjustment::AutoBorrow::Ref(r, m) => { @@ -1094,7 +1163,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { adjustment::AutoBorrow::RawPtr(m) => { let r = self.tcx.mk_region(ty::ReScope(region::Scope { id: expr.hir_id.local_id, - data: region::ScopeData::Node + data: region::ScopeData::Node, })); self.link_region(expr.span, r, ty::BorrowKind::from_mutbl(m), expr_cmt); } @@ -1103,17 +1172,21 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// Like `link_region()`, except that the region is extracted from the type of `id`, /// which must be some reference (`&T`, `&str`, etc). - fn link_region_from_node_type(&self, - span: Span, - id: hir::HirId, - mutbl: hir::Mutability, - cmt_borrowed: &mc::cmt_<'tcx>) { - debug!("link_region_from_node_type(id={:?}, mutbl={:?}, cmt_borrowed={:?})", - id, mutbl, cmt_borrowed); + fn link_region_from_node_type( + &self, + span: Span, + id: hir::HirId, + mutbl: hir::Mutability, + cmt_borrowed: &mc::cmt_<'tcx>, + ) { + debug!( + "link_region_from_node_type(id={:?}, mutbl={:?}, cmt_borrowed={:?})", + id, mutbl, cmt_borrowed + ); let rptr_ty = self.resolve_node_type(id); if let ty::Ref(r, _, _) = rptr_ty.sty { - debug!("rptr_ty={}", rptr_ty); + debug!("rptr_ty={}", rptr_ty); self.link_region(span, r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed); } } @@ -1122,11 +1195,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// kind `borrow_kind` and lifetime `borrow_region`. /// In order to ensure borrowck is satisfied, this may create constraints /// between regions, as explained in `link_reborrowed_region()`. - fn link_region(&self, - span: Span, - borrow_region: ty::Region<'tcx>, - borrow_kind: ty::BorrowKind, - borrow_cmt: &mc::cmt_<'tcx>) { + fn link_region( + &self, + span: Span, + borrow_region: ty::Region<'tcx>, + borrow_kind: ty::BorrowKind, + borrow_cmt: &mc::cmt_<'tcx>, + ) { let origin = infer::DataBorrowed(borrow_cmt.ty, span); self.type_must_outlive(origin, borrow_cmt.ty, borrow_region); @@ -1134,16 +1209,21 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { let mut borrow_cmt_cat = borrow_cmt.cat.clone(); loop { - debug!("link_region(borrow_region={:?}, borrow_kind={:?}, borrow_cmt={:?})", - borrow_region, - borrow_kind, - borrow_cmt); + debug!( + "link_region(borrow_region={:?}, borrow_kind={:?}, borrow_cmt={:?})", + borrow_region, borrow_kind, borrow_cmt + ); match borrow_cmt_cat { Categorization::Deref(ref_cmt, mc::BorrowedPtr(ref_kind, ref_region)) => { - match self.link_reborrowed_region(span, - borrow_region, borrow_kind, - ref_cmt, ref_region, ref_kind, - borrow_cmt.note) { + match self.link_reborrowed_region( + span, + borrow_region, + borrow_kind, + ref_cmt, + ref_region, + ref_kind, + borrow_cmt.note, + ) { Some((c, k)) => { borrow_cmt_cat = c.cat.clone(); borrow_kind = k; @@ -1154,20 +1234,20 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { } } - Categorization::Downcast(cmt_base, _) | - Categorization::Deref(cmt_base, mc::Unique) | - Categorization::Interior(cmt_base, _) => { + Categorization::Downcast(cmt_base, _) + | Categorization::Deref(cmt_base, mc::Unique) + | Categorization::Interior(cmt_base, _) => { // Borrowing interior or owned data requires the base // to be valid and borrowable in the same fashion. borrow_cmt_cat = cmt_base.cat.clone(); borrow_kind = borrow_kind; } - Categorization::Deref(_, mc::UnsafePtr(..)) | - Categorization::StaticItem | - Categorization::Upvar(..) | - Categorization::Local(..) | - Categorization::Rvalue(..) => { + Categorization::Deref(_, mc::UnsafePtr(..)) + | Categorization::StaticItem + | Categorization::Upvar(..) + | Categorization::Local(..) + | Categorization::Rvalue(..) => { // These are all "base cases" with independent lifetimes // that are not subject to inference return; @@ -1218,16 +1298,16 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// /// The return value of this function indicates whether we need to /// recurse and process `ref_cmt` (see case 2 above). - fn link_reborrowed_region(&self, - span: Span, - borrow_region: ty::Region<'tcx>, - borrow_kind: ty::BorrowKind, - ref_cmt: mc::cmt<'tcx>, - ref_region: ty::Region<'tcx>, - mut ref_kind: ty::BorrowKind, - note: mc::Note) - -> Option<(mc::cmt<'tcx>, ty::BorrowKind)> - { + fn link_reborrowed_region( + &self, + span: Span, + borrow_region: ty::Region<'tcx>, + borrow_kind: ty::BorrowKind, + ref_cmt: mc::cmt<'tcx>, + ref_region: ty::Region<'tcx>, + mut ref_kind: ty::BorrowKind, + note: mc::Note, + ) -> Option<(mc::cmt<'tcx>, ty::BorrowKind)> { // Possible upvar ID we may need later to create an entry in the // maybe link map. @@ -1243,7 +1323,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { infer::ReborrowUpvar(span, *upvar_id) } _ => { - span_bug!( span, "Illegal upvar id: {:?}", upvar_id); + span_bug!(span, "Illegal upvar id: {:?}", upvar_id); } } } @@ -1253,14 +1333,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // link infer::ReborrowUpvar(span, *upvar_id) } - _ => { - infer::Reborrow(span) - } + _ => infer::Reborrow(span), }; - debug!("link_reborrowed_region: {:?} <= {:?}", - borrow_region, - ref_region); + debug!( + "link_reborrowed_region: {:?} <= {:?}", + borrow_region, ref_region + ); self.sub_regions(cause, borrow_region, ref_region); // If we end up needing to recurse and establish a region link @@ -1272,10 +1351,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // borrowck requires a unique path to the `&mut` reference but not // necessarily a *mutable* path. let new_borrow_kind = match borrow_kind { - ty::ImmBorrow => - ty::ImmBorrow, - ty::MutBorrow | ty::UniqueImmBorrow => - ty::UniqueImmBorrow + ty::ImmBorrow => ty::ImmBorrow, + ty::MutBorrow | ty::UniqueImmBorrow => ty::UniqueImmBorrow, }; // Decide whether we need to recurse and link any regions within @@ -1329,16 +1406,20 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { /// Checks that the values provided for type/region arguments in a given /// expression are well-formed and in-scope. - fn substs_wf_in_scope(&mut self, - origin: infer::ParameterOrigin, - substs: &Substs<'tcx>, - expr_span: Span, - expr_region: ty::Region<'tcx>) { - debug!("substs_wf_in_scope(substs={:?}, \ - expr_region={:?}, \ - origin={:?}, \ - expr_span={:?})", - substs, expr_region, origin, expr_span); + fn substs_wf_in_scope( + &mut self, + origin: infer::ParameterOrigin, + substs: &Substs<'tcx>, + expr_span: Span, + expr_region: ty::Region<'tcx>, + ) { + debug!( + "substs_wf_in_scope(substs={:?}, \ + expr_region={:?}, \ + origin={:?}, \ + expr_span={:?})", + substs, expr_region, origin, expr_span + ); let origin = infer::ParameterInScope(origin, expr_span); From 1fb4ea91f2844b3a2b0efbfd3cefe16bdb946d23 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 15:02:46 -0400 Subject: [PATCH 02/34] outlives/env: rustfmt --- src/librustc/infer/outlives/env.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 7f59a6794efbd..568e4412fdba2 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{GenericKind, InferCtxt}; use infer::outlives::free_region_map::FreeRegionMap; +use infer::{GenericKind, InferCtxt}; use traits::query::outlives_bounds::{self, OutlivesBound}; use ty::{self, Ty}; @@ -167,9 +167,11 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { for outlives_bound in outlives_bounds { debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); match outlives_bound { - OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) | - OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { - infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); + OutlivesBound::RegionSubRegion(r_a @ &ty::ReEarlyBound(_), &ty::ReVar(vid_b)) + | OutlivesBound::RegionSubRegion(r_a @ &ty::ReFree(_), &ty::ReVar(vid_b)) => { + infcx + .expect("no infcx provided but region vars found") + .add_given(r_a, vid_b); } OutlivesBound::RegionSubParam(r_a, param_b) => { self.region_bound_pairs From 84563dac88f1bda275ccbc8bb66067fd6cbbdcfe Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 15:11:53 -0400 Subject: [PATCH 03/34] build up a map of the region-bound pairs for each body-id Presently unused. --- src/librustc/infer/outlives/env.rs | 65 ++++++++++++++++++++++----- src/librustc_typeck/check/regionck.rs | 9 +++- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 568e4412fdba2..6808e9dc9e33e 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -10,11 +10,11 @@ use infer::outlives::free_region_map::FreeRegionMap; use infer::{GenericKind, InferCtxt}; -use traits::query::outlives_bounds::{self, OutlivesBound}; -use ty::{self, Ty}; - +use rustc_data_structures::fx::FxHashMap; use syntax::ast; use syntax_pos::Span; +use traits::query::outlives_bounds::{self, OutlivesBound}; +use ty::{self, Ty}; /// The `OutlivesEnvironment` collects information about what outlives /// what in a given type-checking setting. For example, if we have a @@ -39,15 +39,51 @@ use syntax_pos::Span; pub struct OutlivesEnvironment<'tcx> { param_env: ty::ParamEnv<'tcx>, free_region_map: FreeRegionMap<'tcx>, - region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>, + + // Contains, for each body B that we are checking (that is, the fn + // item, but also any nested closures), the set of implied region + // bounds that are in scope in that particular body. + // + // Example: + // + // ``` + // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) { + // bar(x, y, |y: &'b T| { .. } // body B1) + // } // body B0 + // ``` + // + // Here, for body B0, the list would be `[T: 'a]`, because we + // infer that `T` must outlive `'a` from the implied bounds on the + // fn declaration. + // + // For the body B1, the list would be `[T: 'a, T: 'b]`, because we + // also can see that -- within the closure body! -- `T` must + // outlive `'b`. This is not necessarily true outside the closure + // body, since the closure may never be called. + // + // We collect this map as we descend the tree. We then use the + // results when proving outlives obligations like `T: 'x` later + // (e.g., if `T: 'x` must be proven within the body B1, then we + // know it is true if either `'a: 'x` or `'b: 'x`). + region_bound_pairs_map: FxHashMap>, + + // Used to compute `region_bound_pairs_map`: contains the set of + // in-scope region-bound pairs thus far. + region_bound_pairs_accum: RegionBoundPairs<'tcx>, } +/// "Region-bound pairs" tracks outlives relations that are known to +/// be true, either because of explicit where clauses like `T: 'a` or +/// because of implied bounds. +pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>; + impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { let mut env = OutlivesEnvironment { param_env, free_region_map: FreeRegionMap::new(), - region_bound_pairs: vec![], + region_bound_pairs_map: FxHashMap::default(), + region_bound_pairs_accum: vec![], }; env.add_outlives_bounds(None, outlives_bounds::explicit_outlives_bounds(param_env)); @@ -62,7 +98,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { /// Borrows current value of the `region_bound_pairs`. pub fn region_bound_pairs(&self) -> &[(ty::Region<'tcx>, GenericKind<'tcx>)] { - &self.region_bound_pairs + &self.region_bound_pairs_accum } /// Returns ownership of the `free_region_map`. @@ -108,12 +144,12 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { /// similar leaks around givens that seem equally suspicious, to /// be honest. --nmatsakis pub fn push_snapshot_pre_closure(&self) -> usize { - self.region_bound_pairs.len() + self.region_bound_pairs_accum.len() } /// See `push_snapshot_pre_closure`. pub fn pop_snapshot_post_closure(&mut self, len: usize) { - self.region_bound_pairs.truncate(len); + self.region_bound_pairs_accum.truncate(len); } /// This method adds "implied bounds" into the outlives environment. @@ -149,6 +185,15 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { } } + /// Save the current set of region-bound pairs under the given `body_id`. + pub fn save_implied_bounds(&mut self, body_id: ast::NodeId) { + let old = self.region_bound_pairs_map.insert( + body_id, + self.region_bound_pairs_accum.clone(), + ); + assert!(old.is_none()); + } + /// Processes outlives bounds that are known to hold, whether from implied or other sources. /// /// The `infcx` parameter is optional; if the implied bounds may @@ -174,11 +219,11 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { .add_given(r_a, vid_b); } OutlivesBound::RegionSubParam(r_a, param_b) => { - self.region_bound_pairs + self.region_bound_pairs_accum .push((r_a, GenericKind::Param(param_b))); } OutlivesBound::RegionSubProjection(r_a, projection_b) => { - self.region_bound_pairs + self.region_bound_pairs_accum .push((r_a, GenericKind::Projection(projection_b))); } OutlivesBound::RegionSubRegion(r_a, r_b) => { diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index e3f9d6cd97ca8..aad474d0a8859 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -130,6 +130,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Subject(subject), self.param_env, ); + + // There are no add'l implied bounds when checking a + // standalone expr (e.g., the `E` in a type like `[u32; E]`). + rcx.outlives_environment.save_implied_bounds(id); + if self.err_count_since_creation() == 0 { // regionck assumes typeck succeeded rcx.visit_body(body); @@ -155,6 +160,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ); rcx.outlives_environment .add_implied_bounds(self, wf_tys, item_id, span); + rcx.outlives_environment.save_implied_bounds(item_id); rcx.visit_region_obligations(item_id); rcx.resolve_regions_and_report_errors(); } @@ -308,7 +314,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { id: ast::NodeId, // the id of the fn itself body: &'gcx hir::Body, span: Span, - ) { + ) { // When we enter a function, we can derive debug!("visit_fn_body(id={})", id); @@ -349,6 +355,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { body_id.node_id, span, ); + self.outlives_environment.save_implied_bounds(body_id.node_id); self.link_fn_args( region::Scope { id: body.value.hir_id.local_id, From 1830c9e8f946f20c9c81f46f8b5de29de24d58a3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 15:43:26 -0400 Subject: [PATCH 04/34] use a `UnlessNll` flag to consolidate error reporting paths --- src/librustc/infer/error_reporting/mod.rs | 6 ++-- src/librustc/infer/mod.rs | 41 +++++++---------------- src/librustc/traits/mod.rs | 8 ++++- src/librustc_typeck/check/dropck.rs | 4 +-- src/librustc_typeck/check/regionck.rs | 19 ++++------- src/librustc_typeck/coherence/builtin.rs | 2 ++ 6 files changed, 32 insertions(+), 48 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index cf76c3b7e02df..fe9e00f8a6038 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -55,7 +55,7 @@ //! ported to this system, and which relies on string concatenation at the //! time of error detection. -use infer; +use infer::{self, UnlessNll}; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; use super::region_constraints::GenericKind; use super::lexical_region_resolve::RegionResolutionError; @@ -298,13 +298,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { &self, region_scope_tree: ®ion::ScopeTree, errors: &Vec>, - will_later_be_reported_by_nll: bool, + unless_nll: UnlessNll, ) { debug!("report_region_errors(): {} errors to start", errors.len()); // If the errors will later be reported by NLL, choose wether to display them or not based // on the borrowck mode - if will_later_be_reported_by_nll { + if unless_nll.0 { match self.tcx.borrowck_mode() { // If we're on AST or Migrate mode, report AST region errors BorrowckMode::Ast | BorrowckMode::Migrate => {}, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index e628a3458f9e6..6e20194dff9b7 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -80,6 +80,16 @@ pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult = Result; // "fixup result" +/// A flag that is given when running region resolution: if true, it +/// indicates that we should not report the region errors to the user +/// if NLL is enabled, since NLL will also detect them (and do a +/// better job of it). +/// +/// Currently, NLL only runs on HIR bodies, so you should use `false` +/// unless you are region-checking a `hir::Body` (basically, a fn or +/// expression). +pub struct UnlessNll(pub bool); + pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'gcx, 'tcx>, @@ -1039,34 +1049,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context: DefId, region_map: ®ion::ScopeTree, outlives_env: &OutlivesEnvironment<'tcx>, - ) { - self.resolve_regions_and_report_errors_inner( - region_context, - region_map, - outlives_env, - false, - ) - } - - /// Like `resolve_regions_and_report_errors`, but skips error - /// reporting if NLL is enabled. This is used for fn bodies where - /// the same error may later be reported by the NLL-based - /// inference. - pub fn resolve_regions_and_report_errors_unless_nll( - &self, - region_context: DefId, - region_map: ®ion::ScopeTree, - outlives_env: &OutlivesEnvironment<'tcx>, - ) { - self.resolve_regions_and_report_errors_inner(region_context, region_map, outlives_env, true) - } - - fn resolve_regions_and_report_errors_inner( - &self, - region_context: DefId, - region_map: ®ion::ScopeTree, - outlives_env: &OutlivesEnvironment<'tcx>, - will_later_be_reported_by_nll: bool, + unless_nll: UnlessNll, ) { assert!( self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), @@ -1098,7 +1081,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // this infcx was in use. This is totally hokey but // otherwise we have a hard time separating legit region // errors from silly ones. - self.report_region_errors(region_map, &errors, will_later_be_reported_by_nll); + self.report_region_errors(region_map, &errors, unless_nll); } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index edf7772f2f78e..6c0fe157a2a65 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -20,6 +20,7 @@ pub use self::ObligationCauseCode::*; use chalk_engine; use hir; use hir::def_id::DefId; +use infer::UnlessNll; use infer::outlives::env::OutlivesEnvironment; use middle::region; use mir::interpret::ConstEvalErr; @@ -715,7 +716,12 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // cares about declarations like `'a: 'b`. let outlives_env = OutlivesEnvironment::new(elaborated_env); - infcx.resolve_regions_and_report_errors(region_context, ®ion_scope_tree, &outlives_env); + infcx.resolve_regions_and_report_errors( + region_context, + ®ion_scope_tree, + &outlives_env, + UnlessNll(false), + ); let predicates = match infcx.fully_resolve(&predicates) { Ok(predicates) => predicates, diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 9d3cbf910e059..c6e0da309a40f 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -11,7 +11,7 @@ use check::regionck::RegionCtxt; use hir::def_id::DefId; -use rustc::infer::{self, InferOk}; +use rustc::infer::{self, InferOk, UnlessNll}; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::ty::subst::{Subst, Substs, UnpackedKind}; @@ -128,7 +128,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( // conservative. -nmatsakis let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env); + infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env, UnlessNll(false)); Ok(()) }) } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index aad474d0a8859..3c462c1ae7a25 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -88,7 +88,7 @@ use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; -use rustc::infer; +use rustc::infer::{self, UnlessNll}; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::ty::adjustment; use rustc::ty::subst::Substs; @@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_body(body); rcx.visit_region_obligations(id); } - rcx.resolve_regions_and_report_errors_unless_nll(); + rcx.resolve_regions_and_report_errors(UnlessNll(true)); assert!(self.tables.borrow().free_region_map.is_empty()); self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); @@ -162,7 +162,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .add_implied_bounds(self, wf_tys, item_id, span); rcx.outlives_environment.save_implied_bounds(item_id); rcx.visit_region_obligations(item_id); - rcx.resolve_regions_and_report_errors(); + rcx.resolve_regions_and_report_errors(UnlessNll(false)); } /// Region check a function body. Not invoked on closures, but @@ -190,7 +190,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_fn_body(fn_id, body, self.tcx.hir.span(fn_id)); } - rcx.resolve_regions_and_report_errors_unless_nll(); + rcx.resolve_regions_and_report_errors(UnlessNll(true)); // In this mode, we also copy the free-region-map into the // tables of the enclosing fcx. In the other regionck modes @@ -399,19 +399,12 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ); } - fn resolve_regions_and_report_errors(&self) { + fn resolve_regions_and_report_errors(&self, unless_nll: UnlessNll) { self.fcx.resolve_regions_and_report_errors( self.subject_def_id, &self.region_scope_tree, &self.outlives_environment, - ); - } - - fn resolve_regions_and_report_errors_unless_nll(&self) { - self.fcx.resolve_regions_and_report_errors_unless_nll( - self.subject_def_id, - &self.region_scope_tree, - &self.outlives_environment, + unless_nll, ); } diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index efc35fad820c8..4def76e892289 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -11,6 +11,7 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. +use rustc::infer::UnlessNll; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::middle::lang_items::UnsizeTraitLangItem; @@ -396,6 +397,7 @@ pub fn coerce_unsized_info<'a, 'gcx>(gcx: TyCtxt<'a, 'gcx, 'gcx>, impl_did, ®ion_scope_tree, &outlives_env, + UnlessNll(false), ); CoerceUnsizedInfo { From 672e071a42f565c6d0611784c2f913f6dc9287d6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 17:28:47 -0400 Subject: [PATCH 05/34] change `RegionObligation` to store a `SubregionOrigin` --- src/librustc/infer/mod.rs | 2 +- src/librustc/infer/outlives/obligations.rs | 55 ++++++++++++++-------- src/librustc/traits/auto_trait.rs | 24 ++++------ src/librustc/traits/fulfill.rs | 26 +++++----- 4 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 6e20194dff9b7..bd8c49620518f 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -418,7 +418,7 @@ pub enum FixupError { pub struct RegionObligation<'tcx> { pub sub_region: ty::Region<'tcx>, pub sup_type: Ty<'tcx>, - pub cause: ObligationCause<'tcx>, + pub origin: SubregionOrigin<'tcx>, } impl fmt::Display for FixupError { diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 817280b97e031..700881ea5329f 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -72,10 +72,10 @@ use hir::def_id::DefId; use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; use syntax::ast; -use traits; +use traits::{self, ObligationCause}; use ty::outlives::Component; use ty::subst::{Subst, Substs}; -use ty::{self, Ty, TyCtxt, TypeFoldable}; +use ty::{self, Region, Ty, TyCtxt, TypeFoldable}; impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// Registers that the given region obligation must be resolved @@ -98,6 +98,26 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { .push((body_id, obligation)); } + pub fn register_region_obligation_with_cause( + &self, + sup_type: Ty<'tcx>, + sub_region: Region<'tcx>, + cause: &ObligationCause<'tcx>, + ) { + let origin = SubregionOrigin::from_obligation_cause(cause, || { + infer::RelateParamBound(cause.span, sup_type) + }); + + self.register_region_obligation( + cause.body_id, + RegionObligation { + sup_type, + sub_region, + origin, + }, + ); + } + /// Trait queries just want to pass back type obligations "as is" pub fn take_registered_region_obligations(&self) -> Vec<(ast::NodeId, RegionObligation<'tcx>)> { ::std::mem::replace(&mut *self.region_obligations.borrow_mut(), vec![]) @@ -154,10 +174,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len()); { let mut r_o = self.region_obligations.borrow_mut(); - my_region_obligations.extend( - r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id) - .map(|(_, obligation)| obligation) - ); + my_region_obligations.extend(r_o.drain_filter(|(ro_body_id, _)| { + *ro_body_id == body_id + }).map(|(_, obligation)| obligation)); } let outlives = &mut TypeOutlives::new( @@ -171,18 +190,14 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { for RegionObligation { sup_type, sub_region, - cause, + origin, } in my_region_obligations { debug!( - "process_registered_region_obligations: sup_type={:?} sub_region={:?} cause={:?}", - sup_type, sub_region, cause + "process_registered_region_obligations: sup_type={:?} sub_region={:?} origin={:?}", + sup_type, sub_region, origin ); - let origin = SubregionOrigin::from_obligation_cause(&cause, || { - infer::RelateParamBound(cause.span, sup_type) - }); - let sup_type = self.resolve_type_vars_if_possible(&sup_type); outlives.type_must_outlive(origin, sup_type, sub_region); } @@ -302,7 +317,8 @@ where let origin = origin.clone(); match component { Component::Region(region1) => { - self.delegate.push_sub_region_constraint(origin, region, region1); + self.delegate + .push_sub_region_constraint(origin, region, region1); } Component::Param(param_ty) => { self.param_ty_must_outlive(origin, region, param_ty); @@ -405,7 +421,8 @@ where } for r in projection_ty.substs.regions() { - self.delegate.push_sub_region_constraint(origin.clone(), region, r); + self.delegate + .push_sub_region_constraint(origin.clone(), region, r); } return; @@ -497,8 +514,7 @@ where ); // see the extensive comment in projection_must_outlive - let ty = self - .tcx + let ty = self.tcx .mk_projection(projection_ty.item_def_id, projection_ty.substs); let recursive_bound = self.recursive_type_bound(ty); @@ -506,7 +522,9 @@ where } fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - let mut bounds = ty.walk_shallow().map(|subty| self.type_bound(subty)).collect::>(); + let mut bounds = ty.walk_shallow() + .map(|subty| self.type_bound(subty)) + .collect::>(); let mut regions = ty.regions(); regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions @@ -674,4 +692,3 @@ impl<'cx, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'gcx, ' self.verify_generic_bound(origin, kind, a, bound) } } - diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs index 4bed3c5935cd7..aa9230a06e0ce 100644 --- a/src/librustc/traits/auto_trait.rs +++ b/src/librustc/traits/auto_trait.rs @@ -19,7 +19,7 @@ use std::collections::VecDeque; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use infer::region_constraints::{Constraint, RegionConstraintData}; -use infer::{InferCtxt, RegionObligation}; +use infer::InferCtxt; use ty::fold::TypeFolder; use ty::{Region, RegionVid}; @@ -693,23 +693,17 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { binder.map_bound_ref(|pred| pred.0).no_late_bound_regions(), ) { (None, Some(t_a)) => { - select.infcx().register_region_obligation( - ast::DUMMY_NODE_ID, - RegionObligation { - sup_type: t_a, - sub_region: select.infcx().tcx.types.re_static, - cause: dummy_cause.clone(), - }, + select.infcx().register_region_obligation_with_cause( + t_a, + select.infcx().tcx.types.re_static, + &dummy_cause, ); } (Some(ty::OutlivesPredicate(t_a, r_b)), _) => { - select.infcx().register_region_obligation( - ast::DUMMY_NODE_ID, - RegionObligation { - sup_type: t_a, - sub_region: r_b, - cause: dummy_cause.clone(), - }, + select.infcx().register_region_obligation_with_cause( + t_a, + r_b, + &dummy_cause, ); } _ => {} diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 707af02acbf47..19ee2c1aabfa4 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use infer::{RegionObligation, InferCtxt}; +use infer::InferCtxt; use mir::interpret::GlobalId; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, ToPredicate}; use ty::error::ExpectedFound; @@ -372,13 +372,11 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, Some(t_a) => { let r_static = self.selcx.tcx().types.re_static; if self.register_region_obligations { - self.selcx.infcx().register_region_obligation( - obligation.cause.body_id, - RegionObligation { - sup_type: t_a, - sub_region: r_static, - cause: obligation.cause.clone(), - }); + self.selcx.infcx().register_region_obligation_with_cause( + t_a, + r_static, + &obligation.cause, + ); } ProcessResult::Changed(vec![]) } @@ -387,13 +385,11 @@ impl<'a, 'b, 'gcx, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'gcx, // If there aren't, register the obligation. Some(ty::OutlivesPredicate(t_a, r_b)) => { if self.register_region_obligations { - self.selcx.infcx().register_region_obligation( - obligation.cause.body_id, - RegionObligation { - sup_type: t_a, - sub_region: r_b, - cause: obligation.cause.clone() - }); + self.selcx.infcx().register_region_obligation_with_cause( + t_a, + r_b, + &obligation.cause, + ); } ProcessResult::Changed(vec![]) } From d5d2232838d9d0fd1a933a87b7659b65918dfb52 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 17:34:08 -0400 Subject: [PATCH 06/34] auto_trait.rs: rustfmt --- src/librustc/traits/auto_trait.rs | 64 +++++++++++++++---------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs index aa9230a06e0ce..b445adea558a1 100644 --- a/src/librustc/traits/auto_trait.rs +++ b/src/librustc/traits/auto_trait.rs @@ -227,7 +227,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .iter() .filter_map(|param| match param.kind { ty::GenericParamDefKind::Lifetime => Some(param.name.to_string()), - _ => None + _ => None, }) .collect(); @@ -359,8 +359,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { &Err(SelectionError::Unimplemented) => { if self.is_of_param(pred.skip_binder().trait_ref.substs) { already_visited.remove(&pred); - self.add_user_pred(&mut user_computed_preds, - ty::Predicate::Trait(pred.clone())); + self.add_user_pred( + &mut user_computed_preds, + ty::Predicate::Trait(pred.clone()), + ); predicates.push_back(pred); } else { debug!( @@ -418,8 +420,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { // under which a type implements an auto trait. A trait predicate involving // a HRTB means that the type needs to work with any choice of lifetime, // not just one specific lifetime (e.g. 'static). - fn add_user_pred<'c>(&self, user_computed_preds: &mut FxHashSet>, - new_pred: ty::Predicate<'c>) { + fn add_user_pred<'c>( + &self, + user_computed_preds: &mut FxHashSet>, + new_pred: ty::Predicate<'c>, + ) { let mut should_add_new = true; user_computed_preds.retain(|&old_pred| { match (&new_pred, old_pred) { @@ -431,20 +436,19 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { if !new_substs.types().eq(old_substs.types()) { // We can't compare lifetimes if the types are different, // so skip checking old_pred - return true + return true; } - for (new_region, old_region) in new_substs - .regions() - .zip(old_substs.regions()) { - + for (new_region, old_region) in + new_substs.regions().zip(old_substs.regions()) + { match (new_region, old_region) { // If both predicates have an 'ReLateBound' (a HRTB) in the // same spot, we do nothing ( ty::RegionKind::ReLateBound(_, _), - ty::RegionKind::ReLateBound(_, _) - ) => {}, + ty::RegionKind::ReLateBound(_, _), + ) => {} (ty::RegionKind::ReLateBound(_, _), _) => { // The new predicate has a HRTB in a spot where the old @@ -458,7 +462,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { // so we return 'false' to remove the old predicate from // user_computed_preds return false; - }, + } (_, ty::RegionKind::ReLateBound(_, _)) => { // This is the opposite situation as the previous arm - the // old predicate has a HRTB lifetime in a place where the @@ -471,10 +475,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } } } - }, + } _ => {} } - return true + return true; }); if should_add_new { @@ -513,28 +517,20 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { match constraint { &Constraint::VarSubVar(r1, r2) => { { - let deps1 = vid_map - .entry(RegionTarget::RegionVid(r1)) - .or_default(); + let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default(); deps1.larger.insert(RegionTarget::RegionVid(r2)); } - let deps2 = vid_map - .entry(RegionTarget::RegionVid(r2)) - .or_default(); + let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default(); deps2.smaller.insert(RegionTarget::RegionVid(r1)); } &Constraint::RegSubVar(region, vid) => { { - let deps1 = vid_map - .entry(RegionTarget::Region(region)) - .or_default(); + let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default(); deps1.larger.insert(RegionTarget::RegionVid(vid)); } - let deps2 = vid_map - .entry(RegionTarget::RegionVid(vid)) - .or_default(); + let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); deps2.smaller.insert(RegionTarget::Region(region)); } &Constraint::VarSubReg(vid, region) => { @@ -542,15 +538,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } &Constraint::RegSubReg(r1, r2) => { { - let deps1 = vid_map - .entry(RegionTarget::Region(r1)) - .or_default(); + let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default(); deps1.larger.insert(RegionTarget::Region(r2)); } - let deps2 = vid_map - .entry(RegionTarget::Region(r2)) - .or_default(); + let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default(); deps2.smaller.insert(RegionTarget::Region(r1)); } } @@ -683,7 +675,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { } } &ty::Predicate::RegionOutlives(ref binder) => { - if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { + if select + .infcx() + .region_outlives_predicate(&dummy_cause, binder) + .is_err() + { return false; } } From 2f31698c263c6fc3a6e5d0ba07b2ea9b882a5c9c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 17:45:00 -0400 Subject: [PATCH 07/34] use `RegionBoundPairs` type alias --- src/librustc/infer/outlives/env.rs | 2 +- src/librustc/infer/outlives/obligations.rs | 9 +++++---- src/librustc/traits/auto_trait.rs | 2 +- .../nll/type_check/constraint_conversion.rs | 5 +++-- src/librustc_mir/borrow_check/nll/type_check/mod.rs | 10 +++++----- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 6808e9dc9e33e..8deabacab5c83 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -97,7 +97,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { } /// Borrows current value of the `region_bound_pairs`. - pub fn region_bound_pairs(&self) -> &[(ty::Region<'tcx>, GenericKind<'tcx>)] { + pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { &self.region_bound_pairs_accum } diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 700881ea5329f..e1f7dcfd90acb 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -71,6 +71,7 @@ use hir::def_id::DefId; use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use infer::outlives::env::RegionBoundPairs; use syntax::ast; use traits::{self, ObligationCause}; use ty::outlives::Component; @@ -158,7 +159,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// processed. pub fn process_registered_region_obligations( &self, - region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, body_id: ast::NodeId, @@ -207,7 +208,7 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// registered in advance. pub fn type_must_outlive( &self, - region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, origin: infer::SubregionOrigin<'tcx>, @@ -240,7 +241,7 @@ where // of these fields. delegate: D, tcx: TyCtxt<'cx, 'gcx, 'tcx>, - region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, } @@ -269,7 +270,7 @@ where pub fn new( delegate: D, tcx: TyCtxt<'cx, 'gcx, 'tcx>, - region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, ) -> Self { diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs index b445adea558a1..e177382430b3b 100644 --- a/src/librustc/traits/auto_trait.rs +++ b/src/librustc/traits/auto_trait.rs @@ -239,7 +239,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { .collect(); for id in body_ids { - infcx.process_registered_region_obligations(&[], None, full_env.clone(), id); + infcx.process_registered_region_obligations(&vec![], None, full_env.clone(), id); } let region_data = infcx diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 430c8d673921b..220cbfe4fff3d 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -15,6 +15,7 @@ use borrow_check::nll::region_infer::{RegionTest, TypeTest}; use borrow_check::nll::type_check::Locations; use borrow_check::nll::universal_regions::UniversalRegions; use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc::infer::region_constraints::{GenericKind, VerifyBound}; use rustc::infer::{self, SubregionOrigin}; @@ -26,7 +27,7 @@ crate struct ConstraintConversion<'a, 'gcx: 'tcx, 'tcx: 'a> { tcx: TyCtxt<'a, 'gcx, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, location_table: &'a LocationTable, - region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, locations: Locations, @@ -41,7 +42,7 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { tcx: TyCtxt<'a, 'gcx, 'tcx>, universal_regions: &'a UniversalRegions<'tcx>, location_table: &'a LocationTable, - region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, locations: Locations, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 70687d0efa402..d4719064c28a1 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -29,7 +29,7 @@ use dataflow::MaybeInitializedPlaces; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryRegionConstraint; -use rustc::infer::region_constraints::GenericKind; +use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; @@ -182,7 +182,7 @@ fn type_check_internal<'a, 'gcx, 'tcx, R>( mir_def_id: DefId, param_env: ty::ParamEnv<'gcx>, mir: &'a Mir<'tcx>, - region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: Option>, borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>, universal_region_relations: Option<&'a UniversalRegionRelations<'tcx>>, @@ -693,7 +693,7 @@ struct TypeChecker<'a, 'gcx: 'tcx, 'tcx: 'a> { last_span: Span, mir: &'a Mir<'tcx>, mir_def_id: DefId, - region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: Option>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>, @@ -802,7 +802,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { mir: &'a Mir<'tcx>, mir_def_id: DefId, param_env: ty::ParamEnv<'gcx>, - region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], + region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: Option>, borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>, universal_region_relations: Option<&'a UniversalRegionRelations<'tcx>>, @@ -2232,7 +2232,7 @@ impl MirPass for TypeckMir { def_id, param_env, mir, - &[], + &vec![], None, None, None, From 9e305a8a23dc6f8e71dadc2670448ed14bf5962d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 17:48:33 -0400 Subject: [PATCH 08/34] type_check/mod.rs: rustfmt --- .../borrow_check/nll/type_check/mod.rs | 237 ++++++++---------- 1 file changed, 100 insertions(+), 137 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index d4719064c28a1..5ebaec1874aef 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -17,12 +17,12 @@ use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, Outlives use borrow_check::nll::facts::AllFacts; use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements}; use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest}; +use borrow_check::nll::renumber; use borrow_check::nll::type_check::free_region_relations::{ CreateResult, UniversalRegionRelations, }; use borrow_check::nll::universal_regions::UniversalRegions; use borrow_check::nll::ToRegionVid; -use borrow_check::nll::renumber; use dataflow::move_paths::MoveData; use dataflow::FlowAtLocation; use dataflow::MaybeInitializedPlaces; @@ -35,13 +35,13 @@ use rustc::mir::interpret::EvalErrorKind::BoundsCheck; use rustc::mir::tcx::PlaceTy; use rustc::mir::visit::{PlaceContext, Visitor}; use rustc::mir::*; -use rustc::traits::{ObligationCause, PredicateObligations}; use rustc::traits::query::type_op; use rustc::traits::query::type_op::custom::CustomTypeOp; use rustc::traits::query::{Fallible, NoSolution}; +use rustc::traits::{ObligationCause, PredicateObligations}; use rustc::ty::fold::TypeFoldable; -use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind}; use rustc::ty::subst::Subst; +use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind}; use std::fmt; use std::rc::Rc; use syntax_pos::{Span, DUMMY_SP}; @@ -161,11 +161,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( Some(&mut borrowck_context), Some(&universal_region_relations), |cx| { - cx.equate_inputs_and_outputs( - mir, - universal_regions, - &normalized_inputs_and_output, - ); + cx.equate_inputs_and_outputs(mir, universal_regions, &normalized_inputs_and_output); liveness::generate(cx, mir, elements, flow_inits, move_data); }, ); @@ -377,14 +373,12 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { debug!("sanitize_constant: expected_ty={:?}", constant.literal.ty); - if let Err(terr) = self.cx - .eq_types( - constant.literal.ty, - constant.ty, - location.to_locations(), - ConstraintCategory::Boring, - ) - { + if let Err(terr) = self.cx.eq_types( + constant.literal.ty, + constant.ty, + location.to_locations(), + ConstraintCategory::Boring, + ) { span_mirbug!( self, constant, @@ -429,12 +423,10 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { let sty = self.sanitize_type(place, sty); let ty = self.tcx().type_of(def_id); let ty = self.cx.normalize(ty, location); - if let Err(terr) = self.cx.eq_types( - ty, - sty, - location.to_locations(), - ConstraintCategory::Boring, - ) { + if let Err(terr) = + self.cx + .eq_types(ty, sty, location.to_locations(), ConstraintCategory::Boring) + { span_mirbug!( self, place, @@ -955,66 +947,55 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let tcx = infcx.tcx; let param_env = self.param_env; let parent_def_id = infcx.tcx.closure_base_def_id(self.mir_def_id); - let opaque_type_map = - self.fully_perform_op( - locations, - category, - CustomTypeOp::new( - |infcx| { - let mut obligations = ObligationAccumulator::default(); - - let dummy_body_id = ObligationCause::dummy().body_id; - let (output_ty, opaque_type_map) = - obligations.add(infcx.instantiate_opaque_types( - parent_def_id, - dummy_body_id, - param_env, - &anon_ty, - )); + let opaque_type_map = self.fully_perform_op( + locations, + category, + CustomTypeOp::new( + |infcx| { + let mut obligations = ObligationAccumulator::default(); + + let dummy_body_id = ObligationCause::dummy().body_id; + let (output_ty, opaque_type_map) = + obligations.add(infcx.instantiate_opaque_types( + parent_def_id, + dummy_body_id, + param_env, + &anon_ty, + )); + debug!( + "eq_opaque_type_and_type: \ + instantiated output_ty={:?} \ + opaque_type_map={:#?} \ + revealed_ty={:?}", + output_ty, opaque_type_map, revealed_ty + ); + obligations.add(infcx + .at(&ObligationCause::dummy(), param_env) + .eq(output_ty, revealed_ty)?); + + for (&opaque_def_id, opaque_decl) in &opaque_type_map { + let opaque_defn_ty = tcx.type_of(opaque_def_id); + let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs); + let opaque_defn_ty = renumber::renumber_regions(infcx, &opaque_defn_ty); debug!( - "eq_opaque_type_and_type: \ - instantiated output_ty={:?} \ - opaque_type_map={:#?} \ - revealed_ty={:?}", - output_ty, - opaque_type_map, - revealed_ty - ); - obligations.add( - infcx - .at(&ObligationCause::dummy(), param_env) - .eq(output_ty, revealed_ty)?, + "eq_opaque_type_and_type: concrete_ty={:?} opaque_defn_ty={:?}", + opaque_decl.concrete_ty, opaque_defn_ty ); + obligations.add(infcx + .at(&ObligationCause::dummy(), param_env) + .eq(opaque_decl.concrete_ty, opaque_defn_ty)?); + } - for (&opaque_def_id, opaque_decl) in &opaque_type_map { - let opaque_defn_ty = tcx.type_of(opaque_def_id); - let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs); - let opaque_defn_ty = renumber::renumber_regions( - infcx, - &opaque_defn_ty, - ); - debug!( - "eq_opaque_type_and_type: concrete_ty={:?} opaque_defn_ty={:?}", - opaque_decl.concrete_ty, - opaque_defn_ty - ); - obligations.add( - infcx - .at(&ObligationCause::dummy(), param_env) - .eq(opaque_decl.concrete_ty, opaque_defn_ty)?, - ); - } - - debug!("eq_opaque_type_and_type: equated"); + debug!("eq_opaque_type_and_type: equated"); - Ok(InferOk { - value: Some(opaque_type_map), - obligations: obligations.into_vec(), - }) - }, - || "input_output".to_string(), - ), - )?; + Ok(InferOk { + value: Some(opaque_type_map), + obligations: obligations.into_vec(), + }) + }, + || "input_output".to_string(), + ), + )?; let universal_region_relations = match self.universal_region_relations { Some(rel) => rel, @@ -1035,7 +1016,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { infcx.constrain_opaque_type( opaque_def_id, &opaque_decl, - universal_region_relations + universal_region_relations, ); Ok(InferOk { value: (), @@ -1073,12 +1054,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let place_ty = place.ty(mir, tcx).to_ty(tcx); let rv_ty = rv.ty(mir, tcx); - if let Err(terr) = self.sub_types_or_anon( - rv_ty, - place_ty, - location.to_locations(), - category, - ) { + if let Err(terr) = + self.sub_types_or_anon(rv_ty, place_ty, location.to_locations(), category) + { span_mirbug!( self, stmt, @@ -1117,7 +1095,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.prove_trait_ref( trait_ref, location.to_locations(), - ConstraintCategory::SizedBound, + ConstraintCategory::SizedBound, ); } } @@ -1148,15 +1126,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } StatementKind::AscribeUserType(ref place, variance, c_ty) => { let place_ty = place.ty(mir, tcx).to_ty(tcx); - if let Err(terr) = - self.relate_type_and_user_type( - place_ty, - variance, - c_ty, - Locations::All(stmt.source_info.span), - ConstraintCategory::TypeAnnotation, - ) - { + if let Err(terr) = self.relate_type_and_user_type( + place_ty, + variance, + c_ty, + Locations::All(stmt.source_info.span), + ConstraintCategory::TypeAnnotation, + ) { span_mirbug!( self, stmt, @@ -1208,12 +1184,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let rv_ty = value.ty(mir, tcx); let locations = term_location.to_locations(); - if let Err(terr) = self.sub_types( - rv_ty, - place_ty, - locations, - ConstraintCategory::Assignment, - ) { + if let Err(terr) = + self.sub_types(rv_ty, place_ty, locations, ConstraintCategory::Assignment) + { span_mirbug!( self, term, @@ -1327,8 +1300,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ty, term_location.to_locations(), ConstraintCategory::Return, - ) - { + ) { span_mirbug!( self, term, @@ -1366,12 +1338,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let locations = term_location.to_locations(); - if let Err(terr) = self.sub_types_or_anon( - sig.output(), - dest_ty, - locations, - category, - ) { + if let Err(terr) = + self.sub_types_or_anon(sig.output(), dest_ty, locations, category) + { span_mirbug!( self, term, @@ -1539,12 +1508,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } - fn check_local( - &mut self, - mir: &Mir<'tcx>, - local: Local, - local_decl: &LocalDecl<'tcx>, - ) { + fn check_local(&mut self, mir: &Mir<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) { match mir.local_kind(local) { LocalKind::ReturnPointer | LocalKind::Arg => { // return values of normal functions are required to be @@ -1713,13 +1677,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ConstraintCategory::Cast, ) { span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); } } @@ -1739,13 +1703,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ConstraintCategory::Cast, ) { span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); } } @@ -1768,13 +1732,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ConstraintCategory::Cast, ) { span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_fn_ptr_from, - ty, - terr - ); + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ty_fn_ptr_from, + ty, + terr + ); } } @@ -2277,4 +2241,3 @@ impl<'tcx> ObligationAccumulator<'tcx> { self.obligations } } - From b2e0215a1f38e38f260f9295aca98e99f6f6a400 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Sep 2018 17:48:51 -0400 Subject: [PATCH 09/34] apply `process_registered_region_obligations` at the end of regionck We used to apply it repeatedly as we went, relying on the current value of the `region_bound_pairs_accum` vector. But now we save those values into a map, so we can just process all the registered region obligations at the end. --- src/librustc/infer/outlives/env.rs | 4 +- src/librustc/infer/outlives/obligations.rs | 53 ++++++++++--------- src/librustc/traits/auto_trait.rs | 11 ++-- src/librustc_typeck/check/regionck.rs | 23 ++++---- src/test/ui/issues/issue-16922.nll.stderr | 11 ---- src/test/ui/issues/issue-16922.stderr | 2 +- .../object-lifetime-default-elision.stderr | 2 +- .../regions/region-object-lifetime-2.stderr | 2 +- .../regions-trait-object-subtyping.stderr | 2 +- 9 files changed, 49 insertions(+), 61 deletions(-) delete mode 100644 src/test/ui/issues/issue-16922.nll.stderr diff --git a/src/librustc/infer/outlives/env.rs b/src/librustc/infer/outlives/env.rs index 8deabacab5c83..631ff58d3e37e 100644 --- a/src/librustc/infer/outlives/env.rs +++ b/src/librustc/infer/outlives/env.rs @@ -97,8 +97,8 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> { } /// Borrows current value of the `region_bound_pairs`. - pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { - &self.region_bound_pairs_accum + pub fn region_bound_pairs_map(&self) -> &FxHashMap> { + &self.region_bound_pairs_map } /// Returns ownership of the `free_region_map`. diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index e1f7dcfd90acb..899699d1113d3 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -70,8 +70,9 @@ //! imply that `'b: 'a`. use hir::def_id::DefId; -use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; use infer::outlives::env::RegionBoundPairs; +use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use rustc_data_structures::fx::FxHashMap; use syntax::ast; use traits::{self, ObligationCause}; use ty::outlives::Component; @@ -159,10 +160,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { /// processed. pub fn process_registered_region_obligations( &self, - region_bound_pairs: &RegionBoundPairs<'tcx>, + region_bound_pairs_map: &FxHashMap>, implicit_region_bound: Option>, param_env: ty::ParamEnv<'tcx>, - body_id: ast::NodeId, ) { assert!( !self.in_snapshot.get(), @@ -171,28 +171,16 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { debug!("process_registered_region_obligations()"); - // pull out the region obligations with the given `body_id` (leaving the rest) - let mut my_region_obligations = Vec::with_capacity(self.region_obligations.borrow().len()); - { - let mut r_o = self.region_obligations.borrow_mut(); - my_region_obligations.extend(r_o.drain_filter(|(ro_body_id, _)| { - *ro_body_id == body_id - }).map(|(_, obligation)| obligation)); - } + let my_region_obligations = self.take_registered_region_obligations(); - let outlives = &mut TypeOutlives::new( - self, - self.tcx, - region_bound_pairs, - implicit_region_bound, - param_env, - ); - - for RegionObligation { - sup_type, - sub_region, - origin, - } in my_region_obligations + for ( + body_id, + RegionObligation { + sup_type, + sub_region, + origin, + }, + ) in my_region_obligations { debug!( "process_registered_region_obligations: sup_type={:?} sub_region={:?} origin={:?}", @@ -200,7 +188,22 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { ); let sup_type = self.resolve_type_vars_if_possible(&sup_type); - outlives.type_must_outlive(origin, sup_type, sub_region); + + if let Some(region_bound_pairs) = region_bound_pairs_map.get(&body_id) { + let outlives = &mut TypeOutlives::new( + self, + self.tcx, + ®ion_bound_pairs, + implicit_region_bound, + param_env, + ); + outlives.type_must_outlive(origin, sup_type, sub_region); + } else { + self.tcx.sess.delay_span_bug( + origin.span(), + &format!("no region-bound-pairs for {:?}", body_id), + ) + } } } diff --git a/src/librustc/traits/auto_trait.rs b/src/librustc/traits/auto_trait.rs index e177382430b3b..8f106a0812538 100644 --- a/src/librustc/traits/auto_trait.rs +++ b/src/librustc/traits/auto_trait.rs @@ -16,10 +16,9 @@ use super::*; use std::collections::hash_map::Entry; use std::collections::VecDeque; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; - use infer::region_constraints::{Constraint, RegionConstraintData}; use infer::InferCtxt; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use ty::fold::TypeFolder; use ty::{Region, RegionVid}; @@ -231,16 +230,14 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { }) .collect(); - let body_ids: FxHashSet<_> = infcx + let body_id_map: FxHashMap<_, _> = infcx .region_obligations .borrow() .iter() - .map(|&(id, _)| id) + .map(|&(id, _)| (id, vec![])) .collect(); - for id in body_ids { - infcx.process_registered_region_obligations(&vec![], None, full_env.clone(), id); - } + infcx.process_registered_region_obligations(&body_id_map, None, full_env.clone()); let region_data = infcx .borrow_region_constraints() diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 3c462c1ae7a25..bc3525adad562 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -88,7 +88,7 @@ use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; -use rustc::infer::{self, UnlessNll}; +use rustc::infer::{self, RegionObligation, UnlessNll}; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::ty::adjustment; use rustc::ty::subst::Substs; @@ -390,16 +390,15 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { // which, when processed, might generate new region // obligations. So make sure we process those. self.select_all_obligations_or_error(); + } + fn resolve_regions_and_report_errors(&self, unless_nll: UnlessNll) { self.infcx.process_registered_region_obligations( - self.outlives_environment.region_bound_pairs(), + self.outlives_environment.region_bound_pairs_map(), self.implicit_region_bound, self.param_env, - self.body_id, ); - } - fn resolve_regions_and_report_errors(&self, unless_nll: UnlessNll) { self.fcx.resolve_regions_and_report_errors( self.subject_def_id, &self.region_scope_tree, @@ -1042,13 +1041,13 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { ty: Ty<'tcx>, region: ty::Region<'tcx>, ) { - self.infcx.type_must_outlive( - self.outlives_environment.region_bound_pairs(), - self.implicit_region_bound, - self.param_env, - origin, - ty, - region, + self.infcx.register_region_obligation( + self.body_id, + RegionObligation { + sub_region: region, + sup_type: ty, + origin, + }, ); } diff --git a/src/test/ui/issues/issue-16922.nll.stderr b/src/test/ui/issues/issue-16922.nll.stderr deleted file mode 100644 index 3406d53489660..0000000000000 --- a/src/test/ui/issues/issue-16922.nll.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0621]: explicit lifetime required in the type of `value` - --> $DIR/issue-16922.rs:14:5 - | -LL | fn foo(value: &T) -> Box { - | -- help: add explicit lifetime `'static` to the type of `value`: `&'static T` -LL | Box::new(value) as Box - | ^^^^^^^^^^^^^^^ lifetime `'static` required - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0621`. diff --git a/src/test/ui/issues/issue-16922.stderr b/src/test/ui/issues/issue-16922.stderr index e70869eb1838d..3406d53489660 100644 --- a/src/test/ui/issues/issue-16922.stderr +++ b/src/test/ui/issues/issue-16922.stderr @@ -4,7 +4,7 @@ error[E0621]: explicit lifetime required in the type of `value` LL | fn foo(value: &T) -> Box { | -- help: add explicit lifetime `'static` to the type of `value`: `&'static T` LL | Box::new(value) as Box - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required + | ^^^^^^^^^^^^^^^ lifetime `'static` required error: aborting due to previous error diff --git a/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr b/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr index 45e7d8451f71c..b8c22583ff8d3 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-elision.stderr @@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the lifetime 'a as defined on the funct | LL | fn load3<'a,'b>(ss: &'a SomeTrait) -> &'b SomeTrait { | ^^ -note: ...so that the type `(dyn SomeTrait + 'a)` is not borrowed for too long +note: ...so that reference does not outlive borrowed content --> $DIR/object-lifetime-default-elision.rs:81:5 | LL | ss diff --git a/src/test/ui/regions/region-object-lifetime-2.stderr b/src/test/ui/regions/region-object-lifetime-2.stderr index e396680f99b4b..314f43585fedb 100644 --- a/src/test/ui/regions/region-object-lifetime-2.stderr +++ b/src/test/ui/regions/region-object-lifetime-2.stderr @@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the lifetime 'a as defined on the funct | LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a Foo) -> &'b () { | ^^ -note: ...so that the type `(dyn Foo + 'a)` is not borrowed for too long +note: ...so that reference does not outlive borrowed content --> $DIR/region-object-lifetime-2.rs:20:5 | LL | x.borrowed() //~ ERROR cannot infer diff --git a/src/test/ui/regions/regions-trait-object-subtyping.stderr b/src/test/ui/regions/regions-trait-object-subtyping.stderr index 1b078855efdbb..a281b36946bfd 100644 --- a/src/test/ui/regions/regions-trait-object-subtyping.stderr +++ b/src/test/ui/regions/regions-trait-object-subtyping.stderr @@ -26,7 +26,7 @@ note: first, the lifetime cannot outlive the lifetime 'a as defined on the funct | LL | fn foo3<'a,'b>(x: &'a mut Dummy) -> &'b mut Dummy { | ^^ -note: ...so that the type `(dyn Dummy + 'a)` is not borrowed for too long +note: ...so that reference does not outlive borrowed content --> $DIR/regions-trait-object-subtyping.rs:25:5 | LL | x //~ ERROR lifetime bound not satisfied From b0e1fec33251c02080403f89a11a78ba452464c0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 14 Sep 2018 17:13:27 -0400 Subject: [PATCH 10/34] break out the code that computes VerifyBounds Later, we'll defer this work until a separate phase. --- src/librustc/infer/outlives/mod.rs | 1 + src/librustc/infer/outlives/obligations.rs | 230 +----------------- src/librustc/infer/outlives/verify.rs | 263 +++++++++++++++++++++ 3 files changed, 276 insertions(+), 218 deletions(-) create mode 100644 src/librustc/infer/outlives/verify.rs diff --git a/src/librustc/infer/outlives/mod.rs b/src/librustc/infer/outlives/mod.rs index bb3703b215732..282aef786f08a 100644 --- a/src/librustc/infer/outlives/mod.rs +++ b/src/librustc/infer/outlives/mod.rs @@ -13,3 +13,4 @@ pub mod env; pub mod free_region_map; pub mod obligations; +pub mod verify; diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 899699d1113d3..c19c8f57d2d07 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -69,14 +69,13 @@ //! might later infer `?U` to something like `&'b u32`, which would //! imply that `'b: 'a`. -use hir::def_id::DefId; use infer::outlives::env::RegionBoundPairs; +use infer::outlives::verify::VerifyBoundCx; use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; use rustc_data_structures::fx::FxHashMap; use syntax::ast; -use traits::{self, ObligationCause}; +use traits::ObligationCause; use ty::outlives::Component; -use ty::subst::{Subst, Substs}; use ty::{self, Region, Ty, TyCtxt, TypeFoldable}; impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> { @@ -244,9 +243,7 @@ where // of these fields. delegate: D, tcx: TyCtxt<'cx, 'gcx, 'tcx>, - region_bound_pairs: &'cx RegionBoundPairs<'tcx>, - implicit_region_bound: Option>, - param_env: ty::ParamEnv<'tcx>, + verify_bound: VerifyBoundCx<'cx, 'gcx, 'tcx>, } pub trait TypeOutlivesDelegate<'tcx> { @@ -280,9 +277,12 @@ where Self { delegate, tcx, - region_bound_pairs, - implicit_region_bound, - param_env, + verify_bound: VerifyBoundCx::new( + tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + ), } } @@ -357,8 +357,8 @@ where region, param_ty, origin ); - let verify_bound = self.param_bound(param_ty); let generic = GenericKind::Param(param_ty); + let verify_bound = self.verify_bound.generic_bound(generic); self.delegate .push_verify(origin, generic, region, verify_bound); } @@ -391,7 +391,7 @@ where // Compute the bounds we can derive from the environment or trait // definition. We know that the projection outlives all the // regions in this list. - let env_bounds = self.projection_declared_bounds(projection_ty); + let env_bounds = self.verify_bound.projection_declared_bounds(projection_ty); debug!("projection_must_outlive: env_bounds={:?}", env_bounds); @@ -463,217 +463,11 @@ where // projection outlive; in some cases, this may add insufficient // edges into the inference graph, leading to inference failures // even though a satisfactory solution exists. - let verify_bound = self.projection_bound(env_bounds, projection_ty); let generic = GenericKind::Projection(projection_ty); + let verify_bound = self.verify_bound.generic_bound(generic); self.delegate .push_verify(origin, generic.clone(), region, verify_bound); } - - fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - match ty.sty { - ty::Param(p) => self.param_bound(p), - ty::Projection(data) => { - let declared_bounds = self.projection_declared_bounds(data); - self.projection_bound(declared_bounds, data) - } - _ => self.recursive_type_bound(ty), - } - } - - fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { - debug!("param_bound(param_ty={:?})", param_ty); - - let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); - - // Add in the default bound of fn body that applies to all in - // scope type parameters: - param_bounds.extend(self.implicit_region_bound); - - VerifyBound::AnyRegion(param_bounds) - } - - fn projection_declared_bounds( - &self, - projection_ty: ty::ProjectionTy<'tcx>, - ) -> Vec> { - // First assemble bounds from where clauses and traits. - - let mut declared_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); - - declared_bounds - .extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty)); - - declared_bounds - } - - fn projection_bound( - &self, - declared_bounds: Vec>, - projection_ty: ty::ProjectionTy<'tcx>, - ) -> VerifyBound<'tcx> { - debug!( - "projection_bound(declared_bounds={:?}, projection_ty={:?})", - declared_bounds, projection_ty - ); - - // see the extensive comment in projection_must_outlive - let ty = self.tcx - .mk_projection(projection_ty.item_def_id, projection_ty.substs); - let recursive_bound = self.recursive_type_bound(ty); - - VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) - } - - fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { - let mut bounds = ty.walk_shallow() - .map(|subty| self.type_bound(subty)) - .collect::>(); - - let mut regions = ty.regions(); - regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push(VerifyBound::AllRegions(regions)); - - // remove bounds that must hold, since they are not interesting - bounds.retain(|b| !b.must_hold()); - - if bounds.len() == 1 { - bounds.pop().unwrap() - } else { - VerifyBound::AllBounds(bounds) - } - } - - fn declared_generic_bounds_from_env( - &self, - generic: GenericKind<'tcx>, - ) -> Vec> { - let tcx = self.tcx; - - // To start, collect bounds from user environment. Note that - // parameter environments are already elaborated, so we don't - // have to worry about that. Comparing using `==` is a bit - // dubious for projections, but it will work for simple cases - // like `T` and `T::Item`. It may not work as well for things - // like `>::Item`. - let generic_ty = generic.to_ty(tcx); - let c_b = self.param_env.caller_bounds; - let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b); - - // Next, collect regions we scraped from the well-formedness - // constraints in the fn signature. To do that, we walk the list - // of known relations from the fn ctxt. - // - // This is crucial because otherwise code like this fails: - // - // fn foo<'a, A>(x: &'a A) { x.bar() } - // - // The problem is that the type of `x` is `&'a A`. To be - // well-formed, then, A must be lower-generic by `'a`, but we - // don't know that this holds from first principles. - for &(r, p) in self.region_bound_pairs { - debug!("generic={:?} p={:?}", generic, p); - if generic == p { - param_bounds.push(r); - } - } - - param_bounds - } - - /// Given a projection like `>::Bar`, returns any bounds - /// declared in the trait definition. For example, if the trait were - /// - /// ```rust - /// trait Foo<'a> { - /// type Bar: 'a; - /// } - /// ``` - /// - /// then this function would return `'x`. This is subject to the - /// limitations around higher-ranked bounds described in - /// `region_bounds_declared_on_associated_item`. - fn declared_projection_bounds_from_trait( - &self, - projection_ty: ty::ProjectionTy<'tcx>, - ) -> Vec> { - debug!("projection_bounds(projection_ty={:?})", projection_ty); - let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id); - for r in &mut bounds { - *r = r.subst(self.tcx, projection_ty.substs); - } - bounds - } - - /// Given the def-id of an associated item, returns any region - /// bounds attached to that associated item from the trait definition. - /// - /// For example: - /// - /// ```rust - /// trait Foo<'a> { - /// type Bar: 'a; - /// } - /// ``` - /// - /// If we were given the def-id of `Foo::Bar`, we would return - /// `'a`. You could then apply the substitutions from the - /// projection to convert this into your namespace. This also - /// works if the user writes `where >::Bar: 'a` on - /// the trait. In fact, it works by searching for just such a - /// where-clause. - /// - /// It will not, however, work for higher-ranked bounds like: - /// - /// ```rust - /// trait Foo<'a, 'b> - /// where for<'x> >::Bar: 'x - /// { - /// type Bar; - /// } - /// ``` - /// - /// This is for simplicity, and because we are not really smart - /// enough to cope with such bounds anywhere. - fn region_bounds_declared_on_associated_item( - &self, - assoc_item_def_id: DefId, - ) -> Vec> { - let tcx = self.tcx; - let assoc_item = tcx.associated_item(assoc_item_def_id); - let trait_def_id = assoc_item.container.assert_trait(); - let trait_predicates = tcx.predicates_of(trait_def_id); - let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); - let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); - self.collect_outlives_from_predicate_list( - identity_proj, - traits::elaborate_predicates(tcx, trait_predicates.predicates), - ) - } - - /// Searches through a predicate list for a predicate `T: 'a`. - /// - /// Careful: does not elaborate predicates, and just uses `==` - /// when comparing `ty` for equality, so `ty` must be something - /// that does not involve inference variables and where you - /// otherwise want a precise match. - fn collect_outlives_from_predicate_list( - &self, - ty: Ty<'tcx>, - predicates: I, - ) -> Vec> - where - I: IntoIterator, - P: AsRef>, - { - predicates - .into_iter() - .filter_map(|p| p.as_ref().to_opt_type_outlives()) - .filter_map(|p| p.no_late_bound_regions()) - .filter(|p| p.0 == ty) - .map(|p| p.1) - .collect() - } } impl<'cx, 'gcx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'gcx, 'tcx> { diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs new file mode 100644 index 0000000000000..3a1e205793104 --- /dev/null +++ b/src/librustc/infer/outlives/verify.rs @@ -0,0 +1,263 @@ +// Copyright 2012-2014 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. + +use hir::def_id::DefId; +use infer::outlives::env::RegionBoundPairs; +use infer::{GenericKind, VerifyBound}; +use traits; +use ty::subst::{Subst, Substs}; +use ty::{self, Ty, TyCtxt}; + +/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` +/// obligation into a series of `'a: 'b` constraints and "verifys", as +/// described on the module comment. The final constraints are emitted +/// via a "delegate" of type `D` -- this is usually the `infcx`, which +/// accrues them into the `region_obligations` code, but for NLL we +/// use something else. +pub struct VerifyBoundCx<'cx, 'gcx: 'tcx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, +} + +impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { + pub fn new( + tcx: TyCtxt<'cx, 'gcx, 'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, + implicit_region_bound: Option>, + param_env: ty::ParamEnv<'tcx>, + ) -> Self { + Self { + tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + } + } + + /// Returns a "verify bound" that encodes what we know about + /// `generic` and the regions it outlives. + pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> { + match generic { + GenericKind::Param(param_ty) => self.param_bound(param_ty), + GenericKind::Projection(projection_ty) => self.projection_bound(projection_ty), + } + } + + fn type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + match ty.sty { + ty::Param(p) => self.param_bound(p), + ty::Projection(data) => self.projection_bound(data), + _ => self.recursive_type_bound(ty), + } + } + + fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + debug!("param_bound(param_ty={:?})", param_ty); + + let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); + + // Add in the default bound of fn body that applies to all in + // scope type parameters: + param_bounds.extend(self.implicit_region_bound); + + VerifyBound::AnyRegion(param_bounds) + } + + /// Searches the where clauses in scope for regions that + /// `projection_ty` is known to outlive. Currently requires an + /// exact match. + pub fn projection_declared_bounds( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + // First assemble bounds from where clauses and traits. + + let mut declared_bounds = + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); + + declared_bounds + .extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty)); + + declared_bounds + } + + pub fn projection_bound( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> VerifyBound<'tcx> { + debug!( + "projection_bound(projection_ty={:?})", + projection_ty + ); + + let declared_bounds = self.projection_declared_bounds(projection_ty); + + debug!("projection_bound: declared_bounds = {:?}", declared_bounds); + + // see the extensive comment in projection_must_outlive + let ty = self.tcx + .mk_projection(projection_ty.item_def_id, projection_ty.substs); + let recursive_bound = self.recursive_type_bound(ty); + + VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) + } + + fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { + let mut bounds = ty.walk_shallow() + .map(|subty| self.type_bound(subty)) + .collect::>(); + + let mut regions = ty.regions(); + regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions + bounds.push(VerifyBound::AllRegions(regions)); + + // remove bounds that must hold, since they are not interesting + bounds.retain(|b| !b.must_hold()); + + if bounds.len() == 1 { + bounds.pop().unwrap() + } else { + VerifyBound::AllBounds(bounds) + } + } + + fn declared_generic_bounds_from_env( + &self, + generic: GenericKind<'tcx>, + ) -> Vec> { + let tcx = self.tcx; + + // To start, collect bounds from user environment. Note that + // parameter environments are already elaborated, so we don't + // have to worry about that. Comparing using `==` is a bit + // dubious for projections, but it will work for simple cases + // like `T` and `T::Item`. It may not work as well for things + // like `>::Item`. + let generic_ty = generic.to_ty(tcx); + let c_b = self.param_env.caller_bounds; + let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b); + + // Next, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must be lower-generic by `'a`, but we + // don't know that this holds from first principles. + for &(r, p) in self.region_bound_pairs { + debug!("generic={:?} p={:?}", generic, p); + if generic == p { + param_bounds.push(r); + } + } + + param_bounds + } + + /// Given a projection like `>::Bar`, returns any bounds + /// declared in the trait definition. For example, if the trait were + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// then this function would return `'x`. This is subject to the + /// limitations around higher-ranked bounds described in + /// `region_bounds_declared_on_associated_item`. + fn declared_projection_bounds_from_trait( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + debug!("projection_bounds(projection_ty={:?})", projection_ty); + let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id); + for r in &mut bounds { + *r = r.subst(self.tcx, projection_ty.substs); + } + bounds + } + + /// Given the def-id of an associated item, returns any region + /// bounds attached to that associated item from the trait definition. + /// + /// For example: + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// If we were given the def-id of `Foo::Bar`, we would return + /// `'a`. You could then apply the substitutions from the + /// projection to convert this into your namespace. This also + /// works if the user writes `where >::Bar: 'a` on + /// the trait. In fact, it works by searching for just such a + /// where-clause. + /// + /// It will not, however, work for higher-ranked bounds like: + /// + /// ```rust + /// trait Foo<'a, 'b> + /// where for<'x> >::Bar: 'x + /// { + /// type Bar; + /// } + /// ``` + /// + /// This is for simplicity, and because we are not really smart + /// enough to cope with such bounds anywhere. + fn region_bounds_declared_on_associated_item( + &self, + assoc_item_def_id: DefId, + ) -> Vec> { + let tcx = self.tcx; + let assoc_item = tcx.associated_item(assoc_item_def_id); + let trait_def_id = assoc_item.container.assert_trait(); + let trait_predicates = tcx.predicates_of(trait_def_id); + let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); + let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); + self.collect_outlives_from_predicate_list( + identity_proj, + traits::elaborate_predicates(tcx, trait_predicates.predicates), + ) + } + + /// Searches through a predicate list for a predicate `T: 'a`. + /// + /// Careful: does not elaborate predicates, and just uses `==` + /// when comparing `ty` for equality, so `ty` must be something + /// that does not involve inference variables and where you + /// otherwise want a precise match. + fn collect_outlives_from_predicate_list( + &self, + ty: Ty<'tcx>, + predicates: I, + ) -> Vec> + where + I: IntoIterator, + P: AsRef>, + { + predicates + .into_iter() + .filter_map(|p| p.as_ref().to_opt_type_outlives()) + .filter_map(|p| p.no_late_bound_regions()) + .filter(|p| p.0 == ty) + .map(|p| p.1) + .collect() + } +} From 9b63dcc7731e1023c525e795ba36472a3e4bd21b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 12:15:48 -0400 Subject: [PATCH 11/34] split out getting the declared bounds from the env versus trait Right now, we just concatenate them --- src/librustc/infer/outlives/obligations.rs | 30 ++++++++++++--------- src/librustc/infer/outlives/verify.rs | 31 ++++++++++++++-------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index c19c8f57d2d07..2db1c5e3d303d 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -391,16 +391,18 @@ where // Compute the bounds we can derive from the environment or trait // definition. We know that the projection outlives all the // regions in this list. - let env_bounds = self.verify_bound.projection_declared_bounds(projection_ty); + let mut declared_bounds = self.verify_bound + .projection_declared_bounds_from_env(projection_ty); - debug!("projection_must_outlive: env_bounds={:?}", env_bounds); + declared_bounds.extend( + self.verify_bound + .projection_declared_bounds_from_trait(projection_ty), + ); - // If we know that the projection outlives 'static, then we're - // done here. - if env_bounds.contains(&&ty::ReStatic) { - debug!("projection_must_outlive: 'static as declared bound"); - return; - } + debug!( + "projection_must_outlive: declared_bounds={:?}", + declared_bounds + ); // If declared bounds list is empty, the only applicable rule is // OutlivesProjectionComponent. If there are inference variables, @@ -417,7 +419,7 @@ where // inference variables, we use a verify constraint instead of adding // edges, which winds up enforcing the same condition. let needs_infer = projection_ty.needs_infer(); - if env_bounds.is_empty() && needs_infer { + if declared_bounds.is_empty() && needs_infer { debug!("projection_must_outlive: no declared bounds"); for component_ty in projection_ty.substs.types() { @@ -440,8 +442,12 @@ where // the requirement that `'b:'r` // - OutlivesProjectionComponent: this would require `'b:'r` in addition to // other conditions - if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) { - let unique_bound = env_bounds[0]; + if !declared_bounds.is_empty() + && declared_bounds[1..] + .iter() + .all(|b| *b == declared_bounds[0]) + { + let unique_bound = declared_bounds[0]; debug!( "projection_must_outlive: unique declared bound = {:?}", unique_bound @@ -449,7 +455,7 @@ where if projection_ty .substs .regions() - .any(|r| env_bounds.contains(&r)) + .any(|r| declared_bounds.contains(&r)) { debug!("projection_must_outlive: unique declared bound appears in trait ref"); self.delegate diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index 3a1e205793104..cd4f3c3e5dc33 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -72,22 +72,26 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { VerifyBound::AnyRegion(param_bounds) } + /// Given a projection like `T::Item`, searches the environment + /// for where-clauses like `T::Item: 'a`. Returns the set of + /// regions `'a` that it finds. This is a "conservative" check -- + /// it may not find all applicable bounds, but all the bounds it + /// returns can be relied upon. + pub fn projection_declared_bounds_from_env( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec> { + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)) + } + /// Searches the where clauses in scope for regions that /// `projection_ty` is known to outlive. Currently requires an /// exact match. - pub fn projection_declared_bounds( + pub fn projection_declared_bounds_from_trait( &self, projection_ty: ty::ProjectionTy<'tcx>, ) -> Vec> { - // First assemble bounds from where clauses and traits. - - let mut declared_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); - - declared_bounds - .extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty)); - - declared_bounds + self.declared_projection_bounds_from_trait(projection_ty) } pub fn projection_bound( @@ -99,7 +103,12 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { projection_ty ); - let declared_bounds = self.projection_declared_bounds(projection_ty); + let mut declared_bounds = + self.projection_declared_bounds_from_env(projection_ty); + + declared_bounds.extend( + self.projection_declared_bounds_from_trait(projection_ty) + ); debug!("projection_bound: declared_bounds = {:?}", declared_bounds); From db0e62692eed43a5efb7024a69e36c6288fff859 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 13:03:34 -0400 Subject: [PATCH 12/34] introduce a "comparison fn" instead of always use `==` --- src/librustc/infer/outlives/verify.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index cd4f3c3e5dc33..a4fd4d4b88047 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -153,7 +153,10 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { // like `>::Item`. let generic_ty = generic.to_ty(tcx); let c_b = self.param_env.caller_bounds; - let mut param_bounds = self.collect_outlives_from_predicate_list(generic_ty, c_b); + let mut param_bounds = self.collect_outlives_from_predicate_list( + |ty| ty == generic_ty, + c_b, + ); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -241,7 +244,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); self.collect_outlives_from_predicate_list( - identity_proj, + |ty| ty == identity_proj, traits::elaborate_predicates(tcx, trait_predicates.predicates), ) } @@ -252,20 +255,16 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { /// when comparing `ty` for equality, so `ty` must be something /// that does not involve inference variables and where you /// otherwise want a precise match. - fn collect_outlives_from_predicate_list( + fn collect_outlives_from_predicate_list( &self, - ty: Ty<'tcx>, - predicates: I, - ) -> Vec> - where - I: IntoIterator, - P: AsRef>, - { + compare_ty: impl Fn(Ty<'tcx>) -> bool, + predicates: impl IntoIterator>>, + ) -> Vec> { predicates .into_iter() .filter_map(|p| p.as_ref().to_opt_type_outlives()) .filter_map(|p| p.no_late_bound_regions()) - .filter(|p| p.0 == ty) + .filter(|p| compare_ty(p.0)) .map(|p| p.1) .collect() } From 13d579bd624005592fdf99a98d3e9f347eaec55a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 13:28:19 -0400 Subject: [PATCH 13/34] introduce the idea of an "approximate match" In fact, however, we currently always give back the same exact answers as ever. But we don't rely on them being exact anymore. --- src/librustc/infer/outlives/obligations.rs | 69 +++++++++++----------- src/librustc/infer/outlives/verify.rs | 26 ++++++-- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 2db1c5e3d303d..88ac1e8590d12 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -388,20 +388,24 @@ where // rule might not apply (but another rule might). For now, we err // on the side of adding too few edges into the graph. - // Compute the bounds we can derive from the environment or trait - // definition. We know that the projection outlives all the - // regions in this list. - let mut declared_bounds = self.verify_bound - .projection_declared_bounds_from_env(projection_ty); - - declared_bounds.extend( - self.verify_bound - .projection_declared_bounds_from_trait(projection_ty), + // Compute the bounds we can derive from the environment. This + // is an "approximate" match -- in some cases, these bounds + // may not apply. + let approx_env_bounds = self.verify_bound + .projection_approx_declared_bounds_from_env(projection_ty); + debug!( + "projection_must_outlive: approx_env_bounds={:?}", + approx_env_bounds ); + // Compute the bounds we can derive from the trait definition. + // These are guaranteed to apply, no matter the inference + // results. + let trait_bounds = self.verify_bound + .projection_declared_bounds_from_trait(projection_ty); debug!( - "projection_must_outlive: declared_bounds={:?}", - declared_bounds + "projection_must_outlive: trait_bounds={:?}", + trait_bounds ); // If declared bounds list is empty, the only applicable rule is @@ -419,7 +423,7 @@ where // inference variables, we use a verify constraint instead of adding // edges, which winds up enforcing the same condition. let needs_infer = projection_ty.needs_infer(); - if declared_bounds.is_empty() && needs_infer { + if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer { debug!("projection_must_outlive: no declared bounds"); for component_ty in projection_ty.substs.types() { @@ -434,34 +438,31 @@ where return; } - // If we find that there is a unique declared bound `'b`, and this bound - // appears in the trait reference, then the best action is to require that `'b:'r`, - // so do that. This is best no matter what rule we use: + // If we found a unique bound `'b` from the trait, and we + // found nothing else from the environment, then the best + // action is to require that `'b: 'r`, so do that. + // + // This is best no matter what rule we use: // - // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to - // the requirement that `'b:'r` - // - OutlivesProjectionComponent: this would require `'b:'r` in addition to - // other conditions - if !declared_bounds.is_empty() - && declared_bounds[1..] + // - OutlivesProjectionEnv: these would translate to the requirement that `'b:'r` + // - OutlivesProjectionTraitDef: these would translate to the requirement that `'b:'r` + // - OutlivesProjectionComponent: this would require `'b:'r` + // in addition to other conditions + if !trait_bounds.is_empty() + && trait_bounds[1..] .iter() - .all(|b| *b == declared_bounds[0]) + .chain(&approx_env_bounds) + .all(|b| *b == trait_bounds[0]) { - let unique_bound = declared_bounds[0]; + let unique_bound = trait_bounds[0]; debug!( - "projection_must_outlive: unique declared bound = {:?}", + "projection_must_outlive: unique trait bound = {:?}", unique_bound ); - if projection_ty - .substs - .regions() - .any(|r| declared_bounds.contains(&r)) - { - debug!("projection_must_outlive: unique declared bound appears in trait ref"); - self.delegate - .push_sub_region_constraint(origin.clone(), region, unique_bound); - return; - } + debug!("projection_must_outlive: unique declared bound appears in trait ref"); + self.delegate + .push_sub_region_constraint(origin.clone(), region, unique_bound); + return; } // Fallback to verifying after the fact that there exists a diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index a4fd4d4b88047..2390ea55c4475 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -74,10 +74,18 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { /// Given a projection like `T::Item`, searches the environment /// for where-clauses like `T::Item: 'a`. Returns the set of - /// regions `'a` that it finds. This is a "conservative" check -- - /// it may not find all applicable bounds, but all the bounds it - /// returns can be relied upon. - pub fn projection_declared_bounds_from_env( + /// regions `'a` that it finds. + /// + /// This is an "approximate" check -- it may not find all + /// applicable bounds, and not all the bounds it returns can be + /// relied upon. In particular, this check ignores region + /// identity. So, for example, if we have `>::Item` where `'0` is a region variable, and the + /// user has `>::Item: 'b` in the environment, then + /// the clause from the environment only applies if `'0 = 'a`, + /// which we don't know yet. But we would still include `'b` in + /// this list. + pub fn projection_approx_declared_bounds_from_env( &self, projection_ty: ty::ProjectionTy<'tcx>, ) -> Vec> { @@ -103,9 +111,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { projection_ty ); + // Search the env for where clauses like `P: 'a`. let mut declared_bounds = - self.projection_declared_bounds_from_env(projection_ty); + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); + // Extend with bounds that we can find from the trait. declared_bounds.extend( self.projection_declared_bounds_from_trait(projection_ty) ); @@ -139,6 +149,12 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { } } + /// Searches the environment for where-clauses like `G: 'a` where + /// `G` is either some type parameter `T` or a projection like + /// `T::Item`. Returns a vector of the `'a` bounds it can find. + /// + /// This is a conservative check -- it may not find all applicable + /// bounds, but all the bounds it returns can be relied upon. fn declared_generic_bounds_from_env( &self, generic: GenericKind<'tcx>, From 4b193cb92aee0d57dc7bfcf5e22e8e168eec6975 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 13:44:20 -0400 Subject: [PATCH 14/34] propagate the `compare_ty` fn further up --- src/librustc/infer/outlives/verify.rs | 35 +++++++++++++-------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index 2390ea55c4475..2275d7001461b 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -102,23 +102,15 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { self.declared_projection_bounds_from_trait(projection_ty) } - pub fn projection_bound( - &self, - projection_ty: ty::ProjectionTy<'tcx>, - ) -> VerifyBound<'tcx> { - debug!( - "projection_bound(projection_ty={:?})", - projection_ty - ); + pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> { + debug!("projection_bound(projection_ty={:?})", projection_ty); // Search the env for where clauses like `P: 'a`. let mut declared_bounds = self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); // Extend with bounds that we can find from the trait. - declared_bounds.extend( - self.projection_declared_bounds_from_trait(projection_ty) - ); + declared_bounds.extend(self.projection_declared_bounds_from_trait(projection_ty)); debug!("projection_bound: declared_bounds = {:?}", declared_bounds); @@ -158,6 +150,14 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { fn declared_generic_bounds_from_env( &self, generic: GenericKind<'tcx>, + ) -> Vec> { + let generic_ty = generic.to_ty(self.tcx); + self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty) + } + + fn declared_generic_bounds_from_env_with_compare_fn( + &self, + compare_ty: impl Fn(Ty<'tcx>) -> bool, ) -> Vec> { let tcx = self.tcx; @@ -167,12 +167,8 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { // dubious for projections, but it will work for simple cases // like `T` and `T::Item`. It may not work as well for things // like `>::Item`. - let generic_ty = generic.to_ty(tcx); let c_b = self.param_env.caller_bounds; - let mut param_bounds = self.collect_outlives_from_predicate_list( - |ty| ty == generic_ty, - c_b, - ); + let mut param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -186,8 +182,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { // well-formed, then, A must be lower-generic by `'a`, but we // don't know that this holds from first principles. for &(r, p) in self.region_bound_pairs { - debug!("generic={:?} p={:?}", generic, p); - if generic == p { + debug!( + "declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}", + (r, p) + ); + if compare_ty(p.to_ty(tcx)) { param_bounds.push(r); } } From 689b791422b7c429e4efcd224b7412c0e47e0638 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 14:00:33 -0400 Subject: [PATCH 15/34] use approx. bounds to decide whether to add outlives obligations Before, if we had a projection like `>::Bar: 'x` and a where clause like `>::Bar: 'a`, we considered those to have nothing to do with one another. Therefore, we would use the "overconstrained" path of adding `T: 'x` and `'0: 'x` requirements. We now do a "fuzzy" match where we erase regions first and hence we see the env bound `'a`. --- src/librustc/infer/outlives/verify.rs | 11 +++++++- .../projection-one-region-closure.rs | 3 +-- .../projection-one-region-closure.stderr | 26 +++++-------------- ...tion-one-region-trait-bound-closure.stderr | 14 +++++----- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index 2275d7001461b..80170a86c3841 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -89,7 +89,16 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { &self, projection_ty: ty::ProjectionTy<'tcx>, ) -> Vec> { - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)) + let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); + let erased_projection_ty = self.tcx.erase_regions(&projection_ty); + self.declared_generic_bounds_from_env_with_compare_fn( + |ty| if let ty::Projection(..) = ty.sty { + let erased_ty = self.tcx.erase_regions(&ty); + erased_ty == erased_projection_ty + } else { + false + }, + ) } /// Searches the where clauses in scope for regions that diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs index 995c4d6dc8133..d525135f759ce 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -85,8 +85,7 @@ where // can do better here with a more involved verification step. with_signature(cell, t, |cell, t| require(cell, t)); - //~^ ERROR the parameter type `T` may not live long enough - //~| ERROR + //~^ ERROR } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr index 4bd96ab4e713d..981320af3bb0e 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -119,8 +119,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) ] = note: number of external vids: 4 - = note: where T: '_#3r - = note: where '_#2r: '_#3r + = note: where >::AssocType: '_#3r note: No external requirements --> $DIR/projection-one-region-closure.rs:72:1 @@ -130,7 +129,7 @@ LL | | where LL | | T: Anything<'b>, LL | | T::AssocType: 'a, ... | -LL | | //~| ERROR +LL | | //~^ ERROR LL | | } | |_^ | @@ -140,27 +139,16 @@ LL | | } T ] -error[E0309]: the parameter type `T` may not live long enough +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-one-region-closure.rs:87:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider adding an explicit lifetime bound `T: ReEarlyBound(0, 'a)`... - -error: unsatisfied lifetime constraints - --> $DIR/projection-one-region-closure.rs:87:29 - | -LL | fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a` + = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... note: External requirements - --> $DIR/projection-one-region-closure.rs:99:29 + --> $DIR/projection-one-region-closure.rs:98:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +165,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where '_#2r: '_#3r note: No external requirements - --> $DIR/projection-one-region-closure.rs:93:1 + --> $DIR/projection-one-region-closure.rs:92:1 | LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -194,6 +182,6 @@ LL | | } T ] -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index ccf70a77bffc1..76d1eee5cb15b 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -101,7 +101,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); extern "rust-call" fn((std::cell::Cell<&'_#3r ()>, T)) ] = note: number of external vids: 4 - = note: where '_#2r: '_#3r + = note: where >::AssocType: '_#3r note: No external requirements --> $DIR/projection-one-region-trait-bound-closure.rs:62:1 @@ -121,16 +121,13 @@ LL | | } T ] -error: unsatisfied lifetime constraints +error[E0309]: the associated type `>::AssocType` may not live long enough --> $DIR/projection-one-region-trait-bound-closure.rs:77:29 | -LL | fn projection_outlives<'a, 'b, T>(cell: Cell<&'a ()>, t: T) - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... note: External requirements --> $DIR/projection-one-region-trait-bound-closure.rs:87:29 @@ -200,3 +197,4 @@ LL | | } error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0309`. From a4955d1b2cbddd8cc921422ba026a23fbd451f2f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 16:25:26 -0400 Subject: [PATCH 16/34] refactor NLL relate_tys to use Region internally, not RegionVid No functional change. --- .../borrow_check/nll/type_check/relate_tys.rs | 101 ++++++++---------- 1 file changed, 42 insertions(+), 59 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index 130b4b31d08e8..d919c5c02c645 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -10,15 +10,13 @@ use borrow_check::nll::constraints::{ConstraintCategory, OutlivesConstraint}; use borrow_check::nll::type_check::{BorrowCheckContext, Locations}; -use borrow_check::nll::universal_regions::UniversalRegions; -use borrow_check::nll::ToRegionVid; use rustc::infer::canonical::{Canonical, CanonicalVarInfos}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin}; use rustc::traits::query::Fallible; use rustc::ty::fold::{TypeFoldable, TypeVisitor}; use rustc::ty::relate::{self, Relate, RelateResult, TypeRelation}; use rustc::ty::subst::Kind; -use rustc::ty::{self, CanonicalTy, CanonicalVar, RegionVid, Ty, TyCtxt}; +use rustc::ty::{self, CanonicalTy, CanonicalVar, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::IndexVec; @@ -122,10 +120,10 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> { /// /// This field stores the instantiations for late-bound regions in /// the `a` type. - a_scopes: Vec, + a_scopes: Vec>, /// Same as `a_scopes`, but for the `b` type. - b_scopes: Vec, + b_scopes: Vec>, /// Where (and why) is this relation taking place? locations: Locations, @@ -152,13 +150,13 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> { #[derive(Clone, Debug)] struct ScopesAndKind<'tcx> { - scopes: Vec, + scopes: Vec>, kind: Kind<'tcx>, } #[derive(Clone, Debug, Default)] -struct BoundRegionScope { - map: FxHashMap, +struct BoundRegionScope<'tcx> { + map: FxHashMap>, } #[derive(Copy, Clone)] @@ -204,7 +202,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { &mut self, value: &ty::Binder>, universally_quantified: UniversallyQuantified, - ) -> BoundRegionScope { + ) -> BoundRegionScope<'tcx> { let mut scope = BoundRegionScope::default(); value.skip_binder().visit_with(&mut ScopeInstantiator { infcx: self.infcx, @@ -227,8 +225,8 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { debruijn: ty::DebruijnIndex, br: &ty::BoundRegion, first_free_index: ty::DebruijnIndex, - scopes: &[BoundRegionScope], - ) -> RegionVid { + scopes: &[BoundRegionScope<'tcx>], + ) -> ty::Region<'tcx> { // The debruijn index is a "reverse index" into the // scopes listing. So when we have INNERMOST (0), we // want the *last* scope pushed, and so forth. @@ -245,28 +243,25 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { /// with. Otherwise just return `r`. fn replace_bound_region( &self, - universal_regions: &UniversalRegions<'tcx>, r: ty::Region<'tcx>, first_free_index: ty::DebruijnIndex, - scopes: &[BoundRegionScope], - ) -> RegionVid { - match r { - ty::ReLateBound(debruijn, br) => { - Self::lookup_bound_region(*debruijn, br, first_free_index, scopes) - } - - ty::ReVar(v) => *v, - - _ => universal_regions.to_region_vid(r), + scopes: &[BoundRegionScope<'tcx>], + ) -> ty::Region<'tcx> { + if let ty::ReLateBound(debruijn, br) = r { + Self::lookup_bound_region(*debruijn, br, first_free_index, scopes) + } else { + r } } /// Push a new outlives requirement into our output set of /// constraints. - fn push_outlives(&mut self, sup: RegionVid, sub: RegionVid) { + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { debug!("push_outlives({:?}: {:?})", sup, sub); if let Some(borrowck_context) = &mut self.borrowck_context { + let sub = borrowck_context.universal_regions.to_region_vid(sub); + let sup = borrowck_context.universal_regions.to_region_vid(sup); borrowck_context .constraints .outlives_constraints @@ -316,10 +311,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { return result; } - fn generalize_value( - &self, - kind: Kind<'tcx>, - ) -> Kind<'tcx> { + fn generalize_value(&self, kind: Kind<'tcx>) -> Kind<'tcx> { TypeGeneralizer { type_rel: self, first_free_index: ty::INNERMOST, @@ -397,37 +389,30 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> a: ty::Region<'tcx>, b: ty::Region<'tcx>, ) -> RelateResult<'tcx, ty::Region<'tcx>> { - if let Some(&mut BorrowCheckContext { - universal_regions, .. - }) = self.borrowck_context - { - if let ty::ReCanonical(var) = a { - self.relate_var(*var, b.into())?; - return Ok(a); - } + if let ty::ReCanonical(var) = a { + self.relate_var(*var, b.into())?; + return Ok(a); + } - debug!( - "regions(a={:?}, b={:?}, variance={:?})", - a, b, self.ambient_variance - ); + debug!( + "regions(a={:?}, b={:?}, variance={:?})", + a, b, self.ambient_variance + ); - let v_a = - self.replace_bound_region(universal_regions, a, ty::INNERMOST, &self.a_scopes); - let v_b = - self.replace_bound_region(universal_regions, b, ty::INNERMOST, &self.b_scopes); + let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes); + let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes); - debug!("regions: v_a = {:?}", v_a); - debug!("regions: v_b = {:?}", v_b); + debug!("regions: v_a = {:?}", v_a); + debug!("regions: v_b = {:?}", v_b); - if self.ambient_covariance() { - // Covariance: a <= b. Hence, `b: a`. - self.push_outlives(v_b, v_a); - } + if self.ambient_covariance() { + // Covariance: a <= b. Hence, `b: a`. + self.push_outlives(v_b, v_a); + } - if self.ambient_contravariance() { - // Contravariant: b <= a. Hence, `a: b`. - self.push_outlives(v_a, v_b); - } + if self.ambient_contravariance() { + // Contravariant: b <= a. Hence, `a: b`. + self.push_outlives(v_a, v_b); } Ok(a) @@ -527,10 +512,8 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> // Reset ambient variance to contravariance. See the // covariant case above for an explanation. - let variance = ::std::mem::replace( - &mut self.ambient_variance, - ty::Variance::Contravariant, - ); + let variance = + ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); self.relate(a.skip_binder(), b.skip_binder())?; @@ -556,7 +539,7 @@ struct ScopeInstantiator<'cx, 'gcx: 'tcx, 'tcx: 'cx> { // The debruijn index of the scope we are instantiating. target_index: ty::DebruijnIndex, universally_quantified: UniversallyQuantified, - bound_region_scope: &'cx mut BoundRegionScope, + bound_region_scope: &'cx mut BoundRegionScope<'tcx>, } impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> { @@ -583,7 +566,7 @@ impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> { } else { NLLRegionVariableOrigin::Existential }; - infcx.next_nll_region_var(origin).to_region_vid() + infcx.next_nll_region_var(origin) }); } From c6f4513804c022150f24fd2c65747e807d430e19 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 16:36:59 -0400 Subject: [PATCH 17/34] extract out NLL-specific code from relate-tys into a delegate No functional change. --- .../borrow_check/nll/type_check/relate_tys.rs | 137 +++++++++++------- 1 file changed, 84 insertions(+), 53 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index d919c5c02c645..f2934985f33e7 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -32,10 +32,8 @@ pub(super) fn sub_types<'tcx>( debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations); TypeRelating::new( infcx, + NllTypeRelatingDelegate::new(borrowck_context, locations, category), ty::Variance::Covariant, - locations, - category, - borrowck_context, ty::List::empty(), ).relate(&a, &b)?; Ok(()) @@ -53,10 +51,8 @@ pub(super) fn eq_types<'tcx>( debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations); TypeRelating::new( infcx, + NllTypeRelatingDelegate::new(borrowck_context, locations, category), ty::Variance::Invariant, - locations, - category, - borrowck_context, ty::List::empty(), ).relate(&a, &b)?; Ok(()) @@ -90,17 +86,21 @@ pub(super) fn relate_type_and_user_type<'tcx>( TypeRelating::new( infcx, + NllTypeRelatingDelegate::new(borrowck_context, locations, category), v1, - locations, - category, - borrowck_context, b_variables, ).relate(&b_value, &a)?; Ok(()) } -struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> { - infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, +struct TypeRelating<'me, 'gcx: 'tcx, 'tcx: 'me, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, + + /// Callback to use when we deduce an outlives relationship + delegate: D, /// How are we relating `a` and `b`? /// @@ -125,15 +125,6 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> { /// Same as `a_scopes`, but for the `b` type. b_scopes: Vec>, - /// Where (and why) is this relation taking place? - locations: Locations, - - category: ConstraintCategory, - - /// This will be `Some` when we are running the type check as part - /// of NLL, and `None` if we are running a "sanity check". - borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>, - /// As we execute, the type on the LHS *may* come from a canonical /// source. In that case, we will sometimes find a constraint like /// `?0 = B`, where `B` is a type from the RHS. The first time we @@ -148,6 +139,54 @@ struct TypeRelating<'cx, 'bccx: 'cx, 'gcx: 'tcx, 'tcx: 'bccx> { canonical_var_values: IndexVec>>, } +trait TypeRelatingDelegate<'tcx> { + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>); +} + +struct NllTypeRelatingDelegate<'me, 'bccx: 'me, 'tcx: 'bccx> { + borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>, + + /// Where (and why) is this relation taking place? + locations: Locations, + + /// What category do we assign the resulting `'a: 'b` relationships? + category: ConstraintCategory, +} + +impl NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { + fn new( + borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>, + locations: Locations, + category: ConstraintCategory, + ) -> Self { + Self { + borrowck_context, + locations, + category, + } + } +} + +impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> { + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { + if let Some(borrowck_context) = &mut self.borrowck_context { + let sub = borrowck_context.universal_regions.to_region_vid(sub); + let sup = borrowck_context.universal_regions.to_region_vid(sup); + borrowck_context + .constraints + .outlives_constraints + .push(OutlivesConstraint { + sup, + sub, + locations: self.locations, + category: self.category, + }); + + // FIXME all facts! + } + } +} + #[derive(Clone, Debug)] struct ScopesAndKind<'tcx> { scopes: Vec>, @@ -162,23 +201,22 @@ struct BoundRegionScope<'tcx> { #[derive(Copy, Clone)] struct UniversallyQuantified(bool); -impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { +impl<'me, 'gcx, 'tcx, D> TypeRelating<'me, 'gcx, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ fn new( - infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, + delegate: D, ambient_variance: ty::Variance, - locations: Locations, - category: ConstraintCategory, - borrowck_context: Option<&'cx mut BorrowCheckContext<'bccx, 'tcx>>, canonical_var_infos: CanonicalVarInfos<'tcx>, ) -> Self { let canonical_var_values = IndexVec::from_elem_n(None, canonical_var_infos.len()); Self { infcx, + delegate, ambient_variance, - borrowck_context, - locations, canonical_var_values, - category, a_scopes: vec![], b_scopes: vec![], } @@ -259,21 +297,7 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { debug!("push_outlives({:?}: {:?})", sup, sub); - if let Some(borrowck_context) = &mut self.borrowck_context { - let sub = borrowck_context.universal_regions.to_region_vid(sub); - let sup = borrowck_context.universal_regions.to_region_vid(sup); - borrowck_context - .constraints - .outlives_constraints - .push(OutlivesConstraint { - sup, - sub, - locations: self.locations, - category: self.category, - }); - - // FIXME all facts! - } + self.delegate.push_outlives(sup, sub); } /// When we encounter a canonical variable `var` in the output, @@ -325,10 +349,11 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelating<'cx, 'bccx, 'gcx, 'tcx> { } } -impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> - for TypeRelating<'cx, 'bccx, 'gcx, 'tcx> +impl TypeRelation<'me, 'gcx, 'tcx> for TypeRelating<'me, 'gcx, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, { - fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { + fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> { self.infcx.tcx } @@ -534,15 +559,15 @@ impl<'cx, 'bccx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> /// binder depth, and finds late-bound regions targeting the /// `for<..`>. For each of those, it creates an entry in /// `bound_region_scope`. -struct ScopeInstantiator<'cx, 'gcx: 'tcx, 'tcx: 'cx> { - infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, +struct ScopeInstantiator<'me, 'gcx: 'tcx, 'tcx: 'me> { + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, // The debruijn index of the scope we are instantiating. target_index: ty::DebruijnIndex, universally_quantified: UniversallyQuantified, - bound_region_scope: &'cx mut BoundRegionScope<'tcx>, + bound_region_scope: &'me mut BoundRegionScope<'tcx>, } -impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> { +impl<'me, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'gcx, 'tcx> { fn visit_binder>(&mut self, t: &ty::Binder) -> bool { self.target_index.shift_in(1); t.super_visit_with(self); @@ -596,8 +621,11 @@ impl<'cx, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'cx, 'gcx, 'tcx> { /// scopes. /// /// [blog post]: https://is.gd/0hKvIr -struct TypeGeneralizer<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> { - type_rel: &'me TypeRelating<'me, 'bccx, 'gcx, 'tcx>, +struct TypeGeneralizer<'me, 'gcx: 'tcx, 'tcx: 'me, D> +where + D: TypeRelatingDelegate<'tcx> + 'me, +{ + type_rel: &'me TypeRelating<'me, 'gcx, 'tcx, D>, /// After we generalize this type, we are going to relative it to /// some other type. What will be the variance at this point? @@ -608,7 +636,10 @@ struct TypeGeneralizer<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> { universe: ty::UniverseIndex, } -impl TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'bbcx, 'gcx, 'tcx> { +impl TypeRelation<'me, 'gcx, 'tcx> for TypeGeneralizer<'me, 'gcx, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> { self.type_rel.infcx.tcx } From 582a369bc336b1eb3aab0cfaabf4fb4359b68492 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Sep 2018 16:57:41 -0400 Subject: [PATCH 18/34] encapsulate `infcx` too into the delegate --- .../borrow_check/nll/type_check/relate_tys.rs | 118 +++++++++++++----- 1 file changed, 84 insertions(+), 34 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs index f2934985f33e7..39e3403136360 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/relate_tys.rs @@ -31,8 +31,8 @@ pub(super) fn sub_types<'tcx>( ) -> Fallible<()> { debug!("sub_types(a={:?}, b={:?}, locations={:?})", a, b, locations); TypeRelating::new( - infcx, - NllTypeRelatingDelegate::new(borrowck_context, locations, category), + infcx.tcx, + NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category), ty::Variance::Covariant, ty::List::empty(), ).relate(&a, &b)?; @@ -50,8 +50,8 @@ pub(super) fn eq_types<'tcx>( ) -> Fallible<()> { debug!("eq_types(a={:?}, b={:?}, locations={:?})", a, b, locations); TypeRelating::new( - infcx, - NllTypeRelatingDelegate::new(borrowck_context, locations, category), + infcx.tcx, + NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category), ty::Variance::Invariant, ty::List::empty(), ).relate(&a, &b)?; @@ -85,8 +85,8 @@ pub(super) fn relate_type_and_user_type<'tcx>( let v1 = ty::Contravariant.xform(v); TypeRelating::new( - infcx, - NllTypeRelatingDelegate::new(borrowck_context, locations, category), + infcx.tcx, + NllTypeRelatingDelegate::new(infcx, borrowck_context, locations, category), v1, b_variables, ).relate(&b_value, &a)?; @@ -97,7 +97,7 @@ struct TypeRelating<'me, 'gcx: 'tcx, 'tcx: 'me, D> where D: TypeRelatingDelegate<'tcx>, { - infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, + tcx: TyCtxt<'me, 'gcx, 'tcx>, /// Callback to use when we deduce an outlives relationship delegate: D, @@ -140,10 +140,37 @@ where } trait TypeRelatingDelegate<'tcx> { + /// Push a constraint `sup: sub` -- this constraint must be + /// satisfied for the two types to be related. `sub` and `sup` may + /// be regions from the type or new variables created through the + /// delegate. fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>); + + /// Creates a new region variable representing an instantiated + /// higher-ranked region; this will be either existential or + /// universal depending on the context. So e.g. if you have + /// `for<'a> fn(..) <: for<'b> fn(..)`, then we will first + /// instantiate `'b` with a universally quantitifed region and + /// then `'a` with an existentially quantified region (the order + /// is important so that the existential region `'a` can see the + /// universal one). + fn next_region_var( + &mut self, + universally_quantified: UniversallyQuantified, + ) -> ty::Region<'tcx>; + + /// Creates a new existential region in the given universe. This + /// is used when handling subtyping and type variables -- if we + /// have that `?X <: Foo<'a>`, for example, we would instantiate + /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh + /// existential variable created by this function. We would then + /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives + /// relation stating that `'?0: 'a`). + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; } -struct NllTypeRelatingDelegate<'me, 'bccx: 'me, 'tcx: 'bccx> { +struct NllTypeRelatingDelegate<'me, 'bccx: 'me, 'gcx: 'tcx, 'tcx: 'bccx> { + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>, /// Where (and why) is this relation taking place? @@ -153,13 +180,15 @@ struct NllTypeRelatingDelegate<'me, 'bccx: 'me, 'tcx: 'bccx> { category: ConstraintCategory, } -impl NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { +impl NllTypeRelatingDelegate<'me, 'bccx, 'gcx, 'tcx> { fn new( + infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, borrowck_context: Option<&'me mut BorrowCheckContext<'bccx, 'tcx>>, locations: Locations, category: ConstraintCategory, ) -> Self { Self { + infcx, borrowck_context, locations, category, @@ -167,7 +196,24 @@ impl NllTypeRelatingDelegate<'me, 'bccx, 'tcx> { } } -impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> { +impl TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, '_, 'tcx> { + fn next_region_var( + &mut self, + universally_quantified: UniversallyQuantified, + ) -> ty::Region<'tcx> { + let origin = if universally_quantified.0 { + NLLRegionVariableOrigin::BoundRegion(self.infcx.create_subuniverse()) + } else { + NLLRegionVariableOrigin::Existential + }; + self.infcx.next_nll_region_var(origin) + } + + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { + self.infcx + .next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, universe) + } + fn push_outlives(&mut self, sup: ty::Region<'tcx>, sub: ty::Region<'tcx>) { if let Some(borrowck_context) = &mut self.borrowck_context { let sub = borrowck_context.universal_regions.to_region_vid(sub); @@ -206,14 +252,14 @@ where D: TypeRelatingDelegate<'tcx>, { fn new( - infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, + tcx: TyCtxt<'me, 'gcx, 'tcx>, delegate: D, ambient_variance: ty::Variance, canonical_var_infos: CanonicalVarInfos<'tcx>, ) -> Self { let canonical_var_values = IndexVec::from_elem_n(None, canonical_var_infos.len()); Self { - infcx, + tcx, delegate, ambient_variance, canonical_var_values, @@ -243,7 +289,7 @@ where ) -> BoundRegionScope<'tcx> { let mut scope = BoundRegionScope::default(); value.skip_binder().visit_with(&mut ScopeInstantiator { - infcx: self.infcx, + delegate: &mut self.delegate, target_index: ty::INNERMOST, universally_quantified, bound_region_scope: &mut scope, @@ -335,9 +381,10 @@ where return result; } - fn generalize_value(&self, kind: Kind<'tcx>) -> Kind<'tcx> { + fn generalize_value(&mut self, kind: Kind<'tcx>) -> Kind<'tcx> { TypeGeneralizer { - type_rel: self, + tcx: self.tcx, + delegate: &mut self.delegate, first_free_index: ty::INNERMOST, ambient_variance: self.ambient_variance, @@ -354,7 +401,7 @@ where D: TypeRelatingDelegate<'tcx>, { fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> { - self.infcx.tcx + self.tcx } fn tag(&self) -> &'static str { @@ -559,15 +606,21 @@ where /// binder depth, and finds late-bound regions targeting the /// `for<..`>. For each of those, it creates an entry in /// `bound_region_scope`. -struct ScopeInstantiator<'me, 'gcx: 'tcx, 'tcx: 'me> { - infcx: &'me InferCtxt<'me, 'gcx, 'tcx>, +struct ScopeInstantiator<'me, 'tcx: 'me, D> +where + D: TypeRelatingDelegate<'tcx> + 'me, +{ + delegate: &'me mut D, // The debruijn index of the scope we are instantiating. target_index: ty::DebruijnIndex, universally_quantified: UniversallyQuantified, bound_region_scope: &'me mut BoundRegionScope<'tcx>, } -impl<'me, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'gcx, 'tcx> { +impl<'me, 'tcx, D> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ fn visit_binder>(&mut self, t: &ty::Binder) -> bool { self.target_index.shift_in(1); t.super_visit_with(self); @@ -578,21 +631,18 @@ impl<'me, 'gcx, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'gcx, 'tcx> { fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { let ScopeInstantiator { - infcx, universally_quantified, + bound_region_scope, + delegate, .. - } = *self; + } = self; match r { ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => { - self.bound_region_scope.map.entry(*br).or_insert_with(|| { - let origin = if universally_quantified.0 { - NLLRegionVariableOrigin::BoundRegion(infcx.create_subuniverse()) - } else { - NLLRegionVariableOrigin::Existential - }; - infcx.next_nll_region_var(origin) - }); + bound_region_scope + .map + .entry(*br) + .or_insert_with(|| delegate.next_region_var(*universally_quantified)); } _ => {} @@ -625,7 +675,9 @@ struct TypeGeneralizer<'me, 'gcx: 'tcx, 'tcx: 'me, D> where D: TypeRelatingDelegate<'tcx> + 'me, { - type_rel: &'me TypeRelating<'me, 'gcx, 'tcx, D>, + tcx: TyCtxt<'me, 'gcx, 'tcx>, + + delegate: &'me mut D, /// After we generalize this type, we are going to relative it to /// some other type. What will be the variance at this point? @@ -641,7 +693,7 @@ where D: TypeRelatingDelegate<'tcx>, { fn tcx(&self) -> TyCtxt<'me, 'gcx, 'tcx> { - self.type_rel.infcx.tcx + self.tcx } fn tag(&self) -> &'static str { @@ -724,9 +776,7 @@ where // though, we may however need to check well-formedness or // risk a problem like #41677 again. - let replacement_region_vid = self.type_rel - .infcx - .next_nll_region_var_in_universe(NLLRegionVariableOrigin::Existential, self.universe); + let replacement_region_vid = self.delegate.generalize_existential(self.universe); Ok(replacement_region_vid) } From 688aaf3bbb62cb331d2b40beb632281e27ef40cd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Sep 2018 12:11:58 -0400 Subject: [PATCH 19/34] lexical_region_resolve: rustfmt --- .../infer/lexical_region_resolve/mod.rs | 74 ++++++++----------- 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 120b45ec01e5e..c7ead1cf66de3 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -10,23 +10,25 @@ //! The code to do lexical region resolution. -use infer::SubregionOrigin; -use infer::RegionVariableOrigin; use infer::region_constraints::Constraint; use infer::region_constraints::GenericKind; use infer::region_constraints::RegionConstraintData; use infer::region_constraints::VarInfos; use infer::region_constraints::VerifyBound; +use infer::RegionVariableOrigin; +use infer::SubregionOrigin; use middle::free_region::RegionRelations; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::graph::implementation::{Graph, Direction, NodeIndex, INCOMING, OUTGOING}; +use rustc_data_structures::graph::implementation::{ + Direction, Graph, NodeIndex, INCOMING, OUTGOING, +}; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use std::fmt; use std::u32; use ty::{self, TyCtxt}; -use ty::{Region, RegionVid}; use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; +use ty::{Region, RegionVid}; mod graphviz; @@ -239,9 +241,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { debug!( "Expanding value of {:?} from {:?} to {:?}", - b_vid, - cur_region, - lub + b_vid, cur_region, lub ); *b_data = VarValue::Value(lub); @@ -254,18 +254,17 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { } } - fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.region_rels.tcx; match (a, b) { - (&ty::ReCanonical(..), _) | - (_, &ty::ReCanonical(..)) | - (&ty::ReClosureBound(..), _) | - (_, &ty::ReClosureBound(..)) | - (&ReLateBound(..), _) | - (_, &ReLateBound(..)) | - (&ReErased, _) | - (_, &ReErased) => { + (&ty::ReCanonical(..), _) + | (_, &ty::ReCanonical(..)) + | (&ty::ReClosureBound(..), _) + | (_, &ty::ReClosureBound(..)) + | (&ReLateBound(..), _) + | (_, &ReLateBound(..)) + | (&ReErased, _) + | (_, &ReErased) => { bug!("cannot relate region: LUB({:?}, {:?})", a, b); } @@ -287,10 +286,10 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { ); } - (&ReEarlyBound(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReEarlyBound(_)) | - (&ReFree(_), &ReScope(s_id)) | - (&ReScope(s_id), &ReFree(_)) => { + (&ReEarlyBound(_), &ReScope(s_id)) + | (&ReScope(s_id), &ReEarlyBound(_)) + | (&ReFree(_), &ReScope(s_id)) + | (&ReScope(s_id), &ReFree(_)) => { // A "free" region can be interpreted as "some region // at least as big as fr.scope". So, we can // reasonably compare free regions and scopes: @@ -332,10 +331,10 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { tcx.mk_region(ReScope(lub)) } - (&ReEarlyBound(_), &ReEarlyBound(_)) | - (&ReFree(_), &ReEarlyBound(_)) | - (&ReEarlyBound(_), &ReFree(_)) | - (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b), + (&ReEarlyBound(_), &ReEarlyBound(_)) + | (&ReFree(_), &ReEarlyBound(_)) + | (&ReEarlyBound(_), &ReFree(_)) + | (&ReFree(_), &ReFree(_)) => self.region_rels.lub_free_regions(a, b), // For these types, we cannot define any additional // relationship: @@ -358,8 +357,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { for (constraint, origin) in &self.data.constraints { debug!( "collect_errors: constraint={:?} origin={:?}", - constraint, - origin + constraint, origin ); match *constraint { Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { @@ -374,9 +372,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { debug!( "collect_errors: region error at {:?}: \ cannot verify that {:?} <= {:?}", - origin, - sub, - sup + origin, sub, sup ); errors.push(RegionResolutionError::ConcreteFailure( @@ -402,10 +398,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { debug!( "collect_errors: region error at {:?}: \ cannot verify that {:?}={:?} <= {:?}", - origin, - a_vid, - a_region, - b_region + origin, a_vid, a_region, b_region ); *a_data = VarValue::ErrorValue; } @@ -430,9 +423,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { debug!( "collect_errors: region error at {:?}: \ cannot verify that {:?} <= {:?}", - verify.origin, - verify.region, - verify.bound + verify.origin, verify.region, verify.bound ); errors.push(RegionResolutionError::GenericBoundFailure( @@ -580,10 +571,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ sup: {:?}", - origin, - node_idx, - lower_bound.region, - upper_bound.region + origin, node_idx, lower_bound.region, upper_bound.region ); errors.push(RegionResolutionError::SubSupConflict( origin, @@ -645,8 +633,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { debug!( "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", - orig_node_idx, - node_idx + orig_node_idx, node_idx ); process_edges(&self.data, &mut state, graph, node_idx, dir); @@ -745,7 +732,6 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { } } - impl<'tcx> LexicalRegionResolutions<'tcx> { fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { match *r { From b5469c5fd75cec185dc2c79a4986decaa1f9be40 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Sep 2018 12:15:40 -0400 Subject: [PATCH 20/34] make `normalize` work on any type-foldable --- .../infer/lexical_region_resolve/mod.rs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index c7ead1cf66de3..50198406e8470 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -29,6 +29,7 @@ use ty::{self, TyCtxt}; use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; use ty::{Region, RegionVid}; +use ty::fold::TypeFoldable; mod graphviz; @@ -110,11 +111,15 @@ struct LexicalResolver<'cx, 'gcx: 'tcx, 'tcx: 'cx> { } impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { + fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> { + self.region_rels.tcx + } + fn infer_variable_values( &mut self, errors: &mut Vec>, ) -> LexicalRegionResolutions<'tcx> { - let mut var_data = self.construct_var_data(self.region_rels.tcx); + let mut var_data = self.construct_var_data(self.tcx()); // Dorky hack to cause `dump_constraints` to only get called // if debug mode is enabled: @@ -255,7 +260,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { } fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { - let tcx = self.region_rels.tcx; + let tcx = self.tcx(); match (a, b) { (&ty::ReCanonical(..), _) | (_, &ty::ReCanonical(..)) @@ -296,10 +301,10 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { let fr_scope = match (a, b) { (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels .region_scope_tree - .early_free_scope(self.region_rels.tcx, br), + .early_free_scope(self.tcx(), br), (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels .region_scope_tree - .free_scope(self.region_rels.tcx, fr), + .free_scope(self.tcx(), fr), _ => bug!(), }; let r_id = self.region_rels @@ -408,7 +413,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { for verify in &self.data.verifys { debug!("collect_errors: verify={:?}", verify); - let sub = var_data.normalize(verify.region); + let sub = var_data.normalize(self.tcx(), verify.region); // This was an inference variable which didn't get // constrained, therefore it can be assume to hold. @@ -712,11 +717,11 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { ) -> bool { match bound { VerifyBound::AnyRegion(rs) => rs.iter() - .map(|&r| var_values.normalize(r)) + .map(|&r| var_values.normalize(self.tcx(), r)) .any(|r| self.region_rels.is_subregion_of(min, r)), VerifyBound::AllRegions(rs) => rs.iter() - .map(|&r| var_values.normalize(r)) + .map(|&r| var_values.normalize(self.tcx(), r)) .all(|r| self.region_rels.is_subregion_of(min, r)), VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)), @@ -733,11 +738,14 @@ impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { } impl<'tcx> LexicalRegionResolutions<'tcx> { - fn normalize(&self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - ty::ReVar(rid) => self.resolve_var(rid), + fn normalize(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + tcx.fold_regions(&value, &mut false, |r, _db| match r { + ty::ReVar(rid) => self.resolve_var(*rid), _ => r, - } + }) } fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { From 85d12e2f0b5aa76f535d1d519925a86eecdb73b4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Sep 2018 12:44:38 -0400 Subject: [PATCH 21/34] remove handling of verify from taintset This lets us remove `for_each_region` and makes things simpler. --- src/librustc/infer/region_constraints/mod.rs | 12 ----- .../infer/region_constraints/taint.rs | 50 +++++++++---------- 2 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index d8f3b9a05bd40..0297340e402fc 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -882,18 +882,6 @@ impl<'a, 'gcx, 'tcx> GenericKind<'tcx> { } impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { - fn for_each_region(&self, f: &mut dyn FnMut(ty::Region<'tcx>)) { - match self { - &VerifyBound::AnyRegion(ref rs) | &VerifyBound::AllRegions(ref rs) => for &r in rs { - f(r); - }, - - &VerifyBound::AnyBound(ref bs) | &VerifyBound::AllBounds(ref bs) => for b in bs { - b.for_each_region(f); - }, - } - } - pub fn must_hold(&self) -> bool { match self { &VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic), diff --git a/src/librustc/infer/region_constraints/taint.rs b/src/librustc/infer/region_constraints/taint.rs index ee45f7bd82801..4f513cd5d484d 100644 --- a/src/librustc/infer/region_constraints/taint.rs +++ b/src/librustc/infer/region_constraints/taint.rs @@ -13,34 +13,39 @@ use super::*; #[derive(Debug)] pub(super) struct TaintSet<'tcx> { directions: TaintDirections, - regions: FxHashSet> + regions: FxHashSet>, } impl<'tcx> TaintSet<'tcx> { - pub(super) fn new(directions: TaintDirections, - initial_region: ty::Region<'tcx>) - -> Self { + pub(super) fn new(directions: TaintDirections, initial_region: ty::Region<'tcx>) -> Self { let mut regions = FxHashSet(); regions.insert(initial_region); - TaintSet { directions: directions, regions: regions } + TaintSet { + directions: directions, + regions: regions, + } } - pub(super) fn fixed_point(&mut self, - tcx: TyCtxt<'_, '_, 'tcx>, - undo_log: &[UndoLogEntry<'tcx>], - verifys: &[Verify<'tcx>]) { + pub(super) fn fixed_point( + &mut self, + tcx: TyCtxt<'_, '_, 'tcx>, + undo_log: &[UndoLogEntry<'tcx>], + verifys: &[Verify<'tcx>], + ) { let mut prev_len = 0; while prev_len < self.len() { - debug!("tainted: prev_len = {:?} new_len = {:?}", - prev_len, self.len()); + debug!( + "tainted: prev_len = {:?} new_len = {:?}", + prev_len, + self.len() + ); prev_len = self.len(); for undo_entry in undo_log { match undo_entry { &AddConstraint(Constraint::VarSubVar(a, b)) => { - self.add_edge(tcx.mk_region(ReVar(a)), - tcx.mk_region(ReVar(b))); + self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); } &AddConstraint(Constraint::RegSubVar(a, b)) => { self.add_edge(a, tcx.mk_region(ReVar(b))); @@ -55,15 +60,13 @@ impl<'tcx> TaintSet<'tcx> { self.add_edge(a, tcx.mk_region(ReVar(b))); } &AddVerify(i) => { - verifys[i].bound.for_each_region(&mut |b| { - self.add_edge(verifys[i].region, b); - }); + span_bug!( + verifys[i].origin.span(), + "we never add verifications while doing higher-ranked things", + ) } - &Purged | - &AddCombination(..) | - &AddVar(..) | - &OpenSnapshot | - &CommitedSnapshot => {} + &Purged | &AddCombination(..) | &AddVar(..) | &OpenSnapshot + | &CommitedSnapshot => {} } } } @@ -77,9 +80,7 @@ impl<'tcx> TaintSet<'tcx> { self.regions.len() } - fn add_edge(&mut self, - source: ty::Region<'tcx>, - target: ty::Region<'tcx>) { + fn add_edge(&mut self, source: ty::Region<'tcx>, target: ty::Region<'tcx>) { if self.directions.incoming { if self.regions.contains(&target) { self.regions.insert(source); @@ -93,4 +94,3 @@ impl<'tcx> TaintSet<'tcx> { } } } - From 58fd6fad24334ca5061dd864cb063bcf7030ecef Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Sep 2018 14:45:02 -0400 Subject: [PATCH 22/34] change to use impl Trait a bit --- src/librustc/infer/outlives/verify.rs | 29 ++++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index 80170a86c3841..380a92b342b30 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -91,14 +91,14 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { ) -> Vec> { let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(&projection_ty); - self.declared_generic_bounds_from_env_with_compare_fn( - |ty| if let ty::Projection(..) = ty.sty { + self.declared_generic_bounds_from_env_with_compare_fn(|ty| { + if let ty::Projection(..) = ty.sty { let erased_ty = self.tcx.erase_regions(&ty); erased_ty == erased_projection_ty } else { false - }, - ) + } + }) } /// Searches the where clauses in scope for regions that @@ -177,7 +177,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { // like `T` and `T::Item`. It may not work as well for things // like `>::Item`. let c_b = self.param_env.caller_bounds; - let mut param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b); + let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b); // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list @@ -190,17 +190,19 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { // The problem is that the type of `x` is `&'a A`. To be // well-formed, then, A must be lower-generic by `'a`, but we // don't know that this holds from first principles. - for &(r, p) in self.region_bound_pairs { + let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| { debug!( "declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}", (r, p) ); if compare_ty(p.to_ty(tcx)) { - param_bounds.push(r); + Some(r) + } else { + None } - } + }); - param_bounds + param_bounds.chain(from_region_bound_pairs).collect() } /// Given a projection like `>::Bar`, returns any bounds @@ -268,9 +270,9 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id); let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs); self.collect_outlives_from_predicate_list( - |ty| ty == identity_proj, + move |ty| ty == identity_proj, traits::elaborate_predicates(tcx, trait_predicates.predicates), - ) + ).collect() } /// Searches through a predicate list for a predicate `T: 'a`. @@ -283,13 +285,12 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { &self, compare_ty: impl Fn(Ty<'tcx>) -> bool, predicates: impl IntoIterator>>, - ) -> Vec> { + ) -> impl Iterator> { predicates .into_iter() .filter_map(|p| p.as_ref().to_opt_type_outlives()) .filter_map(|p| p.no_late_bound_regions()) - .filter(|p| compare_ty(p.0)) + .filter(move |p| compare_ty(p.0)) .map(|p| p.1) - .collect() } } From 18b86e94065ff3e493406a7fb811115a85dc57ce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Sep 2018 12:52:17 -0400 Subject: [PATCH 23/34] introduce `VerifyBound::IfEq` (presently unused) --- .../infer/lexical_region_resolve/mod.rs | 19 ++- src/librustc/infer/region_constraints/mod.rs | 113 ++++++++++++++---- .../nll/type_check/constraint_conversion.rs | 5 + 3 files changed, 111 insertions(+), 26 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 50198406e8470..727b257f6f278 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -25,11 +25,11 @@ use rustc_data_structures::graph::implementation::{ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use std::fmt; use std::u32; -use ty::{self, TyCtxt}; +use ty::fold::TypeFoldable; +use ty::{self, Ty, TyCtxt}; use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; use ty::{ReLateBound, ReScope, ReSkolemized, ReVar}; use ty::{Region, RegionVid}; -use ty::fold::TypeFoldable; mod graphviz; @@ -421,7 +421,8 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { continue; } - if self.bound_is_met(&verify.bound, var_data, sub) { + let verify_kind_ty = verify.kind.to_ty(self.tcx()); + if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) { continue; } @@ -713,9 +714,15 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { &self, bound: &VerifyBound<'tcx>, var_values: &LexicalRegionResolutions<'tcx>, + generic_ty: Ty<'tcx>, min: ty::Region<'tcx>, ) -> bool { match bound { + VerifyBound::IfEq(k, b) => { + (var_values.normalize(self.region_rels.tcx, *k) == generic_ty) + && self.bound_is_met(b, var_values, generic_ty, min) + } + VerifyBound::AnyRegion(rs) => rs.iter() .map(|&r| var_values.normalize(self.tcx(), r)) .any(|r| self.region_rels.is_subregion_of(min, r)), @@ -724,9 +731,11 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { .map(|&r| var_values.normalize(self.tcx(), r)) .all(|r| self.region_rels.is_subregion_of(min, r)), - VerifyBound::AnyBound(bs) => bs.iter().any(|b| self.bound_is_met(b, var_values, min)), + VerifyBound::AnyBound(bs) => bs.iter() + .any(|b| self.bound_is_met(b, var_values, generic_ty, min)), - VerifyBound::AllBounds(bs) => bs.iter().all(|b| self.bound_is_met(b, var_values, min)), + VerifyBound::AllBounds(bs) => bs.iter() + .all(|b| self.bound_is_met(b, var_values, generic_ty, min)), } } } diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 0297340e402fc..9e12ed0b52f7a 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -155,29 +155,98 @@ pub enum GenericKind<'tcx> { Projection(ty::ProjectionTy<'tcx>), } -/// When we introduce a verification step, we wish to test that a -/// particular region (let's call it `'min`) meets some bound. -/// The bound is described the by the following grammar: +EnumTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for GenericKind<'tcx> { + (GenericKind::Param)(a), + (GenericKind::Projection)(a), + } +} + +/// Describes the things that some `GenericKind` value G is known to +/// outlive. Each variant of `VerifyBound` can be thought of as a +/// function: +/// +/// fn(min: Region) -> bool { .. } +/// +/// where `true` means that the region `min` meets that `G: min`. +/// (False means nothing.) +/// +/// So, for example, if we have the type `T` and we have in scope that +/// `T: 'a` and `T: 'b`, then the verify bound might be: +/// +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// +/// This is described with a `AnyRegion('a, 'b)` node. #[derive(Debug, Clone)] pub enum VerifyBound<'tcx> { - /// B = exists {R} --> some 'r in {R} must outlive 'min + /// Given a kind K and a bound B, expands to a function like the + /// following, where `G` is the generic for which this verify + /// bound was created: + /// + /// fn(min) -> bool { + /// if G == K { + /// B(min) + /// } else { + /// false + /// } + /// } + /// + /// In other words, if the generic `G` that we are checking is + /// equal to `K`, then check the associated verify bound + /// (otherwise, false). + /// + /// This is used when we have something in the environment that + /// may or may not be relevant, depending on the region inference + /// results. For example, we may have `where >::Item: 'b` in our where clauses. If we are + /// generating the verify-bound for `>::Item`, then + /// this where-clause is only relevant if `'0` winds up inferred + /// to `'a`. + /// + /// So we would compile to a verify-bound like /// - /// Put another way, the subject value is known to outlive all - /// regions in {R}, so if any of those outlives 'min, then the - /// bound is met. + /// IfEq(>::Item, AnyRegion('a)) + /// + /// meaning, if the subject G is equal to `>::Item` + /// (after inference), and `'a: min`, then `G: min`. + IfEq(Ty<'tcx>, Box>), + + /// Given a set of regions `R`, expands to the function: + /// + /// fn(min) -> bool { + /// exists (r in R) { r: min } + /// } + /// + /// In other words, if some r in R outlives min, then G outlives + /// min. This is used when G is known to outlive all the regions + /// in R. AnyRegion(Vec>), - /// B = forall {R} --> all 'r in {R} must outlive 'min + /// Given a set of regions `R`, expands to the function: /// - /// Put another way, the subject value is known to outlive some - /// region in {R}, so if all of those outlives 'min, then the bound - /// is met. + /// fn(min) -> bool { + /// forall (r in R) { r: min } + /// } + /// + /// In other words, if all r in R outlives min, then G outlives + /// min. This is used when G is known to outlive some region in + /// R, but we don't know which. AllRegions(Vec>), - /// B = exists {B} --> 'min must meet some bound b in {B} + /// Given a set of bounds `B`, expands to the function: + /// + /// fn(min) -> bool { + /// exists (b in B) { b(min) } + /// } AnyBound(Vec>), - /// B = forall {B} --> 'min must meet all bounds b in {B} + /// Given a set of bounds `B`, expands to the function: + /// + /// fn(min) -> bool { + /// forall (b in B) { b(min) } + /// } AllBounds(Vec>), } @@ -884,19 +953,21 @@ impl<'a, 'gcx, 'tcx> GenericKind<'tcx> { impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { pub fn must_hold(&self) -> bool { match self { - &VerifyBound::AnyRegion(ref bs) => bs.contains(&&ty::ReStatic), - &VerifyBound::AllRegions(ref bs) => bs.is_empty(), - &VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()), - &VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()), + VerifyBound::IfEq(..) => false, + VerifyBound::AnyRegion(bs) => bs.contains(&&ty::ReStatic), + VerifyBound::AllRegions(bs) => bs.is_empty(), + VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), + VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), } } pub fn cannot_hold(&self) -> bool { match self { - &VerifyBound::AnyRegion(ref bs) => bs.is_empty(), - &VerifyBound::AllRegions(ref bs) => bs.contains(&&ty::ReEmpty), - &VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()), - &VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()), + VerifyBound::IfEq(_, b) => b.cannot_hold(), + VerifyBound::AnyRegion(bs) => bs.is_empty(), + VerifyBound::AllRegions(bs) => bs.contains(&&ty::ReEmpty), + VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), + VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 220cbfe4fff3d..37a6022469ec7 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -156,6 +156,11 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest { match verify_bound { + VerifyBound::IfEq(..) => { + // FIXME: always false right now + RegionTest::IsOutlivedByAnyRegionIn(vec![]) + } + VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn( regions.iter().map(|r| self.to_region_vid(r)).collect(), ), From 7f8c42d7f01b36132588ff0cd6b1c6a90ed52b23 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Sep 2018 15:58:47 -0400 Subject: [PATCH 24/34] refactor away `AnyRegions` and `AllRegions` It's a bit cleaner to just have `AnyBound` and `AllBound`, after all. --- .../infer/lexical_region_resolve/mod.rs | 12 +++--- src/librustc/infer/outlives/verify.rs | 37 +++++++++++++----- src/librustc/infer/region_constraints/mod.rs | 38 +++++++++---------- .../borrow_check/nll/region_infer/mod.rs | 33 +++++----------- .../nll/type_check/constraint_conversion.rs | 10 ++--- 5 files changed, 61 insertions(+), 69 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 727b257f6f278..a8fbfc3b64dfd 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -723,13 +723,11 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> { && self.bound_is_met(b, var_values, generic_ty, min) } - VerifyBound::AnyRegion(rs) => rs.iter() - .map(|&r| var_values.normalize(self.tcx(), r)) - .any(|r| self.region_rels.is_subregion_of(min, r)), - - VerifyBound::AllRegions(rs) => rs.iter() - .map(|&r| var_values.normalize(self.tcx(), r)) - .all(|r| self.region_rels.is_subregion_of(min, r)), + VerifyBound::OutlivedBy(r) => + self.region_rels.is_subregion_of( + min, + var_values.normalize(self.tcx(), r), + ), VerifyBound::AnyBound(bs) => bs.iter() .any(|b| self.bound_is_met(b, var_values, generic_ty, min)), diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index 380a92b342b30..79fc2df7db9d0 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -63,13 +63,23 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { debug!("param_bound(param_ty={:?})", param_ty); - let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)); + // Start with anything like `T: 'a` we can scrape from the + // environment + let param_bounds = + self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)) + .into_iter(); // Add in the default bound of fn body that applies to all in // scope type parameters: - param_bounds.extend(self.implicit_region_bound); + let param_bounds = + param_bounds + .chain(self.implicit_region_bound); - VerifyBound::AnyRegion(param_bounds) + VerifyBound::AnyBound( + param_bounds + .map(|r| VerifyBound::OutlivedBy(r)) + .collect() + ) } /// Given a projection like `T::Item`, searches the environment @@ -115,20 +125,23 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { debug!("projection_bound(projection_ty={:?})", projection_ty); // Search the env for where clauses like `P: 'a`. - let mut declared_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)); + let env_bounds = + self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)) + .into_iter(); // Extend with bounds that we can find from the trait. - declared_bounds.extend(self.projection_declared_bounds_from_trait(projection_ty)); - - debug!("projection_bound: declared_bounds = {:?}", declared_bounds); + let trait_bounds = + self.projection_declared_bounds_from_trait(projection_ty) + .into_iter(); // see the extensive comment in projection_must_outlive let ty = self.tcx .mk_projection(projection_ty.item_def_id, projection_ty.substs); let recursive_bound = self.recursive_type_bound(ty); - VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) + VerifyBound::AnyBound( + env_bounds.chain(trait_bounds).map(|r| VerifyBound::OutlivedBy(r)).collect() + ).or(recursive_bound) } fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { @@ -138,7 +151,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { let mut regions = ty.regions(); regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push(VerifyBound::AllRegions(regions)); + bounds.push( + VerifyBound::AllBounds( + regions.into_iter().map(|r| VerifyBound::OutlivedBy(r)).collect() + ) + ); // remove bounds that must hold, since they are not interesting bounds.retain(|b| !b.must_hold()); diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 9e12ed0b52f7a..bc9027a08258c 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -213,33 +213,25 @@ pub enum VerifyBound<'tcx> { /// (after inference), and `'a: min`, then `G: min`. IfEq(Ty<'tcx>, Box>), - /// Given a set of regions `R`, expands to the function: + /// Given a region `R`, expands to the function: /// /// fn(min) -> bool { - /// exists (r in R) { r: min } + /// R: min /// } /// - /// In other words, if some r in R outlives min, then G outlives - /// min. This is used when G is known to outlive all the regions - /// in R. - AnyRegion(Vec>), - - /// Given a set of regions `R`, expands to the function: - /// - /// fn(min) -> bool { - /// forall (r in R) { r: min } - /// } - /// - /// In other words, if all r in R outlives min, then G outlives - /// min. This is used when G is known to outlive some region in - /// R, but we don't know which. - AllRegions(Vec>), + /// This is used when we can establish that `G: R` -- therefore, + /// if `R: min`, then by transitivity `G: min`. + OutlivedBy(Region<'tcx>), /// Given a set of bounds `B`, expands to the function: /// /// fn(min) -> bool { /// exists (b in B) { b(min) } /// } + /// + /// In other words, if we meet some bound in `B`, that suffices. + /// This is used when all the bounds in `B` are known to apply to + /// G. AnyBound(Vec>), /// Given a set of bounds `B`, expands to the function: @@ -247,6 +239,10 @@ pub enum VerifyBound<'tcx> { /// fn(min) -> bool { /// forall (b in B) { b(min) } /// } + /// + /// In other words, if we meet *all* bounds in `B`, that suffices. + /// This is used when *some* bound in `B` is known to suffice, but + /// we don't know which. AllBounds(Vec>), } @@ -954,8 +950,8 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { pub fn must_hold(&self) -> bool { match self { VerifyBound::IfEq(..) => false, - VerifyBound::AnyRegion(bs) => bs.contains(&&ty::ReStatic), - VerifyBound::AllRegions(bs) => bs.is_empty(), + VerifyBound::OutlivedBy(ty::ReStatic) => true, + VerifyBound::OutlivedBy(_) => false, VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), } @@ -964,8 +960,8 @@ impl<'a, 'gcx, 'tcx> VerifyBound<'tcx> { pub fn cannot_hold(&self) -> bool { match self { VerifyBound::IfEq(_, b) => b.cannot_hold(), - VerifyBound::AnyRegion(bs) => bs.is_empty(), - VerifyBound::AllRegions(bs) => bs.contains(&&ty::ReEmpty), + VerifyBound::OutlivedBy(ty::ReEmpty) => true, + VerifyBound::OutlivedBy(_) => false, VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 75f14a6bbdac8..250db14bc95c6 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -172,28 +172,19 @@ pub struct TypeTest<'tcx> { /// conveniently include disjuction ("a or b must be true"). #[derive(Clone, Debug)] pub enum RegionTest { - /// The subject region `'x` must by outlived by *some* region in - /// the given set of regions. + /// The subject region `'x` must by outlived by the given region. + /// + /// This test comes from e.g. a where clause like `T: 'a`, which + /// implies that we know that `T: 'a`. Therefore, if we are trying + /// to prove that `T: 'x`, we can do so by showing that `'a: 'x`. + IsOutlivedBy(RegionVid), + + /// Any of the given tests are true. /// /// This test comes from e.g. a where clause like `T: 'a + 'b`, /// which implies that we know that `T: 'a` and that `T: /// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can /// do so by showing that `'a: 'x` *or* `'b: 'x`. - IsOutlivedByAnyRegionIn(Vec), - - /// The subject region `'x` must by outlived by *all* regions in - /// the given set of regions. - /// - /// This test comes from e.g. a projection type like `T = >::Foo`, which must outlive `'a` or `'b`, and - /// maybe both. Therefore we can prove that `T: 'x` if we know - /// that `'a: 'x` *and* `'b: 'x`. - IsOutlivedByAllRegionsIn(Vec), - - /// Any of the given tests are true. - /// - /// This arises from projections, for which there are multiple - /// ways to prove an outlives relationship. Any(Vec), /// All of the given tests are true. @@ -895,13 +886,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); match test { - RegionTest::IsOutlivedByAllRegionsIn(regions) => regions - .iter() - .all(|&r| self.eval_outlives(mir, r, lower_bound)), - - RegionTest::IsOutlivedByAnyRegionIn(regions) => regions - .iter() - .any(|&r| self.eval_outlives(mir, r, lower_bound)), + RegionTest::IsOutlivedBy(r) => self.eval_outlives(mir, *r, lower_bound), RegionTest::Any(tests) => tests .iter() diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 37a6022469ec7..f50516d014aa9 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -158,15 +158,11 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { match verify_bound { VerifyBound::IfEq(..) => { // FIXME: always false right now - RegionTest::IsOutlivedByAnyRegionIn(vec![]) + RegionTest::Any(vec![]) } - VerifyBound::AnyRegion(regions) => RegionTest::IsOutlivedByAnyRegionIn( - regions.iter().map(|r| self.to_region_vid(r)).collect(), - ), - - VerifyBound::AllRegions(regions) => RegionTest::IsOutlivedByAllRegionsIn( - regions.iter().map(|r| self.to_region_vid(r)).collect(), + VerifyBound::OutlivedBy(r) => RegionTest::IsOutlivedBy( + self.to_region_vid(r) ), VerifyBound::AnyBound(bounds) => RegionTest::Any( From dc9317ff8c2c94d3231d34e863ccc96214ef5e83 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Sep 2018 16:12:00 -0400 Subject: [PATCH 25/34] use `IfEq` to defer equality comparison around `where` clauses` --- src/librustc/infer/outlives/obligations.rs | 11 +-- src/librustc/infer/outlives/verify.rs | 97 ++++++++++++---------- 2 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/librustc/infer/outlives/obligations.rs b/src/librustc/infer/outlives/obligations.rs index 88ac1e8590d12..332859d4f81db 100644 --- a/src/librustc/infer/outlives/obligations.rs +++ b/src/librustc/infer/outlives/obligations.rs @@ -401,12 +401,9 @@ where // Compute the bounds we can derive from the trait definition. // These are guaranteed to apply, no matter the inference // results. - let trait_bounds = self.verify_bound - .projection_declared_bounds_from_trait(projection_ty); - debug!( - "projection_must_outlive: trait_bounds={:?}", - trait_bounds - ); + let trait_bounds: Vec<_> = self.verify_bound + .projection_declared_bounds_from_trait(projection_ty) + .collect(); // If declared bounds list is empty, the only applicable rule is // OutlivesProjectionComponent. If there are inference variables, @@ -451,7 +448,7 @@ where if !trait_bounds.is_empty() && trait_bounds[1..] .iter() - .chain(&approx_env_bounds) + .chain(approx_env_bounds.iter().map(|b| &b.1)) .all(|b| *b == trait_bounds[0]) { let unique_bound = trait_bounds[0]; diff --git a/src/librustc/infer/outlives/verify.rs b/src/librustc/infer/outlives/verify.rs index 79fc2df7db9d0..5b23fc19a9d1b 100644 --- a/src/librustc/infer/outlives/verify.rs +++ b/src/librustc/infer/outlives/verify.rs @@ -14,6 +14,7 @@ use infer::{GenericKind, VerifyBound}; use traits; use ty::subst::{Subst, Substs}; use ty::{self, Ty, TyCtxt}; +use util::captures::Captures; /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` /// obligation into a series of `'a: 'b` constraints and "verifys", as @@ -65,21 +66,15 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { // Start with anything like `T: 'a` we can scrape from the // environment - let param_bounds = - self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)) - .into_iter(); + let param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty)) + .into_iter() + .map(|outlives| outlives.1); // Add in the default bound of fn body that applies to all in // scope type parameters: - let param_bounds = - param_bounds - .chain(self.implicit_region_bound); + let param_bounds = param_bounds.chain(self.implicit_region_bound); - VerifyBound::AnyBound( - param_bounds - .map(|r| VerifyBound::OutlivedBy(r)) - .collect() - ) + VerifyBound::AnyBound(param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect()) } /// Given a projection like `T::Item`, searches the environment @@ -98,7 +93,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { pub fn projection_approx_declared_bounds_from_env( &self, projection_ty: ty::ProjectionTy<'tcx>, - ) -> Vec> { + ) -> Vec, ty::Region<'tcx>>> { let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); let erased_projection_ty = self.tcx.erase_regions(&projection_ty); self.declared_generic_bounds_from_env_with_compare_fn(|ty| { @@ -117,31 +112,42 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { pub fn projection_declared_bounds_from_trait( &self, projection_ty: ty::ProjectionTy<'tcx>, - ) -> Vec> { + ) -> impl Iterator> + 'cx + Captures<'gcx> { self.declared_projection_bounds_from_trait(projection_ty) } pub fn projection_bound(&self, projection_ty: ty::ProjectionTy<'tcx>) -> VerifyBound<'tcx> { debug!("projection_bound(projection_ty={:?})", projection_ty); + let projection_ty_as_ty = + self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); + // Search the env for where clauses like `P: 'a`. - let env_bounds = - self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty)) - .into_iter(); + let env_bounds = self.projection_approx_declared_bounds_from_env(projection_ty) + .into_iter() + .map(|ty::OutlivesPredicate(ty, r)| { + let vb = VerifyBound::OutlivedBy(r); + if ty == projection_ty_as_ty { + // Micro-optimize if this is an exact match (this + // occurs often when there are no region variables + // involved). + vb + } else { + VerifyBound::IfEq(ty, Box::new(vb)) + } + }); // Extend with bounds that we can find from the trait. - let trait_bounds = - self.projection_declared_bounds_from_trait(projection_ty) - .into_iter(); + let trait_bounds = self.projection_declared_bounds_from_trait(projection_ty) + .into_iter() + .map(|r| VerifyBound::OutlivedBy(r)); // see the extensive comment in projection_must_outlive let ty = self.tcx .mk_projection(projection_ty.item_def_id, projection_ty.substs); let recursive_bound = self.recursive_type_bound(ty); - VerifyBound::AnyBound( - env_bounds.chain(trait_bounds).map(|r| VerifyBound::OutlivedBy(r)).collect() - ).or(recursive_bound) + VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) } fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> { @@ -151,11 +157,12 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { let mut regions = ty.regions(); regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions - bounds.push( - VerifyBound::AllBounds( - regions.into_iter().map(|r| VerifyBound::OutlivedBy(r)).collect() - ) - ); + bounds.push(VerifyBound::AllBounds( + regions + .into_iter() + .map(|r| VerifyBound::OutlivedBy(r)) + .collect(), + )); // remove bounds that must hold, since they are not interesting bounds.retain(|b| !b.must_hold()); @@ -176,7 +183,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { fn declared_generic_bounds_from_env( &self, generic: GenericKind<'tcx>, - ) -> Vec> { + ) -> Vec, ty::Region<'tcx>>> { let generic_ty = generic.to_ty(self.tcx); self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty) } @@ -184,7 +191,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { fn declared_generic_bounds_from_env_with_compare_fn( &self, compare_ty: impl Fn(Ty<'tcx>) -> bool, - ) -> Vec> { + ) -> Vec, ty::Region<'tcx>>> { let tcx = self.tcx; // To start, collect bounds from user environment. Note that @@ -212,14 +219,23 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { "declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}", (r, p) ); - if compare_ty(p.to_ty(tcx)) { - Some(r) + let p_ty = p.to_ty(tcx); + if compare_ty(p_ty) { + Some(ty::OutlivesPredicate(p_ty, r)) } else { None } }); - param_bounds.chain(from_region_bound_pairs).collect() + param_bounds + .chain(from_region_bound_pairs) + .inspect(|bound| { + debug!( + "declared_generic_bounds_from_env_with_compare_fn: result predicate = {:?}", + bound + ) + }) + .collect() } /// Given a projection like `>::Bar`, returns any bounds @@ -237,13 +253,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { fn declared_projection_bounds_from_trait( &self, projection_ty: ty::ProjectionTy<'tcx>, - ) -> Vec> { + ) -> impl Iterator> + 'cx + Captures<'gcx> { debug!("projection_bounds(projection_ty={:?})", projection_ty); - let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id); - for r in &mut bounds { - *r = r.subst(self.tcx, projection_ty.substs); - } - bounds + let tcx = self.tcx; + self.region_bounds_declared_on_associated_item(projection_ty.item_def_id) + .map(move |r| r.subst(tcx, projection_ty.substs)) } /// Given the def-id of an associated item, returns any region @@ -279,7 +293,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { fn region_bounds_declared_on_associated_item( &self, assoc_item_def_id: DefId, - ) -> Vec> { + ) -> impl Iterator> + 'cx + Captures<'gcx> { let tcx = self.tcx; let assoc_item = tcx.associated_item(assoc_item_def_id); let trait_def_id = assoc_item.container.assert_trait(); @@ -289,7 +303,7 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { self.collect_outlives_from_predicate_list( move |ty| ty == identity_proj, traits::elaborate_predicates(tcx, trait_predicates.predicates), - ).collect() + ).map(|b| b.1) } /// Searches through a predicate list for a predicate `T: 'a`. @@ -302,12 +316,11 @@ impl<'cx, 'gcx, 'tcx> VerifyBoundCx<'cx, 'gcx, 'tcx> { &self, compare_ty: impl Fn(Ty<'tcx>) -> bool, predicates: impl IntoIterator>>, - ) -> impl Iterator> { + ) -> impl Iterator, ty::Region<'tcx>>> { predicates .into_iter() .filter_map(|p| p.as_ref().to_opt_type_outlives()) .filter_map(|p| p.no_late_bound_regions()) .filter(move |p| compare_ty(p.0)) - .map(|p| p.1) } } From 2392a093977815bffe1a9a324ab8235d1802fbe4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 21 Sep 2018 15:25:42 -0400 Subject: [PATCH 26/34] region_infer: rustfmt --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 250db14bc95c6..d76387f5b3101 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -10,9 +10,7 @@ use super::universal_regions::UniversalRegions; use borrow_check::nll::constraints::graph::NormalConstraintGraph; -use borrow_check::nll::constraints::{ - ConstraintSccIndex, ConstraintSet, OutlivesConstraint, -}; +use borrow_check::nll::constraints::{ConstraintSccIndex, ConstraintSet, OutlivesConstraint}; use borrow_check::nll::region_infer::values::{RegionElement, ToElementIndex}; use borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations; use borrow_check::nll::type_check::Locations; @@ -29,7 +27,7 @@ use rustc::util::common; use rustc_data_structures::bit_set::BitSet; use rustc_data_structures::graph::scc::Sccs; use rustc_data_structures::indexed_vec::IndexVec; -use rustc_errors::{DiagnosticBuilder, Diagnostic}; +use rustc_errors::{Diagnostic, DiagnosticBuilder}; use std::rc::Rc; From 0f5dae0322ac47ea457aafadc11fc0eb6c8f2449 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 21 Sep 2018 15:27:53 -0400 Subject: [PATCH 27/34] switch to use `VerifyBound` instead of `RegionTest` --- .../borrow_check/nll/region_infer/mod.rs | 64 +++++++------------ .../nll/type_check/constraint_conversion.rs | 35 +--------- 2 files changed, 27 insertions(+), 72 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index d76387f5b3101..a0bc734d5d75a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -16,7 +16,7 @@ use borrow_check::nll::type_check::free_region_relations::UniversalRegionRelatio use borrow_check::nll::type_check::Locations; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryRegionConstraint; -use rustc::infer::region_constraints::{GenericKind, VarInfos}; +use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; use rustc::mir::{ ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, Local, Location, @@ -160,33 +160,7 @@ pub struct TypeTest<'tcx> { /// A test which, if met by the region `'x`, proves that this type /// constraint is satisfied. - pub test: RegionTest, -} - -/// A "test" that can be applied to some "subject region" `'x`. These are used to -/// describe type constraints. Tests do not presently affect the -/// region values that get inferred for each variable; they only -/// examine the results *after* inference. This means they can -/// conveniently include disjuction ("a or b must be true"). -#[derive(Clone, Debug)] -pub enum RegionTest { - /// The subject region `'x` must by outlived by the given region. - /// - /// This test comes from e.g. a where clause like `T: 'a`, which - /// implies that we know that `T: 'a`. Therefore, if we are trying - /// to prove that `T: 'x`, we can do so by showing that `'a: 'x`. - IsOutlivedBy(RegionVid), - - /// Any of the given tests are true. - /// - /// This test comes from e.g. a where clause like `T: 'a + 'b`, - /// which implies that we know that `T: 'a` and that `T: - /// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can - /// do so by showing that `'a: 'x` *or* `'b: 'x`. - Any(Vec), - - /// All of the given tests are true. - All(Vec), + pub verify_bound: VerifyBound<'tcx>, } impl<'tcx> RegionInferenceContext<'tcx> { @@ -571,7 +545,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if self.eval_region_test(mir, type_test.lower_bound, &type_test.test) { + if self.eval_verify_bound(mir, type_test.lower_bound, &type_test.verify_bound) { continue; } @@ -678,7 +652,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { generic_kind, lower_bound, locations, - test: _, + verify_bound: _, } = type_test; let generic_ty = generic_kind.to_ty(tcx); @@ -705,7 +679,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // where `ur` is a local bound -- we are sometimes in a // position to prove things that our caller cannot. See // #53570 for an example. - if self.eval_region_test(mir, ur, &type_test.test) { + if self.eval_verify_bound(mir, ur, &type_test.verify_bound) { continue; } @@ -877,22 +851,32 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Test if `test` is true when applied to `lower_bound` at /// `point`, and returns true or false. - fn eval_region_test(&self, mir: &Mir<'tcx>, lower_bound: RegionVid, test: &RegionTest) -> bool { + fn eval_verify_bound( + &self, + mir: &Mir<'tcx>, + lower_bound: RegionVid, + verify_bound: &VerifyBound<'tcx>, + ) -> bool { debug!( - "eval_region_test(lower_bound={:?}, test={:?})", - lower_bound, test + "eval_verify_bound(lower_bound={:?}, verify_bound={:?})", + lower_bound, verify_bound ); - match test { - RegionTest::IsOutlivedBy(r) => self.eval_outlives(mir, *r, lower_bound), + match verify_bound { + VerifyBound::IfEq(..) => false, // FIXME + + VerifyBound::OutlivedBy(r) => { + let r_vid = self.to_region_vid(r); + self.eval_outlives(mir, r_vid, lower_bound) + } - RegionTest::Any(tests) => tests + VerifyBound::AnyBound(verify_bounds) => verify_bounds .iter() - .any(|test| self.eval_region_test(mir, lower_bound, test)), + .any(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)), - RegionTest::All(tests) => tests + VerifyBound::AllBounds(verify_bounds) => verify_bounds .iter() - .all(|test| self.eval_region_test(mir, lower_bound, test)), + .all(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)), } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index f50516d014aa9..0ad6183960da9 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -11,7 +11,7 @@ use borrow_check::location::LocationTable; use borrow_check::nll::constraints::{ConstraintCategory, ConstraintSet, OutlivesConstraint}; use borrow_check::nll::facts::AllFacts; -use borrow_check::nll::region_infer::{RegionTest, TypeTest}; +use borrow_check::nll::region_infer::TypeTest; use borrow_check::nll::type_check::Locations; use borrow_check::nll::universal_regions::UniversalRegions; use rustc::infer::canonical::QueryRegionConstraint; @@ -140,44 +140,15 @@ impl<'a, 'gcx, 'tcx> ConstraintConversion<'a, 'gcx, 'tcx> { &self, generic_kind: GenericKind<'tcx>, region: ty::Region<'tcx>, - bound: VerifyBound<'tcx>, + verify_bound: VerifyBound<'tcx>, ) -> TypeTest<'tcx> { let lower_bound = self.to_region_vid(region); - let test = self.verify_bound_to_region_test(&bound); - TypeTest { generic_kind, lower_bound, locations: self.locations, - test, - } - } - - fn verify_bound_to_region_test(&self, verify_bound: &VerifyBound<'tcx>) -> RegionTest { - match verify_bound { - VerifyBound::IfEq(..) => { - // FIXME: always false right now - RegionTest::Any(vec![]) - } - - VerifyBound::OutlivedBy(r) => RegionTest::IsOutlivedBy( - self.to_region_vid(r) - ), - - VerifyBound::AnyBound(bounds) => RegionTest::Any( - bounds - .iter() - .map(|b| self.verify_bound_to_region_test(b)) - .collect(), - ), - - VerifyBound::AllBounds(bounds) => RegionTest::All( - bounds - .iter() - .map(|b| self.verify_bound_to_region_test(b)) - .collect(), - ), + verify_bound, } } From 0b4791e60bb44ae7dcf3b4bb312cc20b99193c4e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 21 Sep 2018 19:26:24 -0400 Subject: [PATCH 28/34] make NLL handle `IfEq` bounds by using SCC normalization --- src/librustc_data_structures/indexed_vec.rs | 7 + .../borrow_check/nll/region_infer/mod.rs | 122 ++++++++- src/test/ui/nll/ty-outlives/issue-53789-1.rs | 91 +++++++ src/test/ui/nll/ty-outlives/issue-53789-2.rs | 251 ++++++++++++++++++ .../ui/nll/ty-outlives/projection-body.rs | 25 ++ ...projection-where-clause-env-wrong-bound.rs | 36 +++ ...jection-where-clause-env-wrong-lifetime.rs | 24 ++ .../projection-where-clause-env.rs | 30 +++ .../projection-where-clause-none.rs | 26 ++ .../projection-where-clause-trait.rs | 27 ++ 10 files changed, 630 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/issue-53789-1.rs create mode 100644 src/test/ui/nll/ty-outlives/issue-53789-2.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-body.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-env.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-none.rs create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 2f11fea46d69a..a59bf9d530c4d 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -535,6 +535,13 @@ impl IndexVec { self.raw.len() } + /// Gives the next index that will be assigned when `push` is + /// called. + #[inline] + pub fn next_index(&self) -> I { + I::new(self.len()) + } + #[inline] pub fn is_empty(&self) -> bool { self.raw.is_empty() diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index a0bc734d5d75a..2dbb5cd9deb14 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -69,6 +69,15 @@ pub struct RegionInferenceContext<'tcx> { /// visible from this index. scc_universes: IndexVec, + /// Contains a "representative" from each SCC. This will be the + /// minimal RegionVid belonging to that universe. It is used as a + /// kind of hacky way to manage checking outlives relationships, + /// since we can 'canonicalize' each region to the representative + /// of its SCC and be sure that -- if they have the same repr -- + /// they *must* be equal (though not having the same repr does not + /// mean they are unequal). + scc_representatives: IndexVec, + /// The final inferred values of the region variables; we compute /// one value per SCC. To get the value for any given *region*, /// you first find which scc it is a part of. @@ -208,6 +217,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions); + let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions); + let mut result = Self { definitions, liveness_constraints, @@ -215,6 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint_graph, constraint_sccs, scc_universes, + scc_representatives, scc_values, type_tests, universal_regions, @@ -251,6 +263,27 @@ impl<'tcx> RegionInferenceContext<'tcx> { scc_universes } + /// For each SCC, we compute a unique `RegionVid` (in fact, the + /// minimal one that belongs to the SCC). See + /// `scc_representatives` field of `RegionInferenceContext` for + /// more details. + fn compute_scc_representatives( + constraints_scc: &Sccs, + definitions: &IndexVec>, + ) -> IndexVec { + let num_sccs = constraints_scc.num_sccs(); + let next_region_vid = definitions.next_index(); + let mut scc_representatives = IndexVec::from_elem_n(next_region_vid, num_sccs); + + for region_vid in definitions.indices() { + let scc = constraints_scc.scc(region_vid); + let prev_min = scc_representatives[scc]; + scc_representatives[scc] = region_vid.min(prev_min); + } + + scc_representatives + } + /// Initializes the region variables for each universally /// quantified region (lifetime parameter). The first N variables /// always correspond to the regions appearing in the function @@ -545,7 +578,14 @@ impl<'tcx> RegionInferenceContext<'tcx> { for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); - if self.eval_verify_bound(mir, type_test.lower_bound, &type_test.verify_bound) { + let generic_ty = type_test.generic_kind.to_ty(tcx); + if self.eval_verify_bound( + tcx, + mir, + generic_ty, + type_test.lower_bound, + &type_test.verify_bound, + ) { continue; } @@ -679,7 +719,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // where `ur` is a local bound -- we are sometimes in a // position to prove things that our caller cannot. See // #53570 for an example. - if self.eval_verify_bound(mir, ur, &type_test.verify_bound) { + if self.eval_verify_bound(tcx, mir, generic_ty, ur, &type_test.verify_bound) { continue; } @@ -853,7 +893,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `point`, and returns true or false. fn eval_verify_bound( &self, + tcx: TyCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>, + generic_ty: Ty<'tcx>, lower_bound: RegionVid, verify_bound: &VerifyBound<'tcx>, ) -> bool { @@ -863,23 +905,85 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); match verify_bound { - VerifyBound::IfEq(..) => false, // FIXME + VerifyBound::IfEq(test_ty, verify_bound1) => { + self.eval_if_eq(tcx, mir, generic_ty, lower_bound, test_ty, verify_bound1) + } VerifyBound::OutlivedBy(r) => { let r_vid = self.to_region_vid(r); self.eval_outlives(mir, r_vid, lower_bound) } - VerifyBound::AnyBound(verify_bounds) => verify_bounds - .iter() - .any(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)), + VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { + self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound) + }), - VerifyBound::AllBounds(verify_bounds) => verify_bounds - .iter() - .all(|verify_bound| self.eval_verify_bound(mir, lower_bound, verify_bound)), + VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| { + self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound) + }), } } + fn eval_if_eq( + &self, + tcx: TyCtxt<'_, '_, 'tcx>, + mir: &Mir<'tcx>, + generic_ty: Ty<'tcx>, + lower_bound: RegionVid, + test_ty: Ty<'tcx>, + verify_bound: &VerifyBound<'tcx>, + ) -> bool { + let generic_ty_normalized = self.normalize_to_scc_representatives(tcx, generic_ty); + let test_ty_normalized = self.normalize_to_scc_representatives(tcx, test_ty); + if generic_ty_normalized == test_ty_normalized { + self.eval_verify_bound(tcx, mir, generic_ty, lower_bound, verify_bound) + } else { + false + } + } + + /// This is a conservative normalization procedure. It takes every + /// free region in `value` and replaces it with the + /// "representative" of its SCC (see `scc_representatives` field). + /// We are guaranteed that if two values normalize to the same + /// thing, then they are equal; this is a conservative check in + /// that they could still be equal even if they normalize to + /// different results. (For example, there might be two regions + /// with the same value that are not in the same SCC). + /// + /// NB. This is not an ideal approach and I would like to revisit + /// it. However, it works pretty well in practice. In particular, + /// this is needed to deal with projection outlives bounds like + /// + /// >::Item: '1 + /// + /// In particular, this routine winds up being important when + /// there are bounds like `where >::Item: 'b` in the + /// environment. In this case, if we can show that `'0 == 'a`, + /// and that `'b: '1`, then we know that the clause is + /// satisfied. In such cases, particularly due to limitations of + /// the trait solver =), we usually wind up with a where-clause like + /// `T: Foo<'a>` in scope, which thus forces `'0 == 'a` to be added as + /// a constraint, and thus ensures that they are in the same SCC. + /// + /// So why can't we do a more correct routine? Well, we could + /// *almost* use the `relate_tys` code, but the way it is + /// currently setup it creates inference variables to deal with + /// higher-ranked things and so forth, and right now the inference + /// context is not permitted to make more inference variables. So + /// we use this kind of hacky solution. + fn normalize_to_scc_representatives(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + tcx.fold_regions(&value, &mut false, |r, _db| { + let vid = self.to_region_vid(r); + let scc = self.constraint_sccs.scc(vid); + let repr = self.scc_representatives[scc]; + tcx.mk_region(ty::ReVar(repr)) + }) + } + // Evaluate whether `sup_region: sub_region @ point`. fn eval_outlives( &self, diff --git a/src/test/ui/nll/ty-outlives/issue-53789-1.rs b/src/test/ui/nll/ty-outlives/issue-53789-1.rs new file mode 100644 index 0000000000000..593cdfdbf711a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/issue-53789-1.rs @@ -0,0 +1,91 @@ +// Regression test for #53789. +// +// compile-pass + +#![feature(nll)] +#![allow(unused_variables)] + +use std::collections::BTreeMap; + +trait ValueTree { + type Value; +} + +trait Strategy { + type Value: ValueTree; +} + +type StrategyFor = StrategyType<'static, A>; +type StrategyType<'a, A> = >::Strategy; + +impl Strategy for (K, V) { + type Value = TupleValueTree<(K, V)>; +} + +impl ValueTree for TupleValueTree<(K, V)> { + type Value = BTreeMapValueTree; +} + +struct TupleValueTree { + tree: T, +} + +struct BTreeMapStrategy(std::marker::PhantomData<(K, V)>) +where + K: Strategy, + V: Strategy; + +struct BTreeMapValueTree(std::marker::PhantomData<(K, V)>) +where + K: ValueTree, + V: ValueTree; + +impl Strategy for BTreeMapStrategy +where + K: Strategy, + V: Strategy, +{ + type Value = BTreeMapValueTree; +} + +impl ValueTree for BTreeMapValueTree +where + K: ValueTree, + V: ValueTree, +{ + type Value = BTreeMap; +} + +trait Arbitrary<'a>: Sized { + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy; + type Parameters; + type Strategy: Strategy; + type ValueTree: ValueTree; +} + +impl<'a, A, B> Arbitrary<'a> for BTreeMap +where + A: Arbitrary<'static>, + B: Arbitrary<'static>, + StrategyFor: 'static, + StrategyFor: 'static, +{ + type ValueTree = ::Value; + type Parameters = (A::Parameters, B::Parameters); + type Strategy = BTreeMapStrategy; + fn arbitrary_with(args: Self::Parameters) -> BTreeMapStrategy { + let (a, b) = args; + btree_map(any_with::(a), any_with::(b)) + } +} + +fn btree_map(key: K, value: V) -> BTreeMapStrategy { + unimplemented!() +} + +fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) -> StrategyType<'a, A> { + unimplemented!() +} + +fn main() { } + diff --git a/src/test/ui/nll/ty-outlives/issue-53789-2.rs b/src/test/ui/nll/ty-outlives/issue-53789-2.rs new file mode 100644 index 0000000000000..62e2833aa1b1a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/issue-53789-2.rs @@ -0,0 +1,251 @@ +// Regression test for #53789. +// +// compile-pass + +#![feature(nll)] +#![allow(unused_variables)] + +use std::collections::BTreeMap; +use std::ops::Range; +use std::cmp::Ord; + +macro_rules! valuetree { + () => { + type ValueTree = + ::Value; + }; +} + +macro_rules! product_unpack { + ($factor: pat) => { + ($factor,) + }; + ($($factor: pat),*) => { + ( $( $factor ),* ) + }; + ($($factor: pat),*,) => { + ( $( $factor ),* ) + }; +} + +macro_rules! product_type { + ($factor: ty) => { + ($factor,) + }; + ($($factor: ty),*) => { + ( $( $factor, )* ) + }; + ($($factor: ty),*,) => { + ( $( $factor, )* ) + }; +} + +macro_rules! default { + ($type: ty, $val: expr) => { + impl Default for $type { + fn default() -> Self { $val.into() } + } + }; +} + +// Pervasive internal sugar +macro_rules! mapfn { + ($(#[$meta:meta])* [$($vis:tt)*] + fn $name:ident[$($gen:tt)*]($parm:ident: $input:ty) -> $output:ty { + $($body:tt)* + }) => { + $(#[$meta])* + #[derive(Clone, Copy)] + $($vis)* struct $name; + impl $($gen)* statics::MapFn<$input> for $name { + type Output = $output; + } + } +} + +macro_rules! opaque_strategy_wrapper { + ($(#[$smeta:meta])* pub struct $stratname:ident + [$($sgen:tt)*][$($swhere:tt)*] + ($innerstrat:ty) -> $stratvtty:ty; + + $(#[$vmeta:meta])* pub struct $vtname:ident + [$($vgen:tt)*][$($vwhere:tt)*] + ($innervt:ty) -> $actualty:ty; + ) => { + $(#[$smeta])* struct $stratname $($sgen)* (std::marker::PhantomData<(K, V)>) + $($swhere)*; + + $(#[$vmeta])* struct $vtname $($vgen)* ($innervt) $($vwhere)*; + + impl $($sgen)* Strategy for $stratname $($sgen)* $($swhere)* { + type Value = $stratvtty; + } + + impl $($vgen)* ValueTree for $vtname $($vgen)* $($vwhere)* { + type Value = $actualty; + } + } +} + +trait ValueTree { + type Value; +} + +trait Strategy { + type Value : ValueTree; +} + +#[derive(Clone)] +struct VecStrategy { + element: T, + size: Range, +} + +fn vec(element: T, size: Range) + -> VecStrategy { + VecStrategy { + element: element, + size: size, + } +} + +type ValueFor = <::Value as ValueTree>::Value; + +trait Arbitrary<'a>: Sized { + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy; + + type Parameters: Default; + type Strategy: Strategy; + type ValueTree: ValueTree; +} + +type StrategyFor = StrategyType<'static, A>; +type StrategyType<'a, A> = >::Strategy; + +//#[derive(Clone, PartialEq, Eq, Hash, Debug, From, Into)] +struct SizeBounds(Range); +default!(SizeBounds, 0..100); + + +impl From> for SizeBounds { + fn from(high: Range) -> Self { + unimplemented!() + } +} + +impl From for Range { + fn from(high: SizeBounds) -> Self { + unimplemented!() + } +} + + +fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) + -> StrategyType<'a, A> { + unimplemented!() +} + +impl Strategy for (K, V) where + ::Value: Ord { + type Value = TupleValueTree<(K, V)>; +} + +impl ValueTree for TupleValueTree<(K, V)> where + ::Value: Ord { + type Value = BTreeMapValueTree; +} + +#[derive(Clone)] +struct VecValueTree { + elements: Vec, +} + +#[derive(Clone, Copy)] +struct TupleValueTree { + tree: T, +} + +opaque_strategy_wrapper! { + #[derive(Clone)] + pub struct BTreeMapStrategy[] + [where K : Strategy, V : Strategy, ValueFor : Ord]( + statics::Filter, + VecToBTreeMap>, MinSize>) + -> BTreeMapValueTree; + + #[derive(Clone)] + pub struct BTreeMapValueTree[] + [where K : ValueTree, V : ValueTree, K::Value : Ord]( + statics::Filter>, + VecToBTreeMap>, MinSize>) + -> BTreeMap; +} + +type RangedParams2 = product_type![SizeBounds, A, B]; + +impl<'a, A, B> Arbitrary<'a> for BTreeMap +where + A: Arbitrary<'static> + Ord, + B: Arbitrary<'static>, +StrategyFor: 'static, +StrategyFor: 'static, +{ + valuetree!(); + type Parameters = RangedParams2; + type Strategy = BTreeMapStrategy; + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + let product_unpack![range, a, b] = args; + btree_map(any_with::(a), any_with::(b), range.into()) + } +} + +#[derive(Clone, Copy)] +struct MinSize(usize); + +mapfn! { + [] fn VecToBTreeMap[] + (vec: Vec<(K, V)>) -> BTreeMap + { + vec.into_iter().collect() + } +} + +fn btree_map + (key: K, value: V, size: Range) + -> BTreeMapStrategy +where ValueFor : Ord { + unimplemented!() +} + +mod statics { + pub(super) trait MapFn { + type Output; + } + + #[derive(Clone)] + pub struct Filter { + source: S, + fun: F, + } + + impl Filter { + pub fn new(source: S, whence: String, filter: F) -> Self { + unimplemented!() + } + } + + #[derive(Clone)] + pub struct Map { + source: S, + fun: F, + } + + impl Map { + pub fn new(source: S, fun: F) -> Self { + unimplemented!() + } + } +} + +fn main() { } + diff --git a/src/test/ui/nll/ty-outlives/projection-body.rs b/src/test/ui/nll/ty-outlives/projection-body.rs new file mode 100644 index 0000000000000..680e26de65bc0 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-body.rs @@ -0,0 +1,25 @@ +// Test that when we infer the lifetime to a subset of the fn body, it +// works out. + +trait MyTrait<'a> { + type Output; +} + +fn foo1() +where + for<'x> T: MyTrait<'x>, +{ + // Here the region `'c` in `>::Output` will be + // inferred to a subset of the fn body. + let x = bar::(); + drop(x); +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.rs new file mode 100644 index 0000000000000..9c2cbfd4a4530 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.rs @@ -0,0 +1,36 @@ +#![feature(nll)] + +// Test that we are able to establish that `>::Output` outlives `'b` here. We need to prove however +// that `>::Output` outlives `'a`, so we also have to +// prove that `'b: 'a`. + +trait MyTrait<'a> { + type Output; +} + +fn foo1<'a, 'b, T>() -> &'a () +where + T: MyTrait<'a>, + >::Output: 'b, +{ + bar::() //~ ERROR may not live long enough +} + +fn foo2<'a, 'b, T>() -> &'a () +where + T: MyTrait<'a>, + >::Output: 'b, + 'b: 'a, +{ + bar::() // OK +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs new file mode 100644 index 0000000000000..07cc37a8b290a --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs @@ -0,0 +1,24 @@ +// Test that if we need to prove that `>::Output: +// 'a`, but we only know that `>::Output: 'a`, that +// doesn't suffice. + +trait MyTrait<'a> { + type Output; +} + +fn foo1<'a, 'b, T>() -> &'a () +where + for<'x> T: MyTrait<'x>, + >::Output: 'a, +{ + bar::<>::Output>() //~ ERROR the associated type `>::Output` may not live long enough +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-env.rs new file mode 100644 index 0000000000000..c6935badf54b2 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env.rs @@ -0,0 +1,30 @@ +#![feature(nll)] + +// Test that when we have a `>::Output: 'a` +// relationship in the environment we take advantage of it. In this +// case, that means we **don't** have to prove that `T: 'a`. +// +// Regression test for #53121. +// +// compile-pass + +trait MyTrait<'a> { + type Output; +} + +fn foo<'a, T>() -> &'a () +where + T: MyTrait<'a>, + >::Output: 'a, +{ + bar::() +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-none.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-none.rs new file mode 100644 index 0000000000000..f0f72f5d27f76 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-none.rs @@ -0,0 +1,26 @@ +#![feature(nll)] + +// Test that we are NOT able to establish that `>::Output: 'a` outlives `'a` here -- we have only one +// recourse, which is to prove that `T: 'a` and `'a: 'a`, but we don't +// know that `T: 'a`. + +trait MyTrait<'a> { + type Output; +} + +fn foo<'a, T>() -> &'a () +where + T: MyTrait<'a>, +{ + bar::() //~ ERROR the parameter type `T` may not live long enough +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs new file mode 100644 index 0000000000000..7c7d64a8cb4d3 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-trait.rs @@ -0,0 +1,27 @@ +#![feature(nll)] + +// Test that we are able to establish that `>::Output: 'a` outlives `'a` (because the trait says +// so). +// +// compile-pass + +trait MyTrait<'a> { + type Output: 'a; +} + +fn foo<'a, T>() -> &'a () +where + T: MyTrait<'a>, +{ + bar::() +} + +fn bar<'a, T>() -> &'a () +where + T: 'a, +{ + &() +} + +fn main() {} From a13c9f6bfd5266e8b5f567a204f8f3e8051a936d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Sep 2018 13:05:10 -0400 Subject: [PATCH 29/34] convert from an `UnlessNll` flag to a `SuppressRegionErrors` flag Hopefully this will help clarify the behavior in the various borrowck modes --- src/librustc/infer/error_reporting/mod.rs | 21 +++-------- src/librustc/infer/mod.rs | 45 +++++++++++++++++------ src/librustc/traits/mod.rs | 4 +- src/librustc_typeck/check/dropck.rs | 4 +- src/librustc_typeck/check/regionck.rs | 17 +++++---- src/librustc_typeck/coherence/builtin.rs | 4 +- 6 files changed, 55 insertions(+), 40 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index fe9e00f8a6038..eda4fc041e892 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -55,7 +55,7 @@ //! ported to this system, and which relies on string concatenation at the //! time of error detection. -use infer::{self, UnlessNll}; +use infer::{self, SuppressRegionErrors}; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; use super::region_constraints::GenericKind; use super::lexical_region_resolve::RegionResolutionError; @@ -68,7 +68,6 @@ use middle::region; use traits::{ObligationCause, ObligationCauseCode}; use ty::{self, subst::Subst, Region, Ty, TyCtxt, TypeFoldable, TyKind}; use ty::error::TypeError; -use session::config::BorrowckMode; use syntax::ast::DUMMY_NODE_ID; use syntax_pos::{Pos, Span}; use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; @@ -298,20 +297,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { &self, region_scope_tree: ®ion::ScopeTree, errors: &Vec>, - unless_nll: UnlessNll, + suppress: SuppressRegionErrors, ) { - debug!("report_region_errors(): {} errors to start", errors.len()); - - // If the errors will later be reported by NLL, choose wether to display them or not based - // on the borrowck mode - if unless_nll.0 { - match self.tcx.borrowck_mode() { - // If we're on AST or Migrate mode, report AST region errors - BorrowckMode::Ast | BorrowckMode::Migrate => {}, - // If we're on MIR or Compare mode, don't report AST region errors as they should - // be reported by NLL - BorrowckMode::Compare | BorrowckMode::Mir => return, - } + debug!("report_region_errors(): {} errors to start, suppress = {:?}", errors.len(), suppress); + + if suppress.suppressed() { + return; } // try to pre-process the errors, which will group some of them diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index bd8c49620518f..dc10ec03feef0 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -24,6 +24,7 @@ use middle::free_region::RegionRelations; use middle::lang_items; use middle::region; use rustc_data_structures::unify as ut; +use session::config::BorrowckMode; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::BTreeMap; use std::fmt; @@ -80,15 +81,37 @@ pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult = Result; // "fixup result" -/// A flag that is given when running region resolution: if true, it -/// indicates that we should not report the region errors to the user -/// if NLL is enabled, since NLL will also detect them (and do a -/// better job of it). -/// -/// Currently, NLL only runs on HIR bodies, so you should use `false` -/// unless you are region-checking a `hir::Body` (basically, a fn or -/// expression). -pub struct UnlessNll(pub bool); +/// A flag that is used to suppress region errors. This is normally +/// false, but sometimes -- when we are doing region checks that the +/// NLL borrow checker will also do -- it might be set to true. +#[derive(Copy, Clone, Default, Debug)] +pub struct SuppressRegionErrors { + suppressed: bool +} + +impl SuppressRegionErrors { + pub fn suppressed(self) -> bool { + self.suppressed + } + + /// Indicates that the MIR borrowck will repeat these region + /// checks, so we should ignore errors if NLL is (unconditionally) + /// enabled. + pub fn when_nll_is_enabled(tcx: TyCtxt<'_, '_, '_>) -> Self { + match tcx.borrowck_mode() { + // If we're on AST or Migrate mode, report AST region errors + BorrowckMode::Ast | BorrowckMode::Migrate => SuppressRegionErrors { + suppressed: false + }, + + // If we're on MIR or Compare mode, don't report AST region errors as they should + // be reported by NLL + BorrowckMode::Compare | BorrowckMode::Mir => SuppressRegionErrors { + suppressed: true + }, + } + } +} pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'gcx, 'tcx>, @@ -1049,7 +1072,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_context: DefId, region_map: ®ion::ScopeTree, outlives_env: &OutlivesEnvironment<'tcx>, - unless_nll: UnlessNll, + suppress: SuppressRegionErrors, ) { assert!( self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(), @@ -1081,7 +1104,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // this infcx was in use. This is totally hokey but // otherwise we have a hard time separating legit region // errors from silly ones. - self.report_region_errors(region_map, &errors, unless_nll); + self.report_region_errors(region_map, &errors, suppress); } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 6c0fe157a2a65..406d3a55b1f7c 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -20,7 +20,7 @@ pub use self::ObligationCauseCode::*; use chalk_engine; use hir; use hir::def_id::DefId; -use infer::UnlessNll; +use infer::SuppressRegionErrors; use infer::outlives::env::OutlivesEnvironment; use middle::region; use mir::interpret::ConstEvalErr; @@ -720,7 +720,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, region_context, ®ion_scope_tree, &outlives_env, - UnlessNll(false), + SuppressRegionErrors::default(), ); let predicates = match infcx.fully_resolve(&predicates) { diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index c6e0da309a40f..ccaa2f06f01de 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -11,7 +11,7 @@ use check::regionck::RegionCtxt; use hir::def_id::DefId; -use rustc::infer::{self, InferOk, UnlessNll}; +use rustc::infer::{self, InferOk, SuppressRegionErrors}; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::ty::subst::{Subst, Substs, UnpackedKind}; @@ -128,7 +128,7 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( // conservative. -nmatsakis let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env, UnlessNll(false)); + infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env, SuppressRegionErrors::default()); Ok(()) }) } diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index bc3525adad562..d7d006728536a 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -88,8 +88,8 @@ use middle::mem_categorization as mc; use middle::mem_categorization::Categorization; use middle::region; use rustc::hir::def_id::DefId; -use rustc::infer::{self, RegionObligation, UnlessNll}; use rustc::infer::outlives::env::OutlivesEnvironment; +use rustc::infer::{self, RegionObligation, SuppressRegionErrors}; use rustc::ty::adjustment; use rustc::ty::subst::Substs; use rustc::ty::{self, Ty}; @@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_body(body); rcx.visit_region_obligations(id); } - rcx.resolve_regions_and_report_errors(UnlessNll(true)); + rcx.resolve_regions_and_report_errors(SuppressRegionErrors::when_nll_is_enabled(self.tcx)); assert!(self.tables.borrow().free_region_map.is_empty()); self.tables.borrow_mut().free_region_map = rcx.outlives_environment.into_free_region_map(); @@ -162,7 +162,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .add_implied_bounds(self, wf_tys, item_id, span); rcx.outlives_environment.save_implied_bounds(item_id); rcx.visit_region_obligations(item_id); - rcx.resolve_regions_and_report_errors(UnlessNll(false)); + rcx.resolve_regions_and_report_errors(SuppressRegionErrors::default()); } /// Region check a function body. Not invoked on closures, but @@ -190,7 +190,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcx.visit_fn_body(fn_id, body, self.tcx.hir.span(fn_id)); } - rcx.resolve_regions_and_report_errors(UnlessNll(true)); + rcx.resolve_regions_and_report_errors(SuppressRegionErrors::when_nll_is_enabled(self.tcx)); // In this mode, we also copy the free-region-map into the // tables of the enclosing fcx. In the other regionck modes @@ -314,7 +314,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { id: ast::NodeId, // the id of the fn itself body: &'gcx hir::Body, span: Span, - ) { + ) { // When we enter a function, we can derive debug!("visit_fn_body(id={})", id); @@ -355,7 +355,8 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { body_id.node_id, span, ); - self.outlives_environment.save_implied_bounds(body_id.node_id); + self.outlives_environment + .save_implied_bounds(body_id.node_id); self.link_fn_args( region::Scope { id: body.value.hir_id.local_id, @@ -392,7 +393,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.select_all_obligations_or_error(); } - fn resolve_regions_and_report_errors(&self, unless_nll: UnlessNll) { + fn resolve_regions_and_report_errors(&self, suppress: SuppressRegionErrors) { self.infcx.process_registered_region_obligations( self.outlives_environment.region_bound_pairs_map(), self.implicit_region_bound, @@ -403,7 +404,7 @@ impl<'a, 'gcx, 'tcx> RegionCtxt<'a, 'gcx, 'tcx> { self.subject_def_id, &self.region_scope_tree, &self.outlives_environment, - unless_nll, + suppress, ); } diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index 4def76e892289..db08bf8095352 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -11,7 +11,7 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. -use rustc::infer::UnlessNll; +use rustc::infer::SuppressRegionErrors; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; use rustc::middle::lang_items::UnsizeTraitLangItem; @@ -397,7 +397,7 @@ pub fn coerce_unsized_info<'a, 'gcx>(gcx: TyCtxt<'a, 'gcx, 'gcx>, impl_did, ®ion_scope_tree, &outlives_env, - UnlessNll(false), + SuppressRegionErrors::default(), ); CoerceUnsizedInfo { From f87189dd3c08c7b8b930cfbe25266318adba293c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Sep 2018 15:36:50 -0400 Subject: [PATCH 30/34] update tests and add stderr files --- .../ui/nll/ty-outlives/projection-body.rs | 2 ++ .../projection-one-region-closure.rs | 12 ++----- .../projection-one-region-closure.stderr | 18 +++------- ...ojection-one-region-trait-bound-closure.rs | 12 ++----- ...tion-one-region-trait-bound-closure.stderr | 23 ++++-------- ...ojection-two-region-trait-bound-closure.rs | 12 ++----- ...tion-two-region-trait-bound-closure.stderr | 36 ++++++++----------- ...ection-where-clause-env-wrong-bound.stderr | 11 ++++++ ...ion-where-clause-env-wrong-lifetime.stderr | 16 +++++++++ .../projection-where-clause-none.stderr | 11 ++++++ 10 files changed, 72 insertions(+), 81 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr diff --git a/src/test/ui/nll/ty-outlives/projection-body.rs b/src/test/ui/nll/ty-outlives/projection-body.rs index 680e26de65bc0..2e105ece8b55c 100644 --- a/src/test/ui/nll/ty-outlives/projection-body.rs +++ b/src/test/ui/nll/ty-outlives/projection-body.rs @@ -1,5 +1,7 @@ // Test that when we infer the lifetime to a subset of the fn body, it // works out. +// +// compile-pass trait MyTrait<'a> { type Output; diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs index d525135f759ce..6667457e13b15 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.rs @@ -74,18 +74,10 @@ where T: Anything<'b>, T::AssocType: 'a, { - // This error is unfortunate. This code ought to type-check: we - // are projecting `>::AssocType`, and we know - // that this outlives `'a` because of the where-clause. However, - // the way the region checker works, we don't register this - // outlives obligation, and hence we get an error: this is because - // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will - // equal `'b` or not, so we ignore the where-clause. Obviously we - // can do better here with a more involved verification step. + // We are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. with_signature(cell, t, |cell, t| require(cell, t)); - //~^ ERROR } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr index 981320af3bb0e..455fbba232007 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-closure.stderr @@ -106,7 +106,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a` note: External requirements - --> $DIR/projection-one-region-closure.rs:87:29 + --> $DIR/projection-one-region-closure.rs:80:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL | | where LL | | T: Anything<'b>, LL | | T::AssocType: 'a, ... | -LL | | //~^ ERROR +LL | | with_signature(cell, t, |cell, t| require(cell, t)); LL | | } | |_^ | @@ -139,16 +139,8 @@ LL | | } T ] -error[E0309]: the associated type `>::AssocType` may not live long enough - --> $DIR/projection-one-region-closure.rs:87:29 - | -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... - note: External requirements - --> $DIR/projection-one-region-closure.rs:98:29 + --> $DIR/projection-one-region-closure.rs:90:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +157,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where '_#2r: '_#3r note: No external requirements - --> $DIR/projection-one-region-closure.rs:92:1 + --> $DIR/projection-one-region-closure.rs:84:1 | LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -182,6 +174,6 @@ LL | | } T ] -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs index 923faadc29618..a94d8239fbec0 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.rs @@ -64,18 +64,10 @@ where T: Anything<'b>, T::AssocType: 'a, { - // This error is unfortunate. This code ought to type-check: we - // are projecting `>::AssocType`, and we know - // that this outlives `'a` because of the where-clause. However, - // the way the region checker works, we don't register this - // outlives obligation, and hence we get an error: this is because - // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will - // equal `'b` or not, so we ignore the where-clause. Obviously we - // can do better here with a more involved verification step. + // We are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. with_signature(cell, t, |cell, t| require(cell, t)); - //~^ ERROR } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr index 76d1eee5cb15b..b98aca74058b9 100644 --- a/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-one-region-trait-bound-closure.stderr @@ -88,7 +88,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a` note: External requirements - --> $DIR/projection-one-region-trait-bound-closure.rs:77:29 + --> $DIR/projection-one-region-trait-bound-closure.rs:70:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | | where LL | | T: Anything<'b>, LL | | T::AssocType: 'a, ... | -LL | | //~^ ERROR +LL | | with_signature(cell, t, |cell, t| require(cell, t)); LL | | } | |_^ | @@ -121,16 +121,8 @@ LL | | } T ] -error[E0309]: the associated type `>::AssocType` may not live long enough - --> $DIR/projection-one-region-trait-bound-closure.rs:77:29 - | -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... - note: External requirements - --> $DIR/projection-one-region-trait-bound-closure.rs:87:29 + --> $DIR/projection-one-region-trait-bound-closure.rs:79:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,7 +138,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where '_#2r: '_#3r note: No external requirements - --> $DIR/projection-one-region-trait-bound-closure.rs:82:1 + --> $DIR/projection-one-region-trait-bound-closure.rs:74:1 | LL | / fn elements_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -164,7 +156,7 @@ LL | | } ] note: External requirements - --> $DIR/projection-one-region-trait-bound-closure.rs:99:29 + --> $DIR/projection-one-region-trait-bound-closure.rs:91:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -179,7 +171,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where '_#1r: '_#2r note: No external requirements - --> $DIR/projection-one-region-trait-bound-closure.rs:91:1 + --> $DIR/projection-one-region-trait-bound-closure.rs:83:1 | LL | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -195,6 +187,5 @@ LL | | } T ] -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs index b492525352442..95c344e6dffb2 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.rs @@ -65,18 +65,10 @@ where T: Anything<'b, 'c>, T::AssocType: 'a, { - // This error is unfortunate. This code ought to type-check: we - // are projecting `>::AssocType`, and we know - // that this outlives `'a` because of the where-clause. However, - // the way the region checker works, we don't register this - // outlives obligation, and hence we get an error: this is because - // what we see is a projection like `>::AssocType`, and we don't yet know if `?0` will - // equal `'b` or not, so we ignore the where-clause. Obviously we - // can do better here with a more involved verification step. + // We are projecting `>::AssocType`, and we know + // that this outlives `'a` because of the where-clause. with_signature(cell, t, |cell, t| require(cell, t)); - //~^ ERROR associated type `>::AssocType` may not live long enough } #[rustc_regions] diff --git a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr index 372b803082f11..f872c87b0bb69 100644 --- a/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr +++ b/src/test/ui/nll/ty-outlives/projection-two-region-trait-bound-closure.stderr @@ -86,7 +86,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... note: External requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:78:29 + --> $DIR/projection-two-region-trait-bound-closure.rs:71:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | | where LL | | T: Anything<'b, 'c>, LL | | T::AssocType: 'a, ... | -LL | | //~^ ERROR associated type `>::AssocType` may not live long enough +LL | | with_signature(cell, t, |cell, t| require(cell, t)); LL | | } | |_^ | @@ -121,16 +121,8 @@ LL | | } T ] -error[E0309]: the associated type `>::AssocType` may not live long enough - --> $DIR/projection-two-region-trait-bound-closure.rs:78:29 - | -LL | with_signature(cell, t, |cell, t| require(cell, t)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: consider adding an explicit lifetime bound `>::AssocType: ReEarlyBound(0, 'a)`... - note: External requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:88:29 + --> $DIR/projection-two-region-trait-bound-closure.rs:80:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -147,7 +139,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where >::AssocType: '_#4r note: No external requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:83:1 + --> $DIR/projection-two-region-trait-bound-closure.rs:75:1 | LL | / fn elements_outlive1<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -166,7 +158,7 @@ LL | | } ] note: External requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:97:29 + --> $DIR/projection-two-region-trait-bound-closure.rs:89:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,7 +175,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where >::AssocType: '_#4r note: No external requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:92:1 + --> $DIR/projection-two-region-trait-bound-closure.rs:84:1 | LL | / fn elements_outlive2<'a, 'b, 'c, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -202,7 +194,7 @@ LL | | } ] note: External requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:105:29 + --> $DIR/projection-two-region-trait-bound-closure.rs:97:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -218,7 +210,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where >::AssocType: '_#2r note: No external requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:101:1 + --> $DIR/projection-two-region-trait-bound-closure.rs:93:1 | LL | / fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -235,7 +227,7 @@ LL | | } ] error: unsatisfied lifetime constraints - --> $DIR/projection-two-region-trait-bound-closure.rs:105:29 + --> $DIR/projection-two-region-trait-bound-closure.rs:97:29 | LL | fn two_regions<'a, 'b, T>(cell: Cell<&'a ()>, t: T) | -- -- lifetime `'b` defined here @@ -246,7 +238,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ closure body requires that `'b` must outlive `'a` note: External requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:115:29 + --> $DIR/projection-two-region-trait-bound-closure.rs:107:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -262,7 +254,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where >::AssocType: '_#3r note: No external requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:110:1 + --> $DIR/projection-two-region-trait-bound-closure.rs:102:1 | LL | / fn two_regions_outlive<'a, 'b, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -280,7 +272,7 @@ LL | | } ] note: External requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:127:29 + --> $DIR/projection-two-region-trait-bound-closure.rs:119:29 | LL | with_signature(cell, t, |cell, t| require(cell, t)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -295,7 +287,7 @@ LL | with_signature(cell, t, |cell, t| require(cell, t)); = note: where >::AssocType: '_#2r note: No external requirements - --> $DIR/projection-two-region-trait-bound-closure.rs:119:1 + --> $DIR/projection-two-region-trait-bound-closure.rs:111:1 | LL | / fn one_region<'a, T>(cell: Cell<&'a ()>, t: T) LL | | where @@ -311,6 +303,6 @@ LL | | } T ] -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr new file mode 100644 index 0000000000000..acb978b5d5a2c --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-bound.stderr @@ -0,0 +1,11 @@ +error[E0309]: the associated type `>::Output` may not live long enough + --> $DIR/projection-where-clause-env-wrong-bound.rs:17:5 + | +LL | bar::() //~ ERROR may not live long enough + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `>::Output: 'a`... + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr new file mode 100644 index 0000000000000..c6d0037138c42 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr @@ -0,0 +1,16 @@ +error[E0309]: the associated type `>::Output` may not live long enough + --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 + | +LL | bar::<>::Output>() //~ ERROR the associated type `>::Output` may not live long enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `>::Output: 'a`... +note: ...so that the type `>::Output` will meet its required lifetime bounds + --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 + | +LL | bar::<>::Output>() //~ ERROR the associated type `>::Output` may not live long enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr new file mode 100644 index 0000000000000..2d171a98789f8 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-none.stderr @@ -0,0 +1,11 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/projection-where-clause-none.rs:16:5 + | +LL | bar::() //~ ERROR the parameter type `T` may not live long enough + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. From ffe87f63eada242a94a762c4304b9fe059310289 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Sep 2018 11:32:39 -0400 Subject: [PATCH 31/34] rustfmt `error_reporting/mod.rs` and `dropck.rs` Pacify the mercilous tidy. --- src/librustc/infer/error_reporting/mod.rs | 132 +++++++++++----------- src/librustc_typeck/check/dropck.rs | 113 ++++++++++-------- 2 files changed, 138 insertions(+), 107 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index eda4fc041e892..e3bbdab4fd965 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -55,22 +55,22 @@ //! ported to this system, and which relies on string concatenation at the //! time of error detection. -use infer::{self, SuppressRegionErrors}; -use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; -use super::region_constraints::GenericKind; use super::lexical_region_resolve::RegionResolutionError; +use super::region_constraints::GenericKind; +use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; +use infer::{self, SuppressRegionErrors}; -use std::{cmp, fmt}; +use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; use hir; -use hir::Node; use hir::def_id::DefId; +use hir::Node; use middle::region; -use traits::{ObligationCause, ObligationCauseCode}; -use ty::{self, subst::Subst, Region, Ty, TyCtxt, TypeFoldable, TyKind}; -use ty::error::TypeError; +use std::{cmp, fmt}; use syntax::ast::DUMMY_NODE_ID; use syntax_pos::{Pos, Span}; -use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; +use traits::{ObligationCause, ObligationCauseCode}; +use ty::error::TypeError; +use ty::{self, subst::Subst, Region, Ty, TyCtxt, TyKind, TypeFoldable}; mod note; @@ -152,8 +152,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } // We shouldn't encounter an error message with ReClosureBound. - ty::ReCanonical(..) | - ty::ReClosureBound(..) => { + ty::ReCanonical(..) | ty::ReClosureBound(..) => { bug!("encountered unexpected ReClosureBound: {:?}", region,); } }; @@ -175,9 +174,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option) { match *region { - ty::ReEarlyBound(_) | ty::ReFree(_) => { + ty::ReEarlyBound(_) | ty::ReFree(_) => { self.msg_span_from_early_bound_and_free_regions(region) - }, + } ty::ReStatic => ("the static lifetime".to_owned(), None), _ => bug!("{:?}", region), } @@ -196,25 +195,28 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Some(Node::Item(it)) => Self::item_scope_tag(&it), Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it), Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it), - _ => unreachable!() + _ => unreachable!(), }; let (prefix, span) = match *region { ty::ReEarlyBound(ref br) => { let mut sp = cm.def_span(self.hir.span(node)); - if let Some(param) = self.hir.get_generics(scope).and_then(|generics| { - generics.get_named(&br.name) - }) { + if let Some(param) = self.hir + .get_generics(scope) + .and_then(|generics| generics.get_named(&br.name)) + { sp = param.span; } (format!("the lifetime {} as defined on", br.name), sp) } ty::ReFree(ty::FreeRegion { - bound_region: ty::BoundRegion::BrNamed(_, ref name), .. + bound_region: ty::BoundRegion::BrNamed(_, ref name), + .. }) => { let mut sp = cm.def_span(self.hir.span(node)); - if let Some(param) = self.hir.get_generics(scope).and_then(|generics| { - generics.get_named(&name) - }) { + if let Some(param) = self.hir + .get_generics(scope) + .and_then(|generics| generics.get_named(&name)) + { sp = param.span; } (format!("the lifetime {} as defined on", name), sp) @@ -277,9 +279,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { fn impl_item_scope_tag(item: &hir::ImplItem) -> &'static str { match item.node { hir::ImplItemKind::Method(..) => "method body", - hir::ImplItemKind::Const(..) | - hir::ImplItemKind::Existential(..) | - hir::ImplItemKind::Type(..) => "associated item", + hir::ImplItemKind::Const(..) + | hir::ImplItemKind::Existential(..) + | hir::ImplItemKind::Type(..) => "associated item", } } @@ -299,7 +301,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { errors: &Vec>, suppress: SuppressRegionErrors, ) { - debug!("report_region_errors(): {} errors to start, suppress = {:?}", errors.len(), suppress); + debug!( + "report_region_errors(): {} errors to start, suppress = {:?}", + errors.len(), + suppress + ); if suppress.suppressed() { return; @@ -473,17 +479,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } else { err.span_label(arm_span, msg); } - }, - hir::MatchSource::TryDesugar => { // Issue #51632 + } + hir::MatchSource::TryDesugar => { + // Issue #51632 if let Ok(try_snippet) = self.tcx.sess.source_map().span_to_snippet(arm_span) { err.span_suggestion_with_applicability( arm_span, "try wrapping with a success variant", format!("Ok({})", try_snippet), - Applicability::MachineApplicable + Applicability::MachineApplicable, ); } - }, + } _ => { let msg = "match arm with an incompatible type"; if self.tcx.sess.source_map().is_multiline(arm_span) { @@ -632,16 +639,21 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { fn strip_generic_default_params( &self, def_id: DefId, - substs: &ty::subst::Substs<'tcx> + substs: &ty::subst::Substs<'tcx>, ) -> &'tcx ty::subst::Substs<'tcx> { let generics = self.tcx.generics_of(def_id); let mut num_supplied_defaults = 0; - let mut type_params = generics.params.iter().rev().filter_map(|param| match param.kind { - ty::GenericParamDefKind::Lifetime => None, - ty::GenericParamDefKind::Type { has_default, .. } => { - Some((param.def_id, has_default)) - } - }).peekable(); + let mut type_params = generics + .params + .iter() + .rev() + .filter_map(|param| match param.kind { + ty::GenericParamDefKind::Lifetime => None, + ty::GenericParamDefKind::Type { has_default, .. } => { + Some((param.def_id, has_default)) + } + }) + .peekable(); let has_default = { let has_default = type_params.peek().map(|(_, has_default)| has_default); *has_default.unwrap_or(&false) @@ -675,10 +687,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) | (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Float(_)) - | ( - &ty::Infer(ty::InferTy::FloatVar(_)), - &ty::Infer(ty::InferTy::FloatVar(_)), - ) => true, + | (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Infer(ty::InferTy::FloatVar(_))) => { + true + } _ => false, } } @@ -694,11 +705,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { "&{}{}{}", r, if r == "" { "" } else { " " }, - if mutbl == hir::MutMutable { - "mut " - } else { - "" - } + if mutbl == hir::MutMutable { "mut " } else { "" } )); s.push_normal(ty.to_string()); } @@ -729,9 +736,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let common_len = cmp::min(len1, len2); let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); - let common_default_params = - remainder1.iter().rev().zip(remainder2.iter().rev()) - .filter(|(a, b)| a == b).count(); + let common_default_params = remainder1 + .iter() + .rev() + .zip(remainder2.iter().rev()) + .filter(|(a, b)| a == b) + .count(); let len = sub1.len() - common_default_params; // Only draw `<...>` if there're lifetime/type arguments. @@ -857,8 +867,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } // When encountering &T != &mut T, highlight only the borrow - (&ty::Ref(r1, ref_ty1, mutbl1), - &ty::Ref(r2, ref_ty2, mutbl2)) if equals(&ref_ty1, &ref_ty2) => { + (&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2)) + if equals(&ref_ty1, &ref_ty2) => + { let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0); push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1); @@ -1059,11 +1070,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, ) { - self.construct_generic_bound_failure(region_scope_tree, - span, - origin, - bound_kind, - sub) + self.construct_generic_bound_failure(region_scope_tree, span, origin, bound_kind, sub) .emit() } @@ -1074,8 +1081,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { origin: Option>, bound_kind: GenericKind<'tcx>, sub: Region<'tcx>, - ) -> DiagnosticBuilder<'a> - { + ) -> DiagnosticBuilder<'a> { // Attempt to obtain the span of the parameter so we can // suggest adding an explicit lifetime bound to it. let type_param_span = match (self.in_progress_tables, bound_kind) { @@ -1152,8 +1158,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { let tail = if has_lifetimes { " + " } else { "" }; let suggestion = format!("{}: {}{}", bound_kind, sub, tail); err.span_suggestion_short_with_applicability( - sp, consider, suggestion, - Applicability::MaybeIncorrect // Issue #41966 + sp, + consider, + suggestion, + Applicability::MaybeIncorrect, // Issue #41966 ); } else { err.help(consider); @@ -1349,12 +1357,10 @@ impl<'tcx> ObligationCause<'tcx> { match self.code { CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"), MatchExpressionArm { source, .. } => Error0308(match source { - hir::MatchSource::IfLetDesugar { .. } => { - "`if let` arms have incompatible types" - }, + hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have incompatible types", hir::MatchSource::TryDesugar => { "try expression alternatives have incompatible types" - }, + } _ => "match arms have incompatible types", }), IfExpression => Error0308("if and else have incompatible types"), diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index ccaa2f06f01de..c1afc12736791 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -11,12 +11,12 @@ use check::regionck::RegionCtxt; use hir::def_id::DefId; -use rustc::infer::{self, InferOk, SuppressRegionErrors}; use rustc::infer::outlives::env::OutlivesEnvironment; +use rustc::infer::{self, InferOk, SuppressRegionErrors}; use rustc::middle::region; +use rustc::traits::{ObligationCause, TraitEngine, TraitEngineExt}; use rustc::ty::subst::{Subst, Substs, UnpackedKind}; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::traits::{ObligationCause, TraitEngine, TraitEngineExt}; use util::common::ErrorReported; use syntax::ast; @@ -39,32 +39,41 @@ use syntax_pos::Span; /// struct/enum definition for the nominal type itself (i.e. /// cannot do `struct S; impl Drop for S { ... }`). /// -pub fn check_drop_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - drop_impl_did: DefId) - -> Result<(), ErrorReported> { +pub fn check_drop_impl<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + drop_impl_did: DefId, +) -> Result<(), ErrorReported> { let dtor_self_type = tcx.type_of(drop_impl_did); let dtor_predicates = tcx.predicates_of(drop_impl_did); match dtor_self_type.sty { ty::Adt(adt_def, self_to_impl_substs) => { - ensure_drop_params_and_item_params_correspond(tcx, - drop_impl_did, - dtor_self_type, - adt_def.did)?; + ensure_drop_params_and_item_params_correspond( + tcx, + drop_impl_did, + dtor_self_type, + adt_def.did, + )?; - ensure_drop_predicates_are_implied_by_item_defn(tcx, - drop_impl_did, - &dtor_predicates, - adt_def.did, - self_to_impl_substs) + ensure_drop_predicates_are_implied_by_item_defn( + tcx, + drop_impl_did, + &dtor_predicates, + adt_def.did, + self_to_impl_substs, + ) } _ => { // Destructors only work on nominal types. This was // already checked by coherence, but compilation may // not have been terminated. let span = tcx.def_span(drop_impl_did); - tcx.sess.delay_span_bug(span, - &format!("should have been rejected by coherence check: {}", - dtor_self_type)); + tcx.sess.delay_span_bug( + span, + &format!( + "should have been rejected by coherence check: {}", + dtor_self_type + ), + ); Err(ErrorReported) } } @@ -74,9 +83,8 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, drop_impl_did: DefId, drop_impl_ty: Ty<'tcx>, - self_type_did: DefId) - -> Result<(), ErrorReported> -{ + self_type_did: DefId, +) -> Result<(), ErrorReported> { let drop_impl_node_id = tcx.hir.as_local_node_id(drop_impl_did).unwrap(); // check that the impl type can be made to match the trait type. @@ -89,22 +97,29 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( let named_type = tcx.type_of(self_type_did); let drop_impl_span = tcx.def_span(drop_impl_did); - let fresh_impl_substs = - infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did); + let fresh_impl_substs = infcx.fresh_substs_for_item(drop_impl_span, drop_impl_did); let fresh_impl_self_ty = drop_impl_ty.subst(tcx, fresh_impl_substs); let cause = &ObligationCause::misc(drop_impl_span, drop_impl_node_id); - match infcx.at(cause, impl_param_env).eq(named_type, fresh_impl_self_ty) { + match infcx + .at(cause, impl_param_env) + .eq(named_type, fresh_impl_self_ty) + { Ok(InferOk { obligations, .. }) => { fulfillment_cx.register_predicate_obligations(infcx, obligations); } Err(_) => { let item_span = tcx.def_span(self_type_did); - struct_span_err!(tcx.sess, drop_impl_span, E0366, - "Implementations of Drop cannot be specialized") - .span_note(item_span, - "Use same sequence of generic type and region \ - parameters that is on the struct/enum definition") + struct_span_err!( + tcx.sess, + drop_impl_span, + E0366, + "Implementations of Drop cannot be specialized" + ).span_note( + item_span, + "Use same sequence of generic type and region \ + parameters that is on the struct/enum definition", + ) .emit(); return Err(ErrorReported); } @@ -128,7 +143,12 @@ fn ensure_drop_params_and_item_params_correspond<'a, 'tcx>( // conservative. -nmatsakis let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty()); - infcx.resolve_regions_and_report_errors(drop_impl_did, ®ion_scope_tree, &outlives_env, SuppressRegionErrors::default()); + infcx.resolve_regions_and_report_errors( + drop_impl_did, + ®ion_scope_tree, + &outlives_env, + SuppressRegionErrors::default(), + ); Ok(()) }) } @@ -140,9 +160,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>( drop_impl_did: DefId, dtor_predicates: &ty::GenericPredicates<'tcx>, self_type_did: DefId, - self_to_impl_substs: &Substs<'tcx>) - -> Result<(), ErrorReported> -{ + self_to_impl_substs: &Substs<'tcx>, +) -> Result<(), ErrorReported> { let mut result = Ok(()); // Here is an example, analogous to that from @@ -213,11 +232,17 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'a, 'tcx>( if !assumptions_in_impl_context.contains(&predicate) { let item_span = tcx.hir.span(self_type_node_id); - struct_span_err!(tcx.sess, drop_impl_span, E0367, - "The requirement `{}` is added only by the Drop impl.", predicate) - .span_note(item_span, - "The same requirement must be part of \ - the struct/enum definition") + struct_span_err!( + tcx.sess, + drop_impl_span, + E0367, + "The requirement `{}` is added only by the Drop impl.", + predicate + ).span_note( + item_span, + "The same requirement must be part of \ + the struct/enum definition", + ) .emit(); result = Err(ErrorReported); } @@ -283,18 +308,18 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( ty: Ty<'tcx>, span: Span, body_id: ast::NodeId, - scope: region::Scope) - -> Result<(), ErrorReported> -{ - debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}", - ty, scope); - + scope: region::Scope, +) -> Result<(), ErrorReported> { + debug!( + "check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}", + ty, scope + ); let parent_scope = match rcx.region_scope_tree.opt_encl_scope(scope) { Some(parent_scope) => parent_scope, // If no enclosing scope, then it must be the root scope // which cannot be outlived. - None => return Ok(()) + None => return Ok(()), }; let parent_scope = rcx.tcx.mk_region(ty::ReScope(parent_scope)); let origin = || infer::SubregionOrigin::SafeDestructor(span); From e2deef32d4a2f07249c567534528d5264ba0a160 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Sep 2018 11:32:58 -0400 Subject: [PATCH 32/34] pacify the mercilous tidy. --- ...jection-where-clause-env-wrong-lifetime.nll.stderr | 11 +++++++++++ .../projection-where-clause-env-wrong-lifetime.rs | 3 ++- .../projection-where-clause-env-wrong-lifetime.stderr | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr new file mode 100644 index 0000000000000..1e953ecff6923 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.nll.stderr @@ -0,0 +1,11 @@ +error[E0309]: the associated type `>::Output` may not live long enough + --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 + | +LL | bar::<>::Output>() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `>::Output: 'a`... + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs index 07cc37a8b290a..9e3590ca71545 100644 --- a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.rs @@ -11,7 +11,8 @@ where for<'x> T: MyTrait<'x>, >::Output: 'a, { - bar::<>::Output>() //~ ERROR the associated type `>::Output` may not live long enough + bar::<>::Output>() + //~^ ERROR the associated type `>::Output` may not live long enough } fn bar<'a, T>() -> &'a () diff --git a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr index c6d0037138c42..d6ade2a603e82 100644 --- a/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr +++ b/src/test/ui/nll/ty-outlives/projection-where-clause-env-wrong-lifetime.stderr @@ -1,14 +1,14 @@ error[E0309]: the associated type `>::Output` may not live long enough --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 | -LL | bar::<>::Output>() //~ ERROR the associated type `>::Output` may not live long enough +LL | bar::<>::Output>() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding an explicit lifetime bound `>::Output: 'a`... note: ...so that the type `>::Output` will meet its required lifetime bounds --> $DIR/projection-where-clause-env-wrong-lifetime.rs:14:5 | -LL | bar::<>::Output>() //~ ERROR the associated type `>::Output` may not live long enough +LL | bar::<>::Output>() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error From cbaf36adaf268eb4e4da7abb0ef2f4cd5380497d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Sep 2018 13:58:22 -0400 Subject: [PATCH 33/34] fix rustc_driver tests --- src/librustc_driver/test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 0a7bd3d97022d..fec69d91d8cf7 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -21,7 +21,7 @@ use rustc::ty::subst::Subst; use rustc::traits::ObligationCause; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::query::OnDiskCache; -use rustc::infer::{self, InferOk, InferResult}; +use rustc::infer::{self, InferOk, InferResult, SuppressRegionErrors}; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::infer::type_variable::TypeVariableOrigin; use rustc_metadata::cstore::CStore; @@ -177,7 +177,7 @@ fn test_env_with_pool( }); let outlives_env = OutlivesEnvironment::new(param_env); let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID); - infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &outlives_env); + infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &outlives_env, SuppressRegionErrors::default()); assert_eq!(tcx.sess.err_count(), expected_err_count); }); }); From f23fd4bc029fccbcb3e56507cac8cde90a81128c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Sep 2018 13:58:32 -0400 Subject: [PATCH 34/34] rustc_driver/test.rs: rustfmt --- src/librustc_driver/test.rs | 382 +++++++++++++++++++++--------------- 1 file changed, 219 insertions(+), 163 deletions(-) diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index fec69d91d8cf7..f18f40bf7a144 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -14,29 +14,29 @@ use std::path::PathBuf; use std::sync::mpsc; use driver; -use rustc_lint; -use rustc_resolve::MakeGlobMap; -use rustc::middle::region; -use rustc::ty::subst::Subst; -use rustc::traits::ObligationCause; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::query::OnDiskCache; -use rustc::infer::{self, InferOk, InferResult, SuppressRegionErrors}; +use errors; +use errors::emitter::Emitter; +use errors::{DiagnosticBuilder, Level}; +use rustc::hir::map as hir_map; use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::infer::type_variable::TypeVariableOrigin; -use rustc_metadata::cstore::CStore; -use rustc::hir::map as hir_map; -use rustc::session::{self, config}; +use rustc::infer::{self, InferOk, InferResult, SuppressRegionErrors}; +use rustc::middle::region; use rustc::session::config::{OutputFilenames, OutputTypes}; +use rustc::session::{self, config}; +use rustc::traits::ObligationCause; +use rustc::ty::query::OnDiskCache; +use rustc::ty::subst::Subst; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::sync::{self, Lrc}; +use rustc_lint; +use rustc_metadata::cstore::CStore; +use rustc_resolve::MakeGlobMap; +use rustc_target::spec::abi::Abi; use syntax; use syntax::ast; -use rustc_target::spec::abi::Abi; -use syntax::source_map::{SourceMap, FilePathMapping, FileName}; -use errors; -use errors::emitter::Emitter; -use errors::{Level, DiagnosticBuilder}; use syntax::feature_gate::UnstableFeatures; +use syntax::source_map::{FileName, FilePathMapping, SourceMap}; use syntax::symbol::Symbol; use syntax_pos::DUMMY_SP; @@ -90,13 +90,15 @@ impl Emitter for ExpectErrorEmitter { fn errors(msgs: &[&str]) -> (Box, usize) { let v = msgs.iter().map(|m| m.to_string()).collect(); - (box ExpectErrorEmitter { messages: v } as Box, msgs.len()) + ( + box ExpectErrorEmitter { messages: v } as Box, + msgs.len(), + ) } -fn test_env(source_string: &str, - args: (Box, usize), - body: F) - where F: FnOnce(Env) +fn test_env(source_string: &str, args: (Box, usize), body: F) +where + F: FnOnce(Env), { syntax::with_globals(|| { let mut options = config::Options::default(); @@ -113,34 +115,41 @@ fn test_env_with_pool( options: config::Options, source_string: &str, (emitter, expected_err_count): (Box, usize), - body: F -) - where F: FnOnce(Env) + body: F, +) where + F: FnOnce(Env), { let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter); - let sess = session::build_session_(options, - None, - diagnostic_handler, - Lrc::new(SourceMap::new(FilePathMapping::empty()))); + let sess = session::build_session_( + options, + None, + diagnostic_handler, + Lrc::new(SourceMap::new(FilePathMapping::empty())), + ); let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let input = config::Input::Str { name: FileName::Anon, input: source_string.to_string(), }; - let krate = driver::phase_1_parse_input(&driver::CompileController::basic(), - &sess, - &input).unwrap(); - let driver::ExpansionResult { defs, resolutions, mut hir_forest, .. } = { - driver::phase_2_configure_and_expand(&sess, - &cstore, - krate, - None, - "test", - None, - MakeGlobMap::No, - |_| Ok(())) - .expect("phase 2 aborted") + let krate = + driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input).unwrap(); + let driver::ExpansionResult { + defs, + resolutions, + mut hir_forest, + .. + } = { + driver::phase_2_configure_and_expand( + &sess, + &cstore, + krate, + None, + "test", + None, + MakeGlobMap::No, + |_| Ok(()), + ).expect("phase 2 aborted") }; let arenas = ty::AllArenas::new(); @@ -155,32 +164,39 @@ fn test_env_with_pool( extra: String::new(), outputs: OutputTypes::new(&[]), }; - TyCtxt::create_and_enter(&sess, - &cstore, - ty::query::Providers::default(), - ty::query::Providers::default(), - &arenas, - resolutions, - hir_map, - OnDiskCache::new_empty(sess.source_map()), - "test_crate", - tx, - &outputs, - |tcx| { - tcx.infer_ctxt().enter(|infcx| { - let mut region_scope_tree = region::ScopeTree::default(); - let param_env = ty::ParamEnv::empty(); - body(Env { - infcx: &infcx, - region_scope_tree: &mut region_scope_tree, - param_env: param_env, + TyCtxt::create_and_enter( + &sess, + &cstore, + ty::query::Providers::default(), + ty::query::Providers::default(), + &arenas, + resolutions, + hir_map, + OnDiskCache::new_empty(sess.source_map()), + "test_crate", + tx, + &outputs, + |tcx| { + tcx.infer_ctxt().enter(|infcx| { + let mut region_scope_tree = region::ScopeTree::default(); + let param_env = ty::ParamEnv::empty(); + body(Env { + infcx: &infcx, + region_scope_tree: &mut region_scope_tree, + param_env: param_env, + }); + let outlives_env = OutlivesEnvironment::new(param_env); + let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID); + infcx.resolve_regions_and_report_errors( + def_id, + ®ion_scope_tree, + &outlives_env, + SuppressRegionErrors::default(), + ); + assert_eq!(tcx.sess.err_count(), expected_err_count); }); - let outlives_env = OutlivesEnvironment::new(param_env); - let def_id = tcx.hir.local_def_id(ast::CRATE_NODE_ID); - infcx.resolve_regions_and_report_errors(def_id, ®ion_scope_tree, &outlives_env, SuppressRegionErrors::default()); - assert_eq!(tcx.sess.err_count(), expected_err_count); - }); - }); + }, + ); } fn d1() -> ty::DebruijnIndex { @@ -196,9 +212,15 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { self.infcx.tcx } - pub fn create_region_hierarchy(&mut self, rh: &RH, - parent: (region::Scope, region::ScopeDepth)) { - let me = region::Scope { id: rh.id, data: region::ScopeData::Node }; + pub fn create_region_hierarchy( + &mut self, + rh: &RH, + parent: (region::Scope, region::ScopeDepth), + ) { + let me = region::Scope { + id: rh.id, + data: region::ScopeData::Node, + }; self.region_scope_tree.record_scope_parent(me, Some(parent)); for child_rh in rh.sub { self.create_region_hierarchy(child_rh, (me, parent.1 + 1)); @@ -211,20 +233,25 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { let dscope = region::Scope { id: hir::ItemLocalId(1), - data: region::ScopeData::Destruction + data: region::ScopeData::Destruction, }; self.region_scope_tree.record_scope_parent(dscope, None); - self.create_region_hierarchy(&RH { - id: hir::ItemLocalId(1), - sub: &[RH { - id: hir::ItemLocalId(10), - sub: &[], + self.create_region_hierarchy( + &RH { + id: hir::ItemLocalId(1), + sub: &[ + RH { + id: hir::ItemLocalId(10), + sub: &[], + }, + RH { + id: hir::ItemLocalId(11), + sub: &[], + }, + ], }, - RH { - id: hir::ItemLocalId(11), - sub: &[], - }], - }, (dscope, 1)); + (dscope, 1), + ); } #[allow(dead_code)] // this seems like it could be useful, even if we don't use it now @@ -236,11 +263,12 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { } }; - fn search_mod(this: &Env, - m: &hir::Mod, - idx: usize, - names: &[String]) - -> Option { + fn search_mod( + this: &Env, + m: &hir::Mod, + idx: usize, + names: &[String], + ) -> Option { assert!(idx < names.len()); for item in &m.item_ids { let item = this.infcx.tcx.hir.expect_item(item.id); @@ -257,22 +285,22 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { } return match it.node { - hir::ItemKind::Use(..) | - hir::ItemKind::ExternCrate(..) | - hir::ItemKind::Const(..) | - hir::ItemKind::Static(..) | - hir::ItemKind::Fn(..) | - hir::ItemKind::ForeignMod(..) | - hir::ItemKind::GlobalAsm(..) | - hir::ItemKind::Existential(..) | - hir::ItemKind::Ty(..) => None, - - hir::ItemKind::Enum(..) | - hir::ItemKind::Struct(..) | - hir::ItemKind::Union(..) | - hir::ItemKind::Trait(..) | - hir::ItemKind::TraitAlias(..) | - hir::ItemKind::Impl(..) => None, + hir::ItemKind::Use(..) + | hir::ItemKind::ExternCrate(..) + | hir::ItemKind::Const(..) + | hir::ItemKind::Static(..) + | hir::ItemKind::Fn(..) + | hir::ItemKind::ForeignMod(..) + | hir::ItemKind::GlobalAsm(..) + | hir::ItemKind::Existential(..) + | hir::ItemKind::Ty(..) => None, + + hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::Impl(..) => None, hir::ItemKind::Mod(ref m) => search_mod(this, m, idx, names), }; @@ -280,7 +308,10 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { } pub fn make_subtype(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(a, b) { + match self.infcx + .at(&ObligationCause::dummy(), self.param_env) + .sub(a, b) + { Ok(_) => true, Err(ref e) => panic!("Encountered error: {}", e), } @@ -302,13 +333,15 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { } pub fn t_fn(&self, input_tys: &[Ty<'tcx>], output_ty: Ty<'tcx>) -> Ty<'tcx> { - self.infcx.tcx.mk_fn_ptr(ty::Binder::bind(self.infcx.tcx.mk_fn_sig( - input_tys.iter().cloned(), - output_ty, - false, - hir::Unsafety::Normal, - Abi::Rust - ))) + self.infcx + .tcx + .mk_fn_ptr(ty::Binder::bind(self.infcx.tcx.mk_fn_sig( + input_tys.iter().cloned(), + output_ty, + false, + hir::Unsafety::Normal, + Abi::Rust, + ))) } pub fn t_nil(&self) -> Ty<'tcx> { @@ -321,23 +354,30 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { pub fn t_param(&self, index: u32) -> Ty<'tcx> { let name = format!("T{}", index); - self.infcx.tcx.mk_ty_param(index, Symbol::intern(&name).as_interned_str()) + self.infcx + .tcx + .mk_ty_param(index, Symbol::intern(&name).as_interned_str()) } pub fn re_early_bound(&self, index: u32, name: &'static str) -> ty::Region<'tcx> { let name = Symbol::intern(name).as_interned_str(); - self.infcx.tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { - def_id: self.infcx.tcx.hir.local_def_id(ast::CRATE_NODE_ID), - index, - name, - })) + self.infcx + .tcx + .mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + def_id: self.infcx.tcx.hir.local_def_id(ast::CRATE_NODE_ID), + index, + name, + })) } - pub fn re_late_bound_with_debruijn(&self, - id: u32, - debruijn: ty::DebruijnIndex) - -> ty::Region<'tcx> { - self.infcx.tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(id))) + pub fn re_late_bound_with_debruijn( + &self, + id: u32, + debruijn: ty::DebruijnIndex, + ) -> ty::Region<'tcx> { + self.infcx + .tcx + .mk_region(ty::ReLateBound(debruijn, ty::BrAnon(id))) } pub fn t_rptr(&self, r: ty::Region<'tcx>) -> Ty<'tcx> { @@ -349,10 +389,11 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize) } - pub fn t_rptr_late_bound_with_debruijn(&self, - id: u32, - debruijn: ty::DebruijnIndex) - -> Ty<'tcx> { + pub fn t_rptr_late_bound_with_debruijn( + &self, + id: u32, + debruijn: ty::DebruijnIndex, + ) -> Ty<'tcx> { let r = self.re_late_bound_with_debruijn(id, debruijn); self.infcx.tcx.mk_imm_ref(r, self.tcx().types.isize) } @@ -360,9 +401,11 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { pub fn t_rptr_scope(&self, id: u32) -> Ty<'tcx> { let r = ty::ReScope(region::Scope { id: hir::ItemLocalId(id), - data: region::ScopeData::Node + data: region::ScopeData::Node, }); - self.infcx.tcx.mk_imm_ref(self.infcx.tcx.mk_region(r), self.tcx().types.isize) + self.infcx + .tcx + .mk_imm_ref(self.infcx.tcx.mk_region(r), self.tcx().types.isize) } pub fn re_free(&self, id: u32) -> ty::Region<'tcx> { @@ -378,14 +421,19 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { } pub fn sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> InferResult<'tcx, ()> { - self.infcx.at(&ObligationCause::dummy(), self.param_env).sub(t1, t2) + self.infcx + .at(&ObligationCause::dummy(), self.param_env) + .sub(t1, t2) } /// Checks that `t1 <: t2` is true (this may register additional /// region checks). pub fn check_sub(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) { match self.sub(t1, t2) { - Ok(InferOk { obligations, value: () }) => { + Ok(InferOk { + obligations, + value: (), + }) => { // None of these tests should require nested obligations: assert!(obligations.is_empty()); } @@ -445,8 +493,10 @@ fn sub_free_bound_false() { env.create_simple_region_hierarchy(); let t_rptr_free1 = env.t_rptr_free(1); let t_rptr_bound1 = env.t_rptr_late_bound(1); - env.check_not_sub(env.t_fn(&[t_rptr_free1], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize)); + env.check_not_sub( + env.t_fn(&[t_rptr_free1], env.tcx().types.isize), + env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), + ); }) } @@ -462,8 +512,10 @@ fn sub_bound_free_true() { env.create_simple_region_hierarchy(); let t_rptr_bound1 = env.t_rptr_late_bound(1); let t_rptr_free1 = env.t_rptr_free(1); - env.check_sub(env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), - env.t_fn(&[t_rptr_free1], env.tcx().types.isize)); + env.check_sub( + env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), + env.t_fn(&[t_rptr_free1], env.tcx().types.isize), + ); }) } @@ -476,10 +528,13 @@ fn sub_free_bound_false_infer() { //! does NOT hold for any instantiation of `_#1`. test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { - let t_infer1 = env.infcx.next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP)); + let t_infer1 = env.infcx + .next_ty_var(TypeVariableOrigin::MiscVariable(DUMMY_SP)); let t_rptr_bound1 = env.t_rptr_late_bound(1); - env.check_not_sub(env.t_fn(&[t_infer1], env.tcx().types.isize), - env.t_fn(&[t_rptr_bound1], env.tcx().types.isize)); + env.check_not_sub( + env.t_fn(&[t_infer1], env.tcx().types.isize), + env.t_fn(&[t_rptr_bound1], env.tcx().types.isize), + ); }) } @@ -487,7 +542,6 @@ fn sub_free_bound_false_infer() { /// This requires adjusting the Debruijn index. #[test] fn subst_ty_renumber_bound() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |env| { // Situation: // Theta = [A -> &'a foo] @@ -509,11 +563,10 @@ fn subst_ty_renumber_bound() { env.t_fn(&[t_ptr_bound2], env.t_nil()) }; - debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}", - t_source, - substs, - t_substituted, - t_expected); + debug!( + "subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}", + t_source, substs, t_substituted, t_expected + ); assert_eq!(t_substituted, t_expected); }) @@ -546,11 +599,10 @@ fn subst_ty_renumber_some_bounds() { env.t_pair(t_rptr_bound1, env.t_fn(&[t_rptr_bound2], env.t_nil())) }; - debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}", - t_source, - substs, - t_substituted, - t_expected); + debug!( + "subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}", + t_source, substs, t_substituted, t_expected + ); assert_eq!(t_substituted, t_expected); }) @@ -559,7 +611,6 @@ fn subst_ty_renumber_some_bounds() { /// Test that we correctly compute whether a type has escaping regions or not. #[test] fn escaping() { - test_env(EMPTY_SOURCE_STR, errors(&[]), |mut env| { // Situation: // Theta = [A -> &'a foo] @@ -608,11 +659,10 @@ fn subst_region_renumber_region() { env.t_fn(&[t_rptr_bound2], env.t_nil()) }; - debug!("subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}", - t_source, - substs, - t_substituted, - t_expected); + debug!( + "subst_bound: t_source={:?} substs={:?} t_substituted={:?} t_expected={:?}", + t_source, substs, t_substituted, t_expected + ); assert_eq!(t_substituted, t_expected); }) @@ -627,9 +677,13 @@ fn walk_ty() { let tup1_ty = tcx.intern_tup(&[int_ty, usize_ty, int_ty, usize_ty]); let tup2_ty = tcx.intern_tup(&[tup1_ty, tup1_ty, usize_ty]); let walked: Vec<_> = tup2_ty.walk().collect(); - assert_eq!(walked, - [tup2_ty, tup1_ty, int_ty, usize_ty, int_ty, usize_ty, tup1_ty, int_ty, - usize_ty, int_ty, usize_ty, usize_ty]); + assert_eq!( + walked, + [ + tup2_ty, tup1_ty, int_ty, usize_ty, int_ty, usize_ty, tup1_ty, int_ty, usize_ty, + int_ty, usize_ty, usize_ty + ] + ); }) } @@ -644,14 +698,16 @@ fn walk_ty_skip_subtree() { // types we expect to see (in order), plus a boolean saying // whether to skip the subtree. - let mut expected = vec![(tup2_ty, false), - (tup1_ty, false), - (int_ty, false), - (usize_ty, false), - (int_ty, false), - (usize_ty, false), - (tup1_ty, true), // skip the isize/usize/isize/usize - (usize_ty, false)]; + let mut expected = vec![ + (tup2_ty, false), + (tup1_ty, false), + (int_ty, false), + (usize_ty, false), + (int_ty, false), + (usize_ty, false), + (tup1_ty, true), // skip the isize/usize/isize/usize + (usize_ty, false), + ]; expected.reverse(); let mut walker = tup2_ty.walk();