From ab83d501a404bede933df8f33e280a7534cc71bf Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sun, 12 Sep 2021 23:07:23 +0200 Subject: [PATCH 01/14] Do not issue E0071 if a type error has already been reported --- .../src/error_codes/E0071.md | 14 +++++----- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 28 +++++++++++++------ src/test/ui/typeck/issue-88844.rs | 14 ++++++++++ src/test/ui/typeck/issue-88844.stderr | 12 ++++++++ 4 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 src/test/ui/typeck/issue-88844.rs create mode 100644 src/test/ui/typeck/issue-88844.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0071.md b/compiler/rustc_error_codes/src/error_codes/E0071.md index bc2c03a022082..a6d6d19762b58 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0071.md +++ b/compiler/rustc_error_codes/src/error_codes/E0071.md @@ -15,13 +15,13 @@ form of initializer was used. For example, the code above can be fixed to: ``` -enum Foo { - FirstValue(i32) -} +type U32 = u32; +let t: U32 = 4; +``` -fn main() { - let u = Foo::FirstValue(0i32); +or: - let t = 4; -} +``` +struct U32 { value: u32 } +let t = U32 { value: 4 }; ``` diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index 9efb52a08b7e1..e74e79fd1bf1a 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -494,15 +494,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((variant, ty)) } else { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); + match ty.kind() { + ty::Error(_) => { + // E0071 might be caused by a spelling error, which will have + // already caused an error message and probably a suggestion + // elsewhere. Refrain from emitting more unhelpful errors here + // (issue #88844). + } + _ => { + struct_span_err!( + self.tcx.sess, + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.sort_string(self.tcx) + ) + .span_label(path_span, "not a struct") + .emit(); + } + } None } } diff --git a/src/test/ui/typeck/issue-88844.rs b/src/test/ui/typeck/issue-88844.rs new file mode 100644 index 0000000000000..116c75aabdbdf --- /dev/null +++ b/src/test/ui/typeck/issue-88844.rs @@ -0,0 +1,14 @@ +// Regression test for #88844. + +struct Struct { value: i32 } +//~^ NOTE: similarly named struct `Struct` defined here + +impl Stuct { +//~^ ERROR: cannot find type `Stuct` in this scope [E0412] +//~| HELP: a struct with a similar name exists + fn new() -> Self { + Self { value: 42 } + } +} + +fn main() {} diff --git a/src/test/ui/typeck/issue-88844.stderr b/src/test/ui/typeck/issue-88844.stderr new file mode 100644 index 0000000000000..90bba90be346c --- /dev/null +++ b/src/test/ui/typeck/issue-88844.stderr @@ -0,0 +1,12 @@ +error[E0412]: cannot find type `Stuct` in this scope + --> $DIR/issue-88844.rs:6:6 + | +LL | struct Struct { value: i32 } + | ------------- similarly named struct `Struct` defined here +... +LL | impl Stuct { + | ^^^^^ help: a struct with a similar name exists: `Struct` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`. From 2a2bfd1e59e9f84d2f68c0587bd7d7c93a237a46 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Wed, 15 Sep 2021 00:11:18 +0200 Subject: [PATCH 02/14] Fix handling of `hir::GenericArg::Infer` in `wrong_number_of_generic_args.rs` --- .../wrong_number_of_generic_args.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index bccc19774e0d9..7e69ad21d0343 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -136,10 +136,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { AngleBrackets::Missing => 0, // Only lifetime arguments can be implied AngleBrackets::Implied => self.gen_args.args.len(), - AngleBrackets::Available => self.gen_args.args.iter().fold(0, |acc, arg| match arg { - hir::GenericArg::Lifetime(_) => acc + 1, - _ => acc, - }), + AngleBrackets::Available => self.gen_args.num_lifetime_params(), } } @@ -148,10 +145,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { AngleBrackets::Missing => 0, // Only lifetime arguments can be implied AngleBrackets::Implied => 0, - AngleBrackets::Available => self.gen_args.args.iter().fold(0, |acc, arg| match arg { - hir::GenericArg::Type(_) | hir::GenericArg::Const(_) => acc + 1, - _ => acc, - }), + AngleBrackets::Available => self.gen_args.num_generic_params(), } } @@ -651,7 +645,9 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let mut found_redundant = false; for arg in self.gen_args.args { match arg { - hir::GenericArg::Type(_) | hir::GenericArg::Const(_) => { + hir::GenericArg::Type(_) + | hir::GenericArg::Const(_) + | hir::GenericArg::Infer(_) => { gen_arg_spans.push(arg.span()); if gen_arg_spans.len() > self.num_expected_type_or_const_args() { found_redundant = true; From 21b7052eb87c397094484227eec75269061d8491 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sun, 25 Jul 2021 20:05:41 +0200 Subject: [PATCH 03/14] Point to closure when emitting 'cannot move out' for captured variable --- .../src/diagnostics/move_errors.rs | 12 +- .../ui/borrowck/borrowck-in-static.stderr | 5 +- .../borrowck/borrowck-move-by-capture.stderr | 21 +- .../borrowck/issue-87456-point-to-closure.rs | 14 + .../issue-87456-point-to-closure.stderr | 22 + ...ove-upvar-from-non-once-ref-closure.stderr | 14 +- src/test/ui/issues/issue-4335.stderr | 5 +- ...-move-out-of-closure-env-issue-1965.stderr | 5 +- ...e-52663-span-decl-captured-variable.stderr | 5 +- ...borrowck-call-is-borrow-issue-12224.stderr | 14 +- .../dont-suggest-ref/move-into-closure.stderr | 554 ++++++++++++------ .../suggestions/option-content-move2.stderr | 27 +- .../unboxed-closure-illegal-move.stderr | 20 +- 13 files changed, 503 insertions(+), 215 deletions(-) create mode 100644 src/test/ui/borrowck/issue-87456-point-to-closure.rs create mode 100644 src/test/ui/borrowck/issue-87456-point-to-closure.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 3c11408458629..b23ce281bef24 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -336,15 +336,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() => { let closure_kind_ty = closure_substs.as_closure().kind_ty(); - let closure_kind = closure_kind_ty.to_opt_closure_kind(); - let capture_description = match closure_kind { - Some(ty::ClosureKind::Fn) => "captured variable in an `Fn` closure", - Some(ty::ClosureKind::FnMut) => "captured variable in an `FnMut` closure", + let closure_kind = match closure_kind_ty.to_opt_closure_kind() { + Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind, Some(ty::ClosureKind::FnOnce) => { bug!("closure kind does not match first argument type") } None => bug!("closure kind not inferred by borrowck"), }; + let capture_description = + format!("captured variable in an `{}` closure", closure_kind); let upvar = &self.upvars[upvar_field.unwrap().index()]; let upvar_hir_id = upvar.place.get_root_variable(); @@ -368,6 +368,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut diag = self.cannot_move_out_of(span, &place_description); diag.span_label(upvar_span, "captured outer variable"); + diag.span_label( + self.body.span, + format!("captured by this `{}` closure", closure_kind), + ); diag } diff --git a/src/test/ui/borrowck/borrowck-in-static.stderr b/src/test/ui/borrowck/borrowck-in-static.stderr index f73c787346d8c..30e74c5ec950c 100644 --- a/src/test/ui/borrowck/borrowck-in-static.stderr +++ b/src/test/ui/borrowck/borrowck-in-static.stderr @@ -4,7 +4,10 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure LL | let x = Box::new(0); | - captured outer variable LL | Box::new(|| x) - | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | ---^ + | | | + | | move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | captured by this `Fn` closure error: aborting due to previous error diff --git a/src/test/ui/borrowck/borrowck-move-by-capture.stderr b/src/test/ui/borrowck/borrowck-move-by-capture.stderr index 628f206e0a896..05489cf18e7fc 100644 --- a/src/test/ui/borrowck/borrowck-move-by-capture.stderr +++ b/src/test/ui/borrowck/borrowck-move-by-capture.stderr @@ -1,15 +1,18 @@ error[E0507]: cannot move out of `bar`, a captured variable in an `FnMut` closure --> $DIR/borrowck-move-by-capture.rs:9:29 | -LL | let bar: Box<_> = box 3; - | --- captured outer variable -LL | let _g = to_fn_mut(|| { -LL | let _h = to_fn_once(move || -> isize { *bar }); - | ^^^^^^^^^^^^^^^^ ---- - | | | - | | move occurs because `bar` has type `Box`, which does not implement the `Copy` trait - | | move occurs due to use in closure - | move out of `bar` occurs here +LL | let bar: Box<_> = box 3; + | --- captured outer variable +LL | let _g = to_fn_mut(|| { + | ________________________- +LL | | let _h = to_fn_once(move || -> isize { *bar }); + | | ^^^^^^^^^^^^^^^^ ---- + | | | | + | | | move occurs because `bar` has type `Box`, which does not implement the `Copy` trait + | | | move occurs due to use in closure + | | move out of `bar` occurs here +LL | | }); + | |_____- captured by this `FnMut` closure error: aborting due to previous error diff --git a/src/test/ui/borrowck/issue-87456-point-to-closure.rs b/src/test/ui/borrowck/issue-87456-point-to-closure.rs new file mode 100644 index 0000000000000..9fc12ba749042 --- /dev/null +++ b/src/test/ui/borrowck/issue-87456-point-to-closure.rs @@ -0,0 +1,14 @@ +// Regression test for #87456. + +fn take_mut(_val: impl FnMut()) {} + +fn main() { + let val = String::new(); + //~^ NOTE: captured outer variable + take_mut(|| { + //~^ NOTE: captured by this `FnMut` closure + let _foo: String = val; + //~^ ERROR: cannot move out of `val`, a captured variable in an `FnMut` closure [E0507] + //~| NOTE: move occurs because + }) +} diff --git a/src/test/ui/borrowck/issue-87456-point-to-closure.stderr b/src/test/ui/borrowck/issue-87456-point-to-closure.stderr new file mode 100644 index 0000000000000..fd38ad7bb0a7f --- /dev/null +++ b/src/test/ui/borrowck/issue-87456-point-to-closure.stderr @@ -0,0 +1,22 @@ +error[E0507]: cannot move out of `val`, a captured variable in an `FnMut` closure + --> $DIR/issue-87456-point-to-closure.rs:10:28 + | +LL | let val = String::new(); + | --- captured outer variable +LL | +LL | take_mut(|| { + | ______________- +LL | | +LL | | let _foo: String = val; + | | ^^^ + | | | + | | move occurs because `val` has type `String`, which does not implement the `Copy` trait + | | help: consider borrowing here: `&val` +LL | | +LL | | +LL | | }) + | |_____- captured by this `FnMut` closure + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0507`. diff --git a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index dbba33f018397..1663ce81d6cf4 100644 --- a/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/src/test/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -1,11 +1,15 @@ error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:11:9 | -LL | let y = vec![format!("World")]; - | - captured outer variable -LL | call(|| { -LL | y.into_iter(); - | ^ move occurs because `y` has type `Vec`, which does not implement the `Copy` trait +LL | let y = vec![format!("World")]; + | - captured outer variable +LL | call(|| { + | __________- +LL | | y.into_iter(); + | | ^ move occurs because `y` has type `Vec`, which does not implement the `Copy` trait +LL | | +LL | | }); + | |_____- captured by this `Fn` closure error: aborting due to previous error diff --git a/src/test/ui/issues/issue-4335.stderr b/src/test/ui/issues/issue-4335.stderr index f187969ff4e86..fa3b58e127937 100644 --- a/src/test/ui/issues/issue-4335.stderr +++ b/src/test/ui/issues/issue-4335.stderr @@ -4,7 +4,10 @@ error[E0507]: cannot move out of `*v`, as `v` is a captured variable in an `FnMu LL | fn f<'r, T>(v: &'r T) -> Box T + 'r> { | - captured outer variable LL | id(Box::new(|| *v)) - | ^^ move occurs because `*v` has type `T`, which does not implement the `Copy` trait + | ---^^ + | | | + | | move occurs because `*v` has type `T`, which does not implement the `Copy` trait + | captured by this `FnMut` closure error: aborting due to previous error diff --git a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 9427ba546a9c1..e12af2d452743 100644 --- a/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/src/test/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -4,7 +4,10 @@ error[E0507]: cannot move out of `i`, a captured variable in an `Fn` closure LL | let i = box 3; | - captured outer variable LL | let _f = to_fn(|| test(i)); - | ^ move occurs because `i` has type `Box`, which does not implement the `Copy` trait + | --------^- + | | | + | | move occurs because `i` has type `Box`, which does not implement the `Copy` trait + | captured by this `Fn` closure error: aborting due to previous error diff --git a/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr index 67115a5ccdd45..c9324f0422cdc 100644 --- a/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/src/test/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -4,7 +4,10 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` LL | let x = (vec![22], vec![44]); | - captured outer variable LL | expect_fn(|| drop(x.0)); - | ^^^ move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait + | --------^^^- + | | | + | | move occurs because `x.0` has type `Vec`, which does not implement the `Copy` trait + | captured by this `Fn` closure error: aborting due to previous error diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr index ab1fa2a4d8765..0f630abd14876 100644 --- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -29,11 +29,17 @@ LL | f.f.call_mut(()) error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure --> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13 | -LL | let mut f = move |g: Box, b: isize| { - | ----- captured outer variable +LL | let mut f = move |g: Box, b: isize| { + | ----- captured outer variable ... -LL | foo(f); - | ^ move occurs because `f` has type `[closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 54:6]`, which does not implement the `Copy` trait +LL | f(Box::new(|a| { + | ________________- +LL | | +LL | | foo(f); + | | ^ move occurs because `f` has type `[closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 54:6]`, which does not implement the `Copy` trait +LL | | +LL | | }), 3); + | |_____- captured by this `FnMut` closure error[E0505]: cannot move out of `f` because it is borrowed --> $DIR/borrowck-call-is-borrow-issue-12224.rs:55:16 diff --git a/src/test/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/src/test/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index c50cbcde85553..fb1055c9c3093 100644 --- a/src/test/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/src/test/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -1,281 +1,487 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:28:21 | -LL | let x = X(Y); - | - captured outer variable +LL | let x = X(Y); + | - captured outer variable ... -LL | let X(_t) = x; - | -- ^ help: consider borrowing here: `&x` - | | - | data moved here - | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; + | | -- ^ help: consider borrowing here: `&x` + | | | + | | data moved here + | | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +LL | | +LL | | +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:32:34 | -LL | let e = Either::One(X(Y)); - | - captured outer variable +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | if let Either::One(_t) = e { } - | -- ^ help: consider borrowing here: `&e` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +LL | | +LL | | if let Either::One(_t) = e { } + | | -- ^ help: consider borrowing here: `&e` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:36:37 | -LL | let e = Either::One(X(Y)); - | - captured outer variable +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | while let Either::One(_t) = e { } - | -- ^ help: consider borrowing here: `&e` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | while let Either::One(_t) = e { } + | | -- ^ help: consider borrowing here: `&e` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:40:15 | -LL | let e = Either::One(X(Y)); - | - captured outer variable +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | match e { - | ^ help: consider borrowing here: `&e` -... -LL | Either::One(_t) - | -- - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match e { + | | ^ help: consider borrowing here: `&e` +... | +LL | | Either::One(_t) + | | -- + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:47:15 | -LL | let e = Either::One(X(Y)); - | - captured outer variable -... -LL | match e { - | ^ help: consider borrowing here: `&e` +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | Either::One(_t) => (), - | -- - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match e { + | | ^ help: consider borrowing here: `&e` +... | +LL | | Either::One(_t) => (), + | | -- + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:56:25 | -LL | let x = X(Y); - | - captured outer variable +LL | let x = X(Y); + | - captured outer variable ... -LL | let X(mut _t) = x; - | ------ ^ help: consider borrowing here: `&x` - | | - | data moved here - | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | let X(mut _t) = x; + | | ------ ^ help: consider borrowing here: `&x` + | | | + | | data moved here + | | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:60:38 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | if let Either::One(mut _t) = em { } - | ------ ^^ help: consider borrowing here: `&em` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | if let Either::One(mut _t) = em { } + | | ------ ^^ help: consider borrowing here: `&em` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:64:41 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | while let Either::One(mut _t) = em { } - | ------ ^^ help: consider borrowing here: `&em` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | while let Either::One(mut _t) = em { } + | | ------ ^^ help: consider borrowing here: `&em` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:68:15 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable -... -LL | match em { - | ^^ help: consider borrowing here: `&em` +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | Either::One(mut _t) - | ------ - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match em { + | | ^^ help: consider borrowing here: `&em` +... | +LL | | Either::One(mut _t) + | | ------ + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure --> $DIR/move-into-closure.rs:75:15 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | match em { - | ^^ help: consider borrowing here: `&em` -... -LL | Either::One(mut _t) => (), - | ------ - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fn(|| { + | ________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match em { + | | ^^ help: consider borrowing here: `&em` +... | +LL | | Either::One(mut _t) => (), + | | ------ + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `Fn` closure error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:95:21 | -LL | let x = X(Y); - | - captured outer variable +LL | let x = X(Y); + | - captured outer variable ... -LL | let X(_t) = x; - | -- ^ help: consider borrowing here: `&x` - | | - | data moved here - | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; + | | -- ^ help: consider borrowing here: `&x` + | | | + | | data moved here + | | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +LL | | +LL | | +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:99:34 | -LL | let e = Either::One(X(Y)); - | - captured outer variable +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | if let Either::One(_t) = e { } - | -- ^ help: consider borrowing here: `&e` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +LL | | +LL | | if let Either::One(_t) = e { } + | | -- ^ help: consider borrowing here: `&e` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:103:37 | -LL | let e = Either::One(X(Y)); - | - captured outer variable +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | while let Either::One(_t) = e { } - | -- ^ help: consider borrowing here: `&e` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | while let Either::One(_t) = e { } + | | -- ^ help: consider borrowing here: `&e` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:107:15 | -LL | let e = Either::One(X(Y)); - | - captured outer variable -... -LL | match e { - | ^ help: consider borrowing here: `&e` +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | Either::One(_t) - | -- - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match e { + | | ^ help: consider borrowing here: `&e` +... | +LL | | Either::One(_t) + | | -- + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:114:15 | -LL | let e = Either::One(X(Y)); - | - captured outer variable +LL | let e = Either::One(X(Y)); + | - captured outer variable ... -LL | match e { - | ^ help: consider borrowing here: `&e` -... -LL | Either::One(_t) => (), - | -- - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match e { + | | ^ help: consider borrowing here: `&e` +... | +LL | | Either::One(_t) => (), + | | -- + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:123:25 | -LL | let x = X(Y); - | - captured outer variable +LL | let x = X(Y); + | - captured outer variable ... -LL | let X(mut _t) = x; - | ------ ^ help: consider borrowing here: `&x` - | | - | data moved here - | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | let X(mut _t) = x; + | | ------ ^ help: consider borrowing here: `&x` + | | | + | | data moved here + | | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:127:38 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | if let Either::One(mut _t) = em { } - | ------ ^^ help: consider borrowing here: `&em` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | if let Either::One(mut _t) = em { } + | | ------ ^^ help: consider borrowing here: `&em` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:131:41 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | while let Either::One(mut _t) = em { } - | ------ ^^ help: consider borrowing here: `&em` - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | while let Either::One(mut _t) = em { } + | | ------ ^^ help: consider borrowing here: `&em` + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:135:15 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable -... -LL | match em { - | ^^ help: consider borrowing here: `&em` +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | Either::One(mut _t) - | ------ - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match em { + | | ^^ help: consider borrowing here: `&em` +... | +LL | | Either::One(mut _t) + | | ------ + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:142:15 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | match em { - | ^^ help: consider borrowing here: `&em` -... -LL | Either::One(mut _t) => (), - | ------ - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match em { + | | ^^ help: consider borrowing here: `&em` +... | +LL | | Either::One(mut _t) => (), + | | ------ + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure --> $DIR/move-into-closure.rs:150:15 | -LL | let mut em = Either::One(X(Y)); - | ------ captured outer variable -... -LL | match em { - | ^^ help: consider borrowing here: `&em` +LL | let mut em = Either::One(X(Y)); + | ------ captured outer variable ... -LL | Either::One(mut _t) => (), - | ------ - | | - | data moved here - | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +LL | consume_fnmut(|| { + | ___________________- +LL | | let X(_t) = x; +LL | | +LL | | +... | +LL | | match em { + | | ^^ help: consider borrowing here: `&em` +... | +LL | | Either::One(mut _t) => (), + | | ------ + | | | + | | data moved here + | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait +... | +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error: aborting due to 21 previous errors diff --git a/src/test/ui/suggestions/option-content-move2.stderr b/src/test/ui/suggestions/option-content-move2.stderr index cfbee1518cd69..a0ce7d05b4d48 100644 --- a/src/test/ui/suggestions/option-content-move2.stderr +++ b/src/test/ui/suggestions/option-content-move2.stderr @@ -1,17 +1,22 @@ error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closure --> $DIR/option-content-move2.rs:9:9 | -LL | let mut var = None; - | ------- captured outer variable -... -LL | move || { - | ^^^^^^^ move out of `var` occurs here -LL | -LL | var = Some(NotCopyable); - | --- - | | - | move occurs because `var` has type `Option`, which does not implement the `Copy` trait - | move occurs due to use in closure +LL | let mut var = None; + | ------- captured outer variable +LL | func(|| { + | __________- +LL | | // Shouldn't suggest `move ||.as_ref()` here +LL | | move || { + | | ^^^^^^^ move out of `var` occurs here +LL | | +LL | | var = Some(NotCopyable); + | | --- + | | | + | | move occurs because `var` has type `Option`, which does not implement the `Copy` trait + | | move occurs due to use in closure +LL | | } +LL | | }); + | |_____- captured by this `FnMut` closure error: aborting due to previous error diff --git a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index f8c90176ff134..482d3e44fe4ea 100644 --- a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -4,7 +4,10 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn(|| drop(x)); - | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | --------^- + | | | + | | move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | captured by this `Fn` closure error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:19:35 @@ -12,7 +15,10 @@ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn_mut(|| drop(x)); - | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | --------^- + | | | + | | move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | captured by this `FnMut` closure error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure --> $DIR/unboxed-closure-illegal-move.rs:28:36 @@ -20,7 +26,10 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn(move || drop(x)); - | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | -------------^- + | | | + | | move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | captured by this `Fn` closure error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -28,7 +37,10 @@ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure LL | let x = Box::new(0); | - captured outer variable LL | let f = to_fn_mut(move || drop(x)); - | ^ move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | -------------^- + | | | + | | move occurs because `x` has type `Box`, which does not implement the `Copy` trait + | captured by this `FnMut` closure error: aborting due to 4 previous errors From 4840f67fcbe02fe6428a0a29bcf329401242b739 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 14 Sep 2021 18:29:38 -0700 Subject: [PATCH 04/14] Add chown functions to std::os::unix::fs to change the owner and group of files This is a straightforward wrapper that uses the existing helpers for C string handling and errno handling. Having this available is convenient for UNIX utility programs written in Rust, and avoids having to call unsafe functions like `libc::chown` directly and handle errors manually, in a program that may otherwise be entirely safe code. In addition, these functions provide a more Rustic interface by accepting appropriate traits and using `None` rather than `-1`. --- library/std/src/os/unix/fs.rs | 70 ++++++++++++++++++++++++++++++++++ library/std/src/sys/unix/fs.rs | 17 +++++++++ 2 files changed, 87 insertions(+) diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 6cf37f23c574d..c26f6a182b74f 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -5,6 +5,7 @@ use super::platform::fs::MetadataExt as _; use crate::fs::{self, OpenOptions, Permissions}; use crate::io; +use crate::os::unix::io::{AsFd, AsRawFd}; use crate::path::Path; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner}; @@ -924,6 +925,75 @@ impl DirBuilderExt for fs::DirBuilder { } } +/// Change the owner and group of the specified path. +/// +/// Specifying either the uid or gid as `None` will leave it unchanged. +/// +/// Changing the owner typically requires privileges, such as root or a specific capability. +/// Changing the group typically requires either being the owner and a member of the group, or +/// having privileges. +/// +/// If called on a symbolic link, this will change the owner and group of the link target. To +/// change the owner and group of the link itself, see [`lchown`]. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(unix_chown)] +/// use std::os::unix::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::chown("/sandbox", Some(0), Some(0))?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "unix_chown", issue = "none")] +pub fn chown>(dir: P, uid: Option, gid: Option) -> io::Result<()> { + sys::fs::chown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) +} + +/// Change the owner and group of the file referenced by the specified open file descriptor. +/// +/// For semantics and required privileges, see [`chown`]. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(unix_chown)] +/// use std::os::unix::fs; +/// +/// fn main() -> std::io::Result<()> { +/// let f = std::fs::File::open("/file")?; +/// fs::fchown(f, Some(0), Some(0))?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "unix_chown", issue = "none")] +pub fn fchown(fd: F, uid: Option, gid: Option) -> io::Result<()> { + sys::fs::fchown(fd.as_fd().as_raw_fd(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) +} + +/// Change the owner and group of the specified path, without dereferencing symbolic links. +/// +/// Identical to [`chown`], except that if called on a symbolic link, this will change the owner +/// and group of the link itself rather than the owner and group of the link target. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(unix_chown)] +/// use std::os::unix::fs; +/// +/// fn main() -> std::io::Result<()> { +/// fs::lchown("/symlink", Some(0), Some(0))?; +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "unix_chown", issue = "none")] +pub fn lchown>(dir: P, uid: Option, gid: Option) -> io::Result<()> { + sys::fs::lchown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) +} + /// Change the root directory of the current process to the specified path. /// /// This typically requires privileges, such as root or a specific capability. diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 6d7524a733afd..a4fff9b2e6473 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1416,6 +1416,23 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { Ok(bytes_copied as u64) } +pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { + let path = cstr(path)?; + cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?; + Ok(()) +} + +pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { + cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?; + Ok(()) +} + +pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { + let path = cstr(path)?; + cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })?; + Ok(()) +} + #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] pub fn chroot(dir: &Path) -> io::Result<()> { let dir = cstr(dir)?; From 862d89e3b5ba092d95586fc2a33b23d1c8672087 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 15 Sep 2021 13:09:54 -0700 Subject: [PATCH 05/14] Add tracking issue for unix_chown --- library/std/src/os/unix/fs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index c26f6a182b74f..30eead9b05901 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -947,7 +947,7 @@ impl DirBuilderExt for fs::DirBuilder { /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "none")] +#[unstable(feature = "unix_chown", issue = "88989")] pub fn chown>(dir: P, uid: Option, gid: Option) -> io::Result<()> { sys::fs::chown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } @@ -968,7 +968,7 @@ pub fn chown>(dir: P, uid: Option, gid: Option) -> io:: /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "none")] +#[unstable(feature = "unix_chown", issue = "88989")] pub fn fchown(fd: F, uid: Option, gid: Option) -> io::Result<()> { sys::fs::fchown(fd.as_fd().as_raw_fd(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } @@ -989,7 +989,7 @@ pub fn fchown(fd: F, uid: Option, gid: Option) -> io::Result< /// Ok(()) /// } /// ``` -#[unstable(feature = "unix_chown", issue = "none")] +#[unstable(feature = "unix_chown", issue = "88989")] pub fn lchown>(dir: P, uid: Option, gid: Option) -> io::Result<()> { sys::fs::lchown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } From 11c0e58c7489df3f71c3aebe04ad6d1b6a9f6262 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 14 Sep 2021 19:14:37 +0100 Subject: [PATCH 06/14] Allow `panic!("{}", computed_str)` in const fn. --- .../src/const_eval/machine.rs | 9 ++- .../src/transform/check_consts/check.rs | 13 +++++ .../src/transform/check_consts/mod.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/panic.rs | 9 +++ library/core/src/panicking.rs | 7 +++ library/std/src/lib.rs | 1 + library/std/src/panic.rs | 6 +- library/std/src/rt.rs | 1 + src/test/ui/consts/const-eval/const_panic.rs | 8 ++- .../ui/consts/const-eval/const_panic.stderr | 38 ++++++++---- .../ui/consts/const-eval/const_panic_2021.rs | 16 +++-- .../consts/const-eval/const_panic_2021.stderr | 58 ++++++++++++------- 14 files changed, 129 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 8a90686b9003f..ae20f6f97b212 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -36,12 +36,17 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { let def_id = instance.def_id(); if Some(def_id) == self.tcx.lang_items().panic_fn() || Some(def_id) == self.tcx.lang_items().panic_str() + || Some(def_id) == self.tcx.lang_items().panic_display() || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { - // &str + // &str or &&str assert!(args.len() == 1); - let msg_place = self.deref_operand(&args[0])?; + let mut msg_place = self.deref_operand(&args[0])?; + while msg_place.layout.ty.is_ref() { + msg_place = self.deref_operand(&msg_place.into())?; + } + let msg = Symbol::intern(self.read_str(&msg_place)?); let span = self.find_closest_untracked_caller_location(); let (file, line, col) = self.location_triple_for_span(span); diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 02b317b898106..038041cdbfbd9 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -888,6 +888,10 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { if is_lang_panic_fn(tcx, callee) { self.check_op(ops::Panic); + // `begin_panic` and `panic_display` are generic functions that accept + // types other than str. Check to enforce that only str can be used in + // const-eval. + // const-eval of the `begin_panic` fn assumes the argument is `&str` if Some(callee) == tcx.lang_items().begin_panic_fn() { match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { @@ -896,6 +900,15 @@ impl Visitor<'tcx> for Checker<'mir, 'tcx> { } } + // const-eval of the `panic_display` fn assumes the argument is `&&str` + if Some(callee) == tcx.lang_items().panic_display() { + match args[0].ty(&self.ccx.body.local_decls, tcx).kind() { + ty::Ref(_, ty, _) if matches!(ty.kind(), ty::Ref(_, ty, _) if ty.is_str()) => + {} + _ => self.check_op(ops::PanicNonStr), + } + } + return; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index a5cb0f4e14b17..d1fd3ceaa589a 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -79,6 +79,7 @@ pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { // Keep in sync with what that function handles! Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().panic_str() + || Some(def_id) == tcx.lang_items().panic_display() || Some(def_id) == tcx.lang_items().begin_panic_fn() || Some(def_id) == tcx.lang_items().panic_fmt() || Some(def_id) == tcx.lang_items().begin_panic_fmt() diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b85ed0cb4bbe5..d69a247054026 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -283,6 +283,7 @@ language_item_table! { // a weak lang item, but do not have it defined. Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::None; PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None; + PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None; PanicStr, sym::panic_str, panic_str, Target::Fn, GenericRequirement::None; ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None; PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c816d06045681..fb6fdcf28def8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -923,6 +923,7 @@ symbols! { panic_2021, panic_abort, panic_bounds_check, + panic_display, panic_fmt, panic_handler, panic_impl, diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 463bec37265d5..7a8b04d6f3c13 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -27,9 +27,14 @@ pub macro panic_2015 { ($msg:literal $(,)?) => ( $crate::panicking::panic($msg) ), + // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint. ($msg:expr $(,)?) => ( $crate::panicking::panic_str($msg) ), + // Special-case the single-argument case for const_panic. + ("{}", $arg:expr $(,)?) => ( + $crate::panicking::panic_display(&$arg) + ), ($fmt:expr, $($arg:tt)+) => ( $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+)) ), @@ -44,6 +49,10 @@ pub macro panic_2021 { () => ( $crate::panicking::panic("explicit panic") ), + // Special-case the single-argument case for const_panic. + ("{}", $arg:expr $(,)?) => ( + $crate::panicking::panic_display(&$arg) + ), ($($t:tt)+) => ( $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)) ), diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 02f32675247c3..a6aa4bf43c865 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -60,6 +60,13 @@ pub fn panic_str(expr: &str) -> ! { panic_fmt(format_args!("{}", expr)); } +#[inline] +#[track_caller] +#[cfg_attr(not(bootstrap), lang = "panic_display")] // needed for const-evaluated panics +pub fn panic_display(x: &T) -> ! { + panic_fmt(format_args!("{}", *x)); +} + #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 83c6ba0e6ea45..559d2672a0da3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -258,6 +258,7 @@ #![feature(const_trait_impl)] #![feature(container_error_extra)] #![feature(core_intrinsics)] +#![feature(core_panic)] #![feature(custom_test_frameworks)] #![feature(decl_macro)] #![feature(doc_cfg)] diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index c1c039584979d..21e9669c11079 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -10,7 +10,7 @@ use crate::thread::Result; #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] -#[allow_internal_unstable(libstd_sys_internals, const_format_args)] +#[allow_internal_unstable(libstd_sys_internals, const_format_args, core_panic)] #[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] #[rustc_macro_transparency = "semitransparent"] pub macro panic_2015 { @@ -20,6 +20,10 @@ pub macro panic_2015 { ($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) }), + // Special-case the single-argument case for const_panic. + ("{}", $arg:expr $(,)?) => ({ + $crate::rt::panic_display(&$arg) + }), ($fmt:expr, $($arg:tt)+) => ({ $crate::rt::begin_panic_fmt(&$crate::const_format_args!($fmt, $($arg)+)) }), diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 72e6c23ee4990..b4f2adf938b56 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -16,6 +16,7 @@ // Re-export some of our utilities which are expected by other crates. pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count}; +pub use core::panicking::panic_display; // To reduce the generated code of the new `lang_start`, this function is doing // the real work. diff --git a/src/test/ui/consts/const-eval/const_panic.rs b/src/test/ui/consts/const-eval/const_panic.rs index e4455d86a1440..100faded079d5 100644 --- a/src/test/ui/consts/const-eval/const_panic.rs +++ b/src/test/ui/consts/const-eval/const_panic.rs @@ -15,10 +15,13 @@ const Y: () = std::unreachable!(); const X: () = std::unimplemented!(); //~^ ERROR evaluation of constant value failed -// + const W: () = std::panic!(MSG); //~^ ERROR evaluation of constant value failed +const W2: () = std::panic!("{}", MSG); +//~^ ERROR evaluation of constant value failed + const Z_CORE: () = core::panic!("cheese"); //~^ ERROR evaluation of constant value failed @@ -33,3 +36,6 @@ const X_CORE: () = core::unimplemented!(); const W_CORE: () = core::panic!(MSG); //~^ ERROR evaluation of constant value failed + +const W2_CORE: () = core::panic!("{}", MSG); +//~^ ERROR evaluation of constant value failed diff --git a/src/test/ui/consts/const-eval/const_panic.stderr b/src/test/ui/consts/const-eval/const_panic.stderr index c0c749ede5612..e98e4a506c0d6 100644 --- a/src/test/ui/consts/const-eval/const_panic.stderr +++ b/src/test/ui/consts/const-eval/const_panic.stderr @@ -39,45 +39,61 @@ LL | const W: () = std::panic!(MSG); = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic.rs:22:20 + --> $DIR/const_panic.rs:22:16 + | +LL | const W2: () = std::panic!("{}", MSG); + | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'hello', $DIR/const_panic.rs:22:16 + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic.rs:25:20 | LL | const Z_CORE: () = core::panic!("cheese"); - | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'cheese', $DIR/const_panic.rs:22:20 + | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'cheese', $DIR/const_panic.rs:25:20 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic.rs:25:21 + --> $DIR/const_panic.rs:28:21 | LL | const Z2_CORE: () = core::panic!(); - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic.rs:25:21 + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic.rs:28:21 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic.rs:28:20 + --> $DIR/const_panic.rs:31:20 | LL | const Y_CORE: () = core::unreachable!(); - | ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:28:20 + | ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:31:20 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic.rs:31:20 + --> $DIR/const_panic.rs:34:20 | LL | const X_CORE: () = core::unimplemented!(); - | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic.rs:31:20 + | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic.rs:34:20 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic.rs:34:20 + --> $DIR/const_panic.rs:37:20 | LL | const W_CORE: () = core::panic!(MSG); - | ^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'hello', $DIR/const_panic.rs:34:20 + | ^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'hello', $DIR/const_panic.rs:37:20 + | + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic.rs:40:21 + | +LL | const W2_CORE: () = core::panic!("{}", MSG); + | ^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'hello', $DIR/const_panic.rs:40:21 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/src/test/ui/consts/const-eval/const_panic_2021.rs b/src/test/ui/consts/const-eval/const_panic_2021.rs index daef34cd6a306..9b8652a776e61 100644 --- a/src/test/ui/consts/const-eval/const_panic_2021.rs +++ b/src/test/ui/consts/const-eval/const_panic_2021.rs @@ -2,6 +2,8 @@ #![feature(const_panic)] #![crate_type = "lib"] +const MSG: &str = "hello"; + const A: () = std::panic!("blåhaj"); //~^ ERROR evaluation of constant value failed @@ -14,14 +16,20 @@ const C: () = std::unreachable!(); const D: () = std::unimplemented!(); //~^ ERROR evaluation of constant value failed -const E: () = core::panic!("shark"); +const E: () = std::panic!("{}", MSG); +//~^ ERROR evaluation of constant value failed + +const A_CORE: () = core::panic!("shark"); +//~^ ERROR evaluation of constant value failed + +const B_CORE: () = core::panic!(); //~^ ERROR evaluation of constant value failed -const F: () = core::panic!(); +const C_CORE: () = core::unreachable!(); //~^ ERROR evaluation of constant value failed -const G: () = core::unreachable!(); +const D_CORE: () = core::unimplemented!(); //~^ ERROR evaluation of constant value failed -const H: () = core::unimplemented!(); +const E_CORE: () = core::panic!("{}", MSG); //~^ ERROR evaluation of constant value failed diff --git a/src/test/ui/consts/const-eval/const_panic_2021.stderr b/src/test/ui/consts/const-eval/const_panic_2021.stderr index c1bdab3693d11..9eb241ae8e59d 100644 --- a/src/test/ui/consts/const-eval/const_panic_2021.stderr +++ b/src/test/ui/consts/const-eval/const_panic_2021.stderr @@ -1,67 +1,83 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:5:15 + --> $DIR/const_panic_2021.rs:7:15 | LL | const A: () = std::panic!("blåhaj"); - | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'blåhaj', $DIR/const_panic_2021.rs:5:15 + | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'blåhaj', $DIR/const_panic_2021.rs:7:15 | = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:8:15 + --> $DIR/const_panic_2021.rs:10:15 | LL | const B: () = std::panic!(); - | ^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic_2021.rs:8:15 + | ^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic_2021.rs:10:15 | = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:11:15 + --> $DIR/const_panic_2021.rs:13:15 | LL | const C: () = std::unreachable!(); - | ^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:11:15 + | ^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:13:15 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:14:15 + --> $DIR/const_panic_2021.rs:16:15 | LL | const D: () = std::unimplemented!(); - | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic_2021.rs:14:15 + | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic_2021.rs:16:15 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:17:15 + --> $DIR/const_panic_2021.rs:19:15 | -LL | const E: () = core::panic!("shark"); - | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'shark', $DIR/const_panic_2021.rs:17:15 +LL | const E: () = std::panic!("{}", MSG); + | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'hello', $DIR/const_panic_2021.rs:19:15 | = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:20:15 + --> $DIR/const_panic_2021.rs:22:20 | -LL | const F: () = core::panic!(); - | ^^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic_2021.rs:20:15 +LL | const A_CORE: () = core::panic!("shark"); + | ^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'shark', $DIR/const_panic_2021.rs:22:20 | = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:23:15 + --> $DIR/const_panic_2021.rs:25:20 | -LL | const G: () = core::unreachable!(); - | ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:23:15 +LL | const B_CORE: () = core::panic!(); + | ^^^^^^^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/const_panic_2021.rs:25:20 + | + = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:28:20 + | +LL | const C_CORE: () = core::unreachable!(); + | ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:28:20 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0080]: evaluation of constant value failed - --> $DIR/const_panic_2021.rs:26:15 + --> $DIR/const_panic_2021.rs:31:20 | -LL | const H: () = core::unimplemented!(); - | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic_2021.rs:26:15 +LL | const D_CORE: () = core::unimplemented!(); + | ^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'not implemented', $DIR/const_panic_2021.rs:31:20 | = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 8 previous errors +error[E0080]: evaluation of constant value failed + --> $DIR/const_panic_2021.rs:34:20 + | +LL | const E_CORE: () = core::panic!("{}", MSG); + | ^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'hello', $DIR/const_panic_2021.rs:34:20 + | + = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0080`. From 9f7e281d47f27c24a59b30cae30c20435be4083c Mon Sep 17 00:00:00 2001 From: Vishad Goyal Date: Thu, 16 Sep 2021 19:28:03 +0530 Subject: [PATCH 07/14] delay error for enabling unstable lib features If #![feature] is used outside the nightly channel for only lib features, the check will be delayed to the stability pass after parsing. This is done so that appropriate help messages can be shown if the #![feature] has been used needlessly --- compiler/rustc_ast_passes/src/feature_gate.rs | 6 ++++++ compiler/rustc_passes/src/stability.rs | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 30bc4edd7e69c..06e9d9ed32933 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -702,10 +702,16 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { } fn maybe_stage_features(sess: &Session, krate: &ast::Crate) { + // checks if `#![feature]` has been used to enable any lang feature + // does not check the same for lib features unless there's at least one + // declared lang feature use rustc_errors::Applicability; if !sess.opts.unstable_features.is_nightly_build() { let lang_features = &sess.features_untracked().declared_lang_features; + if lang_features.len() == 0 { + return; + } for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) { let mut err = struct_span_err!( sess.parse_sess.span_diagnostic, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index b7e43b7785da6..d7c354aeb490f 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -929,6 +929,16 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let declared_lib_features = &tcx.features().declared_lib_features; let mut remaining_lib_features = FxHashMap::default(); for (feature, span) in declared_lib_features { + if !tcx.sess.opts.unstable_features.is_nightly_build() { + struct_span_err!( + tcx.sess, + *span, + E0554, + "`#![feature]` may not be used on the {} release channel", + env!("CFG_RELEASE_CHANNEL") + ) + .emit(); + } if remaining_lib_features.contains_key(&feature) { // Warn if the user enables a lib feature multiple times. duplicate_feature_err(tcx.sess, *span, *feature); From 58765d61ee1762ce0dd9565074000cb9c1267a02 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 18 Jun 2021 10:22:23 +0900 Subject: [PATCH 08/14] Emit clearer diagnostics for parens around `for` loop heads --- compiler/rustc_parse/src/parser/diagnostics.rs | 5 +++-- src/test/ui/parser/recover-for-loop-parens-around-head.rs | 2 +- .../ui/parser/recover-for-loop-parens-around-head.stderr | 7 +++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4fccfc287fd82..ab7478c7e5faa 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1346,8 +1346,9 @@ impl<'a> Parser<'a> { .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap()) .unwrap_or_else(|_| pprust::pat_to_string(&pat)); - self.struct_span_err(self.prev_token.span, "unexpected closing `)`") - .span_label(begin_par_sp, "opening `(`") + let sp = MultiSpan::from_spans(vec![begin_par_sp, self.prev_token.span]); + + self.struct_span_err(sp, "unexpected parenthesis surrounding `for` loop head") .span_suggestion( begin_par_sp.to(self.prev_token.span), "remove parenthesis in `for` loop", diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.rs b/src/test/ui/parser/recover-for-loop-parens-around-head.rs index 779e164634478..8080dbc332ae7 100644 --- a/src/test/ui/parser/recover-for-loop-parens-around-head.rs +++ b/src/test/ui/parser/recover-for-loop-parens-around-head.rs @@ -9,7 +9,7 @@ fn main() { for ( elem in vec ) { //~^ ERROR expected one of `)`, `,`, `@`, or `|`, found keyword `in` - //~| ERROR unexpected closing `)` + //~| ERROR unexpected parenthesis surrounding `for` loop head const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types } } diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr index e97cf544ac268..98f6b5b884bd2 100644 --- a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr +++ b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr @@ -4,13 +4,12 @@ error: expected one of `)`, `,`, `@`, or `|`, found keyword `in` LL | for ( elem in vec ) { | ^^ expected one of `)`, `,`, `@`, or `|` -error: unexpected closing `)` - --> $DIR/recover-for-loop-parens-around-head.rs:10:23 +error: unexpected parenthesis surrounding `for` loop head + --> $DIR/recover-for-loop-parens-around-head.rs:10:9 | LL | for ( elem in vec ) { - | --------------^ + | ^-------------^ | | - | opening `(` | help: remove parenthesis in `for` loop: `elem in vec` error[E0308]: mismatched types From e9bf73cb2b025e188787683f1423177a9102d260 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 17 Sep 2021 13:39:26 +0900 Subject: [PATCH 09/14] Use `multipart_suggestion` --- .../rustc_parse/src/parser/diagnostics.rs | 32 ++++++++----------- compiler/rustc_parse/src/parser/expr.rs | 2 +- ...recover-for-loop-parens-around-head.stderr | 10 ++++-- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index ab7478c7e5faa..88b5f89f6809f 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1334,31 +1334,25 @@ impl<'a> Parser<'a> { pub(super) fn recover_parens_around_for_head( &mut self, pat: P, - expr: &Expr, begin_paren: Option, ) -> P { match (&self.token.kind, begin_paren) { (token::CloseDelim(token::Paren), Some(begin_par_sp)) => { self.bump(); - let pat_str = self - // Remove the `(` from the span of the pattern: - .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap()) - .unwrap_or_else(|_| pprust::pat_to_string(&pat)); - - let sp = MultiSpan::from_spans(vec![begin_par_sp, self.prev_token.span]); - - self.struct_span_err(sp, "unexpected parenthesis surrounding `for` loop head") - .span_suggestion( - begin_par_sp.to(self.prev_token.span), - "remove parenthesis in `for` loop", - format!("{} in {}", pat_str, pprust::expr_to_string(&expr)), - // With e.g. `for (x) in y)` this would replace `(x) in y)` - // with `x) in y)` which is syntactically invalid. - // However, this is prevented before we get here. - Applicability::MachineApplicable, - ) - .emit(); + self.struct_span_err( + MultiSpan::from_spans(vec![begin_par_sp, self.prev_token.span]), + "unexpected parenthesis surrounding `for` loop head", + ) + .multipart_suggestion( + "remove parenthesis in `for` loop", + vec![(begin_par_sp, String::new()), (self.prev_token.span, String::new())], + // With e.g. `for (x) in y)` this would replace `(x) in y)` + // with `x) in y)` which is syntactically invalid. + // However, this is prevented before we get here. + Applicability::MachineApplicable, + ) + .emit(); // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. pat.and_then(|pat| match pat.kind { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 737f1d9cbb15e..d8f9fc9179e89 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2042,7 +2042,7 @@ impl<'a> Parser<'a> { self.check_for_for_in_in_typo(self.prev_token.span); let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; - let pat = self.recover_parens_around_for_head(pat, &expr, begin_paren); + let pat = self.recover_parens_around_for_head(pat, begin_paren); let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?; attrs.extend(iattrs); diff --git a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr index 98f6b5b884bd2..21991348327b3 100644 --- a/src/test/ui/parser/recover-for-loop-parens-around-head.stderr +++ b/src/test/ui/parser/recover-for-loop-parens-around-head.stderr @@ -8,9 +8,13 @@ error: unexpected parenthesis surrounding `for` loop head --> $DIR/recover-for-loop-parens-around-head.rs:10:9 | LL | for ( elem in vec ) { - | ^-------------^ - | | - | help: remove parenthesis in `for` loop: `elem in vec` + | ^ ^ + | +help: remove parenthesis in `for` loop + | +LL - for ( elem in vec ) { +LL + for elem in vec { + | error[E0308]: mismatched types --> $DIR/recover-for-loop-parens-around-head.rs:13:38 From 9342be5538ad5c97e8d2496e1cbbf2530d377e5e Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Thu, 29 Jul 2021 05:49:56 +0900 Subject: [PATCH 10/14] Recover invalid assoc type bounds using `==` --- .../rustc_parse/src/parser/diagnostics.rs | 14 +++++++++- .../ui/const-generics/issues/issue-87493.rs | 14 ++++++++++ .../const-generics/issues/issue-87493.stderr | 26 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/const-generics/issues/issue-87493.rs create mode 100644 src/test/ui/const-generics/issues/issue-87493.stderr diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 4fccfc287fd82..39651bd5e6745 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1955,7 +1955,19 @@ impl<'a> Parser<'a> { } match self.parse_expr_res(Restrictions::CONST_EXPR, None) { Ok(expr) => { - if token::Comma == self.token.kind || self.token.kind.should_end_const_arg() { + // Find a mistake like `MyTrait`. + if token::EqEq == snapshot.token.kind { + err.span_suggestion( + snapshot.token.span, + "replace `==` with `=`", + "=".to_string(), + Applicability::MaybeIncorrect, + ); + let value = self.mk_expr_err(expr.span); + err.emit(); + return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); + } else if token::Comma == self.token.kind || self.token.kind.should_end_const_arg() + { // Avoid the following output by checking that we consumed a full const arg: // help: expressions must be enclosed in braces to be used as const generic // arguments diff --git a/src/test/ui/const-generics/issues/issue-87493.rs b/src/test/ui/const-generics/issues/issue-87493.rs new file mode 100644 index 0000000000000..d8599ab22a32b --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-87493.rs @@ -0,0 +1,14 @@ +pub trait MyTrait { + type Assoc; +} + +pub fn foo(_s: S, _t: T) +where + S: MyTrait, + T: MyTrait, + //~^ ERROR: expected one of `,` or `>`, found `==` + //~| ERROR: this trait takes 0 generic arguments but 1 generic argument was supplied +{ +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-87493.stderr b/src/test/ui/const-generics/issues/issue-87493.stderr new file mode 100644 index 0000000000000..b1ac08b51b50f --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-87493.stderr @@ -0,0 +1,26 @@ +error: expected one of `,` or `>`, found `==` + --> $DIR/issue-87493.rs:8:22 + | +LL | T: MyTrait, + | ^^ + | | + | expected one of `,` or `>` + | help: replace `==` with `=`: `=` + +error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/issue-87493.rs:8:8 + | +LL | T: MyTrait, + | ^^^^^^^------------------- help: remove these generics + | | + | expected 0 generic arguments + | +note: trait defined here, with 0 generic parameters + --> $DIR/issue-87493.rs:1:11 + | +LL | pub trait MyTrait { + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0107`. From ee99bb393953c31169e89597ec893fd15a3ff4ee Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 17 Sep 2021 14:10:41 +0900 Subject: [PATCH 11/14] Apply review comments --- compiler/rustc_parse/src/parser/diagnostics.rs | 4 ++-- src/test/ui/const-generics/issues/issue-87493.stderr | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 39651bd5e6745..1a77057939f5b 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1959,11 +1959,11 @@ impl<'a> Parser<'a> { if token::EqEq == snapshot.token.kind { err.span_suggestion( snapshot.token.span, - "replace `==` with `=`", + "if you meant to use an associated type binding, replace `==` with `=`", "=".to_string(), Applicability::MaybeIncorrect, ); - let value = self.mk_expr_err(expr.span); + let value = self.mk_expr_err(start.to(expr.span)); err.emit(); return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); } else if token::Comma == self.token.kind || self.token.kind.should_end_const_arg() diff --git a/src/test/ui/const-generics/issues/issue-87493.stderr b/src/test/ui/const-generics/issues/issue-87493.stderr index b1ac08b51b50f..8f92eeaffd19d 100644 --- a/src/test/ui/const-generics/issues/issue-87493.stderr +++ b/src/test/ui/const-generics/issues/issue-87493.stderr @@ -2,10 +2,12 @@ error: expected one of `,` or `>`, found `==` --> $DIR/issue-87493.rs:8:22 | LL | T: MyTrait, - | ^^ - | | - | expected one of `,` or `>` - | help: replace `==` with `=`: `=` + | ^^ expected one of `,` or `>` + | +help: if you meant to use an associated type binding, replace `==` with `=` + | +LL | T: MyTrait, + | ~ error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied --> $DIR/issue-87493.rs:8:8 From bc49c3b6516779acb5b0bf2eae6d1404ead216eb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 5 Sep 2021 17:20:37 +0200 Subject: [PATCH 12/14] Allow to pass "compiler" arguments to doc subcommand --- src/bootstrap/doc.rs | 54 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index fbc7f19cb731c..5a1593d740529 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -537,7 +537,7 @@ impl Step for Rustc { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { let builder = run.builder; - run.krate("rustc-main").default_condition(builder.config.docs) + run.krate("rustc-main").path("compiler").default_condition(builder.config.docs) } fn make_run(run: RunConfig<'_>) { @@ -553,9 +553,24 @@ impl Step for Rustc { fn run(self, builder: &Builder<'_>) { let stage = self.stage; let target = self.target; + let mut is_explicit_request = false; builder.info(&format!("Documenting stage{} compiler ({})", stage, target)); - if !builder.config.compiler_docs { + let paths = builder + .paths + .iter() + .map(components_simplified) + .filter_map(|path| { + if path.get(0) == Some(&"compiler") { + is_explicit_request = true; + path.get(1).map(|p| p.to_owned()) + } else { + None + } + }) + .collect::>(); + + if !builder.config.compiler_docs && !is_explicit_request { builder.info("\tskipping - compiler/librustdoc docs disabled"); return; } @@ -603,15 +618,34 @@ impl Step for Rustc { cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); - // Find dependencies for top level crates. let mut compiler_crates = HashSet::new(); - for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] { - compiler_crates.extend( - builder - .in_tree_crates(root_crate, Some(target)) - .into_iter() - .map(|krate| krate.name), - ); + + if paths.is_empty() { + // Find dependencies for top level crates. + for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] { + compiler_crates.extend( + builder + .in_tree_crates(root_crate, Some(target)) + .into_iter() + .map(|krate| krate.name), + ); + } + } else { + for root_crate in paths { + if !builder.src.join("compiler").join(&root_crate).exists() { + builder.info(&format!( + "\tskipping - compiler/{} (unknown compiler crate)", + root_crate + )); + } else { + compiler_crates.extend( + builder + .in_tree_crates(root_crate, Some(target)) + .into_iter() + .map(|krate| krate.name), + ); + } + } } for krate in &compiler_crates { From 57ee7a63ebc28e9ed645702fa152b8f35a0cd538 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 5 Sep 2021 17:24:09 +0200 Subject: [PATCH 13/14] Correctly handle "--open" option when building compiler docs --- src/bootstrap/doc.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 5a1593d740529..a1a954fdfceb2 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -648,15 +648,24 @@ impl Step for Rustc { } } + let mut to_open = None; for krate in &compiler_crates { // Create all crate output directories first to make sure rustdoc uses // relative links. // FIXME: Cargo should probably do this itself. t!(fs::create_dir_all(out_dir.join(krate))); cargo.arg("-p").arg(krate); + if to_open.is_none() { + to_open = Some(krate); + } } builder.run(&mut cargo.into()); + // Let's open the first crate documentation page: + if let Some(krate) = to_open { + let index = out.join(krate).join("index.html"); + open(builder, &index); + } } } From cd3f4da244578a2ab4d17d10016c61b9191b21e4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 15 Sep 2021 13:37:29 +0200 Subject: [PATCH 14/14] Add rustdoc version into the help popup --- src/librustdoc/html/render/write_shared.rs | 16 ++++++++++++---- src/librustdoc/html/static/css/rustdoc.css | 15 ++++++++++++--- src/librustdoc/html/static/css/themes/ayu.css | 4 ++-- src/librustdoc/html/static/css/themes/dark.css | 4 ++-- src/librustdoc/html/static/css/themes/light.css | 4 ++-- src/librustdoc/html/static/js/main.js | 9 +++++++++ 6 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 1dd0917699bf4..c1a83ad5820cb 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -268,10 +268,18 @@ pub(super) fn write_shared( // Maybe we can change the representation to move this out of main.js? write_minify( "main.js", - static_files::MAIN_JS.replace( - "/* INSERT THEMES HERE */", - &format!(" = {}", serde_json::to_string(&themes).unwrap()), - ), + static_files::MAIN_JS + .replace( + "/* INSERT THEMES HERE */", + &format!(" = {}", serde_json::to_string(&themes).unwrap()), + ) + .replace( + "/* INSERT RUSTDOC_VERSION HERE */", + &format!( + "rustdoc {}", + rustc_interface::util::version_str().unwrap_or("unknown version") + ), + ), cx, options, )?; diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 2d4bfc62af68e..eb7cc9309f416 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -928,15 +928,24 @@ body.blur > :not(#help) { display: block; margin-right: 0.5rem; } -#help > div > span { +#help span.top, #help span.bottom { text-align: center; display: block; - margin: 10px 0; font-size: 18px; - border-bottom: 1px solid #ccc; + +} +#help span.top { + text-align: center; + display: block; + margin: 10px 0; + border-bottom: 1px solid; padding-bottom: 4px; margin-bottom: 6px; } +#help span.bottom { + clear: both; + border-top: 1px solid; +} #help dd { margin: 5px 35px; } #help .infos { padding-left: 0; } #help h1, #help h2 { margin-top: 0; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index eada8f4a04dae..c79801e830876 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -286,8 +286,8 @@ details.undocumented > summary::before { border-radius: 4px; } -#help > div > span { - border-bottom-color: #5c6773; +#help span.bottom, #help span.top { + border-color: #5c6773; } .since { diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index d9348be6994e2..d2e54070acd68 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -242,8 +242,8 @@ details.undocumented > summary::before { border-color: #bfbfbf; } -#help > div > span { - border-bottom-color: #bfbfbf; +#help span.bottom, #help span.top { + border-color: #bfbfbf; } #help dt { diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 0ffe5929ea593..25d810560c146 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -232,8 +232,8 @@ details.undocumented > summary::before { border-color: #bfbfbf; } -#help > div > span { - border-bottom-color: #bfbfbf; +#help span.bottom, #help span.top { + border-color: #bfbfbf; } .since { diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 1eebd39256459..e396fd9d288db 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -911,6 +911,7 @@ function hideThemeButtonState() { }); var book_info = document.createElement("span"); + book_info.className = "top"; book_info.innerHTML = "You can find more information in \ the rustdoc book."; @@ -961,6 +962,14 @@ function hideThemeButtonState() { container.appendChild(div_shortcuts); container.appendChild(div_infos); + var rustdoc_version = document.createElement("span"); + rustdoc_version.className = "bottom"; + var rustdoc_version_code = document.createElement("code"); + rustdoc_version_code.innerText = "/* INSERT RUSTDOC_VERSION HERE */"; + rustdoc_version.appendChild(rustdoc_version_code); + + container.appendChild(rustdoc_version); + popup.appendChild(container); insertAfter(popup, searchState.outputElement()); // So that it's only built once and then it'll do nothing when called!