diff --git a/src/doc/reference.md b/src/doc/reference.md index 26fd2fd8d20d6..59721edda707b 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -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 diff --git a/src/librustc/ast_map/mod.rs b/src/librustc/ast_map/mod.rs index bdb481bc93859..10552791d8b86 100644 --- a/src/librustc/ast_map/mod.rs +++ b/src/librustc/ast_map/mod.rs @@ -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) } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 7d50e6f6917cc..fb11aaed61958 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -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)] diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 72e1525b506d1..54c55d76a8270 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -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, } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index c77e96f164888..597401daccfd2 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -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); } diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 8d66ffac5d17c..fbf19a10d93bf 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -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); } } diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index c6df1c395ccec..f63154af72426 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -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> { + match ty.sty { + ty::TyInfer(ty::TyVar(vid)) => self.type_variables.borrow().default(vid), + _ => None + } + } + + pub fn unsolved_variables(&self) -> Vec> { + 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>) -> 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( &self, span: Span, diff --git a/src/librustc/middle/infer/type_variable.rs b/src/librustc/middle/infer/type_variable.rs index 6f1de3ee63597..3684651f85be6 100644 --- a/src/librustc/middle/infer/type_variable.rs +++ b/src/librustc/middle/infer/type_variable.rs @@ -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), + Bounded { + relations: Vec, + default: Option> + } +} + +// 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), + SpecifyVar(ty::TyVid, Vec, Option>), 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> { + 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>) -> 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> { 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 { + 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>, action: UndoEntry) { + fn reverse(values: &mut Vec>, 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 { match v.value { Known(_) => panic!("var_sub_var: variable is known"), - Bounded(ref mut relations) => relations + Bounded { ref mut relations, .. } => relations } } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 4e98ef2753105..7d8a20c42e36e 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -154,7 +154,7 @@ impl<'tcx> Substs<'tcx> { } impl RegionSubsts { - fn map(self, op: F) -> RegionSubsts where + pub fn map(self, op: F) -> RegionSubsts where F: FnOnce(VecPerParamSpace) -> VecPerParamSpace, { match self { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 84ca7cd437a96..ea5ca8acb094f 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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 { } // Data structures used in type unification -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub enum TypeError<'tcx> { Mismatch, UnsafetyMismatch(ExpectedFound), @@ -2068,6 +2068,7 @@ pub enum TypeError<'tcx> { ConvergenceMismatch(ExpectedFound), ProjectionNameMismatched(ExpectedFound), ProjectionBoundsLength(ExpectedFound), + TyParamDefaultMismatch(ExpectedFound>) } /// 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>, 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::>(); + 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::>(); + + 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")); } _ => {} } diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index b6bb82ad7b15b..0c694926ba4b5 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -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), } } diff --git a/src/librustc_data_structures/unify/mod.rs b/src/librustc_data_structures/unify/mod.rs index a899bbacc0301..7582b7ff61d88 100644 --- a/src/librustc_data_structures/unify/mod.rs +++ b/src/librustc_data_structures/unify/mod.rs @@ -339,5 +339,11 @@ impl<'tcx,K,V> UnificationTable pub fn probe(&mut self, a_id: K) -> Option { self.get(a_id).value.clone() } -} + pub fn unsolved_variables(&mut self) -> Vec { + self.values + .iter() + .filter_map(|vv| if vv.value.is_some() { None } else { Some(vv.key()) }) + .collect() + } +} diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs index 217181da1421a..31e4b9c48e20b 100644 --- a/src/librustc_trans/trans/monomorphize.rs +++ b/src/librustc_trans/trans/monomorphize.rs @@ -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(..) | diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index d516c648d7ed8..332b84bfc7bfd 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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>, + substs: Option<&mut Substs<'tcx>>, + space: Option, + 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!(), diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index a8c56b2660ce0..e9869e2a00e5d 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -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>) - -> (Vec>, Vec) + supplied_method_types: Vec>, + 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, diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index a74c004389b4d..e3f55cca9ee51 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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 { + 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` + 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()); - input_types + substs.types.replace(subst::ParamSpace::TypeSpace, input_types); } None => { - fcx.inh.infcx.next_ty_vars(expected_number_of_input_types) + fcx.inh.infcx.type_vars_for_defs( + span, + subst::ParamSpace::TypeSpace, + &mut substs, + type_parameter_defs); } - }; - - assert_eq!(trait_def.generics.types.len(subst::FnSpace), 0); - assert!(trait_def.generics.regions.is_empty()); + } - // Construct a trait-reference `self_ty : Trait` - let substs = subst::Substs::new_trait(input_types, Vec::new(), self_ty); let trait_ref = ty::TraitRef::new(trait_def_id, fcx.tcx().mk_substs(substs)); // Construct an obligation diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index a960123efc6b8..44d769a799f1d 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -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; } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 082dafc72bc6f..85df5d67ff6eb 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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>, + substs: Option<&mut subst::Substs<'tcx>>, + space: Option, + 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> { } } + /// 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) { - debug!("select_all_obligations_and_apply_defaults"); + 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>, + default_map: &FnvHashMap<&Ty<'tcx>, type_variable::Default<'tcx>>, + conflict: Ty<'tcx>) + -> Option> { + 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; } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index b003f0fc4f8a6..00537f66bbcee 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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>, + _substs: Option<&mut Substs<'tcx>>, + _space: Option, + 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, }; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ee895fb1a96eb..c3338f02ee43c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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(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"), } } diff --git a/src/test/auxiliary/default_ty_param_cross_crate_crate.rs b/src/test/auxiliary/default_ty_param_cross_crate_crate.rs new file mode 100644 index 0000000000000..270cfdcb7f651 --- /dev/null +++ b/src/test/auxiliary/default_ty_param_cross_crate_crate.rs @@ -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 or the MIT license +// , 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(PhantomData<(A, B)>); + +pub fn bleh() -> Foo { Foo(PhantomData) } + diff --git a/src/test/compile-fail/default_ty_param_conflict.rs b/src/test/compile-fail/default_ty_param_conflict.rs new file mode 100644 index 0000000000000..48c5cd1ff7706 --- /dev/null +++ b/src/test/compile-fail/default_ty_param_conflict.rs @@ -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 or the MIT license +// , 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 { F::default() } +//~^ NOTE: a default was defined here... + +fn bar(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 +} diff --git a/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs b/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs new file mode 100644 index 0000000000000..4d60724372ada --- /dev/null +++ b/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs @@ -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 or the MIT license +// , 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: Foo) {} +//~^ 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` +} diff --git a/src/test/run-pass/default_ty_param_default_dependent_associated_type.rs b/src/test/run-pass/default_ty_param_default_dependent_associated_type.rs new file mode 100644 index 0000000000000..8fc2c2e6bce70 --- /dev/null +++ b/src/test/run-pass/default_ty_param_default_dependent_associated_type.rs @@ -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 or the MIT license +// , 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 Id for A { + type This = A; +} + +struct Foo::This> { + data: PhantomData<(X, Y)> +} + +impl Foo { + fn new() -> Foo { + Foo { data: PhantomData } + } +} + +fn main() { + let foo = Foo::new(); +} diff --git a/src/test/run-pass/default_ty_param_dependent_defaults.rs b/src/test/run-pass/default_ty_param_dependent_defaults.rs new file mode 100644 index 0000000000000..ac833d0f54744 --- /dev/null +++ b/src/test/run-pass/default_ty_param_dependent_defaults.rs @@ -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 or the MIT license +// , 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: T, data: PhantomData } + +fn main() { + let foo = Foo { t: 'a', data: PhantomData }; +} diff --git a/src/test/run-pass/default_ty_param_method_call_test.rs b/src/test/run-pass/default_ty_param_method_call_test.rs new file mode 100644 index 0000000000000..e8d93092ec53d --- /dev/null +++ b/src/test/run-pass/default_ty_param_method_call_test.rs @@ -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 or the MIT license +// , 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(&self) -> A { + A::default() + } +} + +fn main() { + let f = Foo.method(); + println!("{}", f); +} diff --git a/src/test/run-pass/default_ty_param_struct.rs b/src/test/run-pass/default_ty_param_struct.rs new file mode 100644 index 0000000000000..d9ac51fc23b02 --- /dev/null +++ b/src/test/run-pass/default_ty_param_struct.rs @@ -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 or the MIT license +// , 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); + +impl Foo { + fn new() -> Foo { + Foo(A::default()) + } +} + +fn main() { + let foo = Foo::new(); +} diff --git a/src/test/run-pass/default_ty_param_struct_and_type_alias.rs b/src/test/run-pass/default_ty_param_struct_and_type_alias.rs new file mode 100644 index 0000000000000..6e3e60a02e5e2 --- /dev/null +++ b/src/test/run-pass/default_ty_param_struct_and_type_alias.rs @@ -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 or the MIT license +// , 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 { + data: PhantomData<(K, V, H)> +} + +impl MyHashMap { + fn new() -> MyHashMap { + MyHashMap { data: PhantomData } + } +} + +mod mystd { + use super::{MyHashMap, RandomHasher}; + pub type HashMap = MyHashMap; +} + +fn try_me(hash_map: mystd::HashMap) {} + +fn main() { + let hash_map = mystd::HashMap::new(); + try_me(hash_map); +} diff --git a/src/test/run-pass/default_ty_param_trait_impl.rs b/src/test/run-pass/default_ty_param_trait_impl.rs new file mode 100644 index 0000000000000..c67d3a49aff3d --- /dev/null +++ b/src/test/run-pass/default_ty_param_trait_impl.rs @@ -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 or the MIT license +// , 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 Foo for Vec {} +impl Bar for usize {} + +fn takes_foo(f: F) {} + +fn main() { + let x = Vec::new(); // x: Vec<$0> + takes_foo(x); // adds oblig Vec<$0> : Foo +} diff --git a/src/test/run-pass/default_ty_param_trait_impl_simple.rs b/src/test/run-pass/default_ty_param_trait_impl_simple.rs new file mode 100644 index 0000000000000..067ad524922c0 --- /dev/null +++ b/src/test/run-pass/default_ty_param_trait_impl_simple.rs @@ -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 or the MIT license +// , 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 Foo for Vec { + 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 +} diff --git a/src/test/run-pass/default_ty_param_type_alias.rs b/src/test/run-pass/default_ty_param_type_alias.rs new file mode 100644 index 0000000000000..1b4747406d0c6 --- /dev/null +++ b/src/test/run-pass/default_ty_param_type_alias.rs @@ -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 or the MIT license +// , 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 = HashMap; + +fn main() { + let x = IntMap::new(); +}