Add the notion of normalizing a parameter environment and ensure that

all parameter environments are normalized. Correspondingly, stop
normalizing predicates we extract out of the environment. Fixes #21664.
This commit is contained in:
Niko Matsakis 2015-01-26 14:20:38 -05:00
parent c73a1d0a2c
commit c61d7889b4
9 changed files with 203 additions and 63 deletions

View File

@ -18,7 +18,7 @@ pub use self::ObligationCauseCode::*;
use middle::mem_categorization::Typer;
use middle::subst;
use middle::ty::{self, Ty};
use middle::infer::InferCtxt;
use middle::infer::{self, InferCtxt};
use std::slice::Iter;
use std::rc::Rc;
use syntax::ast;
@ -392,6 +392,52 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
}
}
pub fn normalize_param_env_or_error<'a,'tcx>(unnormalized_env: ty::ParameterEnvironment<'a,'tcx>,
cause: ObligationCause<'tcx>)
-> ty::ParameterEnvironment<'a,'tcx>
{
match normalize_param_env(&unnormalized_env, cause) {
Ok(p) => p,
Err(errors) => {
// this isn't really the ideal place to report errors, but it seems ok
let infcx = infer::new_infer_ctxt(unnormalized_env.tcx);
report_fulfillment_errors(&infcx, &errors);
// normalized failed? use what they gave us, it's better than nothing
unnormalized_env
}
}
}
pub fn normalize_param_env<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
cause: ObligationCause<'tcx>)
-> Result<ty::ParameterEnvironment<'a,'tcx>,
Vec<FulfillmentError<'tcx>>>
{
let tcx = param_env.tcx;
debug!("normalize_param_env(param_env={})",
param_env.repr(tcx));
let predicates: Vec<ty::Predicate<'tcx>> = {
let infcx = infer::new_infer_ctxt(tcx);
let mut selcx = &mut SelectionContext::new(&infcx, param_env);
let mut fulfill_cx = FulfillmentContext::new();
let Normalized { value: predicates, obligations } =
project::normalize(selcx, cause, &param_env.caller_bounds);
for obligation in obligations.into_iter() {
fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation);
}
try!(fulfill_cx.select_all_or_error(selcx.infcx(), param_env));
predicates.iter().map(|p| infcx.resolve_type_vars_if_possible(p)).collect()
};
debug!("normalize_param_env: predicates={}",
predicates.repr(tcx));
Ok(param_env.with_caller_bounds(predicates))
}
impl<'tcx,O> Obligation<'tcx,O> {
pub fn new(cause: ObligationCause<'tcx>,
trait_ref: O)

View File

@ -122,17 +122,15 @@ fn trait_has_sized_self<'tcx>(tcx: &ty::ctxt<'tcx>,
trait_def_id: ast::DefId)
-> bool
{
let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
let param_env = ty::construct_parameter_environment(tcx,
&trait_def.generics,
ast::DUMMY_NODE_ID);
let predicates = param_env.caller_bounds.clone();
let sized_def_id = match tcx.lang_items.sized_trait() {
Some(def_id) => def_id,
None => { return false; /* No Sized trait, can't require it! */ }
};
// Search for a predicate like `Self : Sized` amongst the trait bounds.
let trait_def = ty::lookup_trait_def(tcx, trait_def_id);
let free_substs = ty::construct_free_substs(tcx, &trait_def.generics, ast::DUMMY_NODE_ID);
let predicates = trait_def.generics.to_bounds(tcx, &free_substs).predicates.into_vec();
elaborate_predicates(tcx, predicates)
.any(|predicate| {
match predicate {

View File

@ -60,6 +60,11 @@ struct ProjectionTyCandidateSet<'tcx> {
ambiguous: bool
}
/// Evaluates constraints of the form:
///
/// for<...> <T as Trait>::U == V
///
/// If successful, this may result in additional obligations.
pub fn poly_project_and_unify_type<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &PolyProjectionObligation<'tcx>)
@ -102,8 +107,11 @@ pub fn poly_project_and_unify_type<'cx,'tcx>(
}
}
/// Compute result of projecting an associated type and unify it with
/// `obligation.predicate.ty` (if we can).
/// Evaluates constraints of the form:
///
/// <T as Trait>::U == V
///
/// If successful, this may result in additional obligations.
fn project_and_unify_type<'cx,'tcx>(
selcx: &mut SelectionContext<'cx,'tcx>,
obligation: &ProjectionObligation<'tcx>)
@ -133,6 +141,10 @@ fn project_and_unify_type<'cx,'tcx>(
}
}
/// Normalizes any associated type projections in `value`, replacing
/// them with a fully resolved type where possible. The return value
/// combines the normalized result and any additional obligations that
/// were incurred as result.
pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>,
cause: ObligationCause<'tcx>,
value: &T)
@ -142,6 +154,7 @@ pub fn normalize<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>,
normalize_with_depth(selcx, cause, 0, value)
}
/// As `normalize`, but with a custom depth.
pub fn normalize_with_depth<'a,'b,'tcx,T>(selcx: &'a mut SelectionContext<'b,'tcx>,
cause: ObligationCause<'tcx>,
depth: uint,
@ -251,6 +264,12 @@ impl<'tcx,T> Normalized<'tcx,T> {
}
}
/// The guts of `normalize`: normalize a specific projection like `<T
/// as Trait>::Item`. The result is always a type (and possibly
/// additional obligations). If ambiguity arises, which implies that
/// there are unresolved type variables in the projection, we will
/// substitute a fresh type variable `$X` and generate a new
/// obligation `<T as Trait>::Item == $X` for later.
pub fn normalize_projection_type<'a,'b,'tcx>(
selcx: &'a mut SelectionContext<'b,'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
@ -277,6 +296,10 @@ pub fn normalize_projection_type<'a,'b,'tcx>(
})
}
/// The guts of `normalize`: normalize a specific projection like `<T
/// as Trait>::Item`. The result is always a type (and possibly
/// additional obligations). Returns `None` in the case of ambiguity,
/// which indicates that there are unbound type variables.
fn opt_normalize_projection_type<'a,'b,'tcx>(
selcx: &'a mut SelectionContext<'b,'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,

