connect NLL type checker to the impl trait code

We now add the suitable `impl Trait` constraints.
This commit is contained in:
Niko Matsakis 2017-12-10 10:23:45 -05:00
parent da63aaa7ab
commit 93afb1affc
14 changed files with 339 additions and 38 deletions

View File

@ -93,20 +93,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
/// Moreover, it returns a `AnonTypeMap` that would map `?0` to
/// info about the `impl Iterator<..>` type and `?1` to info about
/// the `impl Debug` type.
///
/// # Parameters
///
/// - `parent_def_id` -- we will only instantiate anonymous types
/// with this parent. This is typically the def-id of the function
/// in whose return type anon types are being instantiated.
/// - `body_id` -- the body-id with which the resulting obligations should
/// be associated
/// - `param_env` -- the in-scope parameter environment to be used for
/// obligations
/// - `value` -- the value within which we are instantiating anon types
pub fn instantiate_anon_types<T: TypeFoldable<'tcx>>(
&self,
parent_def_id: DefId,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'tcx>,
value: &T,
) -> InferOk<'tcx, (T, AnonTypeMap<'tcx>)> {
debug!(
"instantiate_anon_types(value={:?}, body_id={:?}, param_env={:?})",
"instantiate_anon_types(value={:?}, parent_def_id={:?}, body_id={:?}, param_env={:?})",
value,
parent_def_id,
body_id,
param_env,
);
let mut instantiator = Instantiator {
infcx: self,
parent_def_id,
body_id,
param_env,
anon_types: DefIdMap(),
@ -480,6 +494,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
parent_def_id: DefId,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'tcx>,
anon_types: AnonTypeMap<'tcx>,
@ -489,11 +504,33 @@ struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> {
fn instantiate_anon_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
debug!("instantiate_anon_types_in_map(value={:?})", value);
let tcx = self.infcx.tcx;
value.fold_with(&mut BottomUpFolder {
tcx: self.infcx.tcx,
fldop: |ty| if let ty::TyAnon(def_id, substs) = ty.sty {
self.fold_anon_ty(ty, def_id, substs)
} else {
tcx,
fldop: |ty| {
if let ty::TyAnon(def_id, substs) = ty.sty {
// Check that this is `impl Trait` type is declared by
// `parent_def_id`. During the first phase of type-check, this
// is true, but during NLL type-check, we sometimes encounter
// `impl Trait` types in e.g. inferred closure signatures that
// are not 'local' to the current function and hence which
// ought not to be instantiated.
if let Some(anon_node_id) = tcx.hir.as_local_node_id(def_id) {
let anon_parent_node_id = tcx.hir.get_parent(anon_node_id);
let anon_parent_def_id = tcx.hir.local_def_id(anon_parent_node_id);
if self.parent_def_id == anon_parent_def_id {
return self.fold_anon_ty(ty, def_id, substs);
}
debug!("instantiate_anon_types_in_map: \
encountered anon with wrong parent \
def_id={:?} \
anon_parent_def_id={:?}",
def_id,
anon_parent_def_id);
}
}
ty
},
})

View File

@ -865,13 +865,17 @@ impl fmt::Debug for ty::RegionVid {
define_print! {
() ty::InferTy, (self, f, cx) {
display {
match *self {
ty::TyVar(_) => write!(f, "_"),
ty::IntVar(_) => write!(f, "{}", "{integer}"),
ty::FloatVar(_) => write!(f, "{}", "{float}"),
ty::FreshTy(v) => write!(f, "FreshTy({})", v),
ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v),
ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v)
if cx.is_verbose {
print!(f, cx, print_debug(self))
} else {
match *self {
ty::TyVar(_) => write!(f, "_"),
ty::IntVar(_) => write!(f, "{}", "{integer}"),
ty::FloatVar(_) => write!(f, "{}", "{float}"),
ty::FreshTy(v) => write!(f, "FreshTy({})", v),
ty::FreshIntTy(v) => write!(f, "FreshIntTy({})", v),
ty::FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v)
}
}
}
debug {

View File

@ -77,13 +77,12 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
Option<ClosureRegionRequirements<'gcx>>,
) {
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
let liveness = &LivenessResults::compute(mir);
let constraint_sets = &type_check::type_check(
infcx,
mir_node_id,
param_env,
mir,
def_id,
&universal_regions,
&liveness,
flow_inits,

View File

@ -246,13 +246,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.map(|origin| RegionDefinition::new(origin))
.collect();
let nll_dump_cause = ty::tls::with(|tcx| tcx.sess.opts.debugging_opts.nll_dump_cause);
let mut result = Self {
definitions,
elements: elements.clone(),
liveness_constraints: RegionValues::new(
elements,
num_region_variables,
TrackCauses(true),
TrackCauses(nll_dump_cause),
),
inferred_values: None,
constraints: Vec::new(),

View File

@ -16,7 +16,7 @@ use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
/// 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>, mir: &mut Mir<'tcx>) {
pub fn renumber_mir<'tcx>(infcx: &InferCtxt<'_, '_, 'tcx>, mir: &mut Mir<'tcx>) {
debug!("renumber_mir()");
debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
@ -24,26 +24,36 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &mut
visitor.visit_mir(mir);
}
/// Replaces all regions appearing in `value` with fresh inference
/// variables.
pub fn renumber_regions<'tcx, T>(
infcx: &InferCtxt<'_, '_, 'tcx>,
ty_context: TyContext,
value: &T,
) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("renumber_regions(value={:?})", value);
infcx
.tcx
.fold_regions(value, &mut false, |_region, _depth| {
let origin = NLLRegionVariableOrigin::Inferred(ty_context);
infcx.next_nll_region_var(origin)
})
}
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
}
impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
/// Replaces all regions appearing in `value` with fresh inference
/// variables. This is what we do for almost the entire MIR, with
/// the exception of the declared types of our arguments.
fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("renumber_regions(value={:?})", value);
self.infcx
.tcx
.fold_regions(value, &mut false, |_region, _depth| {
let origin = NLLRegionVariableOrigin::Inferred(ty_context);
self.infcx.next_nll_region_var(origin)
})
renumber_regions(self.infcx, ty_context, value)
}
}

View File

@ -17,9 +17,15 @@
//! `RETURN_PLACE` the MIR arguments) are always fully normalize (and
//! contain revealed `impl Trait` values).
use borrow_check::nll::renumber;
use borrow_check::nll::universal_regions::UniversalRegions;
use rustc::hir::def_id::DefId;
use rustc::infer::InferOk;
use rustc::ty::Ty;
use rustc::ty::subst::Subst;
use rustc::mir::*;
use rustc::mir::visit::TyContext;
use rustc::traits::PredicateObligations;
use rustc_data_structures::indexed_vec::Idx;
@ -29,13 +35,17 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
pub(super) fn equate_inputs_and_outputs(
&mut self,
mir: &Mir<'tcx>,
mir_def_id: DefId,
universal_regions: &UniversalRegions<'tcx>,
) {
let tcx = self.infcx.tcx;
let &UniversalRegions {
unnormalized_output_ty,
unnormalized_input_tys,
..
} = universal_regions;
let infcx = self.infcx;
let start_position = Location {
block: START_BLOCK,
@ -52,10 +62,88 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
// Return types are a bit more complex. They may contain existential `impl Trait`
// types.
debug!(
"equate_inputs_and_outputs: unnormalized_output_ty={:?}",
unnormalized_output_ty
);
let output_ty = self.normalize(&unnormalized_output_ty, start_position);
debug!(
"equate_inputs_and_outputs: normalized output_ty={:?}",
output_ty
);
let mir_output_ty = mir.local_decls[RETURN_PLACE].ty;
self.equate_normalized_input_or_output(start_position, output_ty, mir_output_ty);
let anon_type_map = self.fully_perform_op(start_position.at_self(), |cx| {
let mut obligations = ObligationAccumulator::default();
let (output_ty, anon_type_map) = obligations.add(infcx.instantiate_anon_types(
mir_def_id,
cx.body_id,
cx.param_env,
&output_ty,
));
debug!(
"equate_inputs_and_outputs: instantiated output_ty={:?}",
output_ty
);
debug!(
"equate_inputs_and_outputs: anon_type_map={:#?}",
anon_type_map
);
debug!(
"equate_inputs_and_outputs: mir_output_ty={:?}",
mir_output_ty
);
obligations.add(infcx
.at(&cx.misc(cx.last_span), cx.param_env)
.eq(output_ty, mir_output_ty)?);
for (&anon_def_id, anon_decl) in &anon_type_map {
let anon_defn_ty = tcx.type_of(anon_def_id);
let anon_defn_ty = anon_defn_ty.subst(tcx, anon_decl.substs);
let anon_defn_ty = renumber::renumber_regions(
cx.infcx,
TyContext::Location(start_position),
&anon_defn_ty,
);
debug!(
"equate_inputs_and_outputs: concrete_ty={:?}",
anon_decl.concrete_ty
);
debug!("equate_inputs_and_outputs: anon_defn_ty={:?}", anon_defn_ty);
obligations.add(infcx
.at(&cx.misc(cx.last_span), cx.param_env)
.eq(anon_decl.concrete_ty, anon_defn_ty)?);
}
debug!("equate_inputs_and_outputs: equated");
Ok(InferOk {
value: Some(anon_type_map),
obligations: obligations.into_vec(),
})
}).unwrap_or_else(|terr| {
span_mirbug!(
self,
start_position,
"equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
output_ty,
mir_output_ty,
terr
);
None
});
// Finally
if let Some(anon_type_map) = anon_type_map {
self.fully_perform_op(start_position.at_self(), |_cx| {
infcx.constrain_anon_types(&anon_type_map, universal_regions);
Ok(InferOk {
value: (),
obligations: vec![],
})
}).unwrap();
}
}
fn equate_normalized_input_or_output(&mut self, location: Location, a: Ty<'tcx>, b: Ty<'tcx>) {
@ -73,3 +161,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
}
#[derive(Debug, Default)]
struct ObligationAccumulator<'tcx> {
obligations: PredicateObligations<'tcx>,
}
impl<'tcx> ObligationAccumulator<'tcx> {
fn add<T>(&mut self, value: InferOk<'tcx, T>) -> T {
let InferOk { value, obligations } = value;
self.obligations.extend(obligations);
value
}
fn into_vec(self) -> PredicateObligations<'tcx> {
self.obligations
}
}

View File

@ -17,6 +17,7 @@ use borrow_check::nll::universal_regions::UniversalRegions;
use dataflow::FlowAtLocation;
use dataflow::MaybeInitializedLvals;
use dataflow::move_paths::MoveData;
use rustc::hir::def_id::DefId;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
use rustc::infer::region_constraints::{GenericKind, RegionConstraintData};
use rustc::traits::{self, FulfillmentContext};
@ -77,9 +78,9 @@ mod input_output;
/// # 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
/// - `mir_def_id` -- DefId from which the MIR is derived (must be local)
/// - `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
@ -94,14 +95,15 @@ mod input_output;
/// - `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>,
mir_def_id: DefId,
universal_regions: &UniversalRegions<'tcx>,
liveness: &LivenessResults,
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) -> MirTypeckRegionConstraints<'tcx> {
let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap();
let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
type_check_internal(
infcx,
@ -113,7 +115,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
&mut |cx| {
liveness::generate(cx, mir, liveness, flow_inits, move_data);
cx.equate_inputs_and_outputs(mir, universal_regions);
cx.equate_inputs_and_outputs(mir, mir_def_id, universal_regions);
},
)
}

