diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index aece0bda34692..dd78b75472e9e 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -323,42 +323,4 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .and(type_op::ascribe_user_type::AscribeUserType { mir_ty, user_ty }), ); } - - /// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`. - /// - /// FIXME(#104478, #104477): This is a hack for backward-compatibility. - #[instrument(skip(self), level = "debug")] - pub(super) fn ascribe_user_type_skip_wf( - &mut self, - mir_ty: Ty<'tcx>, - user_ty: ty::UserType<'tcx>, - span: Span, - ) { - let ty::UserTypeKind::Ty(user_ty) = user_ty.kind else { bug!() }; - - // A fast path for a common case with closure input/output types. - if let ty::Infer(_) = user_ty.kind() { - self.eq_types(user_ty, mir_ty, Locations::All(span), ConstraintCategory::Boring) - .unwrap(); - return; - } - - // FIXME: Ideally MIR types are normalized, but this is not always true. - let mir_ty = self.normalize(mir_ty, Locations::All(span)); - - let cause = ObligationCause::dummy_with_span(span); - let param_env = self.infcx.param_env; - let _: Result<_, ErrorGuaranteed> = self.fully_perform_op( - Locations::All(span), - ConstraintCategory::Boring, - type_op::custom::CustomTypeOp::new( - |ocx| { - let user_ty = ocx.normalize(&cause, param_env, user_ty); - ocx.eq(&cause, param_env, user_ty, mir_ty)?; - Ok(()) - }, - "ascribe_user_type_skip_wf", - ), - ); - } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index f3b9dcc90a845..829210bccdb1d 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -110,7 +110,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .skip(1 + if is_coroutine_with_implicit_resume_ty { 1 } else { 0 }) .map(|local| &self.body.local_decls[local]), ) { - self.ascribe_user_type_skip_wf( + self.ascribe_user_type( arg_decl.ty, ty::UserType::new(ty::UserTypeKind::Ty(user_ty)), arg_decl.source_info.span, @@ -119,7 +119,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // If the user explicitly annotated the output type, enforce it. let output_decl = &self.body.local_decls[RETURN_PLACE]; - self.ascribe_user_type_skip_wf( + self.ascribe_user_type( output_decl.ty, ty::UserType::new(ty::UserTypeKind::Ty(user_provided_sig.output())), output_decl.source_info.span, diff --git a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs index 0963053f57806..6bc644f256424 100644 --- a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs +++ b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs @@ -1,4 +1,3 @@ -//@ check-pass struct Foo<'a>(&'a ()) where (): Trait<'a>; @@ -21,7 +20,7 @@ where } fn main() { - let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {}; + let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {}; //~ ERROR: lifetime may not live long enough // If `could_use_implied_bounds` were to use implied bounds, // keeping 'a late-bound, then we could assign that function diff --git a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.stderr b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.stderr new file mode 100644 index 0000000000000..dd99590877bca --- /dev/null +++ b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.stderr @@ -0,0 +1,11 @@ +error: lifetime may not live long enough + --> $DIR/hrlt-implied-trait-bounds-roundtrip.rs:23:49 + | +LL | let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {}; + | ^ + | | + | has type `Foo<'1>` + | requires that `'1` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr index 2742162c82119..a5b68f1d22fc6 100644 --- a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -20,7 +20,7 @@ LL | let mut closure = expect_sig(|p, y| *p = y); | - - ^^^^^^ assignment requires that `'1` must outlive `'2` | | | | | has type `&'1 i32` - | has type `&'?1 mut &'2 i32` + | has type `&'2 mut &'?2 i32` note: no external requirements --> $DIR/escape-argument-callee.rs:20:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr index f5527eeb2cdba..964ea3079c924 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -19,6 +19,8 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y = note: late-bound region is '?4 = note: number of external vids: 5 = note: where '?1: '?2 + = note: where '?1: '?2 + = note: where '?1: '?2 note: no external requirements --> $DIR/propagate-approximated-ref.rs:42:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index 9e9eae985973a..d06166e4704db 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -18,6 +18,7 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { = note: late-bound region is '?3 = note: number of external vids: 4 = note: where '?1: '?0 + = note: where '?1: '?0 note: no external requirements --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:31:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index 303fcd4cdfcf3..5fbd37062702a 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -18,6 +18,9 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y = note: late-bound region is '?3 = note: late-bound region is '?4 = note: number of external vids: 5 + = note: where '?1: '?2 + = note: where '?1: '?2 + = note: where '?1: '?0 = note: where '?1: '?0 note: no external requirements diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index 6b04e346c6971..8b5641293b7a0 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -23,7 +23,7 @@ error: lifetime may not live long enough LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { | --------- - has type `&'?6 Cell<&'1 u32>` | | - | has type `&'?4 Cell<&'2 &'?1 u32>` + | has type `&'2 Cell<&'?5 &'?1 u32>` LL | // Only works if 'x: 'y: LL | demand_y(x, y, x.get()) | ^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs index 2cb6ceb0c3b95..bda64151fba3d 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.rs @@ -36,7 +36,7 @@ fn demand_y<'x, 'y>(_cell_x: &Cell<&'x u32>, _cell_y: &Cell<&'y u32>, _y: &'y u3 #[rustc_regions] fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { - establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { + establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { //~ ERROR: lifetime may not live long enough // Only works if 'x: 'y: demand_y(x, y, x.get()) //~^ ERROR diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index ae2129c65f2c0..88eb8fe342d12 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -1,4 +1,4 @@ -note: no external requirements +note: external requirements --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:39:47 | LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { @@ -17,12 +17,16 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y = note: late-bound region is '?10 = note: late-bound region is '?3 = note: late-bound region is '?4 + = note: number of external vids: 5 + = note: where '?1: '?2 + = note: where '?1: '?2 + = note: where '?1: '?2 error: lifetime may not live long enough --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:41:9 | LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { - | ---------- ---------- has type `&'?7 Cell<&'2 &'?2 u32>` + | ---------- ---------- has type `&'2 Cell<&'?8 &'?2 u32>` | | | has type `&'?5 Cell<&'1 &'?1 u32>` LL | // Only works if 'x: 'y: @@ -41,5 +45,24 @@ LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { | = note: defining type: supply -error: aborting due to 1 previous error +error: lifetime may not live long enough + --> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:39:5 + | +LL | fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | / establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| { +LL | | // Only works if 'x: 'y: +LL | | demand_y(x, y, x.get()) +LL | | +LL | | }); + | |______^ argument requires that `'a` must outlive `'b` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of the type `Cell<&'?11 u32>`, which makes the generic argument `&'?11 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + +error: aborting due to 2 previous errors diff --git a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr index e2f48f37f0dad..c84efdcb8e3a6 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr @@ -4,7 +4,7 @@ error: lifetime may not live long enough LL | doit(0, &|x, y| { | - - has type `&'1 i32` | | - | has type `&Cell<&'2 i32>` + | has type `&'2 Cell<&i32>` LL | x.set(y); | ^^^^^^^^ argument requires that `'1` must outlive `'2` | diff --git a/tests/ui/wf/check-wf-of-closure-args-complex.rs b/tests/ui/wf/check-wf-of-closure-args-complex.rs new file mode 100644 index 0000000000000..86eceea5702c5 --- /dev/null +++ b/tests/ui/wf/check-wf-of-closure-args-complex.rs @@ -0,0 +1,48 @@ +// The same as check-wf-of-closure-args-complex, but this example causes +// a use-after-free if we don't perform the wf check. +// +#![feature(unboxed_closures)] + +use std::sync::OnceLock; + +type Payload = Box; + +static STORAGE: OnceLock<&'static Payload> = OnceLock::new(); + +trait Store { + fn store(&self); +} +impl Store for &'static Payload { + fn store(&self) { + STORAGE.set(*self).unwrap(); + } +} + +#[repr(transparent)] +struct MyTy(T); +impl Drop for MyTy { + fn drop(&mut self) { + self.0.store(); + } +} + +trait IsFn: for<'x> Fn<(&'x Payload,)> {} + +impl IsFn for for<'x> fn(&'x Payload) -> MyTy<&'x Payload> {} + +fn foo(f: impl for<'x> Fn(&'x Payload)) { + let a = Box::new(1); + f(&a); +} +fn bar(f: F) { + foo(|x| { f(x); }); +} + +fn main() { + // If no wf-check is done on this closure given to `bar`, this compiles fine and + // a use-after-free will occur. + bar:: fn(&'a Payload) -> MyTy<&'a Payload>>(|x| unsafe { //~ ERROR: lifetime may not live long enough + std::mem::transmute::<&Payload, MyTy<&Payload>>(x) + }); + println!("{}", STORAGE.get().unwrap()); +} diff --git a/tests/ui/wf/check-wf-of-closure-args-complex.stderr b/tests/ui/wf/check-wf-of-closure-args-complex.stderr new file mode 100644 index 0000000000000..a7c23050424a4 --- /dev/null +++ b/tests/ui/wf/check-wf-of-closure-args-complex.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/check-wf-of-closure-args-complex.rs:44:60 + | +LL | bar:: fn(&'a Payload) -> MyTy<&'a Payload>>(|x| unsafe { + | - ^ requires that `'1` must outlive `'static` + | | + | has type `&'1 Box` + +error: aborting due to 1 previous error + diff --git a/tests/ui/wf/check-wf-of-closure-args.rs b/tests/ui/wf/check-wf-of-closure-args.rs new file mode 100644 index 0000000000000..a4c4de7013c2f --- /dev/null +++ b/tests/ui/wf/check-wf-of-closure-args.rs @@ -0,0 +1,14 @@ +// Checks that we perform WF checks on closure args, regardless of wether they +// are used in the closure it self. +// related to issue #104478 + +struct MyTy(T); +trait Trait {} +impl Trait for &'static str {} +fn wf(_: T) {} + +fn main() { + let _: for<'x> fn(MyTy<&'x str>) = |_| {}; //~ ERROR: lifetime may not live long enough + + let _: for<'x> fn(MyTy<&'x str>) = |x| wf(x); //~ ERROR: lifetime may not live long enough +} diff --git a/tests/ui/wf/check-wf-of-closure-args.stderr b/tests/ui/wf/check-wf-of-closure-args.stderr new file mode 100644 index 0000000000000..a4881bc75d07b --- /dev/null +++ b/tests/ui/wf/check-wf-of-closure-args.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/check-wf-of-closure-args.rs:11:41 + | +LL | let _: for<'x> fn(MyTy<&'x str>) = |_| {}; + | ^ + | | + | has type `MyTy<&'1 str>` + | requires that `'1` must outlive `'static` + +error: lifetime may not live long enough + --> $DIR/check-wf-of-closure-args.rs:13:41 + | +LL | let _: for<'x> fn(MyTy<&'x str>) = |x| wf(x); + | ^ + | | + | has type `MyTy<&'1 str>` + | requires that `'1` must outlive `'static` + +error: aborting due to 2 previous errors +