diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 26940cccf2cb..17cea42564d9 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -85,6 +85,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( mir_node_id, param_env, mir, + &universal_regions.region_bound_pairs, fr_fn_body, universal_regions.input_tys, universal_regions.output_ty, 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 47acc8c3f3aa..409f6bd093d0 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -17,7 +17,7 @@ use dataflow::MaybeInitializedLvals; use dataflow::flow_in_progress::FlowInProgress; use dataflow::move_paths::MoveData; use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult}; -use rustc::infer::region_constraints::RegionConstraintData; +use rustc::infer::region_constraints::{GenericKind, RegionConstraintData}; use rustc::traits::{self, FulfillmentContext}; use rustc::ty::error::TypeError; use rustc::ty::fold::TypeFoldable; @@ -54,6 +54,8 @@ mod liveness; /// - `body_id` -- body-id of the MIR being checked /// - `param_env` -- parameter environment to use for trait solving /// - `mir` -- MIR to type-check +/// - `region_bound_pairs` -- the implied outlives obligations between type parameters +/// and lifetimes (e.g., `&'a T` implies `T: 'a`) /// - `implicit_region_bound` -- a region which all generic parameters are assumed /// to outlive; should represent the fn body /// - `input_tys` -- fully liberated, but **not** normalized, expected types of the arguments; @@ -69,6 +71,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: ty::Region<'tcx>, input_tys: &[Ty<'tcx>], output_ty: Ty<'tcx>, @@ -81,6 +84,7 @@ pub(crate) fn type_check<'gcx, 'tcx>( body_id, param_env, mir, + region_bound_pairs, Some(implicit_region_bound), &mut |cx| { liveness::generate(cx, mir, liveness, flow_inits, move_data); @@ -100,10 +104,17 @@ fn type_check_internal<'gcx, 'tcx>( body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, mir: &Mir<'tcx>, + region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>), ) -> MirTypeckRegionConstraints<'tcx> { - let mut checker = TypeChecker::new(infcx, body_id, param_env, implicit_region_bound); + let mut checker = TypeChecker::new( + infcx, + body_id, + param_env, + region_bound_pairs, + implicit_region_bound, + ); let errors_reported = { let mut verifier = TypeVerifier::new(&mut checker, mir); verifier.visit_mir(mir); @@ -563,6 +574,7 @@ struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { param_env: ty::ParamEnv<'gcx>, last_span: Span, body_id: ast::NodeId, + region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, constraints: MirTypeckRegionConstraints<'tcx>, @@ -617,6 +629,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, body_id: ast::NodeId, param_env: ty::ParamEnv<'gcx>, + region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)], implicit_region_bound: Option>, ) -> Self { TypeChecker { @@ -624,6 +637,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { last_span: DUMMY_SP, body_id, param_env, + region_bound_pairs, implicit_region_bound, reported_errors: FxHashSet(), constraints: MirTypeckRegionConstraints::default(), @@ -650,7 +664,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } self.infcx.process_registered_region_obligations( - &[], + self.region_bound_pairs, self.implicit_region_bound, self.param_env, self.body_id, @@ -756,12 +770,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { ); }; } - StatementKind::StorageLive(_) | - StatementKind::StorageDead(_) | - StatementKind::InlineAsm { .. } | - StatementKind::EndRegion(_) | - StatementKind::Validate(..) | - StatementKind::Nop => {} + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::InlineAsm { .. } + | StatementKind::EndRegion(_) + | StatementKind::Validate(..) + | StatementKind::Nop => {} } } @@ -774,13 +788,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { debug!("check_terminator: {:?}", term); let tcx = self.tcx(); match term.kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop | - TerminatorKind::Unreachable | - TerminatorKind::Drop { .. } | - TerminatorKind::FalseEdges { .. } => { + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Return + | TerminatorKind::GeneratorDrop + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::FalseEdges { .. } => { // no checks needed for these } @@ -880,9 +894,11 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // output) types in the signature must be live, since // all the inputs that fed into it were live. for &late_bound_region in map.values() { - self.constraints - .liveness_set - .push((late_bound_region, term_location, Cause::LiveOther(term_location))); + self.constraints.liveness_set.push(( + late_bound_region, + term_location, + Cause::LiveOther(term_location), + )); } if self.is_box_free(func) { @@ -1092,9 +1108,9 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } TerminatorKind::Unreachable => {} - TerminatorKind::Drop { target, unwind, .. } | - TerminatorKind::DropAndReplace { target, unwind, .. } | - TerminatorKind::Assert { + TerminatorKind::Drop { target, unwind, .. } + | TerminatorKind::DropAndReplace { target, unwind, .. } + | TerminatorKind::Assert { target, cleanup: unwind, .. @@ -1350,13 +1366,13 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { }, // FIXME: These other cases have to be implemented in future PRs - Rvalue::Use(..) | - Rvalue::Ref(..) | - Rvalue::Len(..) | - Rvalue::BinaryOp(..) | - Rvalue::CheckedBinaryOp(..) | - Rvalue::UnaryOp(..) | - Rvalue::Discriminant(..) => {} + Rvalue::Use(..) + | Rvalue::Ref(..) + | Rvalue::Len(..) + | Rvalue::BinaryOp(..) + | Rvalue::CheckedBinaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::Discriminant(..) => {} } } @@ -1490,9 +1506,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { let cause = this.misc(this.last_span); let obligations = predicates .iter() - .map(|&p| { - traits::Obligation::new(cause.clone(), this.param_env, p) - }) + .map(|&p| traits::Obligation::new(cause.clone(), this.param_env, p)) .collect(); Ok(InferOk { value: (), @@ -1556,7 +1570,7 @@ impl MirPass for TypeckMir { } let param_env = tcx.param_env(def_id); tcx.infer_ctxt().enter(|infcx| { - let _ = type_check_internal(&infcx, id, param_env, mir, None, &mut |_| ()); + let _ = type_check_internal(&infcx, id, param_env, mir, &[], None, &mut |_| ()); // For verification purposes, we just ignore the resulting // region constraint sets. Not our problem. =) diff --git a/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs b/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs new file mode 100644 index 000000000000..d83985f6ae54 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-implied-bounds.rs @@ -0,0 +1,56 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +// Test that we can deduce when projections like `T::Item` outlive the +// function body. Test that this does not imply that `T: 'a` holds. + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn twice(mut value: T, mut f: F) +where + F: FnMut(&T, Cell<&Option>), + T: Iterator, +{ + let mut n = value.next(); + f(&value, Cell::new(&n)); + f(&value, Cell::new(&n)); +} + +#[rustc_errors] +fn generic1(value: T) { + // No error here: + twice(value, |value_ref, item| invoke1(item)); +} + +fn invoke1<'a, T>(x: Cell<&'a Option>) +where + T: 'a, +{ +} + +#[rustc_errors] +fn generic2(value: T) { + twice(value, |value_ref, item| invoke2(value_ref, item)); + //~^ WARNING not reporting region error due to -Znll + //~| ERROR failed type test +} + +fn invoke2<'a, T, U>(a: &T, b: Cell<&'a Option>) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr new file mode 100644 index 000000000000..167ae5faf269 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr @@ -0,0 +1,14 @@ +warning: not reporting region error due to -Znll + --> $DIR/projection-implied-bounds.rs:45:36 + | +45 | twice(value, |value_ref, item| invoke2(value_ref, item)); + | ^^^^^^^ + +error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#0r, point: bb0[3], span: $DIR/projection-implied-bounds.rs:45:18: 45:60, test: IsOutlivedByAnyRegionIn(['_#1r]) } + --> $DIR/projection-implied-bounds.rs:45:18 + | +45 | twice(value, |value_ref, item| invoke2(value_ref, item)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs new file mode 100644 index 000000000000..3da61aa8074c --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.rs @@ -0,0 +1,55 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![allow(warnings)] +#![feature(dyn_trait)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +// Invoke in such a way that the callee knows: +// +// - 'a: 'x +// +// and it must prove that `T: 'x`. Callee passes along `T: 'a`. +fn twice<'a, F, T>(v: Cell<&'a ()>, value: T, mut f: F) +where + F: for<'x> FnMut(Option>, &T), +{ + f(None, &value); + f(None, &value); +} + +#[rustc_regions] +fn generic(value: T) { + let cell = Cell::new(&()); + twice(cell, value, |a, b| invoke(a, b)); + //~^ WARNING not reporting region error + // + // This error from the old region solver looks bogus. +} + +#[rustc_regions] +fn generic_fail<'a, T>(cell: Cell<&'a ()>, value: T) { + twice(cell, value, |a, b| invoke(a, b)); + //~^ WARNING not reporting region error + //~| WARNING not reporting region error + //~| ERROR failed type test +} + +fn invoke<'a, 'x, T>(x: Option>, y: &T) +where + T: 'x, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr new file mode 100644 index 000000000000..4142860fb516 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -0,0 +1,85 @@ +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-approximate-lower-bound.rs:35:31 + | +35 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:31 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^ + +warning: not reporting region error due to -Znll + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:31 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^ + +note: External requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:35:24 + | +35 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:14 ~ ty_param_closure_approximate_lower_bound[317d]::generic[0]::{{closure}}[0]) with closure substs [ + T, + i16, + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) T)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: External requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:24 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: DefId(0/1:17 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]::{{closure}}[0]) with closure substs [ + T, + i16, + for<'r, 's> extern "rust-call" fn((std::option::Option>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) T)) + ] + = note: number of external vids: 2 + = note: where T: '_#1r + +note: No external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:33:1 + | +33 | / fn generic(value: T) { +34 | | let cell = Cell::new(&()); +35 | | twice(cell, value, |a, b| invoke(a, b)); +36 | | //~^ WARNING not reporting region error +37 | | // +38 | | // This error from the old region solver looks bogus. +39 | | } + | |_^ + | + = note: defining type: DefId(0/0:5 ~ ty_param_closure_approximate_lower_bound[317d]::generic[0]) with substs [ + T + ] + +error: failed type test: TypeTest { generic_kind: T/#0, lower_bound: '_#3r, point: bb0[5], span: $DIR/ty-param-closure-approximate-lower-bound.rs:43:24: 43:43, test: IsOutlivedByAnyRegionIn(['_#2r]) } + --> $DIR/ty-param-closure-approximate-lower-bound.rs:43:24 + | +43 | twice(cell, value, |a, b| invoke(a, b)); + | ^^^^^^^^^^^^^^^^^^^ + +note: No external requirements + --> $DIR/ty-param-closure-approximate-lower-bound.rs:42:1 + | +42 | / fn generic_fail<'a, T>(cell: Cell<&'a ()>, value: T) { +43 | | twice(cell, value, |a, b| invoke(a, b)); +44 | | //~^ WARNING not reporting region error +45 | | //~| WARNING not reporting region error +46 | | //~| ERROR failed type test +47 | | } + | |_^ + | + = note: defining type: DefId(0/0:6 ~ ty_param_closure_approximate_lower_bound[317d]::generic_fail[0]) with substs [ + T + ] + +error: aborting due to previous error + diff --git a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs new file mode 100644 index 000000000000..2ff4035bf271 --- /dev/null +++ b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.rs @@ -0,0 +1,41 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +// Test that we assume that universal types like `T` outlive the +// function body. + +#![allow(warnings)] +#![feature(rustc_attrs)] + +use std::cell::Cell; + +fn twice(value: T, mut f: F) +where + F: FnMut(Cell<&T>), +{ + f(Cell::new(&value)); + f(Cell::new(&value)); +} + +#[rustc_errors] +fn generic(value: T) { + // No error here: + twice(value, |r| invoke(r)); +} + +fn invoke<'a, T>(x: Cell<&'a T>) +where + T: 'a, +{ +} + +fn main() {} diff --git a/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr b/src/test/ui/nll/ty-outlives/ty-param-implied-bounds.stderr new file mode 100644 index 000000000000..e69de29bb2d1