View File

@ -27,6 +27,7 @@ use rustc::hir::def_id::DefId;
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
use rustc::infer::region_constraints::GenericKind;
use rustc::infer::outlives::bounds::{self, OutlivesBound};
use rustc::infer::outlives::free_region_map::FreeRegionRelations;
use rustc::ty::{self, RegionVid, Ty, TyCtxt};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::Substs;
@ -484,9 +485,6 @@ impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
let (unnormalized_output_ty, unnormalized_input_tys) =
inputs_and_output.split_last().unwrap();
// we should not have created any more variables
assert_eq!(self.infcx.num_region_vars(), num_universals);
debug!(
"build: global regions = {}..{}",
FIRST_GLOBAL_INDEX,
@ -793,3 +791,16 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
})
}
}
/// This trait is used by the `impl-trait` constraint code to abstract
/// over the `FreeRegionMap` from lexical regions and
/// `UniversalRegions` (from NLL)`.
impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegions<'tcx> {
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool {
let shorter = shorter.to_region_vid();
assert!(self.is_universal_region(shorter));
let longer = longer.to_region_vid();
assert!(self.is_universal_region(longer));
self.outlives(longer, shorter)
}
}

View File

@ -985,7 +985,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
let ret_ty = fn_sig.output();
fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::SizedReturnType);
let ret_ty = fcx.instantiate_anon_types_from_return_value(&ret_ty);
let ret_ty = fcx.instantiate_anon_types_from_return_value(fn_id, &ret_ty);
fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
fn_sig = fcx.tcx.mk_fn_sig(
fn_sig.inputs().iter().cloned(),
@ -1880,11 +1880,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// function with type variables and records the `AnonTypeMap` for
/// later use during writeback. See
/// `InferCtxt::instantiate_anon_types` for more details.
fn instantiate_anon_types_from_return_value<T: TypeFoldable<'tcx>>(&self, value: &T) -> T {
debug!("instantiate_anon_types_from_return_value(value={:?})", value);
fn instantiate_anon_types_from_return_value<T: TypeFoldable<'tcx>>(
&self,
fn_id: ast::NodeId,
value: &T,
) -> T {
let fn_def_id = self.tcx.hir.local_def_id(fn_id);
debug!(
"instantiate_anon_types_from_return_value(fn_def_id={:?}, value={:?})",
fn_def_id,
value
);
let (value, anon_type_map) = self.register_infer_ok_obligations(
self.instantiate_anon_types(
fn_def_id,
self.body_id,
self.param_env,
value,

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions: normal nll
//[nll] compile-flags: -Znll -Zborrowck=mir
#![feature(conservative_impl_trait,
universal_impl_trait,
fn_traits,

View File

@ -0,0 +1,27 @@
// 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(conservative_impl_trait)]
trait Foo<'a> {
}
impl<'a, T> Foo<'a> for T { }
fn foo<'a, T>(x: &T) -> impl Foo<'a> {
x
//~^ WARNING not reporting region error due to -Znll
//~| ERROR free region `'_#2r` does not outlive free region `ReEarlyBound(0, 'a)`
}
fn main() {}

View File

@ -0,0 +1,14 @@
warning: not reporting region error due to -Znll
--> $DIR/impl-trait-captures.rs:22:5
|
22 | x
| ^
error: free region `'_#2r` does not outlive free region `ReEarlyBound(0, 'a)`
--> $DIR/impl-trait-captures.rs:22:5
|
22 | x
| ^
error: aborting due to previous error

View File

@ -0,0 +1,51 @@
// 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(conservative_impl_trait)]
use std::fmt::Debug;
fn no_region<'a, T>(x: Box<T>) -> impl Debug + 'a
//~^ WARNING not reporting region error due to -Znll
where
T: Debug,
{
x
//~^ ERROR `T` does not outlive
}
fn correct_region<'a, T>(x: Box<T>) -> impl Debug + 'a
where
T: 'a + Debug,
{
x
}
fn wrong_region<'a, 'b, T>(x: Box<T>) -> impl Debug + 'a
//~^ WARNING not reporting region error due to -Znll
where
T: 'b + Debug,
{
x
//~^ ERROR `T` does not outlive
}
fn outlives_region<'a, 'b, T>(x: Box<T>) -> impl Debug + 'a
where
T: 'b + Debug,
'b: 'a,
{
x
}
fn main() {}

View File

@ -0,0 +1,26 @@
warning: not reporting region error due to -Znll
--> $DIR/impl-trait-outlives.rs:18:35
|
18 | fn no_region<'a, T>(x: Box<T>) -> impl Debug + 'a
| ^^^^^^^^^^^^^^^
warning: not reporting region error due to -Znll
--> $DIR/impl-trait-outlives.rs:34:42
|
34 | fn wrong_region<'a, 'b, T>(x: Box<T>) -> impl Debug + 'a
| ^^^^^^^^^^^^^^^
error: `T` does not outlive `'_#1r`
--> $DIR/impl-trait-outlives.rs:23:5
|
23 | x
| ^
error: `T` does not outlive `'_#1r`
--> $DIR/impl-trait-outlives.rs:39:5
|
39 | x
| ^
error: aborting due to 2 previous errors