Skip to content

Commit 423de71

Browse files
committed
Fallback: Stop relying on sty equality, use rollbacks.
I tried preventing having to rollback inference and being clever with sty equality, but dependent defaults means we need full inference here. All application of defaults goes in the same transaction, again because of dependent defaults. We can't mess with the fulfillment context in a snapshot, so we clone our own fulfillment context. The downside is that if we fail one bag we fail all, which can be very confusing as the user may be presented with several errors when only one of them is the "real one". This is not so bad because we may easily have a heuristic that solves the bags that can be solved in isolation and then puts the complicated cases in a single transaction, making the pathological situation quite rare.
1 parent 14a9420 commit 423de71

28 files changed

+205
-85
lines changed

src/librustc/traits/fulfill.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
4242
/// along. Once all type inference constraints have been generated, the
4343
/// method `select_all_or_error` can be used to report any remaining
4444
/// ambiguous cases as errors.
45-
45+
#[derive(Clone)]
4646
pub struct FulfillmentContext<'tcx> {
4747
// A list of all obligations that have been registered with this
4848
// fulfillment context.

src/librustc_data_structures/obligation_forest/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub trait ObligationProcessor {
5757
where I: Clone + Iterator<Item=&'c Self::Obligation>;
5858
}
5959

60+
#[derive(Clone)]
6061
pub struct ObligationForest<O: ForestObligation> {
6162
/// The list of obligations. In between calls to
6263
/// `process_obligations`, this list only contains nodes in the
@@ -81,7 +82,7 @@ pub struct ObligationForest<O: ForestObligation> {
8182
scratch: Option<Vec<usize>>,
8283
}
8384

84-
#[derive(Debug)]
85+
#[derive(Clone, Debug)]
8586
struct Node<O> {
8687
obligation: O,
8788
state: Cell<NodeState>,

src/librustc_typeck/check/mod.rs

Lines changed: 36 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ 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;
117116
use std::mem::replace;
118117
use std::ops::{self, Deref};
119118
use syntax::abi::Abi;
@@ -2002,27 +2001,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
20022001
value)
20032002
}
20042003

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-
20262004
pub fn require_type_meets(&self,
20272005
ty: Ty<'tcx>,
20282006
span: Span,
@@ -2233,20 +2211,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
22332211
bags.entry(root).or_insert(Vec::new()).push(vid);
22342212
}
22352213
}
2236-
// Discard key, bags is a sequence of pairs (bag, done).
2237-
let mut bags = Vec::from_iter(bags.into_iter().map(|b| b.1).map(|b| (b, false)));
2238-
// Attempt to find a fallback for each bag.
2239-
loop {
2240-
// For dependent defaults, solving a bag
2241-
// might allow us to normalize an associated type in another bag.
2242-
// For example in `impl<X = u32, Y = <X as Trait>::Type>`.
2243-
// Therefore we loop over the bags until we are at a fixed point.
2244-
let mut fixed_point = true;
2245-
// Loop over bags that are not done.
2246-
'bags: for &mut(ref bag, ref mut done) in bags.iter_mut().filter(|bag| !bag.1) {
2214+
// We put everything in a single transaction
2215+
// because dependent defaults create cross-bag dependencies.
2216+
// This is bad in that we may report errors
2217+
// for things that actually were successfully inferred.
2218+
let _ = self.commit_if_ok(|_| { self.save_and_restore_in_snapshot_flag(|infcx| {
2219+
// Clone the whole thing so we can use it for this snapshot.
2220+
let mut local_fullfilment = self.fulfillment_cx.borrow().clone();
2221+
// Attempt to find a fallback for each bag.
2222+
// - Fail if there are missing defaults.
2223+
// - Apply default if all default exist.
2224+
'bags: for bag in bags.into_iter().map(|b| b.1) {
22472225
// Partition the bag by the origin of the type param.
22482226
let (fn_or_impl, ty_def) = bag.iter().partition(|&&v| {
2249-
match self.infcx.type_variables.borrow().var_origin(v) {
2227+
match infcx.type_variables.borrow().var_origin(v) {
22502228
TypeParameterDefinition(_, _, OriginOfTyParam::Fn) |
22512229
TypeParameterDefinition(_, _, OriginOfTyParam::Impl) => true,
22522230
TypeParameterDefinition(_, _, OriginOfTyParam::TyDef) => false,
@@ -2259,42 +2237,40 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
22592237
if bag.is_empty() {
22602238
bag = ty_def;
22612239
}
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();
2240+
let get_default = |&v| infcx.type_variables.borrow().default(v).as_user();
22662241
let mut normalized_defaults = Vec::new();
2267-
for default in bag.iter().map(&get_default) {
2268-
if let Some(default) = default {
2269-
match self.eager_normalize_in(default.origin_span, &default.ty) {
2270-
Some(default) => normalized_defaults.push(default),
2271-
None => continue 'bags, // Try again later.
2242+
for (&vid, default) in bag.iter().zip(bag.iter().map(&get_default)) {
2243+
if let Some(d) = default {
2244+
let infr_ok = self.normalize_associated_types_in_as_infer_ok(d.origin_span,
2245+
&d.ty);
2246+
normalized_defaults.push((vid, infr_ok.value));
2247+
for obligation in infr_ok.obligations {
2248+
local_fullfilment.register_predicate_obligation(infcx, obligation);
22722249
}
22732250
} else {
2274-
// Fail, missing default.
2275-
*done = true;
2276-
continue 'bags;
2251+
continue 'bags; // Fail, missing default.
22772252
}
22782253
}
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 {
2254+
// All defaults exist, apply them.
2255+
for (vid, default) in normalized_defaults {
22882256
let ty = self.tcx.mk_var(vid);
2289-
self.demand_eqtype(syntax_pos::DUMMY_SP, &ty, equivalent_default);
2257+
let cause = self.misc(syntax_pos::DUMMY_SP);
2258+
if let Ok(infer_ok) = self.at(&cause, self.param_env).sub(default, ty) {
2259+
for obligation in infer_ok.obligations {
2260+
local_fullfilment.register_predicate_obligation(infcx, obligation);
2261+
}
2262+
}
22902263
debug!("apply_user_type_parameter_fallback: applied fallback to var: {:?} \
2291-
with ty: {:?} with default: {:?}", vid, ty, equivalent_default);
2264+
with ty: {:?} with default: {:?}", vid, ty, default);
22922265
}
22932266
}
2294-
if fixed_point {
2295-
break;
2267+
// Rollback on any conflict.
2268+
if local_fullfilment.select_where_possible(self).is_err() {
2269+
Err(())
2270+
} else {
2271+
Ok(())
22962272
}
2297-
}
2273+
})});
22982274
}
22992275

23002276
// Implements type inference fallback algorithm
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
12+
#![feature(default_type_parameter_fallback)]
13+
14+
use std::marker::PhantomData;
15+
16+
#[derive(Copy, Clone)]
17+
enum Opt<T> {
18+
Som(T),
19+
Non,
20+
}
21+
22+
trait Id {
23+
type Me;
24+
}
25+
26+
impl<A> Id for A {
27+
type Me = A;
28+
}
29+
30+
struct Foo<X, Y, Z> {
31+
data: PhantomData<(X, Y, Z)>,
32+
}
33+
34+
impl<X: Default = u32, Y = <X as Id>::Me, Z = <Y as Id>::Me>
35+
Foo<X, Y, Z> {
36+
fn new(_: Opt<X>, _: Opt<Y>, _: Opt<Z>) -> Foo<X, Y, Z> {
37+
Foo { data: PhantomData }
38+
}
39+
}
40+
41+
fn main() {
42+
let a = Opt::Non;
43+
let _ = Foo::new(a, a, a);
44+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
// compile-flags: --error-format=human
1011

1112
#![feature(default_type_parameter_fallback)]
1213

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
12+
// file at the top-level directory of this distribution and at
13+
// http://rust-lang.org/COPYRIGHT.
14+
//
15+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
16+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
17+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
18+
// option. This file may not be copied, modified, or distributed
19+
// except according to those terms.
20+
21+
#![feature(default_type_parameter_fallback)]
22+
23+
#[derive(Copy, Clone)]
24+
enum Opt<T=String> {
25+
Som(T),
26+
Non,
27+
}
28+
29+
fn main() {
30+
let a = Opt::Non;
31+
let b = Opt::Non;
32+
func1(a, b);
33+
func2(b, a);
34+
35+
let c = Opt::Non;
36+
let d = Opt::Non;
37+
func1(c, d);
38+
func2(c, d);
39+
}
40+
41+
fn func1<X = u32, Y = X>(_: Opt<X>, _: Opt<Y>) {
42+
}
43+
44+
fn func2<X = u32, Y = X>(_: Opt<X>, _: Opt<Y>) {
45+
}

src/test/ui/default-ty-param-fallback/default_dependent_associated_type.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10-
//
10+
// compile-flags: --error-format=human
1111

1212
#![feature(default_type_parameter_fallback)]
1313

src/test/ui/default-ty-param-fallback/dependent_defaults.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10-
//
10+
// compile-flags: --error-format=human
1111

1212
#![feature(default_type_parameter_fallback)]
1313
use std::marker::PhantomData;

src/test/ui/default-ty-param-fallback/dont_skip_conflict.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
// compile-flags: --error-format=human
1011

1112
#![feature(default_type_parameter_fallback)]
1213

src/test/ui/default-ty-param-fallback/dont_skip_conflict.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/dont_skip_conflict.rs:26:13
2+
--> $DIR/dont_skip_conflict.rs:27:13
33
|
4-
26 | let x = Opt::Non;
4+
27 | let x = Opt::Non;
55
| - ^^^^^^^^ cannot infer type for `T`
66
| |
77
| consider giving `x` a type

src/test/ui/default-ty-param-fallback/fallback_conflict.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
// compile-flags: --error-format=human
1011

1112
#![feature(default_type_parameter_fallback)]
1213

@@ -18,9 +19,6 @@ fn foo<F:Default=usize>() -> F { F::default() }
1819
fn bar<B:Debug=isize>(b: B) { println!("{:?}", b); }
1920

2021
fn main() {
21-
// Here, F is instantiated with $0=uint
2222
let x = foo();
23-
24-
// Here, B is instantiated with $1=uint, and constraint $0 <: $1 is added.
2523
bar(x);
2624
}

src/test/ui/default-ty-param-fallback/fallback_conflict_cross_crate.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010
//
11-
//aux-build:default_ty_param_cross_crate_crate.rs
11+
// aux-build:default_ty_param_cross_crate_crate.rs
12+
// compile-flags: --error-format=human
1213

1314
#![feature(default_type_parameter_fallback)]
1415

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/fallback_conflict_cross_crate.rs:22:15
2+
--> $DIR/fallback_conflict_cross_crate.rs:23:15
33
|
4-
22 | let foo = bleh();
4+
23 | let foo = bleh();
55
| --- ^^^^ cannot infer type for `A`
66
| |
77
| consider giving `foo` a type

src/test/ui/default-ty-param-fallback/feature_gate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
// compile-flags: --error-format=human
1011

1112
enum Opt<T=String> {
1213
Som(T),

src/test/ui/default-ty-param-fallback/feature_gate.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/feature_gate.rs:17:5
2+
--> $DIR/feature_gate.rs:18:5
33
|
4-
17 | Opt::Non;
4+
18 | Opt::Non;
55
| ^^^^^^^^ cannot infer type for `T`
66

77
error: aborting due to previous error

src/test/ui/default-ty-param-fallback/future_proof.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
// compile-flags: --error-format=human
1011

1112
#![feature(default_type_parameter_fallback)]
1213

src/test/ui/default-ty-param-fallback/future_proof.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/future_proof.rs:24:5
2+
--> $DIR/future_proof.rs:25:5
33
|
4-
24 | func(noner());
4+
25 | func(noner());
55
| ^^^^ cannot infer type for `P`
66

77
error: aborting due to previous error

src/test/ui/default-ty-param-fallback/method_call_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
10+
// compile-flags: --error-format=human
1011

1112
#![feature(default_type_parameter_fallback)]
1213

src/test/ui/default-ty-param-fallback/method_call_test.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/method_call_test.rs:22:9
2+
--> $DIR/method_call_test.rs:23:9
33
|
4-
22 | let f = Foo.method();
4+
23 | let f = Foo.method();
55
| ^
66
| |
77
| cannot infer type for `_`

0 commit comments

Comments
 (0)