diff --git a/src/librustc/infer/anon_types/mod.rs b/src/librustc/infer/anon_types/mod.rs new file mode 100644 index 00000000000..6af4f13db88 --- /dev/null +++ b/src/librustc/infer/anon_types/mod.rs @@ -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 or the MIT license +// , 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>; + +/// 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 + /// + /// this method would create two type variables, `?0` and `?1`. It would + /// return the type `?0` but also the obligations: + /// + /// ?0: Iterator + /// ?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>( + &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>, +} + +impl<'a, 'gcx, 'tcx> Instantiator<'a, 'gcx, 'tcx> { + fn instantiate_anon_types_in_map>(&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 + } +} diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 9788ec2a5e4..32da08bf476 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -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; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3da71d1c0c5..ef66920604b 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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, } -/// 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>(&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>(&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(&self, span: Span, value: &T) -> T diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index bf8f9d8b24a..e8d669838d3 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -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)]