View File

@ -2157,16 +2157,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
where_clause_trait_ref: ty::PolyTraitRef<'tcx>)
-> Result<Vec<PredicateObligation<'tcx>>,()>
{
let where_clause_trait_ref =
project::normalize_with_depth(self,
obligation.cause.clone(),
obligation.recursion_depth+1,
&where_clause_trait_ref);
let () =
try!(self.match_poly_trait_ref(obligation, where_clause_trait_ref.value.clone()));
try!(self.match_poly_trait_ref(obligation, where_clause_trait_ref));
Ok(where_clause_trait_ref.obligations)
Ok(Vec::new())
}
/// Returns `Ok` if `poly_trait_ref` being true implies that the

View File

@ -2084,11 +2084,7 @@ impl<'tcx> TraitRef<'tcx> {
pub struct ParameterEnvironment<'a, 'tcx:'a> {
pub tcx: &'a ctxt<'tcx>,
/// A substitution that can be applied to move from
/// the "outer" view of a type or method to the "inner" view.
/// In general, this means converting from bound parameters to
/// free parameters. Since we currently represent bound/free type
/// parameters in the same way, this only has an effect on regions.
/// See `construct_free_substs` for details.
pub free_substs: Substs<'tcx>,
/// Each type parameter has an implicit region bound that
@ -2108,6 +2104,19 @@ pub struct ParameterEnvironment<'a, 'tcx:'a> {
}
impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
pub fn with_caller_bounds(&self,
caller_bounds: Vec<ty::Predicate<'tcx>>)
-> ParameterEnvironment<'a,'tcx>
{
ParameterEnvironment {
tcx: self.tcx,
free_substs: self.free_substs.clone(),
implicit_region_bound: self.implicit_region_bound,
caller_bounds: caller_bounds,
selection_cache: traits::SelectionCache::new(),
}
}
pub fn for_item(cx: &'a ctxt<'tcx>, id: NodeId) -> ParameterEnvironment<'a, 'tcx> {
match cx.map.find(id) {
Some(ast_map::NodeImplItem(ref impl_item)) => {
@ -2119,6 +2128,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
let method_generics = &method_ty.generics;
construct_parameter_environment(
cx,
method.span,
method_generics,
method.pe_body().id)
}
@ -2153,6 +2163,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
let method_generics = &method_ty.generics;
construct_parameter_environment(
cx,
method.span,
method_generics,
method.pe_body().id)
}
@ -2179,6 +2190,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
let fn_pty = ty::lookup_item_type(cx, fn_def_id);
construct_parameter_environment(cx,
item.span,
&fn_pty.generics,
body.id)
}
@ -2189,7 +2201,7 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
ast::ItemStatic(..) => {
let def_id = ast_util::local_def(id);
let pty = ty::lookup_item_type(cx, def_id);
construct_parameter_environment(cx, &pty.generics, id)
construct_parameter_environment(cx, item.span, &pty.generics, id)
}
_ => {
cx.sess.span_bug(item.span,
@ -6263,18 +6275,17 @@ pub fn empty_parameter_environment<'a,'tcx>(cx: &'a ctxt<'tcx>) -> ParameterEnvi
selection_cache: traits::SelectionCache::new(), }
}
/// See `ParameterEnvironment` struct def'n for details
pub fn construct_parameter_environment<'a,'tcx>(
/// Constructs and returns a substitution that can be applied to move from
/// the "outer" view of a type or method to the "inner" view.
/// In general, this means converting from bound parameters to
/// free parameters. Since we currently represent bound/free type
/// parameters in the same way, this only has an effect on regions.
pub fn construct_free_substs<'a,'tcx>(
tcx: &'a ctxt<'tcx>,
generics: &ty::Generics<'tcx>,
free_id: ast::NodeId)
-> ParameterEnvironment<'a, 'tcx>
-> Substs<'tcx>
{
//
// Construct the free substs.
//
// map T => T
let mut types = VecPerParamSpace::empty();
push_types_from_defs(tcx, &mut types, generics.types.as_slice());
@ -6283,11 +6294,45 @@ pub fn construct_parameter_environment<'a,'tcx>(
let mut regions = VecPerParamSpace::empty();
push_region_params(&mut regions, free_id, generics.regions.as_slice());
let free_substs = Substs {
return Substs {
types: types,
regions: subst::NonerasedRegions(regions)
};
fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,
free_id: ast::NodeId,
region_params: &[RegionParameterDef])
{
for r in region_params.iter() {
regions.push(r.space, ty::free_region_from_def(free_id, r));
}
}
fn push_types_from_defs<'tcx>(tcx: &ty::ctxt<'tcx>,
types: &mut VecPerParamSpace<Ty<'tcx>>,
defs: &[TypeParameterDef<'tcx>]) {
for def in defs.iter() {
debug!("construct_parameter_environment(): push_types_from_defs: def={:?}",
def.repr(tcx));
let ty = ty::mk_param_from_def(tcx, def);
types.push(def.space, ty);
}
}
}
/// See `ParameterEnvironment` struct def'n for details
pub fn construct_parameter_environment<'a,'tcx>(
tcx: &'a ctxt<'tcx>,
span: Span,
generics: &ty::Generics<'tcx>,
free_id: ast::NodeId)
-> ParameterEnvironment<'a, 'tcx>
{
//
// Construct the free substs.
//
let free_substs = construct_free_substs(tcx, generics, free_id);
let free_id_scope = region::CodeExtent::from_node_id(free_id);
//
@ -6311,7 +6356,21 @@ pub fn construct_parameter_environment<'a,'tcx>(
free_substs.repr(tcx),
predicates.repr(tcx));
return ty::ParameterEnvironment {
//
// Finally, we have to normalize the bounds in the environment, in
// case they contain any associated type projections. This process
// can yield errors if the put in illegal associated types, like
// `<i32 as Foo>::Bar` where `i32` does not implement `Foo`. We
// report these errors right here; this doesn't actually feel
// right to me, because constructing the environment feels like a
// kind of a "idempotent" action, but I'm not sure where would be
// a better place. In practice, we construct environments for
// every fn once during type checking, and we'll abort if there
// are any errors at that point, so after type checking you can be
// sure that this will succeed without errors anyway.
//
let unnormalized_env = ty::ParameterEnvironment {
tcx: tcx,
free_substs: free_substs,
implicit_region_bound: ty::ReScope(free_id_scope),
@ -6319,25 +6378,8 @@ pub fn construct_parameter_environment<'a,'tcx>(
selection_cache: traits::SelectionCache::new(),
};
fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,
free_id: ast::NodeId,
region_params: &[RegionParameterDef])
{
for r in region_params.iter() {
regions.push(r.space, ty::free_region_from_def(free_id, r));
}
}
fn push_types_from_defs<'tcx>(tcx: &ty::ctxt<'tcx>,
types: &mut VecPerParamSpace<Ty<'tcx>>,
defs: &[TypeParameterDef<'tcx>]) {
for def in defs.iter() {
debug!("construct_parameter_environment(): push_types_from_defs: def={:?}",
def.repr(tcx));
let ty = ty::mk_param_from_def(tcx, def);
types.push(def.space, ty);
}
}
let cause = traits::ObligationCause::misc(span, free_id);
return traits::normalize_param_env_or_error(unnormalized_env, cause);
fn record_region_bounds<'tcx>(tcx: &ty::ctxt<'tcx>, predicates: &[ty::Predicate<'tcx>]) {
debug!("record_region_bounds(predicates={:?})", predicates.repr(tcx));

View File

@ -215,14 +215,8 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
debug!("compare_impl_method: impl_bounds={}",
impl_bounds.repr(tcx));
// // Normalize the associated types in the impl_bounds.
// let traits::Normalized { value: impl_bounds, .. } =
// traits::normalize(&mut selcx, normalize_cause.clone(), &impl_bounds);
// Normalize the associated types in the trait_bounds.
let trait_bounds = trait_m.generics.to_bounds(tcx, &trait_to_skol_substs);
// let traits::Normalized { value: trait_bounds, .. } =
// traits::normalize(&mut selcx, normalize_cause, &trait_bounds);
// Obtain the predicate split predicate sets for each.
let trait_pred = trait_bounds.predicates.split();
@ -242,19 +236,18 @@ pub fn compare_impl_method<'tcx>(tcx: &ty::ctxt<'tcx>,
);
// Construct trait parameter environment and then shift it into the skolemized viewpoint.
let mut trait_param_env = impl_param_env.clone();
// The key step here is to update the caller_bounds's predicates to be
// the new hybrid bounds we computed.
trait_param_env.caller_bounds = hybrid_preds.into_vec();
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_body_id);
let trait_param_env = impl_param_env.with_caller_bounds(hybrid_preds.into_vec());
let trait_param_env = traits::normalize_param_env_or_error(trait_param_env,
normalize_cause.clone());
debug!("compare_impl_method: trait_bounds={}",
trait_param_env.caller_bounds.repr(tcx));
let mut selcx = traits::SelectionContext::new(&infcx, &trait_param_env);
let normalize_cause =
traits::ObligationCause::misc(impl_m_span, impl_m_body_id);
for predicate in impl_pred.fns.into_iter() {
let traits::Normalized { value: predicate, .. } =
traits::normalize(&mut selcx, normalize_cause.clone(), &predicate);

View File

@ -467,7 +467,8 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
body: &ast::Block,
id: ast::NodeId,
raw_fty: Ty<'tcx>,
param_env: ty::ParameterEnvironment<'a, 'tcx>) {
param_env: ty::ParameterEnvironment<'a, 'tcx>)
{
match raw_fty.sty {
ty::ty_bare_fn(_, ref fn_ty) => {
let inh = Inherited::new(ccx.tcx, param_env);

View File

@ -127,6 +127,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
reject_non_type_param_bounds(ccx.tcx, item.span, &type_scheme.generics);
let param_env =
ty::construct_parameter_environment(ccx.tcx,
item.span,
&type_scheme.generics,
item.id);
let inh = Inherited::new(ccx.tcx, param_env);

View File

@ -0,0 +1,42 @@
// Copyright 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.
// Test that we normalize associated types that appear in a bound that
// contains a binding. Issue #21664.
#![allow(dead_code)]
pub trait Integral {
type Opposite;
}
impl Integral for i32 {
type Opposite = u32;
}
impl Integral for u32 {
type Opposite = i32;
}
pub trait FnLike<A> {
type R;
}
fn foo<T>()
where T : FnLike<<i32 as Integral>::Opposite, R=bool>
{
bar::<T>();
}
fn bar<T>()
where T : FnLike<u32, R=bool>
{}
fn main() { }