From e132077db74dd6bd26566dbe3610848e3a771256 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Mar 2022 14:10:19 -0400 Subject: [PATCH 01/10] 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 02/10] 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 5974c18efba3bb2ed01082a6cc48c7cb954f8104 Mon Sep 17 00:00:00 2001 From: Caio Date: Thu, 7 Apr 2022 09:32:02 -0300 Subject: [PATCH 03/10] [macro_metavar_expr] Add tests to ensure the feature requirement --- .../rfc-3086-metavar-expr/required-feature.rs | 35 ++++++++ .../required-feature.stderr | 83 ++++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.rs b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.rs index cff6f29a15386..b4fef11f1e29a 100644 --- a/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.rs +++ b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.rs @@ -5,5 +5,40 @@ macro_rules! count { }; } +macro_rules! dollar_dollar { + () => { + macro_rules! bar { + ( $$( $$any:tt )* ) => { $$( $$any )* }; + //~^ ERROR meta-variable expressions are unstable + //~| ERROR meta-variable expressions are unstable + //~| ERROR meta-variable expressions are unstable + //~| ERROR meta-variable expressions are unstable + } + }; +} + +macro_rules! index { + ( $( $e:stmt ),* ) => { + $( ${ignore(e)} ${index()} )* + //~^ ERROR meta-variable expressions are unstable + //~| ERROR meta-variable expressions are unstable + }; +} + +macro_rules! ignore { + ( $( $i:stmt ),* ) => {{ + 0 $( + 1 ${ignore(i)} )* + //~^ ERROR meta-variable expressions are unstable + }}; +} + +macro_rules! length { + ( $( $e:stmt ),* ) => { + $( ${ignore(e)} ${length()} )* + //~^ ERROR meta-variable expressions are unstable + //~| ERROR meta-variable expressions are unstable + }; +} + fn main() { } diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.stderr b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.stderr index f573194479314..ecf598b104d05 100644 --- a/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.stderr +++ b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.stderr @@ -7,6 +7,87 @@ LL | ${ count(e) } = note: see issue #83527 for more information = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable -error: aborting due to previous error +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:11:16 + | +LL | ( $$( $$any:tt )* ) => { $$( $$any )* }; + | ^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:11:20 + | +LL | ( $$( $$any:tt )* ) => { $$( $$any )* }; + | ^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:11:39 + | +LL | ( $$( $$any:tt )* ) => { $$( $$any )* }; + | ^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:11:43 + | +LL | ( $$( $$any:tt )* ) => { $$( $$any )* }; + | ^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:22:13 + | +LL | $( ${ignore(e)} ${index()} )* + | ^^^^^^^^^^^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:22:26 + | +LL | $( ${ignore(e)} ${index()} )* + | ^^^^^^^^^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:30:19 + | +LL | 0 $( + 1 ${ignore(i)} )* + | ^^^^^^^^^^^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:37:13 + | +LL | $( ${ignore(e)} ${length()} )* + | ^^^^^^^^^^^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:37:26 + | +LL | $( ${ignore(e)} ${length()} )* + | ^^^^^^^^^^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0658`. From aa3c141c86652ce72cca6d32c69afbdc23ee51fa Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Thu, 7 Apr 2022 13:44:57 -0700 Subject: [PATCH 04/10] 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 05/10] 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 06/10] 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 07/10] 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 08/10] 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 a87a0d089e793903844ef40851deae48c039f8bb Mon Sep 17 00:00:00 2001 From: Jane Lusby Date: Tue, 19 Oct 2021 15:23:19 -0700 Subject: [PATCH 09/10] Add ThinBox type for 1 stack pointer sized heap allocated trait objects Relevant commit messages from squashed history in order: Add initial version of ThinBox update test to actually capture failure swap to middle ptr impl based on matthieu-m's design Fix stack overflow in debug impl The previous version would take a `&ThinBox` and deref it once, which resulted in a no-op and the same type, which it would then print causing an endless recursion. I've switched to calling `deref` by name to let method resolution handle deref the correct number of times. I've also updated the Drop impl for good measure since it seemed like it could be falling prey to the same bug, and I'll be adding some tests to verify that the drop is happening correctly. add test to verify drop is behaving add doc examples and remove unnecessary Pointee bounds ThinBox: use NonNull ThinBox: tests for size Apply suggestions from code review Co-authored-by: Alphyr <47725341+a1phyr@users.noreply.github.com> use handle_alloc_error and fix drop signature update niche and size tests add cfg for allocating APIs check null before calculating offset add test for zst and trial usage prevent optimizer induced ub in drop and cleanup metadata gathering account for arbitrary size and alignment metadata Thank you nika and thomcc! Update library/alloc/src/boxed/thin.rs Co-authored-by: Josh Triplett Update library/alloc/src/boxed/thin.rs Co-authored-by: Josh Triplett --- library/alloc/src/boxed.rs | 5 + library/alloc/src/boxed/thin.rs | 215 ++++++++++++++++++ library/alloc/src/lib.rs | 2 + library/alloc/tests/lib.rs | 2 + library/alloc/tests/thin_box.rs | 26 +++ library/std/src/error.rs | 8 + library/std/src/lib.rs | 1 + src/test/ui/box/thin_align.rs | 26 +++ src/test/ui/box/thin_drop.rs | 37 +++ src/test/ui/box/thin_new.rs | 30 +++ src/test/ui/box/thin_zst.rs | 34 +++ ...ce-issue-49593-box-never.nofallback.stderr | 4 +- 12 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 library/alloc/src/boxed/thin.rs create mode 100644 library/alloc/tests/thin_box.rs create mode 100644 src/test/ui/box/thin_align.rs create mode 100644 src/test/ui/box/thin_drop.rs create mode 100644 src/test/ui/box/thin_new.rs create mode 100644 src/test/ui/box/thin_zst.rs diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index a56d4de03cd0e..e6faf1df3a810 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -163,6 +163,11 @@ use crate::str::from_boxed_utf8_unchecked; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; +#[unstable(feature = "thin_box", issue = "92791")] +pub use thin::ThinBox; + +mod thin; + /// A pointer type for heap allocation. /// /// See the [module-level documentation](../../std/boxed/index.html) for more. diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs new file mode 100644 index 0000000000000..390030fa2b21c --- /dev/null +++ b/library/alloc/src/boxed/thin.rs @@ -0,0 +1,215 @@ +// Based on +// https://github.com/matthieu-m/rfc2580/blob/b58d1d3cba0d4b5e859d3617ea2d0943aaa31329/examples/thin.rs +// by matthieu-m +use crate::alloc::{self, Layout, LayoutError}; +use core::fmt::{self, Debug, Display, Formatter}; +use core::marker::{PhantomData, Unsize}; +use core::mem; +use core::ops::{Deref, DerefMut}; +use core::ptr::Pointee; +use core::ptr::{self, NonNull}; + +/// ThinBox. +/// +/// A thin pointer for heap allocation, regardless of T. +/// +/// # Examples +/// +/// ``` +/// #![feature(thin_box)] +/// use std::boxed::ThinBox; +/// +/// let five = ThinBox::new(5); +/// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]); +/// +/// use std::mem::{size_of, size_of_val}; +/// let size_of_ptr = size_of::<*const ()>(); +/// assert_eq!(size_of_ptr, size_of_val(&five)); +/// assert_eq!(size_of_ptr, size_of_val(&thin_slice)); +/// ``` +#[unstable(feature = "thin_box", issue = "92791")] +pub struct ThinBox { + ptr: WithHeader<::Metadata>, + _marker: PhantomData, +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl ThinBox { + /// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on + /// the stack. + /// + /// # Examples + /// + /// ``` + /// #![feature(thin_box)] + /// use std::boxed::ThinBox; + /// + /// let five = ThinBox::new(5); + /// ``` + #[cfg(not(no_global_oom_handling))] + pub fn new(value: T) -> Self { + let meta = ptr::metadata(&value); + let ptr = WithHeader::new(meta, value); + ThinBox { ptr, _marker: PhantomData } + } +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl ThinBox { + /// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on + /// the stack. + /// + /// # Examples + /// + /// ``` + /// #![feature(thin_box)] + /// use std::boxed::ThinBox; + /// + /// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]); + /// ``` + #[cfg(not(no_global_oom_handling))] + pub fn new_unsize(value: T) -> Self + where + T: Unsize, + { + let meta = ptr::metadata(&value as &Dyn); + let ptr = WithHeader::new(meta, value); + ThinBox { ptr, _marker: PhantomData } + } +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl Debug for ThinBox { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Debug::fmt(self.deref(), f) + } +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl Display for ThinBox { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(self.deref(), f) + } +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl Deref for ThinBox { + type Target = T; + + fn deref(&self) -> &T { + let value = self.data(); + let metadata = self.meta(); + let pointer = ptr::from_raw_parts(value as *const (), metadata); + unsafe { &*pointer } + } +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl DerefMut for ThinBox { + fn deref_mut(&mut self) -> &mut T { + let value = self.data(); + let metadata = self.meta(); + let pointer = ptr::from_raw_parts_mut::(value as *mut (), metadata); + unsafe { &mut *pointer } + } +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl Drop for ThinBox { + fn drop(&mut self) { + unsafe { + let value = self.deref_mut(); + let value = value as *mut T; + self.ptr.drop::(value); + } + } +} + +#[unstable(feature = "thin_box", issue = "92791")] +impl ThinBox { + fn meta(&self) -> ::Metadata { + // Safety: + // - NonNull and valid. + unsafe { *self.ptr.header() } + } + + fn data(&self) -> *mut u8 { + self.ptr.value() + } +} + +/// A pointer to type-erased data, guaranteed to have a header `H` before the pointed-to location. +struct WithHeader(NonNull, PhantomData); + +impl WithHeader { + #[cfg(not(no_global_oom_handling))] + fn new(header: H, value: T) -> WithHeader { + let value_layout = Layout::new::(); + let Ok((layout, value_offset)) = Self::alloc_layout(value_layout) else { + // We pass an empty layout here because we do not know which layout caused the + // arithmetic overflow in `Layout::extend` and `handle_alloc_error` takes `Layout` as + // its argument rather than `Result`, also this function has been + // stable since 1.28 ._. + // + // On the other hand, look at this gorgeous turbofish! + alloc::handle_alloc_error(Layout::new::<()>()); + }; + + unsafe { + let ptr = alloc::alloc(layout); + + if ptr.is_null() { + alloc::handle_alloc_error(layout); + } + // Safety: + // - The size is at least `aligned_header_size`. + let ptr = ptr.add(value_offset) as *mut _; + + let ptr = NonNull::new_unchecked(ptr); + + let result = WithHeader(ptr, PhantomData); + ptr::write(result.header(), header); + ptr::write(result.value().cast(), value); + + result + } + } + + // Safety: + // - Assumes that `value` can be dereferenced. + unsafe fn drop(&self, value: *mut T) { + unsafe { + // SAFETY: Layout must have been computable if we're in drop + let (layout, value_offset) = + Self::alloc_layout(Layout::for_value_raw(value)).unwrap_unchecked(); + + ptr::drop_in_place::(value); + // We only drop the value because the Pointee trait requires that the metadata is copy + // aka trivially droppable + alloc::dealloc(self.0.as_ptr().sub(value_offset), layout); + } + } + + fn header(&self) -> *mut H { + // Safety: + // - At least `size_of::()` bytes are allocated ahead of the pointer. + // - We know that H will be aligned because the middle pointer is aligned to the greater + // of the alignment of the header and the data and the header size includes the padding + // needed to align the header. Subtracting the header size from the aligned data pointer + // will always result in an aligned header pointer, it just may not point to the + // beginning of the allocation. + unsafe { self.0.as_ptr().sub(Self::header_size()) as *mut H } + } + + fn value(&self) -> *mut u8 { + self.0.as_ptr() + } + + const fn header_size() -> usize { + mem::size_of::() + } + + fn alloc_layout(value_layout: Layout) -> Result<(Layout, usize), LayoutError> { + Layout::new::().extend(value_layout) + } +} diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 2ddb5f231b1d6..c54001dceea49 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -120,6 +120,7 @@ #![feature(nonnull_slice_from_raw_parts)] #![feature(pattern)] #![feature(ptr_internals)] +#![feature(ptr_metadata)] #![feature(receiver_trait)] #![feature(set_ptr_value)] #![feature(slice_group_by)] @@ -152,6 +153,7 @@ #![feature(fundamental)] #![cfg_attr(not(test), feature(generator_trait))] #![feature(lang_items)] +#![feature(let_else)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 16ceb8e373dcb..16d3b36859570 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -39,6 +39,7 @@ #![feature(nonnull_slice_from_raw_parts)] #![feature(panic_update_hook)] #![feature(slice_flatten)] +#![feature(thin_box)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -57,6 +58,7 @@ mod rc; mod slice; mod str; mod string; +mod thin_box; mod vec; mod vec_deque; diff --git a/library/alloc/tests/thin_box.rs b/library/alloc/tests/thin_box.rs new file mode 100644 index 0000000000000..0fe6aaa4d0048 --- /dev/null +++ b/library/alloc/tests/thin_box.rs @@ -0,0 +1,26 @@ +use alloc::boxed::ThinBox; +use core::mem::size_of; + +#[test] +fn want_niche_optimization() { + fn uses_niche() -> bool { + size_of::<*const ()>() == size_of::>>() + } + + trait Tr {} + assert!(uses_niche::()); + assert!(uses_niche::<[i32]>()); + assert!(uses_niche::()); +} + +#[test] +fn want_thin() { + fn is_thin() -> bool { + size_of::<*const ()>() == size_of::>() + } + + trait Tr {} + assert!(is_thin::()); + assert!(is_thin::<[i32]>()); + assert!(is_thin::()); +} diff --git a/library/std/src/error.rs b/library/std/src/error.rs index c3cb71a5dee63..4fb94908c80fd 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -516,6 +516,14 @@ impl Error for Box { } } +#[unstable(feature = "thin_box", issue = "92791")] +impl crate::error::Error for crate::boxed::ThinBox { + fn source(&self) -> Option<&(dyn crate::error::Error + 'static)> { + use core::ops::Deref; + self.deref().source() + } +} + #[stable(feature = "error_by_ref", since = "1.51.0")] impl<'a, T: Error + ?Sized> Error for &'a T { #[allow(deprecated, deprecated_in_future)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5ade65ad9c629..60e7c2af8e4a5 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -290,6 +290,7 @@ #![feature(get_mut_unchecked)] #![feature(map_try_insert)] #![feature(new_uninit)] +#![feature(thin_box)] #![feature(toowned_clone_into)] #![feature(try_reserve_kind)] #![feature(vec_into_raw_parts)] diff --git a/src/test/ui/box/thin_align.rs b/src/test/ui/box/thin_align.rs new file mode 100644 index 0000000000000..3c61d0090e42b --- /dev/null +++ b/src/test/ui/box/thin_align.rs @@ -0,0 +1,26 @@ +#![feature(thin_box)] +// run-pass +use std::boxed::ThinBox; +use std::error::Error; +use std::ops::Deref; +use std::fmt; + +fn main() { + let expected = "Foo error!"; + let a: ThinBox = ThinBox::new_unsize(Foo(expected)); + let a = a.deref(); + let msg = a.to_string(); + assert_eq!(expected, msg); +} + +#[derive(Debug)] +#[repr(align(1024))] +struct Foo(&'static str); + +impl fmt::Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Error for Foo {} diff --git a/src/test/ui/box/thin_drop.rs b/src/test/ui/box/thin_drop.rs new file mode 100644 index 0000000000000..965613c114e3a --- /dev/null +++ b/src/test/ui/box/thin_drop.rs @@ -0,0 +1,37 @@ +#![feature(thin_box)] +// run-pass +use std::boxed::ThinBox; +use std::error::Error; +use std::ops::Deref; +use std::fmt; + +fn main() { + let expected = "Foo error!"; + let mut dropped = false; + { + let foo = Foo(expected, &mut dropped); + let a: ThinBox = ThinBox::new_unsize(foo); + let a = a.deref(); + let msg = a.to_string(); + assert_eq!(expected, msg); + } + assert!(dropped); +} + +#[derive(Debug)] +#[repr(align(1024))] +struct Foo<'a>(&'static str, &'a mut bool); + +impl Drop for Foo<'_> { + fn drop(&mut self) { + *self.1 = true; + } +} + +impl fmt::Display for Foo<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Error for Foo<'_> {} diff --git a/src/test/ui/box/thin_new.rs b/src/test/ui/box/thin_new.rs new file mode 100644 index 0000000000000..53f46478be403 --- /dev/null +++ b/src/test/ui/box/thin_new.rs @@ -0,0 +1,30 @@ +#![feature(thin_box)] +// run-pass +use std::boxed::ThinBox; +use std::error::Error; +use std::{fmt, mem}; + +fn main() { + let thin_error: ThinBox = ThinBox::new_unsize(Foo); + assert_eq!(mem::size_of::<*const i32>(), mem::size_of_val(&thin_error)); + println!("{:?}", thin_error); + + let thin = ThinBox::new(42i32); + assert_eq!(mem::size_of::<*const i32>(), mem::size_of_val(&thin)); + println!("{:?}", thin); + + let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]); + assert_eq!(mem::size_of::<*const i32>(), mem::size_of_val(&thin_slice)); + println!("{:?}", thin_slice); +} + +#[derive(Debug)] +struct Foo; + +impl fmt::Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "boooo!") + } +} + +impl Error for Foo {} diff --git a/src/test/ui/box/thin_zst.rs b/src/test/ui/box/thin_zst.rs new file mode 100644 index 0000000000000..77c400d17bbe5 --- /dev/null +++ b/src/test/ui/box/thin_zst.rs @@ -0,0 +1,34 @@ +#![feature(thin_box)] +// run-pass +use std::boxed::ThinBox; +use std::error::Error; +use std::{fmt, mem}; +use std::ops::DerefMut; + +const EXPECTED: &str = "boooo!"; + +fn main() { + let thin_error: ThinBox = ThinBox::new_unsize(Foo); + assert_eq!(mem::size_of::<*const i32>(), mem::size_of_val(&thin_error)); + let msg = thin_error.to_string(); + assert_eq!(EXPECTED, msg); + + let mut thin_concrete_error: ThinBox = ThinBox::new(Foo); + assert_eq!(mem::size_of::<*const i32>(), mem::size_of_val(&thin_concrete_error)); + let msg = thin_concrete_error.to_string(); + assert_eq!(EXPECTED, msg); + let inner = thin_concrete_error.deref_mut(); + let msg = inner.to_string(); + assert_eq!(EXPECTED, msg); +} + +#[derive(Debug)] +struct Foo; + +impl fmt::Display for Foo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", EXPECTED) + } +} + +impl Error for Foo {} diff --git a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr index a4b10a4c339f9..1b1ce67cb0c1f 100644 --- a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr +++ b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr @@ -13,7 +13,7 @@ LL | /* *mut $0 is coerced to Box here */ Box::<_ /* ! */>::new(x BorrowError BorrowMutError Box - and 42 others + and 43 others = note: required for the cast to the object type `dyn std::error::Error` error[E0277]: the trait bound `(): std::error::Error` is not satisfied @@ -31,7 +31,7 @@ LL | /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x) BorrowError BorrowMutError Box - and 42 others + and 43 others = note: required for the cast to the object type `(dyn std::error::Error + 'static)` error: aborting due to 2 previous errors From 7450c4e3e83674de975c3f029a6bec6dab334ac2 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 8 Apr 2022 17:38:28 +1000 Subject: [PATCH 10/10] Remove explicit delimiter token trees from `Delimited`. They were introduced by the final commit in #95159 and gave a performance win. But since the introduction of `MatcherLoc` they are no longer needed. This commit reverts that change, making the code a bit simpler. --- compiler/rustc_expand/src/mbe.rs | 44 +++++-------------- compiler/rustc_expand/src/mbe/macro_check.rs | 16 +++---- compiler/rustc_expand/src/mbe/macro_parser.rs | 8 ++-- compiler/rustc_expand/src/mbe/macro_rules.rs | 44 +++++++++---------- compiler/rustc_expand/src/mbe/quoted.rs | 26 +++++------ compiler/rustc_expand/src/mbe/transcribe.rs | 16 +++---- 6 files changed, 58 insertions(+), 96 deletions(-) diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index c89015b4c6bb3..c1f1b4e505c36 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -17,48 +17,24 @@ use rustc_data_structures::sync::Lrc; use rustc_span::symbol::Ident; use rustc_span::Span; -/// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`. The delimiter itself -/// might be `NoDelim`. +/// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`. The delimiters +/// might be `NoDelim`, but they are not represented explicitly. #[derive(Clone, PartialEq, Encodable, Decodable, Debug)] struct Delimited { delim: token::DelimToken, - /// Note: This contains the opening and closing delimiters tokens (e.g. `(` and `)`). Note that - /// these could be `NoDelim`. These token kinds must match `delim`, and the methods below - /// debug_assert this. - all_tts: Vec, + /// FIXME: #67062 has details about why this is sub-optimal. + tts: Vec, } impl Delimited { - /// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter. Panics if - /// the delimiter is `NoDelim`. - fn open_tt(&self) -> &TokenTree { - let tt = self.all_tts.first().unwrap(); - debug_assert!(matches!( - tt, - &TokenTree::Token(token::Token { kind: token::OpenDelim(d), .. }) if d == self.delim - )); - tt + /// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter. + fn open_tt(&self, span: DelimSpan) -> TokenTree { + TokenTree::token(token::OpenDelim(self.delim), span.open) } - /// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter. Panics if - /// the delimiter is `NoDelim`. - fn close_tt(&self) -> &TokenTree { - let tt = self.all_tts.last().unwrap(); - debug_assert!(matches!( - tt, - &TokenTree::Token(token::Token { kind: token::CloseDelim(d), .. }) if d == self.delim - )); - tt - } - - /// Returns the tts excluding the outer delimiters. - /// - /// FIXME: #67062 has details about why this is sub-optimal. - fn inner_tts(&self) -> &[TokenTree] { - // These functions are called for the assertions within them. - let _open_tt = self.open_tt(); - let _close_tt = self.close_tt(); - &self.all_tts[1..self.all_tts.len() - 1] + /// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter. + fn close_tt(&self, span: DelimSpan) -> TokenTree { + TokenTree::token(token::CloseDelim(self.delim), span.close) } } diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 0e20e0970f401..fbacebf99c050 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -282,7 +282,7 @@ fn check_binders( // `MetaVarExpr` can not appear in the LHS of a macro arm TokenTree::MetaVarExpr(..) => {} TokenTree::Delimited(_, ref del) => { - for tt in del.inner_tts() { + for tt in &del.tts { check_binders(sess, node_id, tt, macros, binders, ops, valid); } } @@ -345,7 +345,7 @@ fn check_occurrences( check_ops_is_prefix(sess, node_id, macros, binders, ops, dl.entire(), name); } TokenTree::Delimited(_, ref del) => { - check_nested_occurrences(sess, node_id, del.inner_tts(), macros, binders, ops, valid); + check_nested_occurrences(sess, node_id, &del.tts, macros, binders, ops, valid); } TokenTree::Sequence(_, ref seq) => { let ops = ops.push(seq.kleene); @@ -432,20 +432,14 @@ fn check_nested_occurrences( { let macro_rules = state == NestedMacroState::MacroRulesNotName; state = NestedMacroState::Empty; - let rest = check_nested_macro( - sess, - node_id, - macro_rules, - del.inner_tts(), - &nested_macros, - valid, - ); + let rest = + check_nested_macro(sess, node_id, macro_rules, &del.tts, &nested_macros, valid); // If we did not check the whole macro definition, then check the rest as if outside // the macro definition. check_nested_occurrences( sess, node_id, - &del.inner_tts()[rest..], + &del.tts[rest..], macros, binders, ops, diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index ffe8b10e6877a..29020691da38c 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -151,9 +151,11 @@ pub(super) fn compute_locs(sess: &ParseSess, matcher: &[TokenTree]) -> Vec { locs.push(MatcherLoc::Token { token: token.clone() }); } - TokenTree::Delimited(_, delimited) => { + TokenTree::Delimited(span, delimited) => { locs.push(MatcherLoc::Delimited); - inner(sess, &delimited.all_tts, locs, next_metavar, seq_depth); + inner(sess, &[delimited.open_tt(*span)], locs, next_metavar, seq_depth); + inner(sess, &delimited.tts, locs, next_metavar, seq_depth); + inner(sess, &[delimited.close_tt(*span)], locs, next_metavar, seq_depth); } TokenTree::Sequence(_, seq) => { // We can't determine `idx_first_after` and construct the final @@ -293,7 +295,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { .map(|tt| match tt { TokenTree::MetaVarDecl(..) => 1, TokenTree::Sequence(_, seq) => seq.num_captures, - TokenTree::Delimited(_, delim) => count_metavar_decls(delim.inner_tts()), + TokenTree::Delimited(_, delim) => count_metavar_decls(&delim.tts), TokenTree::Token(..) => 0, TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => unreachable!(), }) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 5dc086ee9e603..31dae6a2fb437 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -263,9 +263,7 @@ fn generic_extension<'cx, 'tt>( // Ignore the delimiters on the RHS. let rhs = match &rhses[i] { - mbe::TokenTree::Delimited(_, delimited) => { - delimited.inner_tts().to_vec().clone() - } + mbe::TokenTree::Delimited(_, delimited) => delimited.tts.to_vec(), _ => cx.span_bug(sp, "malformed macro rhs"), }; let arm_span = rhses[i].span(); @@ -470,17 +468,16 @@ pub fn compile_declarative_macro( .iter() .map(|m| { if let MatchedTokenTree(ref tt) = *m { - let mut tts = vec![]; - mbe::quoted::parse( + let tt = mbe::quoted::parse( tt.clone().into(), true, &sess.parse_sess, def.id, features, edition, - &mut tts, - ); - let tt = tts.pop().unwrap(); + ) + .pop() + .unwrap(); valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def, &tt); return tt; } @@ -495,17 +492,16 @@ pub fn compile_declarative_macro( .iter() .map(|m| { if let MatchedTokenTree(ref tt) = *m { - let mut tts = vec![]; - mbe::quoted::parse( + return mbe::quoted::parse( tt.clone().into(), false, &sess.parse_sess, def.id, features, edition, - &mut tts, - ); - return tts.pop().unwrap(); + ) + .pop() + .unwrap(); } sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs") }) @@ -544,7 +540,7 @@ pub fn compile_declarative_macro( // Ignore the delimiters around the matcher. match lhs { mbe::TokenTree::Delimited(_, delimited) => { - mbe::macro_parser::compute_locs(&sess.parse_sess, delimited.inner_tts()) + mbe::macro_parser::compute_locs(&sess.parse_sess, &delimited.tts) } _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "malformed macro lhs"), } @@ -576,7 +572,7 @@ fn check_lhs_nt_follows( // lhs is going to be like TokenTree::Delimited(...), where the // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens. if let mbe::TokenTree::Delimited(_, delimited) = lhs { - check_matcher(sess, features, def, delimited.inner_tts()) + check_matcher(sess, features, def, &delimited.tts) } else { let msg = "invalid macro matcher; matchers must be contained in balanced delimiters"; sess.span_diagnostic.span_err(lhs.span(), msg); @@ -597,7 +593,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { | TokenTree::MetaVarDecl(..) | TokenTree::MetaVarExpr(..) => (), TokenTree::Delimited(_, ref del) => { - if !check_lhs_no_empty_seq(sess, del.inner_tts()) { + if !check_lhs_no_empty_seq(sess, &del.tts) { return false; } } @@ -692,9 +688,9 @@ impl FirstSets { | TokenTree::MetaVarExpr(..) => { first.replace_with(tt.clone()); } - TokenTree::Delimited(_span, ref delimited) => { - build_recur(sets, delimited.inner_tts()); - first.replace_with(delimited.open_tt().clone()); + TokenTree::Delimited(span, ref delimited) => { + build_recur(sets, &delimited.tts); + first.replace_with(delimited.open_tt(span)); } TokenTree::Sequence(sp, ref seq_rep) => { let subfirst = build_recur(sets, &seq_rep.tts); @@ -758,8 +754,8 @@ impl FirstSets { first.add_one(tt.clone()); return first; } - TokenTree::Delimited(_span, ref delimited) => { - first.add_one(delimited.open_tt().clone()); + TokenTree::Delimited(span, ref delimited) => { + first.add_one(delimited.open_tt(span)); return first; } TokenTree::Sequence(sp, ref seq_rep) => { @@ -945,9 +941,9 @@ fn check_matcher_core( suffix_first = build_suffix_first(); } } - TokenTree::Delimited(_span, ref d) => { - let my_suffix = TokenSet::singleton(d.close_tt().clone()); - check_matcher_core(sess, features, def, first_sets, d.inner_tts(), &my_suffix); + TokenTree::Delimited(span, ref d) => { + let my_suffix = TokenSet::singleton(d.close_tt(span)); + check_matcher_core(sess, features, def, first_sets, &d.tts, &my_suffix); // don't track non NT tokens last.replace_with_irrelevant(); diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 48abbd7c18e14..a99a18aae119a 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -45,8 +45,10 @@ pub(super) fn parse( node_id: NodeId, features: &Features, edition: Edition, - result: &mut Vec, -) { +) -> Vec { + // Will contain the final collection of `self::TokenTree` + let mut result = Vec::new(); + // For each token tree in `input`, parse the token into a `self::TokenTree`, consuming // additional trees if need be. let mut trees = input.trees(); @@ -113,6 +115,7 @@ pub(super) fn parse( _ => result.push(tree), } } + result } /// Asks for the `macro_metavar_expr` feature if it is not already declared @@ -205,8 +208,7 @@ fn parse_tree( // If we didn't find a metavar expression above, then we must have a // repetition sequence in the macro (e.g. `$(pat)*`). Parse the // contents of the sequence itself - let mut sequence = vec![]; - parse(tts, parsing_patterns, sess, node_id, features, edition, &mut sequence); + let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition); // Get the Kleene operator and optional separator let (separator, kleene) = parse_sep_and_kleene_op(&mut trees, delim_span.entire(), sess); @@ -269,15 +271,13 @@ fn parse_tree( // `tree` is the beginning of a delimited set of tokens (e.g., `(` or `{`). We need to // descend into the delimited set and further parse it. - tokenstream::TokenTree::Delimited(span, delim, tts) => { - let mut all_tts = vec![]; - // Add the explicit open and close delimiters, which - // `tokenstream::TokenTree::Delimited` lacks. - all_tts.push(TokenTree::token(token::OpenDelim(delim), span.open)); - parse(tts, parsing_patterns, sess, node_id, features, edition, &mut all_tts); - all_tts.push(TokenTree::token(token::CloseDelim(delim), span.close)); - TokenTree::Delimited(span, Lrc::new(Delimited { delim, all_tts })) - } + tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited( + span, + Lrc::new(Delimited { + delim, + tts: parse(tts, parsing_patterns, sess, node_id, features, edition), + }), + ), } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 508108df001ec..b1ab2cc45781a 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -10,7 +10,7 @@ use rustc_errors::{pluralize, PResult}; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -34,14 +34,8 @@ enum Frame { impl Frame { /// Construct a new frame around the delimited set of tokens. - fn new(mut tts: Vec) -> Frame { - // Need to add empty delimiters. - let open_tt = mbe::TokenTree::token(token::OpenDelim(token::NoDelim), DUMMY_SP); - let close_tt = mbe::TokenTree::token(token::CloseDelim(token::NoDelim), DUMMY_SP); - tts.insert(0, open_tt); - tts.push(close_tt); - - let forest = Lrc::new(mbe::Delimited { delim: token::NoDelim, all_tts: tts }); + fn new(tts: Vec) -> Frame { + let forest = Lrc::new(mbe::Delimited { delim: token::NoDelim, tts }); Frame::Delimited { forest, idx: 0, span: DelimSpan::dummy() } } } @@ -52,7 +46,7 @@ impl Iterator for Frame { fn next(&mut self) -> Option { match *self { Frame::Delimited { ref forest, ref mut idx, .. } => { - let res = forest.inner_tts().get(*idx).cloned(); + let res = forest.tts.get(*idx).cloned(); *idx += 1; res } @@ -388,7 +382,7 @@ fn lockstep_iter_size( use mbe::TokenTree; match *tree { TokenTree::Delimited(_, ref delimited) => { - delimited.inner_tts().iter().fold(LockstepIterSize::Unconstrained, |size, tt| { + delimited.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| { size.with(lockstep_iter_size(tt, interpolations, repeats)) }) }