extract instantiate_anon_types to the InferCtxt

No functional change.
This commit is contained in:
Niko Matsakis 2017-12-09 05:49:14 -05:00
parent 4a967c9df7
commit e96f4be03d
4 changed files with 224 additions and 111 deletions

View File

@ -0,0 +1,200 @@
// Copyright 2012-2015 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 hir::def_id::DefId;
use infer::{InferCtxt, InferOk, TypeVariableOrigin};
use syntax::ast;
use traits::{self, PredicateObligation};
use ty::{self, Ty};
use ty::fold::{BottomUpFolder, TypeFoldable};
use ty::subst::Substs;
use util::nodemap::DefIdMap;
pub type AnonTypeMap<'tcx> = DefIdMap<AnonTypeDecl<'tcx>>;
/// Information about the anonymous, abstract types whose values we
/// are inferring in this function (these are the `impl Trait` that
/// appear in the return type).
#[derive(Copy, Clone, Debug)]
pub struct AnonTypeDecl<'tcx> {
/// The substitutions that we apply to the abstract that that this
/// `impl Trait` desugars to. e.g., if:
///
/// fn foo<'a, 'b, T>() -> impl Trait<'a>
///
/// winds up desugared to:
///
/// abstract type Foo<'x, T>: Trait<'x>
/// fn foo<'a, 'b, T>() -> Foo<'a, T>
///
/// then `substs` would be `['a, T]`.
pub substs: &'tcx Substs<'tcx>,
/// The type variable that represents the value of the abstract type
/// that we require. In other words, after we compile this function,
/// we will be created a constraint like:
///
/// Foo<'a, T> = ?C
///
/// where `?C` is the value of this type variable. =) It may
/// naturally refer to the type and lifetime parameters in scope
/// in this function, though ultimately it should only reference
/// those that are arguments to `Foo` in the constraint above. (In
/// other words, `?C` should not include `'b`, even though it's a
/// lifetime parameter on `foo`.)
pub concrete_ty: Ty<'tcx>,
/// True if the `impl Trait` bounds include region bounds.
/// For example, this would be true for:
///
/// fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b
///
/// but false for:
///
/// fn foo<'c>() -> impl Trait<'c>
///
/// unless `Trait` was declared like:
///
/// trait Trait<'c>: 'c
///
/// in which case it would be true.
///
/// This is used during regionck to decide whether we need to
/// impose any additional constraints to ensure that region
/// variables in `concrete_ty` wind up being constrained to
/// something from `substs` (or, at minimum, things that outlive
/// the fn body). (Ultimately, writeback is responsible for this
/// check.)
pub has_required_region_bounds: bool,
}
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
/// Replace all anonymized types in `value` with fresh inference variables
/// and creates appropriate obligations. For example, given the input:
///
/// impl Iterator<Item = impl Debug>
///
/// this method would create two type variables, `?0` and `?1`. It would
/// return the type `?0` but also the obligations:
///
/// ?0: Iterator<Item = ?1>
/// ?1: Debug
///
/// 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.
pub fn instantiate_anon_types<T: TypeFoldable<'tcx>>(
&self,
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={:?})",
value,
body_id,
param_env,
);
let mut instantiator = Instantiator {
infcx: self,
body_id,
param_env,
anon_types: DefIdMap(),
obligations: vec![],
};
let value = instantiator.instantiate_anon_types_in_map(value);
InferOk {
value: (value, instantiator.anon_types),
obligations: instantiator.obligations,
}
}
}
struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'tcx>,
anon_types: AnonTypeMap<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
}
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);
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 {
ty
},
})
}
fn fold_anon_ty(
&mut self,
ty: Ty<'tcx>,
def_id: DefId,
substs: &'tcx Substs<'tcx>,
) -> Ty<'tcx> {
let infcx = self.infcx;
let tcx = infcx.tcx;
debug!(
"instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})",
def_id,
substs
);
// Use the same type variable if the exact same TyAnon appears more
// than once in the return type (e.g. if it's passed to a type alias).
if let Some(anon_defn) = self.anon_types.get(&def_id) {
return anon_defn.concrete_ty;
}
let span = tcx.def_span(def_id);
let ty_var = infcx.next_ty_var(TypeVariableOrigin::TypeInference(span));
let predicates_of = tcx.predicates_of(def_id);
let bounds = predicates_of.instantiate(tcx, substs);
debug!("instantiate_anon_types: bounds={:?}", bounds);
let required_region_bounds = tcx.required_region_bounds(ty, bounds.predicates.clone());
debug!(
"instantiate_anon_types: required_region_bounds={:?}",
required_region_bounds
);
self.anon_types.insert(
def_id,
AnonTypeDecl {
substs,
concrete_ty: ty_var,
has_required_region_bounds: !required_region_bounds.is_empty(),
},
);
debug!("instantiate_anon_types: ty_var={:?}", ty_var);
for predicate in bounds.predicates {
// Change the predicate to refer to the type variable,
// which will be the concrete type, instead of the TyAnon.
// This also instantiates nested `impl Trait`.
let predicate = self.instantiate_anon_types_in_map(&predicate);
let cause = traits::ObligationCause::new(span, self.body_id, traits::SizedReturnType);
// Require that the predicate holds for the concrete type.
debug!("instantiate_anon_types: predicate={:?}", predicate);
self.obligations
.push(traits::Obligation::new(cause, self.param_env, predicate));
}
ty_var
}
}

