mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-04 02:54:00 +00:00
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:
parent
3fcb13ae45
commit
e9824c50ed
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
29
src/test/ui/nll/projection-return.rs
Normal file
29
src/test/ui/nll/projection-return.rs
Normal 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() { }
|
||||
|
Loading…
Reference in New Issue
Block a user