Implement Default TyParam fallback

This patch allows type parameter defaults to influence type inference. This is a possible breaking change since it effects the way type inference works and will have different behavior when mixing defaults and literal fallback.
This commit is contained in:
Jared Roesch 2015-07-07 15:50:02 -07:00 committed by Jared Roesch
parent 7276d8b761
commit bbcb13da88
14 changed files with 340 additions and 45 deletions

View file

@ -95,6 +95,9 @@ pub struct InferCtxt<'a, 'tcx: 'a> {
normalize: bool,
err_count_on_creation: usize,
// Default Type Parameter fallbacks
pub defaults: RefCell<FnvHashMap<Ty<'tcx>, Ty<'tcx>>>,
}
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
@ -350,7 +353,8 @@ pub fn new_infer_ctxt<'a, 'tcx>(tcx: &'a ty::ctxt<'tcx>,
parameter_environment: param_env.unwrap_or(tcx.empty_parameter_environment()),
fulfillment_cx: RefCell::new(traits::FulfillmentContext::new(errors_will_be_reported)),
normalize: false,
err_count_on_creation: tcx.sess.err_count()
err_count_on_creation: tcx.sess.err_count(),
defaults: RefCell::new(FnvHashMap()),
}
}
@ -653,6 +657,30 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}
}
pub fn unsolved_variables(&self) -> Vec<ty::Ty<'tcx>> {
let mut variables = Vec::new();
let unbound_ty_vars = self.type_variables
.borrow()
.unsolved_variables()
.into_iter().map(|t| self.tcx.mk_var(t));
let unbound_int_vars = self.int_unification_table
.borrow_mut()
.unsolved_variables()
.into_iter().map(|v| self.tcx.mk_int_var(v));
let unbound_float_vars = self.float_unification_table
.borrow_mut()
.unsolved_variables()
.into_iter().map(|v| self.tcx.mk_float_var(v));
variables.extend(unbound_ty_vars);
variables.extend(unbound_int_vars);
variables.extend(unbound_float_vars);
return variables;
}
fn combine_fields(&'a self, a_is_expected: bool, trace: TypeTrace<'tcx>)
-> CombineFields<'a, 'tcx> {
CombineFields {infcx: self,
@ -996,6 +1024,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
.collect()
}
pub fn type_vars_for_defs(&self,
defs: &[ty::TypeParameterDef<'tcx>])
-> Vec<ty::Ty<'tcx>> {
let mut vars = Vec::with_capacity(defs.len());
for def in defs.iter() {
let ty_var = self.next_ty_var();
match def.default {
None => {},
Some(default) => { self.defaults.borrow_mut().insert(ty_var, default); }
}
vars.push(ty_var)
}
vars
}
/// Given a set of generics defined on a type or impl, returns a substitution mapping each
/// type/region parameter to a fresh inference variable.
pub fn fresh_substs_for_generics(&self,
@ -1003,9 +1048,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
generics: &ty::Generics<'tcx>)
-> subst::Substs<'tcx>
{
let type_params =
generics.types.map(
|_| self.next_ty_var());
let mut type_params = subst::VecPerParamSpace::empty();
for space in subst::ParamSpace::all().iter() {
type_params.replace(*space, self.type_vars_for_defs(generics.types.get_slice(*space)))
}
let region_params =
generics.regions.map(
|d| self.next_region_var(EarlyBoundRegion(span, d.name)));
@ -1027,8 +1075,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
assert!(generics.regions.len(subst::SelfSpace) == 0);
assert!(generics.regions.len(subst::FnSpace) == 0);
let type_parameter_count = generics.types.len(subst::TypeSpace);
let type_parameters = self.next_ty_vars(type_parameter_count);
let type_parameter_defs = generics.types.get_slice(subst::TypeSpace);
let type_parameters = self.type_vars_for_defs(type_parameter_defs);
let region_param_defs = generics.regions.get_slice(subst::TypeSpace);
let regions = self.region_vars_for_defs(span, region_param_defs);
@ -1268,6 +1316,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.report_and_explain_type_error(trace, err);
}
pub fn report_conflicting_default_types(&self,
span: Span,
expected: Ty<'tcx>,
actual: Ty<'tcx>) {
let trace = TypeTrace {
origin: Misc(span),
values: Types(ty::expected_found {
expected: expected,
found: actual
})
};
self.report_and_explain_type_error(trace, &ty::type_err::terr_ty_param_default_mismatch(ty::expected_found {
expected: expected,
found: actual
}));
}
pub fn replace_late_bound_regions_with_fresh_var<T>(
&self,
span: Span,

View file

@ -195,6 +195,15 @@ impl<'tcx> TypeVariableTable<'tcx> {
escaping_types
}
pub fn unsolved_variables(&self) -> Vec<ty::TyVid> {
self.values.iter().enumerate().filter_map(|(i, value)|
match &value.value {
&TypeVariableValue::Known(_) => None,
&TypeVariableValue::Bounded(_) => Some(ty::TyVid { index: i as u32 })
}
).collect()
}
}
impl<'tcx> sv::SnapshotVecDelegate for Delegate<'tcx> {

View file

@ -2068,6 +2068,7 @@ pub enum TypeError<'tcx> {
ConvergenceMismatch(ExpectedFound<bool>),
ProjectionNameMismatched(ExpectedFound<ast::Name>),
ProjectionBoundsLength(ExpectedFound<usize>),
terr_ty_param_default_mismatch(expected_found<Ty<'tcx>>)
}
/// Bounds suitable for an existentially quantified type parameter
@ -5080,6 +5081,11 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
write!(f, "expected {} associated type bindings, found {}",
values.expected,
values.found)
},
terr_ty_param_default_mismatch(ref values) => {
write!(f, "conflicting type parameter defaults {} {}",
values.expected,
values.found)
}
}
}
@ -5437,6 +5443,11 @@ impl<'tcx> ctxt<'tcx> {
&format!("consider boxing your closure and/or \
using it as a trait object"));
}
},
terr_ty_param_default_mismatch(expected) => {
self.sess.span_note(sp,
&format!("found conflicting defaults {:?} {:?}",
expected.expected, expected.found))
}
_ => {}
}

