impose inputs/ouputs on MIR after the fact

The input/output types found in `UniversalRegions` are not normalized.
The old code used to assign them directly into the MIR, which would
lead to errors when there was a projection in a argument or return
type. This also led to some special cases in the `renumber` code.

We now renumber uniformly but then pass the input/output types into
the MIR type-checker, which equates them with the types found in MIR.
This allows us to normalize at the same time.
This commit is contained in:
Niko Matsakis 2017-12-06 04:30:58 -05:00
parent 3fcb13ae45
commit e9824c50ed
6 changed files with 94 additions and 68 deletions

View File

@ -53,7 +53,7 @@ pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
let universal_regions = UniversalRegions::new(infcx, def_id, param_env);
// Replace all remaining regions with fresh inference variables.
renumber::renumber_mir(infcx, &universal_regions, mir);
renumber::renumber_mir(infcx, mir);
let source = MirSource::item(def_id);
mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(()));
@ -86,6 +86,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
param_env,
mir,
fr_fn_body,
universal_regions.input_tys,
universal_regions.output_ty,
&liveness,
flow_inits,
move_data,

View File

@ -8,50 +8,24 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::subst::Substs;
use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
use rustc::mir::RETURN_PLACE;
use rustc::mir::{BasicBlock, Location, Mir, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use super::ToRegionVid;
use super::universal_regions::UniversalRegions;
/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
universal_regions: &UniversalRegions<'tcx>,
mir: &mut Mir<'tcx>,
) {
pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut Mir<'tcx>) {
debug!("renumber_mir()");
debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
// Update the return type and types of the arguments based on the
// `universal_regions` computation.
debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty);
mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty;
for (&input_ty, local) in universal_regions
.input_tys
.iter()
.zip((1..).map(Local::new))
{
debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local);
mir.local_decls[local].ty = input_ty;
}
let mut visitor = NLLVisitor {
infcx,
arg_count: mir.arg_count,
};
let mut visitor = NLLVisitor { infcx };
visitor.visit_mir(mir);
}
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
arg_count: usize,
}
impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
@ -71,45 +45,13 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
self.infcx.next_nll_region_var(origin)
})
}
/// Checks that all the regions appearing in `value` have already
/// been renumbered. `FreeRegions` code should have done this.
fn assert_free_regions_are_renumbered<T>(&self, value: &T)
where
T: TypeFoldable<'tcx>,
{
debug!("assert_free_regions_are_renumbered(value={:?})", value);
self.infcx.tcx.for_each_free_region(value, |region| {
region.to_region_vid(); // will panic if `region` is not renumbered
});
}
fn is_argument_or_return_slot(&self, local: Local) -> bool {
// The first argument is return slot, next N are arguments.
local.index() <= self.arg_count
}
}
impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
let is_arg = match ty_context {
TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
TyContext::ReturnTy(..) => true,
TyContext::Location(..) => false,
};
debug!(
"visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})",
ty,
is_arg,
ty_context
);
debug!("visit_ty(ty={:?}, ty_context={:?})", ty, ty_context);
if is_arg {
self.assert_free_regions_are_renumbered(ty);
} else {
*ty = self.renumber_regions(ty_context, ty);
}
*ty = self.renumber_regions(ty_context, ty);
debug!("visit_ty: ty={:?}", ty);
}

View File

@ -46,12 +46,31 @@ mod liveness;
/// 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.
///
/// # Parameters
///
/// - `infcx` -- inference context to use
/// - `body_id` -- body-id of the MIR being checked
/// - `param_env` -- parameter environment to use for trait solving
/// - `mir` -- MIR to type-check
/// - `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;
/// the types of the input parameters found in the MIR itself will be equated with these
/// - `output_ty` -- fully liberaetd, but **not** normalized, expected return type;
/// the type for the RETURN_PLACE will be equated with this
/// - `liveness` -- results of a liveness computation on the MIR; used to create liveness
/// constraints for the regions in the types of variables
/// - `flow_inits` -- results of a maybe-init dataflow analysis
/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
pub(crate) fn type_check<'gcx, 'tcx>(
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'gcx>,
mir: &Mir<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
input_tys: &[Ty<'tcx>],
output_ty: Ty<'tcx>,
liveness: &LivenessResults,
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
@ -62,7 +81,16 @@ pub(crate) fn type_check<'gcx, 'tcx>(
param_env,
mir,
Some(implicit_region_bound),
&mut |cx| liveness::generate(cx, mir, liveness, flow_inits, move_data),
&mut |cx| {
liveness::generate(cx, mir, liveness, flow_inits, move_data);
// Equate the input and output tys given by the user with
// the ones found in the MIR.
cx.equate_input_or_output(output_ty, mir.local_decls[RETURN_PLACE].ty);
for (&input_ty, local) in input_tys.iter().zip((1..).map(Local::new)) {
cx.equate_input_or_output(input_ty, mir.local_decls[local].ty);
}
},
)
}
@ -666,6 +694,25 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
})
}
fn equate_input_or_output(&mut self, unnormalized_a: Ty<'tcx>, b: Ty<'tcx>) {
let start_position = Location {
block: START_BLOCK,
statement_index: 0,
};
let a = self.normalize(&unnormalized_a, start_position);
if let Err(terr) = self.eq_types(a, b, start_position.at_self()) {
span_mirbug!(
self,
start_position,
"bad input or output {:?} normalized to {:?} should equal {:?} but got error {:?}",
unnormalized_a,
a,
b,
terr
);
}
}
fn tcx(&self) -> TyCtxt<'a, 'gcx, 'tcx> {
self.infcx.tcx
}

View File

@ -69,12 +69,14 @@ pub struct UniversalRegions<'tcx> {
/// closure type, but for a top-level function it's the `TyFnDef`.
pub defining_ty: Ty<'tcx>,
/// The return type of this function, with all regions replaced
/// by their universal `RegionVid` equivalents.
/// The return type of this function, with all regions replaced by
/// their universal `RegionVid` equivalents. This type is **NOT
/// NORMALIZED**.
pub output_ty: Ty<'tcx>,
/// The fully liberated input types of this function, with all
/// regions replaced by their universal `RegionVid` equivalents.
/// This type is **NOT NORMALIZED**.
pub input_tys: &'tcx [Ty<'tcx>],
/// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to

View File

@ -39,7 +39,11 @@ fn main() {
// | '_#2r | {'_#2r, bb0[0], bb0[1]}
// | '_#3r | {'_#3r, bb0[0], bb0[1]}
// | '_#4r | {'_#4r, bb0[0], bb0[1]}
// | '_#5r | {'_#1r, bb0[0], bb0[1]}
// | '_#6r | {'_#2r, bb0[0], bb0[1]}
// | '_#7r | {'_#1r, bb0[0], bb0[1]}
// | '_#8r | {'_#3r, bb0[0], bb0[1]}
// |
// ...
// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {
// fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool {
// END rustc.use_x.nll.0.mir

View File

@ -0,0 +1,29 @@
// 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
// must-compile-successfully
#![feature(rustc_attrs)]
trait Foo {
type Bar;
}
impl Foo for () {
type Bar = u32;
}
fn foo() -> <() as Foo>::Bar {
22
}
fn main() { }