From 253408b4090bc15b88bb5faecaf1e9765be80587 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 10 May 2022 14:19:19 +0000 Subject: [PATCH] Check that closures satisfy their where bounds --- .../rustc_trait_selection/src/traits/wf.rs | 24 ++-- .../generic_const_exprs/closures.rs | 2 +- .../generic_const_exprs/closures.stderr | 25 +++- .../issue-59311.nll.stderr | 18 +++ .../higher-rank-trait-bounds/issue-59311.rs | 2 +- .../issue-59311.stderr | 13 +- .../ui/type-alias-impl-trait/issue-53092.rs | 14 ++ .../type-alias-impl-trait/issue-53092.stderr | 19 +++ .../issue-60564-working.rs | 24 ++++ .../ui/type-alias-impl-trait/issue-60564.rs | 7 + .../type-alias-impl-trait/issue-60564.stderr | 121 +++++++++++++++++- .../type-alias-impl-trait/wf-check-fn-def.rs | 18 +++ .../wf-check-fn-def.stderr | 19 +++ .../type-alias-impl-trait/wf-check-fn-ptrs.rs | 23 ++++ .../wf_check_closures.rs | 17 +++ .../wf_check_closures.stderr | 19 +++ 16 files changed, 343 insertions(+), 22 deletions(-) create mode 100644 src/test/ui/higher-rank-trait-bounds/issue-59311.nll.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-53092.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-53092.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-60564-working.rs create mode 100644 src/test/ui/type-alias-impl-trait/wf-check-fn-def.rs create mode 100644 src/test/ui/type-alias-impl-trait/wf-check-fn-def.stderr create mode 100644 src/test/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs create mode 100644 src/test/ui/type-alias-impl-trait/wf_check_closures.rs create mode 100644 src/test/ui/type-alias-impl-trait/wf_check_closures.stderr diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index de0ade64247..ca40c3452e2 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -575,7 +575,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // generators don't take arguments. } - ty::Closure(_, substs) => { + ty::Closure(did, substs) => { // Only check the upvar types for WF, not the rest // of the types within. This is needed because we // capture the signature and it may not be WF @@ -596,18 +596,26 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // probably always be WF, because it should be // shorthand for something like `where(T: 'a) { // fn(&'a T) }`, as discussed in #25860. - // - // Note that we are also skipping the generic - // types. This is consistent with the `outlives` - // code, but anyway doesn't matter: within the fn + walker.skip_current_subtree(); // subtree handled below + // FIXME(eddyb) add the type to `walker` instead of recursing. + self.compute(substs.as_closure().tupled_upvars_ty().into()); + // Note that we cannot skip the generic types + // types. Normally, within the fn // body where they are created, the generics will // always be WF, and outside of that fn body we // are not directly inspecting closure types // anyway, except via auto trait matching (which // only inspects the upvar types). - walker.skip_current_subtree(); // subtree handled below - // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(substs.as_closure().tupled_upvars_ty().into()); + // But when a closure is part of a type-alias-impl-trait + // then the function that created the defining site may + // have had more bounds available than the type alias + // specifies. This may cause us to have a closure in the + // hidden type that is not actually well formed and + // can cause compiler crashes when the user abuses unsafe + // code to procure such a closure. + // See src/test/ui/type-alias-impl-trait/wf_check_closures.rs + let obligations = self.nominal_obligations(did, substs); + self.out.extend(obligations); } ty::FnPtr(_) => { diff --git a/src/test/ui/const-generics/generic_const_exprs/closures.rs b/src/test/ui/const-generics/generic_const_exprs/closures.rs index 847843fe1a6..1ea310d063b 100644 --- a/src/test/ui/const-generics/generic_const_exprs/closures.rs +++ b/src/test/ui/const-generics/generic_const_exprs/closures.rs @@ -1,6 +1,6 @@ #![feature(generic_const_exprs)] #![allow(incomplete_features)] fn test() -> [u8; N + (|| 42)()] {} -//~^ ERROR overly complex generic constant +//~^ ERROR cycle detected when building an abstract representation fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/closures.stderr b/src/test/ui/const-generics/generic_const_exprs/closures.stderr index 18010413b93..a15dd2016e9 100644 --- a/src/test/ui/const-generics/generic_const_exprs/closures.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/closures.stderr @@ -1,13 +1,26 @@ -error: overly complex generic constant +error[E0391]: cycle detected when building an abstract representation for test::{constant#0} --> $DIR/closures.rs:3:35 | LL | fn test() -> [u8; N + (|| 42)()] {} - | ^^^^-------^^ - | | - | borrowing is not supported in generic constants + | ^^^^^^^^^^^^^ | - = help: consider moving this anonymous constant into a `const` function - = note: this operation may be supported in the future +note: ...which requires building THIR for `test::{constant#0}`... + --> $DIR/closures.rs:3:35 + | +LL | fn test() -> [u8; N + (|| 42)()] {} + | ^^^^^^^^^^^^^ +note: ...which requires type-checking `test::{constant#0}`... + --> $DIR/closures.rs:3:35 + | +LL | fn test() -> [u8; N + (|| 42)()] {} + | ^^^^^^^^^^^^^ + = note: ...which again requires building an abstract representation for test::{constant#0}, completing the cycle +note: cycle used when checking that `test` is well-formed + --> $DIR/closures.rs:3:1 + | +LL | fn test() -> [u8; N + (|| 42)()] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/higher-rank-trait-bounds/issue-59311.nll.stderr b/src/test/ui/higher-rank-trait-bounds/issue-59311.nll.stderr new file mode 100644 index 00000000000..15e83ab5a34 --- /dev/null +++ b/src/test/ui/higher-rank-trait-bounds/issue-59311.nll.stderr @@ -0,0 +1,18 @@ +error: higher-ranked lifetime error + --> $DIR/issue-59311.rs:17:5 + | +LL | v.t(|| {}); + | ^^^^^^^^^^ + | + = note: could not prove [closure@$DIR/issue-59311.rs:17:9: 17:14] well-formed + +error: higher-ranked lifetime error + --> $DIR/issue-59311.rs:17:9 + | +LL | v.t(|| {}); + | ^^^^^ + | + = note: could not prove for<'a> &'a V: 'static + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/higher-rank-trait-bounds/issue-59311.rs b/src/test/ui/higher-rank-trait-bounds/issue-59311.rs index d617571753c..69708577285 100644 --- a/src/test/ui/higher-rank-trait-bounds/issue-59311.rs +++ b/src/test/ui/higher-rank-trait-bounds/issue-59311.rs @@ -14,7 +14,7 @@ pub fn crash(v: &V) where for<'a> &'a V: T + 'static, { - v.t(|| {}); //~ ERROR: higher-ranked lifetime error + v.t(|| {}); //~ ERROR: `&'a V` does not fulfill the required lifetime } fn main() {} diff --git a/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr b/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr index c16c8206153..3dd05bba5c0 100644 --- a/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr +++ b/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr @@ -1,10 +1,15 @@ -error: higher-ranked lifetime error - --> $DIR/issue-59311.rs:17:9 +error[E0477]: the type `&'a V` does not fulfill the required lifetime + --> $DIR/issue-59311.rs:17:5 | LL | v.t(|| {}); - | ^^^^^ + | ^^^^^^^^^^ | - = note: could not prove for<'a> &'a V: 'static +note: type must satisfy the static lifetime as required by this binding + --> $DIR/issue-59311.rs:15:24 + | +LL | for<'a> &'a V: T + 'static, + | ^^^^^^^ error: aborting due to previous error +For more information about this error, try `rustc --explain E0477`. diff --git a/src/test/ui/type-alias-impl-trait/issue-53092.rs b/src/test/ui/type-alias-impl-trait/issue-53092.rs new file mode 100644 index 00000000000..45792ba97a7 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53092.rs @@ -0,0 +1,14 @@ +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +type Bug = impl Fn(T) -> U + Copy; + +const CONST_BUG: Bug = unsafe { std::mem::transmute(|_: u8| ()) }; + +fn make_bug>() -> Bug { + |x| x.into() //~ ERROR the trait bound `U: From` is not satisfied +} + +fn main() { + CONST_BUG(0); +} diff --git a/src/test/ui/type-alias-impl-trait/issue-53092.stderr b/src/test/ui/type-alias-impl-trait/issue-53092.stderr new file mode 100644 index 00000000000..2d423a0c0df --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-53092.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `U: From` is not satisfied + --> $DIR/issue-53092.rs:9:5 + | +LL | |x| x.into() + | ^^^^^^^^^^^^ the trait `From` is not implemented for `U` + | +note: required by a bound in `make_bug` + --> $DIR/issue-53092.rs:8:19 + | +LL | fn make_bug>() -> Bug { + | ^^^^^^^ required by this bound in `make_bug` +help: consider restricting type parameter `U` + | +LL | type Bug> = impl Fn(T) -> U + Copy; + | +++++++++++++++++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/issue-60564-working.rs b/src/test/ui/type-alias-impl-trait/issue-60564-working.rs new file mode 100644 index 00000000000..38accc8241c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-60564-working.rs @@ -0,0 +1,24 @@ +#![feature(type_alias_impl_trait)] + +// check-pass + +trait IterBits { + type BitsIter: Iterator; + fn iter_bits(self, n: u8) -> Self::BitsIter; +} + +impl IterBits for T +where + T: std::ops::Shr + + std::ops::BitAnd + + std::convert::From + + std::convert::TryInto, + E: std::fmt::Debug, +{ + type BitsIter = impl std::iter::Iterator; + fn iter_bits(self, n: u8) -> Self::BitsIter { + (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + } +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.rs b/src/test/ui/type-alias-impl-trait/issue-60564.rs index 0aeebae639e..48d8dff2b93 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60564.rs +++ b/src/test/ui/type-alias-impl-trait/issue-60564.rs @@ -20,6 +20,13 @@ where (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) //~^ ERROR non-defining opaque type use in defining scope //~| ERROR type mismatch resolving + //~| ERROR type mismatch resolving `>::Error == E` + //~| ERROR no implementation for `T >> T` + //~| ERROR no implementation for `T & T` + //~| ERROR the trait bound `T: From` + //~| ERROR the trait bound `T: Copy` is not satisfied + //~| ERROR `E` doesn't implement `Debug` + //~| ERROR the trait bound `u8: From` is not satisfied } } diff --git a/src/test/ui/type-alias-impl-trait/issue-60564.stderr b/src/test/ui/type-alias-impl-trait/issue-60564.stderr index 9cb07cbbb44..4ef53973a7c 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60564.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-60564.stderr @@ -11,6 +11,122 @@ LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from found type parameter `I` = note: required because of the requirements on the impl of `Iterator` for `Map>, [closure@$DIR/issue-60564.rs:20:28: 20:100]>` +error[E0271]: type mismatch resolving `>::Error == E` + --> $DIR/issue-60564.rs:20:9 + | +LL | type IterBitsIter = impl std::iter::Iterator; + | - this type parameter +... +LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `E`, found enum `Infallible` + | + = note: expected type parameter `E` + found enum `Infallible` +note: required by a bound in `` + --> $DIR/issue-60564.rs:15:37 + | +LL | + std::convert::TryInto, + | ^^^^^^^^^ required by this bound in `` + +error[E0277]: no implementation for `T >> T` + --> $DIR/issue-60564.rs:20:9 + | +LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T >> T` + | +note: required by a bound in `` + --> $DIR/issue-60564.rs:12:8 + | +LL | T: std::ops::Shr + | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `` +help: consider restricting type parameter `T` + | +LL | type IterBitsIter = impl std::iter::Iterator; + | +++++++++++++++ + +error[E0277]: no implementation for `T & T` + --> $DIR/issue-60564.rs:20:9 + | +LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `T & T` + | +note: required by a bound in `` + --> $DIR/issue-60564.rs:13:11 + | +LL | + std::ops::BitAnd + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `` +help: consider restricting type parameter `T` + | +LL | type IterBitsIter = impl std::iter::Iterator; + | ++++++++++++++++++ + +error[E0277]: the trait bound `T: From` is not satisfied + --> $DIR/issue-60564.rs:20:9 + | +LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From` is not implemented for `T` + | +note: required by a bound in `` + --> $DIR/issue-60564.rs:14:11 + | +LL | + std::convert::From + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `` +help: consider restricting type parameter `T` + | +LL | type IterBitsIter, E, I> = impl std::iter::Iterator; + | ++++++++++++++++++++++++ + +error[E0277]: the trait bound `T: Copy` is not satisfied + --> $DIR/issue-60564.rs:20:9 + | +LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `T` + | +note: required by a bound in `` + --> $DIR/issue-60564.rs:10:9 + | +LL | impl IterBits for T + | ^^^^ required by this bound in `` +help: consider restricting type parameter `T` + | +LL | type IterBitsIter = impl std::iter::Iterator; + | +++++++++++++++++++ + +error[E0277]: `E` doesn't implement `Debug` + --> $DIR/issue-60564.rs:20:9 + | +LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `E` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | +note: required by a bound in `` + --> $DIR/issue-60564.rs:16:8 + | +LL | E: std::fmt::Debug, + | ^^^^^^^^^^^^^^^ required by this bound in `` +help: consider restricting type parameter `E` + | +LL | type IterBitsIter = impl std::iter::Iterator; + | +++++++++++++++++ + +error[E0277]: the trait bound `u8: From` is not satisfied + --> $DIR/issue-60564.rs:20:9 + | +LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From` is not implemented for `u8` + | + = note: required because of the requirements on the impl of `Into` for `T` + = note: required because of the requirements on the impl of `TryFrom` for `u8` + = note: required because of the requirements on the impl of `TryInto` for `T` +note: required by a bound in `` + --> $DIR/issue-60564.rs:15:11 + | +LL | + std::convert::TryInto, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `` +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +LL | type IterBitsIter = impl std::iter::Iterator where u8: From; + | +++++++++++++++++ + error: non-defining opaque type use in defining scope --> $DIR/issue-60564.rs:20:9 | @@ -23,6 +139,7 @@ note: used non-generic type `u8` for generic parameter LL | type IterBitsIter = impl std::iter::Iterator; | ^ -error: aborting due to 2 previous errors +error: aborting due to 9 previous errors -For more information about this error, try `rustc --explain E0271`. +Some errors have detailed explanations: E0271, E0277. +For more information about an error, try `rustc --explain E0271`. diff --git a/src/test/ui/type-alias-impl-trait/wf-check-fn-def.rs b/src/test/ui/type-alias-impl-trait/wf-check-fn-def.rs new file mode 100644 index 00000000000..449e9fbd0d8 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/wf-check-fn-def.rs @@ -0,0 +1,18 @@ +#![feature(type_alias_impl_trait)] + +trait Bar { + fn bar(&self); +} + +type FooFn = impl FnOnce(B); + +fn foo() -> FooFn { + fn mop(bar: B) { bar.bar() } + mop // NOTE: no function pointer, but function zst item + //~^ ERROR the trait bound `B: Bar` is not satisfied +} + +fn main() { + let boom: FooFn = unsafe { core::mem::zeroed() }; + boom(42); +} diff --git a/src/test/ui/type-alias-impl-trait/wf-check-fn-def.stderr b/src/test/ui/type-alias-impl-trait/wf-check-fn-def.stderr new file mode 100644 index 00000000000..e0005489d1e --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/wf-check-fn-def.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `B: Bar` is not satisfied + --> $DIR/wf-check-fn-def.rs:11:5 + | +LL | mop // NOTE: no function pointer, but function zst item + | ^^^ the trait `Bar` is not implemented for `B` + | +note: required by a bound in `mop` + --> $DIR/wf-check-fn-def.rs:10:15 + | +LL | fn mop(bar: B) { bar.bar() } + | ^^^ required by this bound in `mop` +help: consider restricting type parameter `B` + | +LL | type FooFn = impl FnOnce(B); + | +++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs b/src/test/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs new file mode 100644 index 00000000000..3b8470e4ae6 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/wf-check-fn-ptrs.rs @@ -0,0 +1,23 @@ +#![feature(type_alias_impl_trait)] + +// build-pass + +trait Bar { + fn bar(&self); +} + +type FooFn = impl FnOnce(B); + +fn foo() -> FooFn { + fn mop(bar: B) { bar.bar() } + mop as fn(B) + // function pointers don't have any obligations on them, + // thus the above compiles. It's obviously unsound to just + // procure a `FooFn` from the ether without making sure that + // the pointer is actually legal for all `B` +} + +fn main() { + let boom: FooFn = unsafe { core::mem::zeroed() }; + boom(42); +} diff --git a/src/test/ui/type-alias-impl-trait/wf_check_closures.rs b/src/test/ui/type-alias-impl-trait/wf_check_closures.rs new file mode 100644 index 00000000000..2c70696ffcf --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/wf_check_closures.rs @@ -0,0 +1,17 @@ +#![feature(type_alias_impl_trait)] + +trait Bar { + fn bar(&self); +} + +type FooFn = impl FnOnce(); + +fn foo(bar: B) -> FooFn { + move || { bar.bar() } + //~^ ERROR the trait bound `B: Bar` is not satisfied +} + +fn main() { + let boom: FooFn = unsafe { core::mem::zeroed() }; + boom(); +} diff --git a/src/test/ui/type-alias-impl-trait/wf_check_closures.stderr b/src/test/ui/type-alias-impl-trait/wf_check_closures.stderr new file mode 100644 index 00000000000..58ae8617b9b --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/wf_check_closures.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `B: Bar` is not satisfied + --> $DIR/wf_check_closures.rs:10:5 + | +LL | move || { bar.bar() } + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `B` + | +note: required by a bound in `foo` + --> $DIR/wf_check_closures.rs:9:11 + | +LL | fn foo(bar: B) -> FooFn { + | ^^^ required by this bound in `foo` +help: consider restricting type parameter `B` + | +LL | type FooFn = impl FnOnce(); + | +++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.