From e9824c50edd067fefb2e4fd6f6d1a4fe739a145b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Dec 2017 04:30:58 -0500 Subject: [PATCH] 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. --- src/librustc_mir/borrow_check/nll/mod.rs | 4 +- src/librustc_mir/borrow_check/nll/renumber.rs | 68 ++----------------- .../borrow_check/nll/type_check/mod.rs | 49 ++++++++++++- .../borrow_check/nll/universal_regions.rs | 6 +- src/test/mir-opt/nll/named-lifetimes-basic.rs | 6 +- src/test/ui/nll/projection-return.rs | 29 ++++++++ 6 files changed, 94 insertions(+), 68 deletions(-) create mode 100644 src/test/ui/nll/projection-return.rs diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 23fa2689053b..6977d91d25a5 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -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, diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index 1262c238a132..79505405692d 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -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(&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); } 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 5c3cdbe2207b..6cdd77048c99 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -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>, 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 } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 5f4a72542b20..99beae13bd9e 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -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 diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs index 71304f71b610..f14979f97339 100644 --- a/src/test/mir-opt/nll/named-lifetimes-basic.rs +++ b/src/test/mir-opt/nll/named-lifetimes-basic.rs @@ -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 diff --git a/src/test/ui/nll/projection-return.rs b/src/test/ui/nll/projection-return.rs new file mode 100644 index 000000000000..31388cf50c55 --- /dev/null +++ b/src/test/ui/nll/projection-return.rs @@ -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 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 +// must-compile-successfully + +#![feature(rustc_attrs)] + +trait Foo { + type Bar; +} + +impl Foo for () { + type Bar = u32; +} + +fn foo() -> <() as Foo>::Bar { + 22 +} + +fn main() { } +