From e132077db74dd6bd26566dbe3610848e3a771256 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Mar 2022 14:10:19 -0400 Subject: [PATCH 1/8] assert_uninit_valid: ensure we detect at least arrays of uninhabited types --- .../intrinsics/panic-uninitialized-zeroed.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs index a1cfee944c8bd..98fd13553c009 100644 --- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -104,6 +104,32 @@ fn main() { "attempted to instantiate uninhabited type `Bar`" ); + test_panic_msg( + || mem::uninitialized::<[Foo; 2]>(), + "attempted to instantiate uninhabited type `[Foo; 2]`" + ); + test_panic_msg( + || mem::zeroed::<[Foo; 2]>(), + "attempted to instantiate uninhabited type `[Foo; 2]`" + ); + test_panic_msg( + || MaybeUninit::<[Foo; 2]>::uninit().assume_init(), + "attempted to instantiate uninhabited type `[Foo; 2]`" + ); + + test_panic_msg( + || mem::uninitialized::<[Bar; 2]>(), + "attempted to instantiate uninhabited type `[Bar; 2]`" + ); + test_panic_msg( + || mem::zeroed::<[Bar; 2]>(), + "attempted to instantiate uninhabited type `[Bar; 2]`" + ); + test_panic_msg( + || MaybeUninit::<[Bar; 2]>::uninit().assume_init(), + "attempted to instantiate uninhabited type `[Bar; 2]`" + ); + // Types that do not like zero-initialziation test_panic_msg( || mem::uninitialized::(), @@ -199,7 +225,9 @@ fn main() { let _val = mem::zeroed::(); let _val = mem::zeroed::>(); let _val = mem::zeroed::>>(); + let _val = mem::zeroed::<[!; 0]>(); let _val = mem::uninitialized::>(); + let _val = mem::uninitialized::<[!; 0]>(); // These are UB because they have not been officially blessed, but we await the resolution // of before doing From 39bff4bf9c998ba77d12b36b26c604340638fa73 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 6 Apr 2022 21:27:46 -0700 Subject: [PATCH 2/8] don't report int/float ambiguity when we have previous errors --- .../src/traits/error_reporting/mod.rs | 22 +++++++++++-- .../ui/traits/no-fallback-multiple-impls.rs | 16 ++++++++++ .../traits/no-fallback-multiple-impls.stderr | 9 ++++++ src/test/ui/traits/test-2.rs | 4 +-- src/test/ui/traits/test-2.stderr | 32 ++----------------- 5 files changed, 48 insertions(+), 35 deletions(-) create mode 100644 src/test/ui/traits/no-fallback-multiple-impls.rs create mode 100644 src/test/ui/traits/no-fallback-multiple-impls.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 9998c5bb087e1..ac98dd5801e40 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -36,6 +36,7 @@ use rustc_span::symbol::{kw, sym}; use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; use std::iter; +use std::ops::ControlFlow; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::AtExt as _; @@ -2226,9 +2227,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { post.dedup(); if self.is_tainted_by_errors() - && crate_names.len() == 1 - && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) - && spans.len() == 0 + && (crate_names.len() == 1 + && spans.len() == 0 + && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) + || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) { // Avoid complaining about other inference issues for expressions like // `42 >> 1`, where the types are still `{integer}`, but we want to @@ -2666,3 +2668,17 @@ impl ArgKind { } } } + +struct HasNumericInferVisitor; + +impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor { + type BreakTy = (); + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) { + ControlFlow::Break(()) + } else { + ControlFlow::CONTINUE + } + } +} diff --git a/src/test/ui/traits/no-fallback-multiple-impls.rs b/src/test/ui/traits/no-fallback-multiple-impls.rs new file mode 100644 index 0000000000000..7ed3796f08b76 --- /dev/null +++ b/src/test/ui/traits/no-fallback-multiple-impls.rs @@ -0,0 +1,16 @@ +trait Fallback { + fn foo(&self) {} +} + +impl Fallback for i32 {} + +impl Fallback for u64 {} + +impl Fallback for usize {} + +fn main() { + missing(); + //~^ ERROR cannot find function `missing` in this scope + 0.foo(); + // But then we shouldn't report an inference ambiguity here... +} diff --git a/src/test/ui/traits/no-fallback-multiple-impls.stderr b/src/test/ui/traits/no-fallback-multiple-impls.stderr new file mode 100644 index 0000000000000..61c9e5aaabdb4 --- /dev/null +++ b/src/test/ui/traits/no-fallback-multiple-impls.stderr @@ -0,0 +1,9 @@ +error[E0425]: cannot find function `missing` in this scope + --> $DIR/no-fallback-multiple-impls.rs:12:5 + | +LL | missing(); + | ^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/traits/test-2.rs b/src/test/ui/traits/test-2.rs index d062de25ac8c1..342928e882a55 100644 --- a/src/test/ui/traits/test-2.rs +++ b/src/test/ui/traits/test-2.rs @@ -6,9 +6,9 @@ impl bar for i32 { fn dup(&self) -> i32 { *self } fn blah(&self) {} } impl bar for u32 { fn dup(&self) -> u32 { *self } fn blah(&self) {} } fn main() { - 10.dup::(); //~ ERROR type annotations needed + 10.dup::(); //~^ ERROR this associated function takes 0 generic arguments but 1 - 10.blah::(); //~ ERROR type annotations needed + 10.blah::(); //~^ ERROR this associated function takes 1 generic argument but 2 (Box::new(10) as Box).dup(); //~^ ERROR E0038 diff --git a/src/test/ui/traits/test-2.stderr b/src/test/ui/traits/test-2.stderr index 5eec012458450..77ea4e4e974eb 100644 --- a/src/test/ui/traits/test-2.stderr +++ b/src/test/ui/traits/test-2.stderr @@ -79,35 +79,7 @@ LL | trait bar { fn dup(&self) -> Self; fn blah(&self); } = note: required because of the requirements on the impl of `CoerceUnsized>` for `Box<{integer}>` = note: required by cast to type `Box` -error[E0283]: type annotations needed - --> $DIR/test-2.rs:9:8 - | -LL | 10.dup::(); - | ^^^ cannot infer type for type `{integer}` - | -note: multiple `impl`s satisfying `{integer}: bar` found - --> $DIR/test-2.rs:5:1 - | -LL | impl bar for i32 { fn dup(&self) -> i32 { *self } fn blah(&self) {} } - | ^^^^^^^^^^^^^^^^ -LL | impl bar for u32 { fn dup(&self) -> u32 { *self } fn blah(&self) {} } - | ^^^^^^^^^^^^^^^^ - -error[E0283]: type annotations needed - --> $DIR/test-2.rs:11:8 - | -LL | 10.blah::(); - | ^^^^ cannot infer type for type `{integer}` - | -note: multiple `impl`s satisfying `{integer}: bar` found - --> $DIR/test-2.rs:5:1 - | -LL | impl bar for i32 { fn dup(&self) -> i32 { *self } fn blah(&self) {} } - | ^^^^^^^^^^^^^^^^ -LL | impl bar for u32 { fn dup(&self) -> u32 { *self } fn blah(&self) {} } - | ^^^^^^^^^^^^^^^^ - -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors -Some errors have detailed explanations: E0038, E0107, E0283. +Some errors have detailed explanations: E0038, E0107. For more information about an error, try `rustc --explain E0038`. From aa3c141c86652ce72cca6d32c69afbdc23ee51fa Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 7 Apr 2022 13:44:57 -0700 Subject: [PATCH 3/8] reword panic vs result section to remove recoverable vs unrecoverable framing --- library/core/src/macros/panic.md | 34 +++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/library/core/src/macros/panic.md b/library/core/src/macros/panic.md index d8206e7893114..98fb7e9e41d7a 100644 --- a/library/core/src/macros/panic.md +++ b/library/core/src/macros/panic.md @@ -24,20 +24,30 @@ See also the macro [`compile_error!`], for raising errors during compilation. # When to use `panic!` vs `Result` -The Rust model of error handling groups errors into two major categories: -recoverable and unrecoverable errors. For a recoverable error, such as a file -not found error, it’s reasonable to report the problem to the user and retry -the operation. Unrecoverable errors are always symptoms of bugs, like trying to -access a location beyond the end of an array. +The Rust language provides two complementary systems for constructing / +representing, reporting, propagating, reacting to, and discarding errors. These +responsibilities are collectively known as "error handling." `panic!` and +`Result` are similar in that they are each the primary interface of their +respective error handling systems; however, the meaning these interfaces attach +to their errors and the responsibilities they fulfill within their respective +error handling systems differ. -The Rust language and standard library provides `Result` and `panic!` as parts -of two complementary systems for representing, reporting, propagating, reacting -to, and discarding errors for in these two categories. +The `panic!` macro is used to construct errors that represent a bug that has +been detected in your program. With `panic!` you provide a message that +describes the bug and the language then constructs an error with that message, +reports it, and propagates it for you. -The `panic!` macro is provided to represent unrecoverable errors, whereas the -`Result` enum is provided to represent recoverable errors. For more detailed -information about error handling check out the [book] or the [`std::result`] -module docs. +`Result` on the other hand is used to wrap other types that represent either +the successful result of some computation, `Ok(T)`, or error types that +represent an anticipated runtime failure mode of that computation, `Err(E)`. +`Result` is used alongside user defined types which represent the various +anticipated runtime failure modes that the associated computation could +encounter. `Result` must be propagated manually, often with the the help of the +`?` operator and `Try` trait, and they must be reported manually, often with +the help of the `Error` trait. + +For more detailed information about error handling check out the [book] or the +[`std::result`] module docs. [ounwrap]: Option::unwrap [runwrap]: Result::unwrap From 43d0497824d3d1a6f0b15d865a00715537673af2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 8 Apr 2022 15:30:37 +0200 Subject: [PATCH 4/8] Fix invalid array access in `beautify_doc_string` --- compiler/rustc_ast/src/util/comments.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index f51b0086dc8b6..8730aeb0f3bff 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -52,7 +52,10 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { // when we try to compute the "horizontal trim". let lines = if kind == CommentKind::Block { // Whatever happens, we skip the first line. - let mut i = if lines[0].trim_start().starts_with('*') { 0 } else { 1 }; + let mut i = lines + .get(0) + .map(|l| if l.trim_start().starts_with('*') { 0 } else { 1 }) + .unwrap_or(0); let mut j = lines.len(); while i < j && lines[i].trim().is_empty() { From 5e8bd9bbaaaa842e96a3ddef3a75208d0c96ffe7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 8 Apr 2022 15:30:53 +0200 Subject: [PATCH 5/8] Add test for empty doc comments with a backline --- src/test/rustdoc/empty-doc-comment.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/test/rustdoc/empty-doc-comment.rs diff --git a/src/test/rustdoc/empty-doc-comment.rs b/src/test/rustdoc/empty-doc-comment.rs new file mode 100644 index 0000000000000..b1dae930e066b --- /dev/null +++ b/src/test/rustdoc/empty-doc-comment.rs @@ -0,0 +1,22 @@ +// Ensure that empty doc comments don't panic. + +/*! +*/ + +/// +/// +pub struct Foo; + +#[doc = " +"] +pub mod Mod { + //! + //! +} + +/** +*/ +pub mod Another { + #![doc = " +"] +} From 1040cab53b95aa47c2ea8e1a6de73eddadc57aa7 Mon Sep 17 00:00:00 2001 From: Aria Beingessner Date: Mon, 21 Mar 2022 19:25:44 -0400 Subject: [PATCH 6/8] WIP PROOF-OF-CONCEPT: Make the compiler complain about all int<->ptr casts. ALL OF THEM --- compiler/rustc_lint_defs/src/builtin.rs | 36 ++++++++++++++ compiler/rustc_typeck/src/check/cast.rs | 64 +++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5704c6ed3b25d..f9ac0a54a2f34 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2648,6 +2648,41 @@ declare_lint! { }; } +declare_lint! { + /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer + /// and a pointer. + /// + /// ### Example + /// + /// fn main() { + /// let my_ref = &0; + /// let my_addr = my_ref as usize; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Casting a pointer to an integer or an integer to a pointer is a lossy operation, + /// because beyond just an *address* a pointer may be associated with a particular + /// *provenance* and *segment*. This information is required by both the compiler + /// and the hardware to correctly execute your code. If you need to do this kind + /// of operation, use ptr::addr and ptr::with_addr. + /// + /// This is a [future-incompatible] lint to transition this to a hard error + /// in the future. See [issue #9999999] for more details. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + /// [issue #9999999]: https://github.com/rust-lang/rust/issues/9999999 + pub FUZZY_PROVENANCE_CASTS, + Warn, + "A lossy pointer-integer integer cast is used", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #9999999 ", + }; +} + declare_lint! { /// The `const_evaluatable_unchecked` lint detects a generic constant used /// in a type. @@ -3101,6 +3136,7 @@ declare_lint_pass! { UNSAFE_OP_IN_UNSAFE_FN, INCOMPLETE_INCLUDE, CENUM_IMPL_DROP_CAST, + FUZZY_PROVENANCE_CASTS, CONST_EVALUATABLE_UNCHECKED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, MUST_NOT_SUSPEND, diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 7ce428ea12466..9b67fd54bd66f 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -807,11 +807,22 @@ impl<'a, 'tcx> CastCheck<'tcx> { // ptr -> * (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast - (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), // ptr-addr-cast - (FnPtr, Int(_)) => Ok(CastKind::FnPtrAddrCast), - // * -> ptr - (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast + // ptr-addr-cast + (Ptr(m_expr), Int(_)) => { + self.fuzzy_provenance_ptr2int_lint(fcx, t_from); + self.check_ptr_addr_cast(fcx, m_expr) + } + (FnPtr, Int(_)) => { + self.fuzzy_provenance_ptr2int_lint(fcx, t_from); + Ok(CastKind::FnPtrAddrCast) + } + // addr-ptr-cast + (Int(_), Ptr(mt)) => { + self.fuzzy_provenance_int2ptr_lint(fcx); + self.check_addr_ptr_cast(fcx, mt) + } + // fn-ptr-cast (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), // prim -> prim @@ -934,6 +945,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx: &FnCtxt<'a, 'tcx>, m_cast: TypeAndMut<'tcx>, ) -> Result { + self.fuzzy_provenance_int2ptr_lint(fcx); // ptr-addr cast. pointer must be thin. match fcx.pointer_kind(m_cast.ty, self.span)? { None => Err(CastError::UnknownCastPtrKind), @@ -973,6 +985,50 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } } + + fn fuzzy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_from: CastTy<'tcx>) { + fcx.tcx.struct_span_lint_hir( + lint::builtin::FUZZY_PROVENANCE_CASTS, + self.expr.hir_id, + self.span, + |err| { + let mut err = err.build(&format!( + "strict provenance disallows casting pointer `{}` to integer `{}`", + self.expr_ty, self.cast_ty + )); + + if let CastTy::FnPtr = t_from { + err.help( + "use `(... as *const u8).addr()` to obtain \ + the address of a function pointer", + ); + } else { + err.help("use `.addr()` to obtain the address of a pointer"); + } + + err.emit(); + }, + ); + } + + fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { + fcx.tcx.struct_span_lint_hir( + lint::builtin::FUZZY_PROVENANCE_CASTS, + self.expr.hir_id, + self.span, + |err| { + err.build(&format!( + "strict provenance disallows casting integer `{}` to pointer `{}`", + self.expr_ty, self.cast_ty + )) + .help( + "use `.with_addr(...)` to adjust a valid pointer \ + in the same allocation, to this address", + ) + .emit(); + }, + ); + } } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { From 98a483423720bda1f51a22f01b378fa8e8e8b9a3 Mon Sep 17 00:00:00 2001 From: niluxv Date: Sat, 2 Apr 2022 21:07:00 +0200 Subject: [PATCH 7/8] Split `fuzzy_provenance_casts` into lossy and fuzzy, feature gate and test it * split `fuzzy_provenance_casts` into a ptr2int and a int2ptr lint * feature gate both lints * update documentation to be more realistic short term * add tests for these lints --- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_lint_defs/src/builtin.rs | 88 +++++++++++++++---- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/cast.rs | 61 +++++++++---- .../language-features/strict-provenance.md | 22 +++++ .../feature-gate-strict_provenance.rs | 19 ++++ .../feature-gate-strict_provenance.stderr | 63 +++++++++++++ .../lint-strict-provenance-fuzzy-casts.rs | 7 ++ .../lint-strict-provenance-fuzzy-casts.stderr | 19 ++++ .../lint-strict-provenance-lossy-casts.rs | 11 +++ .../lint-strict-provenance-lossy-casts.stderr | 23 +++++ 11 files changed, 281 insertions(+), 35 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/strict-provenance.md create mode 100644 src/test/ui/feature-gates/feature-gate-strict_provenance.rs create mode 100644 src/test/ui/feature-gates/feature-gate-strict_provenance.stderr create mode 100644 src/test/ui/lint/lint-strict-provenance-fuzzy-casts.rs create mode 100644 src/test/ui/lint/lint-strict-provenance-fuzzy-casts.stderr create mode 100644 src/test/ui/lint/lint-strict-provenance-lossy-casts.rs create mode 100644 src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 28466315c8687..8340a0b360ef7 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -505,6 +505,8 @@ declare_features! ( (active, static_nobundle, "1.16.0", Some(37403), None), /// Allows attributes on expressions and non-item statements. (active, stmt_expr_attributes, "1.6.0", Some(15701), None), + /// Allows lints part of the strict provenance effort. + (active, strict_provenance, "1.61.0", Some(95228), None), /// Allows the use of `#[target_feature]` on safe functions. (active, target_feature_11, "1.45.0", Some(69098), None), /// Allows using `#[thread_local]` on `static` items. diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f9ac0a54a2f34..89ce307d12cd7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2654,9 +2654,12 @@ declare_lint! { /// /// ### Example /// + /// ```rust + /// #![feature(strict_provenance)] + /// #![warn(fuzzy_provenance_casts)] + /// /// fn main() { - /// let my_ref = &0; - /// let my_addr = my_ref as usize; + /// let _dangling = 16_usize as *const u8; /// } /// ``` /// @@ -2664,23 +2667,75 @@ declare_lint! { /// /// ### Explanation /// - /// Casting a pointer to an integer or an integer to a pointer is a lossy operation, - /// because beyond just an *address* a pointer may be associated with a particular - /// *provenance* and *segment*. This information is required by both the compiler - /// and the hardware to correctly execute your code. If you need to do this kind - /// of operation, use ptr::addr and ptr::with_addr. + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting an integer to a pointer is considered bad style, as a pointer + /// contains, besides the *address* also a *provenance*, indicating what + /// memory the pointer is allowed to read/write. Casting an integer, which + /// doesn't have provenance, to a pointer requires the compiler to assign + /// (guess) provenance. The compiler assigns "all exposed valid" (see the + /// docs of [`ptr::from_exposed_addr`] for more information about this + /// "exposing"). This penalizes the optimiser and is not well suited for + /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI + /// platforms). /// - /// This is a [future-incompatible] lint to transition this to a hard error - /// in the future. See [issue #9999999] for more details. + /// It is much better to use [`ptr::with_addr`] instead to specify the + /// provenance you want. If using this function is not possible because the + /// code relies on exposed provenance then there is as an escape hatch + /// [`ptr::from_exposed_addr`]. /// - /// [future-incompatible]: ../index.md#future-incompatible-lints - /// [issue #9999999]: https://github.com/rust-lang/rust/issues/9999999 + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/ptr/fn.with_addr + /// [`ptr::from_exposed_addr`]: https://doc.rust-lang.org/core/ptr/fn.from_exposed_addr pub FUZZY_PROVENANCE_CASTS, - Warn, - "A lossy pointer-integer integer cast is used", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #9999999 ", - }; + Allow, + "a fuzzy integer to pointer cast is used", + @feature_gate = sym::strict_provenance; +} + +declare_lint! { + /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer + /// and an integer. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance)] + /// #![warn(lossy_provenance_casts)] + /// + /// fn main() { + /// let x: u8 = 37; + /// let _addr: usize = &x as *const u8 as usize; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting a pointer to an integer is a lossy operation, because beyond + /// just an *address* a pointer may be associated with a particular + /// *provenance*. This information is used by the optimiser and for dynamic + /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). + /// + /// Since this cast is lossy, it is considered good style to use the + /// [`ptr::addr`] method instead, which has a similar effect, but doesn't + /// "expose" the pointer provenance. This improves optimisation potential. + /// See the docs of [`ptr::addr`] and [`ptr::expose_addr`] for more information + /// about exposing pointer provenance. + /// + /// If your code can't comply with strict provenance and needs to expose + /// the provenance, then there is [`ptr::expose_addr`] as an escape hatch, + /// which preserves the behaviour of `as usize` casts while being explicit + /// about the semantics. + /// + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::addr`]: https://doc.rust-lang.org/core/ptr/fn.addr + /// [`ptr::expose_addr`]: https://doc.rust-lang.org/core/ptr/fn.expose_addr + pub LOSSY_PROVENANCE_CASTS, + Allow, + "a lossy pointer to integer cast is used", + @feature_gate = sym::strict_provenance; } declare_lint! { @@ -3137,6 +3192,7 @@ declare_lint_pass! { INCOMPLETE_INCLUDE, CENUM_IMPL_DROP_CAST, FUZZY_PROVENANCE_CASTS, + LOSSY_PROVENANCE_CASTS, CONST_EVALUATABLE_UNCHECKED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, MUST_NOT_SUSPEND, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f5803aaa0786e..dc4d10f699c75 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1348,6 +1348,7 @@ symbols! { str_trim, str_trim_end, str_trim_start, + strict_provenance, stringify, stringify_macro, struct_field_attributes, diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 9b67fd54bd66f..6091b8fee00b6 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -809,12 +809,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast - (Ptr(m_expr), Int(_)) => { - self.fuzzy_provenance_ptr2int_lint(fcx, t_from); + (Ptr(m_expr), Int(t_c)) => { + self.lossy_provenance_ptr2int_lint(fcx, t_c); self.check_ptr_addr_cast(fcx, m_expr) } (FnPtr, Int(_)) => { - self.fuzzy_provenance_ptr2int_lint(fcx, t_from); + // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) } // addr-ptr-cast @@ -945,7 +945,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { fcx: &FnCtxt<'a, 'tcx>, m_cast: TypeAndMut<'tcx>, ) -> Result { - self.fuzzy_provenance_int2ptr_lint(fcx); // ptr-addr cast. pointer must be thin. match fcx.pointer_kind(m_cast.ty, self.span)? { None => Err(CastError::UnknownCastPtrKind), @@ -986,25 +985,36 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } - fn fuzzy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_from: CastTy<'tcx>) { + fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { fcx.tcx.struct_span_lint_hir( - lint::builtin::FUZZY_PROVENANCE_CASTS, + lint::builtin::LOSSY_PROVENANCE_CASTS, self.expr.hir_id, self.span, |err| { let mut err = err.build(&format!( - "strict provenance disallows casting pointer `{}` to integer `{}`", + "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`", self.expr_ty, self.cast_ty )); - if let CastTy::FnPtr = t_from { - err.help( - "use `(... as *const u8).addr()` to obtain \ - the address of a function pointer", + let msg = "use `.addr()` to obtain the address of a pointer"; + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + let scalar_cast = match t_c { + ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(), + _ => format!(" as {}", self.cast_ty), + }; + err.span_suggestion( + self.span, + msg, + format!("({}).addr(){}", snippet, scalar_cast), + Applicability::MaybeIncorrect ); } else { - err.help("use `.addr()` to obtain the address of a pointer"); + err.help(msg); } + err.help( + "if you can't comply with strict provenance and need to expose the pointer\ + provenance you can use `.expose_addr()` instead" + ); err.emit(); }, @@ -1017,15 +1027,28 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.expr.hir_id, self.span, |err| { - err.build(&format!( + + let mut err = err.build(&format!( "strict provenance disallows casting integer `{}` to pointer `{}`", self.expr_ty, self.cast_ty - )) - .help( - "use `.with_addr(...)` to adjust a valid pointer \ - in the same allocation, to this address", - ) - .emit(); + )); + let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address"; + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + err.span_suggestion( + self.span, + msg, + format!("(...).with_addr({})", snippet), + Applicability::HasPlaceholders, + ); + } else { + err.help(msg); + } + err.help( + "if you can't comply with strict provenance and don't have a pointer with \ + the correct provenance you can use `std::ptr::from_exposed_addr()` instead" + ); + + err.emit(); }, ); } diff --git a/src/doc/unstable-book/src/language-features/strict-provenance.md b/src/doc/unstable-book/src/language-features/strict-provenance.md new file mode 100644 index 0000000000000..dc60f3f375d3c --- /dev/null +++ b/src/doc/unstable-book/src/language-features/strict-provenance.md @@ -0,0 +1,22 @@ +# `strict_provenance` + +The tracking issue for this feature is: [#95228] + +[#95228]: https://github.com/rust-lang/rust/issues/95228 +----- + +The `strict_provenance` feature allows to enable the `fuzzy_provenance_casts` and `lossy_provenance_casts` lints. +These lint on casts between integers and pointers, that are recommended against or invalid in the strict provenance model. +The same feature gate is also used for the experimental strict provenance API in `std` (actually `core`). + +## Example + +```rust +#![feature(strict_provenance)] +#![warn(fuzzy_provenance_casts)] + +fn main() { + let _dangling = 16_usize as *const u8; + //~^ WARNING: strict provenance disallows casting integer `usize` to pointer `*const u8` +} +``` diff --git a/src/test/ui/feature-gates/feature-gate-strict_provenance.rs b/src/test/ui/feature-gates/feature-gate-strict_provenance.rs new file mode 100644 index 0000000000000..75d0ee5700d07 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-strict_provenance.rs @@ -0,0 +1,19 @@ +// check-pass + +#![deny(fuzzy_provenance_casts)] +//~^ WARNING unknown lint: `fuzzy_provenance_casts` +//~| WARNING unknown lint: `fuzzy_provenance_casts` +//~| WARNING unknown lint: `fuzzy_provenance_casts` +#![deny(lossy_provenance_casts)] +//~^ WARNING unknown lint: `lossy_provenance_casts` +//~| WARNING unknown lint: `lossy_provenance_casts` +//~| WARNING unknown lint: `lossy_provenance_casts` + +fn main() { + // no warnings emitted since the lints are not activated + + let _dangling = 16_usize as *const u8; + + let x: u8 = 37; + let _addr: usize = &x as *const u8 as usize; +} diff --git a/src/test/ui/feature-gates/feature-gate-strict_provenance.stderr b/src/test/ui/feature-gates/feature-gate-strict_provenance.stderr new file mode 100644 index 0000000000000..34bd240c304a7 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-strict_provenance.stderr @@ -0,0 +1,63 @@ +warning: unknown lint: `fuzzy_provenance_casts` + --> $DIR/feature-gate-strict_provenance.rs:3:1 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unknown_lints)]` on by default + = note: the `fuzzy_provenance_casts` lint is unstable + = note: see issue #95228 for more information + = help: add `#![feature(strict_provenance)]` to the crate attributes to enable + +warning: unknown lint: `lossy_provenance_casts` + --> $DIR/feature-gate-strict_provenance.rs:7:1 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `lossy_provenance_casts` lint is unstable + = note: see issue #95228 for more information + = help: add `#![feature(strict_provenance)]` to the crate attributes to enable + +warning: unknown lint: `fuzzy_provenance_casts` + --> $DIR/feature-gate-strict_provenance.rs:3:1 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `fuzzy_provenance_casts` lint is unstable + = note: see issue #95228 for more information + = help: add `#![feature(strict_provenance)]` to the crate attributes to enable + +warning: unknown lint: `lossy_provenance_casts` + --> $DIR/feature-gate-strict_provenance.rs:7:1 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `lossy_provenance_casts` lint is unstable + = note: see issue #95228 for more information + = help: add `#![feature(strict_provenance)]` to the crate attributes to enable + +warning: unknown lint: `fuzzy_provenance_casts` + --> $DIR/feature-gate-strict_provenance.rs:3:1 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `fuzzy_provenance_casts` lint is unstable + = note: see issue #95228 for more information + = help: add `#![feature(strict_provenance)]` to the crate attributes to enable + +warning: unknown lint: `lossy_provenance_casts` + --> $DIR/feature-gate-strict_provenance.rs:7:1 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the `lossy_provenance_casts` lint is unstable + = note: see issue #95228 for more information + = help: add `#![feature(strict_provenance)]` to the crate attributes to enable + +warning: 6 warnings emitted + diff --git a/src/test/ui/lint/lint-strict-provenance-fuzzy-casts.rs b/src/test/ui/lint/lint-strict-provenance-fuzzy-casts.rs new file mode 100644 index 0000000000000..d2d72a68f1396 --- /dev/null +++ b/src/test/ui/lint/lint-strict-provenance-fuzzy-casts.rs @@ -0,0 +1,7 @@ +#![feature(strict_provenance)] +#![deny(fuzzy_provenance_casts)] + +fn main() { + let dangling = 16_usize as *const u8; + //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` +} diff --git a/src/test/ui/lint/lint-strict-provenance-fuzzy-casts.stderr b/src/test/ui/lint/lint-strict-provenance-fuzzy-casts.stderr new file mode 100644 index 0000000000000..e50d243b6ad6d --- /dev/null +++ b/src/test/ui/lint/lint-strict-provenance-fuzzy-casts.stderr @@ -0,0 +1,19 @@ +error: strict provenance disallows casting integer `usize` to pointer `*const u8` + --> $DIR/lint-strict-provenance-fuzzy-casts.rs:5:20 + | +LL | let dangling = 16_usize as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-strict-provenance-fuzzy-casts.rs:2:9 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::from_exposed_addr()` instead +help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address + | +LL | let dangling = (...).with_addr(16_usize); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/src/test/ui/lint/lint-strict-provenance-lossy-casts.rs b/src/test/ui/lint/lint-strict-provenance-lossy-casts.rs new file mode 100644 index 0000000000000..3690fbc904d99 --- /dev/null +++ b/src/test/ui/lint/lint-strict-provenance-lossy-casts.rs @@ -0,0 +1,11 @@ +#![feature(strict_provenance)] +#![deny(lossy_provenance_casts)] + +fn main() { + let x: u8 = 37; + let addr: usize = &x as *const u8 as usize; + //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + + let addr_32bit = &x as *const u8 as u32; + //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` +} diff --git a/src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr b/src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr new file mode 100644 index 0000000000000..489cb03ddd316 --- /dev/null +++ b/src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr @@ -0,0 +1,23 @@ +error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + --> $DIR/lint-strict-provenance-lossy-casts.rs:6:23 + | +LL | let addr: usize = &x as *const u8 as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer: `(&x as *const u8).addr()` + | +note: the lint level is defined here + --> $DIR/lint-strict-provenance-lossy-casts.rs:2:9 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: if you can't comply with strict provenance and need to expose the pointerprovenance you can use `.expose_addr()` instead + +error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` + --> $DIR/lint-strict-provenance-lossy-casts.rs:9:22 + | +LL | let addr_32bit = &x as *const u8 as u32; + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer: `(&x as *const u8).addr() as u32` + | + = help: if you can't comply with strict provenance and need to expose the pointerprovenance you can use `.expose_addr()` instead + +error: aborting due to 2 previous errors + From f6c7f109f8efa12d891b6c37baca5a3dec4e1b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20BRANSTETT?= Date: Fri, 8 Apr 2022 17:33:40 +0200 Subject: [PATCH 8/8] Remove extra space before a where clause in the documentation --- src/librustdoc/html/format.rs | 3 ++- src/test/rustdoc/where.SWhere_Simd_item-decl.html | 1 + .../rustdoc/where.SWhere_TraitWhere_item-decl.html | 3 +++ src/test/rustdoc/where.rs | 13 +++++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/test/rustdoc/where.SWhere_Simd_item-decl.html create mode 100644 src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 5c59609d5b8c6..228976cb171b5 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -371,7 +371,8 @@ crate fn print_where_clause<'a, 'tcx: 'a>( clause = clause.replace("
", &format!("
{}", padding)); clause.insert_str(0, &" ".repeat(indent.saturating_sub(1))); if !end_newline { - clause.insert_str(0, "
"); + // we insert the
after a single space but before multiple spaces at the start + clause.insert_str(if indent == 0 { 1 } else { 0 }, "
"); } } write!(f, "{}", clause) diff --git a/src/test/rustdoc/where.SWhere_Simd_item-decl.html b/src/test/rustdoc/where.SWhere_Simd_item-decl.html new file mode 100644 index 0000000000000..d941ad7de4c48 --- /dev/null +++ b/src/test/rustdoc/where.SWhere_Simd_item-decl.html @@ -0,0 +1 @@ +
pub struct Simd<T, const LANES: usize>(_) 
where
    T: Sized
;
\ No newline at end of file diff --git a/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html new file mode 100644 index 0000000000000..54026ff034e00 --- /dev/null +++ b/src/test/rustdoc/where.SWhere_TraitWhere_item-decl.html @@ -0,0 +1,3 @@ +
pub trait TraitWhere {
+    type Item<'a>
    where
        Self: 'a
; +}
\ No newline at end of file diff --git a/src/test/rustdoc/where.rs b/src/test/rustdoc/where.rs index 549cfff96cb6d..73c75fdb61bcd 100644 --- a/src/test/rustdoc/where.rs +++ b/src/test/rustdoc/where.rs @@ -1,3 +1,4 @@ +#![feature(generic_associated_types)] #![crate_name = "foo"] pub trait MyTrait { fn dummy(&self) { } } @@ -19,6 +20,18 @@ impl Delta where D: MyTrait { pub struct Echo(E); +// @has 'foo/struct.Simd.html' +// @snapshot SWhere_Simd_item-decl - '//div[@class="docblock item-decl"]' +pub struct Simd([T; LANES]) +where + T: Sized; + +// @has 'foo/trait.TraitWhere.html' +// @snapshot SWhere_TraitWhere_item-decl - '//div[@class="docblock item-decl"]' +pub trait TraitWhere { + type Item<'a> where Self: 'a; +} + // @has foo/struct.Echo.html '//*[@class="impl has-srclink"]//h3[@class="code-header in-band"]' \ // "impl MyTrait for Echo where E: MyTrait" // @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header in-band"]' \