Skip to content

Commit 0bd87ef

Browse files
committed
Adjust fallback algorithm.
We only demand equality when we are sure now, fixing bad error messages. We also skip a priority level if we detect a conflict, an improvement over the previous algorithm. But there is a bug related to normalizing dependent defaults, see FIXME.
1 parent 5362bbe commit 0bd87ef

File tree

10 files changed

+119
-57
lines changed

10 files changed

+119
-57
lines changed

src/librustc/infer/type_variable.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ pub enum Default<'tcx> {
9292
None,
9393
}
9494

95+
impl<'tcx> Default<'tcx> {
96+
pub fn get_user(&self) -> Option<UserDefault<'tcx>> {
97+
match *self {
98+
Default::User(ref user_default) => Some(user_default.clone()),
99+
Default::None => None,
100+
_ => bug!("Default exists but is not user"),
101+
}
102+
}
103+
}
104+
95105
// We will use this to store the required information to recapitulate
96106
// what happened when an error occurs.
97107
#[derive(Clone, Debug, PartialEq, Eq, Hash)]

src/librustc_typeck/check/mod.rs

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
9090
use rustc_back::slice::ref_slice;
9191
use namespace::Namespace;
9292
use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
93-
use rustc::infer::type_variable::{TypeVariableOrigin, Default};
93+
use rustc::infer::type_variable::TypeVariableOrigin;
9494
use rustc::middle::region;
9595
use rustc::ty::subst::{Kind, Subst, Substs};
9696
use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
@@ -2185,13 +2185,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
21852185
/// will have `String` as its default.
21862186
///
21872187
/// Adding a default to a type parameter that has none should be backwards compatible.
2188-
/// However if we get conflicting defaults we do not know how to resolve them.
2189-
/// Therefore we must future-proof against such a conflict by not allowing
2190-
/// type variables with defaults to unify with type variables without defaults.
2188+
/// Therefore we take care to future-proof against conflicting defaults.
21912189
///
2192-
/// We may come up with rules to prioritize a type parameter over another.
2193-
/// When unifying type variables, the highest priority parameter involved
2194-
/// has the final say on what the default should be.
21952190
/// Currently we prioritize defaults from impls and fns over defaults in types,
21962191
/// this for example allows `fn foo<T=String>(x: Option<T>)` to work
21972192
/// even though `Option<T>` has no default for `T`.
@@ -2215,10 +2210,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
22152210
bags.entry(root).or_insert(Vec::new()).push(vid);
22162211
}
22172212
}
2218-
// A bag will successfuly fallback to a default if all of it's variables
2219-
// have a default, and that default is the same.
2220-
// Low priority variables will be ignored in the presence of higher priority variables.
2221-
'bags: for (_, mut bag) in bags.into_iter() {
2213+
2214+
// Attempt to find a fallback for each bag.
2215+
for (_, bag) in bags {
22222216
// Partition the bag by the origin of the type param.
22232217
let (fn_or_impl, ty_def) = bag.into_iter().partition(|&v| {
22242218
match self.infcx.type_variables.borrow().var_origin(v) {
@@ -2228,37 +2222,53 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
22282222
_ => bug!("type var does not support fallback")
22292223
}
22302224
});
2231-
// Params from fns or impls take priority, if they exist ignore the rest of the bag.
2232-
bag = fn_or_impl;
2233-
if bag.is_empty() {
2234-
bag = ty_def;
2235-
}
2236-
// For future-proofing against conflicting defaults,
2237-
// if one of the variables has no default, nothing is unified.
2238-
for &vid in &bag {
2239-
let default = self.infcx.type_variables.borrow().default(vid).clone();
2240-
debug!("apply_user_type_parameter_fallback: \
2241-
checking var: {:?} with default: {:?}", vid, default);
2242-
match default {
2243-
Default::User(_) => {},
2244-
_ => continue 'bags,
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;
22452234
}
2246-
}
2247-
for &vid in &bag {
2248-
// If future-proof, then all vars have defaults.
2249-
let default = self.infcx.type_variables.borrow().default(vid).clone();
2250-
let ty = self.tcx.mk_var(vid);
2251-
debug!("apply_user_type_parameter_fallback: applying fallback to var: {:?} \
2252-
with ty: {:?} with default: {:?}", vid, ty, default);
2253-
if let Default::User(user_default) = default {
2254-
let normalized_default = self.normalize_associated_types_in(
2255-
user_default.origin_span,
2256-
&user_default.ty);
2257-
// QUESTION(leodasvacas):
2258-
// This will emit "expected type mismatch" on conflicting defaults, which is bad.
2259-
// I'd be happy to somehow detect or rollback this demand on conflict defaults
2260-
// so we would emit "type annotations needed" instead.
2261-
self.demand_eqtype(user_default.origin_span, &ty, normalized_default);
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;
22622272
}
22632273
}
22642274
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(default_type_parameter_fallback)]
12+
13+
use std::path::Path;
14+
use std::mem::size_of;
15+
16+
enum Opt<T=String> {
17+
Som(T),
18+
Non,
19+
}
20+
21+
fn main() {
22+
// func1 and func2 cannot agree so we apply the fallback from the type.
23+
let x = Opt::Non;
24+
func1(&x);
25+
func2(&x);
26+
}
27+
28+
// Defaults on fns take precedence.
29+
fn func1<P: AsRef<Path> = &'static str>(_: &Opt<P>) {
30+
// Testing that we got String.
31+
assert_eq!(size_of::<P>(), size_of::<String>())
32+
}
33+
34+
fn func2<P: AsRef<Path> = &'static &'static str>(_: &Opt<P>) {
35+
// Testing that we got String.
36+
assert_eq!(size_of::<P>(), size_of::<String>())
37+
}

src/test/ui/default-ty-param-fallback/bad_messages/fallback_conflict.stderr

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/test/ui/default-ty-param-fallback/bad_messages/fallback_conflict_cross_crate.stderr

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0282]: type annotations needed
2+
--> $DIR/fallback_conflict.rs:22:9
3+
|
4+
22 | let x = foo();
5+
| ^
6+
| |
7+
| cannot infer type for `_`
8+
| consider giving `x` a type
9+
10+
error: aborting due to previous error
11+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error[E0282]: type annotations needed
2+
--> $DIR/fallback_conflict_cross_crate.rs:22:15
3+
|
4+
22 | let foo = bleh();
5+
| --- ^^^^ cannot infer type for `A`
6+
| |
7+
| consider giving `foo` a type
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)