From d0ec8ea1cc0c2b85406aff2cf5b55bf5aa551d8c Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 6 May 2018 22:48:56 +0100 Subject: [PATCH 1/6] Implement RFC 2056 - trivial constraints --- src/librustc/traits/mod.rs | 9 ----- src/librustc/traits/select.rs | 65 +++++++++++++++-------------------- 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index a765ffe2396bf..ca62b421b320d 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -641,17 +641,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()) // (*) .collect(); - // (*) Any predicate like `i32: Trait` or whatever doesn't - // need to be in the *environment* to be proven, so screen those - // out. This is important for the soundness of inter-fn - // caching. Note though that we should probably check that these - // predicates hold at the point where the environment is - // constructed, but I am not currently doing so out of laziness. - // -nmatsakis - debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 3ed1e7ea5ebd6..bd7ec4a12b0c6 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -305,9 +305,6 @@ enum BuiltinImplConditions<'tcx> { /// There is no built-in impl. There may be some other /// candidate (a where-clause or user-defined impl). None, - /// There is *no* impl for this, builtin or not. Ignore - /// all where-clauses. - Never, /// It is unknown whether there is an impl. Ambiguous } @@ -781,13 +778,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { mut obligation: TraitObligation<'tcx>) -> Result { - debug!("evaluate_trait_predicate_recursively({:?})", - obligation); + debug!("evaluate_trait_predicate_recursively({:?})", obligation); - if !self.intercrate.is_some() && obligation.is_global() { - // If a param env is consistent, global obligations do not depend on its particular - // value in order to work, so we can clear out the param env and get better - // caching. (If the current param env is inconsistent, we don't care what happens). + if self.intercrate.is_none() && obligation.is_global() + && obligation.param_env.caller_bounds.iter().all(|bound| bound.needs_subst()) { + // If a param env has no global bounds, global obligations do not + // depend on its particular value in order to work, so we can clear + // out the param env and get better caching. debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); obligation.param_env = obligation.param_env.without_caller_bounds(); } @@ -1451,22 +1448,22 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let sized_conditions = self.sized_conditions(obligation); self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates)?; - } else if lang_items.unsize_trait() == Some(def_id) { - self.assemble_candidates_for_unsizing(obligation, &mut candidates); - } else { - if lang_items.clone_trait() == Some(def_id) { - // Same builtin conditions as `Copy`, i.e. every type which has builtin support - // for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone` - // types have builtin support for `Clone`. - let clone_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; - } - - self.assemble_generator_candidates(obligation, &mut candidates)?; - self.assemble_closure_candidates(obligation, &mut candidates)?; - self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; - self.assemble_candidates_from_impls(obligation, &mut candidates)?; - self.assemble_candidates_from_object_ty(obligation, &mut candidates); + } else if lang_items.unsize_trait() == Some(def_id) { + self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else { + if lang_items.clone_trait() == Some(def_id) { + // Same builtin conditions as `Copy`, i.e. every type which has builtin support + // for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone` + // types have builtin support for `Clone`. + let clone_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; + } + + self.assemble_generator_candidates(obligation, &mut candidates)?; + self.assemble_closure_candidates(obligation, &mut candidates)?; + self.assemble_fn_pointer_candidates(obligation, &mut candidates)?; + self.assemble_candidates_from_impls(obligation, &mut candidates)?; + self.assemble_candidates_from_object_ty(obligation, &mut candidates); } self.assemble_candidates_from_projected_tys(obligation, &mut candidates); @@ -2081,13 +2078,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { // BUILTIN BOUNDS // // These cover the traits that are built-in to the language - // itself. This includes `Copy` and `Sized` for sure. For the - // moment, it also includes `Send` / `Sync` and a few others, but - // those will hopefully change to library-defined traits in the - // future. + // itself: `Copy`, `Clone` and `Sized`. - // HACK: if this returns an error, selection exits without considering - // other impls. fn assemble_builtin_bound_candidates<'o>(&mut self, conditions: BuiltinImplConditions<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>) @@ -2106,14 +2098,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { debug!("assemble_builtin_bound_candidates: ambiguous builtin"); Ok(candidates.ambiguous = true) } - BuiltinImplConditions::Never => { Err(Unimplemented) } } } fn sized_conditions(&mut self, obligation: &TraitObligation<'tcx>) -> BuiltinImplConditions<'tcx> { - use self::BuiltinImplConditions::{Ambiguous, None, Never, Where}; + use self::BuiltinImplConditions::{Ambiguous, None, Where}; // NOTE: binder moved to (*) let self_ty = self.infcx.shallow_resolve( @@ -2130,7 +2121,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Where(ty::Binder::dummy(Vec::new())) } - ty::TyStr | ty::TySlice(_) | ty::TyDynamic(..) | ty::TyForeign(..) => Never, + ty::TyStr | ty::TySlice(_) | ty::TyDynamic(..) | ty::TyForeign(..) => None, ty::TyTuple(tys) => { Where(ty::Binder::bind(tys.last().into_iter().cloned().collect())) @@ -2164,7 +2155,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { let self_ty = self.infcx.shallow_resolve( obligation.predicate.skip_binder().self_ty()); - use self::BuiltinImplConditions::{Ambiguous, None, Never, Where}; + use self::BuiltinImplConditions::{Ambiguous, None, Where}; match self_ty.sty { ty::TyInfer(ty::IntVar(_)) | ty::TyInfer(ty::FloatVar(_)) | @@ -2182,7 +2173,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) | ty::TyGenerator(..) | ty::TyGeneratorWitness(..) | ty::TyForeign(..) | ty::TyRef(_, _, hir::MutMutable) => { - Never + None } ty::TyArray(element_ty, _) => { @@ -2202,7 +2193,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { if is_copy_trait || is_clone_trait { Where(ty::Binder::bind(substs.upvar_tys(def_id, self.tcx()).collect())) } else { - Never + None } } From 27183a903034b1fc3aa3296e497a520508493911 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 6 May 2018 22:50:35 +0100 Subject: [PATCH 2/6] Feature gate trivial bounds --- src/librustc/traits/error_reporting.rs | 8 ++ src/librustc/traits/mod.rs | 3 + src/librustc/traits/structural_impls.rs | 1 + src/librustc_typeck/check/wfcheck.rs | 41 ++++++ src/libsyntax/feature_gate.rs | 3 + src/test/ui/feature-gate-trivial_bounds.rs | 78 +++++++++++ .../ui/feature-gate-trivial_bounds.stderr | 127 ++++++++++++++++++ 7 files changed, 261 insertions(+) create mode 100644 src/test/ui/feature-gate-trivial_bounds.rs create mode 100644 src/test/ui/feature-gate-trivial_bounds.stderr diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 25be4a2ff5c8b..040d738b98f02 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1475,6 +1475,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } ObligationCauseCode::ReturnType(_) | ObligationCauseCode::BlockTailExpression(_) => (), + ObligationCauseCode::TrivialBound => { + err.help("see issue #48214"); + if tcx.sess.opts.unstable_features.is_nightly_build() { + err.help("add #![feature(trivial_bounds)] to the \ + crate attributes to enable", + ); + } + } } } diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index ca62b421b320d..cfc4532a8f2c0 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -243,6 +243,9 @@ pub enum ObligationCauseCode<'tcx> { /// Block implicit return BlockTailExpression(ast::NodeId), + + /// #[feature(trivial_bounds)] is not enabled + TrivialBound, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index 822ea17009b6b..b9593047af415 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -243,6 +243,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { super::IntrinsicType => Some(super::IntrinsicType), super::MethodReceiver => Some(super::MethodReceiver), super::BlockTailExpression(id) => Some(super::BlockTailExpression(id)), + super::TrivialBound => Some(super::TrivialBound), } } } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index d0ff44c8e7e10..1a1076f74b2b2 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -46,6 +46,12 @@ impl<'a, 'gcx, 'tcx> CheckWfFcxBuilder<'a, 'gcx, 'tcx> { let param_env = self.param_env; self.inherited.enter(|inh| { let fcx = FnCtxt::new(&inh, param_env, id); + if !inh.tcx.features().trivial_bounds { + // As predicates are cached rather than obligations, this + // needsto be called first so that they are checked with an + // empty param_env. + check_false_global_bounds(&fcx, span, id); + } let wf_tys = f(&fcx, fcx.tcx.global_tcx()); fcx.select_all_obligations_or_error(); fcx.regionck_item(id, span, &wf_tys); @@ -660,6 +666,41 @@ fn reject_shadowing_type_parameters(tcx: TyCtxt, def_id: DefId) { } } +/// Feature gates RFC 2056 - trivial bounds, checking for global bounds that +/// aren't true. +fn check_false_global_bounds<'a, 'gcx, 'tcx>( + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + span: Span, + id: ast::NodeId, +) { + use rustc::ty::TypeFoldable; + + let empty_env = ty::ParamEnv::empty(); + + let def_id = fcx.tcx.hir.local_def_id(id); + let predicates = fcx.tcx.predicates_of(def_id).predicates; + // Check elaborated bounds + let implied_obligations = traits::elaborate_predicates(fcx.tcx, predicates); + + for pred in implied_obligations { + // HAS_LOCAL_NAMES is used to match the existing behvaiour. + if !pred.has_type_flags(ty::TypeFlags::HAS_LOCAL_NAMES) { + let obligation = traits::Obligation::new( + traits::ObligationCause::new( + span, + id, + traits::TrivialBound, + ), + empty_env, + pred, + ); + fcx.register_predicate(obligation); + } + } + + fcx.select_all_obligations_or_error(); +} + pub struct CheckTypeWellFormedVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index cb5125fe9ef5a..bf78723e41365 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -463,6 +463,9 @@ declare_features! ( // Allows use of the :literal macro fragment specifier (RFC 1576) (active, macro_literal_matcher, "1.27.0", Some(35625), None), + + // inconsistent bounds in where clauses + (active, trivial_bounds, "1.28.0", Some(48214), None), ); declare_features! ( diff --git a/src/test/ui/feature-gate-trivial_bounds.rs b/src/test/ui/feature-gate-trivial_bounds.rs new file mode 100644 index 0000000000000..ecc6896b75488 --- /dev/null +++ b/src/test/ui/feature-gate-trivial_bounds.rs @@ -0,0 +1,78 @@ +// 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. + +#![allow(unused)] +#![allow(type_alias_bounds)] + +pub trait Foo { + fn test(&self); +} + +fn generic_function(x: X) {} + +enum E where i32: Foo { V } //~ ERROR + +struct S where i32: Foo; //~ ERROR + +trait T where i32: Foo {} //~ ERROR + +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) { + 3i32.test(); + Foo::test(&4i32); + generic_function(5i32); + } +} + +fn f() where i32: Foo //~ ERROR +{ + let s = S; + 3i32.test(); + Foo::test(&4i32); + generic_function(5i32); +} + +fn use_op(s: String) -> String where String: ::std::ops::Neg { //~ ERROR + -s +} + +fn use_for() where i32: Iterator { //~ ERROR + for _ in 2i32 {} +} + +trait A {} + +impl A for i32 {} + +struct Dst { + x: X, +} + +struct TwoStrs(str, str) where str: Sized; //~ ERROR + +fn unsized_local() where Dst: Sized { //~ ERROR + let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); +} + +fn return_str() -> str where str: Sized { //~ ERROR + *"Sized".to_string().into_boxed_str() +} + +// This is currently accepted because the function pointer isn't +// considered global. +fn global_hr(x: fn(&())) where fn(&()): Foo { // OK + x.test(); +} + +fn main() {} diff --git a/src/test/ui/feature-gate-trivial_bounds.stderr b/src/test/ui/feature-gate-trivial_bounds.stderr new file mode 100644 index 0000000000000..0794e86175b25 --- /dev/null +++ b/src/test/ui/feature-gate-trivial_bounds.stderr @@ -0,0 +1,127 @@ +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/feature-gate-trivial_bounds.rs:20:1 + | +LL | enum E where i32: Foo { V } //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `i32` + | + = 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:22:1 + | +LL | struct S where i32: Foo; //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `i32` + | + = 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:24:1 + | +LL | trait T where i32: Foo {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `i32` + | + = 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:26:1 + | +LL | union U where i32: Foo { f: i32 } //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `i32` + | + = 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:30:1 + | +LL | / impl Foo for () where i32: Foo { //~ ERROR +LL | | fn test(&self) { +LL | | 3i32.test(); +LL | | Foo::test(&4i32); +LL | | generic_function(5i32); +LL | | } +LL | | } + | |_^ the trait `Foo` is not implemented for `i32` + | + = 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:38:1 + | +LL | / fn f() where i32: Foo //~ ERROR +LL | | { +LL | | let s = S; +LL | | 3i32.test(); +LL | | Foo::test(&4i32); +LL | | generic_function(5i32); +LL | | } + | |_^ the trait `Foo` is not implemented for `i32` + | + = help: see issue #48214 + = help: add #![feature(trivial_bounds)] to the crate attributes to enable + +error[E0277]: the trait bound `std::string::String: std::ops::Neg` is not satisfied + --> $DIR/feature-gate-trivial_bounds.rs:46:1 + | +LL | / fn use_op(s: String) -> String where String: ::std::ops::Neg { //~ ERROR +LL | | -s +LL | | } + | |_^ the trait `std::ops::Neg` is not implemented for `std::string::String` + | + = help: see issue #48214 + = help: add #![feature(trivial_bounds)] to the crate attributes to enable + +error[E0277]: the trait bound `i32: std::iter::Iterator` is not satisfied + --> $DIR/feature-gate-trivial_bounds.rs:50:1 + | +LL | / fn use_for() where i32: Iterator { //~ ERROR +LL | | for _ in 2i32 {} +LL | | } + | |_^ `i32` is not an iterator; maybe try calling `.iter()` or a similar method + | + = help: the trait `std::iter::Iterator` is not implemented for `i32` + = 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:1 + | +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` + = help: see issue #48214 + = help: add #![feature(trivial_bounds)] to the crate attributes to enable + +error[E0277]: the trait bound `A + 'static: std::marker::Sized` is not satisfied in `Dst` + --> $DIR/feature-gate-trivial_bounds.rs:64:1 + | +LL | / fn unsized_local() where Dst: Sized { //~ ERROR +LL | | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); +LL | | } + | |_^ `A + 'static` does not have a constant size known at compile-time + | + = help: within `Dst`, the trait `std::marker::Sized` is not implemented for `A + 'static` + = note: required because it appears within the type `Dst` + = 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:68:1 + | +LL | / fn return_str() -> str where str: Sized { //~ ERROR +LL | | *"Sized".to_string().into_boxed_str() +LL | | } + | |_^ `str` does not have a constant size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = help: see issue #48214 + = help: add #![feature(trivial_bounds)] to the crate attributes to enable + +error: aborting due to 11 previous errors + +For more information about this error, try `rustc --explain E0277`. From dabb820b002f738618371b66a4f3f4c6fee17e16 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 6 May 2018 22:52:58 +0100 Subject: [PATCH 3/6] Add trivial bounds lint --- src/librustc_lint/builtin.rs | 58 +++++++++++++++++++ src/librustc_lint/lib.rs | 1 + .../ui/feature-gate-trivial_bounds-lint.rs | 18 ++++++ src/test/ui/trivial-bounds-lint.rs | 50 ++++++++++++++++ src/test/ui/trivial-bounds-lint.stderr | 50 ++++++++++++++++ 5 files changed, 177 insertions(+) create mode 100644 src/test/ui/feature-gate-trivial_bounds-lint.rs create mode 100644 src/test/ui/trivial-bounds-lint.rs create mode 100644 src/test/ui/trivial-bounds-lint.stderr diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index c2dcbad8d2451..912d5c37ca3cd 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1591,3 +1591,61 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExternCrate { self.0 += 1; } } + +/// Lint for trait and lifetime bounds that don't depend on type parameters +/// which either do nothing, or stop the item from being used. +pub struct TrivialConstraints; + +declare_lint! { + TRIVIAL_BOUNDS, + Warn, + "these bounds don't depend on an type parameters" +} + +impl LintPass for TrivialConstraints { + fn get_lints(&self) -> LintArray { + lint_array!(TRIVIAL_BOUNDS) + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints { + fn check_item( + &mut self, + cx: &LateContext<'a, 'tcx>, + item: &'tcx hir::Item, + ) { + use rustc::ty::fold::TypeFoldable; + use rustc::ty::Predicate::*; + + + if cx.tcx.features().trivial_bounds { + let def_id = cx.tcx.hir.local_def_id(item.id); + let predicates = cx.tcx.predicates_of(def_id); + for predicate in &predicates.predicates { + let predicate_kind_name = match *predicate { + Trait(..) => "Trait", + TypeOutlives(..) | + RegionOutlives(..) => "Lifetime", + + // Ignore projections, as they can only be global + // if the trait bound is global + Projection(..) | + // Ignore bounds that a user can't type + WellFormed(..) | + ObjectSafe(..) | + ClosureKind(..) | + Subtype(..) | + ConstEvaluatable(..) => continue, + }; + if !predicate.is_global() { + cx.span_lint( + TRIVIAL_BOUNDS, + item.span, + &format!("{} bound {} does not depend on any type \ + or lifetime parameters", predicate_kind_name, predicate), + ); + } + } + } + } +} diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 39f550a4b459a..ae44ea6b65b9a 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -137,6 +137,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { UnreachablePub, TypeAliasBounds, UnusedBrokenConst, + TrivialConstraints, ); add_builtin_with_new!(sess, diff --git a/src/test/ui/feature-gate-trivial_bounds-lint.rs b/src/test/ui/feature-gate-trivial_bounds-lint.rs new file mode 100644 index 0000000000000..2d2d491bd4df3 --- /dev/null +++ b/src/test/ui/feature-gate-trivial_bounds-lint.rs @@ -0,0 +1,18 @@ +// 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 + +#![allow(unused)] +#![deny(trivial_bounds)] // Ignored without the trivial_bounds feature flag. + +struct A where i32: Copy; + +fn main() {} diff --git a/src/test/ui/trivial-bounds-lint.rs b/src/test/ui/trivial-bounds-lint.rs new file mode 100644 index 0000000000000..e6988cb9f8bfe --- /dev/null +++ b/src/test/ui/trivial-bounds-lint.rs @@ -0,0 +1,50 @@ +// 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)] +#![deny(trivial_bounds)] + +struct A where i32: Copy; //~ ERROR + +trait X {} + +trait Y: Copy {} + +trait Z { + type S: Copy; +} + +// Check only the bound the user writes trigger the lint +fn trivial_elaboration() where T: X + Z, i32: Y {} // OK + +fn global_param() where i32: X<()> {} //~ ERROR + +// Should only error on the trait bound, not the implicit +// projection bound ::S == i32. +fn global_projection() where i32: Z {} //~ ERROR + +impl A { + fn new() -> A { A } +} + +// Lifetime bounds should be linted as well +fn global_lifetimes() where i32: 'static, &'static str: 'static {} +//~^ ERROR +//~| ERROR + +fn local_lifetimes<'a>() where i32: 'a, &'a str: 'a {} // OK + +fn global_outlives() where 'static: 'static {} //~ ERROR + +// Check that each bound is checked individually +fn mixed_bounds() where i32: X + Copy {} //~ ERROR + +fn main() {} diff --git a/src/test/ui/trivial-bounds-lint.stderr b/src/test/ui/trivial-bounds-lint.stderr new file mode 100644 index 0000000000000..6a3e1981025cf --- /dev/null +++ b/src/test/ui/trivial-bounds-lint.stderr @@ -0,0 +1,50 @@ +error: Trait bound i32: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:15:1 + | +LL | struct A where i32: Copy; //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/trivial-bounds-lint.rs:13:9 + | +LL | #![deny(trivial_bounds)] + | ^^^^^^^^^^^^^^ + +error: Trait bound i32: X<()> does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:28:1 + | +LL | fn global_param() where i32: X<()> {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Trait bound i32: Z does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:32:1 + | +LL | fn global_projection() where i32: Z {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Lifetime bound i32 : 'static does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:39:1 + | +LL | fn global_lifetimes() where i32: 'static, &'static str: 'static {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Lifetime bound &'static str : 'static does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:39:1 + | +LL | fn global_lifetimes() where i32: 'static, &'static str: 'static {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Lifetime bound 'static : 'static does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:45:1 + | +LL | fn global_outlives() where 'static: 'static {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Trait bound i32: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-lint.rs:48:1 + | +LL | fn mixed_bounds() where i32: X + Copy {} //~ ERROR + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 7 previous errors + From 9b0dfe184e0d3a5fd607c01e0c861a4b601f32a7 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 6 May 2018 22:54:00 +0100 Subject: [PATCH 4/6] Add tests for trivial bounds --- ...ounds-inconsistent-associated-functions.rs | 29 +++++ ...s-inconsistent-associated-functions.stderr | 0 ...unds-inconsistent-copy-reborrow.nll.stderr | 19 +++ ...ivial-bounds-inconsistent-copy-reborrow.rs | 23 ++++ ...l-bounds-inconsistent-copy-reborrow.stderr | 19 +++ .../ui/trivial-bounds-inconsistent-copy.rs | 43 +++++++ .../trivial-bounds-inconsistent-copy.stderr | 41 +++++++ .../ui/trivial-bounds-inconsistent-sized.rs | 34 ++++++ .../trivial-bounds-inconsistent-sized.stderr | 24 ++++ ...trivial-bounds-inconsistent-well-formed.rs | 22 ++++ ...ial-bounds-inconsistent-well-formed.stderr | 20 ++++ src/test/ui/trivial-bounds-inconsistent.rs | 81 +++++++++++++ .../ui/trivial-bounds-inconsistent.stderr | 112 ++++++++++++++++++ src/test/ui/trivial-bounds-leak-copy.rs | 22 ++++ src/test/ui/trivial-bounds-leak-copy.stderr | 9 ++ src/test/ui/trivial-bounds-leak.rs | 42 +++++++ src/test/ui/trivial-bounds-leak.stderr | 47 ++++++++ 17 files changed, 587 insertions(+) create mode 100644 src/test/ui/trivial-bounds-inconsistent-associated-functions.rs create mode 100644 src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr create mode 100644 src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr create mode 100644 src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs create mode 100644 src/test/ui/trivial-bounds-inconsistent-copy-reborrow.stderr create mode 100644 src/test/ui/trivial-bounds-inconsistent-copy.rs create mode 100644 src/test/ui/trivial-bounds-inconsistent-copy.stderr create mode 100644 src/test/ui/trivial-bounds-inconsistent-sized.rs create mode 100644 src/test/ui/trivial-bounds-inconsistent-sized.stderr create mode 100644 src/test/ui/trivial-bounds-inconsistent-well-formed.rs create mode 100644 src/test/ui/trivial-bounds-inconsistent-well-formed.stderr create mode 100644 src/test/ui/trivial-bounds-inconsistent.rs create mode 100644 src/test/ui/trivial-bounds-inconsistent.stderr create mode 100644 src/test/ui/trivial-bounds-leak-copy.rs create mode 100644 src/test/ui/trivial-bounds-leak-copy.stderr create mode 100644 src/test/ui/trivial-bounds-leak.rs create mode 100644 src/test/ui/trivial-bounds-leak.stderr diff --git a/src/test/ui/trivial-bounds-inconsistent-associated-functions.rs b/src/test/ui/trivial-bounds-inconsistent-associated-functions.rs new file mode 100644 index 0000000000000..49c9df95bc772 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-associated-functions.rs @@ -0,0 +1,29 @@ +// 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 +// Inconsistent bounds with trait implementations + +#![feature(trivial_bounds)] +#![allow(unused)] + +trait A { + fn foo(&self) -> Self where Self: Copy; +} + +impl A for str { + fn foo(&self) -> Self where Self: Copy { *"" } +} + +impl A for i32 { + fn foo(&self) -> Self { 3 } +} + +fn main() {} diff --git a/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr b/src/test/ui/trivial-bounds-inconsistent-associated-functions.stderr new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr new file mode 100644 index 0000000000000..66547863db2fd --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr @@ -0,0 +1,19 @@ +error[E0596]: cannot borrow immutable item `**t` as mutable + --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:16:5 + | +LL | *t //~ ERROR + | ^^ cannot borrow as mutable + | + = note: the value which is causing this path not to be mutable is...: `*t` + +error[E0596]: cannot borrow immutable item `**t` as mutable + --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:20:6 + | +LL | {*t} //~ ERROR + | ^^ cannot borrow as mutable + | + = note: the value which is causing this path not to be mutable is...: `*t` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs new file mode 100644 index 0000000000000..2c4d9d813856c --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.rs @@ -0,0 +1,23 @@ +// 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. + +// Check that reborrows are still illegal with Copy mutable references +#![feature(trivial_bounds)] +#![allow(unused)] + +fn reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy { + *t //~ ERROR +} + +fn copy_reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy { + {*t} //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.stderr b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.stderr new file mode 100644 index 0000000000000..bea2bb66857f2 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.stderr @@ -0,0 +1,19 @@ +error[E0389]: cannot borrow data mutably in a `&` reference + --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:16:5 + | +LL | fn reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy { + | --------------- use `&'a mut &'a mut i32` here to make mutable +LL | *t //~ ERROR + | ^^ assignment into an immutable reference + +error[E0389]: cannot borrow data mutably in a `&` reference + --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:20:6 + | +LL | fn copy_reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy { + | --------------- use `&'a mut &'a mut i32` here to make mutable +LL | {*t} //~ ERROR + | ^^ assignment into an immutable reference + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0389`. diff --git a/src/test/ui/trivial-bounds-inconsistent-copy.rs b/src/test/ui/trivial-bounds-inconsistent-copy.rs new file mode 100644 index 0000000000000..375885a02c75a --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-copy.rs @@ -0,0 +1,43 @@ +// 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 tautalogically false `Copy` bounds +#![feature(trivial_bounds)] +#![allow(unused)] + +fn copy_string(t: String) -> String where String: Copy { + is_copy(&t); + let x = t; + drop(t); + t +} + +fn copy_out_string(t: &String) -> String where String: Copy { + *t +} + +fn copy_string_with_param(x: String) where String: Copy { + let y = x; + let z = x; +} + +// Check that no reborrowing occurs +fn copy_mut<'a>(t: &&'a mut i32) -> &'a mut i32 where for<'b> &'b mut i32: Copy { + is_copy(t); + let x = *t; + drop(x); + x +} + +fn is_copy(t: &T) {} + + +fn main() {} diff --git a/src/test/ui/trivial-bounds-inconsistent-copy.stderr b/src/test/ui/trivial-bounds-inconsistent-copy.stderr new file mode 100644 index 0000000000000..ae63900575611 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-copy.stderr @@ -0,0 +1,41 @@ +warning: Trait bound std::string::String: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-copy.rs:16:1 + | +LL | / fn copy_string(t: String) -> String where String: Copy { +LL | | is_copy(&t); +LL | | let x = t; +LL | | drop(t); +LL | | t +LL | | } + | |_^ + | + = note: #[warn(trivial_bounds)] on by default + +warning: Trait bound std::string::String: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-copy.rs:23:1 + | +LL | / fn copy_out_string(t: &String) -> String where String: Copy { +LL | | *t +LL | | } + | |_^ + +warning: Trait bound std::string::String: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-copy.rs:27:1 + | +LL | / fn copy_string_with_param(x: String) where String: Copy { +LL | | let y = x; +LL | | let z = x; +LL | | } + | |_^ + +warning: Trait bound for<'b> &'b mut i32: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-copy.rs:33:1 + | +LL | / fn copy_mut<'a>(t: &&'a mut i32) -> &'a mut i32 where for<'b> &'b mut i32: Copy { +LL | | is_copy(t); +LL | | let x = *t; +LL | | drop(x); +LL | | x +LL | | } + | |_^ + diff --git a/src/test/ui/trivial-bounds-inconsistent-sized.rs b/src/test/ui/trivial-bounds-inconsistent-sized.rs new file mode 100644 index 0000000000000..14ba11c44de13 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-sized.rs @@ -0,0 +1,34 @@ +// 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 tautalogically false `Sized` bounds +#![feature(trivial_bounds)] +#![allow(unused)] + +trait A {} + +impl A for i32 {} + +struct T { + x: X, +} + +struct S(str, str) where str: Sized; + +fn unsized_local() where for<'a> T: Sized { + let x: T = *(Box::new(T { x: 1 }) as Box>); +} + +fn return_str() -> str where str: Sized { + *"Sized".to_string().into_boxed_str() +} + +fn main() {} diff --git a/src/test/ui/trivial-bounds-inconsistent-sized.stderr b/src/test/ui/trivial-bounds-inconsistent-sized.stderr new file mode 100644 index 0000000000000..ee2ff7d786139 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-sized.stderr @@ -0,0 +1,24 @@ +warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-sized.rs:24:1 + | +LL | struct S(str, str) where str: Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(trivial_bounds)] on by default + +warning: Trait bound for<'a> T: std::marker::Sized does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-sized.rs:26:1 + | +LL | / fn unsized_local() where for<'a> T: Sized { +LL | | let x: T = *(Box::new(T { x: 1 }) as Box>); +LL | | } + | |_^ + +warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-sized.rs:30:1 + | +LL | / fn return_str() -> str where str: Sized { +LL | | *"Sized".to_string().into_boxed_str() +LL | | } + | |_^ + diff --git a/src/test/ui/trivial-bounds-inconsistent-well-formed.rs b/src/test/ui/trivial-bounds-inconsistent-well-formed.rs new file mode 100644 index 0000000000000..5fcdbfc437a81 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-well-formed.rs @@ -0,0 +1,22 @@ +// 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 +// Test that inconsistent bounds are used in well-formedness checks +#![feature(trivial_bounds)] + +use std::fmt::Debug; + +pub fn foo() where Vec: Debug, str: Copy { + let x = vec![*"1"]; + println!("{:?}", x); +} + +fn main() {} diff --git a/src/test/ui/trivial-bounds-inconsistent-well-formed.stderr b/src/test/ui/trivial-bounds-inconsistent-well-formed.stderr new file mode 100644 index 0000000000000..b51ecd4990071 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent-well-formed.stderr @@ -0,0 +1,20 @@ +warning: Trait bound std::vec::Vec: std::fmt::Debug does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-well-formed.rs:17:1 + | +LL | / pub fn foo() where Vec: Debug, str: Copy { +LL | | let x = vec![*"1"]; +LL | | println!("{:?}", x); +LL | | } + | |_^ + | + = note: #[warn(trivial_bounds)] on by default + +warning: Trait bound str: std::marker::Copy does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent-well-formed.rs:17:1 + | +LL | / pub fn foo() where Vec: Debug, str: Copy { +LL | | let x = vec![*"1"]; +LL | | println!("{:?}", x); +LL | | } + | |_^ + diff --git a/src/test/ui/trivial-bounds-inconsistent.rs b/src/test/ui/trivial-bounds-inconsistent.rs new file mode 100644 index 0000000000000..2c8b873b8c946 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent.rs @@ -0,0 +1,81 @@ +// 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 tautalogically false bounds are accepted, and are used +// in type inference. +#![feature(trivial_bounds)] +#![allow(unused)] + +pub trait Foo { + fn test(&self); +} + +fn generic_function(x: X) {} + +enum E where i32: Foo { V } + +struct S where i32: Foo; + +trait T where i32: Foo {} + +union U where i32: Foo { f: i32 } + +type Y where i32: Foo = (); + +impl Foo for () where i32: Foo { + fn test(&self) { + 3i32.test(); + Foo::test(&4i32); + generic_function(5i32); + } +} + +fn f() where i32: Foo { + let s = S; + 3i32.test(); + Foo::test(&4i32); + generic_function(5i32); +} + +fn g() where &'static str: Foo { + "Foo".test(); + Foo::test(&"Foo"); + generic_function("Foo"); +} + +trait A {} + +impl A for i32 {} + +struct Dst { + x: X, +} + +struct TwoStrs(str, str) where str: Sized; + +fn unsized_local() where for<'a> Dst: Sized { + let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); +} + +fn return_str() -> str where str: Sized { + *"Sized".to_string().into_boxed_str() +} + +fn use_op(s: String) -> String where String: ::std::ops::Neg { + -s +} + +fn use_for() where i32: Iterator { + for _ in 2i32 {} +} + +fn main() {} diff --git a/src/test/ui/trivial-bounds-inconsistent.stderr b/src/test/ui/trivial-bounds-inconsistent.stderr new file mode 100644 index 0000000000000..ee3c751829477 --- /dev/null +++ b/src/test/ui/trivial-bounds-inconsistent.stderr @@ -0,0 +1,112 @@ +warning: Trait bound i32: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:24:1 + | +LL | enum E where i32: Foo { V } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: #[warn(trivial_bounds)] on by default + +warning: Trait bound i32: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:26:1 + | +LL | struct S where i32: Foo; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: Trait bound i32: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:28:1 + | +LL | trait T where i32: Foo {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: Trait bound i32: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:30:1 + | +LL | union U where i32: Foo { f: i32 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: where clauses are not enforced in type aliases + --> $DIR/trivial-bounds-inconsistent.rs:32:14 + | +LL | type Y where i32: Foo = (); + | ^^^^^^^^ + | + = note: #[warn(type_alias_bounds)] on by default + = help: the clause will not be checked when the type alias is used, and should be removed + +warning: Trait bound i32: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:32:1 + | +LL | type Y where i32: Foo = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: Trait bound i32: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:34:1 + | +LL | / impl Foo for () where i32: Foo { +LL | | fn test(&self) { +LL | | 3i32.test(); +LL | | Foo::test(&4i32); +LL | | generic_function(5i32); +LL | | } +LL | | } + | |_^ + +warning: Trait bound i32: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:42:1 + | +LL | / fn f() where i32: Foo { +LL | | let s = S; +LL | | 3i32.test(); +LL | | Foo::test(&4i32); +LL | | generic_function(5i32); +LL | | } + | |_^ + +warning: Trait bound &'static str: Foo does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:49:1 + | +LL | / fn g() where &'static str: Foo { +LL | | "Foo".test(); +LL | | Foo::test(&"Foo"); +LL | | generic_function("Foo"); +LL | | } + | |_^ + +warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:63:1 + | +LL | struct TwoStrs(str, str) where str: Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: Trait bound for<'a> Dst: std::marker::Sized does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:65:1 + | +LL | / fn unsized_local() where for<'a> Dst: Sized { +LL | | let x: Dst = *(Box::new(Dst { x: 1 }) as Box>); +LL | | } + | |_^ + +warning: Trait bound str: std::marker::Sized does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:69:1 + | +LL | / fn return_str() -> str where str: Sized { +LL | | *"Sized".to_string().into_boxed_str() +LL | | } + | |_^ + +warning: Trait bound std::string::String: std::ops::Neg does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:73:1 + | +LL | / fn use_op(s: String) -> String where String: ::std::ops::Neg { +LL | | -s +LL | | } + | |_^ + +warning: Trait bound i32: std::iter::Iterator does not depend on any type or lifetime parameters + --> $DIR/trivial-bounds-inconsistent.rs:77:1 + | +LL | / fn use_for() where i32: Iterator { +LL | | for _ in 2i32 {} +LL | | } + | |_^ + diff --git a/src/test/ui/trivial-bounds-leak-copy.rs b/src/test/ui/trivial-bounds-leak-copy.rs new file mode 100644 index 0000000000000..9850ec2bd1fc0 --- /dev/null +++ b/src/test/ui/trivial-bounds-leak-copy.rs @@ -0,0 +1,22 @@ +// 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. + +// Check that false Copy bounds don't leak +#![feature(trivial_bounds)] + +fn copy_out_string(t: &String) -> String where String: Copy { + *t +} + +fn move_out_string(t: &String) -> String { + *t //~ ERROR +} + +fn main() {} diff --git a/src/test/ui/trivial-bounds-leak-copy.stderr b/src/test/ui/trivial-bounds-leak-copy.stderr new file mode 100644 index 0000000000000..3c3fcbf9b803c --- /dev/null +++ b/src/test/ui/trivial-bounds-leak-copy.stderr @@ -0,0 +1,9 @@ +error[E0507]: cannot move out of borrowed content + --> $DIR/trivial-bounds-leak-copy.rs:19:5 + | +LL | *t //~ ERROR + | ^^ cannot move out of borrowed content + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/trivial-bounds-leak.rs b/src/test/ui/trivial-bounds-leak.rs new file mode 100644 index 0000000000000..98cb5b2b50332 --- /dev/null +++ b/src/test/ui/trivial-bounds-leak.rs @@ -0,0 +1,42 @@ +// 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. + +// Check that false bounds don't leak +#![feature(trivial_bounds)] + +pub trait Foo { + fn test(&self); +} + +fn return_str() -> str where str: Sized { + *"Sized".to_string().into_boxed_str() +} + +fn cant_return_str() -> str { //~ ERROR + *"Sized".to_string().into_boxed_str() +} + +fn my_function() where i32: Foo +{ + 3i32.test(); + Foo::test(&4i32); + generic_function(5i32); +} + +fn foo() { + 3i32.test(); //~ ERROR + Foo::test(&4i32); //~ ERROR + generic_function(5i32); //~ ERROR +} + +fn generic_function(t: T) {} + +fn main() {} + diff --git a/src/test/ui/trivial-bounds-leak.stderr b/src/test/ui/trivial-bounds-leak.stderr new file mode 100644 index 0000000000000..df91ba0dd2ac5 --- /dev/null +++ b/src/test/ui/trivial-bounds-leak.stderr @@ -0,0 +1,47 @@ +error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied + --> $DIR/trivial-bounds-leak.rs:22:25 + | +LL | fn cant_return_str() -> str { //~ ERROR + | ^^^ `str` does not have a constant size known at compile-time + | + = help: the trait `std::marker::Sized` is not implemented for `str` + = note: the return type of a function must have a statically known size + +error[E0599]: no method named `test` found for type `i32` in the current scope + --> $DIR/trivial-bounds-leak.rs:34:10 + | +LL | 3i32.test(); //~ ERROR + | ^^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `test`, perhaps you need to implement it: + candidate #1: `Foo` + +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/trivial-bounds-leak.rs:35:5 + | +LL | Foo::test(&4i32); //~ ERROR + | ^^^^^^^^^ the trait `Foo` is not implemented for `i32` + | +note: required by `Foo::test` + --> $DIR/trivial-bounds-leak.rs:15:5 + | +LL | fn test(&self); + | ^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `i32: Foo` is not satisfied + --> $DIR/trivial-bounds-leak.rs:36:5 + | +LL | generic_function(5i32); //~ ERROR + | ^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `i32` + | +note: required by `generic_function` + --> $DIR/trivial-bounds-leak.rs:39:1 + | +LL | fn generic_function(t: T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +Some errors occurred: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. From dc8ce4c139d9f75557fdccad0795d0a149f29d68 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 6 May 2018 22:57:49 +0100 Subject: [PATCH 5/6] Update existing tests for trivial bounds changes --- src/test/compile-fail/cross-fn-cache-hole.rs | 7 +++---- src/test/compile-fail/issue-42796.rs | 2 +- src/test/ui/issue-48728.stderr | 2 ++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/test/compile-fail/cross-fn-cache-hole.rs b/src/test/compile-fail/cross-fn-cache-hole.rs index b034fedb805e3..d437fc019fda0 100644 --- a/src/test/compile-fail/cross-fn-cache-hole.rs +++ b/src/test/compile-fail/cross-fn-cache-hole.rs @@ -11,10 +11,9 @@ // Check that when there are vacuous predicates in the environment // (which make a fn uncallable) we don't erroneously cache those and // then consider them satisfied elsewhere. The current technique for -// doing this is just to filter "global" predicates out of the -// environment, which means that we wind up with an error in the -// function `vacuous`, because even though `i32: Bar` is implied -// by its where clause, that where clause never holds. +// doing this is to not use global caches when there is a chance that +// the environment contains such a predicate. +// We still error for `i32: Bar` pending #48214 trait Foo: Bar { } diff --git a/src/test/compile-fail/issue-42796.rs b/src/test/compile-fail/issue-42796.rs index 10622eccbdcd1..b07c23c3fc72d 100644 --- a/src/test/compile-fail/issue-42796.rs +++ b/src/test/compile-fail/issue-42796.rs @@ -17,7 +17,7 @@ impl Mirror for T { } pub fn poison(victim: String) where >::Image: Copy { - loop { drop(victim); } //~ ERROR use of moved value + loop { drop(victim); } } fn main() { diff --git a/src/test/ui/issue-48728.stderr b/src/test/ui/issue-48728.stderr index 05c87fe66ee85..937266d6d5573 100644 --- a/src/test/ui/issue-48728.stderr +++ b/src/test/ui/issue-48728.stderr @@ -6,6 +6,8 @@ LL | #[derive(Clone)] //~ ERROR conflicting implementations of trait `std::clone ... LL | impl Clone for Node<[T]> { | ------------------------------------------- first implementation here + | + = note: upstream crates may add new impl of trait `std::clone::Clone` for type `[_]` in future versions error: aborting due to previous error From be2900c33b043ca2002bdb11870e8d26c3e410f3 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Tue, 15 May 2018 21:48:35 +0100 Subject: [PATCH 6/6] Make is_global true for latebound regions --- src/librustc/traits/fulfill.rs | 2 +- src/librustc/ty/flags.rs | 8 ++++---- src/librustc/ty/fold.rs | 10 +++++++--- src/librustc/ty/mod.rs | 11 ++++++++--- src/librustc/ty/sty.rs | 8 +++++--- src/librustc_lint/builtin.rs | 2 +- src/librustc_typeck/check/wfcheck.rs | 4 ++-- 7 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 6e20150718110..94ee39470772f 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -330,7 +330,7 @@ fn process_predicate<'a, 'gcx, 'tcx>( ty::Predicate::Trait(ref data) => { let trait_obligation = obligation.with(data.clone()); - if data.is_global() { + if data.is_global() && !data.has_late_bound_regions() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. if selcx.infcx().predicate_must_hold(&obligation) { diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 01de848e0f076..e913f8f568ada 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -79,7 +79,7 @@ impl FlagComputation { } &ty::TyParam(ref p) => { - self.add_flags(TypeFlags::HAS_LOCAL_NAMES); + self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); if p.is_self() { self.add_flags(TypeFlags::HAS_SELF); } else { @@ -89,7 +89,7 @@ impl FlagComputation { &ty::TyGenerator(_, ref substs, _) => { self.add_flags(TypeFlags::HAS_TY_CLOSURE); - self.add_flags(TypeFlags::HAS_LOCAL_NAMES); + self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); self.add_substs(&substs.substs); } @@ -101,12 +101,12 @@ impl FlagComputation { &ty::TyClosure(_, ref substs) => { self.add_flags(TypeFlags::HAS_TY_CLOSURE); - self.add_flags(TypeFlags::HAS_LOCAL_NAMES); + self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); self.add_substs(&substs.substs); } &ty::TyInfer(infer) => { - self.add_flags(TypeFlags::HAS_LOCAL_NAMES); // it might, right? + self.add_flags(TypeFlags::HAS_FREE_LOCAL_NAMES); // it might, right? self.add_flags(TypeFlags::HAS_TY_INFER); match infer { ty::FreshTy(_) | diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 1793b5e1edba8..a1f9fd76b02dc 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -116,10 +116,14 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { /// Indicates whether this value references only 'global' /// types/lifetimes that are the same regardless of what fn we are - /// in. This is used for caching. Errs on the side of returning - /// false. + /// in. This is used for caching. fn is_global(&self) -> bool { - !self.has_type_flags(TypeFlags::HAS_LOCAL_NAMES) + !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES) + } + + /// True if there are any late-bound regions + fn has_late_bound_regions(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND) } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index eede7bd2ea619..01dbef06792cf 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -441,7 +441,7 @@ bitflags! { // true if there are "names" of types and regions and so forth // that are local to a particular fn - const HAS_LOCAL_NAMES = 1 << 10; + const HAS_FREE_LOCAL_NAMES = 1 << 10; // Present if the type belongs in a local type context. // Only set for TyInfer other than Fresh. @@ -455,6 +455,10 @@ bitflags! { // ought to be true only for the results of canonicalization. const HAS_CANONICAL_VARS = 1 << 13; + /// Does this have any `ReLateBound` regions? Used to check + /// if a global bound is safe to evaluate. + const HAS_RE_LATE_BOUND = 1 << 14; + const NEEDS_SUBST = TypeFlags::HAS_PARAMS.bits | TypeFlags::HAS_SELF.bits | TypeFlags::HAS_RE_EARLY_BOUND.bits; @@ -472,9 +476,10 @@ bitflags! { TypeFlags::HAS_TY_ERR.bits | TypeFlags::HAS_PROJECTION.bits | TypeFlags::HAS_TY_CLOSURE.bits | - TypeFlags::HAS_LOCAL_NAMES.bits | + TypeFlags::HAS_FREE_LOCAL_NAMES.bits | TypeFlags::KEEP_IN_LOCAL_TCX.bits | - TypeFlags::HAS_CANONICAL_VARS.bits; + TypeFlags::HAS_CANONICAL_VARS.bits | + TypeFlags::HAS_RE_LATE_BOUND.bits; } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index f0a7ce5497166..b0eb8ecab2506 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1268,7 +1268,9 @@ impl RegionKind { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_SKOL; } - ty::ReLateBound(..) => { } + ty::ReLateBound(..) => { + flags = flags | TypeFlags::HAS_RE_LATE_BOUND; + } ty::ReEarlyBound(..) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; flags = flags | TypeFlags::HAS_RE_EARLY_BOUND; @@ -1291,8 +1293,8 @@ impl RegionKind { } match *self { - ty::ReStatic | ty::ReEmpty | ty::ReErased => (), - _ => flags = flags | TypeFlags::HAS_LOCAL_NAMES, + ty::ReStatic | ty::ReEmpty | ty::ReErased | ty::ReLateBound(..) => (), + _ => flags = flags | TypeFlags::HAS_FREE_LOCAL_NAMES, } debug!("type_flags({:?}) = {:?}", self, flags); diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 912d5c37ca3cd..0b1e9081a725e 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1637,7 +1637,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TrivialConstraints { Subtype(..) | ConstEvaluatable(..) => continue, }; - if !predicate.is_global() { + if predicate.is_global() { cx.span_lint( TRIVIAL_BOUNDS, item.span, diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 1a1076f74b2b2..21f6f62517ba6 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -683,8 +683,8 @@ fn check_false_global_bounds<'a, 'gcx, 'tcx>( let implied_obligations = traits::elaborate_predicates(fcx.tcx, predicates); for pred in implied_obligations { - // HAS_LOCAL_NAMES is used to match the existing behvaiour. - if !pred.has_type_flags(ty::TypeFlags::HAS_LOCAL_NAMES) { + // Match the existing behavior. + if pred.is_global() && !pred.has_late_bound_regions() { let obligation = traits::Obligation::new( traits::ObligationCause::new( span,