View File

@ -48,6 +48,7 @@ use self::outlives::env::OutlivesEnvironment;
use self::type_variable::TypeVariableOrigin;
use self::unify_key::ToType;
pub mod anon_types;
pub mod at;
mod combine;
mod equate;

View File

@ -90,6 +90,7 @@ use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use std::slice;
use namespace::Namespace;
use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
use rustc::infer::anon_types::AnonTypeDecl;
use rustc::infer::type_variable::{TypeVariableOrigin};
use rustc::middle::region;
use rustc::ty::subst::{Kind, Subst, Substs};
@ -97,7 +98,7 @@ use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCo
use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
use rustc::ty::{self, Ty, TyCtxt, Visibility};
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::maps::Providers;
use rustc::ty::util::{Representability, IntTypeExt};
use errors::{DiagnosticBuilder, DiagnosticId};
@ -225,62 +226,6 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
body_id: Option<hir::BodyId>,
}
/// Information about the anonymous, abstract types whose values we
/// are inferring in this function (these are the `impl Trait` that
/// appear in the return type).
#[derive(Debug)]
struct AnonTypeDecl<'tcx> {
/// The substitutions that we apply to the abstract that that this
/// `impl Trait` desugars to. e.g., if:
///
/// fn foo<'a, 'b, T>() -> impl Trait<'a>
///
/// winds up desugared to:
///
/// abstract type Foo<'x, T>: Trait<'x>
/// fn foo<'a, 'b, T>() -> Foo<'a, T>
///
/// then `substs` would be `['a, T]`.
substs: &'tcx Substs<'tcx>,
/// The type variable that represents the value of the abstract type
/// that we require. In other words, after we compile this function,
/// we will be created a constraint like:
///
/// Foo<'a, T> = ?C
///
/// where `?C` is the value of this type variable. =) It may
/// naturally refer to the type and lifetime parameters in scope
/// in this function, though ultimately it should only reference
/// those that are arguments to `Foo` in the constraint above. (In
/// other words, `?C` should not include `'b`, even though it's a
/// lifetime parameter on `foo`.)
concrete_ty: Ty<'tcx>,
/// True if the `impl Trait` bounds include region bounds.
/// For example, this would be true for:
///
/// fn foo<'a, 'b, 'c>() -> impl Trait<'c> + 'a + 'b
///
/// but false for:
///
/// fn foo<'c>() -> impl Trait<'c>
///
/// unless `Trait` was declared like:
///
/// trait Trait<'c>: 'c
///
/// in which case it would be true.
///
/// This is used during regionck to decide whether we need to
/// impose any additional constraints to ensure that region
/// variables in `concrete_ty` wind up being constrained to
/// something from `substs` (or, at minimum, things that outlive
/// the fn body). (Ultimately, writeback is responsible for this
/// check.)
has_required_region_bounds: bool,
}
impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> {
type Target = InferCtxt<'a, 'gcx, 'tcx>;
fn deref(&self) -> &Self::Target {
@ -892,8 +837,6 @@ fn typeck_tables_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
&fn_sig);
let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, false).0;
// Ensure anon_types have been instantiated prior to entering regionck
fcx.instantiate_anon_types(&fn_sig.output());
fcx
} else {
let fcx = FnCtxt::new(&inh, param_env, body.value.id);
@ -1042,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(&ret_ty);
let ret_ty = fcx.instantiate_anon_types_from_return_value(&ret_ty);
fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
fn_sig = fcx.tcx.mk_fn_sig(
fn_sig.inputs().iter().cloned(),
@ -1933,60 +1876,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
result
}
/// Replace all anonymized types with fresh inference variables
/// and record them for writeback.
fn instantiate_anon_types<T: TypeFoldable<'tcx>>(&self, value: &T) -> T {
debug!("instantiate_anon_types(value={:?})", value);
value.fold_with(&mut BottomUpFolder { tcx: self.tcx, fldop: |ty| {
if let ty::TyAnon(def_id, substs) = ty.sty {
debug!("instantiate_anon_types: TyAnon(def_id={:?}, substs={:?})", def_id, substs);
/// Replace the anonymized types from the return value of the
/// 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);
// Use the same type variable if the exact same TyAnon appears more
// than once in the return type (e.g. if it's passed to a type alias).
if let Some(anon_defn) = self.anon_types.borrow().get(&def_id) {
return anon_defn.concrete_ty;
}
let span = self.tcx.def_span(def_id);
let ty_var = self.next_ty_var(TypeVariableOrigin::TypeInference(span));
let (value, anon_type_map) = self.register_infer_ok_obligations(
self.instantiate_anon_types(
self.body_id,
self.param_env,
value,
)
);
let predicates_of = self.tcx.predicates_of(def_id);
let bounds = predicates_of.instantiate(self.tcx, substs);
debug!("instantiate_anon_types: bounds={:?}", bounds);
let mut anon_types = self.anon_types.borrow_mut();
for (ty, decl) in anon_type_map {
let old_value = anon_types.insert(ty, decl);
assert!(old_value.is_none(), "instantiated twice: {:?}/{:?}", ty, decl);
}
let required_region_bounds =
self.tcx.required_region_bounds(ty, bounds.predicates.clone());
debug!("instantiate_anon_types: required_region_bounds={:?}",
required_region_bounds);
self.anon_types.borrow_mut().insert(def_id, AnonTypeDecl {
substs,
concrete_ty: ty_var,
has_required_region_bounds: !required_region_bounds.is_empty(),
});
debug!("instantiate_anon_types: ty_var={:?}", ty_var);
for predicate in bounds.predicates {
// Change the predicate to refer to the type variable,
// which will be the concrete type, instead of the TyAnon.
// This also instantiates nested `impl Trait`.
let predicate = self.instantiate_anon_types(&predicate);
// Require that the predicate holds for the concrete type.
let cause = traits::ObligationCause::new(span,
self.body_id,
traits::SizedReturnType);
debug!("instantiate_anon_types: predicate={:?}", predicate);
self.register_predicate(traits::Obligation::new(cause,
self.param_env,
predicate));
}
ty_var
} else {
ty
}
}})
value
}
fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T

View File

@ -81,6 +81,7 @@ This API is completely unstable and subject to change.
#![feature(match_default_bindings)]
#![feature(never_type)]
#![feature(quote)]
#![feature(refcell_replace_swap)]
#![feature(rustc_diagnostic_macros)]
#![feature(slice_patterns)]