Skip to content

Commit babfa76

Browse files
committed
Get normalization of dependent defaults right.
Since this means there are cross-bag dependencies, we use a fixed point algorithm, looping over the bags until we can no longer solve any of them.
1 parent 0bd87ef commit babfa76

File tree

3 files changed

+97
-65
lines changed

3 files changed

+97
-65
lines changed

src/librustc/infer/type_variable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ pub enum Default<'tcx> {
9393
}
9494

9595
impl<'tcx> Default<'tcx> {
96-
pub fn get_user(&self) -> Option<UserDefault<'tcx>> {
96+
pub fn as_user(&self) -> Option<UserDefault<'tcx>> {
9797
match *self {
9898
Default::User(ref user_default) => Some(user_default.clone()),
9999
Default::None => None,

src/librustc_typeck/check/mod.rs

Lines changed: 89 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ use std::rc::Rc;
113113
use std::collections::hash_map::Entry;
114114
use std::cmp;
115115
use std::fmt::Display;
116+
use std::iter::FromIterator;
116117
use std::mem::replace;
117118
use std::ops::{self, Deref};
118119
use syntax::abi::Abi;
@@ -2001,6 +2002,27 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
20012002
value)
20022003
}
20032004

2005+
fn eager_normalize_in<T>(&self, span: Span, value: &T) -> Option<T>
2006+
where T : TypeFoldable<'tcx>
2007+
{
2008+
let infer_ok = self.inh.partially_normalize_associated_types_in(span,
2009+
self.body_id,
2010+
self.param_env,
2011+
value);
2012+
if infer_ok.obligations.is_empty() {
2013+
Some(infer_ok.value)
2014+
} else {
2015+
self.inh.register_predicates(infer_ok.obligations);
2016+
self.select_obligations_where_possible();
2017+
let resolved = self.resolve_type_vars_if_possible(&infer_ok.value);
2018+
if !resolved.needs_infer() {
2019+
Some(resolved)
2020+
} else {
2021+
None
2022+
}
2023+
}
2024+
}
2025+
20042026
pub fn require_type_meets(&self,
20052027
ty: Ty<'tcx>,
20062028
span: Span,
@@ -2210,67 +2232,76 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
22102232
bags.entry(root).or_insert(Vec::new()).push(vid);
22112233
}
22122234
}
2213-
2235+
// Discard key, bags is a sequence of pairs (bag, done).
2236+
let mut bags = Vec::from_iter(bags.into_iter().map(|b| b.1).map(|b| (b, false)));
22142237
// Attempt to find a fallback for each bag.
2215-
for (_, bag) in bags {
2216-
// Partition the bag by the origin of the type param.
2217-
let (fn_or_impl, ty_def) = bag.into_iter().partition(|&v| {
2218-
match self.infcx.type_variables.borrow().var_origin(v) {
2219-
TypeParameterDefinition(_, _, OriginOfTyParam::Fn) |
2220-
TypeParameterDefinition(_, _, OriginOfTyParam::Impl) => true,
2221-
TypeParameterDefinition(_, _, OriginOfTyParam::TyDef) => false,
2222-
_ => bug!("type var does not support fallback")
2223-
}
2224-
});
2225-
// Params from fns or impls have higher priority than those from type definitions.
2226-
// We consider the priority levels in order and stop at the first success or failure:
2227-
// We succeed if all defaults exist and agree.
2228-
// We fail if existing defaults agree but there are missing defaults.
2229-
// We continue if the priority level is empty or if there are any disagreements.
2230-
let priority_levels: Vec<Vec<ty::TyVid>> = vec![fn_or_impl, ty_def];
2231-
'priority_levels: for priority_level in priority_levels {
2232-
if priority_level.is_empty() {
2233-
continue;
2234-
}
2235-
let normalized_default = |&vid| {
2236-
let default = self.infcx.type_variables.borrow().default(vid).get_user();
2237-
default.map(|d| self.normalize_associated_types_in(d.origin_span, &d.ty))
2238-
};
2239-
let mut existing_defaults = priority_level.iter().filter_map(&normalized_default);
2240-
let equivalent_default = match existing_defaults.next() {
2241-
Some(default) => default,
2242-
None => break, // Failed, no params have defaults at this level.
2243-
};
2244-
2245-
// If there are conflicting defaults, skip this level.
2246-
// FIXME(leodasvacas): In a case like `impl<X: Default=u32, Y = <X as Id>::This>`,
2247-
// as found in the test "dependent_associated_type.rs",
2248-
// if `Y` is normalized before the default of `X` is applied,
2249-
// then it's normalized to `TyInfer`, so we use a fragile workaround here.
2250-
// If it is `Y = Vec<<X as Id>::This>` we are in trouble again.
2251-
// Type equality that considers all inference variables equal is a correct fix.
2252-
if existing_defaults.any(|default| match (&default.sty, &equivalent_default.sty) {
2253-
(&ty::TyInfer(_), &ty::TyInfer(_)) => false,
2254-
(ref a, ref b) => **a != **b
2255-
})
2256-
{
2257-
debug!("apply_user_type_parameter_fallback: skipping priority level");
2258-
continue;
2259-
}
2260-
// All existing defaults agree, but for future-proofing
2261-
// we must fail if there is a param with no default.
2262-
if priority_level.iter().any(|vid| normalized_default(vid) == None) {
2263-
break;
2264-
}
2265-
// All defaults exist and agree, apply the default and succeed.
2266-
for &vid in &priority_level {
2267-
let ty = self.tcx.mk_var(vid);
2268-
debug!("apply_user_type_parameter_fallback: applying fallback to var: {:?} \
2269-
with ty: {:?} with default: {:?}", vid, ty, equivalent_default);
2270-
self.demand_eqtype(syntax_pos::DUMMY_SP, &ty, equivalent_default);
2271-
break 'priority_levels;
2238+
loop {
2239+
// For dependent defaults, solving a bag
2240+
// might allow us to normalize an associated type in another bag.
2241+
// For example in `impl<X = u32, Y = <X as Trait>::Type>`.
2242+
// Therefore we loop over the bags until we are at a fixed point.
2243+
let mut fixed_point = true;
2244+
// Loop over bags that are not done.
2245+
'bags: for &mut(ref bag, ref mut done) in bags.iter_mut().filter(|bag| !bag.1) {
2246+
// Partition the bag by the origin of the type param.
2247+
let (fn_or_impl, ty_def) = bag.iter().partition(|&&v| {
2248+
match self.infcx.type_variables.borrow().var_origin(v) {
2249+
TypeParameterDefinition(_, _, OriginOfTyParam::Fn) |
2250+
TypeParameterDefinition(_, _, OriginOfTyParam::Impl) => true,
2251+
TypeParameterDefinition(_, _, OriginOfTyParam::TyDef) => false,
2252+
_ => bug!("type var does not support fallback")
2253+
}
2254+
});
2255+
// Params from fns or impls have higher priority than those from type definitions.
2256+
// Consider the priority levels in order:
2257+
// Try again later if a default can't be normalized.
2258+
// Succeed if all defaults exist and agree.
2259+
// Fail if existing defaults agree but there are missing defaults.
2260+
// Skip the priority level if it's empty or there are disagreements.
2261+
let priority_levels: Vec<Vec<ty::TyVid>> = vec![fn_or_impl, ty_def];
2262+
'priority_levels: for priority_level in priority_levels {
2263+
if priority_level.is_empty() {
2264+
continue;
2265+
}
2266+
let get_default = |&v| self.infcx.type_variables.borrow().default(v).as_user();
2267+
let mut existing_defaults = Vec::new();
2268+
for default in priority_level.iter().filter_map(&get_default) {
2269+
match self.eager_normalize_in(default.origin_span, &default.ty) {
2270+
Some(def) => existing_defaults.push(def),
2271+
None => continue 'bags, // Try again later.
2272+
}
2273+
}
2274+
let mut existing_defaults = existing_defaults.iter();
2275+
let equivalent_default = match existing_defaults.next() {
2276+
Some(default) => default,
2277+
None => break, // Failed, no params have defaults at this level.
2278+
};
2279+
// On conflicting defaults, skip this level.
2280+
if existing_defaults.any(|&default| default.sty != equivalent_default.sty) {
2281+
debug!("apply_user_type_parameter_fallback: skipping priority level");
2282+
continue;
2283+
}
2284+
// All existing defaults agree, but for future-proofing
2285+
// we must fail if there is a param with no default.
2286+
if priority_level.iter().any(|vid| get_default(vid) == None) {
2287+
break;
2288+
}
2289+
// All defaults exist and agree, apply the default and succeed.
2290+
for &vid in &priority_level {
2291+
let ty = self.tcx.mk_var(vid);
2292+
self.demand_eqtype(syntax_pos::DUMMY_SP, &ty, equivalent_default);
2293+
debug!("apply_user_type_parameter_fallback: applied fallback to var: {:?} \
2294+
with ty: {:?} with default: {:?}", vid, ty, equivalent_default);
2295+
// Progress was made.
2296+
fixed_point = false;
2297+
*done = true;
2298+
break 'priority_levels;
2299+
}
22722300
}
22732301
}
2302+
if fixed_point {
2303+
break;
2304+
}
22742305
}
22752306
}
22762307

src/test/run-pass/default-ty-param-fallback/dependent_associated_type.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@
1414
use std::marker::PhantomData;
1515

1616
trait Id {
17-
type This;
17+
type Me;
1818
}
1919

2020
impl<A> Id for A {
21-
type This = A;
21+
type Me = A;
2222
}
2323

24-
struct Foo<X, Y> {
25-
data: PhantomData<(X, Y)>
24+
struct Foo<X, Y, Z, W> {
25+
data: PhantomData<(X, Y, Z, W)>,
2626
}
2727

28-
impl<X: Default = usize, Y = <X as Id>::This> Foo<X, Y> {
29-
fn new() -> Foo<X, Y> {
28+
impl<X: Default = u32, Y = <X as Id>::Me, Z = <Y as Id>::Me, W = Vec<<X as Id>::Me>>
29+
Foo<X, Y, Z, W> {
30+
fn new() -> Foo<X, Y, Z, W> {
3031
Foo { data: PhantomData }
3132
}
3233
}

0 commit comments

Comments
 (0)