From 4a0a0e949a077a6d83ca152daa404ff47c9c1dcf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Mar 2017 21:29:22 -0500 Subject: [PATCH 01/19] remove type variable defaults code This just limits ourselves to the "old school" defaults: diverging variables and integer variables. --- src/librustc_typeck/check/mod.rs | 211 +----------------- .../compile-fail/default_ty_param_conflict.rs | 33 --- .../default_ty_param_conflict_cross_crate.rs | 32 --- ...param_default_dependent_associated_type.rs | 36 --- .../default_ty_param_dependent_defaults.rs | 19 -- .../default_ty_param_method_call_test.rs | 24 -- src/test/run-pass/default_ty_param_struct.rs | 23 -- .../default_ty_param_struct_and_type_alias.rs | 40 ---- .../run-pass/default_ty_param_trait_impl.rs | 25 --- .../default_ty_param_trait_impl_simple.rs | 26 --- .../run-pass/default_ty_param_type_alias.rs | 19 -- 11 files changed, 3 insertions(+), 485 deletions(-) delete mode 100644 src/test/compile-fail/default_ty_param_conflict.rs delete mode 100644 src/test/compile-fail/default_ty_param_conflict_cross_crate.rs delete mode 100644 src/test/run-pass/default_ty_param_default_dependent_associated_type.rs delete mode 100644 src/test/run-pass/default_ty_param_dependent_defaults.rs delete mode 100644 src/test/run-pass/default_ty_param_method_call_test.rs delete mode 100644 src/test/run-pass/default_ty_param_struct.rs delete mode 100644 src/test/run-pass/default_ty_param_struct_and_type_alias.rs delete mode 100644 src/test/run-pass/default_ty_param_trait_impl.rs delete mode 100644 src/test/run-pass/default_ty_param_trait_impl_simple.rs delete mode 100644 src/test/run-pass/default_ty_param_type_alias.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c995b7e92843d..d300552af2caf 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -88,7 +88,7 @@ use hir::def::{Def, CtorKind}; use hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_back::slice::ref_slice; use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; -use rustc::infer::type_variable::{self, TypeVariableOrigin}; +use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::ty::subst::{Kind, Subst, Substs}; use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; use rustc::ty::{ParamTy, ParameterEnvironment}; @@ -105,7 +105,7 @@ use session::{Session, CompileResult}; use TypeAndSubsts; use lint; use util::common::{ErrorReported, indenter}; -use util::nodemap::{DefIdMap, FxHashMap, FxHashSet, NodeMap}; +use util::nodemap::{DefIdMap, FxHashMap, NodeMap}; use std::cell::{Cell, RefCell}; use std::cmp; @@ -1978,218 +1978,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } + // Implements type inference fallback algorithm fn select_all_obligations_and_apply_defaults(&self) { - if self.tcx.sess.features.borrow().default_type_parameter_fallback { - self.new_select_all_obligations_and_apply_defaults(); - } else { - self.old_select_all_obligations_and_apply_defaults(); - } - } - - // Implements old type inference fallback algorithm - fn old_select_all_obligations_and_apply_defaults(&self) { self.select_obligations_where_possible(); self.default_type_parameters(); self.select_obligations_where_possible(); } - fn new_select_all_obligations_and_apply_defaults(&self) { - use rustc::ty::error::UnconstrainedNumeric::Neither; - use rustc::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.type_variables.clone(); - - // There is a possibility 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 - // has made it possible to make more progress. - self.select_obligations_where_possible(); - - let mut conflicts = Vec::new(); - - // Collect all unsolved type, integral and floating point variables. - let unsolved_variables = self.unsolved_variables(); - - // 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: FxHashMap, _> = - unsolved_variables - .iter() - .filter_map(|t| self.default(t).map(|d| (*t, d))) - .collect(); - - let mut unbound_tyvars = FxHashSet(); - - debug!("select_all_obligations_and_apply_defaults: defaults={:?}", default_map); - - // We loop over the unsolved variables, resolving them and if they are - // and unconstrainted numeric type we add them to the set of unbound - // variables. We do this so we only apply literal fallback to type - // variables without defaults. - for ty in &unsolved_variables { - let resolved = self.resolve_type_vars_if_possible(ty); - if self.type_var_diverges(resolved) { - self.demand_eqtype(syntax_pos::DUMMY_SP, *ty, - self.tcx.mk_diverging_default()); - } else { - match self.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.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(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) => { - unbound_tyvars.insert(ty); - if unbound_tyvars.contains(resolved) { - unbound_tyvars.remove(resolved); - } - } - - _ => {} - } - } - } - - // If there are no more fallbacks to apply at this point we have applied all possible - // defaults and type inference will proceed as normal. - if unbound_tyvars.is_empty() { - break; - } - - // Finally we go 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. - - let _ = self.commit_if_ok(|_: &infer::CombinedSnapshot| { - conflicts.extend( - self.apply_defaults_and_return_conflicts(&unbound_tyvars, &default_map, None) - ); - - // If there are conflicts we rollback, otherwise commit - if conflicts.len() > 0 { - Err(()) - } else { - Ok(()) - } - }); - - // 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 { - let conflicting_default = - self.apply_defaults_and_return_conflicts( - &unbound_tyvars, - &default_map, - Some(conflict) - ) - .last() - .map(|(_, tv)| tv) - .unwrap_or(type_variable::Default { - ty: self.next_ty_var( - TypeVariableOrigin::MiscVariable(syntax_pos::DUMMY_SP)), - origin_span: syntax_pos::DUMMY_SP, - // what do I put here? - def_id: self.tcx.hir.local_def_id(ast::CRATE_NODE_ID) - }); - - // 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. - let (first_default, second_default) = - if default.def_id < conflicting_default.def_id { - (default, conflicting_default) - } else { - (conflicting_default, default) - }; - - - self.report_conflicting_default_types( - first_default.origin_span, - self.body_id, - first_default, - second_default) - } - } - - self.select_obligations_where_possible(); - } - - // For use in error handling related to default type parameter fallback. We explicitly - // apply the default that caused conflict first to a local version of the type variable - // table then apply defaults until we find a conflict. That default must be the one - // that caused conflict earlier. - fn apply_defaults_and_return_conflicts<'b>( - &'b self, - unbound_vars: &'b FxHashSet>, - default_map: &'b FxHashMap, type_variable::Default<'tcx>>, - conflict: Option>, - ) -> impl Iterator, type_variable::Default<'tcx>)> + 'b { - use rustc::ty::error::UnconstrainedNumeric::Neither; - use rustc::ty::error::UnconstrainedNumeric::{UnconstrainedInt, UnconstrainedFloat}; - - conflict.into_iter().chain(unbound_vars.iter().cloned()).flat_map(move |ty| { - if self.type_var_diverges(ty) { - self.demand_eqtype(syntax_pos::DUMMY_SP, ty, - self.tcx.mk_diverging_default()); - } else { - match self.type_is_unconstrained_numeric(ty) { - UnconstrainedInt => { - self.demand_eqtype(syntax_pos::DUMMY_SP, ty, self.tcx.types.i32) - }, - UnconstrainedFloat => { - self.demand_eqtype(syntax_pos::DUMMY_SP, ty, self.tcx.types.f64) - }, - Neither => { - if let Some(default) = default_map.get(ty) { - let default = default.clone(); - let default_ty = self.normalize_associated_types_in( - default.origin_span, &default.ty); - match self.eq_types(false, - &self.misc(default.origin_span), - ty, - default_ty) { - Ok(ok) => self.register_infer_ok_obligations(ok), - Err(_) => { - return Some((ty, default)); - } - } - } - } - } - } - - None - }) - } - fn select_all_obligations_or_error(&self) { debug!("select_all_obligations_or_error"); diff --git a/src/test/compile-fail/default_ty_param_conflict.rs b/src/test/compile-fail/default_ty_param_conflict.rs deleted file mode 100644 index 8cde239ca6edf..0000000000000 --- a/src/test/compile-fail/default_ty_param_conflict.rs +++ /dev/null @@ -1,33 +0,0 @@ -// 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)] - -use std::fmt::Debug; - -// Example from the RFC -fn foo() -> F { F::default() } -//~^ NOTE: a default was defined here... - -fn bar(b: B) { println!("{:?}", b); } -//~^ NOTE: a second default was defined here... - -fn main() { - // Here, F is instantiated with $0=uint - let x = foo(); - //~^ ERROR: mismatched types - //~| NOTE: conflicting type parameter defaults `usize` and `isize` - //~| NOTE: conflicting type parameter defaults `usize` and `isize` - //~| NOTE: ...that was applied to an unconstrained type variable here - - // Here, B is instantiated with $1=uint, and constraint $0 <: $1 is added. - bar(x); - //~^ NOTE: ...that also applies to the same type variable here -} diff --git a/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs b/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs deleted file mode 100644 index e5b035e50aa93..0000000000000 --- a/src/test/compile-fail/default_ty_param_conflict_cross_crate.rs +++ /dev/null @@ -1,32 +0,0 @@ -// 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. -// -//aux-build:default_ty_param_cross_crate_crate.rs - -#![feature(default_type_parameter_fallback)] - -extern crate default_param_test; - -use default_param_test::{Foo, bleh}; - -fn meh(x: Foo) {} -//~^ NOTE: a default was defined here... - -fn main() { - let foo = bleh(); - //~^ NOTE: ...that also applies to the same type variable here - - meh(foo); - //~^ ERROR: mismatched types - //~| NOTE: conflicting type parameter defaults `bool` and `char` - //~| NOTE: conflicting type parameter defaults `bool` and `char` - //~| a second default is defined on `default_param_test::bleh` - //~| NOTE: ...that was applied to an unconstrained type variable here -} diff --git a/src/test/run-pass/default_ty_param_default_dependent_associated_type.rs b/src/test/run-pass/default_ty_param_default_dependent_associated_type.rs deleted file mode 100644 index 8fc2c2e6bce70..0000000000000 --- a/src/test/run-pass/default_ty_param_default_dependent_associated_type.rs +++ /dev/null @@ -1,36 +0,0 @@ -// 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)] - -use std::marker::PhantomData; - -trait Id { - type This; -} - -impl Id for A { - type This = A; -} - -struct Foo::This> { - data: PhantomData<(X, Y)> -} - -impl Foo { - fn new() -> Foo { - Foo { data: PhantomData } - } -} - -fn main() { - let foo = Foo::new(); -} diff --git a/src/test/run-pass/default_ty_param_dependent_defaults.rs b/src/test/run-pass/default_ty_param_dependent_defaults.rs deleted file mode 100644 index ac833d0f54744..0000000000000 --- a/src/test/run-pass/default_ty_param_dependent_defaults.rs +++ /dev/null @@ -1,19 +0,0 @@ -// 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)] -use std::marker::PhantomData; - -struct Foo { t: T, data: PhantomData } - -fn main() { - let foo = Foo { t: 'a', data: PhantomData }; -} diff --git a/src/test/run-pass/default_ty_param_method_call_test.rs b/src/test/run-pass/default_ty_param_method_call_test.rs deleted file mode 100644 index e8d93092ec53d..0000000000000 --- a/src/test/run-pass/default_ty_param_method_call_test.rs +++ /dev/null @@ -1,24 +0,0 @@ -// 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)] - -struct Foo; - -impl Foo { - fn method(&self) -> A { - A::default() - } -} - -fn main() { - let f = Foo.method(); - println!("{}", f); -} diff --git a/src/test/run-pass/default_ty_param_struct.rs b/src/test/run-pass/default_ty_param_struct.rs deleted file mode 100644 index d9ac51fc23b02..0000000000000 --- a/src/test/run-pass/default_ty_param_struct.rs +++ /dev/null @@ -1,23 +0,0 @@ -// 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)] - -struct Foo(A); - -impl Foo { - fn new() -> Foo { - Foo(A::default()) - } -} - -fn main() { - let foo = Foo::new(); -} diff --git a/src/test/run-pass/default_ty_param_struct_and_type_alias.rs b/src/test/run-pass/default_ty_param_struct_and_type_alias.rs deleted file mode 100644 index d3bdab9082e32..0000000000000 --- a/src/test/run-pass/default_ty_param_struct_and_type_alias.rs +++ /dev/null @@ -1,40 +0,0 @@ -// 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)] - -use std::marker::PhantomData; - -pub struct DeterministicHasher; -pub struct RandomHasher; - - -pub struct MyHashMap { - data: PhantomData<(K, V, H)> -} - -impl MyHashMap { - fn new() -> MyHashMap { - MyHashMap { data: PhantomData } - } -} - -mod mystd { - use super::{MyHashMap, RandomHasher}; - pub type HashMap = MyHashMap; -} - -fn try_me(hash_map: mystd::HashMap) {} - -fn main() { - let hash_map = mystd::HashMap::new(); - try_me(hash_map); -} diff --git a/src/test/run-pass/default_ty_param_trait_impl.rs b/src/test/run-pass/default_ty_param_trait_impl.rs deleted file mode 100644 index c67d3a49aff3d..0000000000000 --- a/src/test/run-pass/default_ty_param_trait_impl.rs +++ /dev/null @@ -1,25 +0,0 @@ -// 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)] - -// Another example from the RFC -trait Foo { } -trait Bar { } - -impl Foo for Vec {} -impl Bar for usize {} - -fn takes_foo(f: F) {} - -fn main() { - let x = Vec::new(); // x: Vec<$0> - takes_foo(x); // adds oblig Vec<$0> : Foo -} diff --git a/src/test/run-pass/default_ty_param_trait_impl_simple.rs b/src/test/run-pass/default_ty_param_trait_impl_simple.rs deleted file mode 100644 index 067ad524922c0..0000000000000 --- a/src/test/run-pass/default_ty_param_trait_impl_simple.rs +++ /dev/null @@ -1,26 +0,0 @@ -// 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)] - -// An example from the RFC -trait Foo { fn takes_foo(&self); } -trait Bar { } - -impl Foo for Vec { - fn takes_foo(&self) {} -} - -impl Bar for usize {} - -fn main() { - let x = Vec::new(); // x: Vec<$0> - x.takes_foo(); // adds oblig Vec<$0> : Foo -} diff --git a/src/test/run-pass/default_ty_param_type_alias.rs b/src/test/run-pass/default_ty_param_type_alias.rs deleted file mode 100644 index 1b4747406d0c6..0000000000000 --- a/src/test/run-pass/default_ty_param_type_alias.rs +++ /dev/null @@ -1,19 +0,0 @@ -// 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)] - -use std::collections::HashMap; - -type IntMap = HashMap; - -fn main() { - let x = IntMap::new(); -} From 18ea55fe1636001c4d030911e2c4c165af1cabfc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Mar 2017 13:30:09 -0500 Subject: [PATCH 02/19] remove bivariance There is one fishy part of these changes: when computing the LUB/GLB of a "bivariant" type parameter, I currently return the `a` value. Bivariant type parameters are only allowed in a very particular situation, where the type parameter is only used as an associated type output, like this: ```rust pub struct Foo where A: Fn() -> B { data: A } ``` In principle, if one had `T=Foo` and `U=Foo` and (e.g.) `A: for<'a> Fn() -> &'a u32`, then I think that computing the LUB of `T` and `U` might do the wrong thing. Probably the right behavior is just to create a fresh type variable. However, that particular example would not compile (because the where-clause is illegal; `'a` does not appear in any input type). I was not able to make an example that *would* compile and demonstrate this shortcoming, and handling the LUB/GLB was mildly inconvenient, so I left it as is. I am considering whether to revisit this. --- src/librustc/infer/bivariate.rs | 123 -------------------------------- src/librustc/infer/combine.rs | 7 +- src/librustc/infer/glb.rs | 2 +- src/librustc/infer/lub.rs | 2 +- src/librustc/infer/mod.rs | 1 - src/librustc/infer/sub.rs | 2 +- 6 files changed, 4 insertions(+), 133 deletions(-) delete mode 100644 src/librustc/infer/bivariate.rs diff --git a/src/librustc/infer/bivariate.rs b/src/librustc/infer/bivariate.rs deleted file mode 100644 index 4acb8b807d594..0000000000000 --- a/src/librustc/infer/bivariate.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2014 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. - -//! Applies the "bivariance relationship" to two types and/or regions. -//! If (A,B) are bivariant then either A <: B or B <: A. It occurs -//! when type/lifetime parameters are unconstrained. Usually this is -//! an error, but we permit it in the specific case where a type -//! parameter is constrained in a where-clause via an associated type. -//! -//! There are several ways one could implement bivariance. You could -//! just do nothing at all, for example, or you could fully verify -//! that one of the two subtyping relationships hold. We choose to -//! thread a middle line: we relate types up to regions, but ignore -//! all region relationships. -//! -//! At one point, handling bivariance in this fashion was necessary -//! for inference, but I'm actually not sure if that is true anymore. -//! In particular, it might be enough to say (A,B) are bivariant for -//! all (A,B). - -use super::combine::CombineFields; -use super::type_variable::{BiTo}; - -use ty::{self, Ty, TyCtxt}; -use ty::TyVar; -use ty::relate::{Relate, RelateResult, TypeRelation}; - -pub struct Bivariate<'combine, 'infcx: 'combine, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> { - fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>, - a_is_expected: bool, -} - -impl<'combine, 'infcx, 'gcx, 'tcx> Bivariate<'combine, 'infcx, 'gcx, 'tcx> { - pub fn new(fields: &'combine mut CombineFields<'infcx, 'gcx, 'tcx>, a_is_expected: bool) - -> Bivariate<'combine, 'infcx, 'gcx, 'tcx> - { - Bivariate { fields: fields, a_is_expected: a_is_expected } - } -} - -impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> - for Bivariate<'combine, 'infcx, 'gcx, 'tcx> -{ - fn tag(&self) -> &'static str { "Bivariate" } - - fn tcx(&self) -> TyCtxt<'infcx, 'gcx, 'tcx> { self.fields.tcx() } - - fn a_is_expected(&self) -> bool { self.a_is_expected } - - fn relate_with_variance>(&mut self, - variance: ty::Variance, - a: &T, - b: &T) - -> RelateResult<'tcx, T> - { - match variance { - // If we have Foo and Foo is invariant w/r/t A, - // and we want to assert that - // - // Foo <: Foo || - // Foo <: Foo - // - // then still A must equal B. - ty::Invariant => self.relate(a, b), - - ty::Covariant => self.relate(a, b), - ty::Bivariant => self.relate(a, b), - ty::Contravariant => self.relate(a, b), - } - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("{}.tys({:?}, {:?})", self.tag(), - a, b); - if a == b { return Ok(a); } - - let infcx = self.fields.infcx; - let a = infcx.type_variables.borrow_mut().replace_if_possible(a); - let b = infcx.type_variables.borrow_mut().replace_if_possible(b); - match (&a.sty, &b.sty) { - (&ty::TyInfer(TyVar(a_id)), &ty::TyInfer(TyVar(b_id))) => { - infcx.type_variables.borrow_mut().relate_vars(a_id, BiTo, b_id); - Ok(a) - } - - (&ty::TyInfer(TyVar(a_id)), _) => { - self.fields.instantiate(b, BiTo, a_id, self.a_is_expected)?; - Ok(a) - } - - (_, &ty::TyInfer(TyVar(b_id))) => { - self.fields.instantiate(a, BiTo, b_id, self.a_is_expected)?; - Ok(a) - } - - _ => { - self.fields.infcx.super_combine_tys(self, a, b) - } - } - } - - fn regions(&mut self, a: &'tcx ty::Region, _: &'tcx ty::Region) - -> RelateResult<'tcx, &'tcx ty::Region> { - Ok(a) - } - - fn binders(&mut self, a: &ty::Binder, b: &ty::Binder) - -> RelateResult<'tcx, ty::Binder> - where T: Relate<'tcx> - { - let a1 = self.tcx().erase_late_bound_regions(a); - let b1 = self.tcx().erase_late_bound_regions(b); - let c = self.relate(&a1, &b1)?; - Ok(ty::Binder(c)) - } -} diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 5d33d6e6d2e71..9430421f91014 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -32,7 +32,6 @@ // is also useful to track which value is the "expected" value in // terms of error reporting. -use super::bivariate::Bivariate; use super::equate::Equate; use super::glb::Glb; use super::lub::Lub; @@ -159,10 +158,6 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { Equate::new(self, a_is_expected) } - pub fn bivariate<'a>(&'a mut self, a_is_expected: bool) -> Bivariate<'a, 'infcx, 'gcx, 'tcx> { - Bivariate::new(self, a_is_expected) - } - pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'gcx, 'tcx> { Sub::new(self, a_is_expected) } @@ -251,7 +246,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { // to associate causes/spans with each of the relations in // the stack to get this right. match dir { - BiTo => self.bivariate(a_is_expected).relate(&a_ty, &b_ty), + BiTo => Ok(a_ty), EqTo => self.equate(a_is_expected).relate(&a_ty, &b_ty), SubtypeOf => self.sub(a_is_expected).relate(&a_ty, &b_ty), SupertypeOf => self.sub(a_is_expected).relate_with_variance( diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index 8ccadc6b2af04..8c167e0a8ac91 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -49,7 +49,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> match variance { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), - ty::Bivariant => self.fields.bivariate(self.a_is_expected).relate(a, b), + ty::Bivariant => Ok(a.clone()), ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b), } } diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 89571dea10c34..28ae1ae556b0b 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -49,7 +49,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> match variance { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), - ty::Bivariant => self.fields.bivariate(self.a_is_expected).relate(a, b), + ty::Bivariant => Ok(a.clone()), ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b), } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index b07ef4dfd448e..98c8ce0f03144 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -48,7 +48,6 @@ use self::region_inference::{RegionVarBindings, RegionSnapshot}; use self::type_variable::TypeVariableOrigin; use self::unify_key::ToType; -mod bivariate; mod combine; mod equate; pub mod error_reporting; diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index dae30ea97c80d..a6b0e02d47722 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -65,7 +65,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> match variance { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), - ty::Bivariant => self.fields.bivariate(self.a_is_expected).relate(a, b), + ty::Bivariant => Ok(a.clone()), ty::Contravariant => self.with_expected_switched(|this| { this.relate(b, a) }), } } From 58609ef879e604d161f4ee6c612d6d127120e289 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 9 Mar 2017 21:47:09 -0500 Subject: [PATCH 03/19] add Subtype predicate --- src/librustc/ich/impls_ty.rs | 4 +++ src/librustc/infer/mod.rs | 39 ++++++++++++++++++++++- src/librustc/middle/free_region.rs | 1 + src/librustc/traits/error_reporting.rs | 23 +++++++++++++ src/librustc/traits/fulfill.rs | 21 ++++++++++++ src/librustc/traits/object_safety.rs | 2 ++ src/librustc/traits/select.rs | 12 +++++++ src/librustc/traits/structural_impls.rs | 1 + src/librustc/traits/util.rs | 9 +++++- src/librustc/ty/mod.rs | 17 ++++++++++ src/librustc/ty/structural_impls.rs | 35 ++++++++++++++++++-- src/librustc/ty/util.rs | 1 + src/librustc/ty/wf.rs | 5 +++ src/librustc/util/ppaux.rs | 14 ++++++++ src/librustc_typeck/check/closure.rs | 1 + src/librustc_typeck/check/method/probe.rs | 1 + src/librustdoc/clean/mod.rs | 11 +++++++ 17 files changed, 193 insertions(+), 4 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 7b6f3af2a11ec..b8888eee9c6ef 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -170,6 +170,7 @@ impl_stable_hash_for!(enum ty::Visibility { impl_stable_hash_for!(struct ty::TraitRef<'tcx> { def_id, substs }); impl_stable_hash_for!(struct ty::TraitPredicate<'tcx> { trait_ref }); impl_stable_hash_for!(tuple_struct ty::EquatePredicate<'tcx> { t1, t2 }); +impl_stable_hash_for!(struct ty::SubtypePredicate<'tcx> { a_is_expected, a, b }); impl<'a, 'tcx, A, B> HashStable> for ty::OutlivesPredicate where A: HashStable>, @@ -200,6 +201,9 @@ impl<'a, 'tcx> HashStable> for ty::Predicate<'tcx ty::Predicate::Equate(ref pred) => { pred.hash_stable(hcx, hasher); } + ty::Predicate::Subtype(ref pred) => { + pred.hash_stable(hcx, hasher); + } ty::Predicate::RegionOutlives(ref pred) => { pred.hash_stable(hcx, hasher); } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 98c8ce0f03144..999ebbfa20fbf 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -551,7 +551,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { } impl ExpectedFound { - fn new(a_is_expected: bool, a: T, b: T) -> Self { + pub fn new(a_is_expected: bool, a: T, b: T) -> Self { if a_is_expected { ExpectedFound {expected: a, found: b} } else { @@ -1129,6 +1129,43 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { }) } + pub fn subtype_predicate(&self, + cause: &ObligationCause<'tcx>, + predicate: &ty::PolySubtypePredicate<'tcx>) + -> Option> + { + // Subtle: it's ok to skip the binder here and resolve because + // `shallow_resolve` just ignores anything that is not a type + // variable, and because type variable's can't (at present, at + // least) capture any of the things bound by this binder. + // + // Really, there is no *particular* reason to do this + // `shallow_resolve` here except as a + // micro-optimization. Naturally I could not + // resist. -nmatsakis + let two_unbound_type_vars = { + let a = self.shallow_resolve(predicate.skip_binder().a); + let b = self.shallow_resolve(predicate.skip_binder().b); + a.is_ty_var() && b.is_ty_var() + }; + + if two_unbound_type_vars { + // Two unbound type variables? Can't make progress. + return None; + } + + Some(self.commit_if_ok(|snapshot| { + let (ty::SubtypePredicate { a_is_expected, a, b}, skol_map) = + self.skolemize_late_bound_regions(predicate, snapshot); + + let cause_span = cause.span; + let ok = self.sub_types(a_is_expected, cause, a, b)?; + self.leak_check(false, cause_span, &skol_map, snapshot)?; + self.pop_skolemized(skol_map, snapshot); + Ok(ok.unit()) + })) + } + pub fn region_outlives_predicate(&self, cause: &traits::ObligationCause<'tcx>, predicate: &ty::PolyRegionOutlivesPredicate<'tcx>) diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index cdb081ab40098..963cc4314eda5 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -60,6 +60,7 @@ impl FreeRegionMap { ty::Predicate::Projection(..) | ty::Predicate::Trait(..) | ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | ty::Predicate::WellFormed(..) | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 931c77badad22..8a303a5da1184 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -39,6 +39,7 @@ use ty::error::ExpectedFound; use ty::fast_reject; use ty::fold::TypeFolder; use ty::subst::Subst; +use ty::SubtypePredicate; use util::nodemap::{FxHashMap, FxHashSet}; use syntax_pos::{DUMMY_SP, Span}; @@ -112,6 +113,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { FulfillmentErrorCode::CodeAmbiguity => { self.maybe_report_ambiguity(&error.obligation); } + FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { + self.report_mismatched_types(&error.obligation.cause, + expected_found.expected, + expected_found.found, + err.clone()) + .emit(); + } } } @@ -555,6 +563,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { err } + ty::Predicate::Subtype(ref predicate) => { + // TODO + panic!("subtype requirement not satisfied {:?}", predicate) + } + ty::Predicate::Equate(ref predicate) => { let predicate = self.resolve_type_vars_if_possible(predicate); let err = self.equality_predicate(&obligation.cause, @@ -761,6 +774,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + ty::Predicate::Subtype(ref data) => { + if data.references_error() || self.tcx.sess.has_errors() { + // no need to overload user in such cases + } else { + let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); + assert!(a.is_ty_var() && b.is_ty_var()); // else other would've been instantiated + self.need_type_info(obligation, a); + } + } + _ => { if !self.tcx.sess.has_errors() { let mut err = struct_span_err!(self.tcx.sess, diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index b87d18464377f..64453f2983b92 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -11,6 +11,7 @@ use dep_graph::DepGraph; use infer::{InferCtxt, InferOk}; use ty::{self, Ty, TypeFoldable, ToPolyTraitRef, TyCtxt, ToPredicate}; +use ty::error::ExpectedFound; use rustc_data_structures::obligation_forest::{ObligationForest, Error}; use rustc_data_structures::obligation_forest::{ForestObligation, ObligationProcessor}; use std::marker::PhantomData; @@ -496,6 +497,26 @@ fn process_predicate<'a, 'gcx, 'tcx>( s => Ok(s) } } + + ty::Predicate::Subtype(ref subtype) => { + match selcx.infcx().subtype_predicate(&obligation.cause, subtype) { + None => { + // none means that both are unresolved + pending_obligation.stalled_on = vec![subtype.skip_binder().a, + subtype.skip_binder().b]; + Ok(None) + } + Some(Ok(ok)) => { + Ok(Some(ok.obligations)) + } + Some(Err(err)) => { + let expected_found = ExpectedFound::new(subtype.skip_binder().a_is_expected, + subtype.skip_binder().a, + subtype.skip_binder().b); + Err(FulfillmentErrorCode::CodeSubtypeError(expected_found, err)) + } + } + } } } diff --git a/src/librustc/traits/object_safety.rs b/src/librustc/traits/object_safety.rs index 7cd0b26940d91..d190635bec306 100644 --- a/src/librustc/traits/object_safety.rs +++ b/src/librustc/traits/object_safety.rs @@ -178,6 +178,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) | ty::Predicate::ClosureKind(..) | + ty::Predicate::Subtype(..) | ty::Predicate::Equate(..) => { false } @@ -209,6 +210,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::Predicate::Projection(..) | ty::Predicate::Trait(..) | ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | ty::Predicate::RegionOutlives(..) | ty::Predicate::WellFormed(..) | ty::Predicate::ObjectSafe(..) | diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 38ea1e4a19b91..67d50210ba39a 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -568,6 +568,18 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { } } + ty::Predicate::Subtype(ref p) => { + // does this code ever run? + match self.infcx.subtype_predicate(&obligation.cause, p) { + Some(Ok(InferOk { obligations, .. })) => { + self.inferred_obligations.extend(obligations); + EvaluatedToOk + }, + Some(Err(_)) => EvaluatedToErr, + None => EvaluatedToAmbig, + } + } + ty::Predicate::WellFormed(ty) => { match ty::wf::obligations(self.infcx, obligation.cause.body_id, ty, obligation.cause.span) { diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 44ef461327ddb..fcaa29be9632c 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -130,6 +130,7 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { match *self { super::CodeSelectionError(ref e) => write!(f, "{:?}", e), super::CodeProjectionError(ref e) => write!(f, "{:?}", e), + super::CodeSubtypeError(ref a, ref b) => write!(f, "CodeSubtypeError({:?}, {:?})", a, b), super::CodeAmbiguity => write!(f, "Ambiguity") } } diff --git a/src/librustc/traits/util.rs b/src/librustc/traits/util.rs index 602f27a64d4d8..d4245ec9b2475 100644 --- a/src/librustc/traits/util.rs +++ b/src/librustc/traits/util.rs @@ -42,7 +42,10 @@ fn anonymize_predicate<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, ty::Predicate::ObjectSafe(data), ty::Predicate::ClosureKind(closure_def_id, kind) => - ty::Predicate::ClosureKind(closure_def_id, kind) + ty::Predicate::ClosureKind(closure_def_id, kind), + + ty::Predicate::Subtype(ref data) => + ty::Predicate::Subtype(tcx.anonymize_late_bound_regions(data)), } } @@ -160,6 +163,10 @@ impl<'cx, 'gcx, 'tcx> Elaborator<'cx, 'gcx, 'tcx> { // `X == Y`, though conceivably we might. For example, // `&X == &Y` implies that `X == Y`. } + ty::Predicate::Subtype(..) => { + // Currently, we do not "elaborate" predicates like `X + // <: Y`, though conceivably we might. + } ty::Predicate::Projection(..) => { // Nothing to elaborate in a projection predicate. } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 292e30e3d41f1..d720911db39fb 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -755,6 +755,9 @@ pub enum Predicate<'tcx> { /// for some substitutions `...` and T being a closure type. /// Satisfied (or refuted) once we know the closure's kind. ClosureKind(DefId, ClosureKind), + + /// `T1 <: T2` + Subtype(PolySubtypePredicate<'tcx>), } impl<'a, 'gcx, 'tcx> Predicate<'tcx> { @@ -833,6 +836,8 @@ impl<'a, 'gcx, 'tcx> Predicate<'tcx> { Predicate::Trait(ty::Binder(data.subst(tcx, substs))), Predicate::Equate(ty::Binder(ref data)) => Predicate::Equate(ty::Binder(data.subst(tcx, substs))), + Predicate::Subtype(ty::Binder(ref data)) => + Predicate::Subtype(ty::Binder(data.subst(tcx, substs))), Predicate::RegionOutlives(ty::Binder(ref data)) => Predicate::RegionOutlives(ty::Binder(data.subst(tcx, substs))), Predicate::TypeOutlives(ty::Binder(ref data)) => @@ -912,6 +917,14 @@ pub type PolyRegionOutlivesPredicate<'tcx> = PolyOutlivesPredicate<&'tcx ty::Reg &'tcx ty::Region>; pub type PolyTypeOutlivesPredicate<'tcx> = PolyOutlivesPredicate, &'tcx ty::Region>; +#[derive(Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +pub struct SubtypePredicate<'tcx> { + pub a_is_expected: bool, + pub a: Ty<'tcx>, + pub b: Ty<'tcx> +} +pub type PolySubtypePredicate<'tcx> = ty::Binder>; + /// This kind of predicate has no *direct* correspondent in the /// syntax, but it roughly corresponds to the syntactic forms: /// @@ -1025,6 +1038,9 @@ impl<'tcx> Predicate<'tcx> { ty::Predicate::Equate(ty::Binder(ref data)) => { vec![data.0, data.1] } + ty::Predicate::Subtype(ty::Binder(SubtypePredicate { a, b, a_is_expected: _ })) => { + vec![a, b] + } ty::Predicate::TypeOutlives(ty::Binder(ref data)) => { vec![data.0] } @@ -1061,6 +1077,7 @@ impl<'tcx> Predicate<'tcx> { } Predicate::Projection(..) | Predicate::Equate(..) | + Predicate::Subtype(..) | Predicate::RegionOutlives(..) | Predicate::WellFormed(..) | Predicate::ObjectSafe(..) | diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 9126600e3f653..a4466d7d84011 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -111,6 +111,18 @@ impl<'a, 'tcx> Lift<'tcx> for ty::EquatePredicate<'a> { } } +impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { + type Lifted = ty::SubtypePredicate<'tcx>; + fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) + -> Option> { + tcx.lift(&(self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a: a, + b: b, + }) + } +} + impl<'tcx, A: Copy+Lift<'tcx>, B: Copy+Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate { type Lifted = ty::OutlivesPredicate; fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option { @@ -167,6 +179,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::Predicate<'a> { ty::Predicate::Equate(ref binder) => { tcx.lift(binder).map(ty::Predicate::Equate) } + ty::Predicate::Subtype(ref binder) => { + tcx.lift(binder).map(ty::Predicate::Subtype) + } ty::Predicate::RegionOutlives(ref binder) => { tcx.lift(binder).map(ty::Predicate::RegionOutlives) } @@ -693,6 +708,8 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { ty::Predicate::Trait(a.fold_with(folder)), ty::Predicate::Equate(ref binder) => ty::Predicate::Equate(binder.fold_with(folder)), + ty::Predicate::Subtype(ref binder) => + ty::Predicate::Subtype(binder.fold_with(folder)), ty::Predicate::RegionOutlives(ref binder) => ty::Predicate::RegionOutlives(binder.fold_with(folder)), ty::Predicate::TypeOutlives(ref binder) => @@ -712,6 +729,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { match *self { ty::Predicate::Trait(ref a) => a.visit_with(visitor), ty::Predicate::Equate(ref binder) => binder.visit_with(visitor), + ty::Predicate::Subtype(ref binder) => binder.visit_with(visitor), ty::Predicate::RegionOutlives(ref binder) => binder.visit_with(visitor), ty::Predicate::TypeOutlives(ref binder) => binder.visit_with(visitor), ty::Predicate::Projection(ref binder) => binder.visit_with(visitor), @@ -776,8 +794,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::InstantiatedPredicates<'tcx> { impl<'tcx> TypeFoldable<'tcx> for ty::EquatePredicate<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::EquatePredicate(self.0.fold_with(folder), - self.1.fold_with(folder)) + ty::EquatePredicate(self.0.fold_with(folder), self.1.fold_with(folder)) } fn super_visit_with>(&self, visitor: &mut V) -> bool { @@ -785,6 +802,20 @@ impl<'tcx> TypeFoldable<'tcx> for ty::EquatePredicate<'tcx> { } } +impl<'tcx> TypeFoldable<'tcx> for ty::SubtypePredicate<'tcx> { + fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { + ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a: self.a.fold_with(folder), + b: self.b.fold_with(folder) + } + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + self.a.visit_with(visitor) || self.b.visit_with(visitor) + } +} + impl<'tcx> TypeFoldable<'tcx> for ty::TraitPredicate<'tcx> { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { ty::TraitPredicate { diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index fd8191303a9a6..2efefd750ae8a 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -312,6 +312,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { ty::Predicate::Projection(..) | ty::Predicate::Trait(..) | ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | ty::Predicate::WellFormed(..) | ty::Predicate::ObjectSafe(..) | ty::Predicate::ClosureKind(..) | diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index 8a5bd6862cf45..0b0e8a180cc36 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -94,6 +94,10 @@ pub fn predicate_obligations<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, } ty::Predicate::ClosureKind(..) => { } + ty::Predicate::Subtype(ref data) => { + wf.compute(data.skip_binder().a); // (*) + wf.compute(data.skip_binder().b); // (*) + } } wf.normalize() @@ -156,6 +160,7 @@ pub fn implied_bounds<'a, 'gcx, 'tcx>( match obligation.predicate { ty::Predicate::Trait(..) | ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | ty::Predicate::Projection(..) | ty::Predicate::ClosureKind(..) | ty::Predicate::ObjectSafe(..) => diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 6323f1dc0d4c4..2daf71d95addf 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -416,6 +416,7 @@ impl<'tcx> fmt::Debug for ty::Predicate<'tcx> { match *self { ty::Predicate::Trait(ref a) => write!(f, "{:?}", a), ty::Predicate::Equate(ref pair) => write!(f, "{:?}", pair), + ty::Predicate::Subtype(ref pair) => write!(f, "{:?}", pair), ty::Predicate::RegionOutlives(ref pair) => write!(f, "{:?}", pair), ty::Predicate::TypeOutlives(ref pair) => write!(f, "{:?}", pair), ty::Predicate::Projection(ref pair) => write!(f, "{:?}", pair), @@ -676,6 +677,12 @@ impl<'tcx> fmt::Display for ty::Binder> { } } +impl<'tcx> fmt::Display for ty::Binder> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) + } +} + impl<'tcx> fmt::Display for ty::Binder> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ty::tls::with(|tcx| in_binder(f, tcx, self, tcx.lift(self))) @@ -897,6 +904,12 @@ impl<'tcx> fmt::Display for ty::EquatePredicate<'tcx> { } } +impl<'tcx> fmt::Display for ty::SubtypePredicate<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} <: {}", self.a, self.b) + } +} + impl<'tcx> fmt::Debug for ty::TraitPredicate<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "TraitPredicate({:?})", @@ -949,6 +962,7 @@ impl<'tcx> fmt::Display for ty::Predicate<'tcx> { match *self { ty::Predicate::Trait(ref data) => write!(f, "{}", data), ty::Predicate::Equate(ref predicate) => write!(f, "{}", predicate), + ty::Predicate::Subtype(ref predicate) => write!(f, "{}", predicate), ty::Predicate::RegionOutlives(ref predicate) => write!(f, "{}", predicate), ty::Predicate::TypeOutlives(ref predicate) => write!(f, "{}", predicate), ty::Predicate::Projection(ref predicate) => write!(f, "{}", predicate), diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 51fbc5aab6cd1..78176b155691c 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -169,6 +169,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()), ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()), ty::Predicate::Equate(..) => None, + ty::Predicate::Subtype(..) => None, ty::Predicate::RegionOutlives(..) => None, ty::Predicate::TypeOutlives(..) => None, ty::Predicate::WellFormed(..) => None, diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 5b0418921563a..8071fe3cc2806 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -576,6 +576,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { } } ty::Predicate::Equate(..) | + ty::Predicate::Subtype(..) | ty::Predicate::Projection(..) | ty::Predicate::RegionOutlives(..) | ty::Predicate::WellFormed(..) | diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ac72d7d29a24c..1a194cd125462 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -875,6 +875,7 @@ impl<'a> Clean for ty::Predicate<'a> { match *self { Predicate::Trait(ref pred) => pred.clean(cx), Predicate::Equate(ref pred) => pred.clean(cx), + Predicate::Subtype(ref pred) => pred.clean(cx), Predicate::RegionOutlives(ref pred) => pred.clean(cx), Predicate::TypeOutlives(ref pred) => pred.clean(cx), Predicate::Projection(ref pred) => pred.clean(cx), @@ -904,6 +905,16 @@ impl<'tcx> Clean for ty::EquatePredicate<'tcx> { } } +impl<'tcx> Clean for ty::SubtypePredicate<'tcx> { + fn clean(&self, cx: &DocContext) -> WherePredicate { + let ty::SubtypePredicate { a_is_expected: _, a, b } = *self; + WherePredicate::EqPredicate { // TODO This is obviously wrong :P + lhs: a.clean(cx), + rhs: b.clean(cx) + } + } +} + impl<'tcx> Clean for ty::OutlivesPredicate<&'tcx ty::Region, &'tcx ty::Region> { fn clean(&self, cx: &DocContext) -> WherePredicate { let ty::OutlivesPredicate(ref a, ref b) = *self; From 4e4bdea0ae8b3b1995b002374db1a7b7639eb52d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Mar 2017 05:19:49 -0500 Subject: [PATCH 04/19] propagate sub-obligations better The most interesting place is the hinting mechanism; once we start having subtyping obligations, it's important to see those through. --- src/librustc_typeck/check/method/probe.rs | 16 +++++++-------- src/librustc_typeck/check/mod.rs | 25 ++++++++++++++++++++--- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 8071fe3cc2806..59dbbfe49f0a9 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -1149,19 +1149,16 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.probe(|_| { // First check that the self type can be related. - match self.sub_types(false, - &ObligationCause::dummy(), - self_ty, - probe.xform_self_ty) { - Ok(InferOk { obligations, value: () }) => { - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()) - } + let sub_obligations = match self.sub_types(false, + &ObligationCause::dummy(), + self_ty, + probe.xform_self_ty) { + Ok(InferOk { obligations, value: () }) => obligations, Err(_) => { debug!("--> cannot relate self-types"); return false; } - } + }; // If so, impls may carry other conditions (e.g., where // clauses) that must be considered. Make sure that those @@ -1200,6 +1197,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { // Evaluate those obligations to see if they might possibly hold. let mut all_true = true; for o in obligations.iter() + .chain(sub_obligations.iter()) .chain(norm_obligations.iter()) .chain(ref_obligations.iter()) { if !selcx.evaluate_obligation(o) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d300552af2caf..77213b5a7436f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -90,7 +90,7 @@ use rustc_back::slice::ref_slice; use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin}; use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::ty::subst::{Kind, Subst, Substs}; -use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal}; +use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode, Reveal}; use rustc::ty::{ParamTy, ParameterEnvironment}; use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue}; use rustc::ty::{self, Ty, TyCtxt, Visibility}; @@ -2552,11 +2552,30 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // No argument expectations are produced if unification fails. let origin = self.misc(call_span); let ures = self.sub_types(false, &origin, formal_ret, ret_ty); + // FIXME(#15760) can't use try! here, FromError doesn't default // to identity so the resulting type is not constrained. match ures { - Ok(ok) => self.register_infer_ok_obligations(ok), - Err(e) => return Err(e), + Ok(ok) => { + // Process any obligations locally as much as + // we can. We don't care if some things turn + // out unconstrained or ambiguous, as we're + // just trying to get hints here. + let result = self.save_and_restore_obligations_in_snapshot_flag(|_| { + let mut fulfill = FulfillmentContext::new(); + let ok = ok; // FIXME(#30046) + for obligation in ok.obligations { + fulfill.register_predicate_obligation(self, obligation); + } + fulfill.select_where_possible(self) + }); + + match result { + Ok(()) => { } + Err(_) => return Err(()), + } + } + Err(_) => return Err(()), } // Record all the argument types, with the substitutions From 105ec7e3bbbef50076f0e6963e8a9327bacf7b5f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 Mar 2017 05:21:27 -0500 Subject: [PATCH 05/19] use obligations to propagate sub-typing instead of the TV code --- src/librustc/infer/sub.rs | 25 +++++++++++++++++++++---- src/librustc/traits/mod.rs | 5 ++++- src/test/compile-fail/issue-7813.rs | 5 ++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index a6b0e02d47722..1a94f12973de8 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -12,8 +12,10 @@ use super::SubregionOrigin; use super::combine::CombineFields; use super::type_variable::{SubtypeOf, SupertypeOf}; +use traits::Obligation; use ty::{self, Ty, TyCtxt}; use ty::TyVar; +use ty::fold::TypeFoldable; use ty::relate::{Cause, Relate, RelateResult, TypeRelation}; use std::mem; @@ -79,10 +81,25 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> let a = infcx.type_variables.borrow_mut().replace_if_possible(a); let b = infcx.type_variables.borrow_mut().replace_if_possible(b); match (&a.sty, &b.sty) { - (&ty::TyInfer(TyVar(a_id)), &ty::TyInfer(TyVar(b_id))) => { - infcx.type_variables - .borrow_mut() - .relate_vars(a_id, SubtypeOf, b_id); + (&ty::TyInfer(TyVar(_)), &ty::TyInfer(TyVar(_))) => { + // Shouldn't have any LBR here, so we can safely put + // this under a binder below without fear of accidental + // capture. + assert!(!a.has_escaping_regions()); + assert!(!b.has_escaping_regions()); + + // can't make progress on `A <: B` if both A and B are + // type variables, so record an obligation. + self.fields.obligations.push( + Obligation::new( + self.fields.trace.cause.clone(), + ty::Predicate::Subtype( + ty::Binder(ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a, + b, + })))); + Ok(a) } (&ty::TyInfer(TyVar(a_id)), _) => { diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 47cbccdd2ab10..ea243d65881ea 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -20,7 +20,8 @@ use hir::def_id::DefId; use middle::free_region::FreeRegionMap; use ty::subst::Substs; use ty::{self, Ty, TyCtxt, TypeFoldable, ToPredicate}; -use infer::InferCtxt; +use ty::error::{ExpectedFound, TypeError}; +use infer::{InferCtxt}; use std::rc::Rc; use syntax::ast; @@ -214,6 +215,8 @@ pub struct FulfillmentError<'tcx> { pub enum FulfillmentErrorCode<'tcx> { CodeSelectionError(SelectionError<'tcx>), CodeProjectionError(MismatchedProjectionTypes<'tcx>), + CodeSubtypeError(ExpectedFound>, + TypeError<'tcx>), // always comes from a SubtypePredicate CodeAmbiguity, } diff --git a/src/test/compile-fail/issue-7813.rs b/src/test/compile-fail/issue-7813.rs index fdd89058fd397..662b9e894ba3a 100644 --- a/src/test/compile-fail/issue-7813.rs +++ b/src/test/compile-fail/issue-7813.rs @@ -10,7 +10,6 @@ fn main() { let v = &[]; - let it = v.iter(); //~ ERROR type annotations needed [E0282] - //~| NOTE cannot infer type for `T` - //~| NOTE consider giving `it` a type + //~^ NOTE consider giving `it` a type + let it = v.iter(); //~ ERROR cannot infer type for `_` } From e58e2b423dabc816ba5d9e2382373622cc07bc8d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Mar 2017 09:05:39 -0400 Subject: [PATCH 06/19] remove the subtyping relations from TypeVariable --- src/librustc/infer/combine.rs | 14 ++- src/librustc/infer/equate.rs | 9 +- src/librustc/infer/sub.rs | 7 +- src/librustc/infer/type_variable.rs | 144 ++++++---------------------- src/librustc/lib.rs | 1 + src/test/compile-fail/issue-7813.rs | 6 +- 6 files changed, 51 insertions(+), 130 deletions(-) diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 9430421f91014..825f279e78e4b 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -38,7 +38,6 @@ use super::lub::Lub; use super::sub::Sub; use super::InferCtxt; use super::{MiscVariable, TypeTrace}; -use super::type_variable::{RelationDir, BiTo, EqTo, SubtypeOf, SupertypeOf}; use ty::{IntType, UintType}; use ty::{self, Ty, TyCtxt}; @@ -59,6 +58,11 @@ pub struct CombineFields<'infcx, 'gcx: 'infcx+'tcx, 'tcx: 'infcx> { pub obligations: PredicateObligations<'tcx>, } +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub enum RelationDir { + SubtypeOf, SupertypeOf, EqTo +} + impl<'infcx, 'gcx, 'tcx> InferCtxt<'infcx, 'gcx, 'tcx> { pub fn super_combine_tys(&self, relation: &mut R, @@ -177,6 +181,8 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { a_is_expected: bool) -> RelateResult<'tcx, ()> { + use self::RelationDir::*; + // We use SmallVector here instead of Vec because this code is hot and // it's rare that the stack length exceeds 1. let mut stack = SmallVector::new(); @@ -224,7 +230,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { // Generalize type if necessary. let generalized_ty = match dir { EqTo => self.generalize(a_ty, b_vid, false), - BiTo | SupertypeOf | SubtypeOf => self.generalize(a_ty, b_vid, true), + SupertypeOf | SubtypeOf => self.generalize(a_ty, b_vid, true), }?; debug!("instantiate(a_ty={:?}, dir={:?}, \ b_vid={:?}, generalized_ty={:?})", @@ -232,8 +238,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { generalized_ty); self.infcx.type_variables .borrow_mut() - .instantiate_and_push( - b_vid, generalized_ty, &mut stack); + .instantiate(b_vid, generalized_ty); generalized_ty } }; @@ -246,7 +251,6 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { // to associate causes/spans with each of the relations in // the stack to get this right. match dir { - BiTo => Ok(a_ty), EqTo => self.equate(a_is_expected).relate(&a_ty, &b_ty), SubtypeOf => self.sub(a_is_expected).relate(&a_ty, &b_ty), SupertypeOf => self.sub(a_is_expected).relate_with_variance( diff --git a/src/librustc/infer/equate.rs b/src/librustc/infer/equate.rs index bf247acec5a2d..f620965ced845 100644 --- a/src/librustc/infer/equate.rs +++ b/src/librustc/infer/equate.rs @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use super::combine::CombineFields; +use super::combine::{CombineFields, RelationDir}; use super::{Subtype}; -use super::type_variable::{EqTo}; use ty::{self, Ty, TyCtxt}; use ty::TyVar; @@ -58,17 +57,17 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> let b = infcx.type_variables.borrow_mut().replace_if_possible(b); match (&a.sty, &b.sty) { (&ty::TyInfer(TyVar(a_id)), &ty::TyInfer(TyVar(b_id))) => { - infcx.type_variables.borrow_mut().relate_vars(a_id, EqTo, b_id); + infcx.type_variables.borrow_mut().equate(a_id, b_id); Ok(a) } (&ty::TyInfer(TyVar(a_id)), _) => { - self.fields.instantiate(b, EqTo, a_id, self.a_is_expected)?; + self.fields.instantiate(b, RelationDir::EqTo, a_id, self.a_is_expected)?; Ok(a) } (_, &ty::TyInfer(TyVar(b_id))) => { - self.fields.instantiate(a, EqTo, b_id, self.a_is_expected)?; + self.fields.instantiate(a, RelationDir::EqTo, b_id, self.a_is_expected)?; Ok(a) } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index 1a94f12973de8..f1de9b043e36f 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -9,8 +9,7 @@ // except according to those terms. use super::SubregionOrigin; -use super::combine::CombineFields; -use super::type_variable::{SubtypeOf, SupertypeOf}; +use super::combine::{CombineFields, RelationDir}; use traits::Obligation; use ty::{self, Ty, TyCtxt}; @@ -104,11 +103,11 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> } (&ty::TyInfer(TyVar(a_id)), _) => { self.fields - .instantiate(b, SupertypeOf, a_id, !self.a_is_expected)?; + .instantiate(b, RelationDir::SupertypeOf, a_id, !self.a_is_expected)?; Ok(a) } (_, &ty::TyInfer(TyVar(b_id))) => { - self.fields.instantiate(a, SubtypeOf, b_id, self.a_is_expected)?; + self.fields.instantiate(a, RelationDir::SubtypeOf, b_id, self.a_is_expected)?; Ok(a) } diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index 67f37e5f9272e..298b2a97d5f36 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -8,11 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::RelationDir::*; use self::TypeVariableValue::*; -use self::UndoEntry::*; use hir::def_id::{DefId}; -use syntax::util::small_vector::SmallVector; use syntax::ast; use syntax_pos::Span; use ty::{self, Ty}; @@ -55,7 +52,6 @@ struct TypeVariableData<'tcx> { enum TypeVariableValue<'tcx> { Known(Ty<'tcx>), Bounded { - relations: Vec, default: Option> } } @@ -76,33 +72,13 @@ pub struct Snapshot { eq_snapshot: ut::Snapshot, } -enum UndoEntry<'tcx> { - // The type of the var was specified. - SpecifyVar(ty::TyVid, Vec, Option>), - Relate(ty::TyVid, ty::TyVid), - RelateRange(ty::TyVid, usize), +struct Instantiate<'tcx> { + vid: ty::TyVid, + default: Option>, } struct Delegate<'tcx>(PhantomData<&'tcx ()>); -type Relation = (RelationDir, ty::TyVid); - -#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] -pub enum RelationDir { - SubtypeOf, SupertypeOf, EqTo, BiTo -} - -impl RelationDir { - fn opposite(self) -> RelationDir { - match self { - SubtypeOf => SupertypeOf, - SupertypeOf => SubtypeOf, - EqTo => EqTo, - BiTo => BiTo, - } - } -} - impl<'tcx> TypeVariableTable<'tcx> { pub fn new() -> TypeVariableTable<'tcx> { TypeVariableTable { @@ -111,10 +87,6 @@ impl<'tcx> TypeVariableTable<'tcx> { } } - fn relations<'a>(&'a mut self, a: ty::TyVid) -> &'a mut Vec { - relations(self.values.get_mut(a.index as usize)) - } - pub fn default(&self, vid: ty::TyVid) -> Option> { match &self.values.get(vid.index as usize).value { &Known(_) => None, @@ -130,68 +102,37 @@ impl<'tcx> TypeVariableTable<'tcx> { &self.values.get(vid.index as usize).origin } - /// Records that `a <: b`, `a :> b`, or `a == b`, depending on `dir`. + /// Records that `a == b`, depending on `dir`. /// /// Precondition: neither `a` nor `b` are known. - pub fn relate_vars(&mut self, a: ty::TyVid, dir: RelationDir, b: ty::TyVid) { - let a = self.root_var(a); - let b = self.root_var(b); - if a != b { - if dir == EqTo { - // a and b must be equal which we mark in the unification table - let root = self.eq_relations.union(a, b); - // In addition to being equal, all relations from the variable which is no longer - // the root must be added to the root so they are not forgotten as the other - // variable should no longer be referenced (other than to get the root) - let other = if a == root { b } else { a }; - let count = { - let (relations, root_relations) = if other.index < root.index { - let (pre, post) = self.values.split_at_mut(root.index as usize); - (relations(&mut pre[other.index as usize]), relations(&mut post[0])) - } else { - let (pre, post) = self.values.split_at_mut(other.index as usize); - (relations(&mut post[0]), relations(&mut pre[root.index as usize])) - }; - root_relations.extend_from_slice(relations); - relations.len() - }; - self.values.record(RelateRange(root, count)); - } else { - self.relations(a).push((dir, b)); - self.relations(b).push((dir.opposite(), a)); - self.values.record(Relate(a, b)); - } - } + pub fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { + debug_assert!(self.probe(a).is_none()); + debug_assert!(self.probe(b).is_none()); + self.eq_relations.union(a, b); } - /// Instantiates `vid` with the type `ty` and then pushes an entry onto `stack` for each of the - /// relations of `vid` to other variables. The relations will have the form `(ty, dir, vid1)` - /// where `vid1` is some other variable id. + /// Instantiates `vid` with the type `ty`. /// /// Precondition: `vid` must be a root in the unification table - pub fn instantiate_and_push( - &mut self, - vid: ty::TyVid, - ty: Ty<'tcx>, - stack: &mut SmallVector<(Ty<'tcx>, RelationDir, ty::TyVid)>) - { + /// and has not previously been instantiated. + pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) { debug_assert!(self.root_var(vid) == vid); - let old_value = { - let value_ptr = &mut self.values.get_mut(vid.index as usize).value; - mem::replace(value_ptr, Known(ty)) - }; + debug_assert!(self.probe(vid).is_none()); - let (relations, default) = match old_value { - Bounded { relations, default } => (relations, default), - Known(_) => bug!("Asked to instantiate variable that is \ - already instantiated") + let old_value = { + let vid_data = &mut self.values[vid.index as usize]; + mem::replace(&mut vid_data.value, TypeVariableValue::Known(ty)) }; - for &(dir, vid) in &relations { - stack.push((ty, dir, vid)); + match old_value { + TypeVariableValue::Bounded { default } => { + self.values.record(Instantiate { vid: vid, default: default }); + } + TypeVariableValue::Known(old_ty) => { + bug!("instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}", + vid, ty, old_ty) + } } - - self.values.record(SpecifyVar(vid, relations, default)); } pub fn new_var(&mut self, @@ -201,7 +142,7 @@ impl<'tcx> TypeVariableTable<'tcx> { debug!("new_var(diverging={:?}, origin={:?})", diverging, origin); self.eq_relations.new_key(()); let index = self.values.push(TypeVariableData { - value: Bounded { relations: vec![], default: default }, + value: Bounded { default: default }, origin: origin, diverging: diverging }); @@ -298,7 +239,7 @@ impl<'tcx> TypeVariableTable<'tcx> { debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold); } - sv::UndoLog::Other(SpecifyVar(vid, ..)) => { + sv::UndoLog::Other(Instantiate { vid, .. }) => { if vid.index < new_elem_threshold { // quick check to see if this variable was // created since the snapshot started or not. @@ -334,35 +275,12 @@ impl<'tcx> TypeVariableTable<'tcx> { impl<'tcx> sv::SnapshotVecDelegate for Delegate<'tcx> { type Value = TypeVariableData<'tcx>; - type Undo = UndoEntry<'tcx>; - - fn reverse(values: &mut Vec>, action: UndoEntry<'tcx>) { - match action { - SpecifyVar(vid, relations, default) => { - values[vid.index as usize].value = Bounded { - relations: relations, - default: default - }; - } + type Undo = Instantiate<'tcx>; - Relate(a, b) => { - relations(&mut (*values)[a.index as usize]).pop(); - relations(&mut (*values)[b.index as usize]).pop(); - } - - RelateRange(i, n) => { - let relations = relations(&mut (*values)[i.index as usize]); - for _ in 0..n { - relations.pop(); - } - } - } - } -} - -fn relations<'a>(v: &'a mut TypeVariableData) -> &'a mut Vec { - match v.value { - Known(_) => bug!("var_sub_var: variable is known"), - Bounded { ref mut relations, .. } => relations + fn reverse(values: &mut Vec>, action: Instantiate<'tcx>) { + let Instantiate { vid, default } = action; + values[vid.index as usize].value = Bounded { + default: default + }; } } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 3b002fd4dfc1a..9176a4c01575f 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -32,6 +32,7 @@ #![feature(i128_type)] #![feature(libc)] #![feature(loop_break_value)] +#![feature(never_type)] #![feature(nonzero)] #![cfg_attr(stage0, feature(pub_restricted))] #![feature(quote)] diff --git a/src/test/compile-fail/issue-7813.rs b/src/test/compile-fail/issue-7813.rs index 662b9e894ba3a..2551ed0208af3 100644 --- a/src/test/compile-fail/issue-7813.rs +++ b/src/test/compile-fail/issue-7813.rs @@ -9,7 +9,7 @@ // except according to those terms. fn main() { - let v = &[]; - //~^ NOTE consider giving `it` a type - let it = v.iter(); //~ ERROR cannot infer type for `_` + let v = &[]; //~ NOTE consider giving `v` a type + let it = v.iter(); //~ ERROR type annotations needed + //~^ NOTE cannot infer type for `_` } From e4b762b532f037d07be282d413c7a864e182b3fc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 16 Mar 2017 09:58:21 -0400 Subject: [PATCH 07/19] add regression test for #30225 Fixes #30225 --- src/test/compile-fail/issue-30225.rs | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/compile-fail/issue-30225.rs diff --git a/src/test/compile-fail/issue-30225.rs b/src/test/compile-fail/issue-30225.rs new file mode 100644 index 0000000000000..7acbbfb8826df --- /dev/null +++ b/src/test/compile-fail/issue-30225.rs @@ -0,0 +1,48 @@ +// Copyright 2016 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. + +// Regression test for #30225, which was an ICE that would trigger as +// a result of a poor interaction between trait result caching and +// type inference. Specifically, at that time, unification could cause +// unrelated type variables to become instantiated, if subtyping +// relationships existed. These relationships are now propagated +// through obligations and hence everything works out fine. + +trait Foo : Sized { + fn foo(self, u: Option, v: Option) {} +} + +struct A; +struct B; + +impl Foo for () {} // impl A +impl Foo for u32 {} // impl B, creating ambiguity + +fn toxic() { + // cache the resolution <() as Foo<$0,$1>> = impl A + let u = None; + let v = None; + Foo::foo((), u, v); +} + +fn bomb() { + let mut u = None; // type is Option<$0> + let mut v = None; // type is Option<$1> + let mut x = None; // type is Option<$2> + + Foo::foo(x.unwrap(),u,v); // register <$2 as Foo<$0, $1>> + u = v; // mark $0 and $1 in a subtype relationship + //~^ ERROR mismatched types + x = Some(()); // set $2 = (), allowing impl selection + // to proceed for <() as Foo<$0, $1>> = impl A. + // kaboom, this *used* to trigge an ICE +} + +fn main() {} From d1033d06bae530881415bda6972c5c8a16d07ade Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Apr 2017 15:59:34 -0400 Subject: [PATCH 08/19] add FIXME to #18653 --- src/librustc/infer/combine.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 825f279e78e4b..a23589e7f3f79 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -224,6 +224,9 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { // Check whether `vid` has been instantiated yet. If not, // make a generalized form of `ty` and instantiate with // that. + // + // FIXME(#18653) -- we need to generalize nested type + // variables too. let b_ty = match b_ty { Some(t) => t, // ...already instantiated. None => { // ...not yet instantiated: From 77d9e38e9499df1dac393937d62d3bf626c51abb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Apr 2017 16:14:05 -0400 Subject: [PATCH 09/19] add FIXME for bivariant lub/glb --- src/librustc/infer/glb.rs | 1 + src/librustc/infer/lub.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/librustc/infer/glb.rs b/src/librustc/infer/glb.rs index 8c167e0a8ac91..a6dd18c113f1a 100644 --- a/src/librustc/infer/glb.rs +++ b/src/librustc/infer/glb.rs @@ -49,6 +49,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> match variance { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test ty::Bivariant => Ok(a.clone()), ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b), } diff --git a/src/librustc/infer/lub.rs b/src/librustc/infer/lub.rs index 28ae1ae556b0b..d7e5c92b6e17b 100644 --- a/src/librustc/infer/lub.rs +++ b/src/librustc/infer/lub.rs @@ -49,6 +49,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> match variance { ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test ty::Bivariant => Ok(a.clone()), ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b), } From 14f1e3459f02e3ce45232a84cd923c86eef55cb5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 16:58:04 -0400 Subject: [PATCH 10/19] fix a bug in compiletest JSON parsing for duplicate errors In some cases, we give multiple primary spans, in which case we would report one `//~` annotation per primary span. That was very confusing because these things are reported to the user as a single error. UI tests would be better here. --- src/test/compile-fail/binop-move-semantics.rs | 2 -- src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs | 1 - .../compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs | 1 - src/test/compile-fail/issue-25579.rs | 1 - src/test/compile-fail/issue-38412.rs | 1 - src/test/compile-fail/lint-unused-imports.rs | 1 - src/tools/compiletest/src/json.rs | 1 + 7 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/test/compile-fail/binop-move-semantics.rs b/src/test/compile-fail/binop-move-semantics.rs index 0cc6ea3e984d9..cff0064497aff 100644 --- a/src/test/compile-fail/binop-move-semantics.rs +++ b/src/test/compile-fail/binop-move-semantics.rs @@ -62,7 +62,6 @@ fn mut_plus_immut() { &mut f + &f; //~ ERROR: cannot borrow `f` as immutable because it is also borrowed as mutable - //~^ cannot borrow `f` as immutable because it is also borrowed as mutable } fn immut_plus_mut() { @@ -71,7 +70,6 @@ fn immut_plus_mut() { &f + &mut f; //~ ERROR: cannot borrow `f` as mutable because it is also borrowed as immutable - //~^ cannot borrow `f` as mutable because it is also borrowed as immutable } fn main() {} diff --git a/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs b/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs index 56cbe0b187867..f09e7ffd7e4b7 100644 --- a/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs +++ b/src/test/compile-fail/borrowck/borrowck-lend-flow-loop.rs @@ -109,7 +109,6 @@ fn while_aliased_mut_cond(cond: bool, cond2: bool) { borrow(&*v); //~ ERROR cannot borrow if cond2 { x = &mut v; //~ ERROR cannot borrow - //~^ ERROR cannot borrow } } } diff --git a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs index f789d44016eb1..38e0e27a7b98e 100644 --- a/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs +++ b/src/test/compile-fail/borrowck/borrowck-mut-borrow-linear-errors.rs @@ -19,7 +19,6 @@ fn main() { match 1 { 1 => { addr = &mut x; } //~^ ERROR cannot borrow `x` as mutable more than once at a time - //~| ERROR cannot borrow `x` as mutable more than once at a time 2 => { addr = &mut x; } //~^ ERROR cannot borrow `x` as mutable more than once at a time _ => { addr = &mut x; } diff --git a/src/test/compile-fail/issue-25579.rs b/src/test/compile-fail/issue-25579.rs index 849c9aa18c905..323ce3b0adf33 100644 --- a/src/test/compile-fail/issue-25579.rs +++ b/src/test/compile-fail/issue-25579.rs @@ -17,7 +17,6 @@ fn causes_ice(mut l: &mut Sexpression) { loop { match l { &mut Sexpression::Num(ref mut n) => {}, &mut Sexpression::Cons(ref mut expr) => { //~ ERROR cannot borrow `l.0` - //~| ERROR cannot borrow `l.0` l = &mut **expr; //~ ERROR cannot assign to `l` } }} diff --git a/src/test/compile-fail/issue-38412.rs b/src/test/compile-fail/issue-38412.rs index 3b62aaf2ab8e9..b4feadbacf740 100644 --- a/src/test/compile-fail/issue-38412.rs +++ b/src/test/compile-fail/issue-38412.rs @@ -11,7 +11,6 @@ fn main() { let Box(a) = loop { }; //~^ ERROR expected tuple struct/variant, found struct `Box` - //~| ERROR expected tuple struct/variant, found struct `Box` // (The below is a trick to allow compiler to infer a type for // variable `a` without attempting to ascribe a type to the diff --git a/src/test/compile-fail/lint-unused-imports.rs b/src/test/compile-fail/lint-unused-imports.rs index f6f7c210f466a..5bb2ab75c53fd 100644 --- a/src/test/compile-fail/lint-unused-imports.rs +++ b/src/test/compile-fail/lint-unused-imports.rs @@ -21,7 +21,6 @@ use std::fmt::{}; // Should get errors for both 'Some' and 'None' use std::option::Option::{Some, None}; //~^ ERROR unused imports: `None`, `Some` -//~| ERROR unused imports: `None`, `Some` use test::A; //~ ERROR unused import: `test::A` // Be sure that if we just bring some methods into scope that they're also diff --git a/src/tools/compiletest/src/json.rs b/src/tools/compiletest/src/json.rs index d9da1bdc34858..06cbd9a3df416 100644 --- a/src/tools/compiletest/src/json.rs +++ b/src/tools/compiletest/src/json.rs @@ -97,6 +97,7 @@ fn push_expected_errors(expected_errors: &mut Vec, let primary_spans: Vec<_> = spans_in_this_file.iter() .cloned() .filter(|span| span.is_primary) + .take(1) // sometimes we have more than one showing up in the json; pick first .collect(); let primary_spans = if primary_spans.is_empty() { // subdiagnostics often don't have a span of their own; From 3a5bbf89b2229c629c6f01bdd87354cba136d133 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 17:16:57 -0400 Subject: [PATCH 11/19] avoid unneeded subtype obligations in lub/glb In some specific cases, the new scheme was failing to learn as much from a LUB/GLB operaiton as the old code, which caused coercion to go awry. A slight ordering hack fixes this. --- src/librustc/infer/lattice.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/librustc/infer/lattice.rs b/src/librustc/infer/lattice.rs index f7b26a918b3a2..d4d090f0153d0 100644 --- a/src/librustc/infer/lattice.rs +++ b/src/librustc/infer/lattice.rs @@ -44,6 +44,10 @@ pub trait LatticeDir<'f, 'gcx: 'f+'tcx, 'tcx: 'f> : TypeRelation<'f, 'gcx, 'tcx> // Relates the type `v` to `a` and `b` such that `v` represents // the LUB/GLB of `a` and `b` as appropriate. + // + // Subtle hack: ordering *may* be significant here. This method + // relates `v` to `a` first, which may help us to avoid unecessary + // type variable obligations. See caller for details. fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>; } @@ -74,7 +78,29 @@ pub fn super_lattice_tys<'a, 'gcx, 'tcx, L>(this: &mut L, Ok(v) } - (&ty::TyInfer(TyVar(..)), _) | + // If one side is known to be a variable and one is not, + // create a variable (`v`) to represent the LUB. Make sure to + // relate `v` to the non-type-variable first (by passing it + // first to `relate_bound`). Otherwise, we would produce a + // subtype obligation that must then be processed. + // + // Example: if the LHS is a type variable, and RHS is + // `Box`, then we current compare `v` to the RHS first, + // which will instantiate `v` with `Box`. Then when `v` + // is compared to the LHS, we instantiate LHS with `Box`. + // But if we did in reverse order, we would create a `v <: + // LHS` (or vice versa) constraint and then instantiate + // `v`. This would require further processing to achieve same + // end-result; in partiular, this screws up some of the logic + // in coercion, which expects LUB to figure out that the LHS + // is (e.g.) `Box`. A more obvious solution might be to + // iterate on the subtype obligations that are returned, but I + // think this suffices. -nmatsakis + (&ty::TyInfer(TyVar(..)), _) => { + let v = infcx.next_ty_var(TypeVariableOrigin::LatticeVariable(this.cause().span)); + this.relate_bound(v, b, a)?; + Ok(v) + } (_, &ty::TyInfer(TyVar(..))) => { let v = infcx.next_ty_var(TypeVariableOrigin::LatticeVariable(this.cause().span)); this.relate_bound(v, a, b)?; From bca56e82a1bbd775498257cc080f5ea27b214f49 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 17:17:58 -0400 Subject: [PATCH 12/19] generalize type variables too When we are generalizing a super/sub-type, we have to replace type variables with a fresh variable (and not just region variables). So if we know that `Box <: ?U`, for example, we instantiate `?U` with `Box` and then relate `Box` to `Box` (and hence require that `?T <: ?V`). This change has some complex interactions, however: First, the occurs check must be updated to detect constraints like `?T <: ?U` and `?U <: Box`. If we're not careful, we'll create a never-ending sequence of new variables. To address this, we add a second unification set into `type_variables` that tracks type variables related through **either** equality **or** subtyping, and use that during the occurs-check. Second, the "fudge regions if ok" code was expecting no new type variables to be created. It must be updated to create new type variables outside of the probe. This is relatively straight-forward under the new scheme, since type variables are now independent from one another, and any relations are moderated by pending subtype obliations and so forth. This part would be tricky to backport though. cc #18653 cc #40951 --- src/librustc/infer/combine.rs | 46 +++++--- src/librustc/infer/fudge.rs | 75 ++++++++---- src/librustc/infer/mod.rs | 6 +- src/librustc/infer/sub.rs | 8 +- src/librustc/infer/type_variable.rs | 108 +++++++++++++++++- src/librustc/traits/error_reporting.rs | 24 +++- src/test/run-pass/issue-40951.rs | 20 ++++ .../run-pass/type-infer-generalize-ty-var.rs | 60 ++++++++++ 8 files changed, 297 insertions(+), 50 deletions(-) create mode 100644 src/test/run-pass/issue-40951.rs create mode 100644 src/test/run-pass/type-infer-generalize-ty-var.rs diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index a23589e7f3f79..03ed654e3cce4 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -264,20 +264,27 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { Ok(()) } - /// Attempts to generalize `ty` for the type variable `for_vid`. This checks for cycle -- that - /// is, whether the type `ty` references `for_vid`. If `make_region_vars` is true, it will also - /// replace all regions with fresh variables. Returns `TyError` in the case of a cycle, `Ok` - /// otherwise. + /// Attempts to generalize `ty` for the type variable `for_vid`. + /// This checks for cycle -- that is, whether the type `ty` + /// references `for_vid`. If `make_region_vars` is true, it will + /// also replace all regions with fresh variables. Returns + /// `TyError` in the case of a cycle, `Ok` otherwise. + /// + /// Preconditions: + /// + /// - `for_vid` is a "root vid" fn generalize(&self, ty: Ty<'tcx>, for_vid: ty::TyVid, make_region_vars: bool) -> RelateResult<'tcx, Ty<'tcx>> { + debug_assert!(self.infcx.type_variables.borrow_mut().root_var(for_vid) == for_vid); + let mut generalize = Generalizer { infcx: self.infcx, span: self.trace.cause.span, - for_vid: for_vid, + for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid), make_region_vars: make_region_vars, cycle_detected: false }; @@ -293,7 +300,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, span: Span, - for_vid: ty::TyVid, + for_vid_sub_root: ty::TyVid, make_region_vars: bool, cycle_detected: bool, } @@ -305,17 +312,17 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { // Check to see whether the type we are genealizing references - // `vid`. At the same time, also update any type variables to - // the values that they are bound to. This is needed to truly - // check for cycles, but also just makes things readable. - // - // (In particular, you could have something like `$0 = Box<$1>` - // where `$1` has already been instantiated with `Box<$0>`) + // any other type variable related to `vid` via + // subtyping. This is basically our "occurs check", preventing + // us from creating infinitely sized types. match t.sty { ty::TyInfer(ty::TyVar(vid)) => { let mut variables = self.infcx.type_variables.borrow_mut(); let vid = variables.root_var(vid); - if vid == self.for_vid { + let sub_vid = variables.sub_root_var(vid); + if sub_vid == self.for_vid_sub_root { + // If sub-roots are equal, then `for_vid` and + // `vid` are related via subtyping. self.cycle_detected = true; self.tcx().types.err } else { @@ -324,7 +331,18 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx drop(variables); self.fold_ty(u) } - None => t, + None => { + if self.make_region_vars { + let origin = variables.origin(vid); + let new_var_id = variables.new_var(false, origin, None); + let u = self.tcx().mk_var(new_var_id); + debug!("generalize: replacing original vid={:?} with new={:?}", + vid, u); + u + } else { + t + } + } } } } diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index 806b94486615f..ab0ff32dcc3ec 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ty::{self, TyCtxt}; +use infer::type_variable::TypeVariableMap; +use ty::{self, Ty, TyCtxt}; use ty::fold::{TypeFoldable, TypeFolder}; use super::InferCtxt; @@ -54,57 +55,52 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { /// the actual types (`?T`, `Option(&self, origin: &RegionVariableOrigin, f: F) -> Result where F: FnOnce() -> Result, T: TypeFoldable<'tcx>, { - let (region_vars, value) = self.probe(|snapshot| { - let vars_at_start = self.type_variables.borrow().num_vars(); + debug!("fudge_regions_if_ok(origin={:?})", origin); + let (type_variables, region_vars, value) = self.probe(|snapshot| { match f() { Ok(value) => { let value = self.resolve_type_vars_if_possible(&value); // At this point, `value` could in principle refer - // to regions that have been created during the - // snapshot (we assert below that `f()` does not - // create any new type variables, so there - // shouldn't be any of those). Once we exit - // `probe()`, those are going to be popped, so we - // will have to eliminate any references to them. - - assert_eq!(self.type_variables.borrow().num_vars(), vars_at_start, - "type variables were created during fudge_regions_if_ok"); + // to types/regions that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + + let type_variables = + self.type_variables.borrow_mut().types_created_since_snapshot( + &snapshot.type_snapshot); let region_vars = self.region_vars.vars_created_since_snapshot( &snapshot.region_vars_snapshot); - Ok((region_vars, value)) + Ok((type_variables, region_vars, value)) } Err(e) => Err(e), } })?; // At this point, we need to replace any of the now-popped - // region variables that appear in `value` with a fresh region - // variable. We can't do this during the probe because they - // would just get popped then too. =) + // type/region variables that appear in `value` with a fresh + // variable of the appropriate kind. We can't do this during + // the probe because they would just get popped then too. =) // Micro-optimization: if no variables have been created, then // `value` can't refer to any of them. =) So we can just return it. - if region_vars.is_empty() { + if type_variables.is_empty() && region_vars.is_empty() { return Ok(value); } let mut fudger = RegionFudger { infcx: self, + type_variables: &type_variables, region_vars: ®ion_vars, origin: origin }; @@ -115,6 +111,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { pub struct RegionFudger<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, + type_variables: &'a TypeVariableMap, region_vars: &'a Vec, origin: &'a RegionVariableOrigin, } @@ -124,6 +121,40 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> { self.infcx.tcx } + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.sty { + ty::TyInfer(ty::InferTy::TyVar(vid)) => { + match self.type_variables.get(&vid) { + None => { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!(self.infcx.type_variables.borrow_mut().probe(vid).is_none()); + ty + } + + Some(info) => { + // This variable was created during the + // fudging; it was mapped the root + // `root_vid`. There are now two + // possibilities: either the root was creating + // during the fudging too, in which case we + // want a fresh variable, or it was not, in + // which case we can return it. + if self.type_variables.contains_key(&info.root_vid) { + self.infcx.next_ty_var(info.root_origin) + } else { + self.infcx.tcx.mk_var(info.root_vid) + } + } + } + } + _ => ty.super_fold_with(self), + } + } + fn fold_region(&mut self, r: &'tcx ty::Region) -> &'tcx ty::Region { match *r { ty::ReVar(v) if self.region_vars.contains(&v) => { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 999ebbfa20fbf..e98792b120de2 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1036,9 +1036,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.probe(|_| { let origin = &ObligationCause::dummy(); let trace = TypeTrace::types(origin, true, a, b); - self.sub(true, trace, &a, &b).map(|InferOk { obligations, .. }| { - // FIXME(#32730) propagate obligations - assert!(obligations.is_empty()); + self.sub(true, trace, &a, &b).map(|InferOk { obligations: _, .. }| { + // Ignore obligations, since we are unrolling + // everything anyway. }) }) } diff --git a/src/librustc/infer/sub.rs b/src/librustc/infer/sub.rs index f1de9b043e36f..2a7dbbc026bc0 100644 --- a/src/librustc/infer/sub.rs +++ b/src/librustc/infer/sub.rs @@ -80,7 +80,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> let a = infcx.type_variables.borrow_mut().replace_if_possible(a); let b = infcx.type_variables.borrow_mut().replace_if_possible(b); match (&a.sty, &b.sty) { - (&ty::TyInfer(TyVar(_)), &ty::TyInfer(TyVar(_))) => { + (&ty::TyInfer(TyVar(a_vid)), &ty::TyInfer(TyVar(b_vid))) => { // Shouldn't have any LBR here, so we can safely put // this under a binder below without fear of accidental // capture. @@ -88,7 +88,11 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx> assert!(!b.has_escaping_regions()); // can't make progress on `A <: B` if both A and B are - // type variables, so record an obligation. + // type variables, so record an obligation. We also + // have to record in the `type_variables` tracker that + // the two variables are equal modulo subtyping, which + // is important to the occurs check later on. + infcx.type_variables.borrow_mut().sub(a_vid, b_vid); self.fields.obligations.push( Obligation::new( self.fields.trace.cause.clone(), diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index 298b2a97d5f36..a32404c1ac515 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -18,16 +18,39 @@ use std::cmp::min; use std::marker::PhantomData; use std::mem; use std::u32; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::unify as ut; pub struct TypeVariableTable<'tcx> { values: sv::SnapshotVec>, + + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X == ?Y`. eq_relations: ut::UnificationTable, + + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second + /// table exists only to help with the occurs check. In particular, + /// we want to report constraints like these as an occurs check + /// violation: + /// + /// ?1 <: ?3 + /// Box <: ?1 + /// + /// This works because `?1` and `?3` are unified in the + /// `sub_relations` relation (not in `eq_relations`). Then when we + /// process the `Box <: ?1` constraint, we do an occurs check + /// on `Box` and find a potential cycle. + /// + /// This is reasonable because, in Rust, subtypes have the same + /// "skeleton" and hence there is no possible type such that + /// (e.g.) `Box <: ?3` for any `?3`. + sub_relations: ut::UnificationTable, } /// Reasons to create a type inference variable -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub enum TypeVariableOrigin { MiscVariable(Span), NormalizeProjectionType(Span), @@ -41,6 +64,14 @@ pub enum TypeVariableOrigin { DivergingBlockExpr(Span), DivergingFn(Span), LatticeVariable(Span), + Generalized(ty::TyVid), +} + +pub type TypeVariableMap = FxHashMap; + +pub struct TypeVariableInfo { + pub root_vid: ty::TyVid, + pub root_origin: TypeVariableOrigin, } struct TypeVariableData<'tcx> { @@ -70,6 +101,7 @@ pub struct Default<'tcx> { pub struct Snapshot { snapshot: sv::Snapshot, eq_snapshot: ut::Snapshot, + sub_snapshot: ut::Snapshot, } struct Instantiate<'tcx> { @@ -84,6 +116,7 @@ impl<'tcx> TypeVariableTable<'tcx> { TypeVariableTable { values: sv::SnapshotVec::new(), eq_relations: ut::UnificationTable::new(), + sub_relations: ut::UnificationTable::new(), } } @@ -109,6 +142,16 @@ impl<'tcx> TypeVariableTable<'tcx> { debug_assert!(self.probe(a).is_none()); debug_assert!(self.probe(b).is_none()); self.eq_relations.union(a, b); + self.sub_relations.union(a, b); + } + + /// Records that `a <: b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) { + debug_assert!(self.probe(a).is_none()); + debug_assert!(self.probe(b).is_none()); + self.sub_relations.union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -141,6 +184,7 @@ impl<'tcx> TypeVariableTable<'tcx> { default: Option>,) -> ty::TyVid { debug!("new_var(diverging={:?}, origin={:?})", diverging, origin); self.eq_relations.new_key(()); + self.sub_relations.new_key(()); let index = self.values.push(TypeVariableData { value: Bounded { default: default }, origin: origin, @@ -155,15 +199,41 @@ impl<'tcx> TypeVariableTable<'tcx> { self.values.len() } + /// Returns the "root" variable of `vid` in the `eq_relations` + /// equivalence table. All type variables that have been equated + /// will yield the same root variable (per the union-find + /// algorithm), so `root_var(a) == root_var(b)` implies that `a == + /// b` (transitively). pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { self.eq_relations.find(vid) } + /// Returns the "root" variable of `vid` in the `sub_relations` + /// equivalence table. All type variables that have been are + /// related via equality or subtyping will yield the same root + /// variable (per the union-find algorithm), so `sub_root_var(a) + /// == sub_root_var(b)` implies that: + /// + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + self.sub_relations.find(vid) + } + + /// True if `a` and `b` have same "sub-root" (i.e., exists some + /// type X such that `forall i in {a, b}. (i <: X || X <: i)`. + pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool { + self.sub_root_var(a) == self.sub_root_var(b) + } + pub fn probe(&mut self, vid: ty::TyVid) -> Option> { let vid = self.root_var(vid); self.probe_root(vid) } + pub fn origin(&self, vid: ty::TyVid) -> TypeVariableOrigin { + self.values.get(vid.index as usize).origin.clone() + } + /// Retrieves the type of `vid` given that it is currently a root in the unification table pub fn probe_root(&mut self, vid: ty::TyVid) -> Option> { debug_assert!(self.root_var(vid) == vid); @@ -189,6 +259,7 @@ impl<'tcx> TypeVariableTable<'tcx> { Snapshot { snapshot: self.values.start_snapshot(), eq_snapshot: self.eq_relations.snapshot(), + sub_snapshot: self.sub_relations.snapshot(), } } @@ -204,13 +275,40 @@ impl<'tcx> TypeVariableTable<'tcx> { } }); - self.values.rollback_to(s.snapshot); - self.eq_relations.rollback_to(s.eq_snapshot); + let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; + self.values.rollback_to(snapshot); + self.eq_relations.rollback_to(eq_snapshot); + self.sub_relations.rollback_to(sub_snapshot); } pub fn commit(&mut self, s: Snapshot) { - self.values.commit(s.snapshot); - self.eq_relations.commit(s.eq_snapshot); + let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; + self.values.commit(snapshot); + self.eq_relations.commit(eq_snapshot); + self.sub_relations.commit(sub_snapshot); + } + + /// Returns a map `{V1 -> V2}`, where the keys `{V1}` are + /// ty-variables created during the snapshot, and the values + /// `{V2}` are the root variables that they were unified with, + /// along with their origin. + pub fn types_created_since_snapshot(&mut self, s: &Snapshot) -> TypeVariableMap { + let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot); + let eq_relations = &mut self.eq_relations; + let values = &self.values; + + actions_since_snapshot + .iter() + .filter_map(|action| match action { + &sv::UndoLog::NewElem(index) => Some(ty::TyVid { index: index as u32 }), + _ => None, + }) + .map(|vid| { + let root_vid = eq_relations.find(vid); + let root_origin = values.get(vid.index as usize).origin.clone(); + (vid, TypeVariableInfo { root_vid, root_origin }) + }) + .collect() } pub fn types_escaping_snapshot(&mut self, s: &Snapshot) -> Vec> { diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 8a303a5da1184..f7a7d0e2071f2 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -69,6 +69,19 @@ struct FindLocalByTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { found_pattern: Option<&'a Pat>, } +impl<'a, 'gcx, 'tcx> FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { + fn is_match(&self, ty: Ty<'tcx>) -> bool { + ty == *self.target_ty || match (&ty.sty, &self.target_ty.sty) { + (&ty::TyInfer(ty::TyVar(a_vid)), &ty::TyInfer(ty::TyVar(b_vid))) => + self.infcx.type_variables + .borrow_mut() + .sub_unified(a_vid, b_vid), + + _ => false, + } + } +} + impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> { NestedVisitorMap::None @@ -77,7 +90,7 @@ impl<'a, 'gcx, 'tcx> Visitor<'a> for FindLocalByTypeVisitor<'a, 'gcx, 'tcx> { fn visit_local(&mut self, local: &'a Local) { if let Some(&ty) = self.infcx.tables.borrow().node_types.get(&local.id) { let ty = self.infcx.resolve_type_vars_if_possible(&ty); - let is_match = ty.walk().any(|t| t == *self.target_ty); + let is_match = ty.walk().any(|t| self.is_match(t)); if is_match && self.found_pattern.is_none() { self.found_pattern = Some(&*local.pat); @@ -564,8 +577,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } ty::Predicate::Subtype(ref predicate) => { - // TODO - panic!("subtype requirement not satisfied {:?}", predicate) + // Errors for Subtype predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) } ty::Predicate::Equate(ref predicate) => { @@ -779,7 +794,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // no need to overload user in such cases } else { let &SubtypePredicate { a_is_expected: _, a, b } = data.skip_binder(); - assert!(a.is_ty_var() && b.is_ty_var()); // else other would've been instantiated + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); self.need_type_info(obligation, a); } } diff --git a/src/test/run-pass/issue-40951.rs b/src/test/run-pass/issue-40951.rs new file mode 100644 index 0000000000000..adc7101b16aa1 --- /dev/null +++ b/src/test/run-pass/issue-40951.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +// Regression test for #40951. + +const FOO: [&'static str; 1] = ["foo"]; + +fn find(t: &[T], element: &T) { } + +fn main() { + let x = format!("hi"); + find(&FOO, &&*x); +} diff --git a/src/test/run-pass/type-infer-generalize-ty-var.rs b/src/test/run-pass/type-infer-generalize-ty-var.rs new file mode 100644 index 0000000000000..d7fb85ca4842e --- /dev/null +++ b/src/test/run-pass/type-infer-generalize-ty-var.rs @@ -0,0 +1,60 @@ +// Copyright 2016 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. + +// Test a scenario where we generate a constraint like `?1 <: &?2`. +// In such a case, it is important that we instantiate `?1` with `&?3` +// where `?3 <: ?2`, and not with `&?2`. This is a regression test for +// #18653. The important thing is that we build. + +use std::cell::RefCell; + +enum Wrap { + WrapSome(A), + WrapNone +} + +use Wrap::*; + +struct T; +struct U; + +trait Get { + fn get(&self) -> &T; +} + +impl Get for Wrap { + fn get(&self) -> &(MyShow + 'static) { + static x: usize = 42; + &x + } +} + +impl Get for Wrap { + fn get(&self) -> &usize { + static x: usize = 55; + &x + } +} + +trait MyShow { fn dummy(&self) { } } +impl<'a> MyShow for &'a (MyShow + 'a) { } +impl MyShow for usize { } +fn constrain<'a>(rc: RefCell<&'a (MyShow + 'a)>) { } + +fn main() { + let mut collection: Wrap<_> = WrapNone; + + { + let __arg0 = Get::get(&collection); + let __args_cell = RefCell::new(__arg0); + constrain(__args_cell); + } + collection = WrapSome(T); +} From 1c138ed1c79cfac39c47cc4119a464f9b638a9c6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 17:10:26 -0400 Subject: [PATCH 13/19] update various test cases that generate slightly different output For the most part, it seems to be better, but one side-effect is that I cannot seem to reproduce E0102 anymore. --- src/librustc_typeck/diagnostics.rs | 2 +- src/test/compile-fail/E0102.rs | 5 +++-- src/test/compile-fail/destructure-trait-ref.rs | 2 +- src/test/compile-fail/issue-12187-1.rs | 1 + src/test/compile-fail/issue-12187-2.rs | 1 + src/test/compile-fail/issue-7813.rs | 7 ++++--- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index fb951fd20e564..2d72052f1e5ad 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1378,7 +1378,7 @@ E0102: r##" You hit this error because the compiler lacks the information to determine the type of this variable. Erroneous code example: -```compile_fail,E0102 +```compile_fail,E0282 // could be an array of anything let x = []; // error: cannot determine a type for this local variable ``` diff --git a/src/test/compile-fail/E0102.rs b/src/test/compile-fail/E0102.rs index 1d64798bb8382..6a17ddebd1dc1 100644 --- a/src/test/compile-fail/E0102.rs +++ b/src/test/compile-fail/E0102.rs @@ -10,6 +10,7 @@ fn main() { let x = []; - //~^ ERROR E0102 - //~| NOTE cannot resolve type of variable + //~^ ERROR type annotations needed + //~| NOTE consider giving `x` a type + //~| NOTE cannot infer type for `_` } diff --git a/src/test/compile-fail/destructure-trait-ref.rs b/src/test/compile-fail/destructure-trait-ref.rs index 835ec8e4a5e7e..09bd3a2fc57d9 100644 --- a/src/test/compile-fail/destructure-trait-ref.rs +++ b/src/test/compile-fail/destructure-trait-ref.rs @@ -35,7 +35,7 @@ fn main() { // n == m let &x = &1isize as &T; //~ ERROR type `&T` cannot be dereferenced let &&x = &(&1isize as &T); //~ ERROR type `&T` cannot be dereferenced - let box x = box 1isize as Box; //~ ERROR `T: std::marker::Sized` is not satisfied + let box x = box 1isize as Box; //~ ERROR type `std::boxed::Box` cannot be dereferenced // n > m let &&x = &1isize as &T; diff --git a/src/test/compile-fail/issue-12187-1.rs b/src/test/compile-fail/issue-12187-1.rs index 346fae11070e1..6aeb9442c40ed 100644 --- a/src/test/compile-fail/issue-12187-1.rs +++ b/src/test/compile-fail/issue-12187-1.rs @@ -16,4 +16,5 @@ fn main() { let &v = new(); //~^ ERROR type annotations needed [E0282] //~| NOTE cannot infer type for `_` + //~| NOTE consider giving a type to pattern } diff --git a/src/test/compile-fail/issue-12187-2.rs b/src/test/compile-fail/issue-12187-2.rs index 848174d6fe1e0..d52ed06c4085d 100644 --- a/src/test/compile-fail/issue-12187-2.rs +++ b/src/test/compile-fail/issue-12187-2.rs @@ -16,4 +16,5 @@ fn main() { let &v = new(); //~^ ERROR type annotations needed [E0282] //~| NOTE cannot infer type for `_` + //~| NOTE consider giving a type to pattern } diff --git a/src/test/compile-fail/issue-7813.rs b/src/test/compile-fail/issue-7813.rs index 2551ed0208af3..a5f001b785cc9 100644 --- a/src/test/compile-fail/issue-7813.rs +++ b/src/test/compile-fail/issue-7813.rs @@ -9,7 +9,8 @@ // except according to those terms. fn main() { - let v = &[]; //~ NOTE consider giving `v` a type - let it = v.iter(); //~ ERROR type annotations needed - //~^ NOTE cannot infer type for `_` + let v = &[]; //~ ERROR type annotations needed + //~| NOTE consider giving `v` a type + //~| NOTE cannot infer type for `_` + let it = v.iter(); } From 761808ef40b1e2cc9b35f0891a66d2dcf3274834 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 17:11:05 -0400 Subject: [PATCH 14/19] just panic in rustdoc if we encounter a subtype predicate These are not user expressible anyhow. --- src/librustdoc/clean/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1a194cd125462..fb8ba51853fe8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -838,7 +838,7 @@ impl Clean> for ty::Region { pub enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec}, - EqPredicate { lhs: Type, rhs: Type } + EqPredicate { lhs: Type, rhs: Type }, } impl Clean for hir::WherePredicate { @@ -906,12 +906,9 @@ impl<'tcx> Clean for ty::EquatePredicate<'tcx> { } impl<'tcx> Clean for ty::SubtypePredicate<'tcx> { - fn clean(&self, cx: &DocContext) -> WherePredicate { - let ty::SubtypePredicate { a_is_expected: _, a, b } = *self; - WherePredicate::EqPredicate { // TODO This is obviously wrong :P - lhs: a.clean(cx), - rhs: b.clean(cx) - } + fn clean(&self, _cx: &DocContext) -> WherePredicate { + panic!("subtype predicates are an internal rustc artifact \ + and should not be seen by rustdoc") } } From 59babd80bde4ce2f96768e44047ba0e44afc8eac Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 17:11:22 -0400 Subject: [PATCH 15/19] add some comments and `debug!` calls to "obligation forest" --- .../obligation_forest/mod.rs | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs index a46238309bb46..3515e5c5ede35 100644 --- a/src/librustc_data_structures/obligation_forest/mod.rs +++ b/src/librustc_data_structures/obligation_forest/mod.rs @@ -43,7 +43,16 @@ pub trait ObligationProcessor { obligation: &mut Self::Obligation) -> Result>, Self::Error>; - fn process_backedge<'c, I>(&mut self, cycle: I, + /// As we do the cycle check, we invoke this callback when we + /// encounter an actual cycle. `cycle` is an iterator that starts + /// at the start of the cycle in the stack and walks **toward the + /// top**. + /// + /// In other words, if we had O1 which required O2 which required + /// O3 which required O1, we would give an iterator yielding O1, + /// O2, O3 (O1 is not yielded twice). + fn process_backedge<'c, I>(&mut self, + cycle: I, _marker: PhantomData<&'c Self::Obligation>) where I: Clone + Iterator; } @@ -239,8 +248,8 @@ impl ObligationForest { } } Entry::Vacant(v) => { - debug!("register_obligation_at({:?}, {:?}) - ok", - obligation, parent); + debug!("register_obligation_at({:?}, {:?}) - ok, new index is {}", + obligation, parent, self.nodes.len()); v.insert(NodeIndex::new(self.nodes.len())); self.cache_list.push(obligation.as_predicate().clone()); self.nodes.push(Node::new(parent, obligation)); @@ -376,6 +385,9 @@ impl ObligationForest { where P: ObligationProcessor { let mut stack = self.scratch.take().unwrap(); + debug_assert!(stack.is_empty()); + + debug!("process_cycles()"); for index in 0..self.nodes.len() { // For rustc-benchmarks/inflate-0.1.0 this state test is extremely @@ -389,6 +401,9 @@ impl ObligationForest { } } + debug!("process_cycles: complete"); + + debug_assert!(stack.is_empty()); self.scratch = Some(stack); } @@ -402,21 +417,6 @@ impl ObligationForest { NodeState::OnDfsStack => { let index = stack.iter().rposition(|n| *n == index).unwrap(); - // I need a Clone closure - #[derive(Clone)] - struct GetObligation<'a, O: 'a>(&'a [Node]); - impl<'a, 'b, O> FnOnce<(&'b usize,)> for GetObligation<'a, O> { - type Output = &'a O; - extern "rust-call" fn call_once(self, args: (&'b usize,)) -> &'a O { - &self.0[*args.0].obligation - } - } - impl<'a, 'b, O> FnMut<(&'b usize,)> for GetObligation<'a, O> { - extern "rust-call" fn call_mut(&mut self, args: (&'b usize,)) -> &'a O { - &self.0[*args.0].obligation - } - } - processor.process_backedge(stack[index..].iter().map(GetObligation(&self.nodes)), PhantomData); } @@ -645,3 +645,20 @@ impl Node { } } } + +// I need a Clone closure +#[derive(Clone)] +struct GetObligation<'a, O: 'a>(&'a [Node]); + +impl<'a, 'b, O> FnOnce<(&'b usize,)> for GetObligation<'a, O> { + type Output = &'a O; + extern "rust-call" fn call_once(self, args: (&'b usize,)) -> &'a O { + &self.0[*args.0].obligation + } +} + +impl<'a, 'b, O> FnMut<(&'b usize,)> for GetObligation<'a, O> { + extern "rust-call" fn call_mut(&mut self, args: (&'b usize,)) -> &'a O { + &self.0[*args.0].obligation + } +} From 0fae3324a2f58e921e2094a6971962c0b09c04ee Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 17:11:41 -0400 Subject: [PATCH 16/19] add some debug! to coercion --- src/librustc_typeck/check/coercion.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index a5acd0c7e5300..2033eaf886166 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -195,8 +195,10 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { // Consider coercing the subtype to a DST let unsize = self.coerce_unsized(a, b); if unsize.is_ok() { + debug!("coerce: unsize successful"); return unsize; } + debug!("coerce: unsize failed"); // Examine the supertype and consider auto-borrowing. // @@ -745,7 +747,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { { let prev_ty = self.resolve_type_vars_with_obligations(prev_ty); let new_ty = self.resolve_type_vars_with_obligations(new_ty); - debug!("coercion::try_find_lub({:?}, {:?})", prev_ty, new_ty); + debug!("coercion::try_find_coercion_lub({:?}, {:?})", prev_ty, new_ty); // Special-ish case: we can coerce any type `T` into the `!` // type, but only if the source expression diverges. From 7832db8031a9051d377e1da92bbcd56e366354c1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Apr 2017 17:12:00 -0400 Subject: [PATCH 17/19] fix long line --- src/librustc/traits/structural_impls.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index fcaa29be9632c..9d0b1035ade49 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -130,7 +130,8 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { match *self { super::CodeSelectionError(ref e) => write!(f, "{:?}", e), super::CodeProjectionError(ref e) => write!(f, "{:?}", e), - super::CodeSubtypeError(ref a, ref b) => write!(f, "CodeSubtypeError({:?}, {:?})", a, b), + super::CodeSubtypeError(ref a, ref b) => + write!(f, "CodeSubtypeError({:?}, {:?})", a, b), super::CodeAmbiguity => write!(f, "Ambiguity") } } From fa437f49af90e1a78771f2262d51ddd01b668199 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Apr 2017 12:36:43 -0400 Subject: [PATCH 18/19] do not consult union-find during `fudge_regions_if_ok` --- src/librustc/infer/fudge.rs | 16 ++++------------ src/librustc/infer/type_variable.rs | 14 +++----------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/librustc/infer/fudge.rs b/src/librustc/infer/fudge.rs index ab0ff32dcc3ec..72b23a3bc181c 100644 --- a/src/librustc/infer/fudge.rs +++ b/src/librustc/infer/fudge.rs @@ -135,19 +135,11 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFudger<'a, 'gcx, 'tcx> { ty } - Some(info) => { + Some(&origin) => { // This variable was created during the - // fudging; it was mapped the root - // `root_vid`. There are now two - // possibilities: either the root was creating - // during the fudging too, in which case we - // want a fresh variable, or it was not, in - // which case we can return it. - if self.type_variables.contains_key(&info.root_vid) { - self.infcx.next_ty_var(info.root_origin) - } else { - self.infcx.tcx.mk_var(info.root_vid) - } + // fudging. Recreate it with a fresh variable + // here. + self.infcx.next_ty_var(origin) } } } diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index a32404c1ac515..34bb9feb5c921 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -67,12 +67,7 @@ pub enum TypeVariableOrigin { Generalized(ty::TyVid), } -pub type TypeVariableMap = FxHashMap; - -pub struct TypeVariableInfo { - pub root_vid: ty::TyVid, - pub root_origin: TypeVariableOrigin, -} +pub type TypeVariableMap = FxHashMap; struct TypeVariableData<'tcx> { value: TypeVariableValue<'tcx>, @@ -294,8 +289,6 @@ impl<'tcx> TypeVariableTable<'tcx> { /// along with their origin. pub fn types_created_since_snapshot(&mut self, s: &Snapshot) -> TypeVariableMap { let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot); - let eq_relations = &mut self.eq_relations; - let values = &self.values; actions_since_snapshot .iter() @@ -304,9 +297,8 @@ impl<'tcx> TypeVariableTable<'tcx> { _ => None, }) .map(|vid| { - let root_vid = eq_relations.find(vid); - let root_origin = values.get(vid.index as usize).origin.clone(); - (vid, TypeVariableInfo { root_vid, root_origin }) + let origin = self.values.get(vid.index as usize).origin.clone(); + (vid, origin) }) .collect() } From 1cc7621dec567ded8965fd6c01d80104cfec4d68 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Apr 2017 12:51:19 -0400 Subject: [PATCH 19/19] simplify code to remove now unused "stack" and fix comments --- src/librustc/infer/combine.rs | 139 ++++++++++------------------ src/librustc/infer/type_variable.rs | 7 +- 2 files changed, 54 insertions(+), 92 deletions(-) diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs index 03ed654e3cce4..b73079b02bdd9 100644 --- a/src/librustc/infer/combine.rs +++ b/src/librustc/infer/combine.rs @@ -47,7 +47,6 @@ use ty::relate::{RelateResult, TypeRelation}; use traits::PredicateObligations; use syntax::ast; -use syntax::util::small_vector::SmallVector; use syntax_pos::Span; #[derive(Clone)] @@ -174,6 +173,15 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { Glb::new(self, a_is_expected) } + /// Here dir is either EqTo, SubtypeOf, or SupertypeOf. The + /// idea is that we should ensure that the type `a_ty` is equal + /// to, a subtype of, or a supertype of (respectively) the type + /// to which `b_vid` is bound. + /// + /// Since `b_vid` has not yet been instantiated with a type, we + /// will first instantiate `b_vid` with a *generalized* version + /// of `a_ty`. Generalization introduces other inference + /// variables wherever subtyping could occur. pub fn instantiate(&mut self, a_ty: Ty<'tcx>, dir: RelationDir, @@ -183,92 +191,49 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { { use self::RelationDir::*; - // We use SmallVector here instead of Vec because this code is hot and - // it's rare that the stack length exceeds 1. - let mut stack = SmallVector::new(); - stack.push((a_ty, dir, b_vid)); - loop { - // For each turn of the loop, we extract a tuple - // - // (a_ty, dir, b_vid) - // - // to relate. Here dir is either SubtypeOf or - // SupertypeOf. The idea is that we should ensure that - // the type `a_ty` is a subtype or supertype (respectively) of the - // type to which `b_vid` is bound. - // - // If `b_vid` has not yet been instantiated with a type - // (which is always true on the first iteration, but not - // necessarily true on later iterations), we will first - // instantiate `b_vid` with a *generalized* version of - // `a_ty`. Generalization introduces other inference - // variables wherever subtyping could occur (at time of - // this writing, this means replacing free regions with - // region variables). - let (a_ty, dir, b_vid) = match stack.pop() { - None => break, - Some(e) => e, - }; - // Get the actual variable that b_vid has been inferred to - let (b_vid, b_ty) = { - let mut variables = self.infcx.type_variables.borrow_mut(); - let b_vid = variables.root_var(b_vid); - (b_vid, variables.probe_root(b_vid)) - }; - - debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", - a_ty, - dir, - b_vid); - - // Check whether `vid` has been instantiated yet. If not, - // make a generalized form of `ty` and instantiate with - // that. - // - // FIXME(#18653) -- we need to generalize nested type - // variables too. - let b_ty = match b_ty { - Some(t) => t, // ...already instantiated. - None => { // ...not yet instantiated: - // Generalize type if necessary. - let generalized_ty = match dir { - EqTo => self.generalize(a_ty, b_vid, false), - SupertypeOf | SubtypeOf => self.generalize(a_ty, b_vid, true), - }?; - debug!("instantiate(a_ty={:?}, dir={:?}, \ - b_vid={:?}, generalized_ty={:?})", - a_ty, dir, b_vid, - generalized_ty); - self.infcx.type_variables - .borrow_mut() - .instantiate(b_vid, generalized_ty); - generalized_ty - } - }; - - // The original triple was `(a_ty, dir, b_vid)` -- now we have - // resolved `b_vid` to `b_ty`, so apply `(a_ty, dir, b_ty)`: - // - // FIXME(#16847): This code is non-ideal because all these subtype - // relations wind up attributed to the same spans. We need - // to associate causes/spans with each of the relations in - // the stack to get this right. - match dir { - EqTo => self.equate(a_is_expected).relate(&a_ty, &b_ty), - SubtypeOf => self.sub(a_is_expected).relate(&a_ty, &b_ty), - SupertypeOf => self.sub(a_is_expected).relate_with_variance( - ty::Contravariant, &a_ty, &b_ty), - }?; - } + // Get the actual variable that b_vid has been inferred to + debug_assert!(self.infcx.type_variables.borrow_mut().probe(b_vid).is_none()); + + debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid); + + // Generalize type of `a_ty` appropriately depending on the + // direction. As an example, assume: + // + // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an + // inference variable, + // - and `dir` == `SubtypeOf`. + // + // Then the generalized form `b_ty` would be `&'?2 ?3`, where + // `'?2` and `?3` are fresh region/type inference + // variables. (Down below, we will relate `a_ty <: b_ty`, + // adding constraints like `'x: '?2` and `?1 <: ?3`.) + let b_ty = self.generalize(a_ty, b_vid, dir == EqTo)?; + debug!("instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})", + a_ty, dir, b_vid, b_ty); + self.infcx.type_variables.borrow_mut().instantiate(b_vid, b_ty); + + // Finally, relate `b_ty` to `a_ty`, as described in previous comment. + // + // FIXME(#16847): This code is non-ideal because all these subtype + // relations wind up attributed to the same spans. We need + // to associate causes/spans with each of the relations in + // the stack to get this right. + match dir { + EqTo => self.equate(a_is_expected).relate(&a_ty, &b_ty), + SubtypeOf => self.sub(a_is_expected).relate(&a_ty, &b_ty), + SupertypeOf => self.sub(a_is_expected).relate_with_variance( + ty::Contravariant, &a_ty, &b_ty), + }?; Ok(()) } /// Attempts to generalize `ty` for the type variable `for_vid`. /// This checks for cycle -- that is, whether the type `ty` - /// references `for_vid`. If `make_region_vars` is true, it will - /// also replace all regions with fresh variables. Returns - /// `TyError` in the case of a cycle, `Ok` otherwise. + /// references `for_vid`. If `is_eq_relation` is false, it will + /// also replace all regions/unbound-type-variables with fresh + /// variables. Returns `TyError` in the case of a cycle, `Ok` + /// otherwise. /// /// Preconditions: /// @@ -276,16 +241,14 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> { fn generalize(&self, ty: Ty<'tcx>, for_vid: ty::TyVid, - make_region_vars: bool) + is_eq_relation: bool) -> RelateResult<'tcx, Ty<'tcx>> { - debug_assert!(self.infcx.type_variables.borrow_mut().root_var(for_vid) == for_vid); - let mut generalize = Generalizer { infcx: self.infcx, span: self.trace.cause.span, for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid), - make_region_vars: make_region_vars, + is_eq_relation: is_eq_relation, cycle_detected: false }; let u = ty.fold_with(&mut generalize); @@ -301,7 +264,7 @@ struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> { infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, span: Span, for_vid_sub_root: ty::TyVid, - make_region_vars: bool, + is_eq_relation: bool, cycle_detected: bool, } @@ -332,7 +295,7 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx self.fold_ty(u) } None => { - if self.make_region_vars { + if !self.is_eq_relation { let origin = variables.origin(vid); let new_var_id = variables.new_var(false, origin, None); let u = self.tcx().mk_var(new_var_id); @@ -379,7 +342,7 @@ impl<'cx, 'gcx, 'tcx> ty::fold::TypeFolder<'gcx, 'tcx> for Generalizer<'cx, 'gcx ty::ReScope(..) | ty::ReVar(..) | ty::ReFree(..) => { - if !self.make_region_vars { + if self.is_eq_relation { return r; } } diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs index 34bb9feb5c921..4ae2a8026409d 100644 --- a/src/librustc/infer/type_variable.rs +++ b/src/librustc/infer/type_variable.rs @@ -151,11 +151,10 @@ impl<'tcx> TypeVariableTable<'tcx> { /// Instantiates `vid` with the type `ty`. /// - /// Precondition: `vid` must be a root in the unification table - /// and has not previously been instantiated. + /// Precondition: `vid` must not have been previously instantiated. pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) { - debug_assert!(self.root_var(vid) == vid); - debug_assert!(self.probe(vid).is_none()); + let vid = self.root_var(vid); + debug_assert!(self.probe_root(vid).is_none()); let old_value = { let vid_data = &mut self.values[vid.index as usize];