Skip to content

Commit 14a9420

Browse files
committed
Fallback: Don't skip conflicting defaults.
While this was future-proof wrt API evolution, it is not future proof wrt to compiler evolution, such as making priority levels more fine-grained or just fixing bugs. It's a not very useful behavior and a complexity this feature doesn't need. Refactored the fallback algorithm to be simpler. Just fails on conflicting defaults.
1 parent babfa76 commit 14a9420

File tree

4 files changed

+71
-50
lines changed

4 files changed

+71
-50
lines changed

src/librustc_typeck/check/mod.rs

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,9 +2209,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
22092209
/// Adding a default to a type parameter that has none should be backwards compatible.
22102210
/// Therefore we take care to future-proof against conflicting defaults.
22112211
///
2212-
/// Currently we prioritize defaults from impls and fns over defaults in types,
2212+
/// Currently we prioritize params from impls and fns over params in types,
22132213
/// this for example allows `fn foo<T=String>(x: Option<T>)` to work
22142214
/// even though `Option<T>` has no default for `T`.
2215+
/// Lower priority defaults are considered when there are no higher priority params involved.
22152216
fn apply_user_type_parameter_fallback(&self) {
22162217
use self::TypeVariableOrigin::TypeParameterDefinition;
22172218
use ty::OriginOfTyParam;
@@ -2253,51 +2254,42 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
22532254
}
22542255
});
22552256
// 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) {
2257+
// The first non-empty priority level is considered.
2258+
let mut bag: Vec<ty::TyVid> = fn_or_impl;
2259+
if bag.is_empty() {
2260+
bag = ty_def;
2261+
}
2262+
// - Try again later if a default can't be normalized.
2263+
// - Fail if there are conflicting or missing defaults.
2264+
// - Succeess if all defaults exist and agree.
2265+
let get_default = |&v| self.infcx.type_variables.borrow().default(v).as_user();
2266+
let mut normalized_defaults = Vec::new();
2267+
for default in bag.iter().map(&get_default) {
2268+
if let Some(default) = default {
22692269
match self.eager_normalize_in(default.origin_span, &default.ty) {
2270-
Some(def) => existing_defaults.push(def),
2270+
Some(default) => normalized_defaults.push(default),
22712271
None => continue 'bags, // Try again later.
22722272
}
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;
2273+
} else {
2274+
// Fail, missing default.
22972275
*done = true;
2298-
break 'priority_levels;
2276+
continue 'bags;
22992277
}
23002278
}
2279+
let equivalent_default = normalized_defaults[0];
2280+
// Fail on conflicting defaults.
2281+
if normalized_defaults.iter().any(|&d| d.sty != equivalent_default.sty) {
2282+
break;
2283+
}
2284+
// All defaults exist and agree, success, apply the default.
2285+
fixed_point = false;
2286+
*done = true;
2287+
for &vid in &bag {
2288+
let ty = self.tcx.mk_var(vid);
2289+
self.demand_eqtype(syntax_pos::DUMMY_SP, &ty, equivalent_default);
2290+
debug!("apply_user_type_parameter_fallback: applied fallback to var: {:?} \
2291+
with ty: {:?} with default: {:?}", vid, ty, equivalent_default);
2292+
}
23012293
}
23022294
if fixed_point {
23032295
break;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2017 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+
type Strang = &'static str;
14+
15+
fn main() {
16+
let a = None;
17+
func1(a);
18+
func2(a);
19+
}
20+
21+
// Defaults on fns take precedence.
22+
fn func1<P = &'static str>(_: Option<P>) {}
23+
fn func2<P = Strang>(_: Option<P>) {}

src/test/run-pass/default-ty-param-fallback/skip_conflict.rs renamed to src/test/ui/default-ty-param-fallback/dont_skip_conflict.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,15 @@ enum Opt<T=String> {
1919
}
2020

2121
fn main() {
22-
// func1 and func2 cannot agree so we apply the fallback from the type.
22+
// func1 and func2 cannot agree so we fail.
23+
// NB: While it would be future-proof wrt API evolution to use the `String` default in `Opt`,
24+
// that is not future proof wrt to compiler evolution,
25+
// such as making priority levels more fine-grained or just fixing bugs.
2326
let x = Opt::Non;
2427
func1(&x);
2528
func2(&x);
2629
}
2730

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-
}
31+
fn func1<P: AsRef<Path> = &'static str>(_: &Opt<P>) { }
3332

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-
}
33+
fn func2<P: AsRef<Path> = &'static &'static str>(_: &Opt<P>) { }
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/dont_skip_conflict.rs:26:13
3+
|
4+
26 | let x = Opt::Non;
5+
| - ^^^^^^^^ cannot infer type for `T`
6+
| |
7+
| consider giving `x` a type
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)