Auto merge of #26870 - jroesch:default-typaram-fallback, r=nikomatsakis
This PR completes [RFC 213](https://github.com/rust-lang/rfcs/blob/master/text/0213-defaulted-type-params.md) by allowing default type parameters to influence inference. This is almost certainly a breaking change due to interactions between default type parameters and the old fallback algorithm used for integral and floating point literals. The error messages still require polish but I wanted to get early review and feedback from others on the the changes, error messages, and test cases. I also imagine we will want to run anywhere from 1-3 versions of this on crater and evaluate the impact, and it would be best to get that ball rolling. The only outstanding issue I'm aware of is that type alias defaults don't work. It seems this may require significant restructuring, since during inference type aliases have already been expanded. @nikomatsakis might be able to provide some clarity here. r? @nikomatsakis cc @eddyb @Gankro @aturon @brson
This commit is contained in:
commit
a5c12f4e39
@ -2368,6 +2368,8 @@ The currently implemented features of the reference compiler are:
|
||||
internally without imposing on callers
|
||||
(i.e. making them behave like function calls in
|
||||
terms of encapsulation).
|
||||
* - `default_type_parameter_fallback` - Allows type parameter defaults to
|
||||
influence type inference.
|
||||
|
||||
If a feature is promoted to a language feature, then all existing programs will
|
||||
start to receive compilation warnings about `#![feature]` directives which enabled
|
||||
|
@ -119,6 +119,7 @@ pub enum Node<'ast> {
|
||||
NodeStructCtor(&'ast StructDef),
|
||||
|
||||
NodeLifetime(&'ast Lifetime),
|
||||
NodeTyParam(&'ast TyParam)
|
||||
}
|
||||
|
||||
/// Represents an entry and its parent NodeID.
|
||||
@ -142,6 +143,7 @@ enum MapEntry<'ast> {
|
||||
EntryBlock(NodeId, &'ast Block),
|
||||
EntryStructCtor(NodeId, &'ast StructDef),
|
||||
EntryLifetime(NodeId, &'ast Lifetime),
|
||||
EntryTyParam(NodeId, &'ast TyParam),
|
||||
|
||||
/// Roots for node trees.
|
||||
RootCrate,
|
||||
@ -175,7 +177,8 @@ impl<'ast> MapEntry<'ast> {
|
||||
NodePat(n) => EntryPat(p, n),
|
||||
NodeBlock(n) => EntryBlock(p, n),
|
||||
NodeStructCtor(n) => EntryStructCtor(p, n),
|
||||
NodeLifetime(n) => EntryLifetime(p, n)
|
||||
NodeLifetime(n) => EntryLifetime(p, n),
|
||||
NodeTyParam(n) => EntryTyParam(p, n),
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +197,7 @@ impl<'ast> MapEntry<'ast> {
|
||||
EntryBlock(id, _) => id,
|
||||
EntryStructCtor(id, _) => id,
|
||||
EntryLifetime(id, _) => id,
|
||||
EntryTyParam(id, _) => id,
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
@ -213,6 +217,7 @@ impl<'ast> MapEntry<'ast> {
|
||||
EntryBlock(_, n) => NodeBlock(n),
|
||||
EntryStructCtor(_, n) => NodeStructCtor(n),
|
||||
EntryLifetime(_, n) => NodeLifetime(n),
|
||||
EntryTyParam(_, n) => NodeTyParam(n),
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
@ -573,6 +578,7 @@ impl<'ast> Map<'ast> {
|
||||
Some(NodePat(pat)) => pat.span,
|
||||
Some(NodeBlock(block)) => block.span,
|
||||
Some(NodeStructCtor(_)) => self.expect_item(self.get_parent(id)).span,
|
||||
Some(NodeTyParam(ty_param)) => ty_param.span,
|
||||
_ => return None,
|
||||
};
|
||||
Some(sp)
|
||||
@ -815,6 +821,14 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
|
||||
self.parent_node = parent_node;
|
||||
}
|
||||
|
||||
fn visit_generics(&mut self, generics: &'ast Generics) {
|
||||
for ty_param in generics.ty_params.iter() {
|
||||
self.insert(ty_param.id, NodeTyParam(ty_param));
|
||||
}
|
||||
|
||||
visit::walk_generics(self, generics);
|
||||
}
|
||||
|
||||
fn visit_trait_item(&mut self, ti: &'ast TraitItem) {
|
||||
let parent_node = self.parent_node;
|
||||
self.parent_node = ti.id;
|
||||
@ -1015,7 +1029,7 @@ impl<'a> NodePrinter for pprust::State<'a> {
|
||||
NodePat(a) => self.print_pat(&*a),
|
||||
NodeBlock(a) => self.print_block(&*a),
|
||||
NodeLifetime(a) => self.print_lifetime(&*a),
|
||||
|
||||
NodeTyParam(_) => panic!("cannot print TyParam"),
|
||||
// these cases do not carry enough information in the
|
||||
// ast_map to reconstruct their full structure for pretty
|
||||
// printing.
|
||||
@ -1123,6 +1137,9 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String {
|
||||
format!("lifetime {}{}",
|
||||
pprust::lifetime_to_string(&**l), id_str)
|
||||
}
|
||||
Some(NodeTyParam(ref ty_param)) => {
|
||||
format!("typaram {:?}{}", ty_param, id_str)
|
||||
}
|
||||
None => {
|
||||
format!("unknown node{}", id_str)
|
||||
}
|
||||
|
@ -56,6 +56,7 @@
|
||||
#![feature(slice_splits)]
|
||||
#![feature(slice_patterns)]
|
||||
#![feature(slice_position_elem)]
|
||||
#![feature(slice_concat_ext)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(str_char)]
|
||||
#![feature(str_match_indices)]
|
||||
|
@ -833,6 +833,7 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F)
|
||||
assert_eq!(next(st), '|');
|
||||
let index = parse_u32(st);
|
||||
assert_eq!(next(st), '|');
|
||||
let default_def_id = parse_def_(st, NominalType, conv);
|
||||
let default = parse_opt(st, |st| parse_ty_(st, conv));
|
||||
let object_lifetime_default = parse_object_lifetime_default(st, conv);
|
||||
|
||||
@ -841,6 +842,7 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F)
|
||||
def_id: def_id,
|
||||
space: space,
|
||||
index: index,
|
||||
default_def_id: default_def_id,
|
||||
default: default,
|
||||
object_lifetime_default: object_lifetime_default,
|
||||
}
|
||||
|
@ -409,9 +409,9 @@ pub fn enc_region_bounds<'a, 'tcx>(w: &mut Encoder,
|
||||
|
||||
pub fn enc_type_param_def<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>,
|
||||
v: &ty::TypeParameterDef<'tcx>) {
|
||||
mywrite!(w, "{}:{}|{}|{}|",
|
||||
mywrite!(w, "{}:{}|{}|{}|{}|",
|
||||
token::get_name(v.name), (cx.ds)(v.def_id),
|
||||
v.space.to_uint(), v.index);
|
||||
v.space.to_uint(), v.index, (cx.ds)(v.default_def_id));
|
||||
enc_opt(w, v.default, |w, t| enc_ty(w, cx, t));
|
||||
enc_object_lifetime_default(w, cx, v.object_lifetime_default);
|
||||
}
|
||||
|
@ -893,8 +893,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
self.report_inference_failure(vo.clone());
|
||||
}
|
||||
self.give_suggestion(same_regions);
|
||||
for &(ref trace, terr) in trace_origins {
|
||||
self.report_and_explain_type_error(trace.clone(), &terr);
|
||||
for &(ref trace, ref terr) in trace_origins {
|
||||
self.report_and_explain_type_error(trace.clone(), terr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -653,6 +653,50 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a type variable's default fallback if any exists. A default
|
||||
/// must be attached to the variable when created, if it is created
|
||||
/// without a default, this will return None.
|
||||
///
|
||||
/// This code does not apply to integral or floating point variables,
|
||||
/// only to use declared defaults.
|
||||
///
|
||||
/// See `new_ty_var_with_default` to create a type variable with a default.
|
||||
/// See `type_variable::Default` for details about what a default entails.
|
||||
pub fn default(&self, ty: Ty<'tcx>) -> Option<type_variable::Default<'tcx>> {
|
||||
match ty.sty {
|
||||
ty::TyInfer(ty::TyVar(vid)) => self.type_variables.borrow().default(vid),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -956,13 +1000,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
pub fn next_ty_var_id(&self, diverging: bool) -> TyVid {
|
||||
self.type_variables
|
||||
.borrow_mut()
|
||||
.new_var(diverging)
|
||||
.new_var(diverging, None)
|
||||
}
|
||||
|
||||
pub fn next_ty_var(&self) -> Ty<'tcx> {
|
||||
self.tcx.mk_var(self.next_ty_var_id(false))
|
||||
}
|
||||
|
||||
pub fn next_ty_var_with_default(&self,
|
||||
default: Option<type_variable::Default<'tcx>>) -> Ty<'tcx> {
|
||||
let ty_var_id = self.type_variables
|
||||
.borrow_mut()
|
||||
.new_var(false, default);
|
||||
|
||||
self.tcx.mk_var(ty_var_id)
|
||||
}
|
||||
|
||||
pub fn next_diverging_ty_var(&self) -> Ty<'tcx> {
|
||||
self.tcx.mk_var(self.next_ty_var_id(true))
|
||||
}
|
||||
@ -996,6 +1049,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
// We have to take `&mut Substs` in order to provide the correct substitutions for defaults
|
||||
// along the way, for this reason we don't return them.
|
||||
pub fn type_vars_for_defs(&self,
|
||||
span: Span,
|
||||
space: subst::ParamSpace,
|
||||
substs: &mut Substs<'tcx>,
|
||||
defs: &[ty::TypeParameterDef<'tcx>]) {
|
||||
|
||||
let mut vars = Vec::with_capacity(defs.len());
|
||||
|
||||
for def in defs.iter() {
|
||||
let default = def.default.map(|default| {
|
||||
type_variable::Default {
|
||||
ty: default.subst_spanned(self.tcx, substs, Some(span)),
|
||||
origin_span: span,
|
||||
def_id: def.default_def_id
|
||||
}
|
||||
});
|
||||
|
||||
let ty_var = self.next_ty_var_with_default(default);
|
||||
substs.types.push(space, ty_var);
|
||||
vars.push(ty_var)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,13 +1081,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
generics: &ty::Generics<'tcx>)
|
||||
-> subst::Substs<'tcx>
|
||||
{
|
||||
let type_params =
|
||||
generics.types.map(
|
||||
|_| self.next_ty_var());
|
||||
let type_params = subst::VecPerParamSpace::empty();
|
||||
|
||||
let region_params =
|
||||
generics.regions.map(
|
||||
|d| self.next_region_var(EarlyBoundRegion(span, d.name)));
|
||||
subst::Substs::new(type_params, region_params)
|
||||
|
||||
let mut substs = subst::Substs::new(type_params, region_params);
|
||||
|
||||
for space in subst::ParamSpace::all().iter() {
|
||||
self.type_vars_for_defs(
|
||||
span,
|
||||
*space,
|
||||
&mut substs,
|
||||
generics.types.get_slice(*space));
|
||||
}
|
||||
|
||||
return substs;
|
||||
}
|
||||
|
||||
/// Given a set of generics defined on a trait, returns a substitution mapping each output
|
||||
@ -1027,13 +1115,17 @@ 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_params = Vec::new();
|
||||
|
||||
let region_param_defs = generics.regions.get_slice(subst::TypeSpace);
|
||||
let regions = self.region_vars_for_defs(span, region_param_defs);
|
||||
|
||||
subst::Substs::new_trait(type_parameters, regions, self_ty)
|
||||
let mut substs = subst::Substs::new_trait(type_params, regions, self_ty);
|
||||
|
||||
let type_parameter_defs = generics.types.get_slice(subst::TypeSpace);
|
||||
self.type_vars_for_defs(span, subst::TypeSpace, &mut substs, type_parameter_defs);
|
||||
|
||||
return substs;
|
||||
}
|
||||
|
||||
pub fn fresh_bound_region(&self, debruijn: ty::DebruijnIndex) -> ty::Region {
|
||||
@ -1268,6 +1360,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
self.report_and_explain_type_error(trace, err);
|
||||
}
|
||||
|
||||
pub fn report_conflicting_default_types(&self,
|
||||
span: Span,
|
||||
expected: type_variable::Default<'tcx>,
|
||||
actual: type_variable::Default<'tcx>) {
|
||||
let trace = TypeTrace {
|
||||
origin: Misc(span),
|
||||
values: Types(ty::ExpectedFound {
|
||||
expected: expected.ty,
|
||||
found: actual.ty
|
||||
})
|
||||
};
|
||||
|
||||
self.report_and_explain_type_error(trace,
|
||||
&TypeError::TyParamDefaultMismatch(ty::ExpectedFound {
|
||||
expected: expected,
|
||||
found: actual
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn replace_late_bound_regions_with_fresh_var<T>(
|
||||
&self,
|
||||
span: Span,
|
||||
|
@ -11,8 +11,10 @@
|
||||
pub use self::RelationDir::*;
|
||||
use self::TypeVariableValue::*;
|
||||
use self::UndoEntry::*;
|
||||
|
||||
use middle::ty::{self, Ty};
|
||||
use syntax::ast::DefId;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use std::cmp::min;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
@ -30,16 +32,30 @@ struct TypeVariableData<'tcx> {
|
||||
|
||||
enum TypeVariableValue<'tcx> {
|
||||
Known(Ty<'tcx>),
|
||||
Bounded(Vec<Relation>),
|
||||
Bounded {
|
||||
relations: Vec<Relation>,
|
||||
default: Option<Default<'tcx>>
|
||||
}
|
||||
}
|
||||
|
||||
// We will use this to store the required information to recapitulate what happened when
|
||||
// an error occurs.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Default<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
/// The span where the default was incurred
|
||||
pub origin_span: Span,
|
||||
/// The definition that the default originates from
|
||||
pub def_id: DefId
|
||||
}
|
||||
|
||||
pub struct Snapshot {
|
||||
snapshot: sv::Snapshot
|
||||
}
|
||||
|
||||
enum UndoEntry {
|
||||
enum UndoEntry<'tcx> {
|
||||
// The type of the var was specified.
|
||||
SpecifyVar(ty::TyVid, Vec<Relation>),
|
||||
SpecifyVar(ty::TyVid, Vec<Relation>, Option<Default<'tcx>>),
|
||||
Relate(ty::TyVid, ty::TyVid),
|
||||
}
|
||||
|
||||
@ -72,6 +88,13 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
||||
relations(self.values.get_mut(a.index as usize))
|
||||
}
|
||||
|
||||
pub fn default(&self, vid: ty::TyVid) -> Option<Default<'tcx>> {
|
||||
match &self.values.get(vid.index as usize).value {
|
||||
&Known(_) => None,
|
||||
&Bounded { ref default, .. } => default.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool {
|
||||
self.values.get(vid.index as usize).diverging
|
||||
}
|
||||
@ -101,8 +124,8 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
||||
mem::replace(value_ptr, Known(ty))
|
||||
};
|
||||
|
||||
let relations = match old_value {
|
||||
Bounded(b) => b,
|
||||
let (relations, default) = match old_value {
|
||||
Bounded { relations, default } => (relations, default),
|
||||
Known(_) => panic!("Asked to instantiate variable that is \
|
||||
already instantiated")
|
||||
};
|
||||
@ -111,12 +134,14 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
||||
stack.push((ty, dir, vid));
|
||||
}
|
||||
|
||||
self.values.record(SpecifyVar(vid, relations));
|
||||
self.values.record(SpecifyVar(vid, relations, default));
|
||||
}
|
||||
|
||||
pub fn new_var(&mut self, diverging: bool) -> ty::TyVid {
|
||||
pub fn new_var(&mut self,
|
||||
diverging: bool,
|
||||
default: Option<Default<'tcx>>) -> ty::TyVid {
|
||||
let index = self.values.push(TypeVariableData {
|
||||
value: Bounded(vec![]),
|
||||
value: Bounded { relations: vec![], default: default },
|
||||
diverging: diverging
|
||||
});
|
||||
ty::TyVid { index: index as u32 }
|
||||
@ -124,7 +149,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
||||
|
||||
pub fn probe(&self, vid: ty::TyVid) -> Option<Ty<'tcx>> {
|
||||
match self.values.get(vid.index as usize).value {
|
||||
Bounded(..) => None,
|
||||
Bounded { .. } => None,
|
||||
Known(t) => Some(t)
|
||||
}
|
||||
}
|
||||
@ -179,7 +204,7 @@ impl<'tcx> TypeVariableTable<'tcx> {
|
||||
debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold);
|
||||
}
|
||||
|
||||
sv::UndoLog::Other(SpecifyVar(vid, _)) => {
|
||||
sv::UndoLog::Other(SpecifyVar(vid, _, _)) => {
|
||||
if vid.index < new_elem_threshold {
|
||||
// quick check to see if this variable was
|
||||
// created since the snapshot started or not.
|
||||
@ -195,16 +220,30 @@ 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> {
|
||||
type Value = TypeVariableData<'tcx>;
|
||||
type Undo = UndoEntry;
|
||||
type Undo = UndoEntry<'tcx>;
|
||||
|
||||
fn reverse(values: &mut Vec<TypeVariableData<'tcx>>, action: UndoEntry) {
|
||||
fn reverse(values: &mut Vec<TypeVariableData<'tcx>>, action: UndoEntry<'tcx>) {
|
||||
match action {
|
||||
SpecifyVar(vid, relations) => {
|
||||
values[vid.index as usize].value = Bounded(relations);
|
||||
SpecifyVar(vid, relations, default) => {
|
||||
values[vid.index as usize].value = Bounded {
|
||||
relations: relations,
|
||||
default: default
|
||||
};
|
||||
}
|
||||
|
||||
Relate(a, b) => {
|
||||
@ -218,6 +257,6 @@ impl<'tcx> sv::SnapshotVecDelegate for Delegate<'tcx> {
|
||||
fn relations<'a>(v: &'a mut TypeVariableData) -> &'a mut Vec<Relation> {
|
||||
match v.value {
|
||||
Known(_) => panic!("var_sub_var: variable is known"),
|
||||
Bounded(ref mut relations) => relations
|
||||
Bounded { ref mut relations, .. } => relations
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ impl<'tcx> Substs<'tcx> {
|
||||
}
|
||||
|
||||
impl RegionSubsts {
|
||||
fn map<F>(self, op: F) -> RegionSubsts where
|
||||
pub fn map<F>(self, op: F) -> RegionSubsts where
|
||||
F: FnOnce(VecPerParamSpace<ty::Region>) -> VecPerParamSpace<ty::Region>,
|
||||
{
|
||||
match self {
|
||||
|
@ -54,6 +54,7 @@ use middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangIte
|
||||
use middle::region;
|
||||
use middle::resolve_lifetime;
|
||||
use middle::infer;
|
||||
use middle::infer::type_variable;
|
||||
use middle::pat_util;
|
||||
use middle::region::RegionMaps;
|
||||
use middle::stability;
|
||||
@ -78,6 +79,7 @@ use std::ops;
|
||||
use std::rc::Rc;
|
||||
use std::vec::IntoIter;
|
||||
use collections::enum_set::{self, EnumSet, CLike};
|
||||
use collections::slice::SliceConcatExt;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use syntax::abi;
|
||||
use syntax::ast::{CrateNum, DefId, ItemImpl, ItemTrait, LOCAL_CRATE};
|
||||
@ -114,8 +116,6 @@ pub struct Field<'tcx> {
|
||||
pub mt: TypeAndMut<'tcx>
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Enum information
|
||||
#[derive(Clone)]
|
||||
pub struct VariantInfo<'tcx> {
|
||||
@ -2038,7 +2038,7 @@ pub struct ExpectedFound<T> {
|
||||
}
|
||||
|
||||
// Data structures used in type unification
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum TypeError<'tcx> {
|
||||
Mismatch,
|
||||
UnsafetyMismatch(ExpectedFound<ast::Unsafety>),
|
||||
@ -2068,6 +2068,7 @@ pub enum TypeError<'tcx> {
|
||||
ConvergenceMismatch(ExpectedFound<bool>),
|
||||
ProjectionNameMismatched(ExpectedFound<ast::Name>),
|
||||
ProjectionBoundsLength(ExpectedFound<usize>),
|
||||
TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>)
|
||||
}
|
||||
|
||||
/// Bounds suitable for an existentially quantified type parameter
|
||||
@ -2280,6 +2281,7 @@ pub struct TypeParameterDef<'tcx> {
|
||||
pub def_id: ast::DefId,
|
||||
pub space: subst::ParamSpace,
|
||||
pub index: u32,
|
||||
pub default_def_id: DefId, // for use in error reporing about defaults
|
||||
pub default: Option<Ty<'tcx>>,
|
||||
pub object_lifetime_default: ObjectLifetimeDefault,
|
||||
}
|
||||
@ -5080,6 +5082,11 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
|
||||
write!(f, "expected {} associated type bindings, found {}",
|
||||
values.expected,
|
||||
values.found)
|
||||
},
|
||||
TyParamDefaultMismatch(ref values) => {
|
||||
write!(f, "conflicting type parameter defaults `{}` and `{}`",
|
||||
values.expected.ty,
|
||||
values.found.ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5399,7 +5406,7 @@ impl<'tcx> ctxt<'tcx> {
|
||||
pub fn note_and_explain_type_err(&self, err: &TypeError<'tcx>, sp: Span) {
|
||||
use self::TypeError::*;
|
||||
|
||||
match *err {
|
||||
match err.clone() {
|
||||
RegionsDoesNotOutlive(subregion, superregion) => {
|
||||
self.note_and_explain_region("", subregion, "...");
|
||||
self.note_and_explain_region("...does not necessarily outlive ",
|
||||
@ -5437,6 +5444,56 @@ impl<'tcx> ctxt<'tcx> {
|
||||
&format!("consider boxing your closure and/or \
|
||||
using it as a trait object"));
|
||||
}
|
||||
},
|
||||
TyParamDefaultMismatch(values) => {
|
||||
let expected = values.expected;
|
||||
let found = values.found;
|
||||
self.sess.span_note(sp,
|
||||
&format!("conflicting type parameter defaults `{}` and `{}`",
|
||||
expected.ty,
|
||||
found.ty));
|
||||
|
||||
match (expected.def_id.krate == ast::LOCAL_CRATE,
|
||||
self.map.opt_span(expected.def_id.node)) {
|
||||
(true, Some(span)) => {
|
||||
self.sess.span_note(span,
|
||||
&format!("a default was defined here..."));
|
||||
}
|
||||
(_, _) => {
|
||||
let elems = csearch::get_item_path(self, expected.def_id)
|
||||
.into_iter()
|
||||
.map(|p| p.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
self.sess.note(
|
||||
&format!("a default is defined on `{}`",
|
||||
elems.join("::")));
|
||||
}
|
||||
}
|
||||
|
||||
self.sess.span_note(
|
||||
expected.origin_span,
|
||||
&format!("...that was applied to an unconstrained type variable here"));
|
||||
|
||||
match (found.def_id.krate == ast::LOCAL_CRATE,
|
||||
self.map.opt_span(found.def_id.node)) {
|
||||
(true, Some(span)) => {
|
||||
self.sess.span_note(span,
|
||||
&format!("a second default was defined here..."));
|
||||
}
|
||||
(_, _) => {
|
||||
let elems = csearch::get_item_path(self, found.def_id)
|
||||
.into_iter()
|
||||
.map(|p| p.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.sess.note(
|
||||
&format!("a second default is defined on `{}`", elems.join(" ")));
|
||||
}
|
||||
}
|
||||
|
||||
self.sess.span_note(
|
||||
found.origin_span,
|
||||
&format!("...that also applies to the same type variable here"));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -340,6 +340,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> {
|
||||
space: self.space,
|
||||
index: self.index,
|
||||
default: self.default.fold_with(folder),
|
||||
default_def_id: self.default_def_id,
|
||||
object_lifetime_default: self.object_lifetime_default.fold_with(folder),
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +266,7 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
// Ugh -- but this ensures any new variants won't be forgotten
|
||||
ast_map::NodeForeignItem(..) |
|
||||
ast_map::NodeLifetime(..) |
|
||||
ast_map::NodeTyParam(..) |
|
||||
ast_map::NodeExpr(..) |
|
||||
ast_map::NodeStmt(..) |
|
||||
ast_map::NodeArg(..) |
|
||||
|
@ -55,7 +55,7 @@ use middle::def;
|
||||
use middle::implicator::object_region_bounds;
|
||||
use middle::resolve_lifetime as rl;
|
||||
use middle::privacy::{AllPublic, LastMod};
|
||||
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
|
||||
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace};
|
||||
use middle::traits;
|
||||
use middle::ty::{self, RegionEscape, Ty, ToPredicate, HasTypeFlags};
|
||||
use middle::ty_fold;
|
||||
@ -111,7 +111,11 @@ pub trait AstConv<'tcx> {
|
||||
}
|
||||
|
||||
/// What type should we use when a type is omitted?
|
||||
fn ty_infer(&self, span: Span) -> Ty<'tcx>;
|
||||
fn ty_infer(&self,
|
||||
param_and_substs: Option<ty::TypeParameterDef<'tcx>>,
|
||||
substs: Option<&mut Substs<'tcx>>,
|
||||
space: Option<ParamSpace>,
|
||||
span: Span) -> Ty<'tcx>;
|
||||
|
||||
/// Projecting an associated type from a (potentially)
|
||||
/// higher-ranked trait reference is more complicated, because of
|
||||
@ -403,7 +407,11 @@ fn create_substs_for_ast_path<'tcx>(
|
||||
// they were optional (e.g. paths inside expressions).
|
||||
let mut type_substs = if param_mode == PathParamMode::Optional &&
|
||||
types_provided.is_empty() {
|
||||
(0..formal_ty_param_count).map(|_| this.ty_infer(span)).collect()
|
||||
let mut substs = region_substs.clone();
|
||||
ty_param_defs
|
||||
.iter()
|
||||
.map(|p| this.ty_infer(Some(p.clone()), Some(&mut substs), Some(TypeSpace), span))
|
||||
.collect()
|
||||
} else {
|
||||
types_provided
|
||||
};
|
||||
@ -1654,7 +1662,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
|
||||
// values in a ExprClosure, or as
|
||||
// the type of local variables. Both of these cases are
|
||||
// handled specially and will not descend into this routine.
|
||||
this.ty_infer(ast_ty.span)
|
||||
this.ty_infer(None, None, None, ast_ty.span)
|
||||
}
|
||||
};
|
||||
|
||||
@ -1670,7 +1678,7 @@ pub fn ty_of_arg<'tcx>(this: &AstConv<'tcx>,
|
||||
{
|
||||
match a.ty.node {
|
||||
ast::TyInfer if expected_ty.is_some() => expected_ty.unwrap(),
|
||||
ast::TyInfer => this.ty_infer(a.ty.span),
|
||||
ast::TyInfer => this.ty_infer(None, None, None, a.ty.span),
|
||||
_ => ast_ty_to_ty(this, rscope, &*a.ty),
|
||||
}
|
||||
}
|
||||
@ -1789,7 +1797,7 @@ fn ty_of_method_or_bare_fn<'a, 'tcx>(this: &AstConv<'tcx>,
|
||||
|
||||
let output_ty = match decl.output {
|
||||
ast::Return(ref output) if output.node == ast::TyInfer =>
|
||||
ty::FnConverging(this.ty_infer(output.span)),
|
||||
ty::FnConverging(this.ty_infer(None, None, None, output.span)),
|
||||
ast::Return(ref output) =>
|
||||
ty::FnConverging(convert_ty_with_lifetime_elision(this,
|
||||
implied_output_region,
|
||||
@ -1929,7 +1937,7 @@ pub fn ty_of_closure<'tcx>(
|
||||
_ if is_infer && expected_ret_ty.is_some() =>
|
||||
expected_ret_ty.unwrap(),
|
||||
_ if is_infer =>
|
||||
ty::FnConverging(this.ty_infer(decl.output.span())),
|
||||
ty::FnConverging(this.ty_infer(None, None, None, decl.output.span())),
|
||||
ast::Return(ref output) =>
|
||||
ty::FnConverging(ast_ty_to_ty(this, &rb, &**output)),
|
||||
ast::DefaultReturn(..) => unreachable!(),
|
||||
|
@ -84,9 +84,12 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
||||
|
||||
// Create substitutions for the method's type parameters.
|
||||
let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick);
|
||||
let (method_types, method_regions) =
|
||||
self.instantiate_method_substs(&pick, supplied_method_types);
|
||||
let all_substs = rcvr_substs.with_method(method_types, method_regions);
|
||||
let all_substs =
|
||||
self.instantiate_method_substs(
|
||||
&pick,
|
||||
supplied_method_types,
|
||||
rcvr_substs);
|
||||
|
||||
debug!("all_substs={:?}", all_substs);
|
||||
|
||||
// Create the final signature for the method, replacing late-bound regions.
|
||||
@ -302,30 +305,18 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
||||
|
||||
fn instantiate_method_substs(&mut self,
|
||||
pick: &probe::Pick<'tcx>,
|
||||
supplied_method_types: Vec<Ty<'tcx>>)
|
||||
-> (Vec<Ty<'tcx>>, Vec<ty::Region>)
|
||||
supplied_method_types: Vec<Ty<'tcx>>,
|
||||
substs: subst::Substs<'tcx>)
|
||||
-> subst::Substs<'tcx>
|
||||
{
|
||||
// Determine the values for the generic parameters of the method.
|
||||
// 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_types = {
|
||||
if num_supplied_types == 0 {
|
||||
self.fcx.infcx().next_ty_vars(num_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)
|
||||
} 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");
|
||||
vec![self.tcx().types.err; num_method_types]
|
||||
} else {
|
||||
supplied_method_types
|
||||
}
|
||||
};
|
||||
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();
|
||||
|
||||
|
||||
// Create subst for early-bound lifetime parameters, combining
|
||||
// parameters from the type and those from the method.
|
||||
@ -337,7 +328,35 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
|
||||
pick.item.as_opt_method().unwrap()
|
||||
.generics.regions.get_slice(subst::FnSpace));
|
||||
|
||||
(method_types, method_regions)
|
||||
let subst::Substs { types, regions } = substs;
|
||||
let regions = regions.map(|r| r.with_vec(subst::FnSpace, method_regions));
|
||||
let mut final_substs = subst::Substs { types: types, regions: regions };
|
||||
|
||||
if num_supplied_types == 0 {
|
||||
self.fcx.infcx().type_vars_for_defs(
|
||||
self.span,
|
||||
subst::FnSpace,
|
||||
&mut final_substs,
|
||||
method_types);
|
||||
} else if num_method_types == 0 {
|
||||
span_err!(self.tcx().sess, self.span, E0035,
|
||||
"does not take type parameters");
|
||||
self.fcx.infcx().type_vars_for_defs(
|
||||
self.span,
|
||||
subst::FnSpace,
|
||||
&mut final_substs,
|
||||
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");
|
||||
final_substs.types.replace(
|
||||
subst::FnSpace,
|
||||
vec![self.tcx().types.err; num_method_types]);
|
||||
} else {
|
||||
final_substs.types.replace(subst::FnSpace, supplied_method_types);
|
||||
}
|
||||
|
||||
return final_substs;
|
||||
}
|
||||
|
||||
fn unify_receivers(&mut self,
|
||||
|
@ -167,23 +167,30 @@ 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 input_types = match opt_input_types {
|
||||
Some(input_types) => {
|
||||
assert_eq!(expected_number_of_input_types, input_types.len());
|
||||
input_types
|
||||
}
|
||||
|
||||
None => {
|
||||
fcx.inh.infcx.next_ty_vars(expected_number_of_input_types)
|
||||
}
|
||||
};
|
||||
let type_parameter_defs = trait_def.generics.types.get_slice(subst::TypeSpace);
|
||||
let expected_number_of_input_types = type_parameter_defs.len();
|
||||
|
||||
assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0);
|
||||
assert!(trait_def.generics.regions.is_empty());
|
||||
|
||||
// Construct a trait-reference `self_ty : Trait<input_tys>`
|
||||
let substs = subst::Substs::new_trait(input_types, Vec::new(), self_ty);
|
||||
let mut substs = subst::Substs::new_trait(Vec::new(), Vec::new(), self_ty);
|
||||
|
||||
match opt_input_types {
|
||||
Some(input_types) => {
|
||||
assert_eq!(expected_number_of_input_types, input_types.len());
|
||||
substs.types.replace(subst::ParamSpace::TypeSpace, input_types);
|
||||
}
|
||||
|
||||
None => {
|
||||
fcx.inh.infcx.type_vars_for_defs(
|
||||
span,
|
||||
subst::ParamSpace::TypeSpace,
|
||||
&mut substs,
|
||||
type_parameter_defs);
|
||||
}
|
||||
}
|
||||
|
||||
let trait_ref = ty::TraitRef::new(trait_def_id, fcx.tcx().mk_substs(substs));
|
||||
|
||||
// Construct an obligation
|
||||
|
@ -1200,16 +1200,12 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
|
||||
return impl_ty;
|
||||
}
|
||||
|
||||
let placeholder;
|
||||
let mut placeholder;
|
||||
let mut substs = substs;
|
||||
if
|
||||
!method.generics.types.is_empty_in(subst::FnSpace) ||
|
||||
!method.generics.regions.is_empty_in(subst::FnSpace)
|
||||
{
|
||||
let method_types =
|
||||
self.infcx().next_ty_vars(
|
||||
method.generics.types.len(subst::FnSpace));
|
||||
|
||||
// In general, during probe we erase regions. See
|
||||
// `impl_self_ty()` for an explanation.
|
||||
let method_regions =
|
||||
@ -1218,7 +1214,14 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
|
||||
.map(|_| ty::ReStatic)
|
||||
.collect();
|
||||
|
||||
placeholder = (*substs).clone().with_method(method_types, method_regions);
|
||||
placeholder = (*substs).clone().with_method(Vec::new(), method_regions);
|
||||
|
||||
self.infcx().type_vars_for_defs(
|
||||
self.span,
|
||||
subst::FnSpace,
|
||||
&mut placeholder,
|
||||
method.generics.types.get_slice(subst::FnSpace));
|
||||
|
||||
substs = &placeholder;
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,7 @@ use fmt_macros::{Parser, Piece, Position};
|
||||
use middle::astconv_util::{check_path_args, NO_TPS, NO_REGIONS};
|
||||
use middle::def;
|
||||
use middle::infer;
|
||||
use middle::infer::type_variable;
|
||||
use middle::pat_util::{self, pat_id_map};
|
||||
use middle::privacy::{AllPublic, LastMod};
|
||||
use middle::region::{self, CodeExtent};
|
||||
@ -108,6 +109,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};
|
||||
@ -1137,8 +1139,27 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
|
||||
trait_def.associated_type_names.contains(&assoc_name)
|
||||
}
|
||||
|
||||
fn ty_infer(&self, _span: Span) -> Ty<'tcx> {
|
||||
self.infcx().next_ty_var()
|
||||
fn ty_infer(&self,
|
||||
ty_param_def: Option<ty::TypeParameterDef<'tcx>>,
|
||||
substs: Option<&mut subst::Substs<'tcx>>,
|
||||
space: Option<subst::ParamSpace>,
|
||||
span: Span) -> Ty<'tcx> {
|
||||
// Grab the default doing subsitution
|
||||
let default = ty_param_def.and_then(|def| {
|
||||
def.default.map(|ty| type_variable::Default {
|
||||
ty: ty.subst_spanned(self.tcx(), substs.as_ref().unwrap(), Some(span)),
|
||||
origin_span: span,
|
||||
def_id: def.default_def_id
|
||||
})
|
||||
});
|
||||
|
||||
let ty_var = self.infcx().next_ty_var_with_default(default);
|
||||
|
||||
// Finally we add the type variable to the substs
|
||||
match substs {
|
||||
None => ty_var,
|
||||
Some(substs) => { substs.types.push(space.unwrap(), ty_var); ty_var }
|
||||
}
|
||||
}
|
||||
|
||||
fn projected_ty_from_poly_trait_ref(&self,
|
||||
@ -1255,28 +1276,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 {}",
|
||||
@ -1710,14 +1709,260 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn select_all_obligations_and_apply_defaults(&self) {
|
||||
debug!("select_all_obligations_and_apply_defaults");
|
||||
/// Apply "fallbacks" to some types
|
||||
/// ! gets replaced with (), unconstrained ints with i32, and unconstrained floats with f64.
|
||||
fn default_type_parameters(&self) {
|
||||
use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
|
||||
for ty in &self.infcx().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 => {
|
||||
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32)
|
||||
},
|
||||
UnconstrainedFloat => {
|
||||
demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.f64)
|
||||
}
|
||||
Neither => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_all_obligations_and_apply_defaults(&self) {
|
||||
if self.tcx().sess.features.borrow().default_type_parameter_fallback {
|
||||
self.new_select_all_obligations_and_apply_defaults();
|
||||
} else {
|
||||
self.old_select_all_obligations_and_apply_defaults();
|
||||
}
|
||||
}
|
||||
|
||||
// Implements old type inference fallback algorithm
|
||||
fn old_select_all_obligations_and_apply_defaults(&self) {
|
||||
self.select_obligations_where_possible();
|
||||
self.default_type_parameters();
|
||||
self.select_obligations_where_possible();
|
||||
}
|
||||
|
||||
fn new_select_all_obligations_and_apply_defaults(&self) {
|
||||
use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
|
||||
|
||||
// For the time being this errs on the side of being memory wasteful but provides better
|
||||
// error reporting.
|
||||
// let type_variables = self.infcx().type_variables.clone();
|
||||
|
||||
// There is a possibility that this algorithm will have to run an arbitrary number of times
|
||||
// to terminate so we bound it by the compiler's recursion limit.
|
||||
for _ in (0..self.tcx().sess.recursion_limit.get()) {
|
||||
// First we try to solve all obligations, it is possible that the last iteration
|
||||
// has made it possible to make more progress.
|
||||
self.select_obligations_where_possible();
|
||||
|
||||
let mut conflicts = Vec::new();
|
||||
|
||||
// Collect all unsolved type, integral and floating point variables.
|
||||
let unsolved_variables = self.inh.infcx.unsolved_variables();
|
||||
|
||||
// We must collect the defaults *before* we do any unification. Because we have
|
||||
// directly attached defaults to the type variables any unification that occurs
|
||||
// will erase defaults causing conflicting defaults to be completely ignored.
|
||||
let default_map: FnvHashMap<_, _> =
|
||||
unsolved_variables
|
||||
.iter()
|
||||
.filter_map(|t| self.infcx().default(t).map(|d| (t, d)))
|
||||
.collect();
|
||||
|
||||
let mut unbound_tyvars = HashSet::new();
|
||||
|
||||
debug!("select_all_obligations_and_apply_defaults: defaults={:?}", default_map);
|
||||
|
||||
// We loop over the unsolved variables, resolving them and if they are
|
||||
// and unconstrainted numberic type we add them to the set of unbound
|
||||
// variables. We do this so we only apply literal fallback to type
|
||||
// variables without defaults.
|
||||
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 | UnconstrainedFloat => {
|
||||
unbound_tyvars.insert(resolved);
|
||||
},
|
||||
Neither => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We now remove any numeric types that also have defaults, and instead insert
|
||||
// the type variable with a defined fallback.
|
||||
for ty in &unsolved_variables {
|
||||
if let Some(_default) = default_map.get(ty) {
|
||||
let resolved = self.infcx().resolve_type_vars_if_possible(ty);
|
||||
|
||||
debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}",
|
||||
ty, _default);
|
||||
|
||||
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 there are no more fallbacks to apply at this point we have applied all possible
|
||||
// defaults and type inference will procede as normal.
|
||||
if unbound_tyvars.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Finally we go through each of the unbound type variables and unify them with
|
||||
// the proper fallback, reporting a conflicting default error if any of the
|
||||
// unifications fail. We know it must be a conflicting default because the
|
||||
// variable would only be in `unbound_tyvars` and have a concrete value if
|
||||
// it had been solved by previously applying a default.
|
||||
|
||||
// We wrap this in a transaction for error reporting, if we detect a conflict
|
||||
// we will rollback the inference context to its prior state so we can probe
|
||||
// for conflicts and correctly report them.
|
||||
|
||||
|
||||
let _ = self.infcx().commit_if_ok(|_: &infer::CombinedSnapshot| {
|
||||
for ty in &unbound_tyvars {
|
||||
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 => {
|
||||
if let Some(default) = default_map.get(ty) {
|
||||
let default = default.clone();
|
||||
match infer::mk_eqty(self.infcx(), false,
|
||||
infer::Misc(default.origin_span),
|
||||
ty, default.ty) {
|
||||
Ok(()) => {}
|
||||
Err(_) => {
|
||||
conflicts.push((*ty, default));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are conflicts we rollback, otherwise commit
|
||||
if conflicts.len() > 0 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
|
||||
if conflicts.len() > 0 {
|
||||
// Loop through each conflicting default, figuring out the default that caused
|
||||
// a unification failure and then report an error for each.
|
||||
for (conflict, default) in conflicts {
|
||||
let conflicting_default =
|
||||
self.find_conflicting_default(&unbound_tyvars, &default_map, conflict)
|
||||
.unwrap_or(type_variable::Default {
|
||||
ty: self.infcx().next_ty_var(),
|
||||
origin_span: codemap::DUMMY_SP,
|
||||
def_id: local_def(0) // what do I put here?
|
||||
});
|
||||
|
||||
// This is to ensure that we elimnate any non-determinism from the error
|
||||
// reporting by fixing an order, it doesn't matter what order we choose
|
||||
// just that it is consistent.
|
||||
let (first_default, second_default) =
|
||||
if default.def_id < conflicting_default.def_id {
|
||||
(default, conflicting_default)
|
||||
} else {
|
||||
(conflicting_default, default)
|
||||
};
|
||||
|
||||
|
||||
self.infcx().report_conflicting_default_types(
|
||||
first_default.origin_span,
|
||||
first_default,
|
||||
second_default)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.select_obligations_where_possible();
|
||||
}
|
||||
|
||||
// For use in error handling related to default type parameter fallback. We explicitly
|
||||
// apply the default that caused conflict first to a local version of the type variable
|
||||
// table then apply defaults until we find a conflict. That default must be the one
|
||||
// that caused conflict earlier.
|
||||
fn find_conflicting_default(&self,
|
||||
unbound_vars: &HashSet<Ty<'tcx>>,
|
||||
default_map: &FnvHashMap<&Ty<'tcx>, type_variable::Default<'tcx>>,
|
||||
conflict: Ty<'tcx>)
|
||||
-> Option<type_variable::Default<'tcx>> {
|
||||
use middle::ty::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither};
|
||||
|
||||
// Ensure that we apply the conflicting default first
|
||||
let mut unbound_tyvars = Vec::with_capacity(unbound_vars.len() + 1);
|
||||
unbound_tyvars.push(conflict);
|
||||
unbound_tyvars.extend(unbound_vars.iter());
|
||||
|
||||
let mut result = None;
|
||||
// We run the same code as above applying defaults in order, this time when
|
||||
// we find the conflict we just return it for error reporting above.
|
||||
|
||||
// We also run this inside snapshot that never commits so we can do error
|
||||
// reporting for more then one conflict.
|
||||
for ty in &unbound_tyvars {
|
||||
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 => {
|
||||
if let Some(default) = default_map.get(ty) {
|
||||
let default = default.clone();
|
||||
match infer::mk_eqty(self.infcx(), false,
|
||||
infer::Misc(default.origin_span),
|
||||
ty, default.ty) {
|
||||
Ok(()) => {}
|
||||
Err(_) => {
|
||||
result = Some(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn select_all_obligations_or_error(&self) {
|
||||
debug!("select_all_obligations_or_error");
|
||||
|
||||
@ -1726,6 +1971,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
assert!(self.inh.deferred_call_resolutions.borrow().is_empty());
|
||||
|
||||
self.select_all_obligations_and_apply_defaults();
|
||||
|
||||
let mut fulfillment_cx = self.inh.infcx.fulfillment_cx.borrow_mut();
|
||||
match fulfillment_cx.select_all_or_error(self.infcx()) {
|
||||
Ok(()) => { }
|
||||
@ -2421,14 +2667,18 @@ 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 substs = subst::Substs::new_type(tps, rps);
|
||||
let mut substs = subst::Substs::new(
|
||||
VecPerParamSpace::empty(),
|
||||
VecPerParamSpace::new(rps, Vec::new(), Vec::new()));
|
||||
fcx.inh.infcx.type_vars_for_defs(span, ParamSpace::TypeSpace, &mut substs, tps);
|
||||
let substd_ty = fcx.instantiate_type_scheme(span, &substs, &raw_ty);
|
||||
|
||||
TypeAndSubsts { substs: substs, ty: substd_ty }
|
||||
@ -4434,7 +4684,7 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
|
||||
// variables. If the user provided some types, we may still need
|
||||
// to add defaults. If the user provided *too many* types, that's
|
||||
// a problem.
|
||||
for &space in &ParamSpace::all() {
|
||||
for &space in &[subst::SelfSpace, subst::TypeSpace, subst::FnSpace] {
|
||||
adjust_type_parameters(fcx, span, space, type_defs,
|
||||
require_type_space, &mut substs);
|
||||
assert_eq!(substs.types.len(space), type_defs.len(space));
|
||||
@ -4647,7 +4897,8 @@ 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, Vec::new());
|
||||
fcx.infcx().type_vars_for_defs(span, space, substs, &desired[..]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -404,7 +404,11 @@ impl<'a, 'tcx> AstConv<'tcx> for ItemCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_infer(&self, span: Span) -> Ty<'tcx> {
|
||||
fn ty_infer(&self,
|
||||
_ty_param_def: Option<ty::TypeParameterDef<'tcx>>,
|
||||
_substs: Option<&mut Substs<'tcx>>,
|
||||
_space: Option<ParamSpace>,
|
||||
span: Span) -> Ty<'tcx> {
|
||||
span_err!(self.tcx().sess, span, E0121,
|
||||
"the type placeholder `_` is not allowed within types on item signatures");
|
||||
self.tcx().types.err
|
||||
@ -1643,11 +1647,14 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
|
||||
// the node id for the Self type parameter.
|
||||
let param_id = trait_id;
|
||||
|
||||
let parent = ccx.tcx.map.get_parent(param_id);
|
||||
|
||||
let def = ty::TypeParameterDef {
|
||||
space: SelfSpace,
|
||||
index: 0,
|
||||
name: special_idents::type_self.name,
|
||||
def_id: local_def(param_id),
|
||||
default_def_id: local_def(parent),
|
||||
default: None,
|
||||
object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault,
|
||||
};
|
||||
@ -1916,11 +1923,14 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
||||
compute_object_lifetime_default(ccx, param.id,
|
||||
¶m.bounds, &ast_generics.where_clause);
|
||||
|
||||
let parent = tcx.map.get_parent(param.id);
|
||||
|
||||
let def = ty::TypeParameterDef {
|
||||
space: space,
|
||||
index: index,
|
||||
name: param.ident.name,
|
||||
def_id: local_def(param.id),
|
||||
default_def_id: local_def(parent),
|
||||
default: default,
|
||||
object_lifetime_default: object_lifetime_default,
|
||||
};
|
||||
|
@ -163,6 +163,8 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
|
||||
|
||||
// Allows the definition recursive static items.
|
||||
("static_recursion", "1.3.0", Active),
|
||||
// Allows default type parameters to influence type inference.
|
||||
("default_type_parameter_fallback", "1.3.0", Active)
|
||||
];
|
||||
// (changing above list without updating src/doc/reference.md makes @cmr sad)
|
||||
|
||||
@ -341,7 +343,8 @@ pub struct Features {
|
||||
/// #![feature] attrs for non-language (library) features
|
||||
pub declared_lib_features: Vec<(InternedString, Span)>,
|
||||
pub const_fn: bool,
|
||||
pub static_recursion: bool
|
||||
pub static_recursion: bool,
|
||||
pub default_type_parameter_fallback: bool,
|
||||
}
|
||||
|
||||
impl Features {
|
||||
@ -366,7 +369,8 @@ impl Features {
|
||||
declared_stable_lang_features: Vec::new(),
|
||||
declared_lib_features: Vec::new(),
|
||||
const_fn: false,
|
||||
static_recursion: false
|
||||
static_recursion: false,
|
||||
default_type_parameter_fallback: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -864,7 +868,8 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
|
||||
declared_stable_lang_features: accepted_features,
|
||||
declared_lib_features: unknown_features,
|
||||
const_fn: cx.has_feature("const_fn"),
|
||||
static_recursion: cx.has_feature("static_recursion")
|
||||
static_recursion: cx.has_feature("static_recursion"),
|
||||
default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"),
|
||||
}
|
||||
}
|
||||
|
||||
|
19
src/test/auxiliary/default_ty_param_cross_crate_crate.rs
Normal file
19
src/test/auxiliary/default_ty_param_cross_crate_crate.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![crate_name = "default_param_test"]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct Foo<A, B>(PhantomData<(A, B)>);
|
||||
|
||||
pub fn bleh<A=i32, X=char>() -> Foo<A, X> { Foo(PhantomData) }
|
||||
|
32
src/test/compile-fail/default_ty_param_conflict.rs
Normal file
32
src/test/compile-fail/default_ty_param_conflict.rs
Normal file
@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
// Example from the RFC
|
||||
fn foo<F:Default=usize>() -> F { F::default() }
|
||||
//~^ NOTE: a default was defined here...
|
||||
|
||||
fn bar<B:Debug=isize>(b: B) { println!("{:?}", b); }
|
||||
//~^ NOTE: a second default was defined here...
|
||||
|
||||
fn main() {
|
||||
// Here, F is instantiated with $0=uint
|
||||
let x = foo();
|
||||
//~^ ERROR: mismatched types
|
||||
//~| NOTE: conflicting type parameter defaults `usize` and `isize`
|
||||
//~| NOTE: ...that was applied to an unconstrained type variable here
|
||||
|
||||
// Here, B is instantiated with $1=uint, and constraint $0 <: $1 is added.
|
||||
bar(x);
|
||||
//~^ NOTE: ...that also applies to the same type variable here
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
//
|
||||
//aux-build:default_ty_param_cross_crate_crate.rs
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
extern crate default_param_test;
|
||||
|
||||
use default_param_test::{Foo, bleh};
|
||||
|
||||
fn meh<X, B=bool>(x: Foo<X, B>) {}
|
||||
//~^ NOTE: a default was defined here...
|
||||
|
||||
fn main() {
|
||||
let foo = bleh();
|
||||
//~^ NOTE: ...that also applies to the same type variable here
|
||||
|
||||
meh(foo);
|
||||
//~^ ERROR: mismatched types:
|
||||
//~| NOTE: conflicting type parameter defaults `bool` and `char`
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
//
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
trait Id {
|
||||
type This;
|
||||
}
|
||||
|
||||
impl<A> Id for A {
|
||||
type This = A;
|
||||
}
|
||||
|
||||
struct Foo<X: Default = usize, Y = <X as Id>::This> {
|
||||
data: PhantomData<(X, Y)>
|
||||
}
|
||||
|
||||
impl<X: Default, Y> Foo<X, Y> {
|
||||
fn new() -> Foo<X, Y> {
|
||||
Foo { data: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo = Foo::new();
|
||||
}
|
19
src/test/run-pass/default_ty_param_dependent_defaults.rs
Normal file
19
src/test/run-pass/default_ty_param_dependent_defaults.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
//
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct Foo<T,U=T> { t: T, data: PhantomData<U> }
|
||||
|
||||
fn main() {
|
||||
let foo = Foo { t: 'a', data: PhantomData };
|
||||
}
|
24
src/test/run-pass/default_ty_param_method_call_test.rs
Normal file
24
src/test/run-pass/default_ty_param_method_call_test.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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.
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn method<A:Default=String>(&self) -> A {
|
||||
A::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let f = Foo.method();
|
||||
println!("{}", f);
|
||||
}
|
23
src/test/run-pass/default_ty_param_struct.rs
Normal file
23
src/test/run-pass/default_ty_param_struct.rs
Normal 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.
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
struct Foo<A>(A);
|
||||
|
||||
impl<A:Default=i32> Foo<A> {
|
||||
fn new() -> Foo<A> {
|
||||
Foo(A::default())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo = Foo::new();
|
||||
}
|
40
src/test/run-pass/default_ty_param_struct_and_type_alias.rs
Normal file
40
src/test/run-pass/default_ty_param_struct_and_type_alias.rs
Normal file
@ -0,0 +1,40 @@
|
||||
// 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.
|
||||
//
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
struct DeterministicHasher;
|
||||
struct RandomHasher;
|
||||
|
||||
|
||||
struct MyHashMap<K, V, H=DeterministicHasher> {
|
||||
data: PhantomData<(K, V, H)>
|
||||
}
|
||||
|
||||
impl<K, V, H> MyHashMap<K, V, H> {
|
||||
fn new() -> MyHashMap<K, V, H> {
|
||||
MyHashMap { data: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
mod mystd {
|
||||
use super::{MyHashMap, RandomHasher};
|
||||
pub type HashMap<K, V, H=RandomHasher> = MyHashMap<K, V, H>;
|
||||
}
|
||||
|
||||
fn try_me<H>(hash_map: mystd::HashMap<i32, i32, H>) {}
|
||||
|
||||
fn main() {
|
||||
let hash_map = mystd::HashMap::new();
|
||||
try_me(hash_map);
|
||||
}
|
25
src/test/run-pass/default_ty_param_trait_impl.rs
Normal file
25
src/test/run-pass/default_ty_param_trait_impl.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
// Another example from the RFC
|
||||
trait Foo { }
|
||||
trait Bar { }
|
||||
|
||||
impl<T:Bar=usize> Foo for Vec<T> {}
|
||||
impl Bar for usize {}
|
||||
|
||||
fn takes_foo<F:Foo>(f: F) {}
|
||||
|
||||
fn main() {
|
||||
let x = Vec::new(); // x: Vec<$0>
|
||||
takes_foo(x); // adds oblig Vec<$0> : Foo
|
||||
}
|
26
src/test/run-pass/default_ty_param_trait_impl_simple.rs
Normal file
26
src/test/run-pass/default_ty_param_trait_impl_simple.rs
Normal 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.
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
// 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 Bar for usize {}
|
||||
|
||||
fn main() {
|
||||
let x = Vec::new(); // x: Vec<$0>
|
||||
x.takes_foo(); // adds oblig Vec<$0> : Foo
|
||||
}
|
19
src/test/run-pass/default_ty_param_type_alias.rs
Normal file
19
src/test/run-pass/default_ty_param_type_alias.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// 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.
|
||||
|
||||
#![feature(default_type_parameter_fallback)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
type IntMap<K=usize> = HashMap<K, usize>;
|
||||
|
||||
fn main() {
|
||||
let x = IntMap::new();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user