move some parts of liveness to happen during type checking
This allows us to re-use the `normalize` method on `TypeCheck`, which is important since normalization may create fresh region variables. This is not an ideal solution, though, since the current representation of "liveness constraints" (a vector of (region, point) pairs) is rather inefficient. Could do somewhat better by converting to indices, but it'd still be less good than the older code. Unclear how important this is.
This commit is contained in:
parent
4a940b3215
commit
47c1921b9a
@ -12,21 +12,13 @@ use rustc::hir;
|
||||
use rustc::mir::{BasicBlock, BasicBlockData, Location, Place, Mir, Rvalue};
|
||||
use rustc::mir::visit::Visitor;
|
||||
use rustc::mir::Place::Projection;
|
||||
use rustc::mir::{Local, PlaceProjection, ProjectionElem};
|
||||
use rustc::mir::{PlaceProjection, ProjectionElem};
|
||||
use rustc::mir::visit::TyContext;
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::traits::{self, ObligationCause};
|
||||
use rustc::ty::{self, ClosureSubsts, Ty};
|
||||
use rustc::ty::{self, ClosureSubsts};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::fold::TypeFoldable;
|
||||
use rustc::util::common::ErrorReported;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use syntax::codemap::DUMMY_SP;
|
||||
use dataflow::{FlowAtLocation, FlowsAtLocation};
|
||||
use dataflow::MaybeInitializedLvals;
|
||||
use dataflow::move_paths::{HasMoveData, MoveData};
|
||||
|
||||
use super::LivenessResults;
|
||||
use super::ToRegionVid;
|
||||
use super::region_infer::RegionInferenceContext;
|
||||
|
||||
@ -34,19 +26,11 @@ pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
liveness: &LivenessResults,
|
||||
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) {
|
||||
let mut cg = ConstraintGeneration {
|
||||
infcx,
|
||||
regioncx,
|
||||
mir,
|
||||
liveness,
|
||||
param_env,
|
||||
flow_inits,
|
||||
move_data,
|
||||
};
|
||||
|
||||
for (bb, data) in mir.basic_blocks().iter_enumerated() {
|
||||
@ -59,16 +43,10 @@ struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
|
||||
infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
regioncx: &'cg mut RegionInferenceContext<'tcx>,
|
||||
mir: &'cg Mir<'tcx>,
|
||||
liveness: &'cg LivenessResults,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
flow_inits: &'cg mut FlowAtLocation<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
|
||||
move_data: &'cg MoveData<'tcx>,
|
||||
}
|
||||
|
||||
|
||||
impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
|
||||
fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
|
||||
self.add_liveness_constraints(bb);
|
||||
self.super_basic_block_data(bb, data);
|
||||
}
|
||||
|
||||
@ -130,84 +108,6 @@ impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx
|
||||
}
|
||||
|
||||
impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
|
||||
/// Liveness constraints:
|
||||
///
|
||||
/// > If a variable V is live at point P, then all regions R in the type of V
|
||||
/// > must include the point P.
|
||||
fn add_liveness_constraints(&mut self, bb: BasicBlock) {
|
||||
debug!("add_liveness_constraints(bb={:?})", bb);
|
||||
|
||||
self.liveness
|
||||
.regular
|
||||
.simulate_block(self.mir, bb, |location, live_locals| {
|
||||
for live_local in live_locals.iter() {
|
||||
let live_local_ty = self.mir.local_decls[live_local].ty;
|
||||
self.add_regular_live_constraint(live_local_ty, location);
|
||||
}
|
||||
});
|
||||
|
||||
let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
|
||||
self.liveness
|
||||
.drop
|
||||
.simulate_block(self.mir, bb, |location, live_locals| {
|
||||
all_live_locals.push((location, live_locals.iter().collect()));
|
||||
});
|
||||
debug!(
|
||||
"add_liveness_constraints: all_live_locals={:#?}",
|
||||
all_live_locals
|
||||
);
|
||||
|
||||
let terminator_index = self.mir.basic_blocks()[bb].statements.len();
|
||||
self.flow_inits.reset_to_entry_of(bb);
|
||||
while let Some((location, live_locals)) = all_live_locals.pop() {
|
||||
for live_local in live_locals {
|
||||
debug!(
|
||||
"add_liveness_constraints: location={:?} live_local={:?}",
|
||||
location,
|
||||
live_local
|
||||
);
|
||||
|
||||
self.flow_inits.each_state_bit(|mpi_init| {
|
||||
debug!(
|
||||
"add_liveness_constraints: location={:?} initialized={:?}",
|
||||
location,
|
||||
&self.flow_inits
|
||||
.operator()
|
||||
.move_data()
|
||||
.move_paths[mpi_init]
|
||||
);
|
||||
});
|
||||
|
||||
let mpi = self.move_data.rev_lookup.find_local(live_local);
|
||||
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
|
||||
debug!(
|
||||
"add_liveness_constraints: mpi={:?} has initialized child {:?}",
|
||||
self.move_data.move_paths[mpi],
|
||||
self.move_data.move_paths[initialized_child]
|
||||
);
|
||||
|
||||
let live_local_ty = self.mir.local_decls[live_local].ty;
|
||||
self.add_drop_live_constraint(live_local_ty, location);
|
||||
}
|
||||
}
|
||||
|
||||
if location.statement_index == terminator_index {
|
||||
debug!(
|
||||
"add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
|
||||
location
|
||||
);
|
||||
self.flow_inits.reconstruct_terminator_effect(location);
|
||||
} else {
|
||||
debug!(
|
||||
"add_liveness_constraints: reconstruct_statement_effect from {:#?}",
|
||||
location
|
||||
);
|
||||
self.flow_inits.reconstruct_statement_effect(location);
|
||||
}
|
||||
self.flow_inits.apply_local_effect(location);
|
||||
}
|
||||
}
|
||||
|
||||
/// Some variable with type `live_ty` is "regular live" at
|
||||
/// `location` -- i.e., it may be used later. This means that all
|
||||
/// regions appearing in the type `live_ty` must be live at
|
||||
@ -230,75 +130,6 @@ impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
|
||||
});
|
||||
}
|
||||
|
||||
/// Some variable with type `live_ty` is "drop live" at `location`
|
||||
/// -- i.e., it may be dropped later. This means that *some* of
|
||||
/// the regions in its type must be live at `location`. The
|
||||
/// precise set will depend on the dropck constraints, and in
|
||||
/// particular this takes `#[may_dangle]` into account.
|
||||
fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
|
||||
debug!(
|
||||
"add_drop_live_constraint(dropped_ty={:?}, location={:?})",
|
||||
dropped_ty,
|
||||
location
|
||||
);
|
||||
|
||||
let tcx = self.infcx.tcx;
|
||||
let mut types = vec![(dropped_ty, 0)];
|
||||
let mut known = FxHashSet();
|
||||
while let Some((ty, depth)) = types.pop() {
|
||||
let span = DUMMY_SP; // FIXME
|
||||
let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
|
||||
Ok(result) => result,
|
||||
Err(ErrorReported) => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let ty::DtorckConstraint {
|
||||
outlives,
|
||||
dtorck_types,
|
||||
} = result;
|
||||
|
||||
// All things in the `outlives` array may be touched by
|
||||
// the destructor and must be live at this point.
|
||||
for outlive in outlives {
|
||||
self.add_regular_live_constraint(outlive, location);
|
||||
}
|
||||
|
||||
// However, there may also be some types that
|
||||
// `dtorck_constraint_for_ty` could not resolve (e.g.,
|
||||
// associated types and parameters). We need to normalize
|
||||
// associated types here and possibly recursively process.
|
||||
for ty in dtorck_types {
|
||||
let cause = ObligationCause::dummy();
|
||||
// We know that our original `dropped_ty` is well-formed,
|
||||
// so region obligations resulting from this normalization
|
||||
// should always hold.
|
||||
//
|
||||
// Therefore we ignore them instead of trying to match
|
||||
// them up with a location.
|
||||
let fulfillcx = traits::FulfillmentContext::new_ignoring_regions();
|
||||
match traits::fully_normalize_with_fulfillcx(
|
||||
self.infcx, fulfillcx, cause, self.param_env, &ty
|
||||
) {
|
||||
Ok(ty) => match ty.sty {
|
||||
ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
|
||||
self.add_regular_live_constraint(ty, location);
|
||||
}
|
||||
|
||||
_ => if known.insert(ty) {
|
||||
types.push((ty, depth + 1));
|
||||
},
|
||||
},
|
||||
|
||||
Err(errors) => {
|
||||
self.infcx.report_fulfillment_errors(&errors, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_reborrow_constraint(
|
||||
&mut self,
|
||||
location: Location,
|
||||
|
@ -77,7 +77,16 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
||||
) {
|
||||
// Run the MIR type-checker.
|
||||
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
|
||||
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
|
||||
let liveness = &LivenessResults::compute(mir);
|
||||
let constraint_sets = &type_check::type_check(
|
||||
infcx,
|
||||
mir_node_id,
|
||||
param_env,
|
||||
mir,
|
||||
&liveness,
|
||||
flow_inits,
|
||||
move_data,
|
||||
);
|
||||
|
||||
// Create the region inference context, taking ownership of the region inference
|
||||
// data that was contained in `infcx`.
|
||||
@ -85,19 +94,9 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
||||
let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
|
||||
subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
|
||||
|
||||
// Compute what is live where.
|
||||
let liveness = &LivenessResults::compute(mir);
|
||||
|
||||
// Generate non-subtyping constraints.
|
||||
constraint_generation::generate_constraints(
|
||||
infcx,
|
||||
&mut regioncx,
|
||||
&mir,
|
||||
param_env,
|
||||
liveness,
|
||||
flow_inits,
|
||||
move_data,
|
||||
);
|
||||
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir);
|
||||
|
||||
// Solve the region constraints.
|
||||
let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
|
||||
|
@ -425,11 +425,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// therefore add `end('a)` into the region for `'b` -- but we
|
||||
/// have no evidence that `'b` outlives `'a`, so we want to report
|
||||
/// an error.
|
||||
fn check_type_tests(
|
||||
&self,
|
||||
infcx: &InferCtxt<'_, '_, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
) {
|
||||
fn check_type_tests(&self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
|
||||
for type_test in &self.type_tests {
|
||||
debug!("check_type_test: {:?}", type_test);
|
||||
|
||||
@ -473,13 +469,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
.iter()
|
||||
.any(|&r| self.eval_outlives(mir, r, lower_bound, point)),
|
||||
|
||||
RegionTest::Any(tests) => tests
|
||||
.iter()
|
||||
.any(|test| self.eval_region_test(mir, point, lower_bound, test)),
|
||||
RegionTest::Any(tests) => tests.iter().any(|test| {
|
||||
self.eval_region_test(mir, point, lower_bound, test)
|
||||
}),
|
||||
|
||||
RegionTest::All(tests) => tests
|
||||
.iter()
|
||||
.all(|test| self.eval_region_test(mir, point, lower_bound, test)),
|
||||
RegionTest::All(tests) => tests.iter().all(|test| {
|
||||
self.eval_region_test(mir, point, lower_bound, test)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
220
src/librustc_mir/borrow_check/nll/type_check/liveness.rs
Normal file
220
src/librustc_mir/borrow_check/nll/type_check/liveness.rs
Normal file
@ -0,0 +1,220 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
use dataflow::{FlowAtLocation, FlowsAtLocation};
|
||||
use dataflow::MaybeInitializedLvals;
|
||||
use dataflow::move_paths::{HasMoveData, MoveData};
|
||||
use rustc::mir::{BasicBlock, Location, Mir};
|
||||
use rustc::mir::Local;
|
||||
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc::util::common::ErrorReported;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use syntax::codemap::DUMMY_SP;
|
||||
use util::liveness::LivenessResults;
|
||||
|
||||
use super::TypeChecker;
|
||||
|
||||
/// Combines liveness analysis with initialization analysis to
|
||||
/// determine which variables are live at which points, both due to
|
||||
/// ordinary uses and drops. Returns a set of (ty, location) pairs
|
||||
/// that indicate which types must be live at which point in the CFG.
|
||||
/// This vector is consumed by `constraint_generation`.
|
||||
///
|
||||
/// NB. This computation requires normalization; therefore, it must be
|
||||
/// performed before
|
||||
pub(super) fn generate<'gcx, 'tcx>(
|
||||
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
liveness: &LivenessResults,
|
||||
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) {
|
||||
let tcx = cx.tcx();
|
||||
let mut generator = TypeLivenessGenerator {
|
||||
cx,
|
||||
tcx,
|
||||
mir,
|
||||
liveness,
|
||||
flow_inits,
|
||||
move_data,
|
||||
};
|
||||
|
||||
for bb in mir.basic_blocks().indices() {
|
||||
generator.add_liveness_constraints(bb);
|
||||
}
|
||||
}
|
||||
|
||||
struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
|
||||
where
|
||||
'typeck: 'gen,
|
||||
'flow: 'gen,
|
||||
'tcx: 'typeck + 'flow,
|
||||
'gcx: 'tcx,
|
||||
{
|
||||
cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
|
||||
tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
|
||||
mir: &'gen Mir<'tcx>,
|
||||
liveness: &'gen LivenessResults,
|
||||
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedLvals<'flow, 'gcx, 'tcx>>,
|
||||
move_data: &'gen MoveData<'tcx>,
|
||||
}
|
||||
|
||||
impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> {
|
||||
/// Liveness constraints:
|
||||
///
|
||||
/// > If a variable V is live at point P, then all regions R in the type of V
|
||||
/// > must include the point P.
|
||||
fn add_liveness_constraints(&mut self, bb: BasicBlock) {
|
||||
debug!("add_liveness_constraints(bb={:?})", bb);
|
||||
|
||||
self.liveness
|
||||
.regular
|
||||
.simulate_block(self.mir, bb, |location, live_locals| {
|
||||
for live_local in live_locals.iter() {
|
||||
let live_local_ty = self.mir.local_decls[live_local].ty;
|
||||
self.push_type_live_constraint(live_local_ty, location);
|
||||
}
|
||||
});
|
||||
|
||||
let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
|
||||
self.liveness
|
||||
.drop
|
||||
.simulate_block(self.mir, bb, |location, live_locals| {
|
||||
all_live_locals.push((location, live_locals.iter().collect()));
|
||||
});
|
||||
debug!(
|
||||
"add_liveness_constraints: all_live_locals={:#?}",
|
||||
all_live_locals
|
||||
);
|
||||
|
||||
let terminator_index = self.mir.basic_blocks()[bb].statements.len();
|
||||
self.flow_inits.reset_to_entry_of(bb);
|
||||
while let Some((location, live_locals)) = all_live_locals.pop() {
|
||||
for live_local in live_locals {
|
||||
debug!(
|
||||
"add_liveness_constraints: location={:?} live_local={:?}",
|
||||
location,
|
||||
live_local
|
||||
);
|
||||
|
||||
self.flow_inits.each_state_bit(|mpi_init| {
|
||||
debug!(
|
||||
"add_liveness_constraints: location={:?} initialized={:?}",
|
||||
location,
|
||||
&self.flow_inits.operator().move_data().move_paths[mpi_init]
|
||||
);
|
||||
});
|
||||
|
||||
let mpi = self.move_data.rev_lookup.find_local(live_local);
|
||||
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
|
||||
debug!(
|
||||
"add_liveness_constraints: mpi={:?} has initialized child {:?}",
|
||||
self.move_data.move_paths[mpi],
|
||||
self.move_data.move_paths[initialized_child]
|
||||
);
|
||||
|
||||
let live_local_ty = self.mir.local_decls[live_local].ty;
|
||||
self.add_drop_live_constraint(live_local_ty, location);
|
||||
}
|
||||
}
|
||||
|
||||
if location.statement_index == terminator_index {
|
||||
debug!(
|
||||
"add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
|
||||
location
|
||||
);
|
||||
self.flow_inits.reconstruct_terminator_effect(location);
|
||||
} else {
|
||||
debug!(
|
||||
"add_liveness_constraints: reconstruct_statement_effect from {:#?}",
|
||||
location
|
||||
);
|
||||
self.flow_inits.reconstruct_statement_effect(location);
|
||||
}
|
||||
self.flow_inits.apply_local_effect(location);
|
||||
}
|
||||
}
|
||||
|
||||
/// Some variable with type `live_ty` is "regular live" at
|
||||
/// `location` -- i.e., it may be used later. This means that all
|
||||
/// regions appearing in the type `live_ty` must be live at
|
||||
/// `location`.
|
||||
fn push_type_live_constraint<T>(&mut self, value: T, location: Location)
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
debug!(
|
||||
"push_type_live_constraint(live_ty={:?}, location={:?})",
|
||||
value,
|
||||
location
|
||||
);
|
||||
|
||||
self.tcx.for_each_free_region(&value, |live_region| {
|
||||
self.cx
|
||||
.constraints
|
||||
.liveness_set
|
||||
.push((live_region, location));
|
||||
});
|
||||
}
|
||||
|
||||
/// Some variable with type `live_ty` is "drop live" at `location`
|
||||
/// -- i.e., it may be dropped later. This means that *some* of
|
||||
/// the regions in its type must be live at `location`. The
|
||||
/// precise set will depend on the dropck constraints, and in
|
||||
/// particular this takes `#[may_dangle]` into account.
|
||||
fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
|
||||
debug!(
|
||||
"add_drop_live_constraint(dropped_ty={:?}, location={:?})",
|
||||
dropped_ty,
|
||||
location
|
||||
);
|
||||
|
||||
let tcx = self.cx.infcx.tcx;
|
||||
let mut types = vec![(dropped_ty, 0)];
|
||||
let mut known = FxHashSet();
|
||||
while let Some((ty, depth)) = types.pop() {
|
||||
let span = DUMMY_SP; // FIXME
|
||||
let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
|
||||
Ok(result) => result,
|
||||
Err(ErrorReported) => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let ty::DtorckConstraint {
|
||||
outlives,
|
||||
dtorck_types,
|
||||
} = result;
|
||||
|
||||
// All things in the `outlives` array may be touched by
|
||||
// the destructor and must be live at this point.
|
||||
for outlive in outlives {
|
||||
self.push_type_live_constraint(outlive, location);
|
||||
}
|
||||
|
||||
// However, there may also be some types that
|
||||
// `dtorck_constraint_for_ty` could not resolve (e.g.,
|
||||
// associated types and parameters). We need to normalize
|
||||
// associated types here and possibly recursively process.
|
||||
for ty in dtorck_types {
|
||||
let ty = self.cx.normalize(&ty, location);
|
||||
match ty.sty {
|
||||
ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
|
||||
self.push_type_live_constraint(ty, location);
|
||||
}
|
||||
|
||||
_ => if known.insert(ty) {
|
||||
types.push((ty, depth + 1));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,9 @@
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
|
||||
use dataflow::FlowAtLocation;
|
||||
use dataflow::MaybeInitializedLvals;
|
||||
use dataflow::move_paths::MoveData;
|
||||
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
|
||||
use rustc::infer::region_constraints::RegionConstraintData;
|
||||
use rustc::traits::{self, FulfillmentContext};
|
||||
@ -26,22 +29,43 @@ use std::fmt;
|
||||
use syntax::ast;
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
use transform::{MirPass, MirSource};
|
||||
use util::liveness::LivenessResults;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
|
||||
mod liveness;
|
||||
|
||||
/// Type checks the given `mir` in the context of the inference
|
||||
/// context `infcx`. Returns any region constraints that have yet to
|
||||
/// be proven.
|
||||
/// be proven. This result is includes liveness constraints that
|
||||
/// ensure that regions appearing in the types of all local variables
|
||||
/// are live at all points where that local variable may later be
|
||||
/// used.
|
||||
///
|
||||
/// This phase of type-check ought to be infallible -- this is because
|
||||
/// the original, HIR-based type-check succeeded. So if any errors
|
||||
/// occur here, we will get a `bug!` reported.
|
||||
pub fn type_check<'a, 'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||
pub(crate) fn type_check<'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
param_env: ty::ParamEnv<'gcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
liveness: &LivenessResults,
|
||||
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) -> MirTypeckRegionConstraints<'tcx> {
|
||||
type_check_internal(infcx, body_id, param_env, mir, &mut |cx| {
|
||||
liveness::generate(cx, mir, liveness, flow_inits, move_data)
|
||||
})
|
||||
}
|
||||
|
||||
fn type_check_internal<'gcx, 'tcx>(
|
||||
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
|
||||
body_id: ast::NodeId,
|
||||
param_env: ty::ParamEnv<'gcx>,
|
||||
mir: &Mir<'tcx>,
|
||||
extra: &mut FnMut(&mut TypeChecker<'_, 'gcx, 'tcx>),
|
||||
) -> MirTypeckRegionConstraints<'tcx> {
|
||||
let mut checker = TypeChecker::new(infcx, body_id, param_env);
|
||||
let errors_reported = {
|
||||
@ -55,9 +79,12 @@ pub fn type_check<'a, 'gcx, 'tcx>(
|
||||
checker.typeck_mir(mir);
|
||||
}
|
||||
|
||||
extra(&mut checker);
|
||||
|
||||
checker.constraints
|
||||
}
|
||||
|
||||
|
||||
fn mirbug(tcx: TyCtxt, span: Span, msg: &str) {
|
||||
// We sometimes see MIR failures (notably predicate failures) due to
|
||||
// the fact that we check rvalue sized predicates here. So use `delay_span_bug`
|
||||
@ -503,7 +530,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
||||
/// constraints needed for it to be valid and well-typed. Along the
|
||||
/// way, it accrues region constraints -- these can later be used by
|
||||
/// NLL region checking.
|
||||
pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
||||
struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'gcx>,
|
||||
last_span: Span,
|
||||
@ -1474,7 +1501,7 @@ impl MirPass for TypeckMir {
|
||||
}
|
||||
let param_env = tcx.param_env(def_id);
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
let _region_constraint_sets = type_check(&infcx, id, param_env, mir);
|
||||
let _ = type_check_internal(&infcx, id, param_env, mir, &mut |_| ());
|
||||
|
||||
// For verification purposes, we just ignore the resulting
|
||||
// region constraint sets. Not our problem. =)
|
||||
|
53
src/test/ui/nll/ty-outlives/projection-fn.rs
Normal file
53
src/test/ui/nll/ty-outlives/projection-fn.rs
Normal file
@ -0,0 +1,53 @@
|
||||
// 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
|
||||
|
||||
#![allow(warnings)]
|
||||
#![feature(dyn_trait)]
|
||||
|
||||
trait Anything { }
|
||||
|
||||
impl<T> Anything for T { }
|
||||
|
||||
fn no_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a>
|
||||
where
|
||||
T: Iterator,
|
||||
{
|
||||
Box::new(x.next())
|
||||
//~^ WARNING not reporting region error due to -Znll
|
||||
//~| ERROR failed type test
|
||||
}
|
||||
|
||||
fn correct_region<'a, T>(mut x: T) -> Box<dyn Anything + 'a>
|
||||
where
|
||||
T: 'a + Iterator,
|
||||
{
|
||||
Box::new(x.next())
|
||||
}
|
||||
|
||||
fn wrong_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a>
|
||||
where
|
||||
T: 'b + Iterator,
|
||||
{
|
||||
Box::new(x.next())
|
||||
//~^ WARNING not reporting region error due to -Znll
|
||||
//~| ERROR failed type test
|
||||
}
|
||||
|
||||
fn outlives_region<'a, 'b, T>(mut x: T) -> Box<dyn Anything + 'a>
|
||||
where
|
||||
T: 'b + Iterator,
|
||||
'b: 'a,
|
||||
{
|
||||
Box::new(x.next())
|
||||
}
|
||||
|
||||
fn main() {}
|
26
src/test/ui/nll/ty-outlives/projection-fn.stderr
Normal file
26
src/test/ui/nll/ty-outlives/projection-fn.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/projection-fn.rs:24:5
|
||||
|
|
||||
24 | Box::new(x.next())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: not reporting region error due to -Znll
|
||||
--> $DIR/projection-fn.rs:40:5
|
||||
|
|
||||
40 | Box::new(x.next())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#3r, point: bb5[0], span: $DIR/projection-fn.rs:24:5: 24:23, test: IsOutlivedByAnyRegionIn([]) }
|
||||
--> $DIR/projection-fn.rs:24:5
|
||||
|
|
||||
24 | Box::new(x.next())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: failed type test: TypeTest { generic_kind: ProjectionTy { substs: Slice([T]), item_def_id: DefId(2/0:1695 ~ core[2633]::iter[0]::iterator[0]::Iterator[0]::Item[0]) }, lower_bound: '_#4r, point: bb5[0], span: $DIR/projection-fn.rs:40:5: 40:23, test: IsOutlivedByAnyRegionIn(['_#2r]) }
|
||||
--> $DIR/projection-fn.rs:40:5
|
||||
|
|
||||
40 | Box::new(x.next())
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user