From d72fcdfbeaeb42d07fb2a85616385748558ff523 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Mon, 10 Aug 2015 17:56:02 -0700 Subject: [PATCH 1/7] Change fallback priorties to prefer user defaults --- src/librustc/middle/infer/type_variable.rs | 43 ++++---- src/librustc_typeck/check/mod.rs | 100 ++++++++---------- .../run-pass/default_over_literal_fallback.rs | 15 +++ 3 files changed, 82 insertions(+), 76 deletions(-) create mode 100755 src/test/run-pass/default_over_literal_fallback.rs diff --git a/src/librustc/middle/infer/type_variable.rs b/src/librustc/middle/infer/type_variable.rs index e4af098c2a42d..60beada098ce5 100644 --- a/src/librustc/middle/infer/type_variable.rs +++ b/src/librustc/middle/infer/type_variable.rs @@ -27,14 +27,14 @@ pub struct TypeVariableTable<'tcx> { struct TypeVariableData<'tcx> { value: TypeVariableValue<'tcx>, - diverging: bool + default: Option>, + diverging: bool, } enum TypeVariableValue<'tcx> { Known(Ty<'tcx>), Bounded { relations: Vec, - default: Option> } } @@ -53,9 +53,9 @@ pub struct Snapshot { snapshot: sv::Snapshot } -enum UndoEntry<'tcx> { +enum UndoEntry { // The type of the var was specified. - SpecifyVar(ty::TyVid, Vec, Option>), + SpecifyVar(ty::TyVid, Vec), Relate(ty::TyVid, ty::TyVid), } @@ -89,10 +89,7 @@ impl<'tcx> TypeVariableTable<'tcx> { } pub fn default(&self, vid: ty::TyVid) -> Option> { - match &self.values.get(vid.index as usize).value { - &Known(_) => None, - &Bounded { ref default, .. } => default.clone() - } + self.values.get(vid.index as usize).default.clone() } pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool { @@ -124,8 +121,8 @@ impl<'tcx> TypeVariableTable<'tcx> { mem::replace(value_ptr, Known(ty)) }; - let (relations, default) = match old_value { - Bounded { relations, default } => (relations, default), + let relations = match old_value { + Bounded { relations } => relations , Known(_) => panic!("Asked to instantiate variable that is \ already instantiated") }; @@ -134,14 +131,15 @@ impl<'tcx> TypeVariableTable<'tcx> { stack.push((ty, dir, vid)); } - self.values.record(SpecifyVar(vid, relations, default)); + self.values.record(SpecifyVar(vid, relations)); } pub fn new_var(&mut self, diverging: bool, default: Option>) -> ty::TyVid { let index = self.values.push(TypeVariableData { - value: Bounded { relations: vec![], default: default }, + value: Bounded { relations: vec![] }, + default: default, diverging: diverging }); ty::TyVid { index: index as u32 } @@ -204,7 +202,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. @@ -226,23 +224,26 @@ impl<'tcx> TypeVariableTable<'tcx> { .iter() .enumerate() .filter_map(|(i, value)| match &value.value { - &TypeVariableValue::Known(_) => None, - &TypeVariableValue::Bounded { .. } => Some(ty::TyVid { index: i as u32 }) - }) - .collect() + &TypeVariableValue::Bounded { .. } => Some(ty::TyVid { index: i as u32 }), + &TypeVariableValue::Known(v) => match v.sty { + ty::TyInfer(ty::FloatVar(_)) | ty::TyInfer(ty::IntVar(_)) => + Some(ty::TyVid { index: i as u32 }), + _ => None + } + }) + .collect() } } impl<'tcx> sv::SnapshotVecDelegate for Delegate<'tcx> { type Value = TypeVariableData<'tcx>; - type Undo = UndoEntry<'tcx>; + type Undo = UndoEntry; - fn reverse(values: &mut Vec>, action: UndoEntry<'tcx>) { + fn reverse(values: &mut Vec>, action: UndoEntry) { match action { - SpecifyVar(vid, relations, default) => { + SpecifyVar(vid, relations) => { values[vid.index as usize].value = Bounded { relations: relations, - default: default }; } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9dd7cfea43f69..a36a28b3bb06d 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1812,7 +1812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 + // It is a possible 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 @@ -1837,47 +1837,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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. + // Examine all unsolved variables, and narrow them to the set that have applicable + // defaults. We want to process any unsolved variables that have either an explicit + // user default, literal fallback, or are diverging. 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(_) => { + 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); - } - } - _ => {} } + } else { + if self.infcx().type_var_diverges(resolved) { + unbound_tyvars.insert(ty); + } else { + match self.infcx().type_is_unconstrained_numeric(resolved) { + UnconstrainedInt | UnconstrainedFloat => { + unbound_tyvars.insert(ty); + }, + Neither => {} + } + } } } @@ -1887,21 +1873,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { break; } - // Finally we go through each of the unbound type variables and unify them with + // Finally we loop 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. - + // unifications fail. + // + // We know that a conflict must be due to multiple applicable defaults, because the + // variable would only be in `unbound_tyvars` and have been previously unified with + // another type if we had applied a default. + // We wrap all of this in a transaction for error reporting. If we detect a conflict + // we will rollback to the previous state so we can check what 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()); + 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)); + } + } } else { match self.infcx().type_is_unconstrained_numeric(ty) { UnconstrainedInt => { @@ -1910,23 +1904,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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)); - } - } - } - } + Neither => {} } } } + // If there are conflicts we rollback, otherwise commit if conflicts.len() > 0 { Err(()) @@ -1935,6 +1918,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); + // Finally for any leftover variables that diverage we should apply the unit fallback rules. + for ty in &unbound_tyvars { + if self.infcx().type_var_diverges(ty) { + demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil()); + } + } + 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. diff --git a/src/test/run-pass/default_over_literal_fallback.rs b/src/test/run-pass/default_over_literal_fallback.rs new file mode 100755 index 0000000000000..bc5478240a272 --- /dev/null +++ b/src/test/run-pass/default_over_literal_fallback.rs @@ -0,0 +1,15 @@ +// 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)] + +fn foo(t: T) -> usize { std::mem::size_of_val(&t) } + +fn main() { assert_eq!(foo(22), std::mem::size_of::()) } From c0b76386ac098b2017889531e3f459bbd7d767d8 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Wed, 19 Aug 2015 00:42:09 -0700 Subject: [PATCH 2/7] Refactor default type parameter fallback The previous implementation was not very clear, this commit attempts to clarify and clean up the code that applies all defaults. --- src/librustc/middle/infer/mod.rs | 38 +++-- src/librustc/middle/infer/type_variable.rs | 58 +++++-- src/librustc_typeck/check/mod.rs | 183 ++++++++++----------- 3 files changed, 149 insertions(+), 130 deletions(-) diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 917727907ba88..5a975da9dad01 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -616,40 +616,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. + /// without a default, this will return NoDefault. /// /// 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> { + pub fn default(&self, ty: Ty<'tcx>) -> type_variable::Default<'tcx> { match ty.sty { - ty::TyInfer(ty::TyVar(vid)) => self.type_variables.borrow().default(vid), - _ => None + ty::TyInfer(ty::TyVar(vid)) => self.type_variables + .borrow() + .default(vid) + .clone(), + _ => type_variable::Default::None, } } - pub fn unsolved_variables(&self) -> Vec> { + // Returns a vector containing all type variables that have an applicable default, + // along with their defaults. + // + // NB: You must be careful to only apply defaults once, if a variable is unififed + // it many no longer be unsolved and apply a second default will mostly likely + // result in a type error. + pub fn candidates_for_defaulting(&self) + -> Vec<(ty::Ty<'tcx>, type_variable::Default<'tcx>)> { let mut variables = Vec::new(); let unbound_ty_vars = self.type_variables .borrow() - .unsolved_variables() + .candidates_for_defaulting() .into_iter() - .map(|t| self.tcx.mk_var(t)); + .map(|(t, d)| (self.tcx.mk_var(t), d)); let unbound_int_vars = self.int_unification_table .borrow_mut() .unsolved_variables() .into_iter() - .map(|v| self.tcx.mk_int_var(v)); + .map(|v| (self.tcx.mk_int_var(v), type_variable::Default::Integer)); let unbound_float_vars = self.float_unification_table .borrow_mut() .unsolved_variables() .into_iter() - .map(|v| self.tcx.mk_float_var(v)); + .map(|v| (self.tcx.mk_float_var(v), type_variable::Default::Float)); variables.extend(unbound_ty_vars); variables.extend(unbound_int_vars); @@ -960,7 +970,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn next_ty_var_with_default(&self, - default: Option>) -> Ty<'tcx> { + default: Option>) -> Ty<'tcx> { let ty_var_id = self.type_variables .borrow_mut() .new_var(false, default); @@ -1013,7 +1023,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { for def in defs.iter() { let default = def.default.map(|default| { - type_variable::Default { + type_variable::UserDefault { ty: default.subst_spanned(self.tcx, substs, Some(span)), origin_span: span, def_id: def.default_def_id @@ -1319,13 +1329,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn report_conflicting_default_types(&self, span: Span, - expected: type_variable::Default<'tcx>, - actual: type_variable::Default<'tcx>) { + expected: type_variable::UserDefault<'tcx>, + actual: type_variable::UserDefault<'tcx>) { let trace = TypeTrace { origin: Misc(span), values: Types(ExpectedFound { expected: expected.ty, - found: actual.ty + found: actual.ty, }) }; diff --git a/src/librustc/middle/infer/type_variable.rs b/src/librustc/middle/infer/type_variable.rs index 60beada098ce5..caec364af4813 100644 --- a/src/librustc/middle/infer/type_variable.rs +++ b/src/librustc/middle/infer/type_variable.rs @@ -27,8 +27,7 @@ pub struct TypeVariableTable<'tcx> { struct TypeVariableData<'tcx> { value: TypeVariableValue<'tcx>, - default: Option>, - diverging: bool, + default: Default<'tcx>, } enum TypeVariableValue<'tcx> { @@ -38,10 +37,20 @@ enum TypeVariableValue<'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 enum Default<'tcx> { + User(UserDefault<'tcx>), + Integer, + Float, + Diverging, + None, +} + +// 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 UserDefault<'tcx> { + /// The default type provided by the user pub ty: Ty<'tcx>, /// The span where the default was incurred pub origin_span: Span, @@ -88,12 +97,15 @@ impl<'tcx> TypeVariableTable<'tcx> { relations(self.values.get_mut(a.index as usize)) } - pub fn default(&self, vid: ty::TyVid) -> Option> { - self.values.get(vid.index as usize).default.clone() + pub fn default(&self, vid: ty::TyVid) -> &Default<'tcx> { + &self.values.get(vid.index as usize).default } pub fn var_diverges<'a>(&'a self, vid: ty::TyVid) -> bool { - self.values.get(vid.index as usize).diverging + match self.values.get(vid.index as usize).default { + Default::Diverging => true, + _ => false, + } } /// Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`. @@ -136,12 +148,19 @@ impl<'tcx> TypeVariableTable<'tcx> { pub fn new_var(&mut self, diverging: bool, - default: Option>) -> ty::TyVid { + default: Option>) -> ty::TyVid { + let default = if diverging { + Default::Diverging + } else { + default.map(|u| Default::User(u)) + .unwrap_or(Default::None) + }; + let index = self.values.push(TypeVariableData { value: Bounded { relations: vec![] }, default: default, - diverging: diverging }); + ty::TyVid { index: index as u32 } } @@ -219,17 +238,20 @@ impl<'tcx> TypeVariableTable<'tcx> { escaping_types } - pub fn unsolved_variables(&self) -> Vec { + pub fn candidates_for_defaulting(&self) -> Vec<(ty::TyVid, Default<'tcx>)> { self.values .iter() .enumerate() - .filter_map(|(i, value)| match &value.value { - &TypeVariableValue::Bounded { .. } => Some(ty::TyVid { index: i as u32 }), - &TypeVariableValue::Known(v) => match v.sty { - ty::TyInfer(ty::FloatVar(_)) | ty::TyInfer(ty::IntVar(_)) => - Some(ty::TyVid { index: i as u32 }), - _ => None - } + .filter_map(|(i, value)| { + let vid = match &value.value { + &TypeVariableValue::Bounded { .. } => Some(ty::TyVid { index: i as u32 }), + &TypeVariableValue::Known(v) => match v.sty { + ty::TyInfer(ty::FloatVar(_)) | ty::TyInfer(ty::IntVar(_)) => + Some(ty::TyVid { index: i as u32 }), + _ => None + } + }; + vid.map(|v| (v, value.default.clone())) }) .collect() } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a36a28b3bb06d..329bac606fa26 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -88,6 +88,7 @@ use middle::def; use middle::def_id::{DefId, LOCAL_CRATE}; use middle::infer; use middle::infer::type_variable; +use middle::infer::type_variable::Default; use middle::pat_util::{self, pat_id_map}; use middle::privacy::{AllPublic, LastMod}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace}; @@ -1186,7 +1187,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { span: Span) -> Ty<'tcx> { // Grab the default doing subsitution let default = ty_param_def.and_then(|def| { - def.default.map(|ty| type_variable::Default { + def.default.map(|ty| type_variable::UserDefault { ty: ty.subst_spanned(self.tcx(), substs.as_ref().unwrap(), Some(span)), origin_span: span, def_id: def.default_def_id @@ -1822,54 +1823,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut conflicts = Vec::new(); // Collect all unsolved type, integral and floating point variables. - let unsolved_variables = self.inh.infcx.unsolved_variables(); + let unsolved_variables = self.inh.infcx.candidates_for_defaulting(); - // 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); + let mut has_user_default = HashSet::new(); + let mut has_literal_fallback = HashSet::new(); + let mut is_diverging = HashSet::new(); // Examine all unsolved variables, and narrow them to the set that have applicable // defaults. We want to process any unsolved variables that have either an explicit // user default, literal fallback, or are diverging. - for ty in &unsolved_variables { + for &(ref ty, ref default) in &unsolved_variables { let resolved = self.infcx().resolve_type_vars_if_possible(ty); - if let Some(_default) = default_map.get(ty) { + match default { + &Default::User(ref user_default) => { + debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}", + ty, default); - debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}", - ty, _default); - - match resolved.sty { - ty::TyInfer(_) => { - unbound_tyvars.insert(*ty); - } - _ => {} + has_user_default.insert((*ty, user_default.clone())); + }, + &Default::Float | + &Default::Integer => { + has_literal_fallback.insert(*ty); } - } else { - if self.infcx().type_var_diverges(resolved) { - unbound_tyvars.insert(ty); - } else { - match self.infcx().type_is_unconstrained_numeric(resolved) { - UnconstrainedInt | UnconstrainedFloat => { - unbound_tyvars.insert(ty); - }, - Neither => {} - } + &Default::Diverging => { + is_diverging.insert(*ty); } + &Default::None => {} } } // 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() { + if has_user_default.len() == 0 && + has_literal_fallback.len() == 0 && + is_diverging.len() == 0 { break; } @@ -1885,26 +1872,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // we will rollback to the previous state so we can check what conflicts and // correctly report them. let _ = self.infcx().commit_if_ok(|_: &infer::CombinedSnapshot| { - for ty in &unbound_tyvars { - 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)); - } - } - } 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 => {} + for &(ref ty, ref default) in &has_user_default { + let default = default.clone(); + match infer::mk_eqty(self.infcx(), false, + infer::Misc(codemap::DUMMY_SP), // default.origin_span), + ty, default.ty) { + Ok(()) => {} + Err(_) => { + conflicts.push((*ty, default)); } } } @@ -1918,25 +1893,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); - // Finally for any leftover variables that diverage we should apply the unit fallback rules. - for ty in &unbound_tyvars { - if self.infcx().type_var_diverges(ty) { - demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil()); - } - } - 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 { + for conflict in conflicts { let conflicting_default = - self.find_conflicting_default(&unbound_tyvars, &default_map, conflict) - .unwrap_or(type_variable::Default { + self.find_conflicting_default(&has_user_default, &conflict) + .unwrap_or(type_variable::UserDefault { ty: self.infcx().next_ty_var(), origin_span: codemap::DUMMY_SP, def_id: DefId::local(0) // what do I put here? }); + let default = conflict.1; + // 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. @@ -1954,6 +1924,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { second_default) } } + + for ty in &has_literal_fallback { + let resolved = self.infcx().resolve_type_vars_if_possible(ty); + 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 => {} + } + } + + // Finally for any leftover variables that diverage + // we should apply the unit fallback rules. + for ty in &is_diverging { + if self.infcx().type_var_diverges(ty) { + demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil()); + } + } } self.select_obligations_where_possible(); @@ -1964,17 +1955,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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::error::UnconstrainedNumeric::Neither; - use middle::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat}; - + tys_with_defaults: &HashSet<(Ty<'tcx>, type_variable::UserDefault<'tcx>)>, + conflict: &(Ty<'tcx>, type_variable::UserDefault<'tcx>)) + -> Option> { // Ensure that we apply the conflicting default first - let mut unbound_tyvars = Vec::with_capacity(unbound_vars.len() + 1); + let mut unbound_tyvars = Vec::with_capacity(tys_with_defaults.len() + 1); unbound_tyvars.push(conflict); - unbound_tyvars.extend(unbound_vars.iter()); + unbound_tyvars.extend(tys_with_defaults.iter()); let mut result = None; // We run the same code as above applying defaults in order, this time when @@ -1982,30 +1969,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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); - } - } - } - } + for &(ref ty, ref default) in tys_with_defaults { + let default = default.clone(); + match infer::mk_eqty(self.infcx(), false, + infer::Misc(codemap::DUMMY_SP), // default.origin_span), + ty, default.ty) { + Ok(()) => {} + Err(_) => { + result = Some(default); + break; } } } @@ -2082,6 +2054,21 @@ impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum LvaluePreference { + PreferMutLvalue, + NoPreference +} + +impl LvaluePreference { + pub fn from_mutbl(m: ast::Mutability) -> Self { + match m { + ast::MutMutable => PreferMutLvalue, + ast::MutImmutable => NoPreference, + } + } +} + /// Whether `autoderef` requires types to resolve. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum UnresolvedTypeAction { From 8a93f34864baa07c9dba379ab73380fc21a2853c Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Wed, 19 Aug 2015 00:48:23 -0700 Subject: [PATCH 3/7] Address nits and add assertation --- src/librustc_typeck/check/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 329bac606fa26..8f56debabf287 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1823,7 +1823,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut conflicts = Vec::new(); // Collect all unsolved type, integral and floating point variables. - let unsolved_variables = self.inh.infcx.candidates_for_defaulting(); + let defaults_to_apply = self.inh.infcx.candidates_for_defaulting(); let mut has_user_default = HashSet::new(); let mut has_literal_fallback = HashSet::new(); @@ -1832,8 +1832,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Examine all unsolved variables, and narrow them to the set that have applicable // defaults. We want to process any unsolved variables that have either an explicit // user default, literal fallback, or are diverging. - for &(ref ty, ref default) in &unsolved_variables { - let resolved = self.infcx().resolve_type_vars_if_possible(ty); + for &(ref ty, ref default) in &defaults_to_apply { + // We should NEVER process anything but a TyInfer. + assert!(match ty.sty { ty::TyInfer(_) => true, _ => false }); match default { &Default::User(ref user_default) => { debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}", @@ -1925,6 +1926,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + // Apply integer and floating point fallbacks to any variables that + // weren't solved by the previous phase where we applied user defaults. for ty in &has_literal_fallback { let resolved = self.infcx().resolve_type_vars_if_possible(ty); match self.infcx().type_is_unconstrained_numeric(resolved) { From 8b0085034371b2ef79da84f62d40778a3463fa88 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Wed, 19 Aug 2015 01:09:41 -0700 Subject: [PATCH 4/7] Fix tidy --- src/librustc/middle/infer/mod.rs | 6 ++++-- src/librustc_typeck/check/mod.rs | 11 ++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 5a975da9dad01..d193975d35f48 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -653,13 +653,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .borrow_mut() .unsolved_variables() .into_iter() - .map(|v| (self.tcx.mk_int_var(v), type_variable::Default::Integer)); + .map(|v| (self.tcx.mk_int_var(v), + type_variable::Default::Integer)); let unbound_float_vars = self.float_unification_table .borrow_mut() .unsolved_variables() .into_iter() - .map(|v| (self.tcx.mk_float_var(v), type_variable::Default::Float)); + .map(|v| (self.tcx.mk_float_var(v), + type_variable::Default::Float)); variables.extend(unbound_ty_vars); variables.extend(unbound_int_vars); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 8f56debabf287..a87d9030d8a45 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1835,10 +1835,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for &(ref ty, ref default) in &defaults_to_apply { // We should NEVER process anything but a TyInfer. assert!(match ty.sty { ty::TyInfer(_) => true, _ => false }); + match default { &Default::User(ref user_default) => { - debug!("select_all_obligations_and_apply_defaults: ty: {:?} with default: {:?}", - ty, default); + debug!("select_all_obligations_and_apply_defaults: \ + ty: {:?} with default: {:?}", ty, default); has_user_default.insert((*ty, user_default.clone())); }, @@ -1958,9 +1959,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // table then apply defaults until we find a conflict. That default must be the one // that caused conflict earlier. fn find_conflicting_default(&self, - tys_with_defaults: &HashSet<(Ty<'tcx>, type_variable::UserDefault<'tcx>)>, - conflict: &(Ty<'tcx>, type_variable::UserDefault<'tcx>)) - -> Option> { + tys_with_defaults: &HashSet<(Ty<'tcx>, type_variable::UserDefault<'tcx>)>, + conflict: &(Ty<'tcx>, type_variable::UserDefault<'tcx>)) + -> Option> { // Ensure that we apply the conflicting default first let mut unbound_tyvars = Vec::with_capacity(tys_with_defaults.len() + 1); unbound_tyvars.push(conflict); From fb4134e8e9dfec9b526c449b9e31994aa432858c Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Mon, 24 Aug 2015 23:42:30 -0700 Subject: [PATCH 5/7] Ensure defaults are normalized after application Thanks to @eddyb for bringing this bug to my attention. --- src/librustc_typeck/check/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a87d9030d8a45..6ac6a7de98c9f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1876,9 +1876,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let _ = self.infcx().commit_if_ok(|_: &infer::CombinedSnapshot| { for &(ref ty, ref default) in &has_user_default { let default = default.clone(); + let normalized_default = self.inh.normalize_associated_types_in(codemap::DUMMY_SP, 0, &default.ty); match infer::mk_eqty(self.infcx(), false, infer::Misc(codemap::DUMMY_SP), // default.origin_span), - ty, default.ty) { + ty, normalized_default) { Ok(()) => {} Err(_) => { conflicts.push((*ty, default)); @@ -1975,9 +1976,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // reporting for more then one conflict. for &(ref ty, ref default) in tys_with_defaults { let default = default.clone(); + let normalized_default = self.inh.normalize_associated_types_in(codemap::DUMMY_SP, 0, &default.ty); match infer::mk_eqty(self.infcx(), false, infer::Misc(codemap::DUMMY_SP), // default.origin_span), - ty, default.ty) { + ty, normalized_default) { Ok(()) => {} Err(_) => { result = Some(default); From 13023041a698ca253f8c4488c558202c01552422 Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Thu, 3 Sep 2015 15:24:41 -0700 Subject: [PATCH 6/7] Address nits and fix tidy ... again --- src/librustc_typeck/check/mod.rs | 17 ++++++++++++----- ...rs => default_ty_param_fallback_conflict.rs} | 0 ...t_ty_param_fallback_conflict_cross_crate.rs} | 0 ...llback_default_dependent_associated_type.rs} | 0 ...m_fallback_default_over_literal_fallback.rs} | 0 ...ult_ty_param_fallback_dependent_defaults.rs} | 0 ...lt_ty_param_fallback_ensure_normalization.rs | 16 ++++++++++++++++ ...fault_ty_param_fallback_method_call_test.rs} | 0 ...t.rs => default_ty_param_fallback_struct.rs} | 0 ..._ty_param_fallback_struct_and_type_alias.rs} | 0 ... => default_ty_param_fallback_trait_impl.rs} | 0 ...ault_ty_param_fallback_trait_impl_simple.rs} | 0 ... => default_ty_param_fallback_type_alias.rs} | 0 13 files changed, 28 insertions(+), 5 deletions(-) rename src/test/compile-fail/{default_ty_param_conflict.rs => default_ty_param_fallback_conflict.rs} (100%) rename src/test/compile-fail/{default_ty_param_conflict_cross_crate.rs => default_ty_param_fallback_conflict_cross_crate.rs} (100%) rename src/test/run-pass/{default_ty_param_default_dependent_associated_type.rs => default_ty_param_fallback_default_dependent_associated_type.rs} (100%) rename src/test/run-pass/{default_over_literal_fallback.rs => default_ty_param_fallback_default_over_literal_fallback.rs} (100%) rename src/test/run-pass/{default_ty_param_dependent_defaults.rs => default_ty_param_fallback_dependent_defaults.rs} (100%) create mode 100644 src/test/run-pass/default_ty_param_fallback_ensure_normalization.rs rename src/test/run-pass/{default_ty_param_method_call_test.rs => default_ty_param_fallback_method_call_test.rs} (100%) rename src/test/run-pass/{default_ty_param_struct.rs => default_ty_param_fallback_struct.rs} (100%) rename src/test/run-pass/{default_ty_param_struct_and_type_alias.rs => default_ty_param_fallback_struct_and_type_alias.rs} (100%) rename src/test/run-pass/{default_ty_param_trait_impl.rs => default_ty_param_fallback_trait_impl.rs} (100%) rename src/test/run-pass/{default_ty_param_trait_impl_simple.rs => default_ty_param_fallback_trait_impl_simple.rs} (100%) rename src/test/run-pass/{default_ty_param_type_alias.rs => default_ty_param_fallback_type_alias.rs} (100%) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 6ac6a7de98c9f..02e0ebaf7983f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1876,9 +1876,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let _ = self.infcx().commit_if_ok(|_: &infer::CombinedSnapshot| { for &(ref ty, ref default) in &has_user_default { let default = default.clone(); - let normalized_default = self.inh.normalize_associated_types_in(codemap::DUMMY_SP, 0, &default.ty); + + let normalized_default = self.inh.normalize_associated_types_in( + default.origin_span, + 0, &default.ty); + match infer::mk_eqty(self.infcx(), false, - infer::Misc(codemap::DUMMY_SP), // default.origin_span), + infer::Misc(default.origin_span), ty, normalized_default) { Ok(()) => {} Err(_) => { @@ -1887,7 +1891,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // If there are conflicts we rollback, otherwise commit if conflicts.len() > 0 { Err(()) @@ -1976,9 +1979,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // reporting for more then one conflict. for &(ref ty, ref default) in tys_with_defaults { let default = default.clone(); - let normalized_default = self.inh.normalize_associated_types_in(codemap::DUMMY_SP, 0, &default.ty); + + let normalized_default = self.inh.normalize_associated_types_in( + default.origin_span, 0, + &default.ty); + match infer::mk_eqty(self.infcx(), false, - infer::Misc(codemap::DUMMY_SP), // default.origin_span), + infer::Misc(default.origin_span), ty, normalized_default) { Ok(()) => {} Err(_) => { diff --git a/src/test/compile-fail/default_ty_param_conflict.rs b/src/test/compile-fail/default_ty_param_fallback_conflict.rs similarity index 100% rename from src/test/compile-fail/default_ty_param_conflict.rs rename to src/test/compile-fail/default_ty_param_fallback_conflict.rs diff --git a/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs b/src/test/compile-fail/default_ty_param_fallback_conflict_cross_crate.rs similarity index 100% rename from src/test/compile-fail/default_ty_param_conflict_cross_crate.rs rename to src/test/compile-fail/default_ty_param_fallback_conflict_cross_crate.rs diff --git a/src/test/run-pass/default_ty_param_default_dependent_associated_type.rs b/src/test/run-pass/default_ty_param_fallback_default_dependent_associated_type.rs similarity index 100% rename from src/test/run-pass/default_ty_param_default_dependent_associated_type.rs rename to src/test/run-pass/default_ty_param_fallback_default_dependent_associated_type.rs diff --git a/src/test/run-pass/default_over_literal_fallback.rs b/src/test/run-pass/default_ty_param_fallback_default_over_literal_fallback.rs similarity index 100% rename from src/test/run-pass/default_over_literal_fallback.rs rename to src/test/run-pass/default_ty_param_fallback_default_over_literal_fallback.rs diff --git a/src/test/run-pass/default_ty_param_dependent_defaults.rs b/src/test/run-pass/default_ty_param_fallback_dependent_defaults.rs similarity index 100% rename from src/test/run-pass/default_ty_param_dependent_defaults.rs rename to src/test/run-pass/default_ty_param_fallback_dependent_defaults.rs diff --git a/src/test/run-pass/default_ty_param_fallback_ensure_normalization.rs b/src/test/run-pass/default_ty_param_fallback_ensure_normalization.rs new file mode 100644 index 0000000000000..bc9e309563ef6 --- /dev/null +++ b/src/test/run-pass/default_ty_param_fallback_ensure_normalization.rs @@ -0,0 +1,16 @@ +// 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)] +#![feature(iter_arith)] + +fn main() { + print!("{}", (1..4).sum()); +} diff --git a/src/test/run-pass/default_ty_param_method_call_test.rs b/src/test/run-pass/default_ty_param_fallback_method_call_test.rs similarity index 100% rename from src/test/run-pass/default_ty_param_method_call_test.rs rename to src/test/run-pass/default_ty_param_fallback_method_call_test.rs diff --git a/src/test/run-pass/default_ty_param_struct.rs b/src/test/run-pass/default_ty_param_fallback_struct.rs similarity index 100% rename from src/test/run-pass/default_ty_param_struct.rs rename to src/test/run-pass/default_ty_param_fallback_struct.rs diff --git a/src/test/run-pass/default_ty_param_struct_and_type_alias.rs b/src/test/run-pass/default_ty_param_fallback_struct_and_type_alias.rs similarity index 100% rename from src/test/run-pass/default_ty_param_struct_and_type_alias.rs rename to src/test/run-pass/default_ty_param_fallback_struct_and_type_alias.rs diff --git a/src/test/run-pass/default_ty_param_trait_impl.rs b/src/test/run-pass/default_ty_param_fallback_trait_impl.rs similarity index 100% rename from src/test/run-pass/default_ty_param_trait_impl.rs rename to src/test/run-pass/default_ty_param_fallback_trait_impl.rs diff --git a/src/test/run-pass/default_ty_param_trait_impl_simple.rs b/src/test/run-pass/default_ty_param_fallback_trait_impl_simple.rs similarity index 100% rename from src/test/run-pass/default_ty_param_trait_impl_simple.rs rename to src/test/run-pass/default_ty_param_fallback_trait_impl_simple.rs diff --git a/src/test/run-pass/default_ty_param_type_alias.rs b/src/test/run-pass/default_ty_param_fallback_type_alias.rs similarity index 100% rename from src/test/run-pass/default_ty_param_type_alias.rs rename to src/test/run-pass/default_ty_param_fallback_type_alias.rs From bbcc6655785a501d01042277979d30ef4945391e Mon Sep 17 00:00:00 2001 From: Jared Roesch Date: Wed, 16 Sep 2015 13:50:19 -0700 Subject: [PATCH 7/7] Rebase fixes --- src/librustc/middle/ty/error.rs | 2 +- src/librustc_typeck/check/mod.rs | 34 ++++++++------------------------ 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/librustc/middle/ty/error.rs b/src/librustc/middle/ty/error.rs index 72c4366c5bcd5..a4cbb19055f14 100644 --- a/src/librustc/middle/ty/error.rs +++ b/src/librustc/middle/ty/error.rs @@ -57,7 +57,7 @@ pub enum TypeError<'tcx> { ConvergenceMismatch(ExpectedFound), ProjectionNameMismatched(ExpectedFound), ProjectionBoundsLength(ExpectedFound), - TyParamDefaultMismatch(ExpectedFound>) + TyParamDefaultMismatch(ExpectedFound>) } #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)] diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 02e0ebaf7983f..9331ca6c48e90 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1767,17 +1767,18 @@ 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::error::UnconstrainedNumeric::Neither; - use middle::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat}; - for ty in &self.infcx().unsolved_variables() { + use middle::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat, Neither}; + let unsolved_variables = self.infcx().candidates_for_defaulting(); + for &(ref ty, _) in &unsolved_variables { let resolved = self.infcx().resolve_type_vars_if_possible(ty); - if self.infcx().type_var_diverges(resolved) { + let diverges = self.infcx().type_var_diverges(resolved); + if diverges { demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().mk_nil()); } else { - match self.infcx().type_is_unconstrained_numeric(resolved) { + let unconstrained = + self.infcx().type_is_unconstrained_numeric(resolved); + match unconstrained { UnconstrainedInt => { demand::eqtype(self, codemap::DUMMY_SP, *ty, self.tcx().types.i32) }, @@ -1809,10 +1810,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { use middle::ty::error::UnconstrainedNumeric::Neither; use middle::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat}; - // 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(); - // It is a possible 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()) { @@ -2067,21 +2064,6 @@ impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum LvaluePreference { - PreferMutLvalue, - NoPreference -} - -impl LvaluePreference { - pub fn from_mutbl(m: ast::Mutability) -> Self { - match m { - ast::MutMutable => PreferMutLvalue, - ast::MutImmutable => NoPreference, - } - } -} - /// Whether `autoderef` requires types to resolve. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum UnresolvedTypeAction {