diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index 3bd4e11b0e8b9..e284b3fc75a64 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -648,13 +648,8 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let predicates: Vec<_> = util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.to_vec()) - .filter(|p| !p.is_global() || p.has_late_bound_regions()) // (*) .collect(); - // (*) FIXME(#50825) This shouldn't be needed. - // Removing the bounds here stopped them from being prefered in selection. - // See the issue-50825 ui tests for examples - debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 7a52a5cbf5acc..08af80543dff2 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -2011,9 +2011,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // attempt to evaluate recursive bounds to see if they are // satisfied. - /// Returns true if `candidate_i` should be dropped in favor of - /// `candidate_j`. Generally speaking we will drop duplicate - /// candidates and prefer where-clause candidates. /// Returns true if `victim` should be dropped in favor of /// `other`. Generally speaking we will drop duplicate /// candidates and prefer where-clause candidates. @@ -2025,13 +2022,46 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { other: &EvaluatedCandidate<'tcx>) -> bool { + // Check if a bound would previously have been removed when normalizing + // the param_env so that it can be given the lowest priority. See + // #50825 for the motivation for this. + let is_global = |cand: &ty::PolyTraitRef<'_>| { + cand.is_global() && !cand.has_late_bound_regions() + }; + if victim.candidate == other.candidate { return true; } match other.candidate { + ParamCandidate(ref cand) => match victim.candidate { + AutoImplCandidate(..) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates"); + } + ImplCandidate(..) | + ClosureCandidate | + GeneratorCandidate | + FnPointerCandidate | + BuiltinObjectCandidate | + BuiltinUnsizeCandidate | + BuiltinCandidate { .. } => { + // Global bounds from the where clause should be ignored + // here (see issue #50825). Otherwise, we have a where + // clause so don't go around looking for impls. + !is_global(cand) + } + ObjectCandidate | + ProjectionCandidate => { + // Arbitrarily give param candidates priority + // over projection and object candidates. + !is_global(cand) + }, + ParamCandidate(..) => false, + }, ObjectCandidate | - ParamCandidate(_) | ProjectionCandidate => match victim.candidate { + ProjectionCandidate => match victim.candidate { AutoImplCandidate(..) => { bug!( "default implementations shouldn't be recorded \ @@ -2044,8 +2074,6 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { BuiltinObjectCandidate | BuiltinUnsizeCandidate | BuiltinCandidate { .. } => { - // We have a where-clause so don't go around looking - // for impls. true } ObjectCandidate | @@ -2054,22 +2082,44 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // over projection and object candidates. true }, - ParamCandidate(..) => false, + ParamCandidate(ref cand) => is_global(cand), }, ImplCandidate(other_def) => { // See if we can toss out `victim` based on specialization. // This requires us to know *for sure* that the `other` impl applies // i.e. EvaluatedToOk: if other.evaluation == EvaluatedToOk { - if let ImplCandidate(victim_def) = victim.candidate { - let tcx = self.tcx().global_tcx(); - return tcx.specializes((other_def, victim_def)) || - tcx.impls_are_allowed_to_overlap(other_def, victim_def); + match victim.candidate { + ImplCandidate(victim_def) => { + let tcx = self.tcx().global_tcx(); + return tcx.specializes((other_def, victim_def)) || + tcx.impls_are_allowed_to_overlap(other_def, victim_def); + } + ParamCandidate(ref cand) => { + // Prefer the impl to a global where clause candidate. + return is_global(cand); + } + _ => () } } false }, + ClosureCandidate | + GeneratorCandidate | + FnPointerCandidate | + BuiltinObjectCandidate | + BuiltinUnsizeCandidate | + BuiltinCandidate { .. } => { + match victim.candidate { + ParamCandidate(ref cand) => { + // Prefer these to a global where-clause bound + // (see issue #50825) + is_global(cand) && other.evaluation == EvaluatedToOk + } + _ => false, + } + } _ => false } } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 4b4b982b1be41..65725bfb95d0e 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -711,6 +711,7 @@ fn check_false_global_bounds<'a, 'gcx, 'tcx>( for pred in implied_obligations { // Match the existing behavior. if pred.is_global() && !pred.has_late_bound_regions() { + let pred = fcx.normalize_associated_types_in(span, &pred); let obligation = traits::Obligation::new( traits::ObligationCause::new( span, diff --git a/src/test/ui/feature-gate-trivial_bounds.rs b/src/test/ui/feature-gate-trivial_bounds.rs index e72b8782e50d5..dba66e0b69bfc 100644 --- a/src/test/ui/feature-gate-trivial_bounds.rs +++ b/src/test/ui/feature-gate-trivial_bounds.rs @@ -28,7 +28,7 @@ union U where i32: Foo { f: i32 } //~ ERROR type Y where i32: Foo = (); // OK - bound is ignored impl Foo for () where i32: Foo { //~ ERROR - fn test(&self) { //~ ERROR + fn test(&self) { 3i32.test(); Foo::test(&4i32); generic_function(5i32); @@ -60,7 +60,7 @@ struct Dst { } struct TwoStrs(str, str) where str: Sized; //~ ERROR -//~^ ERROR + fn unsized_local() where Dst: Sized { //~ ERROR let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); diff --git a/src/test/ui/feature-gate-trivial_bounds.stderr b/src/test/ui/feature-gate-trivial_bounds.stderr index 32b263119e5e2..9c2c80600b8c8 100644 --- a/src/test/ui/feature-gate-trivial_bounds.stderr +++ b/src/test/ui/feature-gate-trivial_bounds.stderr @@ -38,7 +38,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied --> $DIR/feature-gate-trivial_bounds.rs:30:1 | LL | / impl Foo for () where i32: Foo { //~ ERROR -LL | | fn test(&self) { //~ ERROR +LL | | fn test(&self) { LL | | 3i32.test(); LL | | Foo::test(&4i32); LL | | generic_function(5i32); @@ -97,15 +97,6 @@ LL | struct TwoStrs(str, str) where str: Sized; //~ ERROR = help: see issue #48214 = help: add #![feature(trivial_bounds)] to the crate attributes to enable -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied - --> $DIR/feature-gate-trivial_bounds.rs:62:16 - | -LL | struct TwoStrs(str, str) where str: Sized; //~ ERROR - | ^^^ `str` does not have a constant size known at compile-time - | - = help: the trait `std::marker::Sized` is not implemented for `str` - = note: only the last field of a struct may have a dynamically sized type - error[E0277]: the trait bound `A + 'static: std::marker::Sized` is not satisfied in `Dst` --> $DIR/feature-gate-trivial_bounds.rs:65:1 | @@ -131,22 +122,6 @@ LL | | } = help: see issue #48214 = help: add #![feature(trivial_bounds)] to the crate attributes to enable -error[E0277]: the trait bound `i32: Foo` is not satisfied - --> $DIR/feature-gate-trivial_bounds.rs:31:5 - | -LL | / fn test(&self) { //~ ERROR -LL | | 3i32.test(); -LL | | Foo::test(&4i32); -LL | | generic_function(5i32); -LL | | } - | |_____^ the trait `Foo` is not implemented for `i32` - | -note: required by `Foo` - --> $DIR/feature-gate-trivial_bounds.rs:14:1 - | -LL | pub trait Foo { - | ^^^^^^^^^^^^^ - -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/issue-50825-1.rs b/src/test/ui/issue-50825-1.rs index d179530c01485..1eee9b7c883c6 100644 --- a/src/test/ui/issue-50825-1.rs +++ b/src/test/ui/issue-50825-1.rs @@ -10,7 +10,7 @@ // run-pass // regression test for issue #50825 -// Make sure that the `impl` bound (): X is prefered over +// Make sure that the `impl` bound (): X is preferred over // the (): X bound in the where clause. trait X { diff --git a/src/test/ui/issue-50825.rs b/src/test/ui/issue-50825.rs index bc15760e77c57..e45156b96cd2c 100644 --- a/src/test/ui/issue-50825.rs +++ b/src/test/ui/issue-50825.rs @@ -10,7 +10,7 @@ // run-pass // regression test for issue #50825 -// Make sure that the built-in bound {integer}: Sized is prefered over +// Make sure that the built-in bound {integer}: Sized is preferred over // the u64: Sized bound in the where clause. fn foo(y: &[()]) diff --git a/src/test/ui/issue-51044.rs b/src/test/ui/issue-51044.rs new file mode 100644 index 0000000000000..6424c4229bda8 --- /dev/null +++ b/src/test/ui/issue-51044.rs @@ -0,0 +1,40 @@ +// Copyright 2018 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. + +// run-pass +// regression test for issue #50825 +// Check that the feature gate normalizes associated types. + +#![allow(dead_code)] +struct Foo(T); +struct Duck; +struct Quack; + +trait Hello where A: Animal { +} + +trait Animal { + type Noise; +} + +trait Loud { +} + +impl Loud for f32 { +} + +impl Animal for Duck { + type Noise = Quack; +} + +impl Hello for Foo where f32: Loud<::Noise> { +} + +fn main() {} diff --git a/src/test/ui/trivial-bounds-inconsistent-associated-functions.rs b/src/test/ui/trivial-bounds-inconsistent-associated-functions.rs index 4cacbc2c91445..49c9df95bc772 100644 --- a/src/test/ui/trivial-bounds-inconsistent-associated-functions.rs +++ b/src/test/ui/trivial-bounds-inconsistent-associated-functions.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // run-pass // Inconsistent bounds with trait implementations diff --git a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs index a743b4296980d..2c4d9d813856c 100644 --- a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs +++ b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // Check that reborrows are still illegal with Copy mutable references #![feature(trivial_bounds)] #![allow(unused)] diff --git a/src/test/ui/trivial-bounds-inconsistent-copy.rs b/src/test/ui/trivial-bounds-inconsistent-copy.rs index f73c96a002d43..375885a02c75a 100644 --- a/src/test/ui/trivial-bounds-inconsistent-copy.rs +++ b/src/test/ui/trivial-bounds-inconsistent-copy.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // run-pass // Check tautalogically false `Copy` bounds #![feature(trivial_bounds)] diff --git a/src/test/ui/trivial-bounds-inconsistent-projection-error.rs b/src/test/ui/trivial-bounds-inconsistent-projection-error.rs new file mode 100644 index 0000000000000..1a3bd3a8cd7cc --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-projection-error.rs @@ -0,0 +1,33 @@ +// Copyright 2018 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(trivial_bounds)] +#![allow(unused)] + +struct B; + +trait A { + type X; + fn get_x() -> Self::X; +} + +impl A for B { + type X = u8; + fn get_x() -> u8 { 0 } +} + +fn global_bound_is_hidden() -> u8 +where + B: A +{ + B::get_x() //~ ERROR +} + +fn main () {} diff --git a/src/test/ui/trivial-bounds-inconsistent-projection-error.stderr b/src/test/ui/trivial-bounds-inconsistent-projection-error.stderr new file mode 100644 index 0000000000000..0f720bee2b4e6 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-projection-error.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/trivial-bounds-inconsistent-projection-error.rs:30:5 + | +LL | fn global_bound_is_hidden() -> u8 + | -- expected `u8` because of return type +... +LL | B::get_x() //~ ERROR + | ^^^^^^^^^^ expected u8, found i32 + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/trivial-bounds-inconsistent-projection.rs b/src/test/ui/trivial-bounds-inconsistent-projection.rs new file mode 100644 index 0000000000000..8de6f06bf5f5d --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-projection.rs @@ -0,0 +1,64 @@ +// Copyright 2018 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. + +// run-pass +// Check that global bounds result in the expected choice of associated type + +#![feature(trivial_bounds)] +#![allow(unused)] + +struct B; + +trait A { + type X; + fn get_x() -> Self::X; +} + +impl A for B { + type X = u8; + fn get_x() -> u8 { 0 } +} + +fn underspecified_bound() -> u8 +where + B: A +{ + B::get_x() +} + +fn inconsistent_bound() -> i32 +where + B: A +{ + B::get_x() +} + +fn redundant_bound() -> u8 +where + B: A +{ + B::get_x() +} + +fn inconsistent_dup_bound() -> i32 +where + B: A + A +{ + B::get_x() +} + +fn redundant_dup_bound() -> u8 +where + B: A + A +{ + B::get_x() +} + +fn main () {} diff --git a/src/test/ui/trivial-bounds-inconsistent-projection.stderr b/src/test/ui/trivial-bounds-inconsistent-projection.stderr new file mode 100644 index 0000000000000..201a041830f84 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-projection.stderr @@ -0,0 +1,57 @@ +warning: Trait bound B: A does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-projection.rs:29:1 + | +LL | / fn underspecified_bound() -> u8 +LL | | where +LL | | B: A +LL | | { +LL | | B::get_x() +LL | | } + | |_^ + | + = note: #[warn(trivial_bounds)] on by default + +warning: Trait bound B: A does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-projection.rs:36:1 + | +LL | / fn inconsistent_bound() -> i32 +LL | | where +LL | | B: A +LL | | { +LL | | B::get_x() +LL | | } + | |_^ + +warning: Trait bound B: A does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-projection.rs:43:1 + | +LL | / fn redundant_bound() -> u8 +LL | | where +LL | | B: A +LL | | { +LL | | B::get_x() +LL | | } + | |_^ + +warning: Trait bound B: A does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-projection.rs:50:1 + | +LL | / fn inconsistent_dup_bound() -> i32 +LL | | where +LL | | B: A + A +LL | | { +LL | | B::get_x() +LL | | } + | |_^ + +warning: Trait bound B: A does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-projection.rs:57:1 + | +LL | / fn redundant_dup_bound() -> u8 +LL | | where +LL | | B: A + A +LL | | { +LL | | B::get_x() +LL | | } + | |_^ + diff --git a/src/test/ui/trivial-bounds-inconsistent-sized.rs b/src/test/ui/trivial-bounds-inconsistent-sized.rs index 11f0080fbabf1..14ba11c44de13 100644 --- a/src/test/ui/trivial-bounds-inconsistent-sized.rs +++ b/src/test/ui/trivial-bounds-inconsistent-sized.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // run-pass // Check tautalogically false `Sized` bounds #![feature(trivial_bounds)] diff --git a/src/test/ui/trivial-bounds-inconsistent-well-formed.rs b/src/test/ui/trivial-bounds-inconsistent-well-formed.rs index a78ecdc8ff3d4..5fcdbfc437a81 100644 --- a/src/test/ui/trivial-bounds-inconsistent-well-formed.rs +++ b/src/test/ui/trivial-bounds-inconsistent-well-formed.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // run-pass // Test that inconsistent bounds are used in well-formedness checks #![feature(trivial_bounds)] diff --git a/src/test/ui/trivial-bounds-inconsistent.rs b/src/test/ui/trivial-bounds-inconsistent.rs index c8e8c320bc5ff..2c8b873b8c946 100644 --- a/src/test/ui/trivial-bounds-inconsistent.rs +++ b/src/test/ui/trivial-bounds-inconsistent.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // run-pass // Check that tautalogically false bounds are accepted, and are used diff --git a/src/test/ui/trivial-bounds-leak-copy.rs b/src/test/ui/trivial-bounds-leak-copy.rs index 6f000006ca9b0..9850ec2bd1fc0 100644 --- a/src/test/ui/trivial-bounds-leak-copy.rs +++ b/src/test/ui/trivial-bounds-leak-copy.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // Check that false Copy bounds don't leak #![feature(trivial_bounds)] diff --git a/src/test/ui/trivial-bounds-leak.rs b/src/test/ui/trivial-bounds-leak.rs index 15dee64f70e75..98cb5b2b50332 100644 --- a/src/test/ui/trivial-bounds-leak.rs +++ b/src/test/ui/trivial-bounds-leak.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) // Check that false bounds don't leak #![feature(trivial_bounds)] diff --git a/src/test/ui/trivial-bounds-lint.rs b/src/test/ui/trivial-bounds-lint.rs index e37600a653a90..e6988cb9f8bfe 100644 --- a/src/test/ui/trivial-bounds-lint.rs +++ b/src/test/ui/trivial-bounds-lint.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-test FIXME(#50825) #![feature(trivial_bounds)] #![allow(unused)] #![deny(trivial_bounds)] diff --git a/src/test/ui/trivial-bounds-object.rs b/src/test/ui/trivial-bounds-object.rs new file mode 100644 index 0000000000000..00986eefbda7d --- /dev/null +++ b/src/test/ui/trivial-bounds-object.rs @@ -0,0 +1,28 @@ +// Copyright 2018 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. + +// run-pass +// Check that the object bound dyn A + 'a: A is preferred over the +// where clause bound dyn A + 'static: A. + +#![allow(unused)] + +trait A { + fn test(&self); +} + +fn foo(x: &dyn A) +where + dyn A + 'static: A, // Using this bound would lead to a lifetime error. +{ + x.test(); +} + +fn main () {}