View file

@ -339,5 +339,11 @@ impl<'tcx,K,V> UnificationTable<K>
pub fn probe(&mut self, a_id: K) -> Option<V> {
self.get(a_id).value.clone()
}
}
pub fn unsolved_variables(&mut self) -> Vec<K> {
self.values
.iter()
.filter_map(|vv| if vv.value.is_some() { None } else { Some(vv.key()) })
.collect()
}
}

View file

@ -309,15 +309,17 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
// If they were not explicitly supplied, just construct fresh
// variables.
let num_supplied_types = supplied_method_types.len();
let num_method_types = pick.item.as_opt_method().unwrap()
.generics.types.len(subst::FnSpace);
let method = pick.item.as_opt_method().unwrap();
let method_types = method.generics.types.get_slice(subst::FnSpace);
let num_method_types = method_types.len();
let method_types = {
if num_supplied_types == 0 {
self.fcx.infcx().next_ty_vars(num_method_types)
self.fcx.infcx().type_vars_for_defs(method_types)
} else if num_method_types == 0 {
span_err!(self.tcx().sess, self.span, E0035,
"does not take type parameters");
self.fcx.infcx().next_ty_vars(num_method_types)
self.fcx.infcx().type_vars_for_defs(method_types)
} else if num_supplied_types != num_method_types {
span_err!(self.tcx().sess, self.span, E0036,
"incorrect number of type parameters given for this method");

View file

@ -167,7 +167,8 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let trait_def = fcx.tcx().lookup_trait_def(trait_def_id);
let expected_number_of_input_types = trait_def.generics.types.len(subst::TypeSpace);
let type_parameter_defs = trait_def.generics.types.get_slice(subst::TypeSpace);
let expected_number_of_input_types = type_parameter_defs.len();
let input_types = match opt_input_types {
Some(input_types) => {
assert_eq!(expected_number_of_input_types, input_types.len());
@ -175,7 +176,7 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
None => {
fcx.inh.infcx.next_ty_vars(expected_number_of_input_types)
fcx.inh.infcx.type_vars_for_defs(type_parameter_defs)
}
};

View file

@ -1207,8 +1207,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
!method.generics.regions.is_empty_in(subst::FnSpace)
{
let method_types =
self.infcx().next_ty_vars(
method.generics.types.len(subst::FnSpace));
self.infcx().type_vars_for_defs(
method.generics.types.get_slice(subst::FnSpace));
// In general, during probe we erase regions. See
// `impl_self_ty()` for an explanation.

View file

@ -108,6 +108,7 @@ use util::nodemap::{DefIdMap, FnvHashMap, NodeMap};
use util::lev_distance::lev_distance;
use std::cell::{Cell, Ref, RefCell};
use std::collections::HashSet;
use std::mem::replace;
use std::slice;
use syntax::{self, abi, attr};
@ -1255,28 +1256,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// Apply "fallbacks" to some types
/// ! gets replaced with (), unconstrained ints with i32, and unconstrained floats with f64.
pub fn default_type_parameters(&self) {
use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
for (_, &mut ref ty) in &mut self.inh.tables.borrow_mut().node_types {
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
if self.infcx().type_var_diverges(resolved) {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
} else {
match self.infcx().type_is_unconstrained_numeric(resolved) {
UnconstrainedInt => {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
},
UnconstrainedFloat => {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
}
Neither => { }
}
}
}
}
#[inline]
pub fn write_ty(&self, node_id: ast::NodeId, ty: Ty<'tcx>) {
debug!("write_ty({}, {:?}) in fcx {}",
@ -1711,11 +1690,98 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
fn select_all_obligations_and_apply_defaults(&self) {
debug!("select_all_obligations_and_apply_defaults");
use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
self.select_obligations_where_possible();
self.default_type_parameters();
self.select_obligations_where_possible();
debug!("select_all_obligations_and_apply_defaults: defaults={:?}", self.infcx().defaults);
for _ in (0..self.tcx().sess.recursion_limit.get()) {
self.select_obligations_where_possible();
let unsolved_variables = self.inh.infcx.unsolved_variables();
let mut unbound_tyvars = HashSet::new();
// Gather all unconstrainted integer and float variables
for ty in &unsolved_variables {
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
if self.infcx().type_var_diverges(resolved) {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
} else {
match self.infcx().type_is_unconstrained_numeric(resolved) {
UnconstrainedInt => {
unbound_tyvars.insert(resolved);
},
UnconstrainedFloat => {
unbound_tyvars.insert(resolved);
}
Neither => {}
}
}
}
// Collect the set of variables that need fallback applied
for ty in &unsolved_variables {
if self.inh.infcx.defaults.borrow().contains_key(ty) {
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}",
ty, self.inh.infcx.defaults.borrow().get(ty));
match resolved.sty {
ty::TyInfer(ty::TyVar(_)) => {
unbound_tyvars.insert(ty);
}
ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) => {
unbound_tyvars.insert(ty);
if unbound_tyvars.contains(resolved) {
unbound_tyvars.remove(resolved);
}
}
_ => {}
}
}
}
if unbound_tyvars.is_empty() {
break;
}
// Go through the unbound variables and unify them with the proper fallbacks
for ty in &unbound_tyvars {
// let resolved = self.infcx().resolve_type_vars_if_possible(ty);
if self.infcx().type_var_diverges(ty) {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil());
} else {
match self.infcx().type_is_unconstrained_numeric(ty) {
UnconstrainedInt => {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
},
UnconstrainedFloat => {
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
}
Neither => {
let default_map = self.inh.infcx.defaults.borrow();
if let Some(default) = default_map.get(ty) {
match infer::mk_eqty(self.infcx(), false,
infer::Misc(codemap::DUMMY_SP),
ty, default) {
Ok(()) => { /* ok */ }
Err(_) => {
self.infcx().report_conflicting_default_types(
codemap::DUMMY_SP,
ty,
default)
}
}
}
}
}
}
}
self.select_obligations_where_possible();
}
}
fn select_all_obligations_or_error(&self) {
@ -2421,13 +2487,15 @@ pub fn impl_self_ty<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let tcx = fcx.tcx();
let ity = tcx.lookup_item_type(did);
let (n_tps, rps, raw_ty) =
(ity.generics.types.len(subst::TypeSpace),
let (tps, rps, raw_ty) =
(ity.generics.types.get_slice(subst::TypeSpace),
ity.generics.regions.get_slice(subst::TypeSpace),
ity.ty);
debug!("impl_self_ty: tps={:?} rps={:?} raw_ty={:?}", tps, rps, raw_ty);
let rps = fcx.inh.infcx.region_vars_for_defs(span, rps);
let tps = fcx.inh.infcx.next_ty_vars(n_tps);
let tps = fcx.inh.infcx.type_vars_for_defs(tps);
let substs = subst::Substs::new_type(tps, rps);
let substd_ty = fcx.instantiate_type_scheme(span, &substs, &raw_ty);
@ -4647,7 +4715,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
// Nothing specified at all: supply inference variables for
// everything.
if provided_len == 0 && !(require_type_space && space == subst::TypeSpace) {
substs.types.replace(space, fcx.infcx().next_ty_vars(desired.len()));
substs.types.replace(space, fcx.infcx().type_vars_for_defs(&desired[..]));
return;
}

View file

@ -0,0 +1,23 @@
// 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.
use std::fmt::Debug;
// Example from the RFC
fn foo<F:Default=usize>() -> F { F::default() }
fn bar<B:Debug=isize>(b: B) { println!("{:?}", b); }
fn main() {
// Here, F is instantiated with $0=uint
let x = foo();
// Here, B is instantiated with $1=uint, and constraint $0 <: $1 is added.
bar(x);
}

View file

@ -0,0 +1,17 @@
// 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.
use std::collections::HashMap;
type IntMap<K=usize> = HashMap<K, usize>;
fn main() {
let x = IntMap::new();
}

View file

@ -0,0 +1,22 @@
// 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.
struct Foo;
impl Foo {
fn method<A:Default=String>(&self) -> A {
A::default()
}
}
fn main() {
let f = Foo.method();
println!("{}", f);
}

View file

@ -0,0 +1,21 @@
// 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.
struct Foo<A>(A);
impl<A:Default=i32> Foo<A> {
fn new() -> Foo<A> {
Foo(A::default())
}
}
fn main() {
let foo = Foo::new();
}

View file

@ -0,0 +1,23 @@
// 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.
// Another example from the RFC
trait Foo { }
trait Bar { }
impl<T:Bar=usize> Foo for Vec<T> {} // Impl 1
impl Bar for usize { } // Impl 2
fn takes_foo<F:Foo>(f: F) { }
fn main() {
let x = Vec::new(); // x: Vec<$0>
takes_foo(x); // adds oblig Vec<$0> : Foo
}

View file

@ -0,0 +1,26 @@
// 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.
// An example from the RFC
trait Foo { fn takes_foo(&self); }
trait Bar { }
impl<T:Bar=usize> Foo for Vec<T> {
fn takes_foo(&self) {}
} // Impl 1
impl Bar for usize { } // Impl 2
// fn takes_foo<F:Foo>(f: F) { }
fn main() {
let x = Vec::new(); // x: Vec<$0>
x.takes_foo(); // adds oblig Vec<$0> : Foo
}