Skip to content

propagate region_bound_pairs into MIR type-check #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc_mir/borrow_check/nll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
80 changes: 47 additions & 33 deletions src/librustc_mir/borrow_check/nll/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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>,
Expand All @@ -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);
Expand All @@ -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<ty::Region<'tcx>>,
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);
Expand Down Expand Up @@ -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<ty::Region<'tcx>>,
reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
constraints: MirTypeckRegionConstraints<'tcx>,
Expand Down Expand Up @@ -617,13 +629,15 @@ 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<ty::Region<'tcx>>,
) -> Self {
TypeChecker {
infcx,
last_span: DUMMY_SP,
body_id,
param_env,
region_bound_pairs,
implicit_region_bound,
reported_errors: FxHashSet(),
constraints: MirTypeckRegionConstraints::default(),
Expand All @@ -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,
Expand Down Expand Up @@ -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 => {}
}
}

Expand All @@ -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
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
..
Expand Down Expand Up @@ -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(..) => {}
}
}

Expand Down Expand Up @@ -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: (),
Expand Down Expand Up @@ -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. =)
Expand Down
56 changes: 56 additions & 0 deletions src/test/ui/nll/ty-outlives/projection-implied-bounds.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<F, T>(mut value: T, mut f: F)
where
F: FnMut(&T, Cell<&Option<T::Item>>),
T: Iterator,
{
let mut n = value.next();
f(&value, Cell::new(&n));
f(&value, Cell::new(&n));
}

#[rustc_errors]
fn generic1<T: Iterator>(value: T) {
// No error here:
twice(value, |value_ref, item| invoke1(item));
}

fn invoke1<'a, T>(x: Cell<&'a Option<T>>)
where
T: 'a,
{
}

#[rustc_errors]
fn generic2<T: Iterator>(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<U>>)
where
T: 'a,
{
}

fn main() {}
14 changes: 14 additions & 0 deletions src/test/ui/nll/ty-outlives/projection-implied-bounds.stderr
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Cell<&'a &'x ()>>, &T),
{
f(None, &value);
f(None, &value);
}

#[rustc_regions]
fn generic<T>(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<Cell<&'x &'a ()>>, y: &T)
where
T: 'x,
{
}

fn main() {}
Loading