From 483f13ce913b7ceaeae9173d21cd3dc343f6dbc1 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 21 Apr 2024 10:41:04 -0400 Subject: [PATCH 01/31] Make sure that the method resolution matches in note_source_of_type_mismatch_constraint --- compiler/rustc_hir_typeck/src/demand.rs | 33 +++++++++++++++---- tests/crashes/118185.rs | 26 --------------- ...hod-lookup-returns-sig-with-fewer-args.rs} | 5 ++- ...-lookup-returns-sig-with-fewer-args.stderr | 17 ++++++++++ 4 files changed, 46 insertions(+), 35 deletions(-) delete mode 100644 tests/crashes/118185.rs rename tests/{crashes/118185-2.rs => ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs} (85%) create mode 100644 tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index d6d22a43fe0b8..ed81ed640361a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -377,8 +377,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(arg_ty) = self.node_ty_opt(args[idx].hir_id) else { return false; }; - let possible_rcvr_ty = expr_finder.uses.iter().find_map(|binding| { + let possible_rcvr_ty = expr_finder.uses.iter().rev().find_map(|binding| { let possible_rcvr_ty = self.node_ty_opt(binding.hir_id)?; + if possible_rcvr_ty.is_ty_var() { + return None; + } // Fudge the receiver, so we can do new inference on it. let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger); let method = self @@ -390,6 +393,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { binding, ) .ok()?; + // Make sure we select the same method that we started with... + if Some(method.def_id) + != self.typeck_results.borrow().type_dependent_def_id(call_expr.hir_id) + { + return None; + } // Unify the method signature with our incompatible arg, to // do inference in the *opposite* direction and to find out // what our ideal rcvr ty would look like. @@ -460,6 +469,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) else { continue; }; + // Make sure we select the same method that we started with... + if Some(method.def_id) + != self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) + { + continue; + } let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger); let ideal_method = self @@ -509,13 +524,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // blame arg, if possible. Don't do this if we're coming from // arg mismatch code, because we'll possibly suggest a mutually // incompatible fix at the original mismatch site. + // HACK(compiler-errors): We don't actually consider the implications + // of our inference guesses in `emit_type_mismatch_suggestions`, so + // only suggest things when we know our type error is precisely due to + // a type mismatch, and not via some projection or something. See #116155. if matches!(source, TypeMismatchSource::Ty(_)) && let Some(ideal_method) = ideal_method - && let ideal_arg_ty = self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1]) - // HACK(compiler-errors): We don't actually consider the implications - // of our inference guesses in `emit_type_mismatch_suggestions`, so - // only suggest things when we know our type error is precisely due to - // a type mismatch, and not via some projection or something. See #116155. + && Some(ideal_method.def_id) + == self + .typeck_results + .borrow() + .type_dependent_def_id(parent_expr.hir_id) + && let ideal_arg_ty = + self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1]) && !ideal_arg_ty.has_non_region_infer() { self.emit_type_mismatch_suggestions( diff --git a/tests/crashes/118185.rs b/tests/crashes/118185.rs deleted file mode 100644 index c3a29c3a3f54a..0000000000000 --- a/tests/crashes/118185.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ known-bug: #118185 - -fn main() { - let target: Target = create_target(); - target.get(0); // correct arguments work - target.get(10.0); // CRASH HERE -} - -// must be generic -fn create_target() -> T { - unimplemented!() -} - -// unimplemented trait, but contains function with the same name -pub trait RandomTrait { - fn get(&mut self); // but less arguments -} - -struct Target; - -impl Target { - // correct function with arguments - pub fn get(&self, data: i32) { - unimplemented!() - } -} diff --git a/tests/crashes/118185-2.rs b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs similarity index 85% rename from tests/crashes/118185-2.rs rename to tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs index c3a29c3a3f54a..fd41beecb0a2a 100644 --- a/tests/crashes/118185-2.rs +++ b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs @@ -1,9 +1,8 @@ -//@ known-bug: #118185 - fn main() { let target: Target = create_target(); target.get(0); // correct arguments work - target.get(10.0); // CRASH HERE + target.get(10.0); // (used to crash here) + //~^ ERROR mismatched types } // must be generic diff --git a/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr new file mode 100644 index 0000000000000..0f86916fcdae4 --- /dev/null +++ b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr @@ -0,0 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/diagnostic-method-lookup-returns-sig-with-fewer-args.rs:4:16 + | +LL | target.get(10.0); // (used to crash here) + | --- ^^^^ expected `i32`, found floating-point number + | | + | arguments to this method are incorrect + | +note: method defined here + --> $DIR/diagnostic-method-lookup-returns-sig-with-fewer-args.rs:22:12 + | +LL | pub fn get(&self, data: i32) { + | ^^^ --------- + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 75f57670b03a33dd165685fe1f252969ef5c16ea Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 May 2024 17:22:35 +0200 Subject: [PATCH 02/31] miri: rename intrinsic_fallback_checks_ub to intrinsic_fallback_is_spec --- library/core/src/intrinsics.rs | 10 +++++----- src/tools/miri/src/intrinsics/mod.rs | 6 +++--- .../tests/fail/intrinsic_fallback_checks_ub.stderr | 14 -------------- ..._checks_ub.rs => intrinsic_fallback_is_spec.rs} | 2 +- .../tests/fail/intrinsic_fallback_is_spec.stderr | 14 ++++++++++++++ 5 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr rename src/tools/miri/tests/fail/{intrinsic_fallback_checks_ub.rs => intrinsic_fallback_is_spec.rs} (75%) create mode 100644 src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d1450bf12ce72..1027d48364002 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -987,7 +987,7 @@ pub const unsafe fn assume(b: bool) { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] pub const fn likely(b: bool) -> bool { b } @@ -1007,7 +1007,7 @@ pub const fn likely(b: bool) -> bool { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] #[rustc_nounwind] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] pub const fn unlikely(b: bool) -> bool { b } @@ -2471,7 +2471,7 @@ extern "rust-intrinsic" { #[rustc_nounwind] #[rustc_do_not_const_check] #[inline] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { (ptr == other) as u8 } @@ -2736,7 +2736,7 @@ pub const fn ub_checks() -> bool { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[rustc_intrinsic] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { // const eval overrides this function, but runtime code for now just returns null pointers. // See . @@ -2757,7 +2757,7 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_nounwind] #[rustc_intrinsic] -#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_checks_ub)] +#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)] pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) { // Runtime NOP } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index effd7f6d5435f..d94485a42a85a 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -43,18 +43,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`") } - let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub"); + let intrinsic_fallback_is_spec = Symbol::intern("intrinsic_fallback_is_spec"); if this .tcx .get_attrs_by_path( instance.def_id(), - &[sym::miri, intrinsic_fallback_checks_ub], + &[sym::miri, intrinsic_fallback_is_spec], ) .next() .is_none() { throw_unsup_format!( - "miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that" + "Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that" ); } Ok(Some(ty::Instance { diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr b/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr deleted file mode 100644 index 699dda52096f3..0000000000000 --- a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: unsupported operation: miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that - --> $DIR/intrinsic_fallback_checks_ub.rs:LL:CC - | -LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that - | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = note: BACKTRACE: - = note: inside `main` at $DIR/intrinsic_fallback_checks_ub.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs similarity index 75% rename from src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs rename to src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs index 93c9d3d7814ca..888c548e49b5b 100644 --- a/src/tools/miri/tests/fail/intrinsic_fallback_checks_ub.rs +++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs @@ -10,5 +10,5 @@ pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { fn main() { ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); - //~^ ERROR: can only use intrinsic fallback bodies that check UB. + //~^ ERROR: can only use intrinsic fallback bodies that exactly reflect the specification } diff --git a/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr new file mode 100644 index 0000000000000..db3941a32a56a --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that + --> $DIR/intrinsic_fallback_is_spec.rs:LL:CC + | +LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/intrinsic_fallback_is_spec.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From 7faa879486f6aa2871634b54cef3f886591ef323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 11 May 2024 15:40:34 +0200 Subject: [PATCH 03/31] Pattern types: Prohibit generic args on const params --- compiler/rustc_hir_analysis/messages.ftl | 6 +-- .../src/hir_ty_lowering/errors.rs | 12 +++--- .../src/hir_ty_lowering/mod.rs | 11 ++++-- .../bad_const_generics_args_on_const_param.rs | 10 +++++ ..._const_generics_args_on_const_param.stderr | 38 +++++++++++++++++++ tests/ui/type/pattern_types/bad_pat.stderr | 2 +- 6 files changed, 66 insertions(+), 13 deletions(-) create mode 100644 tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs create mode 100644 tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 3edea0191faf1..cf492a2a3fee3 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -371,9 +371,9 @@ hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function .suggestion = cast the value to `{$cast_ty}` .help = cast the value to `{$cast_ty}` -hir_analysis_pattern_type_non_const_range = "range patterns must have constant range start and end" -hir_analysis_pattern_type_wild_pat = "wildcard patterns are not permitted for pattern types" - .label = "this type is the same as the inner type without a pattern" +hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end +hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types + .label = this type is the same as the inner type without a pattern hir_analysis_placeholder_not_allowed_item_signatures = the placeholder `_` is not allowed within types on item signatures for {$kind} .label = not allowed in type signatures diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 8e64a9425bb9f..a8153c4a08819 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1388,7 +1388,7 @@ pub enum GenericsArgsErrExtend<'tcx> { span: Span, }, SelfTyParam(Span), - TyParam(DefId), + Param(DefId), DefVariant, None, } @@ -1504,11 +1504,11 @@ fn generics_args_err_extend<'a>( GenericsArgsErrExtend::DefVariant => { err.note("enum variants can't have type parameters"); } - GenericsArgsErrExtend::TyParam(def_id) => { - if let Some(span) = tcx.def_ident_span(def_id) { - let name = tcx.item_name(def_id); - err.span_note(span, format!("type parameter `{name}` defined here")); - } + GenericsArgsErrExtend::Param(def_id) => { + let span = tcx.def_ident_span(def_id).unwrap(); + let kind = tcx.def_descr(def_id); + let name = tcx.item_name(def_id); + err.span_note(span, format!("{kind} `{name}` defined here")); } GenericsArgsErrExtend::SelfTyParam(span) => { err.span_suggestion_verbose( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index f7213442ac2dd..37c6ef84f2466 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1756,7 +1756,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assert_eq!(opt_self_ty, None); let _ = self.prohibit_generic_args( path.segments.iter(), - GenericsArgsErrExtend::TyParam(def_id), + GenericsArgsErrExtend::Param(def_id), ); self.lower_ty_param(hir_id) } @@ -2196,10 +2196,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::ExprKind::Path(hir::QPath::Resolved( _, - &hir::Path { - res: Res::Def(DefKind::ConstParam, def_id), .. + path @ &hir::Path { + res: Res::Def(DefKind::ConstParam, def_id), + .. }, )) => { + let _ = self.prohibit_generic_args( + path.segments.iter(), + GenericsArgsErrExtend::Param(def_id), + ); let ty = tcx .type_of(def_id) .no_bound_vars() diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs new file mode 100644 index 0000000000000..050b7b44b4e0d --- /dev/null +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs @@ -0,0 +1,10 @@ +#![feature(pattern_types, core_pattern_type)] +#![allow(internal_features)] + +type Pat = + std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); +//~^ ERROR type and const arguments are not allowed on const parameter `START` +//~| ERROR type arguments are not allowed on const parameter `END` +//~| ERROR associated type bindings are not allowed here + +fn main() {} diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr new file mode 100644 index 0000000000000..40effe924da81 --- /dev/null +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr @@ -0,0 +1,38 @@ +error[E0109]: type and const arguments are not allowed on const parameter `START` + --> $DIR/bad_const_generics_args_on_const_param.rs:5:44 + | +LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); + | ----- ^^ ^^^ ^ type and const arguments not allowed + | | + | not allowed on const parameter `START` + | +note: const parameter `START` defined here + --> $DIR/bad_const_generics_args_on_const_param.rs:4:16 + | +LL | type Pat = + | ^^^^^ + +error[E0109]: type arguments are not allowed on const parameter `END` + --> $DIR/bad_const_generics_args_on_const_param.rs:5:64 + | +LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); + | --- ^ type argument not allowed + | | + | not allowed on const parameter `END` + | +note: const parameter `END` defined here + --> $DIR/bad_const_generics_args_on_const_param.rs:4:34 + | +LL | type Pat = + | ^^^ + +error[E0229]: associated type bindings are not allowed here + --> $DIR/bad_const_generics_args_on_const_param.rs:5:67 + | +LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); + | ^^^^^^^^^^ associated type not allowed here + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0109, E0229. +For more information about an error, try `rustc --explain E0109`. diff --git a/tests/ui/type/pattern_types/bad_pat.stderr b/tests/ui/type/pattern_types/bad_pat.stderr index 4f0f0bc974276..2abf27100c1de 100644 --- a/tests/ui/type/pattern_types/bad_pat.stderr +++ b/tests/ui/type/pattern_types/bad_pat.stderr @@ -14,7 +14,7 @@ LL | type Positive2 = pattern_type!(i32 is 0..=); | = note: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) -error: "wildcard patterns are not permitted for pattern types" +error: wildcard patterns are not permitted for pattern types --> $DIR/bad_pat.rs:11:33 | LL | type Wild = pattern_type!(() is _); From 7c76eec30ff6cdb00b234896f6e0c5b76ef7143f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 May 2024 09:59:08 +0200 Subject: [PATCH 04/31] reference type safety invariant docs: clarification --- library/core/src/primitive_docs.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index d8597369b9bfd..50e0afab9e6cf 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1475,14 +1475,17 @@ mod prim_usize {} /// /// For instance, this means that unsafe code in a safe function may assume these invariants are /// ensured of arguments passed by the caller, and it may assume that these invariants are ensured -/// of return values from any safe functions it calls. In most cases, the inverse is also true: -/// unsafe code must not violate these invariants when passing arguments to safe functions or -/// returning values from safe functions; such violations may result in undefined behavior. Where -/// exceptions to this latter requirement exist, they will be called out explicitly in documentation. +/// of return values from any safe functions it calls. +/// +/// For the other direction, things are more complicated: when unsafe code passes arguments +/// to safe functions or returns values from safe functions, they generally must *at least* +/// not violate these invariants. The full requirements are stronger, as the reference generally +/// must point to data that is safe to use at type `T`. /// /// It is not decided yet whether unsafe code may violate these invariants temporarily on internal /// data. As a consequence, unsafe code which violates these invariants temporarily on internal data -/// may become unsound in future versions of Rust depending on how this question is decided. +/// may be unsound or become unsound in future versions of Rust depending on how this question is +/// decided. /// /// [allocated object]: ptr#allocated-object #[stable(feature = "rust1", since = "1.0.0")] From f4931437c249ed5275ae20eb4ac58e3e4484fa98 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 May 2024 12:49:49 -0700 Subject: [PATCH 05/31] Clean up unneeded warnings from let-else syntax test --- tests/ui/parser/bad-let-else-statement.rs | 59 ++-- tests/ui/parser/bad-let-else-statement.stderr | 300 +++--------------- 2 files changed, 69 insertions(+), 290 deletions(-) diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index de41a07568fb1..8ed55f949598e 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -3,8 +3,7 @@ #![feature(explicit_tail_calls)] fn a() { - let foo = { - //~^ WARN irrefutable `let...else` pattern + let 0 = { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -22,8 +21,7 @@ fn b() { } fn c() { - let foo = if true { - //~^ WARN irrefutable `let...else` pattern + let 0 = if true { 1 } else { 0 @@ -43,8 +41,7 @@ fn d() { } fn e() { - let foo = match true { - //~^ WARN irrefutable `let...else` pattern + let 0 = match true { true => 1, false => 0 } else { @@ -53,10 +50,12 @@ fn e() { }; } -struct X {a: i32} fn f() { - let foo = X { - //~^ WARN irrefutable `let...else` pattern + struct X { + a: i32, + } + + let X { a: 0 } = X { a: 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -74,8 +73,7 @@ fn g() { } fn h() { - let foo = const { - //~^ WARN irrefutable `let...else` pattern + let 0 = const { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -84,8 +82,7 @@ fn h() { } fn i() { - let foo = &{ - //~^ WARN irrefutable `let...else` pattern + let 0 = &{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -94,8 +91,8 @@ fn i() { } fn j() { - let bar = 0; - let foo = bar = { //~ ERROR: cannot assign twice + let mut bar = 0; + let foo = bar = { //~^ WARN irrefutable `let...else` pattern 1 } else { @@ -105,8 +102,7 @@ fn j() { } fn k() { - let foo = 1 + { - //~^ WARN irrefutable `let...else` pattern + let 0 = 1 + { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -115,8 +111,8 @@ fn k() { } fn l() { - let foo = 1..{ - //~^ WARN irrefutable `let...else` pattern + const RANGE: std::ops::Range = 0..0; + let RANGE = 1..{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -125,8 +121,7 @@ fn l() { } fn m() { - let foo = return { - //~^ WARN irrefutable `let...else` pattern + let 0 = return { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -135,8 +130,7 @@ fn m() { } fn n() { - let foo = -{ - //~^ WARN irrefutable `let...else` pattern + let 0 = -{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -145,8 +139,7 @@ fn n() { } fn o() -> Result<(), ()> { - let foo = do yeet { - //~^ WARN irrefutable `let...else` pattern + let 0 = do yeet { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -155,8 +148,7 @@ fn o() -> Result<(), ()> { } fn p() { - let foo = become { - //~^ WARN irrefutable `let...else` pattern + let 0 = become { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -185,22 +177,23 @@ fn r() { fn s() { macro_rules! a { - () => { {} } - //~^ WARN irrefutable `let...else` pattern - //~| WARN irrefutable `let...else` pattern + () => { + { 1 } + }; } macro_rules! b { (1) => { - let x = a!() else { return; }; + let 0 = a!() else { return; }; }; (2) => { - let x = a! {} else { return; }; + let 0 = a! {} else { return; }; //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed }; } - b!(1); b!(2); + b!(1); + b!(2); } fn main() {} diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 3f7e176b3e31f..60b4600ff35d8 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -1,19 +1,18 @@ error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:9:5 + --> $DIR/bad-let-else-statement.rs:8:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = ({ -LL | +LL ~ let 0 = ({ LL | 1 LL ~ }) else { | error: `for...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:18:7 + --> $DIR/bad-let-else-statement.rs:17:7 | LL | let foo = for i in 1..2 { | --- `else` is attached to this loop @@ -28,22 +27,22 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:30:5 + --> $DIR/bad-let-else-statement.rs:28:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (if true { -LL | - ... +LL ~ let 0 = (if true { +LL | 1 +LL | } else { LL | 0 LL ~ }) else { | error: `loop...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:39:7 + --> $DIR/bad-let-else-statement.rs:37:7 | LL | let foo = loop { | ---- `else` is attached to this loop @@ -58,36 +57,34 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:50:5 + --> $DIR/bad-let-else-statement.rs:47:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (match true { -LL | +LL ~ let 0 = (match true { LL | true => 1, LL | false => 0 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:61:5 + --> $DIR/bad-let-else-statement.rs:60:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (X { -LL | +LL ~ let X { a: 0 } = (X { LL | a: 1 LL ~ }) else { | error: `while...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:70:7 + --> $DIR/bad-let-else-statement.rs:69:7 | LL | let foo = while false { | ----- `else` is attached to this loop @@ -102,35 +99,33 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:80:5 + --> $DIR/bad-let-else-statement.rs:78:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (const { -LL | +LL ~ let 0 = (const { LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:90:5 + --> $DIR/bad-let-else-statement.rs:87:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = &({ -LL | +LL ~ let 0 = &({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:101:5 + --> $DIR/bad-let-else-statement.rs:98:5 | LL | } else { | ^ @@ -144,91 +139,85 @@ LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:111:5 + --> $DIR/bad-let-else-statement.rs:107:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = 1 + ({ -LL | +LL ~ let 0 = 1 + ({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:121:5 + --> $DIR/bad-let-else-statement.rs:117:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = 1..({ -LL | +LL ~ let RANGE = 1..({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:131:5 + --> $DIR/bad-let-else-statement.rs:126:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = return ({ -LL | +LL ~ let 0 = return ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:141:5 + --> $DIR/bad-let-else-statement.rs:135:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = -({ -LL | +LL ~ let 0 = -({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:151:5 + --> $DIR/bad-let-else-statement.rs:144:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = do yeet ({ -LL | +LL ~ let 0 = do yeet ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:161:5 + --> $DIR/bad-let-else-statement.rs:153:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = become ({ -LL | +LL ~ let 0 = become ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:171:5 + --> $DIR/bad-let-else-statement.rs:163:5 | LL | } else { | ^ @@ -242,7 +231,7 @@ LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:181:31 + --> $DIR/bad-let-else-statement.rs:173:31 | LL | let bad = format_args! {""} else { return; }; | ^ @@ -253,98 +242,22 @@ LL | let bad = format_args! ("") else { return; }; | ~ ~ error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:198:25 + --> $DIR/bad-let-else-statement.rs:190:25 | -LL | let x = a! {} else { return; }; +LL | let 0 = a! {} else { return; }; | ^ ... -LL | b!(1); b!(2); - | ----- in this macro invocation +LL | b!(2); + | ----- in this macro invocation | = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) help: use parentheses instead of braces for this macro | -LL | let x = a! () else { return; }; +LL | let 0 = a! () else { return; }; | ~~ warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:6:5 - | -LL | / let foo = { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - = note: `#[warn(irrefutable_let_patterns)]` on by default - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:25:5 - | -LL | / let foo = if true { -LL | | -LL | | 1 -LL | | } else { -LL | | 0 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:46:5 - | -LL | / let foo = match true { -LL | | -LL | | true => 1, -LL | | false => 0 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:58:5 - | -LL | / let foo = X { -LL | | -LL | | a: 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:77:5 - | -LL | / let foo = const { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:87:5 - | -LL | / let foo = &{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:98:5 + --> $DIR/bad-let-else-statement.rs:95:5 | LL | / let foo = bar = { LL | | @@ -354,96 +267,10 @@ LL | | } else { | = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause - -error[E0384]: cannot assign twice to immutable variable `bar` - --> $DIR/bad-let-else-statement.rs:98:15 - | -LL | let bar = 0; - | --- - | | - | first assignment to `bar` - | help: consider making this binding mutable: `mut bar` -LL | let foo = bar = { - | _______________^ -LL | | -LL | | 1 -LL | | } else { - | |_____^ cannot assign twice to immutable variable - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:108:5 - | -LL | / let foo = 1 + { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:118:5 - | -LL | / let foo = 1..{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:128:5 - | -LL | / let foo = return { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:138:5 - | -LL | / let foo = -{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:148:5 - | -LL | / let foo = do yeet { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:158:5 - | -LL | / let foo = become { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause + = note: `#[warn(irrefutable_let_patterns)]` on by default warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:168:5 + --> $DIR/bad-let-else-statement.rs:160:5 | LL | / let foo = |x: i32| { LL | | @@ -455,7 +282,7 @@ LL | | } else { = help: consider removing the `else` clause warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:178:5 + --> $DIR/bad-let-else-statement.rs:170:5 | LL | let ok = format_args!("") else { return; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -464,7 +291,7 @@ LL | let ok = format_args!("") else { return; }; = help: consider removing the `else` clause warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:181:5 + --> $DIR/bad-let-else-statement.rs:173:5 | LL | let bad = format_args! {""} else { return; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -472,46 +299,5 @@ LL | let bad = format_args! {""} else { return; }; = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:188:19 - | -LL | () => { {} } - | ___________________^ -LL | | -LL | | -LL | | } -... | -LL | | (1) => { -LL | | let x = a!() else { return; }; - | |____________^ -... -LL | b!(1); b!(2); - | ----- in this macro invocation - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:188:19 - | -LL | () => { {} } - | ___________________^ -LL | | -LL | | -LL | | } -... | -LL | | (2) => { -LL | | let x = a! {} else { return; }; - | |____________^ -... -LL | b!(1); b!(2); - | ----- in this macro invocation - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 20 previous errors; 18 warnings emitted +error: aborting due to 19 previous errors; 4 warnings emitted -For more information about this error, try `rustc --explain E0384`. From 75a34ca26294cb79bd8b126c0fc18f4c207faaf0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 May 2024 13:02:54 -0700 Subject: [PATCH 06/31] Add test of trailing brace in a cast expression --- tests/ui/parser/bad-let-else-statement.rs | 14 ++++++++++++++ tests/ui/parser/bad-let-else-statement.stderr | 14 +++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index 8ed55f949598e..2dce9ed24d2d1 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -196,4 +196,18 @@ fn s() { b!(2); } +fn t() { + macro_rules! primitive { + (8) => { u8 }; + } + + let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! { + //~^ WARN irrefutable `let...else` pattern + 8 + } else { + // FIXME: right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + fn main() {} diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 60b4600ff35d8..76097aaca8327 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -299,5 +299,17 @@ LL | let bad = format_args! {""} else { return; }; = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause -error: aborting due to 19 previous errors; 4 warnings emitted +warning: irrefutable `let...else` pattern + --> $DIR/bad-let-else-statement.rs:204:5 + | +LL | / let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! { +LL | | +LL | | 8 +LL | | } else { + | |_____^ + | + = note: this pattern will always match, so the `else` clause is useless + = help: consider removing the `else` clause + +error: aborting due to 19 previous errors; 5 warnings emitted From a36b94d0887d42b692935c918c8cc869ca6c61b4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 May 2024 12:28:10 -0700 Subject: [PATCH 07/31] Disallow cast with trailing braced macro in let-else --- compiler/rustc_ast/src/util/classify.rs | 95 ++++++++++++++++++- compiler/rustc_parse/src/parser/stmt.rs | 28 +++--- tests/ui/parser/bad-let-else-statement.rs | 2 +- tests/ui/parser/bad-let-else-statement.stderr | 16 +++- 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index f6e9e1a87c4bb..382c903625fbd 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -81,8 +81,17 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { } } +pub enum TrailingBrace<'a> { + /// Trailing brace in a macro call, like the one in `x as *const brace! {}`. + /// We will suggest changing the macro call to a different delimiter. + MacCall(&'a ast::MacCall), + /// Trailing brace in any other expression, such as `a + B {}`. We will + /// suggest wrapping the innermost expression in parentheses: `a + (B {})`. + Expr(&'a ast::Expr), +} + /// If an expression ends with `}`, returns the innermost expression ending in the `}` -pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { +pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { loop { match &expr.kind { AddrOf(_, _, e) @@ -111,10 +120,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | Struct(..) | TryBlock(..) | While(..) - | ConstBlock(_) => break Some(expr), + | ConstBlock(_) => break Some(TrailingBrace::Expr(expr)), + + Cast(_, ty) => { + break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall); + } MacCall(mac) => { - break (mac.args.delim == Delimiter::Brace).then_some(expr); + break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac)); } InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => { @@ -131,7 +144,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | MethodCall(_) | Tup(_) | Lit(_) - | Cast(_, _) | Type(_, _) | Await(_, _) | Field(_, _) @@ -148,3 +160,78 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { } } } + +/// If the type's last token is `}`, it must be due to a braced macro call, such +/// as in `*const brace! { ... }`. Returns that trailing macro call. +fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { + loop { + match &ty.kind { + ast::TyKind::MacCall(mac) => { + break (mac.args.delim == Delimiter::Brace).then_some(mac); + } + + ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => { + ty = &mut_ty.ty; + } + + ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output { + ast::FnRetTy::Default(_) => break None, + ast::FnRetTy::Ty(ret) => ty = ret, + }, + + ast::TyKind::Path(_, path) => match path_return_type(path) { + Some(trailing_ty) => ty = trailing_ty, + None => break None, + }, + + ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => { + match bounds.last() { + Some(ast::GenericBound::Trait(bound, _)) => { + match path_return_type(&bound.trait_ref.path) { + Some(trailing_ty) => ty = trailing_ty, + None => break None, + } + } + Some(ast::GenericBound::Outlives(_)) | None => break None, + } + } + + ast::TyKind::Slice(..) + | ast::TyKind::Array(..) + | ast::TyKind::Never + | ast::TyKind::Tup(..) + | ast::TyKind::Paren(..) + | ast::TyKind::Typeof(..) + | ast::TyKind::Infer + | ast::TyKind::ImplicitSelf + | ast::TyKind::CVarArgs + | ast::TyKind::Pat(..) + | ast::TyKind::Dummy + | ast::TyKind::Err(..) => break None, + + // These end in brace, but cannot occur in a let-else statement. + // They are only parsed as fields of a data structure. For the + // purpose of denying trailing braces in the expression of a + // let-else, we can disregard these. + ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None, + } + } +} + +/// Returns the trailing return type in the given path, if it has one. +/// +/// ```ignore (illustrative) +/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void +/// ^^^^^^^^^^^^^^^^^^^^^ +/// ``` +fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> { + let last_segment = path.segments.last()?; + let args = last_segment.args.as_ref()?; + match &**args { + ast::GenericArgs::Parenthesized(args) => match &args.output { + ast::FnRetTy::Default(_) => None, + ast::FnRetTy::Ty(ret) => Some(ret), + }, + ast::GenericArgs::AngleBracketed(_) => None, + } +} diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index d70afebf1b2da..941b145e2dba2 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -15,7 +15,7 @@ use ast::Label; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; -use rustc_ast::util::classify; +use rustc_ast::util::classify::{self, TrailingBrace}; use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; @@ -407,18 +407,24 @@ impl<'a> Parser<'a> { fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) { if let Some(trailing) = classify::expr_trailing_brace(init) { - let sugg = match &trailing.kind { - ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs { - left: mac.args.dspan.open, - right: mac.args.dspan.close, - }, - _ => errors::WrapInParentheses::Expression { - left: trailing.span.shrink_to_lo(), - right: trailing.span.shrink_to_hi(), - }, + let (span, sugg) = match trailing { + TrailingBrace::MacCall(mac) => ( + mac.span(), + errors::WrapInParentheses::MacroArgs { + left: mac.args.dspan.open, + right: mac.args.dspan.close, + }, + ), + TrailingBrace::Expr(expr) => ( + expr.span, + errors::WrapInParentheses::Expression { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + ), }; self.dcx().emit_err(errors::InvalidCurlyInLetElse { - span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)), + span: span.with_lo(span.hi() - BytePos(1)), sugg, }); } diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index 2dce9ed24d2d1..ff6619cbc9864 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -205,7 +205,7 @@ fn t() { //~^ WARN irrefutable `let...else` pattern 8 } else { - // FIXME: right curly brace `}` before `else` in a `let...else` statement not allowed + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed return; }; } diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 76097aaca8327..0bf6a346dfb17 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -241,6 +241,20 @@ help: use parentheses instead of braces for this macro LL | let bad = format_args! ("") else { return; }; | ~ ~ +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:207:5 + | +LL | } else { + | ^ + | +help: use parentheses instead of braces for this macro + | +LL ~ let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! ( +LL | +LL | 8 +LL ~ ) else { + | + error: right curly brace `}` before `else` in a `let...else` statement not allowed --> $DIR/bad-let-else-statement.rs:190:25 | @@ -311,5 +325,5 @@ LL | | } else { = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause -error: aborting due to 19 previous errors; 5 warnings emitted +error: aborting due to 20 previous errors; 5 warnings emitted From 5c33a5690de89587b645fb0e7b657fe545e4d0e8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 12:35:11 +0200 Subject: [PATCH 08/31] offset, offset_from: allow zero-byte offset on arbitrary pointers --- .../src/const_eval/machine.rs | 20 +++++-- .../src/interpret/intrinsics.rs | 1 + .../rustc_const_eval/src/interpret/memory.rs | 39 ++++++------ .../src/interpret/validity.rs | 11 +++- library/core/src/intrinsics.rs | 8 +-- library/core/src/ptr/const_ptr.rs | 23 ++++---- library/core/src/ptr/mod.rs | 11 +--- library/core/src/ptr/mut_ptr.rs | 23 ++++---- .../dangling_pointers/dangling_zst_deref.rs | 10 ---- .../dangling_zst_deref.stderr | 25 -------- .../maybe_null_pointer_deref_zst.rs | 5 -- .../maybe_null_pointer_deref_zst.stderr | 15 ----- .../maybe_null_pointer_write_zst.rs | 8 --- .../maybe_null_pointer_write_zst.stderr | 15 ----- .../null_pointer_deref_zst.rs | 5 -- .../null_pointer_deref_zst.stderr | 15 ----- .../null_pointer_write_zst.rs | 8 --- .../null_pointer_write_zst.stderr | 15 ----- .../miri/tests/fail/intrinsics/copy_null.rs | 15 ----- .../tests/fail/intrinsics/copy_null.stderr | 15 ----- .../fail/intrinsics/ptr_offset_0_plus_0.rs | 9 --- .../intrinsics/ptr_offset_0_plus_0.stderr | 15 ----- .../fail/intrinsics/ptr_offset_from_oob.rs | 7 --- .../intrinsics/ptr_offset_from_oob.stderr | 15 ----- .../fail/intrinsics/ptr_offset_ptr_plus_0.rs | 7 --- .../intrinsics/ptr_offset_ptr_plus_0.stderr | 20 ------- .../tests/fail/intrinsics/write_bytes_null.rs | 10 ---- .../fail/intrinsics/write_bytes_null.stderr | 15 ----- src/tools/miri/tests/fail/zst2.rs | 12 ---- src/tools/miri/tests/fail/zst2.stderr | 25 -------- src/tools/miri/tests/fail/zst3.rs | 15 ----- src/tools/miri/tests/fail/zst3.stderr | 20 ------- .../tests/fail/{zst1.rs => zst_local_oob.rs} | 0 .../{zst1.stderr => zst_local_oob.stderr} | 4 +- .../miri/tests/pass/align_offset_symbolic.rs | 7 +-- .../pass/zero-sized-accesses-and-offsets.rs | 59 +++++++++++++++++++ tests/ui/const-ptr/forbidden_slices.rs | 7 ++- tests/ui/const-ptr/forbidden_slices.stderr | 42 ++++++------- tests/ui/consts/const-compare-bytes-ub.rs | 4 +- tests/ui/consts/const-compare-bytes-ub.stderr | 6 +- tests/ui/consts/copy-intrinsic.rs | 16 +++-- tests/ui/consts/copy-intrinsic.stderr | 16 ++--- tests/ui/consts/dangling-alloc-id-ice.rs | 2 +- tests/ui/consts/dangling-alloc-id-ice.stderr | 10 +--- tests/ui/consts/offset_from_ub.rs | 8 +-- tests/ui/consts/offset_from_ub.stderr | 14 +---- tests/ui/consts/offset_ub.rs | 6 +- tests/ui/consts/offset_ub.stderr | 17 +----- 48 files changed, 200 insertions(+), 475 deletions(-) delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs delete mode 100644 src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr delete mode 100644 src/tools/miri/tests/fail/intrinsics/copy_null.rs delete mode 100644 src/tools/miri/tests/fail/intrinsics/copy_null.stderr delete mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs delete mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr delete mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs delete mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr delete mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs delete mode 100644 src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr delete mode 100644 src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs delete mode 100644 src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr delete mode 100644 src/tools/miri/tests/fail/zst2.rs delete mode 100644 src/tools/miri/tests/fail/zst2.stderr delete mode 100644 src/tools/miri/tests/fail/zst3.rs delete mode 100644 src/tools/miri/tests/fail/zst3.stderr rename src/tools/miri/tests/fail/{zst1.rs => zst_local_oob.rs} (100%) rename src/tools/miri/tests/fail/{zst1.stderr => zst_local_oob.stderr} (88%) create mode 100644 src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 836e548ae2b7f..310fd462d5f8c 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -25,9 +25,9 @@ use rustc_target::spec::abi::Abi as CallAbi; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, + self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup, throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame, - ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar, }; use super::error::*; @@ -759,11 +759,21 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ecx: &InterpCx<'mir, 'tcx, Self>, alloc_id: AllocId, ) -> InterpResult<'tcx> { + // Check if this is the currently evaluated static. if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) { - Err(ConstEvalErrKind::RecursiveStatic.into()) - } else { - Ok(()) + return Err(ConstEvalErrKind::RecursiveStatic.into()); } + // If this is another static, make sure we fire off the query to detect cycles. + // But only do that when checks for static recursion are enabled. + if ecx.machine.static_root_ids.is_some() { + if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) { + if ecx.tcx.is_foreign_item(def_id) { + throw_unsup!(ExternStatic(def_id)); + } + ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?; + } + } + Ok(()) } } diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index dce4d56f7e007..72dad562695e2 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -255,6 +255,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { name = intrinsic_name, ); } + // This will always return 0. (a, b) } (Err(_), _) | (_, Err(_)) => { diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 350fd480fbaae..737f2fd8bb983 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -413,6 +413,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// to the allocation it points to. Supports both shared and mutable references, as the actual /// checking is offloaded to a helper closure. /// + /// `alloc_size` will only get called for non-zero-sized accesses. + /// /// Returns `None` if and only if the size is 0. fn check_and_deref_ptr( &self, @@ -425,18 +427,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { M::ProvenanceExtra, ) -> InterpResult<'tcx, (Size, Align, T)>, ) -> InterpResult<'tcx, Option> { + // Everything is okay with size 0. + if size.bytes() == 0 { + return Ok(None); + } + Ok(match self.ptr_try_get_alloc_id(ptr) { Err(addr) => { - // We couldn't get a proper allocation. This is only okay if the access size is 0, - // and the address is not null. - if size.bytes() > 0 || addr == 0 { - throw_ub!(DanglingIntPointer(addr, msg)); - } - None + // We couldn't get a proper allocation. + throw_ub!(DanglingIntPointer(addr, msg)); } Ok((alloc_id, offset, prov)) => { let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?; - // Test bounds. This also ensures non-null. + // Test bounds. // It is sufficient to check this for the end pointer. Also check for overflow! if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) { throw_ub!(PointerOutOfBounds { @@ -447,14 +450,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { msg, }) } - // Ensure we never consider the null pointer dereferenceable. - if M::Provenance::OFFSET_IS_ADDR { - assert_ne!(ptr.addr(), Size::ZERO); - } - // We can still be zero-sized in this branch, in which case we have to - // return `None`. - if size.bytes() == 0 { None } else { Some(ret_val) } + Some(ret_val) } }) } @@ -641,16 +638,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { size, CheckInAllocMsg::MemoryAccessTest, |alloc_id, offset, prov| { - if !self.memory.validation_in_progress.get() { - // We want to call the hook on *all* accesses that involve an AllocId, - // including zero-sized accesses. That means we have to do it here - // rather than below in the `Some` branch. - M::before_alloc_read(self, alloc_id)?; - } let alloc = self.get_alloc_raw(alloc_id)?; Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc))) }, )?; + // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized + // accesses. That means we cannot rely on the closure above or the `Some` branch below. We + // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked. + if !self.memory.validation_in_progress.get() { + if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) { + M::before_alloc_read(self, alloc_id)?; + } + } if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc { let range = alloc_range(offset, size); diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 2bd4d9dc07a51..a47828bb63c25 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -434,6 +434,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' found_bytes: has.bytes() }, ); + // Make sure this is non-null. (ZST references can be dereferenceable and null.) + if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? { + throw_validation_failure!(self.path, NullPtr { ptr_kind }) + } // Do not allow pointers to uninhabited types. if place.layout.abi.is_uninhabited() { let ty = place.layout.ty; @@ -456,8 +460,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // `!` is a ZST and we want to validate it. if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) { let mut skip_recursive_check = false; - let alloc_actual_mutbl = mutability(self.ecx, alloc_id); - if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) { + if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id) + { let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() }; // Special handling for pointers to statics (irrespective of their type). assert!(!self.ecx.tcx.is_thread_local_static(did)); @@ -495,6 +499,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // If this allocation has size zero, there is no actual mutability here. let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id); if size != Size::ZERO { + let alloc_actual_mutbl = mutability(self.ecx, alloc_id); // Mutable pointer to immutable memory is no good. if ptr_expected_mutbl == Mutability::Mut && alloc_actual_mutbl == Mutability::Not @@ -831,6 +836,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> trace!("visit_value: {:?}, {:?}", *op, op.layout); // Check primitive types -- the leaves of our recursive descent. + // We assume that the Scalar validity range does not restrict these values + // any further than `try_visit_primitive` does! if self.try_visit_primitive(op)? { return Ok(()); } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index d1450bf12ce72..90a7e2bf382e5 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1483,10 +1483,10 @@ extern "rust-intrinsic" { /// /// # Safety /// - /// Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of an allocated object. If either pointer is out of - /// bounds or arithmetic overflow occurs then any further use of the - /// returned value will result in undefined behavior. + /// If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or one byte past the end of an allocated object. If either pointer is out + /// of bounds or arithmetic overflow occurs then any further use of the returned value will + /// result in undefined behavior. /// /// The stabilized version of this intrinsic is [`pointer::offset`]. #[must_use = "returns a new pointer rather than modifying its argument"] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 73bb256518d89..27b0c69d32d0f 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -465,8 +465,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or one byte past the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -676,11 +677,11 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be either empty or in bounds of that object. (See below for an example.) + /// * or both be derived from an integer literal/constant, and point to the same address. /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. @@ -951,8 +952,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or one byte past the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -1035,8 +1037,9 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or one byte past the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 8d7192c1b0fd1..e75f62c53c111 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -15,18 +15,13 @@ //! The precise rules for validity are not determined yet. The guarantees that are //! provided at this point are very minimal: //! -//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst]. +//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer. +//! The following points are only concerned with non-zero-sized accesses. +//! * A [null] pointer is *never* valid. //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer //! be *dereferenceable*: the memory range of the given size starting at the pointer must all be //! within the bounds of a single allocated object. Note that in Rust, //! every (stack-allocated) variable is considered a separate allocated object. -//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated -//! memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However, -//! casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if -//! some memory happens to exist at that address and gets deallocated. This corresponds to writing -//! your own allocator: allocating zero-sized objects is not very hard. The canonical way to -//! obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`]. -//FIXME: mention `ptr::dangling` above, once it is stable. //! * All accesses performed by functions in this module are *non-atomic* in the sense //! of [atomic operations] used to synchronize between threads. This means it is //! undefined behavior to perform two concurrent accesses to the same location from different diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index b67930503e015..3d05715f7a46d 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -480,8 +480,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or one byte past the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -904,11 +905,11 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both `self` and `origin` must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * `self` and `origin` must either /// - /// * Both pointers must be *derived from* a pointer to the same object. - /// (See below for an example.) + /// * both be *derived from* a pointer to the same [allocated object], and the memory range between + /// the two pointers must be either empty or in bounds of that object. (See below for an example.) + /// * or both be derived from an integer literal/constant, and point to the same address. /// /// * The distance between the pointers, in bytes, must be an exact multiple /// of the size of `T`. @@ -1095,8 +1096,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or one byte past the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. /// @@ -1179,8 +1181,9 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * Both the starting and resulting pointer must be either in bounds or one - /// byte past the end of the same [allocated object]. + /// * If the computed offset is non-zero, then both the starting and resulting pointer must be + /// either in bounds or one byte past the end of the same [allocated object]. + /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. /// diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs deleted file mode 100644 index a1fefe04ab69c..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Make sure we find these even with many checks disabled. -//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation - -fn main() { - let p = { - let b = Box::new(42); - &*b as *const i32 as *const () - }; - let _x = unsafe { *p }; //~ ERROR: has been freed -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr deleted file mode 100644 index 72b9a4a2d6cab..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | let _x = unsafe { *p }; - | ^^ memory access failed: ALLOC has been freed, so this pointer is dangling - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information -help: ALLOC was allocated here: - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | let b = Box::new(42); - | ^^^^^^^^^^^^ -help: ALLOC was deallocated here: - --> $DIR/dangling_zst_deref.rs:LL:CC - | -LL | }; - | ^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/dangling_zst_deref.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs deleted file mode 100644 index 73d0b12068013..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - // This pointer *could* be NULL so we cannot load from it, not even at ZST - let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const (); - let _x: () = unsafe { *ptr }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr deleted file mode 100644 index 13c53e20b8a64..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - --> $DIR/maybe_null_pointer_deref_zst.rs:LL:CC - | -LL | let _x: () = unsafe { *ptr }; - | ^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/maybe_null_pointer_deref_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs deleted file mode 100644 index 5537207ae424f..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs +++ /dev/null @@ -1,8 +0,0 @@ -fn main() { - // This pointer *could* be NULL so we cannot load from it, not even at ZST. - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0]; - unsafe { *ptr = zst_val }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr deleted file mode 100644 index e4e23e8ace1ce..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - --> $DIR/maybe_null_pointer_write_zst.rs:LL:CC - | -LL | unsafe { *ptr = zst_val }; - | ^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/maybe_null_pointer_write_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs deleted file mode 100644 index f8af43ff3529c..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[allow(deref_nullptr)] -fn main() { - let x: () = unsafe { *std::ptr::null() }; //~ ERROR: memory access failed: null pointer is a dangling pointer - panic!("this should never print: {:?}", x); -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr deleted file mode 100644 index 1a8794f3ceb7d..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/null_pointer_deref_zst.rs:LL:CC - | -LL | let x: () = unsafe { *std::ptr::null() }; - | ^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/null_pointer_deref_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs deleted file mode 100644 index edd6c8fadce46..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[allow(deref_nullptr)] -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) }; - //~^ERROR: memory access failed: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr deleted file mode 100644 index 1d4704e2a0e89..0000000000000 --- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/null_pointer_write_zst.rs:LL:CC - | -LL | unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/null_pointer_write_zst.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.rs b/src/tools/miri/tests/fail/intrinsics/copy_null.rs deleted file mode 100644 index 237e517f2875a..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/copy_null.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -extern "rust-intrinsic" { - fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); -} - -fn main() { - let mut data = [0u16; 4]; - let ptr = &mut data[0] as *mut u16; - // Even copying 0 elements from NULL should error. - unsafe { - copy_nonoverlapping(std::ptr::null(), ptr, 0); //~ ERROR: memory access failed: null pointer is a dangling pointer - } -} diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr b/src/tools/miri/tests/fail/intrinsics/copy_null.stderr deleted file mode 100644 index d73c03475d69d..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/copy_null.rs:LL:CC - | -LL | copy_nonoverlapping(std::ptr::null(), ptr, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/copy_null.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs deleted file mode 100644 index e2329c1313984..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@compile-flags: -Zmiri-permissive-provenance - -#[rustfmt::skip] // fails with "left behind trailing whitespace" -fn main() { - let x = 0 as *mut i32; - let _x = x.wrapping_offset(8); // ok, this has no inbounds tag - let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds - //~^ERROR: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr deleted file mode 100644 index a8984c7fa1670..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - --> $DIR/ptr_offset_0_plus_0.rs:LL:CC - | -LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/ptr_offset_0_plus_0.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs deleted file mode 100644 index 0e5acf08b2030..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - let start_ptr = &4 as *const _ as *const u8; - let length = 10; - let end_ptr = start_ptr.wrapping_add(length); - // Even if the offset is 0, a dangling OOB pointer is not allowed. - unsafe { end_ptr.offset_from(end_ptr) }; //~ERROR: pointer at offset 10 is out-of-bounds -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr deleted file mode 100644 index 32a4461d6bf04..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds - --> $DIR/ptr_offset_from_oob.rs:LL:CC - | -LL | unsafe { end_ptr.offset_from(end_ptr) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/ptr_offset_from_oob.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs deleted file mode 100644 index 575e28854b1a9..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[rustfmt::skip] // fails with "left behind trailing whitespace" -fn main() { - let x = Box::into_raw(Box::new(0u32)); - let x = x.wrapping_offset(8); // ok, this has no inbounds tag - let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to - //~^ERROR: pointer at offset 32 is out-of-bounds -} diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr deleted file mode 100644 index 304d362bbb9f9..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds - --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - | -LL | let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to - | ^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information -help: ALLOC was allocated here: - --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - | -LL | let x = Box::into_raw(Box::new(0u32)); - | ^^^^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/ptr_offset_ptr_plus_0.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs deleted file mode 100644 index 2f46c820fb73b..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(intrinsics)] - -// Directly call intrinsic to avoid debug assertions in libstd -extern "rust-intrinsic" { - fn write_bytes(dst: *mut T, val: u8, count: usize); -} - -fn main() { - unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; //~ ERROR: memory access failed: null pointer is a dangling pointer -} diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr deleted file mode 100644 index def180935cc39..0000000000000 --- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) - --> $DIR/write_bytes_null.rs:LL:CC - | -LL | unsafe { write_bytes::(std::ptr::null_mut(), 0, 0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at $DIR/write_bytes_null.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/zst2.rs b/src/tools/miri/tests/fail/zst2.rs deleted file mode 100644 index 04218c264a3ef..0000000000000 --- a/src/tools/miri/tests/fail/zst2.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - - // make sure ZST accesses are checked against being "truly" dangling pointers - // (into deallocated allocations). - let mut x_box = Box::new(1u8); - let x = &mut *x_box as *mut _ as *mut [u8; 0]; - drop(x_box); - unsafe { *x = zst_val }; //~ ERROR: has been freed -} diff --git a/src/tools/miri/tests/fail/zst2.stderr b/src/tools/miri/tests/fail/zst2.stderr deleted file mode 100644 index b3f65e7866dc8..0000000000000 --- a/src/tools/miri/tests/fail/zst2.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling - --> $DIR/zst2.rs:LL:CC - | -LL | unsafe { *x = zst_val }; - | ^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information -help: ALLOC was allocated here: - --> $DIR/zst2.rs:LL:CC - | -LL | let mut x_box = Box::new(1u8); - | ^^^^^^^^^^^^^ -help: ALLOC was deallocated here: - --> $DIR/zst2.rs:LL:CC - | -LL | drop(x_box); - | ^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/zst2.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/zst3.rs b/src/tools/miri/tests/fail/zst3.rs deleted file mode 100644 index 454bef25f2234..0000000000000 --- a/src/tools/miri/tests/fail/zst3.rs +++ /dev/null @@ -1,15 +0,0 @@ -fn main() { - // Not using the () type here, as writes of that type do not even have MIR generated. - // Also not assigning directly as that's array initialization, not assignment. - let zst_val = [1u8; 0]; - - // make sure ZST accesses are checked against being "truly" dangling pointers - // (that are out-of-bounds). - let mut x_box = Box::new(1u8); - let x = (&mut *x_box as *mut u8).wrapping_offset(1); - // This one is just "at the edge", but still okay - unsafe { *(x as *mut [u8; 0]) = zst_val }; - // One byte further is OOB. - let x = x.wrapping_offset(1); - unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR: out-of-bounds -} diff --git a/src/tools/miri/tests/fail/zst3.stderr b/src/tools/miri/tests/fail/zst3.stderr deleted file mode 100644 index b9495fbd2074f..0000000000000 --- a/src/tools/miri/tests/fail/zst3.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds - --> $DIR/zst3.rs:LL:CC - | -LL | unsafe { *(x as *mut [u8; 0]) = zst_val }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information -help: ALLOC was allocated here: - --> $DIR/zst3.rs:LL:CC - | -LL | let mut x_box = Box::new(1u8); - | ^^^^^^^^^^^^^ - = note: BACKTRACE (of the first span): - = note: inside `main` at $DIR/zst3.rs:LL:CC - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/fail/zst1.rs b/src/tools/miri/tests/fail/zst_local_oob.rs similarity index 100% rename from src/tools/miri/tests/fail/zst1.rs rename to src/tools/miri/tests/fail/zst_local_oob.rs diff --git a/src/tools/miri/tests/fail/zst1.stderr b/src/tools/miri/tests/fail/zst_local_oob.stderr similarity index 88% rename from src/tools/miri/tests/fail/zst1.stderr rename to src/tools/miri/tests/fail/zst_local_oob.stderr index cda837da7e71c..ba1ccaa0a3c8d 100644 --- a/src/tools/miri/tests/fail/zst1.stderr +++ b/src/tools/miri/tests/fail/zst_local_oob.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds - --> $DIR/zst1.rs:LL:CC + --> $DIR/zst_local_oob.rs:LL:CC | LL | let _val = unsafe { *x }; | ^^ memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds @@ -7,7 +7,7 @@ LL | let _val = unsafe { *x }; = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `main` at $DIR/zst1.rs:LL:CC + = note: inside `main` at $DIR/zst_local_oob.rs:LL:CC note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs index dec3d779a789a..9647277821fa1 100644 --- a/src/tools/miri/tests/pass/align_offset_symbolic.rs +++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs @@ -118,10 +118,9 @@ fn vtable() { let parts: (*const (), *const u8) = unsafe { mem::transmute(ptr) }; let vtable = parts.1; let offset = vtable.align_offset(mem::align_of::()); - let _vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0]; - // FIXME: we can't actually do the access since vtable pointers act like zero-sized allocations. - // Enable the next line once https://github.com/rust-lang/rust/issues/117945 is implemented. - //let _place = unsafe { &*vtable_aligned }; + let vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0]; + // Zero-sized deref, so no in-bounds requirement. + let _place = unsafe { &*vtable_aligned }; } fn main() { diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs new file mode 100644 index 0000000000000..2d142bef73c4a --- /dev/null +++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs @@ -0,0 +1,59 @@ +//! Tests specific for : zero-sized operations. +#![feature(strict_provenance)] + +use std::ptr; + +fn main() { + // Null. + test_ptr(ptr::null_mut::<()>()); + // No provenance. + test_ptr(ptr::without_provenance_mut::<()>(1)); + // Out-of-bounds. + let mut b = Box::new(0i32); + let ptr = ptr::addr_of_mut!(*b) as *mut (); + test_ptr(ptr.wrapping_byte_add(2)); + // Dangling (use-after-free). + drop(b); + test_ptr(ptr); +} + +fn test_ptr(ptr: *mut ()) { + unsafe { + // Reads and writes. + let mut val = *ptr; + *ptr = val; + ptr.read(); + ptr.write(()); + // Memory access intrinsics. + // - memcpy (1st and 2nd argument) + ptr.copy_from_nonoverlapping(&(), 1); + ptr.copy_to_nonoverlapping(&mut val, 1); + // - memmove (1st and 2nd argument) + ptr.copy_from(&(), 1); + ptr.copy_to(&mut val, 1); + // - memset + ptr.write_bytes(0u8, 1); + // Offset. + let _ = ptr.offset(0); + let _ = ptr.offset(1); // this is still 0 bytes + // Distance. + let ptr = ptr.cast::(); + ptr.offset_from(ptr); + /* + FIXME: this is disabled for now as these cases are not yet allowed. + // Distance from other "bad" pointers that have the same address, but different provenance. Some + // of this is library UB, but we don't want it to be language UB since that would violate + // provenance monotonicity: if we allow computing the distance between two ptrs with no + // provenance, we have to allow computing it between two ptrs with arbitrary provenance. + // - Distance from "no provenance" + ptr.offset_from(ptr::without_provenance_mut(ptr.addr())); + // - Distance from out-of-bounds pointer + let mut b = Box::new(0i32); + let other_ptr = ptr::addr_of_mut!(*b); + ptr.offset_from(other_ptr.with_addr(ptr.addr())); + // - Distance from use-after-free pointer + drop(b); + ptr.offset_from(other_ptr.with_addr(ptr.addr())); + */ + } +} diff --git a/tests/ui/const-ptr/forbidden_slices.rs b/tests/ui/const-ptr/forbidden_slices.rs index 85baeedf2215f..2550a3a01096b 100644 --- a/tests/ui/const-ptr/forbidden_slices.rs +++ b/tests/ui/const-ptr/forbidden_slices.rs @@ -12,7 +12,7 @@ use std::{ slice::{from_ptr_range, from_raw_parts}, }; -// Null is never valid for reads +// Null is never valid for references pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) }; //~^ ERROR: it is undefined behavior to use this value pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; @@ -46,10 +46,11 @@ pub static S8: &[u64] = unsafe { }; pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; -pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; +//~^ ERROR it is undefined behavior to use this value +pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore pub static R2: &[u32] = unsafe { let ptr = &D0 as *const u32; - from_ptr_range(ptr..ptr.add(2)) + from_ptr_range(ptr..ptr.add(2)) // errors inside libcore }; pub static R4: &[u8] = unsafe { //~^ ERROR: it is undefined behavior to use this value diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr index 250366da2a6ad..eb41a25c818a0 100644 --- a/tests/ui/const-ptr/forbidden_slices.stderr +++ b/tests/ui/const-ptr/forbidden_slices.stderr @@ -88,20 +88,16 @@ LL | pub static S8: &[u64] = unsafe { HEX_DUMP } -error[E0080]: could not evaluate static initializer - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | - = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::sub_ptr` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `from_ptr_range::<'_, u32>` - --> $SRC_DIR/core/src/slice/raw.rs:LL:COL -note: inside `R0` - --> $DIR/forbidden_slices.rs:48:34 +error[E0080]: it is undefined behavior to use this value + --> $DIR/forbidden_slices.rs:48:1 | LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference + | + = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -113,9 +109,9 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, ()>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R1` - --> $DIR/forbidden_slices.rs:49:33 + --> $DIR/forbidden_slices.rs:50:33 | -LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; +LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -127,13 +123,13 @@ error[E0080]: could not evaluate static initializer note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `R2` - --> $DIR/forbidden_slices.rs:52:25 + --> $DIR/forbidden_slices.rs:53:25 | -LL | from_ptr_range(ptr..ptr.add(2)) +LL | from_ptr_range(ptr..ptr.add(2)) // errors inside libcore | ^^^^^^^^^^ error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:54:1 + --> $DIR/forbidden_slices.rs:55:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized memory, but expected an integer @@ -144,7 +140,7 @@ LL | pub static R4: &[u8] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:59:1 + --> $DIR/forbidden_slices.rs:60:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered a pointer, but expected an integer @@ -157,7 +153,7 @@ LL | pub static R5: &[u8] = unsafe { = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:64:1 + --> $DIR/forbidden_slices.rs:65:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean @@ -168,7 +164,7 @@ LL | pub static R6: &[bool] = unsafe { } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:69:1 + --> $DIR/forbidden_slices.rs:70:1 | LL | pub static R7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) @@ -186,7 +182,7 @@ error[E0080]: could not evaluate static initializer note: inside `std::ptr::const_ptr::::add` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `R8` - --> $DIR/forbidden_slices.rs:76:25 + --> $DIR/forbidden_slices.rs:77:25 | LL | from_ptr_range(ptr..ptr.add(1)) | ^^^^^^^^^^ @@ -201,7 +197,7 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, u32>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R9` - --> $DIR/forbidden_slices.rs:81:34 + --> $DIR/forbidden_slices.rs:82:34 | LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -216,7 +212,7 @@ note: inside `std::ptr::const_ptr::::sub_ptr` note: inside `from_ptr_range::<'_, u32>` --> $SRC_DIR/core/src/slice/raw.rs:LL:COL note: inside `R10` - --> $DIR/forbidden_slices.rs:82:35 + --> $DIR/forbidden_slices.rs:83:35 | LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/consts/const-compare-bytes-ub.rs b/tests/ui/consts/const-compare-bytes-ub.rs index b357bab96a4df..903ba15e62291 100644 --- a/tests/ui/consts/const-compare-bytes-ub.rs +++ b/tests/ui/consts/const-compare-bytes-ub.rs @@ -7,11 +7,11 @@ use std::mem::MaybeUninit; fn main() { const LHS_NULL: i32 = unsafe { - compare_bytes(0 as *const u8, 2 as *const u8, 0) + compare_bytes(0 as *const u8, 2 as *const u8, 1) //~^ ERROR evaluation of constant value failed }; const RHS_NULL: i32 = unsafe { - compare_bytes(1 as *const u8, 0 as *const u8, 0) + compare_bytes(1 as *const u8, 0 as *const u8, 1) //~^ ERROR evaluation of constant value failed }; const DANGLING_PTR_NON_ZERO_LENGTH: i32 = unsafe { diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr index d8971eb9969f7..9e49706c4c836 100644 --- a/tests/ui/consts/const-compare-bytes-ub.stderr +++ b/tests/ui/consts/const-compare-bytes-ub.stderr @@ -1,14 +1,14 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:10:9 | -LL | compare_bytes(0 as *const u8, 2 as *const u8, 0) +LL | compare_bytes(0 as *const u8, 2 as *const u8, 1) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:14:9 | -LL | compare_bytes(1 as *const u8, 0 as *const u8, 0) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) +LL | compare_bytes(1 as *const u8, 0 as *const u8, 1) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x1[noalloc] is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed --> $DIR/const-compare-bytes-ub.rs:18:9 diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs index 94d7bdc6bae9f..4183dc0fcd6db 100644 --- a/tests/ui/consts/copy-intrinsic.rs +++ b/tests/ui/consts/copy-intrinsic.rs @@ -23,16 +23,20 @@ const COPY_ZERO: () = unsafe { const COPY_OOB_1: () = unsafe { let mut x = 0i32; let dangle = (&mut x as *mut i32).wrapping_add(10); - // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs. - copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR evaluation of constant value failed [E0080] - //~| pointer at offset 40 is out-of-bounds + // Zero-sized copy is fine. + copy_nonoverlapping(0x100 as *const i32, dangle, 0); + // Non-zero-sized copy is not. + copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080] + //~| 0x100[noalloc] is a dangling pointer }; const COPY_OOB_2: () = unsafe { let x = 0i32; let dangle = (&x as *const i32).wrapping_add(10); - // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs. - copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR evaluation of constant value failed [E0080] - //~| pointer at offset 40 is out-of-bounds + // Zero-sized copy is fine. + copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); + // Non-zero-sized copy is not. + copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080] + //~| offset 40 is out-of-bounds }; const COPY_SIZE_OVERFLOW: () = unsafe { diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr index 0e4e6a6ad6c43..d34e61cd96273 100644 --- a/tests/ui/consts/copy-intrinsic.stderr +++ b/tests/ui/consts/copy-intrinsic.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:27:5 + --> $DIR/copy-intrinsic.rs:29:5 | -LL | copy_nonoverlapping(0x100 as *const i32, dangle, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer at offset 40 is out-of-bounds +LL | copy_nonoverlapping(0x100 as *const i32, dangle, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x100[noalloc] is a dangling pointer (it has no provenance) error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:34:5 + --> $DIR/copy-intrinsic.rs:38:5 | -LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC1 has size 4, so pointer at offset 40 is out-of-bounds +LL | copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer to 4 bytes starting at offset 40 is out-of-bounds error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:41:5 + --> $DIR/copy-intrinsic.rs:45:5 | LL | copy(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy` error[E0080]: evaluation of constant value failed - --> $DIR/copy-intrinsic.rs:47:5 + --> $DIR/copy-intrinsic.rs:51:5 | LL | copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::() * 8 - 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping` diff --git a/tests/ui/consts/dangling-alloc-id-ice.rs b/tests/ui/consts/dangling-alloc-id-ice.rs index 76d6f33baf34b..6b07b8b3cc80d 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.rs +++ b/tests/ui/consts/dangling-alloc-id-ice.rs @@ -10,7 +10,7 @@ union Foo<'a> { } const FOO: &() = { - //~^ ERROR it is undefined behavior to use this value + //~^ ERROR encountered dangling pointer let y = (); unsafe { Foo { y: &y }.long_live_the_unit } }; diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr index 881c0b162edca..de31acf9fa441 100644 --- a/tests/ui/consts/dangling-alloc-id-ice.stderr +++ b/tests/ui/consts/dangling-alloc-id-ice.stderr @@ -1,14 +1,8 @@ -error[E0080]: it is undefined behavior to use this value +error: encountered dangling pointer in final value of constant --> $DIR/dangling-alloc-id-ice.rs:12:1 | LL | const FOO: &() = { - | ^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free) - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { - HEX_DUMP - } + | ^^^^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs index e0dd27079156f..57767e965962b 100644 --- a/tests/ui/consts/offset_from_ub.rs +++ b/tests/ui/consts/offset_from_ub.rs @@ -34,8 +34,8 @@ pub const NOT_MULTIPLE_OF_SIZE: isize = { pub const OFFSET_FROM_NULL: isize = { let ptr = 0 as *const u8; - unsafe { ptr_offset_from(ptr, ptr) } //~ERROR evaluation of constant value failed - //~| null pointer is a dangling pointer + // Null isn't special for zero-sized "accesses" (i.e., the range between the two pointers) + unsafe { ptr_offset_from(ptr, ptr) } }; pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC @@ -67,8 +67,8 @@ const OUT_OF_BOUNDS_SAME: isize = { let start_ptr = &4 as *const _ as *const u8; let length = 10; let end_ptr = (start_ptr).wrapping_add(length); - unsafe { ptr_offset_from(end_ptr, end_ptr) } //~ERROR evaluation of constant value failed - //~| pointer at offset 10 is out-of-bounds + // Out-of-bounds is fine as long as the range between the pointers is empty. + unsafe { ptr_offset_from(end_ptr, end_ptr) } }; pub const DIFFERENT_ALLOC_UNSIGNED: usize = { diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr index e3bac8d5e31ad..65f75a6e05875 100644 --- a/tests/ui/consts/offset_from_ub.stderr +++ b/tests/ui/consts/offset_from_ub.stderr @@ -23,12 +23,6 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder -error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:37:14 - | -LL | unsafe { ptr_offset_from(ptr, ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance) - error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:44:14 | @@ -47,12 +41,6 @@ error[E0080]: evaluation of constant value failed LL | unsafe { ptr_offset_from(start_ptr, end_ptr) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC1 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds -error[E0080]: evaluation of constant value failed - --> $DIR/offset_from_ub.rs:70:14 - | -LL | unsafe { ptr_offset_from(end_ptr, end_ptr) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC2 has size 4, so pointer at offset 10 is out-of-bounds - error[E0080]: evaluation of constant value failed --> $DIR/offset_from_ub.rs:79:14 | @@ -109,6 +97,6 @@ note: inside `OFFSET_VERY_FAR2` LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/offset_ub.rs b/tests/ui/consts/offset_ub.rs index 920ecb687cf5c..36e4ff1281a11 100644 --- a/tests/ui/consts/offset_ub.rs +++ b/tests/ui/consts/offset_ub.rs @@ -17,10 +17,10 @@ pub const NEGATIVE_OFFSET: *const u8 = unsafe { [0u8; 1].as_ptr().wrapping_offse pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; //~NOTE -// Right now, a zero offset from null is UB -pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; //~NOTE - // Make sure that we don't panic when computing abs(offset*size_of::()) pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE +// Offset-by-zero is allowed. +pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; + fn main() {} diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr index b398b20393fc4..89371f06d9d65 100644 --- a/tests/ui/consts/offset_ub.stderr +++ b/tests/ui/consts/offset_ub.stderr @@ -128,19 +128,6 @@ note: inside `DANGLING` LL | pub const DANGLING: *const u8 = unsafe { ptr::NonNull::::dangling().as_ptr().offset(4) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0080]: evaluation of constant value failed - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL - | - = note: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance) - | -note: inside `std::ptr::const_ptr::::offset` - --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL -note: inside `NULL_OFFSET_ZERO` - --> $DIR/offset_ub.rs:21:50 - | -LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::().offset(0) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error[E0080]: evaluation of constant value failed --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | @@ -149,11 +136,11 @@ error[E0080]: evaluation of constant value failed note: inside `std::ptr::const_ptr::::offset` --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL note: inside `UNDERFLOW_ABS` - --> $DIR/offset_ub.rs:24:47 + --> $DIR/offset_ub.rs:21:47 | LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0080`. From 2e97dae8d468623474d05dd84c270583ec3ed374 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 18 May 2024 12:40:36 -0400 Subject: [PATCH 09/31] An async closure may implement FnMut/Fn if it has no self-borrows --- compiler/rustc_middle/src/ty/sty.rs | 39 +++++++++++++++++++ .../src/solve/assembly/structural_traits.rs | 13 +++---- .../src/traits/select/candidate_assembly.rs | 15 ++----- .../async-closures/implements-fnmut.rs | 23 +++++++++++ 4 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 tests/ui/async-await/async-closures/implements-fnmut.rs diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 9dbcd938e6eda..6526bed5bdd63 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -401,6 +401,45 @@ impl<'tcx> CoroutineClosureArgs<'tcx> { pub fn coroutine_witness_ty(self) -> Ty<'tcx> { self.split().coroutine_witness_ty } + + pub fn has_self_borrows(&self) -> bool { + match self.coroutine_captures_by_ref_ty().kind() { + ty::FnPtr(sig) => sig + .skip_binder() + .visit_with(&mut HasRegionsBoundAt { binder: ty::INNERMOST }) + .is_break(), + ty::Error(_) => true, + _ => bug!(), + } + } +} +/// Unlike `has_escaping_bound_vars` or `outermost_exclusive_binder`, this will +/// detect only regions bound *at* the debruijn index. +struct HasRegionsBoundAt { + binder: ty::DebruijnIndex, +} +// FIXME: Could be optimized to not walk into components with no escaping bound vars. +impl<'tcx> TypeVisitor> for HasRegionsBoundAt { + type Result = ControlFlow<()>; + fn visit_binder>>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> Self::Result { + self.binder.shift_in(1); + t.super_visit_with(self)?; + self.binder.shift_out(1); + ControlFlow::Continue(()) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { + if let ty::ReBound(binder, _) = *r + && self.binder == binder + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } } #[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 90cc33e0275b6..f5e397f12d2da 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -300,14 +300,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( return Err(NoSolution); } - // If `Fn`/`FnMut`, we only implement this goal if we - // have no captures. - let no_borrows = match args.tupled_upvars_ty().kind() { - ty::Tuple(tys) => tys.is_empty(), - ty::Error(_) => false, - _ => bug!("tuple_fields called on non-tuple"), - }; - if closure_kind != ty::ClosureKind::FnOnce && !no_borrows { + // A coroutine-closure implements `FnOnce` *always*, since it may + // always be called once. It additionally implements `Fn`/`FnMut` + // only if it has no upvars referencing the closure-env lifetime, + // and if the closure kind permits it. + if closure_kind != ty::ClosureKind::FnOnce && args.has_self_borrows() { return Err(NoSolution); } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index b9e853a067874..fd7c47ad6fb3d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -418,20 +418,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Ambiguity if upvars haven't been constrained yet && !args.tupled_upvars_ty().is_ty_var() { - let no_borrows = match args.tupled_upvars_ty().kind() { - ty::Tuple(tys) => tys.is_empty(), - ty::Error(_) => false, - _ => bug!("tuple_fields called on non-tuple"), - }; // A coroutine-closure implements `FnOnce` *always*, since it may // always be called once. It additionally implements `Fn`/`FnMut` - // only if it has no upvars (therefore no borrows from the closure - // that would need to be represented with a lifetime) and if the - // closure kind permits it. - // FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut` - // if it takes all of its upvars by copy, and none by ref. This would - // require us to record a bit more information during upvar analysis. - if no_borrows && closure_kind.extends(kind) { + // only if it has no upvars referencing the closure-env lifetime, + // and if the closure kind permits it. + if closure_kind.extends(kind) && !args.has_self_borrows() { candidates.vec.push(ClosureCandidate { is_const }); } else if kind == ty::ClosureKind::FnOnce { candidates.vec.push(ClosureCandidate { is_const }); diff --git a/tests/ui/async-await/async-closures/implements-fnmut.rs b/tests/ui/async-await/async-closures/implements-fnmut.rs new file mode 100644 index 0000000000000..1ed326cd0618b --- /dev/null +++ b/tests/ui/async-await/async-closures/implements-fnmut.rs @@ -0,0 +1,23 @@ +//@ check-pass +//@ edition: 2021 + +// Demonstrates that an async closure may implement `FnMut` (not just `async FnMut`!) +// if it has no self-borrows. In this case, `&Ty` is not borrowed from the closure env, +// since it's fine to reborrow it with its original lifetime. See the doc comment on +// `should_reborrow_from_env_of_parent_coroutine_closure` for more detail for when we +// must borrow from the closure env. + +#![feature(async_closure)] + +fn main() {} + +fn needs_fn_mut(x: impl FnMut() -> T) {} + +fn hello(x: &Ty) { + needs_fn_mut(async || { x.hello(); }); +} + +struct Ty; +impl Ty { + fn hello(&self) {} +} From 6b3058204a91bb21e2aa845fedfab85a68f75b94 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 19 May 2024 23:45:25 -0400 Subject: [PATCH 10/31] Force the inner coroutine of an async closure to `move` if the outer closure is `move` and `FnOnce` --- compiler/rustc_hir_typeck/src/upvar.rs | 89 ++++++++++++------- .../force-move-due-to-actually-fnonce.rs | 27 ++++++ .../force-move-due-to-inferred-kind.rs | 24 +++++ 3 files changed, 109 insertions(+), 31 deletions(-) create mode 100644 tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs create mode 100644 tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 9d16f0d48159e..e29a410e2e543 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -204,6 +204,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fake_reads: Default::default(), }; + let _ = euv::ExprUseVisitor::new( + &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id), + &mut delegate, + ) + .consume_body(body); + + // There are several curious situations with coroutine-closures where + // analysis is too aggressive with borrows when the coroutine-closure is + // marked `move`. Specifically: + // + // 1. If the coroutine-closure was inferred to be `FnOnce` during signature + // inference, then it's still possible that we try to borrow upvars from + // the coroutine-closure because they are not used by the coroutine body + // in a way that forces a move. See the test: + // `async-await/async-closures/force-move-due-to-inferred-kind.rs`. + // + // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it + // uses its upvars, but not *all* upvars would force the closure to `FnOnce`. + // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`. + // + // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` + // coroutine bodies can't borrow from their parent closure. To fix this, + // we force the inner coroutine to also be `move`. This only matters for + // coroutine-closures that are `move` since otherwise they themselves will + // be borrowing from the outer environment, so there's no self-borrows occuring. + // + // One *important* note is that we do a call to `process_collected_capture_information` + // to eagerly test whether the coroutine would end up `FnOnce`, but we do this + // *before* capturing all the closure args by-value below, since that would always + // cause the analysis to return `FnOnce`. + if let UpvarArgs::Coroutine(..) = args + && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = + self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") + && let parent_hir_id = + self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id)) + && let parent_ty = self.node_ty(parent_hir_id) + && let hir::CaptureBy::Value { move_kw } = + self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause + { + // (1.) Closure signature inference forced this closure to `FnOnce`. + if let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) { + capture_clause = hir::CaptureBy::Value { move_kw }; + } + // (2.) The way that the closure uses its upvars means it's `FnOnce`. + else if let (_, ty::ClosureKind::FnOnce, _) = self + .process_collected_capture_information( + capture_clause, + &delegate.capture_information, + ) + { + capture_clause = hir::CaptureBy::Value { move_kw }; + } + } + // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode // to `ByRef` for the `async {}` block internal to async fns/closure. This means // that we would *not* be moving all of the parameters into the async block by default. @@ -253,34 +307,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let _ = euv::ExprUseVisitor::new( - &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id), - &mut delegate, - ) - .consume_body(body); - - // If a coroutine is comes from a coroutine-closure that is `move`, but - // the coroutine-closure was inferred to be `FnOnce` during signature - // inference, then it's still possible that we try to borrow upvars from - // the coroutine-closure because they are not used by the coroutine body - // in a way that forces a move. - // - // This would lead to an impossible to satisfy situation, since `AsyncFnOnce` - // coroutine bodies can't borrow from their parent closure. To fix this, - // we force the inner coroutine to also be `move`. This only matters for - // coroutine-closures that are `move` since otherwise they themselves will - // be borrowing from the outer environment, so there's no self-borrows occuring. - if let UpvarArgs::Coroutine(..) = args - && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) = - self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind") - && let parent_hir_id = - self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id)) - && let parent_ty = self.node_ty(parent_hir_id) - && let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) - { - capture_clause = self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause; - } - debug!( "For closure={:?}, capture_information={:#?}", closure_def_id, delegate.capture_information @@ -289,7 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); let (capture_information, closure_kind, origin) = self - .process_collected_capture_information(capture_clause, delegate.capture_information); + .process_collected_capture_information(capture_clause, &delegate.capture_information); self.compute_min_captures(closure_def_id, capture_information, span); @@ -545,13 +571,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn process_collected_capture_information( &self, capture_clause: hir::CaptureBy, - capture_information: InferredCaptureInformation<'tcx>, + capture_information: &InferredCaptureInformation<'tcx>, ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) { let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM; let mut origin: Option<(Span, Place<'tcx>)> = None; let processed = capture_information - .into_iter() + .iter() + .cloned() .map(|(place, mut capture_info)| { // Apply rules for safety before inferring closure kind let (place, capture_kind) = diff --git a/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs new file mode 100644 index 0000000000000..ce49f55e3e304 --- /dev/null +++ b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs @@ -0,0 +1,27 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ check-pass + +#![feature(async_closure)] + +extern crate block_on; + +fn consume(_: String) {} + +fn main() { + block_on::block_on(async { + let x = 1i32; + let s = String::new(); + // `consume(s)` pulls the closure's kind down to `FnOnce`, + // which means that we don't treat the borrow of `x` as a + // self-borrow (with `'env` lifetime). This leads to a lifetime + // error which is solved by forcing the inner coroutine to + // be `move` as well, so that it moves `x`. + let c = async move || { + println!("{x}"); + // This makes the closure FnOnce... + consume(s); + }; + c().await; + }); +} diff --git a/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs new file mode 100644 index 0000000000000..803c990ef93f1 --- /dev/null +++ b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs @@ -0,0 +1,24 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ check-pass + +#![feature(async_closure)] + +extern crate block_on; + +fn force_fnonce(t: T) -> T { t } + +fn main() { + block_on::block_on(async { + let x = 1i32; + // `force_fnonce` pulls the closure's kind down to `FnOnce`, + // which means that we don't treat the borrow of `x` as a + // self-borrow (with `'env` lifetime). This leads to a lifetime + // error which is solved by forcing the inner coroutine to + // be `move` as well, so that it moves `x`. + let c = force_fnonce(async move || { + println!("{x}"); + }); + c().await; + }); +} From 8f11f48a027888b71efbb1769b432606e824e577 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Tue, 21 May 2024 10:54:36 +0200 Subject: [PATCH 11/31] Use Backtrace::force_capture instead of Backtrace::capture in rustc_log --- compiler/rustc_log/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index 81257f9be8823..e11c45b66e6df 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -159,7 +159,9 @@ where if !target.contains(&self.backtrace_target) { return Ok(()); } - let backtrace = std::backtrace::Backtrace::capture(); + // Use Backtrace::force_capture because we don't want to depend on the + // RUST_BACKTRACE environment variable being set. + let backtrace = std::backtrace::Backtrace::force_capture(); writeln!(writer, "stack backtrace: \n{backtrace:?}") } } From 63fe640f5d2eb402247906e22b8bf14bf8e90b61 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 19 May 2024 23:08:59 +0000 Subject: [PATCH 12/31] Update check-cfg lists for core --- library/core/Cargo.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 11d33971f2563..0c86f430f3250 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -36,3 +36,12 @@ optimize_for_size = [] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = [] + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(no_fp_fmt_parse)', + 'cfg(bootstrap)', + 'cfg(stdarch_intel_sde)', + 'cfg(feature, values("all_lane_counts"))', +] From 0734ae22f5e821023155e2df24628a1c6df54f2e Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 19 May 2024 23:14:48 +0000 Subject: [PATCH 13/31] Update check-cfg lists for alloc --- library/alloc/Cargo.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 2e7fcb9dbd35f..cddf87a92c502 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -40,3 +40,12 @@ compiler-builtins-weak-intrinsics = ["compiler_builtins/weak-intrinsics"] panic_immediate_abort = ["core/panic_immediate_abort"] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size"] + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(no_global_oom_handling)', + 'cfg(bootstrap)', + 'cfg(no_rc)', + 'cfg(no_sync)', +] From 73602bf4088b31daa0da10d3ea926e1145faa5d5 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Sun, 19 May 2024 23:27:41 +0000 Subject: [PATCH 14/31] Update check-cfg lists for std --- library/std/Cargo.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 5b36867fe2436..4b8ee4c130918 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -97,3 +97,13 @@ heap_size = 0x8000000 name = "stdbenches" path = "benches/lib.rs" test = true + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(bootstrap)', + 'cfg(backtrace_in_libstd)', + 'cfg(netbsd10)', + 'cfg(target_arch, values("xtensa"))', + 'cfg(feature, values("std", "as_crate"))', +] From c7d2f4592fb19a8c3d7ae5e6a1594edb4608c75f Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 20 May 2024 15:06:14 +0000 Subject: [PATCH 15/31] addresss reviews --- library/alloc/Cargo.toml | 5 ++++- library/core/Cargo.toml | 6 +++++- src/bootstrap/src/lib.rs | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index cddf87a92c502..3960f71681264 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -43,9 +43,12 @@ optimize_for_size = ["core/optimize_for_size"] [lints.rust.unexpected_cfgs] level = "warn" +# x.py uses beta cargo, so `check-cfg` entries do not yet take effect +# for rust-lang/rust. But for users of `-Zbuild-std` it does. +# The unused warning is waiting for rust-lang/cargo#13925 to reach beta. check-cfg = [ - 'cfg(no_global_oom_handling)', 'cfg(bootstrap)', + 'cfg(no_global_oom_handling)', 'cfg(no_rc)', 'cfg(no_sync)', ] diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 0c86f430f3250..756f68e604805 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -39,9 +39,13 @@ debug_refcell = [] [lints.rust.unexpected_cfgs] level = "warn" +# x.py uses beta cargo, so `check-cfg` entries do not yet take effect +# for rust-lang/rust. But for users of `-Zbuild-std` it does. +# The unused warning is waiting for rust-lang/cargo#13925 to reach beta. check-cfg = [ 'cfg(no_fp_fmt_parse)', 'cfg(bootstrap)', 'cfg(stdarch_intel_sde)', - 'cfg(feature, values("all_lane_counts"))', + # This matches `EXTRA_CHECK_CFGS` in `src/bootstrap/src/lib.rs`. + 'cfg(feature, values(any()))', ] diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 698a576effa63..0578638de5ce2 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -84,6 +84,7 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (Some(Mode::ToolRustc), "rust_analyzer", None), (Some(Mode::ToolStd), "rust_analyzer", None), (Some(Mode::Codegen), "parallel_compiler", None), + // NOTE: consider updating `check-cfg` entries in `std/Cargo.toml` too. (Some(Mode::Std), "stdarch_intel_sde", None), (Some(Mode::Std), "no_fp_fmt_parse", None), (Some(Mode::Std), "no_global_oom_handling", None), From df3a32066fe4d9a1c8f922d7abfa5f76578a876a Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 20 May 2024 15:08:28 +0000 Subject: [PATCH 16/31] tidy alphabetica --- library/core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 756f68e604805..daf2612833ddc 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -43,8 +43,8 @@ level = "warn" # for rust-lang/rust. But for users of `-Zbuild-std` it does. # The unused warning is waiting for rust-lang/cargo#13925 to reach beta. check-cfg = [ - 'cfg(no_fp_fmt_parse)', 'cfg(bootstrap)', + 'cfg(no_fp_fmt_parse)', 'cfg(stdarch_intel_sde)', # This matches `EXTRA_CHECK_CFGS` in `src/bootstrap/src/lib.rs`. 'cfg(feature, values(any()))', From 30e0ab84f985d4da0f9eb802e047f5462f4a8a65 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Tue, 21 May 2024 09:30:43 +0000 Subject: [PATCH 17/31] maybe replace check-cfg values in bootstrap with ones in Cargo.toml --- src/bootstrap/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 0578638de5ce2..38de5e3800005 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -85,6 +85,8 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (Some(Mode::ToolStd), "rust_analyzer", None), (Some(Mode::Codegen), "parallel_compiler", None), // NOTE: consider updating `check-cfg` entries in `std/Cargo.toml` too. + // cfg(bootstrap) remove these once the bootstrap compiler supports + // `lints.rust.unexpected_cfgs.check-cfg` (Some(Mode::Std), "stdarch_intel_sde", None), (Some(Mode::Std), "no_fp_fmt_parse", None), (Some(Mode::Std), "no_global_oom_handling", None), From 9526ce60fd5bb46221f4a0c35dda694aa14716b0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 May 2024 16:31:57 +0200 Subject: [PATCH 18/31] improve comment wording --- compiler/rustc_const_eval/src/interpret/validity.rs | 3 ++- library/core/src/intrinsics.rs | 4 ++-- library/core/src/ptr/const_ptr.rs | 12 ++++++------ library/core/src/ptr/mut_ptr.rs | 12 ++++++------ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index a47828bb63c25..e36d30184c858 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -434,7 +434,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' found_bytes: has.bytes() }, ); - // Make sure this is non-null. (ZST references can be dereferenceable and null.) + // Make sure this is non-null. We checked dereferenceability above, but if `size` is zero + // that does not imply non-null. if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? { throw_validation_failure!(self.path, NullPtr { ptr_kind }) } diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 90a7e2bf382e5..33ecaae98e518 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1484,7 +1484,7 @@ extern "rust-intrinsic" { /// # Safety /// /// If the computed offset is non-zero, then both the starting and resulting pointer must be - /// either in bounds or one byte past the end of an allocated object. If either pointer is out + /// either in bounds or at the end of an allocated object. If either pointer is out /// of bounds or arithmetic overflow occurs then any further use of the returned value will /// result in undefined behavior. /// @@ -1502,7 +1502,7 @@ extern "rust-intrinsic" { /// # Safety /// /// Unlike the `offset` intrinsic, this intrinsic does not restrict the - /// resulting pointer to point into or one byte past the end of an allocated + /// resulting pointer to point into or at the end of an allocated /// object, and it wraps with two's complement arithmetic. The resulting /// value is not necessarily valid to be used to actually access memory. /// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 27b0c69d32d0f..c8065b2e70906 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -465,8 +465,8 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * If the computed offset is non-zero, then both the starting and resulting pointer must be - /// either in bounds or one byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. @@ -952,8 +952,8 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * If the computed offset is non-zero, then both the starting and resulting pointer must be - /// either in bounds or one byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. @@ -1037,8 +1037,8 @@ impl *const T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * If the computed offset is non-zero, then both the starting and resulting pointer must be - /// either in bounds or one byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 3d05715f7a46d..f87496bd400f8 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -480,8 +480,8 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * If the computed offset is non-zero, then both the starting and resulting pointer must be - /// either in bounds or one byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. @@ -1096,8 +1096,8 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * If the computed offset is non-zero, then both the starting and resulting pointer must be - /// either in bounds or one byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset, **in bytes**, cannot overflow an `isize`. @@ -1181,8 +1181,8 @@ impl *mut T { /// If any of the following conditions are violated, the result is Undefined /// Behavior: /// - /// * If the computed offset is non-zero, then both the starting and resulting pointer must be - /// either in bounds or one byte past the end of the same [allocated object]. + /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting + /// pointer must be either in bounds or at the end of the same [allocated object]. /// (If it is zero, then the function is always well-defined.) /// /// * The computed offset cannot exceed `isize::MAX` **bytes**. From 4486c24db3ca1c698b34ea08bee15194774b53df Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 21 May 2024 12:28:30 -0700 Subject: [PATCH 19/31] Add URL and crate_name to test cases --- tests/rustdoc/issue-80233-normalize-auto-trait.rs | 4 +++- .../issue-81141-private-reexport-in-public-api-2.rs | 1 + ...ssue-81141-private-reexport-in-public-api-generics.rs | 1 + .../issue-81141-private-reexport-in-public-api-hidden.rs | 1 + ...issue-81141-private-reexport-in-public-api-private.rs | 1 + .../issue-81141-private-reexport-in-public-api.rs | 1 + tests/rustdoc/issue-82465-asref-for-and-of-local.rs | 5 ++++- ...375-multiple-mods-w-same-name-doc-inline-last-item.rs | 1 + .../issue-83375-multiple-mods-w-same-name-doc-inline.rs | 1 + tests/rustdoc/issue-85454.rs | 1 + tests/rustdoc/issue-86620.rs | 6 ++++-- tests/rustdoc/issue-88600.rs | 4 +++- tests/rustdoc/issue-89309-heading-levels.rs | 1 + tests/rustdoc/issue-89852.rs | 6 ++++-- .../rustdoc/issue-94183-blanket-impl-reexported-trait.rs | 1 + tests/rustdoc/issue-95633.rs | 1 + tests/rustdoc/issue-95873.rs | 5 ++++- tests/rustdoc/issue-96381.rs | 1 + tests/rustdoc/issue-98697.rs | 9 +++++---- ...e-99221-multiple-macro-rules-w-same-name-submodule.rs | 1 + 20 files changed, 40 insertions(+), 12 deletions(-) diff --git a/tests/rustdoc/issue-80233-normalize-auto-trait.rs b/tests/rustdoc/issue-80233-normalize-auto-trait.rs index 62fbc2444dbc2..0649801863077 100644 --- a/tests/rustdoc/issue-80233-normalize-auto-trait.rs +++ b/tests/rustdoc/issue-80233-normalize-auto-trait.rs @@ -1,7 +1,9 @@ // Regression test for issue #80233 // Tests that we don't ICE when processing auto traits +// https://github.com/rust-lang/rust/issues/80233 #![crate_type = "lib"] +#![crate_name = "foo"] pub trait Trait1 {} pub trait Trait2 { @@ -30,7 +32,7 @@ impl Trait3 for Vec { pub struct Struct1 {} -// @has issue_80233_normalize_auto_trait/struct.Question.html +// @has foo/struct.Question.html // @has - '//h3[@class="code-header"]' 'impl Send for Question' pub struct Question { pub ins: < as Trait3>::Type3 as Trait2>::Type2, diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs index fba310cec6d6b..c066f54b32b20 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs @@ -1,5 +1,6 @@ //@ edition:2015 +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use external::Public as Private; diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs index 7e289508628f5..1c86c769a124f 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use crate::bar::Foo as Alias; diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs index 388f69ba3265a..7d6fadf26e2f2 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Z unstable-options --document-hidden-items +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] #[doc(hidden)] diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs index 2633f98c4f303..6bf507838d55d 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs @@ -1,5 +1,6 @@ //@ compile-flags: --document-private-items +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use crate::bar::Bar as Alias; diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs index bd54d02c6ec8f..d695ed7fbfac2 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs @@ -1,6 +1,7 @@ // This test ensures that if a private re-export is present in a public API, it'll be // replaced by the first public item in the re-export chain or by the private item. +// https://github.com/rust-lang/rust/issues/81141 #![crate_name = "foo"] use crate::bar::Bar as Alias; diff --git a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs b/tests/rustdoc/issue-82465-asref-for-and-of-local.rs index adf4d111a6cb9..e620468890435 100644 --- a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs +++ b/tests/rustdoc/issue-82465-asref-for-and-of-local.rs @@ -1,7 +1,10 @@ +// https://github.com/rust-lang/rust/issues/82465 +#![crate_name = "foo"] + use std::convert::AsRef; pub struct Local; -// @has issue_82465_asref_for_and_of_local/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef for Local' +// @has foo/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef for Local' impl AsRef for Local { fn as_ref(&self) -> &str { todo!() diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs index 9bce25846d858..7bad825b35fe4 100644 --- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs +++ b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/83375 #![crate_name = "foo"] pub mod sub { diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs index d0960dfef4362..9b3dfd45370b1 100644 --- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs +++ b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/83375 #![crate_name = "foo"] pub mod sub { diff --git a/tests/rustdoc/issue-85454.rs b/tests/rustdoc/issue-85454.rs index 790db0c5dcfef..de806db77094d 100644 --- a/tests/rustdoc/issue-85454.rs +++ b/tests/rustdoc/issue-85454.rs @@ -1,6 +1,7 @@ //@ aux-build:issue-85454.rs //@ build-aux-docs #![crate_name = "foo"] +// https://github.com/rust-lang/rust/issues/85454 extern crate issue_85454; diff --git a/tests/rustdoc/issue-86620.rs b/tests/rustdoc/issue-86620.rs index a7ac0f1d291b8..537dadd212419 100644 --- a/tests/rustdoc/issue-86620.rs +++ b/tests/rustdoc/issue-86620.rs @@ -1,9 +1,11 @@ //@ aux-build:issue-86620-1.rs +#![crate_name = "foo"] +// https://github.com/rust-lang/rust/issues/86620 extern crate issue_86620_1; use issue_86620_1::*; -// @!has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip -// @has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip +// @!has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip +// @has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip pub struct S; diff --git a/tests/rustdoc/issue-88600.rs b/tests/rustdoc/issue-88600.rs index f89af472f6e42..31d96e9db758b 100644 --- a/tests/rustdoc/issue-88600.rs +++ b/tests/rustdoc/issue-88600.rs @@ -1,4 +1,6 @@ // This test ensure that #[doc(hidden)] is applied correctly in enum variant fields. +// https://github.com/rust-lang/rust/issues/88600 +#![crate_name = "foo"] // Denotes a field which should be hidden. pub struct H; @@ -6,7 +8,7 @@ pub struct H; // Denotes a field which should not be hidden (shown). pub struct S; -// @has issue_88600/enum.FooEnum.html +// @has foo/enum.FooEnum.html pub enum FooEnum { // @has - '//*[@id="variant.HiddenTupleItem"]//h3' 'HiddenTupleItem(/* private fields */)' // @count - '//*[@id="variant.HiddenTupleItem.field.0"]' 0 diff --git a/tests/rustdoc/issue-89309-heading-levels.rs b/tests/rustdoc/issue-89309-heading-levels.rs index bb706c28ffa51..caa994285252f 100644 --- a/tests/rustdoc/issue-89309-heading-levels.rs +++ b/tests/rustdoc/issue-89309-heading-levels.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/89309 #![crate_name = "foo"] // @has foo/trait.Read.html diff --git a/tests/rustdoc/issue-89852.rs b/tests/rustdoc/issue-89852.rs index e9b3d80c92ee8..cffe1289ce823 100644 --- a/tests/rustdoc/issue-89852.rs +++ b/tests/rustdoc/issue-89852.rs @@ -1,10 +1,12 @@ //@ edition:2018 +// https://github.com/rust-lang/rust/issues/89852 +#![crate_name = "foo"] #![no_core] #![feature(no_core)] -// @matchesraw 'issue_89852/sidebar-items.js' '"repro"' -// @!matchesraw 'issue_89852/sidebar-items.js' '"repro".*"repro"' +// @matchesraw 'foo/sidebar-items.js' '"repro"' +// @!matchesraw 'foo/sidebar-items.js' '"repro".*"repro"' #[macro_export] macro_rules! repro { diff --git a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs b/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs index 95ddd4c74715a..343e030da9e2b 100644 --- a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs +++ b/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs @@ -2,6 +2,7 @@ // This test ensures that a publicly re-exported private trait will // appear in the blanket impl list. +// https://github.com/rust-lang/rust/issues/94183 #![crate_name = "foo"] // @has 'foo/struct.S.html' diff --git a/tests/rustdoc/issue-95633.rs b/tests/rustdoc/issue-95633.rs index 5695ef579f2b5..7f612a0b60ae4 100644 --- a/tests/rustdoc/issue-95633.rs +++ b/tests/rustdoc/issue-95633.rs @@ -1,6 +1,7 @@ //@ compile-flags: --document-private-items // This ensures that no ICE is triggered when rustdoc is run on this code. +// https://github.com/rust-lang/rust/issues/95633 mod stdlib { pub (crate) use std::i8; diff --git a/tests/rustdoc/issue-95873.rs b/tests/rustdoc/issue-95873.rs index 83f1f2f75bf02..5a817fb3409e9 100644 --- a/tests/rustdoc/issue-95873.rs +++ b/tests/rustdoc/issue-95873.rs @@ -1,2 +1,5 @@ -// @has issue_95873/index.html "//*[@class='item-name']" "pub use ::std as x;" +// https://github.com/rust-lang/rust/issues/95873 +#![crate_name = "foo"] + +// @has foo/index.html "//*[@class='item-name']" "pub use ::std as x;" pub use ::std as x; diff --git a/tests/rustdoc/issue-96381.rs b/tests/rustdoc/issue-96381.rs index 90875c076057d..6d6e5e0a8398b 100644 --- a/tests/rustdoc/issue-96381.rs +++ b/tests/rustdoc/issue-96381.rs @@ -1,4 +1,5 @@ //@ should-fail +// https://github.com/rust-lang/rust/issues/96381 #![allow(unused)] diff --git a/tests/rustdoc/issue-98697.rs b/tests/rustdoc/issue-98697.rs index df9f291511134..545af512d3d02 100644 --- a/tests/rustdoc/issue-98697.rs +++ b/tests/rustdoc/issue-98697.rs @@ -1,5 +1,6 @@ //@ aux-build:issue-98697-reexport-with-anonymous-lifetime.rs //@ ignore-cross-compile +#![crate_name = "foo"] // When reexporting a function with a HRTB with anonymous lifetimes, // make sure the anonymous lifetimes are not rendered. @@ -8,10 +9,10 @@ extern crate issue_98697_reexport_with_anonymous_lifetime; -// @has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro()where F: Fn(&str)' -// @!has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<' +// @has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro()where F: Fn(&str)' +// @!has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<' pub use issue_98697_reexport_with_anonymous_lifetime::repro; -// @has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra' -// @!has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<' +// @has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra' +// @!has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<' pub use issue_98697_reexport_with_anonymous_lifetime::Extra; diff --git a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs b/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs index d3ccd1c069b6e..ed1e42c1f4eeb 100644 --- a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs +++ b/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs @@ -2,6 +2,7 @@ //@ build-aux-docs //@ ignore-cross-compile +// https://github.com/rust-lang/rust/issues/99221 #![crate_name = "foo"] #[macro_use] From 55bd054a21533cbaa02836b0ca125c2eaaa4b8f4 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 21 May 2024 12:28:34 -0700 Subject: [PATCH 20/31] rustdoc: rename `issue-\d+.rs` tests to have meaningful names --- ...-asref-for-and-of-local.rs => asref-for-and-of-local-82465.rs} | 0 ...reexported-trait.rs => blanket-impl-reexported-trait-94183.rs} | 0 .../{issue-88600.rs => enum-variant-doc-hidden-field-88600.rs} | 0 .../{issue-89309-heading-levels.rs => heading-levels-89309.rs} | 0 .../{issue-86620.rs => method-anchor-in-blanket-impl-86620.rs} | 0 ...ule.rs => multiple-macro-rules-w-same-name-submodule-99221.rs} | 0 ...oc-inline.rs => multiple-mods-w-same-name-doc-inline-83375.rs} | 0 ...rs => multiple-mods-w-same-name-doc-inline-last-item-83375.rs} | 0 ...0233-normalize-auto-trait.rs => normalize-auto-trait-80233.rs} | 0 ...ort-in-public-api.rs => private-export-in-public-api-81141.rs} | 0 ...-public-api-2.rs => private-reexport-in-public-api-81141-2.rs} | 0 ...nerics.rs => private-reexport-in-public-api-generics-81141.rs} | 0 ...i-hidden.rs => private-reexport-in-public-api-hidden-81141.rs} | 0 ...private.rs => private-reexport-in-public-api-private-81141.rs} | 0 tests/rustdoc/{issue-89852.rs => pub-use-exported-macro-89852.rs} | 0 ...95633.rs => pub-use-primitive-document-private-items-95633.rs} | 0 tests/rustdoc/{issue-95873.rs => pub-use-root-path-95873.rs} | 0 tests/rustdoc/{issue-85454.rs => qpath-self-85454.rs} | 0 .../{issue-98697.rs => reexport-with-anonymous-lifetime-98697.rs} | 0 .../{issue-96381.rs => underscore-type-in-trait-impl-96381.rs} | 0 20 files changed, 0 insertions(+), 0 deletions(-) rename tests/rustdoc/{issue-82465-asref-for-and-of-local.rs => asref-for-and-of-local-82465.rs} (100%) rename tests/rustdoc/{issue-94183-blanket-impl-reexported-trait.rs => blanket-impl-reexported-trait-94183.rs} (100%) rename tests/rustdoc/{issue-88600.rs => enum-variant-doc-hidden-field-88600.rs} (100%) rename tests/rustdoc/{issue-89309-heading-levels.rs => heading-levels-89309.rs} (100%) rename tests/rustdoc/{issue-86620.rs => method-anchor-in-blanket-impl-86620.rs} (100%) rename tests/rustdoc/{issue-99221-multiple-macro-rules-w-same-name-submodule.rs => multiple-macro-rules-w-same-name-submodule-99221.rs} (100%) rename tests/rustdoc/{issue-83375-multiple-mods-w-same-name-doc-inline.rs => multiple-mods-w-same-name-doc-inline-83375.rs} (100%) rename tests/rustdoc/{issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs => multiple-mods-w-same-name-doc-inline-last-item-83375.rs} (100%) rename tests/rustdoc/{issue-80233-normalize-auto-trait.rs => normalize-auto-trait-80233.rs} (100%) rename tests/rustdoc/{issue-81141-private-reexport-in-public-api.rs => private-export-in-public-api-81141.rs} (100%) rename tests/rustdoc/{issue-81141-private-reexport-in-public-api-2.rs => private-reexport-in-public-api-81141-2.rs} (100%) rename tests/rustdoc/{issue-81141-private-reexport-in-public-api-generics.rs => private-reexport-in-public-api-generics-81141.rs} (100%) rename tests/rustdoc/{issue-81141-private-reexport-in-public-api-hidden.rs => private-reexport-in-public-api-hidden-81141.rs} (100%) rename tests/rustdoc/{issue-81141-private-reexport-in-public-api-private.rs => private-reexport-in-public-api-private-81141.rs} (100%) rename tests/rustdoc/{issue-89852.rs => pub-use-exported-macro-89852.rs} (100%) rename tests/rustdoc/{issue-95633.rs => pub-use-primitive-document-private-items-95633.rs} (100%) rename tests/rustdoc/{issue-95873.rs => pub-use-root-path-95873.rs} (100%) rename tests/rustdoc/{issue-85454.rs => qpath-self-85454.rs} (100%) rename tests/rustdoc/{issue-98697.rs => reexport-with-anonymous-lifetime-98697.rs} (100%) rename tests/rustdoc/{issue-96381.rs => underscore-type-in-trait-impl-96381.rs} (100%) diff --git a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs b/tests/rustdoc/asref-for-and-of-local-82465.rs similarity index 100% rename from tests/rustdoc/issue-82465-asref-for-and-of-local.rs rename to tests/rustdoc/asref-for-and-of-local-82465.rs diff --git a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs b/tests/rustdoc/blanket-impl-reexported-trait-94183.rs similarity index 100% rename from tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs rename to tests/rustdoc/blanket-impl-reexported-trait-94183.rs diff --git a/tests/rustdoc/issue-88600.rs b/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs similarity index 100% rename from tests/rustdoc/issue-88600.rs rename to tests/rustdoc/enum-variant-doc-hidden-field-88600.rs diff --git a/tests/rustdoc/issue-89309-heading-levels.rs b/tests/rustdoc/heading-levels-89309.rs similarity index 100% rename from tests/rustdoc/issue-89309-heading-levels.rs rename to tests/rustdoc/heading-levels-89309.rs diff --git a/tests/rustdoc/issue-86620.rs b/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs similarity index 100% rename from tests/rustdoc/issue-86620.rs rename to tests/rustdoc/method-anchor-in-blanket-impl-86620.rs diff --git a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs b/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs similarity index 100% rename from tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs rename to tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs similarity index 100% rename from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs rename to tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs similarity index 100% rename from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs rename to tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs diff --git a/tests/rustdoc/issue-80233-normalize-auto-trait.rs b/tests/rustdoc/normalize-auto-trait-80233.rs similarity index 100% rename from tests/rustdoc/issue-80233-normalize-auto-trait.rs rename to tests/rustdoc/normalize-auto-trait-80233.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/private-export-in-public-api-81141.rs similarity index 100% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api.rs rename to tests/rustdoc/private-export-in-public-api-81141.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs b/tests/rustdoc/private-reexport-in-public-api-81141-2.rs similarity index 100% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs rename to tests/rustdoc/private-reexport-in-public-api-81141-2.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs b/tests/rustdoc/private-reexport-in-public-api-generics-81141.rs similarity index 100% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs rename to tests/rustdoc/private-reexport-in-public-api-generics-81141.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs b/tests/rustdoc/private-reexport-in-public-api-hidden-81141.rs similarity index 100% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs rename to tests/rustdoc/private-reexport-in-public-api-hidden-81141.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs b/tests/rustdoc/private-reexport-in-public-api-private-81141.rs similarity index 100% rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs rename to tests/rustdoc/private-reexport-in-public-api-private-81141.rs diff --git a/tests/rustdoc/issue-89852.rs b/tests/rustdoc/pub-use-exported-macro-89852.rs similarity index 100% rename from tests/rustdoc/issue-89852.rs rename to tests/rustdoc/pub-use-exported-macro-89852.rs diff --git a/tests/rustdoc/issue-95633.rs b/tests/rustdoc/pub-use-primitive-document-private-items-95633.rs similarity index 100% rename from tests/rustdoc/issue-95633.rs rename to tests/rustdoc/pub-use-primitive-document-private-items-95633.rs diff --git a/tests/rustdoc/issue-95873.rs b/tests/rustdoc/pub-use-root-path-95873.rs similarity index 100% rename from tests/rustdoc/issue-95873.rs rename to tests/rustdoc/pub-use-root-path-95873.rs diff --git a/tests/rustdoc/issue-85454.rs b/tests/rustdoc/qpath-self-85454.rs similarity index 100% rename from tests/rustdoc/issue-85454.rs rename to tests/rustdoc/qpath-self-85454.rs diff --git a/tests/rustdoc/issue-98697.rs b/tests/rustdoc/reexport-with-anonymous-lifetime-98697.rs similarity index 100% rename from tests/rustdoc/issue-98697.rs rename to tests/rustdoc/reexport-with-anonymous-lifetime-98697.rs diff --git a/tests/rustdoc/issue-96381.rs b/tests/rustdoc/underscore-type-in-trait-impl-96381.rs similarity index 100% rename from tests/rustdoc/issue-96381.rs rename to tests/rustdoc/underscore-type-in-trait-impl-96381.rs From 28ce5883210da1ed90cda9b7da3d0b16e2794e69 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 20 May 2024 12:57:07 -0400 Subject: [PATCH 21/31] Uplift binder --- compiler/rustc_errors/src/diagnostic_impls.rs | 9 + .../src/hir_ty_lowering/errors.rs | 2 +- .../src/hir_ty_lowering/mod.rs | 1 + compiler/rustc_middle/src/ty/codec.rs | 58 +-- compiler/rustc_middle/src/ty/context.rs | 13 +- compiler/rustc_middle/src/ty/generic_args.rs | 8 + compiler/rustc_middle/src/ty/predicate.rs | 153 +------- compiler/rustc_middle/src/ty/print/pretty.rs | 19 +- compiler/rustc_middle/src/ty/region.rs | 4 + .../rustc_middle/src/ty/structural_impls.rs | 32 -- compiler/rustc_middle/src/ty/sty.rs | 257 +------------ compiler/rustc_middle/src/ty/visit.rs | 98 ----- .../src/canonicalizer.rs | 6 +- .../src/traits/error_reporting/suggestions.rs | 3 +- compiler/rustc_type_ir/src/binder.rs | 340 ++++++++++++++++++ compiler/rustc_type_ir/src/fold.rs | 13 +- compiler/rustc_type_ir/src/inherent.rs | 87 +++-- compiler/rustc_type_ir/src/interner.rs | 42 ++- compiler/rustc_type_ir/src/ir_print.rs | 11 +- compiler/rustc_type_ir/src/lib.rs | 10 + compiler/rustc_type_ir/src/predicate.rs | 147 +++++++- compiler/rustc_type_ir/src/ty_kind.rs | 49 ++- compiler/rustc_type_ir/src/visit.rs | 8 +- 23 files changed, 703 insertions(+), 667 deletions(-) create mode 100644 compiler/rustc_type_ir/src/binder.rs diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 4bf7dccab9238..9614f4e80e17c 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -118,6 +118,15 @@ impl IntoDiagArg for rustc_type_ir::FnSig { } } +impl IntoDiagArg for rustc_type_ir::Binder +where + T: IntoDiagArg, +{ + fn into_diag_arg(self) -> DiagArgValue { + self.skip_binder().into_diag_arg() + } +} + into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize); impl IntoDiagArg for bool { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 00356ece58523..168a370bba93e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -18,7 +18,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::traits::FulfillmentError; use rustc_middle::bug; use rustc_middle::query::Key; -use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, suggest_constraining_type_param}; use rustc_middle::ty::{AdtDef, Ty, TyCtxt, TypeVisitableExt}; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 8caeb85204b75..0cd77fe774fb8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -40,6 +40,7 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput}; +use rustc_middle::ty::print::PrintPolyTraitRefExt as _; use rustc_middle::ty::{ self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt, TypeVisitableExt, diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index c0effe9804cd7..07652b4792921 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -115,18 +115,11 @@ impl<'tcx, E: TyEncoder>> Encodable for Ty<'tcx> { } } -impl<'tcx, E: TyEncoder>> Encodable - for ty::Binder<'tcx, ty::PredicateKind<'tcx>> -{ - fn encode(&self, e: &mut E) { - self.bound_vars().encode(e); - encode_with_shorthand(e, &self.skip_binder(), TyEncoder::predicate_shorthands); - } -} - impl<'tcx, E: TyEncoder>> Encodable for ty::Predicate<'tcx> { fn encode(&self, e: &mut E) { - self.kind().encode(e); + let kind = self.kind(); + kind.bound_vars().encode(e); + encode_with_shorthand(e, &kind.skip_binder(), TyEncoder::predicate_shorthands); } } @@ -233,13 +226,11 @@ impl<'tcx, D: TyDecoder>> Decodable for Ty<'tcx> { } } -impl<'tcx, D: TyDecoder>> Decodable - for ty::Binder<'tcx, ty::PredicateKind<'tcx>> -{ - fn decode(decoder: &mut D) -> ty::Binder<'tcx, ty::PredicateKind<'tcx>> { +impl<'tcx, D: TyDecoder>> Decodable for ty::Predicate<'tcx> { + fn decode(decoder: &mut D) -> ty::Predicate<'tcx> { let bound_vars = Decodable::decode(decoder); // Handle shorthands first, if we have a usize > 0x80. - ty::Binder::bind_with_vars( + let predicate_kind = ty::Binder::bind_with_vars( if decoder.positioned_at_shorthand() { let pos = decoder.read_usize(); assert!(pos >= SHORTHAND_OFFSET); @@ -250,13 +241,7 @@ impl<'tcx, D: TyDecoder>> Decodable as Decodable>::decode(decoder) }, bound_vars, - ) - } -} - -impl<'tcx, D: TyDecoder>> Decodable for ty::Predicate<'tcx> { - fn decode(decoder: &mut D) -> ty::Predicate<'tcx> { - let predicate_kind = Decodable::decode(decoder); + ); decoder.interner().mk_predicate(predicate_kind) } } @@ -599,32 +584,3 @@ macro_rules! implement_ty_decoder { } } } - -macro_rules! impl_binder_encode_decode { - ($($t:ty),+ $(,)?) => { - $( - impl<'tcx, E: TyEncoder>> Encodable for ty::Binder<'tcx, $t> { - fn encode(&self, e: &mut E) { - self.bound_vars().encode(e); - self.as_ref().skip_binder().encode(e); - } - } - impl<'tcx, D: TyDecoder>> Decodable for ty::Binder<'tcx, $t> { - fn decode(decoder: &mut D) -> Self { - let bound_vars = Decodable::decode(decoder); - ty::Binder::bind_with_vars(Decodable::decode(decoder), bound_vars) - } - } - )* - } -} - -impl_binder_encode_decode! { - &'tcx ty::List>, - ty::FnSig<'tcx>, - ty::Predicate<'tcx>, - ty::TraitPredicate<'tcx>, - ty::ExistentialPredicate<'tcx>, - ty::TraitRef<'tcx>, - ty::ExistentialTraitRef<'tcx>, -} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8185c99c2fd20..a457319c5f89f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -31,8 +31,7 @@ use crate::ty::{ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, ConstData, GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern, PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity, - Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, TypeVisitable, - Visibility, + Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, Visibility, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; @@ -96,9 +95,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type GenericArg = ty::GenericArg<'tcx>; type Term = ty::Term<'tcx>; - type Binder>> = Binder<'tcx, T>; - type BoundVars = &'tcx List; - type BoundVar = ty::BoundVariableKind; + type BoundVarKinds = &'tcx List; + type BoundVarKind = ty::BoundVariableKind; type CanonicalVars = CanonicalVarInfos<'tcx>; type PredefinedOpaques = solve::PredefinedOpaques<'tcx>; @@ -138,6 +136,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ParamEnv = ty::ParamEnv<'tcx>; type Predicate = Predicate<'tcx>; + type Clause = Clause<'tcx>; type TraitPredicate = ty::TraitPredicate<'tcx>; type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>; type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>; @@ -245,6 +244,10 @@ impl<'tcx> rustc_type_ir::inherent::Abi> for abi::Abi { } impl<'tcx> rustc_type_ir::inherent::Safety> for hir::Safety { + fn is_safe(self) -> bool { + matches!(self, hir::Safety::Safe) + } + fn prefix_str(self) -> &'static str { self.prefix_str() } diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 3d263e62de66e..cb11bb3ef16d3 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -51,6 +51,14 @@ impl<'tcx> rustc_type_ir::inherent::GenericArgs> for ty::GenericArg fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::GenericArgsRef<'tcx> { GenericArgs::identity_for_item(tcx, def_id) } + + fn extend_with_error( + tcx: TyCtxt<'tcx>, + def_id: DefId, + original_args: &[ty::GenericArg<'tcx>], + ) -> ty::GenericArgsRef<'tcx> { + ty::GenericArgs::extend_with_error(tcx, def_id, original_args) + } } impl<'tcx> rustc_type_ir::inherent::IntoKind for GenericArg<'tcx> { diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index be91249a25f8d..067d490078d37 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -8,8 +8,8 @@ use rustc_type_ir as ir; use std::cmp::Ordering; use crate::ty::{ - self, Binder, DebruijnIndex, EarlyBinder, PredicatePolarity, Term, Ty, TyCtxt, TypeFlags, - Upcast, UpcastFrom, WithCachedTypeInfo, + self, DebruijnIndex, EarlyBinder, PredicatePolarity, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom, + WithCachedTypeInfo, }; pub type TraitRef<'tcx> = ir::TraitRef>; @@ -155,6 +155,8 @@ pub struct Clause<'tcx>( pub(super) Interned<'tcx, WithCachedTypeInfo>>>, ); +impl<'tcx> rustc_type_ir::inherent::Clause> for Clause<'tcx> {} + impl<'tcx> Clause<'tcx> { pub fn as_predicate(self) -> Predicate<'tcx> { Predicate(self.0) @@ -231,34 +233,6 @@ impl<'tcx> ExistentialPredicate<'tcx> { pub type PolyExistentialPredicate<'tcx> = ty::Binder<'tcx, ExistentialPredicate<'tcx>>; -impl<'tcx> PolyExistentialPredicate<'tcx> { - /// Given an existential predicate like `?Self: PartialEq` (e.g., derived from `dyn PartialEq`), - /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self` - /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq`, in our example). - pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Clause<'tcx> { - match self.skip_binder() { - ExistentialPredicate::Trait(tr) => { - self.rebind(tr).with_self_ty(tcx, self_ty).upcast(tcx) - } - ExistentialPredicate::Projection(p) => { - self.rebind(p.with_self_ty(tcx, self_ty)).upcast(tcx) - } - ExistentialPredicate::AutoTrait(did) => { - let generics = tcx.generics_of(did); - let trait_ref = if generics.own_params.len() == 1 { - ty::TraitRef::new(tcx, did, [self_ty]) - } else { - // If this is an ill-formed auto trait, then synthesize - // new error args for the missing generics. - let err_args = ty::GenericArgs::extend_with_error(tcx, did, &[self_ty.into()]); - ty::TraitRef::new(tcx, did, err_args) - }; - self.rebind(trait_ref).upcast(tcx) - } - } - } -} - impl<'tcx> ty::List> { /// Returns the "principal `DefId`" of this set of existential predicates. /// @@ -322,49 +296,9 @@ impl<'tcx> ty::List> { } pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>; - -impl<'tcx> PolyTraitRef<'tcx> { - pub fn self_ty(&self) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound_ref(|tr| tr.self_ty()) - } - - pub fn def_id(&self) -> DefId { - self.skip_binder().def_id - } -} - pub type PolyExistentialTraitRef<'tcx> = ty::Binder<'tcx, ExistentialTraitRef<'tcx>>; - -impl<'tcx> PolyExistentialTraitRef<'tcx> { - pub fn def_id(&self) -> DefId { - self.skip_binder().def_id - } - - /// Object types don't have a self type specified. Therefore, when - /// we convert the principal trait-ref into a normal trait-ref, - /// you must give *some* self type. A common choice is `mk_err()` - /// or some placeholder type. - pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::PolyTraitRef<'tcx> { - self.map_bound(|trait_ref| trait_ref.with_self_ty(tcx, self_ty)) - } -} - pub type PolyExistentialProjection<'tcx> = ty::Binder<'tcx, ExistentialProjection<'tcx>>; -impl<'tcx> PolyExistentialProjection<'tcx> { - pub fn with_self_ty( - &self, - tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, - ) -> ty::PolyProjectionPredicate<'tcx> { - self.map_bound(|p| p.with_self_ty(tcx, self_ty)) - } - - pub fn item_def_id(&self) -> DefId { - self.skip_binder().def_id - } -} - impl<'tcx> Clause<'tcx> { /// Performs a instantiation suitable for going from a /// poly-trait-ref to supertraits that must hold if that @@ -473,22 +407,6 @@ impl<'tcx> Clause<'tcx> { pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; -impl<'tcx> PolyTraitPredicate<'tcx> { - pub fn def_id(self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().def_id() - } - - pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound(|trait_ref| trait_ref.self_ty()) - } - - #[inline] - pub fn polarity(self) -> PredicatePolarity { - self.skip_binder().polarity - } -} - /// `A: B` #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] @@ -497,47 +415,10 @@ pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty: pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; - pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; - pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; - pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>; -impl<'tcx> PolyProjectionPredicate<'tcx> { - /// Returns the `DefId` of the trait of the associated item being projected. - #[inline] - pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId { - self.skip_binder().projection_term.trait_def_id(tcx) - } - - /// Get the [PolyTraitRef] required for this projection to be well formed. - /// Note that for generic associated types the predicates of the associated - /// type also need to be checked. - #[inline] - pub fn required_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> { - // Note: unlike with `TraitRef::to_poly_trait_ref()`, - // `self.0.trait_ref` is permitted to have escaping regions. - // This is because here `self` has a `Binder` and so does our - // return value, so we are preserving the number of binding - // levels. - self.map_bound(|predicate| predicate.projection_term.trait_ref(tcx)) - } - - pub fn term(&self) -> Binder<'tcx, Term<'tcx>> { - self.map_bound(|predicate| predicate.term) - } - - /// The `DefId` of the `TraitItem` for the associated type. - /// - /// Note that this is not the `DefId` of the `TraitRef` containing this - /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. - pub fn projection_def_id(&self) -> DefId { - // Ok to skip binder since trait `DefId` does not care about regions. - self.skip_binder().projection_term.def_id - } -} - pub trait ToPolyTraitRef<'tcx> { fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; } @@ -554,8 +435,8 @@ impl<'tcx> UpcastFrom, PredicateKind<'tcx>> for Predicate<'tcx> { } } -impl<'tcx> UpcastFrom, Binder<'tcx, PredicateKind<'tcx>>> for Predicate<'tcx> { - fn upcast_from(from: Binder<'tcx, PredicateKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, PredicateKind<'tcx>>> for Predicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, PredicateKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { tcx.mk_predicate(from) } } @@ -566,8 +447,8 @@ impl<'tcx> UpcastFrom, ClauseKind<'tcx>> for Predicate<'tcx> { } } -impl<'tcx> UpcastFrom, Binder<'tcx, ClauseKind<'tcx>>> for Predicate<'tcx> { - fn upcast_from(from: Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ClauseKind<'tcx>>> for Predicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { tcx.mk_predicate(from.map_bound(PredicateKind::Clause)) } } @@ -580,12 +461,12 @@ impl<'tcx> UpcastFrom, Clause<'tcx>> for Predicate<'tcx> { impl<'tcx> UpcastFrom, ClauseKind<'tcx>> for Clause<'tcx> { fn upcast_from(from: ClauseKind<'tcx>, tcx: TyCtxt<'tcx>) -> Self { - tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(from))).expect_clause() + tcx.mk_predicate(ty::Binder::dummy(PredicateKind::Clause(from))).expect_clause() } } -impl<'tcx> UpcastFrom, Binder<'tcx, ClauseKind<'tcx>>> for Clause<'tcx> { - fn upcast_from(from: Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, ClauseKind<'tcx>>> for Clause<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { tcx.mk_predicate(from.map_bound(|clause| PredicateKind::Clause(clause))).expect_clause() } } @@ -609,22 +490,22 @@ impl<'tcx> UpcastFrom, TraitRef<'tcx>> for Clause<'tcx> { } } -impl<'tcx> UpcastFrom, Binder<'tcx, TraitRef<'tcx>>> for Predicate<'tcx> { - fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, TraitRef<'tcx>>> for Predicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { let pred: PolyTraitPredicate<'tcx> = from.upcast(tcx); pred.upcast(tcx) } } -impl<'tcx> UpcastFrom, Binder<'tcx, TraitRef<'tcx>>> for Clause<'tcx> { - fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, TraitRef<'tcx>>> for Clause<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { let pred: PolyTraitPredicate<'tcx> = from.upcast(tcx); pred.upcast(tcx) } } -impl<'tcx> UpcastFrom, Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> { - fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, ty::Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> { + fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self { from.map_bound(|trait_ref| TraitPredicate { trait_ref, polarity: PredicatePolarity::Positive, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 0dbb17e9db44e..f0bd071e451f8 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2934,12 +2934,13 @@ impl<'tcx> ty::TraitRef<'tcx> { } } +#[extension(pub trait PrintPolyTraitRefExt<'tcx>)] impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> { - pub fn print_only_trait_path(self) -> ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>> { + fn print_only_trait_path(self) -> ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>> { self.map_bound(|tr| tr.print_only_trait_path()) } - pub fn print_trait_sugared(self) -> ty::Binder<'tcx, TraitRefPrintSugared<'tcx>> { + fn print_trait_sugared(self) -> ty::Binder<'tcx, TraitRefPrintSugared<'tcx>> { self.map_bound(|tr| tr.print_trait_sugared()) } } @@ -2960,8 +2961,9 @@ impl<'tcx> ty::TraitPredicate<'tcx> { } } +#[extension(pub trait PrintPolyTraitPredicateExt<'tcx>)] impl<'tcx> ty::PolyTraitPredicate<'tcx> { - pub fn print_modifiers_and_trait_path( + fn print_modifiers_and_trait_path( self, ) -> ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>> { self.map_bound(TraitPredPrintModifiersAndPath) @@ -3016,17 +3018,6 @@ forward_display_to_print! { &'tcx ty::List>, ty::Const<'tcx>, - // HACK(eddyb) these are exhaustive instead of generic, - // because `for<'tcx>` isn't possible yet. - ty::PolyExistentialProjection<'tcx>, - ty::PolyExistentialTraitRef<'tcx>, - ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - ty::Binder<'tcx, TraitRefPrintSugared<'tcx>>, - ty::Binder<'tcx, ty::FnSig<'tcx>>, - ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, - ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>>, - ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>, ty::OutlivesPredicate, ty::Region<'tcx>>, ty::OutlivesPredicate, ty::Region<'tcx>> } diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index 551e2ea229561..d7da37385e1fc 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -384,6 +384,10 @@ impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundRegion { fn var(self) -> BoundVar { self.var } + + fn assert_eq(self, var: ty::BoundVariableKind) { + assert_eq!(self.kind, var.expect_region()) + } } impl core::fmt::Debug for BoundRegion { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 81d92a2a448e4..af3aa3b56f7bb 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -387,38 +387,6 @@ impl<'tcx> TypeVisitable> for ty::AdtDef<'tcx> { } } -impl<'tcx, T: TypeFoldable>> TypeFoldable> for ty::Binder<'tcx, T> { - fn try_fold_with>>( - self, - folder: &mut F, - ) -> Result { - folder.try_fold_binder(self) - } -} - -impl<'tcx, T: TypeVisitable>> TypeVisitable> for ty::Binder<'tcx, T> { - fn visit_with>>(&self, visitor: &mut V) -> V::Result { - visitor.visit_binder(self) - } -} - -impl<'tcx, T: TypeFoldable>> TypeSuperFoldable> for ty::Binder<'tcx, T> { - fn try_super_fold_with>>( - self, - folder: &mut F, - ) -> Result { - self.try_map_bound(|ty| ty.try_fold_with(folder)) - } -} - -impl<'tcx, T: TypeVisitable>> TypeSuperVisitable> - for ty::Binder<'tcx, T> -{ - fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { - self.as_ref().skip_binder().visit_with(visitor) - } -} - impl<'tcx> TypeFoldable> for &'tcx ty::List> { fn try_fold_with>>( self, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 2d9d178449e09..fc9a854c85338 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -3,17 +3,16 @@ #![allow(rustc::usage_of_ty_tykind)] use crate::infer::canonical::Canonical; -use crate::ty::visit::ValidateBoundVars; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + TypeVisitable, TypeVisitor, }; use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use crate::ty::{List, ParamEnv}; use hir::def::{CtorKind, DefKind}; use rustc_data_structures::captures::Captures; -use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan}; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; @@ -21,11 +20,11 @@ use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT}; -use rustc_target::spec::abi::{self, Abi}; +use rustc_target::spec::abi; use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::iter; -use std::ops::{ControlFlow, Deref, Range}; +use std::ops::{ControlFlow, Range}; use ty::util::IntTypeExt; use rustc_type_ir::TyKind::*; @@ -40,6 +39,7 @@ pub type TyKind<'tcx> = ir::TyKind>; pub type TypeAndMut<'tcx> = ir::TypeAndMut>; pub type AliasTy<'tcx> = ir::AliasTy>; pub type FnSig<'tcx> = ir::FnSig>; +pub type Binder<'tcx, T> = ir::Binder, T>; pub trait Article { fn article(&self) -> &'static str; @@ -373,7 +373,7 @@ impl<'tcx> CoroutineClosureArgs<'tcx> { self.split().signature_parts_ty } - pub fn coroutine_closure_sig(self) -> ty::Binder<'tcx, CoroutineClosureSignature<'tcx>> { + pub fn coroutine_closure_sig(self) -> Binder<'tcx, CoroutineClosureSignature<'tcx>> { let interior = self.coroutine_witness_ty(); let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { bug!() }; sig.map_bound(|sig| { @@ -898,203 +898,6 @@ impl BoundVariableKind { } } -/// Binder is a binder for higher-ranked lifetimes or types. It is part of the -/// compiler's representation for things like `for<'a> Fn(&'a isize)` -/// (which would be represented by the type `PolyTraitRef == -/// Binder<'tcx, TraitRef>`). Note that when we instantiate, -/// erase, or otherwise "discharge" these bound vars, we change the -/// type from `Binder<'tcx, T>` to just `T` (see -/// e.g., `liberate_late_bound_regions`). -/// -/// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[derive(HashStable, Lift)] -pub struct Binder<'tcx, T> { - value: T, - bound_vars: &'tcx List, -} - -impl<'tcx, T> Binder<'tcx, T> -where - T: TypeVisitable>, -{ - /// Wraps `value` in a binder, asserting that `value` does not - /// contain any bound vars that would be bound by the - /// binder. This is commonly used to 'inject' a value T into a - /// different binding level. - #[track_caller] - pub fn dummy(value: T) -> Binder<'tcx, T> { - assert!( - !value.has_escaping_bound_vars(), - "`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder." - ); - Binder { value, bound_vars: ty::List::empty() } - } - - pub fn bind_with_vars(value: T, bound_vars: &'tcx List) -> Binder<'tcx, T> { - if cfg!(debug_assertions) { - let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); - } - Binder { value, bound_vars } - } -} - -impl<'tcx, T> rustc_type_ir::inherent::BoundVars> for ty::Binder<'tcx, T> { - fn bound_vars(&self) -> &'tcx List { - self.bound_vars - } - - fn has_no_bound_vars(&self) -> bool { - self.bound_vars.is_empty() - } -} - -impl<'tcx, T> Binder<'tcx, T> { - /// Skips the binder and returns the "bound" value. This is a - /// risky thing to do because it's easy to get confused about - /// De Bruijn indices and the like. It is usually better to - /// discharge the binder using `no_bound_vars` or - /// `instantiate_bound_regions` or something like - /// that. `skip_binder` is only valid when you are either - /// extracting data that has nothing to do with bound vars, you - /// are doing some sort of test that does not involve bound - /// regions, or you are being very careful about your depth - /// accounting. - /// - /// Some examples where `skip_binder` is reasonable: - /// - /// - extracting the `DefId` from a PolyTraitRef; - /// - comparing the self type of a PolyTraitRef to see if it is equal to - /// a type parameter `X`, since the type `X` does not reference any regions - pub fn skip_binder(self) -> T { - self.value - } - - pub fn bound_vars(&self) -> &'tcx List { - self.bound_vars - } - - pub fn as_ref(&self) -> Binder<'tcx, &T> { - Binder { value: &self.value, bound_vars: self.bound_vars } - } - - pub fn as_deref(&self) -> Binder<'tcx, &T::Target> - where - T: Deref, - { - Binder { value: &self.value, bound_vars: self.bound_vars } - } - - pub fn map_bound_ref>>(&self, f: F) -> Binder<'tcx, U> - where - F: FnOnce(&T) -> U, - { - self.as_ref().map_bound(f) - } - - pub fn map_bound>>(self, f: F) -> Binder<'tcx, U> - where - F: FnOnce(T) -> U, - { - let Binder { value, bound_vars } = self; - let value = f(value); - if cfg!(debug_assertions) { - let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); - } - Binder { value, bound_vars } - } - - pub fn try_map_bound>, E>( - self, - f: F, - ) -> Result, E> - where - F: FnOnce(T) -> Result, - { - let Binder { value, bound_vars } = self; - let value = f(value)?; - if cfg!(debug_assertions) { - let mut validator = ValidateBoundVars::new(bound_vars); - value.visit_with(&mut validator); - } - Ok(Binder { value, bound_vars }) - } - - /// Wraps a `value` in a binder, using the same bound variables as the - /// current `Binder`. This should not be used if the new value *changes* - /// the bound variables. Note: the (old or new) value itself does not - /// necessarily need to *name* all the bound variables. - /// - /// This currently doesn't do anything different than `bind`, because we - /// don't actually track bound vars. However, semantically, it is different - /// because bound vars aren't allowed to change here, whereas they are - /// in `bind`. This may be (debug) asserted in the future. - pub fn rebind(&self, value: U) -> Binder<'tcx, U> - where - U: TypeVisitable>, - { - Binder::bind_with_vars(value, self.bound_vars) - } - - /// Unwraps and returns the value within, but only if it contains - /// no bound vars at all. (In other words, if this binder -- - /// and indeed any enclosing binder -- doesn't bind anything at - /// all.) Otherwise, returns `None`. - /// - /// (One could imagine having a method that just unwraps a single - /// binder, but permits late-bound vars bound by enclosing - /// binders, but that would require adjusting the debruijn - /// indices, and given the shallow binding structure we often use, - /// would not be that useful.) - pub fn no_bound_vars(self) -> Option - where - T: TypeVisitable>, - { - // `self.value` is equivalent to `self.skip_binder()` - if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } - } - - /// Splits the contents into two things that share the same binder - /// level as the original, returning two distinct binders. - /// - /// `f` should consider bound regions at depth 1 to be free, and - /// anything it produces with bound regions at depth 1 will be - /// bound in the resulting return values. - pub fn split(self, f: F) -> (Binder<'tcx, U>, Binder<'tcx, V>) - where - F: FnOnce(T) -> (U, V), - { - let Binder { value, bound_vars } = self; - let (u, v) = f(value); - (Binder { value: u, bound_vars }, Binder { value: v, bound_vars }) - } -} - -impl<'tcx, T> Binder<'tcx, Option> { - pub fn transpose(self) -> Option> { - let Binder { value, bound_vars } = self; - value.map(|value| Binder { value, bound_vars }) - } -} - -impl<'tcx, T: IntoIterator> Binder<'tcx, T> { - pub fn iter(self) -> impl Iterator> { - let Binder { value, bound_vars } = self; - value.into_iter().map(|value| Binder { value, bound_vars }) - } -} - -impl<'tcx, T> IntoDiagArg for Binder<'tcx, T> -where - T: IntoDiagArg, -{ - fn into_diag_arg(self) -> DiagArgValue { - self.value.into_diag_arg() - } -} - #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)] pub struct GenSig<'tcx> { pub resume_ty: Ty<'tcx>, @@ -1103,48 +906,6 @@ pub struct GenSig<'tcx> { } pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>; - -impl<'tcx> PolyFnSig<'tcx> { - #[inline] - pub fn inputs(&self) -> Binder<'tcx, &'tcx [Ty<'tcx>]> { - self.map_bound_ref(|fn_sig| fn_sig.inputs()) - } - - #[inline] - #[track_caller] - pub fn input(&self, index: usize) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound_ref(|fn_sig| fn_sig.inputs()[index]) - } - - pub fn inputs_and_output(&self) -> ty::Binder<'tcx, &'tcx List>> { - self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output) - } - - #[inline] - pub fn output(&self) -> ty::Binder<'tcx, Ty<'tcx>> { - self.map_bound_ref(|fn_sig| fn_sig.output()) - } - - pub fn c_variadic(&self) -> bool { - self.skip_binder().c_variadic - } - - pub fn safety(&self) -> hir::Safety { - self.skip_binder().safety - } - - pub fn abi(&self) -> abi::Abi { - self.skip_binder().abi - } - - pub fn is_fn_trait_compatible(&self) -> bool { - matches!( - self.skip_binder(), - ty::FnSig { safety: rustc_hir::Safety::Safe, abi: Abi::Rust, c_variadic: false, .. } - ) - } -} - pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)] @@ -1203,6 +964,10 @@ impl<'tcx> rustc_type_ir::inherent::BoundVarLike> for BoundTy { fn var(self) -> BoundVar { self.var } + + fn assert_eq(self, var: ty::BoundVariableKind) { + assert_eq!(self.kind, var.expect_ty()) + } } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)] @@ -2001,7 +1766,7 @@ impl<'tcx> Ty<'tcx> { FnPtr(f) => *f, Error(_) => { // ignore errors (#54954) - ty::Binder::dummy(ty::FnSig { + Binder::dummy(ty::FnSig { inputs_and_output: ty::List::empty(), c_variadic: false, safety: hir::Safety::Safe, diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 218567bbd59e8..b1bbfd420e1b1 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -1,7 +1,6 @@ use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags}; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sso::SsoHashSet; use rustc_type_ir::fold::TypeFoldable; use std::ops::ControlFlow; @@ -145,103 +144,6 @@ impl<'tcx> TyCtxt<'tcx> { } } -pub struct ValidateBoundVars<'tcx> { - bound_vars: &'tcx ty::List, - binder_index: ty::DebruijnIndex, - // We may encounter the same variable at different levels of binding, so - // this can't just be `Ty` - visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>, -} - -impl<'tcx> ValidateBoundVars<'tcx> { - pub fn new(bound_vars: &'tcx ty::List) -> Self { - ValidateBoundVars { - bound_vars, - binder_index: ty::INNERMOST, - visited: SsoHashSet::default(), - } - } -} - -impl<'tcx> TypeVisitor> for ValidateBoundVars<'tcx> { - type Result = ControlFlow<()>; - - fn visit_binder>>( - &mut self, - t: &Binder<'tcx, T>, - ) -> Self::Result { - self.binder_index.shift_in(1); - let result = t.super_visit_with(self); - self.binder_index.shift_out(1); - result - } - - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { - if t.outer_exclusive_binder() < self.binder_index - || !self.visited.insert((self.binder_index, t)) - { - return ControlFlow::Break(()); - } - match *t.kind() { - ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { - if self.bound_vars.len() <= bound_ty.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); - } - let list_var = self.bound_vars[bound_ty.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Ty(kind) => { - if kind != bound_ty.kind { - bug!( - "Mismatched type kinds: {:?} doesn't var in list {:?}", - bound_ty.kind, - list_var - ); - } - } - _ => { - bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var) - } - } - } - - _ => (), - }; - - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result { - match *r { - ty::ReBound(index, br) if index == self.binder_index => { - if self.bound_vars.len() <= br.var.as_usize() { - bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars); - } - let list_var = self.bound_vars[br.var.as_usize()]; - match list_var { - ty::BoundVariableKind::Region(kind) => { - if kind != br.kind { - bug!( - "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})", - br.kind, - list_var, - self.bound_vars - ); - } - } - _ => bug!( - "Mismatched bound variable kinds! Expected region, found {:?}", - list_var - ), - } - } - - _ => (), - }; - - ControlFlow::Continue(()) - } -} - /// Collects all the late-bound regions at the innermost binding level /// into a hash set. struct LateBoundRegionsCollector { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 696639e9c1b65..127ebde5fec3f 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -217,10 +217,9 @@ impl, I: Interner> TypeFolder self.infcx.interner() } - fn fold_binder(&mut self, t: I::Binder) -> I::Binder + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { self.binder_index.shift_in(1); let t = t.super_fold_with(self); @@ -455,10 +454,9 @@ impl TypeFolder for RegionsToStatic { self.interner } - fn fold_binder(&mut self, t: I::Binder) -> I::Binder + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { self.binder.shift_in(1); let t = t.super_fold_with(self); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index f9b6b281f9246..6c56ebb62aed1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -47,7 +47,8 @@ use crate::infer::InferCtxtExt as _; use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_middle::ty::print::{ - with_forced_trimmed_paths, with_no_trimmed_paths, PrintTraitPredicateExt as _, + with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _, + PrintTraitPredicateExt as _, }; use itertools::EitherOrBoth; diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs new file mode 100644 index 0000000000000..5336b915a1d38 --- /dev/null +++ b/compiler/rustc_type_ir/src/binder.rs @@ -0,0 +1,340 @@ +use std::fmt::Debug; +use std::hash::Hash; +use std::ops::{ControlFlow, Deref}; + +#[cfg(feature = "nightly")] +use rustc_macros::HashStable_NoContext; +use rustc_serialize::Decodable; + +use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; +use crate::inherent::*; +use crate::lift::Lift; +use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; +use crate::{self as ty, Interner, SsoHashSet}; + +/// Binder is a binder for higher-ranked lifetimes or types. It is part of the +/// compiler's representation for things like `for<'a> Fn(&'a isize)` +/// (which would be represented by the type `PolyTraitRef == +/// Binder`). Note that when we instantiate, +/// erase, or otherwise "discharge" these bound vars, we change the +/// type from `Binder` to just `T` (see +/// e.g., `liberate_late_bound_regions`). +/// +/// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "T: Clone"), + Copy(bound = "T: Copy"), + Hash(bound = "T: Hash"), + PartialEq(bound = "T: PartialEq"), + Eq(bound = "T: Eq"), + Debug(bound = "T: Debug") +)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub struct Binder { + value: T, + bound_vars: I::BoundVarKinds, +} + +// FIXME: We manually derive `Lift` because the `derive(Lift_Generic)` doesn't +// understand how to turn `T` to `T::Lifted` in the output `type Lifted`. +impl Lift for Binder +where + T: Lift, + I::BoundVarKinds: Lift, +{ + type Lifted = Binder; + + fn lift_to_tcx(self, tcx: U) -> Option { + Some(Binder { + value: self.value.lift_to_tcx(tcx)?, + bound_vars: self.bound_vars.lift_to_tcx(tcx)?, + }) + } +} + +macro_rules! impl_binder_encode_decode { + ($($t:ty),+ $(,)?) => { + $( + impl> rustc_serialize::Encodable for ty::Binder + where + $t: rustc_serialize::Encodable, + I::BoundVarKinds: rustc_serialize::Encodable, + { + fn encode(&self, e: &mut E) { + self.bound_vars().encode(e); + self.as_ref().skip_binder().encode(e); + } + } + impl> Decodable for ty::Binder + where + $t: TypeVisitable + rustc_serialize::Decodable, + I::BoundVarKinds: rustc_serialize::Decodable, + { + fn decode(decoder: &mut D) -> Self { + let bound_vars = Decodable::decode(decoder); + ty::Binder::bind_with_vars(<$t>::decode(decoder), bound_vars) + } + } + )* + } +} + +impl_binder_encode_decode! { + ty::FnSig, + ty::TraitPredicate, + ty::ExistentialPredicate, + ty::TraitRef, + ty::ExistentialTraitRef, +} + +impl Binder +where + T: TypeVisitable, +{ + /// Wraps `value` in a binder, asserting that `value` does not + /// contain any bound vars that would be bound by the + /// binder. This is commonly used to 'inject' a value T into a + /// different binding level. + #[track_caller] + pub fn dummy(value: T) -> Binder { + assert!( + !value.has_escaping_bound_vars(), + "`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder." + ); + Binder { value, bound_vars: Default::default() } + } + + pub fn bind_with_vars(value: T, bound_vars: I::BoundVarKinds) -> Binder { + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(bound_vars); + value.visit_with(&mut validator); + } + Binder { value, bound_vars } + } +} + +impl> TypeFoldable for Binder { + fn try_fold_with>(self, folder: &mut F) -> Result { + folder.try_fold_binder(self) + } +} + +impl> TypeVisitable for Binder { + fn visit_with>(&self, visitor: &mut V) -> V::Result { + visitor.visit_binder(self) + } +} + +impl> TypeSuperFoldable for Binder { + fn try_super_fold_with>( + self, + folder: &mut F, + ) -> Result { + self.try_map_bound(|ty| ty.try_fold_with(folder)) + } +} + +impl> TypeSuperVisitable for Binder { + fn super_visit_with>(&self, visitor: &mut V) -> V::Result { + self.as_ref().skip_binder().visit_with(visitor) + } +} + +impl Binder { + /// Skips the binder and returns the "bound" value. This is a + /// risky thing to do because it's easy to get confused about + /// De Bruijn indices and the like. It is usually better to + /// discharge the binder using `no_bound_vars` or + /// `instantiate_bound_regions` or something like + /// that. `skip_binder` is only valid when you are either + /// extracting data that has nothing to do with bound vars, you + /// are doing some sort of test that does not involve bound + /// regions, or you are being very careful about your depth + /// accounting. + /// + /// Some examples where `skip_binder` is reasonable: + /// + /// - extracting the `DefId` from a PolyTraitRef; + /// - comparing the self type of a PolyTraitRef to see if it is equal to + /// a type parameter `X`, since the type `X` does not reference any regions + pub fn skip_binder(self) -> T { + self.value + } + + pub fn bound_vars(&self) -> I::BoundVarKinds { + self.bound_vars + } + + pub fn as_ref(&self) -> Binder { + Binder { value: &self.value, bound_vars: self.bound_vars } + } + + pub fn as_deref(&self) -> Binder + where + T: Deref, + { + Binder { value: &self.value, bound_vars: self.bound_vars } + } + + pub fn map_bound_ref>(&self, f: F) -> Binder + where + F: FnOnce(&T) -> U, + { + self.as_ref().map_bound(f) + } + + pub fn map_bound>(self, f: F) -> Binder + where + F: FnOnce(T) -> U, + { + let Binder { value, bound_vars } = self; + let value = f(value); + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(bound_vars); + value.visit_with(&mut validator); + } + Binder { value, bound_vars } + } + + pub fn try_map_bound, E>(self, f: F) -> Result, E> + where + F: FnOnce(T) -> Result, + { + let Binder { value, bound_vars } = self; + let value = f(value)?; + if cfg!(debug_assertions) { + let mut validator = ValidateBoundVars::new(bound_vars); + value.visit_with(&mut validator); + } + Ok(Binder { value, bound_vars }) + } + + /// Wraps a `value` in a binder, using the same bound variables as the + /// current `Binder`. This should not be used if the new value *changes* + /// the bound variables. Note: the (old or new) value itself does not + /// necessarily need to *name* all the bound variables. + /// + /// This currently doesn't do anything different than `bind`, because we + /// don't actually track bound vars. However, semantically, it is different + /// because bound vars aren't allowed to change here, whereas they are + /// in `bind`. This may be (debug) asserted in the future. + pub fn rebind(&self, value: U) -> Binder + where + U: TypeVisitable, + { + Binder::bind_with_vars(value, self.bound_vars) + } + + /// Unwraps and returns the value within, but only if it contains + /// no bound vars at all. (In other words, if this binder -- + /// and indeed any enclosing binder -- doesn't bind anything at + /// all.) Otherwise, returns `None`. + /// + /// (One could imagine having a method that just unwraps a single + /// binder, but permits late-bound vars bound by enclosing + /// binders, but that would require adjusting the debruijn + /// indices, and given the shallow binding structure we often use, + /// would not be that useful.) + pub fn no_bound_vars(self) -> Option + where + T: TypeVisitable, + { + // `self.value` is equivalent to `self.skip_binder()` + if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) } + } + + /// Splits the contents into two things that share the same binder + /// level as the original, returning two distinct binders. + /// + /// `f` should consider bound regions at depth 1 to be free, and + /// anything it produces with bound regions at depth 1 will be + /// bound in the resulting return values. + pub fn split(self, f: F) -> (Binder, Binder) + where + F: FnOnce(T) -> (U, V), + { + let Binder { value, bound_vars } = self; + let (u, v) = f(value); + (Binder { value: u, bound_vars }, Binder { value: v, bound_vars }) + } +} + +impl Binder> { + pub fn transpose(self) -> Option> { + let Binder { value, bound_vars } = self; + value.map(|value| Binder { value, bound_vars }) + } +} + +impl Binder { + pub fn iter(self) -> impl Iterator> { + let Binder { value, bound_vars } = self; + value.into_iter().map(move |value| Binder { value, bound_vars }) + } +} + +pub struct ValidateBoundVars { + bound_vars: I::BoundVarKinds, + binder_index: ty::DebruijnIndex, + // We may encounter the same variable at different levels of binding, so + // this can't just be `Ty` + visited: SsoHashSet<(ty::DebruijnIndex, I::Ty)>, +} + +impl ValidateBoundVars { + pub fn new(bound_vars: I::BoundVarKinds) -> Self { + ValidateBoundVars { + bound_vars, + binder_index: ty::INNERMOST, + visited: SsoHashSet::default(), + } + } +} + +impl TypeVisitor for ValidateBoundVars { + type Result = ControlFlow<()>; + + fn visit_binder>(&mut self, t: &Binder) -> Self::Result { + self.binder_index.shift_in(1); + let result = t.super_visit_with(self); + self.binder_index.shift_out(1); + result + } + + fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + if t.outer_exclusive_binder() < self.binder_index + || !self.visited.insert((self.binder_index, t)) + { + return ControlFlow::Break(()); + } + match t.kind() { + ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { + let idx = bound_ty.var().as_usize(); + if self.bound_vars.len() <= idx { + panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars); + } + bound_ty.assert_eq(self.bound_vars[idx]); + } + _ => {} + }; + + t.super_visit_with(self) + } + + fn visit_region(&mut self, r: I::Region) -> Self::Result { + match r.kind() { + ty::ReBound(index, br) if index == self.binder_index => { + let idx = br.var().as_usize(); + if self.bound_vars.len() <= idx { + panic!("Not enough bound vars: {:?} not found in {:?}", r, self.bound_vars); + } + br.assert_eq(self.bound_vars[idx]); + } + + _ => (), + }; + + ControlFlow::Continue(()) + } +} diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs index 01bb3d73dbdf3..405aba30241b7 100644 --- a/compiler/rustc_type_ir/src/fold.rs +++ b/compiler/rustc_type_ir/src/fold.rs @@ -48,8 +48,8 @@ use rustc_index::{Idx, IndexVec}; use std::mem; -use crate::Lrc; -use crate::{visit::TypeVisitable, Interner}; +use crate::visit::TypeVisitable; +use crate::{self as ty, Interner, Lrc}; #[cfg(feature = "nightly")] type Never = !; @@ -128,10 +128,9 @@ pub trait TypeSuperFoldable: TypeFoldable { pub trait TypeFolder: FallibleTypeFolder { fn interner(&self) -> I; - fn fold_binder(&mut self, t: I::Binder) -> I::Binder + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { t.super_fold_with(self) } @@ -167,10 +166,9 @@ pub trait FallibleTypeFolder: Sized { fn interner(&self) -> I; - fn try_fold_binder(&mut self, t: I::Binder) -> Result, Self::Error> + fn try_fold_binder(&mut self, t: ty::Binder) -> Result, Self::Error> where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { t.try_super_fold_with(self) } @@ -206,10 +204,9 @@ where TypeFolder::interner(self) } - fn try_fold_binder(&mut self, t: I::Binder) -> Result, Never> + fn try_fold_binder(&mut self, t: ty::Binder) -> Result, Never> where T: TypeFoldable, - I::Binder: TypeSuperFoldable, { Ok(self.fold_binder(t)) } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index f66c6e722f7ca..77fe30a466087 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -8,11 +8,8 @@ use std::hash::Hash; use std::ops::Deref; use crate::fold::{TypeFoldable, TypeSuperFoldable}; -use crate::visit::{Flags, TypeSuperVisitable}; -use crate::{ - AliasTy, AliasTyKind, BoundVar, ConstKind, ConstVid, DebruijnIndex, DebugWithInfcx, InferConst, - InferTy, Interner, RegionKind, TyKind, TyVid, UnevaluatedConst, UniverseIndex, -}; +use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable}; +use crate::{self as ty, DebugWithInfcx, Interner, UpcastFrom}; pub trait Ty>: Copy @@ -21,24 +18,30 @@ pub trait Ty>: + Eq + Into + Into - + IntoKind> + + IntoKind> + TypeSuperVisitable + TypeSuperFoldable + Flags { fn new_bool(interner: I) -> Self; - fn new_infer(interner: I, var: InferTy) -> Self; + fn new_infer(interner: I, var: ty::InferTy) -> Self; - fn new_var(interner: I, var: TyVid) -> Self; + fn new_var(interner: I, var: ty::TyVid) -> Self; - fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; + fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; - fn new_alias(interner: I, kind: AliasTyKind, alias_ty: AliasTy) -> Self; + fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy) -> Self; } pub trait Tys>: - Copy + Debug + Hash + Eq + IntoIterator + Deref> + Copy + + Debug + + Hash + + Eq + + IntoIterator + + Deref> + + TypeVisitable { fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty); } @@ -49,13 +52,21 @@ pub trait Abi>: Copy + Debug + Hash + Eq { } pub trait Safety>: Copy + Debug + Hash + Eq { + fn is_safe(self) -> bool; + fn prefix_str(self) -> &'static str; } pub trait Region>: - Copy + DebugWithInfcx + Hash + Eq + Into + IntoKind> + Flags + Copy + + DebugWithInfcx + + Hash + + Eq + + Into + + IntoKind> + + Flags { - fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self; + fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; fn new_static(interner: I) -> Self; } @@ -67,18 +78,23 @@ pub trait Const>: + Eq + Into + Into - + IntoKind> + + IntoKind> + TypeSuperVisitable + TypeSuperFoldable + Flags { - fn new_infer(interner: I, var: InferConst, ty: I::Ty) -> Self; + fn new_infer(interner: I, var: ty::InferConst, ty: I::Ty) -> Self; - fn new_var(interner: I, var: ConstVid, ty: I::Ty) -> Self; + fn new_var(interner: I, var: ty::ConstVid, ty: I::Ty) -> Self; - fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar, ty: I::Ty) -> Self; + fn new_anon_bound( + interner: I, + debruijn: ty::DebruijnIndex, + var: ty::BoundVar, + ty: I::Ty, + ) -> Self; - fn new_unevaluated(interner: I, uv: UnevaluatedConst, ty: I::Ty) -> Self; + fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst, ty: I::Ty) -> Self; fn ty(self) -> I::Ty; } @@ -100,6 +116,12 @@ pub trait GenericArgs>: fn type_at(self, i: usize) -> I::Ty; fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs; + + fn extend_with_error( + tcx: I, + def_id: I::DefId, + original_args: &[I::GenericArg], + ) -> I::GenericArgs; } pub trait Predicate>: @@ -108,14 +130,25 @@ pub trait Predicate>: fn is_coinductive(self, interner: I) -> bool; } +pub trait Clause>: + Copy + + Debug + + Hash + + Eq + // FIXME: Remove these, uplift the `Upcast` impls. + + UpcastFrom>> + + UpcastFrom>> +{ +} + /// Common capabilities of placeholder kinds pub trait PlaceholderLike: Copy + Debug + Hash + Eq { - fn universe(self) -> UniverseIndex; - fn var(self) -> BoundVar; + fn universe(self) -> ty::UniverseIndex; + fn var(self) -> ty::BoundVar; - fn with_updated_universe(self, ui: UniverseIndex) -> Self; + fn with_updated_universe(self, ui: ty::UniverseIndex) -> Self; - fn new(ui: UniverseIndex, var: BoundVar) -> Self; + fn new(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self; } pub trait IntoKind { @@ -124,12 +157,8 @@ pub trait IntoKind { fn kind(self) -> Self::Kind; } -pub trait BoundVars { - fn bound_vars(&self) -> I::BoundVars; - - fn has_no_bound_vars(&self) -> bool; -} - pub trait BoundVarLike { - fn var(self) -> BoundVar; + fn var(self) -> ty::BoundVar; + + fn assert_eq(self, var: I::BoundVarKind); } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6516d5b164540..2ab81dfff3268 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -28,19 +28,28 @@ pub trait Interner: + IrPrint> + IrPrint> { - type DefId: Copy + Debug + Hash + Eq; + type DefId: Copy + Debug + Hash + Eq + TypeVisitable; type AdtDef: Copy + Debug + Hash + Eq; type GenericArgs: GenericArgs; /// The slice of args for a specific item. For a GAT like `type Foo<'a>`, it will be `['a]`, /// not including the args from the parent item (trait or impl). type OwnItemArgs: Copy + Debug + Hash + Eq; - type GenericArg: Copy + DebugWithInfcx + Hash + Eq + IntoKind>; - type Term: Copy + Debug + Hash + Eq + IntoKind>; - - type Binder>: BoundVars + TypeSuperVisitable; - type BoundVars: IntoIterator; - type BoundVar; + type GenericArg: Copy + + DebugWithInfcx + + Hash + + Eq + + IntoKind> + + TypeVisitable; + type Term: Copy + Debug + Hash + Eq + IntoKind> + TypeVisitable; + + type BoundVarKinds: Copy + + Debug + + Hash + + Eq + + Deref> + + Default; + type BoundVarKind: Copy + Debug + Hash + Eq; type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator>; type PredefinedOpaques: Copy + Debug + Hash + Eq; @@ -51,7 +60,7 @@ pub trait Interner: // Kinds of tys type Ty: Ty; type Tys: Tys; - type FnInputTys: Copy + Debug + Hash + Eq + Deref; + type FnInputTys: Copy + Debug + Hash + Eq + Deref + TypeVisitable; type ParamTy: Copy + Debug + Hash + Eq; type BoundTy: Copy + Debug + Hash + Eq + BoundVarLike; type PlaceholderTy: PlaceholderLike; @@ -84,14 +93,15 @@ pub trait Interner: // Predicates type ParamEnv: Copy + Debug + Hash + Eq; type Predicate: Predicate; - type TraitPredicate: Copy + Debug + Hash + Eq; - type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; - type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; - type ProjectionPredicate: Copy + Debug + Hash + Eq; - type NormalizesTo: Copy + Debug + Hash + Eq; - type SubtypePredicate: Copy + Debug + Hash + Eq; - type CoercePredicate: Copy + Debug + Hash + Eq; - type ClosureKind: Copy + Debug + Hash + Eq; + type Clause: Clause; + type TraitPredicate: Copy + Debug + Hash + Eq + TypeVisitable; + type RegionOutlivesPredicate: Copy + Debug + Hash + Eq + TypeVisitable; + type TypeOutlivesPredicate: Copy + Debug + Hash + Eq + TypeVisitable; + type ProjectionPredicate: Copy + Debug + Hash + Eq + TypeVisitable; + type NormalizesTo: Copy + Debug + Hash + Eq + TypeVisitable; + type SubtypePredicate: Copy + Debug + Hash + Eq + TypeVisitable; + type CoercePredicate: Copy + Debug + Hash + Eq + TypeVisitable; + type ClosureKind: Copy + Debug + Hash + Eq + TypeVisitable; type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo]) -> Self::CanonicalVars; diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index af4b9eef14b92..6d575b8e44230 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -1,7 +1,7 @@ use std::fmt; use crate::{ - AliasTerm, AliasTy, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, + AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, Interner, NormalizesTo, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, }; @@ -22,6 +22,15 @@ macro_rules! define_display_via_print { } } +impl fmt::Display for Binder +where + I: IrPrint>, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + >>::print(self, fmt) + } +} + macro_rules! define_debug_via_print { ($($ty:ident),+ $(,)?) => { $( diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index fa9bda9a2f754..4a461b5b5f3c8 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -8,10 +8,14 @@ #[cfg(feature = "nightly")] extern crate self as rustc_type_ir; +#[cfg(feature = "nightly")] +use rustc_data_structures::sso::SsoHashSet; #[cfg(feature = "nightly")] use rustc_data_structures::sync::Lrc; #[cfg(feature = "nightly")] use rustc_macros::{Decodable, Encodable, HashStable_NoContext}; +#[cfg(not(feature = "nightly"))] +use std::collections::HashSet as SsoHashSet; use std::fmt; use std::hash::Hash; #[cfg(not(feature = "nightly"))] @@ -31,6 +35,7 @@ pub mod ty_kind; #[macro_use] mod macros; +mod binder; mod canonical; mod const_kind; mod debug; @@ -43,6 +48,7 @@ mod predicate_kind; mod region_kind; mod upcast; +pub use binder::*; pub use canonical::*; #[cfg(feature = "nightly")] pub use codec::*; @@ -374,6 +380,10 @@ impl inherent::BoundVarLike for BoundVar { fn var(self) -> BoundVar { self } + + fn assert_eq(self, _var: I::BoundVarKind) { + unreachable!("FIXME: We really should have a separate `BoundConst` for consts") + } } /// Represents the various closure traits in the language. This diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index c0619d782c681..b4f3d62f10e3a 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -6,10 +6,9 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEn use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; +use crate::upcast::Upcast; use crate::visit::TypeVisitableExt as _; -use crate::{ - AliasTy, AliasTyKind, DebugWithInfcx, InferCtxtLike, Interner, UnevaluatedConst, WithInfcx, -}; +use crate::{self as ty, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; /// A complete reference to a trait. These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: @@ -75,6 +74,16 @@ impl TraitRef { } } +impl ty::Binder> { + pub fn self_ty(&self) -> ty::Binder { + self.map_bound_ref(|tr| tr.self_ty()) + } + + pub fn def_id(&self) -> I::DefId { + self.skip_binder().def_id + } +} + #[derive(derivative::Derivative)] #[derivative( Clone(bound = ""), @@ -112,6 +121,22 @@ impl TraitPredicate { } } +impl ty::Binder> { + pub fn def_id(self) -> I::DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().def_id() + } + + pub fn self_ty(self) -> ty::Binder { + self.map_bound(|trait_ref| trait_ref.self_ty()) + } + + #[inline] + pub fn polarity(self) -> PredicatePolarity { + self.skip_binder().polarity + } +} + impl fmt::Debug for TraitPredicate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME(effects) printing? @@ -204,6 +229,34 @@ impl DebugWithInfcx for ExistentialPredicate { } } +impl ty::Binder> { + /// Given an existential predicate like `?Self: PartialEq` (e.g., derived from `dyn PartialEq`), + /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self` + /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq`, in our example). + pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> I::Clause { + match self.skip_binder() { + ExistentialPredicate::Trait(tr) => { + self.rebind(tr).with_self_ty(tcx, self_ty).upcast(tcx) + } + ExistentialPredicate::Projection(p) => { + self.rebind(p.with_self_ty(tcx, self_ty)).upcast(tcx) + } + ExistentialPredicate::AutoTrait(did) => { + let generics = tcx.generics_of(did); + let trait_ref = if generics.count() == 1 { + ty::TraitRef::new(tcx, did, [self_ty]) + } else { + // If this is an ill-formed auto trait, then synthesize + // new error args for the missing generics. + let err_args = GenericArgs::extend_with_error(tcx, did, &[self_ty.into()]); + ty::TraitRef::new(tcx, did, err_args) + }; + self.rebind(trait_ref).upcast(tcx) + } + } + } +} + /// An existential reference to a trait, where `Self` is erased. /// For example, the trait object `Trait<'a, 'b, X, Y>` is: /// ```ignore (illustrative) @@ -253,6 +306,20 @@ impl ExistentialTraitRef { } } +impl ty::Binder> { + pub fn def_id(&self) -> I::DefId { + self.skip_binder().def_id + } + + /// Object types don't have a self type specified. Therefore, when + /// we convert the principal trait-ref into a normal trait-ref, + /// you must give *some* self type. A common choice is `mk_err()` + /// or some placeholder type. + pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> ty::Binder> { + self.map_bound(|trait_ref| trait_ref.with_self_ty(tcx, self_ty)) + } +} + /// A `ProjectionPredicate` for an `ExistentialTraitRef`. #[derive(derivative::Derivative)] #[derivative( @@ -308,6 +375,16 @@ impl ExistentialProjection { } } +impl ty::Binder> { + pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> ty::Binder> { + self.map_bound(|p| p.with_self_ty(tcx, self_ty)) + } + + pub fn item_def_id(&self) -> I::DefId { + self.skip_binder().def_id + } +} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))] pub enum AliasTermKind { @@ -414,7 +491,7 @@ impl AliasTerm { AliasTerm { def_id, args, _use_alias_term_new_instead: () } } - pub fn expect_ty(self, interner: I) -> AliasTy { + pub fn expect_ty(self, interner: I) -> ty::AliasTy { match self.kind(interner) { AliasTermKind::ProjectionTy | AliasTermKind::InherentTy @@ -424,7 +501,7 @@ impl AliasTerm { panic!("Cannot turn `UnevaluatedConst` into `AliasTy`") } } - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () } + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () } } pub fn kind(self, interner: I) -> AliasTermKind { @@ -435,32 +512,32 @@ impl AliasTerm { match self.kind(interner) { AliasTermKind::ProjectionTy => Ty::new_alias( interner, - AliasTyKind::Projection, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Projection, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::InherentTy => Ty::new_alias( interner, - AliasTyKind::Inherent, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Inherent, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::OpaqueTy => Ty::new_alias( interner, - AliasTyKind::Opaque, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Opaque, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::WeakTy => Ty::new_alias( interner, - AliasTyKind::Weak, - AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, + ty::AliasTyKind::Weak, + ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }, ) .into(), AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => { I::Const::new_unevaluated( interner, - UnevaluatedConst::new(self.def_id, self.args), + ty::UnevaluatedConst::new(self.def_id, self.args), interner.type_of_instantiated(self.def_id, self.args), ) .into() @@ -514,14 +591,14 @@ impl AliasTerm { } } -impl From> for AliasTerm { - fn from(ty: AliasTy) -> Self { +impl From> for AliasTerm { + fn from(ty: ty::AliasTy) -> Self { AliasTerm { args: ty.args, def_id: ty.def_id, _use_alias_term_new_instead: () } } } -impl From> for AliasTerm { - fn from(ct: UnevaluatedConst) -> Self { +impl From> for AliasTerm { + fn from(ct: ty::UnevaluatedConst) -> Self { AliasTerm { args: ct.args, def_id: ct.def, _use_alias_term_new_instead: () } } } @@ -571,6 +648,40 @@ impl ProjectionPredicate { } } +impl ty::Binder> { + /// Returns the `DefId` of the trait of the associated item being projected. + #[inline] + pub fn trait_def_id(&self, tcx: I) -> I::DefId { + self.skip_binder().projection_term.trait_def_id(tcx) + } + + /// Get the trait ref required for this projection to be well formed. + /// Note that for generic associated types the predicates of the associated + /// type also need to be checked. + #[inline] + pub fn required_poly_trait_ref(&self, tcx: I) -> ty::Binder> { + // Note: unlike with `TraitRef::to_poly_trait_ref()`, + // `self.0.trait_ref` is permitted to have escaping regions. + // This is because here `self` has a `Binder` and so does our + // return value, so we are preserving the number of binding + // levels. + self.map_bound(|predicate| predicate.projection_term.trait_ref(tcx)) + } + + pub fn term(&self) -> ty::Binder { + self.map_bound(|predicate| predicate.term) + } + + /// The `DefId` of the `TraitItem` for the associated type. + /// + /// Note that this is not the `DefId` of the `TraitRef` containing this + /// associated type, which is in `tcx.associated_item(projection_def_id()).container`. + pub fn projection_def_id(&self) -> I::DefId { + // Ok to skip binder since trait `DefId` does not care about regions. + self.skip_binder().projection_term.def_id + } +} + impl fmt::Debug for ProjectionPredicate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_term, self.term) diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 629ea9fb839c9..38082bf3c16fb 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -8,7 +8,7 @@ use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Gen use std::fmt; use crate::inherent::*; -use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, TraitRef, WithInfcx}; +use crate::{self as ty, DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; use self::TyKind::*; @@ -514,7 +514,7 @@ impl AliasTy { /// For example, if this is a projection of `::Item<'a>`, /// then this function would return a `T: StreamingIterator` trait reference and /// `['a]` as the own args. - pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef, I::OwnItemArgs) { + pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef, I::OwnItemArgs) { debug_assert_eq!(self.kind(interner), AliasTyKind::Projection); interner.trait_ref_and_own_args_for_alias(self.def_id, self.args) } @@ -526,7 +526,7 @@ impl AliasTy { /// WARNING: This will drop the args for generic associated types /// consider calling [Self::trait_ref_and_own_args] to get those /// as well. - pub fn trait_ref(self, interner: I) -> TraitRef { + pub fn trait_ref(self, interner: I) -> ty::TraitRef { self.trait_ref_and_own_args(interner).0 } } @@ -982,6 +982,49 @@ impl FnSig { pub fn output(self) -> I::Ty { self.split_inputs_and_output().1 } + + pub fn is_fn_trait_compatible(self) -> bool { + let FnSig { safety, abi, c_variadic, .. } = self; + !c_variadic && safety.is_safe() && abi.is_rust() + } +} + +impl ty::Binder> { + #[inline] + pub fn inputs(self) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.inputs()) + } + + #[inline] + #[track_caller] + pub fn input(self, index: usize) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.inputs()[index]) + } + + pub fn inputs_and_output(self) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.inputs_and_output) + } + + #[inline] + pub fn output(self) -> ty::Binder { + self.map_bound(|fn_sig| fn_sig.output()) + } + + pub fn c_variadic(self) -> bool { + self.skip_binder().c_variadic + } + + pub fn safety(self) -> I::Safety { + self.skip_binder().safety + } + + pub fn abi(self) -> I::Abi { + self.skip_binder().abi + } + + pub fn is_fn_trait_compatible(&self) -> bool { + self.skip_binder().is_fn_trait_compatible() + } } impl fmt::Debug for FnSig { diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 3d4125f600ef8..6880c7b8cefce 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -90,7 +90,7 @@ pub trait TypeVisitor: Sized { #[cfg(not(feature = "nightly"))] type Result: VisitorResult; - fn visit_binder>(&mut self, t: &I::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { t.super_visit_with(self) } @@ -376,11 +376,11 @@ impl std::fmt::Debug for HasTypeFlagsVisitor { impl TypeVisitor for HasTypeFlagsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &I::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { // If we're looking for the HAS_BINDER_VARS flag, check if the // binder has vars. This won't be present in the binder's bound // value, so we need to check here too. - if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.has_no_bound_vars() { + if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() { return ControlFlow::Break(FoundFlags); } @@ -476,7 +476,7 @@ struct HasEscapingVarsVisitor { impl TypeVisitor for HasEscapingVarsVisitor { type Result = ControlFlow; - fn visit_binder>(&mut self, t: &I::Binder) -> Self::Result { + fn visit_binder>(&mut self, t: &ty::Binder) -> Self::Result { self.outer_index.shift_in(1); let result = t.super_visit_with(self); self.outer_index.shift_out(1); From 1c8230ea3c54e3f020f1df75d77810e8a5689410 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 21 May 2024 11:07:54 -0400 Subject: [PATCH 22/31] Uplift OutlivesPredicate, remove a bunch of unnecessary associated types from Interner --- .../src/type_check/constraint_conversion.rs | 4 +-- .../rustc_hir_analysis/src/outlives/utils.rs | 2 +- .../rustc_infer/src/infer/outlives/env.rs | 3 +- .../rustc_infer/src/infer/outlives/verify.rs | 6 ++-- compiler/rustc_middle/src/infer/canonical.rs | 4 +-- compiler/rustc_middle/src/ty/context.rs | 10 ------ compiler/rustc_middle/src/ty/predicate.rs | 33 +++++++------------ compiler/rustc_middle/src/ty/print/pretty.rs | 8 ++--- .../rustc_smir/src/rustc_smir/convert/ty.rs | 7 ++-- .../query/type_op/implied_outlives_bounds.rs | 3 +- compiler/rustc_type_ir/src/const_kind.rs | 4 +-- compiler/rustc_type_ir/src/interner.rs | 9 ----- compiler/rustc_type_ir/src/ir_print.rs | 12 ++++++- compiler/rustc_type_ir/src/predicate.rs | 29 ++++++++++++++++ compiler/rustc_type_ir/src/predicate_kind.rs | 20 +++++------ 15 files changed, 79 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 5aa8fe213814d..b23ad2e158425 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -136,7 +136,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { fn convert( &mut self, - predicate: ty::OutlivesPredicate, ty::Region<'tcx>>, + predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, constraint_category: ConstraintCategory<'tcx>, ) { debug!("generate: constraints at: {:#?}", self.locations); @@ -276,7 +276,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { &self, ty: Ty<'tcx>, next_outlives_predicates: &mut Vec<( - ty::OutlivesPredicate, ty::Region<'tcx>>, + ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, ConstraintCategory<'tcx>, )>, ) -> Ty<'tcx> { diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 95290bbecf2c4..5086c2af3f658 100644 --- a/compiler/rustc_hir_analysis/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -9,7 +9,7 @@ use smallvec::smallvec; /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred /// must be added to the struct header. pub(crate) type RequiredPredicates<'tcx> = - FxIndexMap, ty::Region<'tcx>>, Span>; + FxIndexMap>, Span>; /// Given a requirement `T: 'a` or `'b: 'a`, deduce the /// outlives_component and add it to `required_predicates` diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index c44a5082f68bd..5bcb4f29364d7 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -64,8 +64,7 @@ struct OutlivesEnvironmentBuilder<'tcx> { /// "Region-bound pairs" tracks outlives relations that are known to /// be true, either because of explicit where-clauses like `T: 'a` or /// because of implied bounds. -pub type RegionBoundPairs<'tcx> = - FxIndexSet, Region<'tcx>>>; +pub type RegionBoundPairs<'tcx> = FxIndexSet>>; impl<'tcx> OutlivesEnvironment<'tcx> { /// Create a builder using `ParamEnv` and add explicit outlives bounds into it. diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index bd981c2056770..7e977b9b95455 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -94,7 +94,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub fn approx_declared_bounds_from_env( &self, alias_ty: ty::AliasTy<'tcx>, - ) -> Vec, ty::Region<'tcx>>>> { + ) -> Vec> { let erased_alias_ty = self.tcx.erase_regions(alias_ty.to_ty(self.tcx)); self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty) } @@ -193,7 +193,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env( &self, generic_ty: Ty<'tcx>, - ) -> Vec, ty::Region<'tcx>>>> { + ) -> Vec> { assert!(matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_))); self.declared_generic_bounds_from_env_for_erased_ty(generic_ty) } @@ -213,7 +213,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn declared_generic_bounds_from_env_for_erased_ty( &self, erased_ty: Ty<'tcx>, - ) -> Vec, ty::Region<'tcx>>>> { + ) -> Vec> { let tcx = self.tcx; // To start, collect bounds from user environment. Note that diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 49bf03e9c7544..dba71d88f404b 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -32,7 +32,7 @@ use std::collections::hash_map::Entry; use crate::infer::MemberConstraint; use crate::mir::ConstraintCategory; use crate::ty::GenericArg; -use crate::ty::{self, List, Region, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; +use crate::ty::{self, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt}; pub type Canonical<'tcx, V> = ir::Canonical, V>; pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo>; @@ -141,7 +141,7 @@ impl<'tcx, R> QueryResponse<'tcx, R> { } pub type QueryOutlivesConstraint<'tcx> = - (ty::OutlivesPredicate, Region<'tcx>>, ConstraintCategory<'tcx>); + (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>); TrivialTypeTraversalImpls! { crate::infer::canonical::Certainty, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a457319c5f89f..896114e2483c7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -121,7 +121,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type Abi = abi::Abi; type Const = ty::Const<'tcx>; - type AliasConst = ty::UnevaluatedConst<'tcx>; type PlaceholderConst = ty::PlaceholderConst; type ParamConst = ty::ParamConst; type BoundConst = ty::BoundVar; @@ -137,15 +136,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type ParamEnv = ty::ParamEnv<'tcx>; type Predicate = Predicate<'tcx>; type Clause = Clause<'tcx>; - type TraitPredicate = ty::TraitPredicate<'tcx>; - type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>; - type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>; - type ProjectionPredicate = ty::ProjectionPredicate<'tcx>; - type NormalizesTo = ty::NormalizesTo<'tcx>; - type SubtypePredicate = ty::SubtypePredicate<'tcx>; - - type CoercePredicate = ty::CoercePredicate<'tcx>; - type ClosureKind = ty::ClosureKind; type Clauses = ty::Clauses<'tcx>; diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 067d490078d37..293cc0a7eca3e 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -1,9 +1,7 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::intern::Interned; use rustc_hir::def_id::DefId; -use rustc_macros::{ - extension, HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, -}; +use rustc_macros::{extension, HashStable}; use rustc_type_ir as ir; use std::cmp::Ordering; @@ -24,6 +22,15 @@ pub type PredicateKind<'tcx> = ir::PredicateKind>; pub type NormalizesTo<'tcx> = ir::NormalizesTo>; pub type CoercePredicate<'tcx> = ir::CoercePredicate>; pub type SubtypePredicate<'tcx> = ir::SubtypePredicate>; +pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate, T>; +pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>; +pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>; +pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; +pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; +pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; +pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; +pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; +pub type PolyProjectionPredicate<'tcx> = ty::Binder<'tcx, ProjectionPredicate<'tcx>>; /// A statement that can be proven by a trait solver. This includes things that may /// show up in where clauses, such as trait predicates and projection predicates, @@ -405,20 +412,6 @@ impl<'tcx> Clause<'tcx> { } } -pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>; - -/// `A: B` -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] -pub struct OutlivesPredicate(pub A, pub B); -pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; -pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>; -pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>; -pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>; -pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>; -pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>; - pub trait ToPolyTraitRef<'tcx> { fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>; } @@ -545,10 +538,8 @@ impl<'tcx> UpcastFrom, PolyRegionOutlivesPredicate<'tcx>> for Predi } } -impl<'tcx> UpcastFrom, OutlivesPredicate, ty::Region<'tcx>>> - for Predicate<'tcx> -{ - fn upcast_from(from: OutlivesPredicate, ty::Region<'tcx>>, tcx: TyCtxt<'tcx>) -> Self { +impl<'tcx> UpcastFrom, TypeOutlivesPredicate<'tcx>> for Predicate<'tcx> { + fn upcast_from(from: TypeOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self { ty::Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(from))).upcast(tcx) } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f0bd071e451f8..a1ead1bb59f54 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2860,10 +2860,9 @@ where } } -impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate +impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate<'tcx, T> where T: Print<'tcx, P>, - U: Print<'tcx, P>, { fn print(&self, cx: &mut P) -> Result<(), PrintError> { define_scoped_cx!(cx); @@ -3016,10 +3015,7 @@ forward_display_to_print! { ty::Region<'tcx>, Ty<'tcx>, &'tcx ty::List>, - ty::Const<'tcx>, - - ty::OutlivesPredicate, ty::Region<'tcx>>, - ty::OutlivesPredicate, ty::Region<'tcx>> + ty::Const<'tcx> } define_print! { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index 15447983abbc2..be20924670c37 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -707,12 +707,11 @@ impl<'tcx> Stable<'tcx> for ty::TraitPredicate<'tcx> { } } -impl<'tcx, A, B, U, V> Stable<'tcx> for ty::OutlivesPredicate +impl<'tcx, T> Stable<'tcx> for ty::OutlivesPredicate<'tcx, T> where - A: Stable<'tcx, T = U>, - B: Stable<'tcx, T = V>, + T: Stable<'tcx>, { - type T = stable_mir::ty::OutlivesPredicate; + type T = stable_mir::ty::OutlivesPredicate; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { let ty::OutlivesPredicate(a, b) = self; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index f7e84a46639d4..00cc77e71e72a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -162,8 +162,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); let mut wf_args = vec![ty.into()]; - let mut outlives_bounds: Vec, ty::Region<'tcx>>> = - vec![]; + let mut outlives_bounds: Vec>> = vec![]; while let Some(arg) = wf_args.pop() { if !checked_wf_args.insert(arg) { diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index af07e9ff96bf8..7076df2893feb 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -5,7 +5,7 @@ use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use std::fmt; -use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +use crate::{self as ty, DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; use self::ConstKind::*; @@ -29,7 +29,7 @@ pub enum ConstKind { /// An unnormalized const item such as an anon const or assoc const or free const item. /// Right now anything other than anon consts does not actually work properly but this /// should - Unevaluated(I::AliasConst), + Unevaluated(ty::UnevaluatedConst), /// Used to hold computed value. Value(I::ValueConst), diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 2ab81dfff3268..9b8bb210ff424 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -76,7 +76,6 @@ pub trait Interner: // Kinds of consts type Const: Const; - type AliasConst: Copy + DebugWithInfcx + Hash + Eq; type PlaceholderConst: PlaceholderLike; type ParamConst: Copy + Debug + Hash + Eq; type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike; @@ -94,14 +93,6 @@ pub trait Interner: type ParamEnv: Copy + Debug + Hash + Eq; type Predicate: Predicate; type Clause: Clause; - type TraitPredicate: Copy + Debug + Hash + Eq + TypeVisitable; - type RegionOutlivesPredicate: Copy + Debug + Hash + Eq + TypeVisitable; - type TypeOutlivesPredicate: Copy + Debug + Hash + Eq + TypeVisitable; - type ProjectionPredicate: Copy + Debug + Hash + Eq + TypeVisitable; - type NormalizesTo: Copy + Debug + Hash + Eq + TypeVisitable; - type SubtypePredicate: Copy + Debug + Hash + Eq + TypeVisitable; - type CoercePredicate: Copy + Debug + Hash + Eq + TypeVisitable; - type ClosureKind: Copy + Debug + Hash + Eq + TypeVisitable; type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo]) -> Self::CanonicalVars; diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs index 6d575b8e44230..d57d0816680bf 100644 --- a/compiler/rustc_type_ir/src/ir_print.rs +++ b/compiler/rustc_type_ir/src/ir_print.rs @@ -2,7 +2,8 @@ use std::fmt; use crate::{ AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig, - Interner, NormalizesTo, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, + Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, SubtypePredicate, + TraitPredicate, TraitRef, }; pub trait IrPrint { @@ -58,3 +59,12 @@ define_display_via_print!( ); define_debug_via_print!(TraitRef, ExistentialTraitRef, ExistentialProjection); + +impl fmt::Display for OutlivesPredicate +where + I: IrPrint>, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + >>::print(self, fmt) + } +} diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index b4f3d62f10e3a..4e12c6b3d6713 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -6,10 +6,39 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEn use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; +use crate::lift::Lift; use crate::upcast::Upcast; use crate::visit::TypeVisitableExt as _; use crate::{self as ty, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx}; +/// `A: 'region` +#[derive(derivative::Derivative)] +#[derivative( + Clone(bound = "A: Clone"), + Copy(bound = "A: Copy"), + Hash(bound = "A: Hash"), + PartialEq(bound = "A: PartialEq"), + Eq(bound = "A: Eq"), + Debug(bound = "A: fmt::Debug") +)] +#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))] +pub struct OutlivesPredicate(pub A, pub I::Region); + +// FIXME: We manually derive `Lift` because the `derive(Lift_Generic)` doesn't +// understand how to turn `A` to `A::Lifted` in the output `type Lifted`. +impl Lift for OutlivesPredicate +where + A: Lift, + I::Region: Lift, +{ + type Lifted = OutlivesPredicate; + + fn lift_to_tcx(self, tcx: U) -> Option { + Some(OutlivesPredicate(self.0.lift_to_tcx(tcx)?, self.1.lift_to_tcx(tcx)?)) + } +} + /// A complete reference to a trait. These take numerous guises in syntax, /// but perhaps the most recognizable form is in a where-clause: /// ```ignore (illustrative) diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index c477ab14153e2..efe270ed60836 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -3,7 +3,7 @@ use rustc_macros::{Decodable, Encodable, HashStable_NoContext, TyDecodable, TyEn use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; use std::fmt; -use crate::Interner; +use crate::{self as ty, Interner}; /// A clause is something that can appear in where bounds or be inferred /// by implied bounds. @@ -15,17 +15,17 @@ pub enum ClauseKind { /// Corresponds to `where Foo: Bar`. `Foo` here would be /// the `Self` type of the trait reference and `A`, `B`, and `C` /// would be the type parameters. - Trait(I::TraitPredicate), + Trait(ty::TraitPredicate), - /// `where 'a: 'b` - RegionOutlives(I::RegionOutlivesPredicate), + /// `where 'a: 'r` + RegionOutlives(ty::OutlivesPredicate), - /// `where T: 'a` - TypeOutlives(I::TypeOutlivesPredicate), + /// `where T: 'r` + TypeOutlives(ty::OutlivesPredicate), /// `where ::Name == X`, approximately. /// See the `ProjectionPredicate` struct for details. - Projection(I::ProjectionPredicate), + Projection(ty::ProjectionPredicate), /// Ensures that a const generic argument to a parameter `const N: u8` /// is of type `u8`. @@ -75,7 +75,7 @@ pub enum PredicateKind { /// This obligation is created most often when we have two /// unresolved type variables and hence don't have enough /// information to process the subtyping obligation yet. - Subtype(I::SubtypePredicate), + Subtype(ty::SubtypePredicate), /// `T1` coerced to `T2` /// @@ -85,7 +85,7 @@ pub enum PredicateKind { /// obligation yet. At the moment, we actually process coercions /// very much like subtyping and don't handle the full coercion /// logic. - Coerce(I::CoercePredicate), + Coerce(ty::CoercePredicate), /// Constants must be equal. The first component is the const that is expected. ConstEquate(I::Const, I::Const), @@ -102,7 +102,7 @@ pub enum PredicateKind { /// `T as Trait>::Assoc`, `Projection(::Assoc, ?x)` constrains `?x` /// to `::Assoc` while `NormalizesTo(::Assoc, ?x)` /// results in `NoSolution`. - NormalizesTo(I::NormalizesTo), + NormalizesTo(ty::NormalizesTo), /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. /// This predicate requires two terms to be equal to eachother. From 95150d72465db491f4b04b73545e106462bd003b Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 3 May 2024 21:17:57 -0400 Subject: [PATCH 23/31] Add a footer in FileEncoder and check for it in MemDecoder --- compiler/rustc_codegen_ssa/src/lib.rs | 5 ++- compiler/rustc_driver_impl/messages.ftl | 2 + compiler/rustc_driver_impl/src/lib.rs | 8 ++-- .../src/session_diagnostics.rs | 6 +++ compiler/rustc_incremental/messages.ftl | 2 + compiler/rustc_incremental/src/errors.rs | 6 +++ .../rustc_incremental/src/persist/load.rs | 21 ++++++++-- compiler/rustc_metadata/src/locator.rs | 7 +++- compiler/rustc_metadata/src/rmeta/decoder.rs | 28 ++++++++++--- .../src/rmeta/def_path_hash_map.rs | 2 +- .../rustc_middle/src/query/on_disk_cache.rs | 39 +++++++++---------- .../src/dep_graph/serialized.rs | 10 ++--- compiler/rustc_serialize/src/opaque.rs | 16 +++++--- compiler/rustc_serialize/tests/leb128.rs | 13 +++++-- compiler/rustc_serialize/tests/opaque.rs | 2 +- src/librustdoc/scrape_examples.rs | 4 +- 16 files changed, 117 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 4eb24d71009aa..9bf8fde55fce0 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -195,6 +195,7 @@ pub enum CodegenErrors { EmptyVersionNumber, EncodingVersionMismatch { version_array: String, rlink_version: u32 }, RustcVersionMismatch { rustc_version: String }, + CorruptFile, } pub fn provide(providers: &mut Providers) { @@ -265,7 +266,9 @@ impl CodegenResults { }); } - let mut decoder = MemDecoder::new(&data[4..], 0); + let Some(mut decoder) = MemDecoder::new(&data[4..], 0) else { + return Err(CodegenErrors::CorruptFile); + }; let rustc_version = decoder.read_str(); if rustc_version != sess.cfg_version { return Err(CodegenErrors::RustcVersionMismatch { diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl index 5b39248302e7a..31837e0176433 100644 --- a/compiler/rustc_driver_impl/messages.ftl +++ b/compiler/rustc_driver_impl/messages.ftl @@ -10,6 +10,8 @@ driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error} driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}` driver_impl_ice_version = rustc {$version} running on {$triple} +driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}` + driver_impl_rlink_empty_version_number = The input does not contain version number driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index ba6b9ef078467..30cc41ecea046 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -96,7 +96,7 @@ mod signal_handler { use crate::session_diagnostics::{ RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch, - RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead, + RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, }; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -645,8 +645,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { match err { CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType), CodegenErrors::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber), - CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => sess - .dcx() + CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => dcx .emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }), CodegenErrors::RustcVersionMismatch { rustc_version } => { dcx.emit_fatal(RLinkRustcVersionMismatch { @@ -654,6 +653,9 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { current_version: sess.cfg_version, }) } + CodegenErrors::CorruptFile => { + dcx.emit_fatal(RlinkCorruptFile { file: file.display().to_string() }); + } }; } }; diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 1a9683e840afd..864b122233a46 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -32,6 +32,12 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> { #[diag(driver_impl_rlink_no_a_file)] pub(crate) struct RlinkNotAFile; +#[derive(Diagnostic)] +#[diag(driver_impl_rlink_corrupt_file)] +pub(crate) struct RlinkCorruptFile { + pub file: String, +} + #[derive(Diagnostic)] #[diag(driver_impl_ice)] pub(crate) struct Ice; diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl index e74173b24a97b..de2177ebb6e0f 100644 --- a/compiler/rustc_incremental/messages.ftl +++ b/compiler/rustc_incremental/messages.ftl @@ -21,6 +21,8 @@ incremental_cargo_help_2 = incremental_copy_workproduct_to_cache = error copying object file `{$from}` to incremental directory as `{$to}`: {$err} +incremental_corrupt_file = corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant. + incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err} incremental_create_incr_comp_dir = diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index 61bb0353a9f4b..e94a7fb876bbe 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -306,3 +306,9 @@ pub struct DeleteWorkProduct<'a> { pub path: &'a Path, pub err: std::io::Error, } + +#[derive(Diagnostic)] +#[diag(incremental_corrupt_file)] +pub struct CorruptFile<'a> { + pub path: &'a Path, +} diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 26aaa24771fe8..c96d22ce45d73 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -115,7 +115,12 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { // Decode the list of work_products - let mut work_product_decoder = MemDecoder::new(&work_products_data[..], start_pos); + let Some(mut work_product_decoder) = + MemDecoder::new(&work_products_data[..], start_pos) + else { + sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path }); + return LoadResult::DataOutOfDate; + }; let work_products: Vec = Decodable::decode(&mut work_product_decoder); @@ -145,7 +150,10 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err), LoadResult::Ok { data: (bytes, start_pos) } => { - let mut decoder = MemDecoder::new(&bytes, start_pos); + let Some(mut decoder) = MemDecoder::new(&bytes, start_pos) else { + sess.dcx().emit_warn(errors::CorruptFile { path: &path }); + return LoadResult::DataOutOfDate; + }; let prev_commandline_args_hash = u64::decode(&mut decoder); if prev_commandline_args_hash != expected_hash { @@ -181,9 +189,14 @@ pub fn load_query_result_cache(sess: &Session) -> Option> { let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache"); - match load_data(&query_cache_path(sess), sess) { + let path = query_cache_path(sess); + match load_data(&path, sess) { LoadResult::Ok { data: (bytes, start_pos) } => { - Some(OnDiskCache::new(sess, bytes, start_pos)) + let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|| { + sess.dcx().emit_warn(errors::CorruptFile { path: &path }); + OnDiskCache::new_empty(sess.source_map()) + }); + Some(cache) } _ => Some(OnDiskCache::new_empty(sess.source_map())), } diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 7de03be6da627..fa33eccd2a1c2 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -853,7 +853,12 @@ fn get_metadata_section<'p>( slice_owned(mmap, Deref::deref) } }; - let blob = MetadataBlob(raw_bytes); + let Some(blob) = MetadataBlob::new(raw_bytes) else { + return Err(MetadataError::LoadFailure(format!( + "corrupt metadata encountered in {}", + filename.display() + ))); + }; match blob.check_compatibility(cfg_version) { Ok(()) => Ok(blob), Err(None) => Err(MetadataError::LoadFailure(format!( diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bb68c6eaf092e..1058f58da131c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -40,10 +40,9 @@ use rustc_span::hygiene::HygieneDecodeContext; mod cstore_impl; /// A reference to the raw binary version of crate metadata. -/// A `MetadataBlob` internally is just a reference counted pointer to -/// the actual data, so cloning it is cheap. -#[derive(Clone)] -pub(crate) struct MetadataBlob(pub(crate) OwnedSlice); +/// This struct applies [`MemDecoder`]'s validation when constructed +/// so that later constructions are guaranteed to succeed. +pub(crate) struct MetadataBlob(OwnedSlice); impl std::ops::Deref for MetadataBlob { type Target = [u8]; @@ -54,6 +53,16 @@ impl std::ops::Deref for MetadataBlob { } } +impl MetadataBlob { + pub fn new(slice: OwnedSlice) -> Option { + if MemDecoder::new(&*slice, 0).is_some() { Some(Self(slice)) } else { None } + } + + pub fn bytes(&self) -> &OwnedSlice { + &self.0 + } +} + /// A map from external crate numbers (as decoded from some crate file) to /// local crate numbers (as generated during this session). Each external /// crate may refer to types in other external crates, and each has their @@ -165,7 +174,14 @@ pub(super) trait Metadata<'a, 'tcx>: Copy { fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> { let tcx = self.tcx(); DecodeContext { - opaque: MemDecoder::new(self.blob(), pos), + // FIXME: This unwrap should never panic because we check that it won't when creating + // `MetadataBlob`. Ideally we'd just have a `MetadataDecoder` and hand out subslices of + // it as we do elsewhere in the compiler using `MetadataDecoder::split_at`. But we own + // the data for the decoder so holding onto the `MemDecoder` too would make us a + // self-referential struct which is downright goofy because `MetadataBlob` is already + // self-referential. Probably `MemDecoder` should contain an `OwnedSlice`, but that + // demands a significant refactoring due to our crate graph. + opaque: MemDecoder::new(self.blob(), pos).unwrap(), cdata: self.cdata(), blob: self.blob(), sess: self.sess().or(tcx.map(|tcx| tcx.sess)), @@ -393,7 +409,7 @@ impl<'a, 'tcx> TyDecoder for DecodeContext<'a, 'tcx> { where F: FnOnce(&mut Self) -> R, { - let new_opaque = MemDecoder::new(self.opaque.data(), pos); + let new_opaque = self.opaque.split_at(pos); let old_opaque = mem::replace(&mut self.opaque, new_opaque); let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode); let r = f(self); diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 9950bc1c31f70..861bf6b2769e9 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> Decodable> for DefPathHashMapRef<'static> fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefPathHashMapRef<'static> { let len = d.read_usize(); let pos = d.position(); - let o = d.blob().clone().0.slice(|blob| &blob[pos..pos + len]); + let o = d.blob().bytes().clone().slice(|blob| &blob[pos..pos + len]); // Although we already have the data we need via the `OwnedSlice`, we still need // to advance the `DecodeContext`'s position so it's in a valid state after diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 2dcb58729ff96..6815e4263d56c 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -154,24 +154,22 @@ impl EncodedSourceFileId { impl<'sess> OnDiskCache<'sess> { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. - pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Self { - debug_assert!(sess.opts.incremental.is_some()); - - // Wrap in a scope so we can borrow `data`. - let footer: Footer = { - let mut decoder = MemDecoder::new(&data, start_pos); - - // Decode the *position* of the footer, which can be found in the - // last 8 bytes of the file. - let footer_pos = decoder - .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| { - IntEncodedWithFixedSize::decode(decoder).0 as usize - }); - // Decode the file footer, which contains all the lookup tables, etc. - decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER)) - }; + pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Option { + assert!(sess.opts.incremental.is_some()); - Self { + let mut decoder = MemDecoder::new(&data, start_pos)?; + + // Decode the *position* of the footer, which can be found in the + // last 8 bytes of the file. + let footer_pos = decoder + .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| { + IntEncodedWithFixedSize::decode(decoder).0 as usize + }); + // Decode the file footer, which contains all the lookup tables, etc. + let footer: Footer = + decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER)); + + Some(Self { serialized_data: RwLock::new(Some(data)), file_index_to_stable_id: footer.file_index_to_stable_id, file_index_to_file: Default::default(), @@ -184,7 +182,7 @@ impl<'sess> OnDiskCache<'sess> { expn_data: footer.expn_data, foreign_expn_data: footer.foreign_expn_data, hygiene_context: Default::default(), - } + }) } pub fn new_empty(source_map: &'sess SourceMap) -> Self { @@ -437,7 +435,8 @@ impl<'sess> OnDiskCache<'sess> { let serialized_data = self.serialized_data.read(); let mut decoder = CacheDecoder { tcx, - opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()), + opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()) + .unwrap(), source_map: self.source_map, file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, @@ -558,7 +557,7 @@ impl<'a, 'tcx> TyDecoder for CacheDecoder<'a, 'tcx> { { debug_assert!(pos < self.opaque.len()); - let new_opaque = MemDecoder::new(self.opaque.data(), pos); + let new_opaque = self.opaque.split_at(pos); let old_opaque = mem::replace(&mut self.opaque, new_opaque); let r = f(self); self.opaque = old_opaque; diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index b426bb888f460..8e91d9dd60b3c 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -182,15 +182,13 @@ impl SerializedDepGraph { pub fn decode(d: &mut MemDecoder<'_>) -> Arc { // The last 16 bytes are the node count and edge count. debug!("position: {:?}", d.position()); - let (node_count, edge_count, graph_size) = - d.with_position(d.len() - 3 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| { + let (node_count, edge_count) = + d.with_position(d.len() - 2 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| { debug!("position: {:?}", d.position()); let node_count = IntEncodedWithFixedSize::decode(d).0 as usize; let edge_count = IntEncodedWithFixedSize::decode(d).0 as usize; - let graph_size = IntEncodedWithFixedSize::decode(d).0 as usize; - (node_count, edge_count, graph_size) + (node_count, edge_count) }); - assert_eq!(d.len(), graph_size); debug!("position: {:?}", d.position()); debug!(?node_count, ?edge_count); @@ -606,8 +604,6 @@ impl EncoderState { debug!("position: {:?}", encoder.position()); IntEncodedWithFixedSize(node_count).encode(&mut encoder); IntEncodedWithFixedSize(edge_count).encode(&mut encoder); - let graph_size = encoder.position() + IntEncodedWithFixedSize::ENCODED_SIZE; - IntEncodedWithFixedSize(graph_size as u64).encode(&mut encoder); debug!("position: {:?}", encoder.position()); // Drop the encoder so that nothing is written after the counts. let result = encoder.finish(); diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index eec83c02d3537..1b4b1d2436f47 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -17,6 +17,8 @@ use crate::int_overflow::DebugStrictAdd; pub type FileEncodeResult = Result; +const FOOTER: &[u8] = b"rust-end-file"; + /// The size of the buffer in `FileEncoder`. const BUF_SIZE: usize = 8192; @@ -181,6 +183,7 @@ impl FileEncoder { } pub fn finish(&mut self) -> FileEncodeResult { + self.write_all(FOOTER); self.flush(); #[cfg(debug_assertions)] { @@ -261,15 +264,18 @@ pub struct MemDecoder<'a> { impl<'a> MemDecoder<'a> { #[inline] - pub fn new(data: &'a [u8], position: usize) -> MemDecoder<'a> { + pub fn new(data: &'a [u8], position: usize) -> Option> { + let data = data.strip_suffix(FOOTER)?; let Range { start, end } = data.as_ptr_range(); - MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData } + Some(MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData }) } #[inline] - pub fn data(&self) -> &'a [u8] { - // SAFETY: This recovers the original slice, only using members we never modify. - unsafe { std::slice::from_raw_parts(self.start, self.len()) } + pub fn split_at(&self, position: usize) -> MemDecoder<'a> { + assert!(position <= self.len()); + // SAFETY: We checked above that this offset is within the original slice + let current = unsafe { self.start.add(position) }; + MemDecoder { start: self.start, current, end: self.end, _marker: PhantomData } } #[inline] diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index dc9b32a968b52..2bf4f38d3c346 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -1,4 +1,5 @@ use rustc_serialize::leb128::*; +use rustc_serialize::opaque::MemDecoder; use rustc_serialize::Decoder; macro_rules! impl_test_unsigned_leb128 { @@ -25,13 +26,15 @@ macro_rules! impl_test_unsigned_leb128 { let n = $write_fn_name(&mut buf, x); stream.extend(&buf[..n]); } + let stream_end = stream.len(); + stream.extend(b"rust-end-file"); - let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0); + let mut decoder = MemDecoder::new(&stream, 0).unwrap(); for &expected in &values { let actual = $read_fn_name(&mut decoder); assert_eq!(expected, actual); } - assert_eq!(stream.len(), decoder.position()); + assert_eq!(stream_end, decoder.position()); } }; } @@ -72,13 +75,15 @@ macro_rules! impl_test_signed_leb128 { let n = $write_fn_name(&mut buf, x); stream.extend(&buf[..n]); } + let stream_end = stream.len(); + stream.extend(b"rust-end-file"); - let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0); + let mut decoder = MemDecoder::new(&stream, 0).unwrap(); for &expected in &values { let actual = $read_fn_name(&mut decoder); assert_eq!(expected, actual); } - assert_eq!(stream.len(), decoder.position()); + assert_eq!(stream_end, decoder.position()); } }; } diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs index 45ff85f38d2bf..833151d82be35 100644 --- a/compiler/rustc_serialize/tests/opaque.rs +++ b/compiler/rustc_serialize/tests/opaque.rs @@ -42,7 +42,7 @@ fn check_round_trip< encoder.finish().unwrap(); let data = fs::read(&tmpfile).unwrap(); - let mut decoder = MemDecoder::new(&data[..], 0); + let mut decoder = MemDecoder::new(&data[..], 0).unwrap(); for value in values { let decoded = Decodable::decode(&mut decoder); assert_eq!(value, decoded); diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 9c9b386edda7c..0bf98886965c2 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -344,7 +344,9 @@ pub(crate) fn load_call_locations( Ok(bytes) => bytes, Err(e) => dcx.fatal(format!("failed to load examples: {e}")), }; - let mut decoder = MemDecoder::new(&bytes, 0); + let Some(mut decoder) = MemDecoder::new(&bytes, 0) else { + dcx.fatal(format!("Corrupt metadata encountered in {path}")) + }; let calls = AllCallLocations::decode(&mut decoder); for (function, fn_calls) in calls.into_iter() { From c3a606237d9c01e4622b267031596bf632567e6f Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 21 May 2024 20:12:07 -0400 Subject: [PATCH 24/31] PR feedback --- compiler/rustc_codegen_ssa/src/lib.rs | 2 +- compiler/rustc_driver_impl/src/lib.rs | 2 +- compiler/rustc_driver_impl/src/session_diagnostics.rs | 4 ++-- compiler/rustc_incremental/src/persist/load.rs | 6 +++--- compiler/rustc_metadata/src/locator.rs | 2 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 7 +++++-- compiler/rustc_middle/src/query/on_disk_cache.rs | 7 +++++-- compiler/rustc_serialize/src/opaque.rs | 10 +++++----- compiler/rustc_serialize/tests/leb128.rs | 5 +++-- src/librustdoc/scrape_examples.rs | 2 +- 10 files changed, 27 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 9bf8fde55fce0..66a7a2e090ae6 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -266,7 +266,7 @@ impl CodegenResults { }); } - let Some(mut decoder) = MemDecoder::new(&data[4..], 0) else { + let Ok(mut decoder) = MemDecoder::new(&data[4..], 0) else { return Err(CodegenErrors::CorruptFile); }; let rustc_version = decoder.read_str(); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 30cc41ecea046..5532eff7be661 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -654,7 +654,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { }) } CodegenErrors::CorruptFile => { - dcx.emit_fatal(RlinkCorruptFile { file: file.display().to_string() }); + dcx.emit_fatal(RlinkCorruptFile { file }); } }; } diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 864b122233a46..449878f28c4e4 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -34,8 +34,8 @@ pub(crate) struct RlinkNotAFile; #[derive(Diagnostic)] #[diag(driver_impl_rlink_corrupt_file)] -pub(crate) struct RlinkCorruptFile { - pub file: String, +pub(crate) struct RlinkCorruptFile<'a> { + pub file: &'a std::path::Path, } #[derive(Diagnostic)] diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index c96d22ce45d73..9e6ce06678513 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -115,7 +115,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { // Decode the list of work_products - let Some(mut work_product_decoder) = + let Ok(mut work_product_decoder) = MemDecoder::new(&work_products_data[..], start_pos) else { sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path }); @@ -150,7 +150,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult<(Arc, WorkPr LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err), LoadResult::Ok { data: (bytes, start_pos) } => { - let Some(mut decoder) = MemDecoder::new(&bytes, start_pos) else { + let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else { sess.dcx().emit_warn(errors::CorruptFile { path: &path }); return LoadResult::DataOutOfDate; }; @@ -192,7 +192,7 @@ pub fn load_query_result_cache(sess: &Session) -> Option> { let path = query_cache_path(sess); match load_data(&path, sess) { LoadResult::Ok { data: (bytes, start_pos) } => { - let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|| { + let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| { sess.dcx().emit_warn(errors::CorruptFile { path: &path }); OnDiskCache::new_empty(sess.source_map()) }); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index fa33eccd2a1c2..6ff19974c1e79 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -853,7 +853,7 @@ fn get_metadata_section<'p>( slice_owned(mmap, Deref::deref) } }; - let Some(blob) = MetadataBlob::new(raw_bytes) else { + let Ok(blob) = MetadataBlob::new(raw_bytes) else { return Err(MetadataError::LoadFailure(format!( "corrupt metadata encountered in {}", filename.display() diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1058f58da131c..f91e121a240ed 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -54,10 +54,13 @@ impl std::ops::Deref for MetadataBlob { } impl MetadataBlob { - pub fn new(slice: OwnedSlice) -> Option { - if MemDecoder::new(&*slice, 0).is_some() { Some(Self(slice)) } else { None } + /// Runs the [`MemDecoder`] validation and if it passes, constructs a new [`MetadataBlob`]. + pub fn new(slice: OwnedSlice) -> Result { + if MemDecoder::new(&slice, 0).is_ok() { Ok(Self(slice)) } else { Err(()) } } + /// Since this has passed the validation of [`MetadataBlob::new`], this returns bytes which are + /// known to pass the [`MemDecoder`] validation. pub fn bytes(&self) -> &OwnedSlice { &self.0 } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 6815e4263d56c..941911c223090 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -154,7 +154,10 @@ impl EncodedSourceFileId { impl<'sess> OnDiskCache<'sess> { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. - pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Option { + /// + /// The serialized cache has some basic integrity checks, if those checks indicate that the + /// on-disk data is corrupt, an error is returned. + pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result { assert!(sess.opts.incremental.is_some()); let mut decoder = MemDecoder::new(&data, start_pos)?; @@ -169,7 +172,7 @@ impl<'sess> OnDiskCache<'sess> { let footer: Footer = decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER)); - Some(Self { + Ok(Self { serialized_data: RwLock::new(Some(data)), file_index_to_stable_id: footer.file_index_to_stable_id, file_index_to_file: Default::default(), diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 1b4b1d2436f47..1dcb69920d73e 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -17,7 +17,7 @@ use crate::int_overflow::DebugStrictAdd; pub type FileEncodeResult = Result; -const FOOTER: &[u8] = b"rust-end-file"; +pub const MAGIC_END_BYTES: &[u8] = b"rust-end-file"; /// The size of the buffer in `FileEncoder`. const BUF_SIZE: usize = 8192; @@ -183,7 +183,7 @@ impl FileEncoder { } pub fn finish(&mut self) -> FileEncodeResult { - self.write_all(FOOTER); + self.write_all(MAGIC_END_BYTES); self.flush(); #[cfg(debug_assertions)] { @@ -264,10 +264,10 @@ pub struct MemDecoder<'a> { impl<'a> MemDecoder<'a> { #[inline] - pub fn new(data: &'a [u8], position: usize) -> Option> { - let data = data.strip_suffix(FOOTER)?; + pub fn new(data: &'a [u8], position: usize) -> Result, ()> { + let data = data.strip_suffix(MAGIC_END_BYTES).ok_or(())?; let Range { start, end } = data.as_ptr_range(); - Some(MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData }) + Ok(MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData }) } #[inline] diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs index 2bf4f38d3c346..fafe4b91a95d3 100644 --- a/compiler/rustc_serialize/tests/leb128.rs +++ b/compiler/rustc_serialize/tests/leb128.rs @@ -1,5 +1,6 @@ use rustc_serialize::leb128::*; use rustc_serialize::opaque::MemDecoder; +use rustc_serialize::opaque::MAGIC_END_BYTES; use rustc_serialize::Decoder; macro_rules! impl_test_unsigned_leb128 { @@ -27,7 +28,7 @@ macro_rules! impl_test_unsigned_leb128 { stream.extend(&buf[..n]); } let stream_end = stream.len(); - stream.extend(b"rust-end-file"); + stream.extend(MAGIC_END_BYTES); let mut decoder = MemDecoder::new(&stream, 0).unwrap(); for &expected in &values { @@ -76,7 +77,7 @@ macro_rules! impl_test_signed_leb128 { stream.extend(&buf[..n]); } let stream_end = stream.len(); - stream.extend(b"rust-end-file"); + stream.extend(MAGIC_END_BYTES); let mut decoder = MemDecoder::new(&stream, 0).unwrap(); for &expected in &values { diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index 0bf98886965c2..e9b380fdeac66 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -344,7 +344,7 @@ pub(crate) fn load_call_locations( Ok(bytes) => bytes, Err(e) => dcx.fatal(format!("failed to load examples: {e}")), }; - let Some(mut decoder) = MemDecoder::new(&bytes, 0) else { + let Ok(mut decoder) = MemDecoder::new(&bytes, 0) else { dcx.fatal(format!("Corrupt metadata encountered in {path}")) }; let calls = AllCallLocations::decode(&mut decoder); From b5923a95a886588fd40fccd69f1ccca504234194 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 21 May 2024 21:21:26 -0700 Subject: [PATCH 25/31] Move tests into appropriate subdirectories --- .../pub-use-primitive-document-private-items-95633.rs | 1 + tests/rustdoc/{ => inline_cross}/auxiliary/issue-85454.rs | 0 .../auxiliary/reexport-with-anonymous-lifetime-98697.rs} | 0 tests/rustdoc/{ => inline_cross}/qpath-self-85454.rs | 0 .../reexport-with-anonymous-lifetime-98697.rs | 8 ++++---- .../blanket-impl-reexported-trait-94183.rs | 0 .../private-reexport-in-public-api-81141-2.rs | 0 .../private-reexport-in-public-api-81141.rs} | 0 .../private-reexport-in-public-api-generics-81141.rs | 0 .../private-reexport-in-public-api-hidden-81141.rs | 0 .../private-reexport-in-public-api-private-81141.rs | 0 .../reexported-macro-and-macro-export-sidebar-89852.rs} | 0 .../{ => synthetic_auto}/normalize-auto-trait-80233.rs | 0 13 files changed, 5 insertions(+), 4 deletions(-) rename tests/{rustdoc => rustdoc-ui}/pub-use-primitive-document-private-items-95633.rs (93%) rename tests/rustdoc/{ => inline_cross}/auxiliary/issue-85454.rs (100%) rename tests/rustdoc/{auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs => inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs} (100%) rename tests/rustdoc/{ => inline_cross}/qpath-self-85454.rs (100%) rename tests/rustdoc/{ => inline_cross}/reexport-with-anonymous-lifetime-98697.rs (72%) rename tests/rustdoc/{ => inline_local}/blanket-impl-reexported-trait-94183.rs (100%) rename tests/rustdoc/{ => inline_local}/private-reexport-in-public-api-81141-2.rs (100%) rename tests/rustdoc/{private-export-in-public-api-81141.rs => inline_local/private-reexport-in-public-api-81141.rs} (100%) rename tests/rustdoc/{ => inline_local}/private-reexport-in-public-api-generics-81141.rs (100%) rename tests/rustdoc/{ => inline_local}/private-reexport-in-public-api-hidden-81141.rs (100%) rename tests/rustdoc/{ => inline_local}/private-reexport-in-public-api-private-81141.rs (100%) rename tests/rustdoc/{pub-use-exported-macro-89852.rs => inline_local/reexported-macro-and-macro-export-sidebar-89852.rs} (100%) rename tests/rustdoc/{ => synthetic_auto}/normalize-auto-trait-80233.rs (100%) diff --git a/tests/rustdoc/pub-use-primitive-document-private-items-95633.rs b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs similarity index 93% rename from tests/rustdoc/pub-use-primitive-document-private-items-95633.rs rename to tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs index 7f612a0b60ae4..d53a67cde8b43 100644 --- a/tests/rustdoc/pub-use-primitive-document-private-items-95633.rs +++ b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs @@ -1,3 +1,4 @@ +//@ check-pass //@ compile-flags: --document-private-items // This ensures that no ICE is triggered when rustdoc is run on this code. diff --git a/tests/rustdoc/auxiliary/issue-85454.rs b/tests/rustdoc/inline_cross/auxiliary/issue-85454.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-85454.rs rename to tests/rustdoc/inline_cross/auxiliary/issue-85454.rs diff --git a/tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs b/tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs similarity index 100% rename from tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs rename to tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs diff --git a/tests/rustdoc/qpath-self-85454.rs b/tests/rustdoc/inline_cross/qpath-self-85454.rs similarity index 100% rename from tests/rustdoc/qpath-self-85454.rs rename to tests/rustdoc/inline_cross/qpath-self-85454.rs diff --git a/tests/rustdoc/reexport-with-anonymous-lifetime-98697.rs b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs similarity index 72% rename from tests/rustdoc/reexport-with-anonymous-lifetime-98697.rs rename to tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs index 545af512d3d02..fe6e5a39c8106 100644 --- a/tests/rustdoc/reexport-with-anonymous-lifetime-98697.rs +++ b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs @@ -1,4 +1,4 @@ -//@ aux-build:issue-98697-reexport-with-anonymous-lifetime.rs +//@ aux-build:reexport-with-anonymous-lifetime-98697.rs //@ ignore-cross-compile #![crate_name = "foo"] @@ -7,12 +7,12 @@ // // https://github.com/rust-lang/rust/issues/98697 -extern crate issue_98697_reexport_with_anonymous_lifetime; +extern crate reexport_with_anonymous_lifetime_98697; // @has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro()where F: Fn(&str)' // @!has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<' -pub use issue_98697_reexport_with_anonymous_lifetime::repro; +pub use reexport_with_anonymous_lifetime_98697::repro; // @has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra' // @!has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<' -pub use issue_98697_reexport_with_anonymous_lifetime::Extra; +pub use reexport_with_anonymous_lifetime_98697::Extra; diff --git a/tests/rustdoc/blanket-impl-reexported-trait-94183.rs b/tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs similarity index 100% rename from tests/rustdoc/blanket-impl-reexported-trait-94183.rs rename to tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs diff --git a/tests/rustdoc/private-reexport-in-public-api-81141-2.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs similarity index 100% rename from tests/rustdoc/private-reexport-in-public-api-81141-2.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs diff --git a/tests/rustdoc/private-export-in-public-api-81141.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs similarity index 100% rename from tests/rustdoc/private-export-in-public-api-81141.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs diff --git a/tests/rustdoc/private-reexport-in-public-api-generics-81141.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs similarity index 100% rename from tests/rustdoc/private-reexport-in-public-api-generics-81141.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs diff --git a/tests/rustdoc/private-reexport-in-public-api-hidden-81141.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs similarity index 100% rename from tests/rustdoc/private-reexport-in-public-api-hidden-81141.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs diff --git a/tests/rustdoc/private-reexport-in-public-api-private-81141.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs similarity index 100% rename from tests/rustdoc/private-reexport-in-public-api-private-81141.rs rename to tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs diff --git a/tests/rustdoc/pub-use-exported-macro-89852.rs b/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs similarity index 100% rename from tests/rustdoc/pub-use-exported-macro-89852.rs rename to tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs diff --git a/tests/rustdoc/normalize-auto-trait-80233.rs b/tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs similarity index 100% rename from tests/rustdoc/normalize-auto-trait-80233.rs rename to tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs From 722b2ee17d846d9ba1a7478a5368a3bbaae3e665 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Wed, 22 May 2024 07:38:37 +0200 Subject: [PATCH 26/31] Remove unnecessary `.md` from the documentation sidebar --- src/doc/rustc/src/SUMMARY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 6f81495585707..4ef2fcebe9283 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -20,10 +20,10 @@ - [\*-apple-darwin](platform-support/apple-darwin.md) - [i686-apple-darwin](platform-support/i686-apple-darwin.md) - [x86_64h-apple-darwin](platform-support/x86_64h-apple-darwin.md) - - [arm64e-apple-darwin.md](platform-support/arm64e-apple-darwin.md) + - [arm64e-apple-darwin](platform-support/arm64e-apple-darwin.md) - [\*-apple-ios](platform-support/apple-ios.md) - [\*-apple-ios-macabi](platform-support/apple-ios-macabi.md) - - [arm64e-apple-ios.md](platform-support/arm64e-apple-ios.md) + - [arm64e-apple-ios](platform-support/arm64e-apple-ios.md) - [\*-apple-tvos](platform-support/apple-tvos.md) - [\*-apple-watchos](platform-support/apple-watchos.md) - [\*-apple-visionos](platform-support/apple-visionos.md) From 2c61c0085964a6e11c3ba51951abd71a7ff9066c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 21 May 2024 15:16:09 +1000 Subject: [PATCH 27/31] Fix up whitespace in `compiler/rustc_macros/src/serialize.rs`. --- compiler/rustc_macros/src/serialize.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs index 5fa11d22f0e75..7b5dd1601c1f6 100644 --- a/compiler/rustc_macros/src/serialize.rs +++ b/compiler/rustc_macros/src/serialize.rs @@ -13,7 +13,7 @@ pub fn type_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: quote! {} }; - s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound }); + s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound }); s.add_bounds(synstructure::AddBounds::Fields); s.underscore_const(true); @@ -34,7 +34,7 @@ pub fn meta_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let decoder_ty = quote! { __D }; - s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_span::SpanDecoder}); + s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_span::SpanDecoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); @@ -43,7 +43,7 @@ pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke pub fn decodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let decoder_ty = quote! { __D }; - s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder}); + s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_serialize::Decoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); @@ -120,7 +120,7 @@ fn decode_field(field: &syn::Field) -> proc_macro2::TokenStream { let __decoder = quote! { __decoder }; // Use the span of the field for the method call, so // that backtraces will point to the field. - quote_spanned! {field_span=> #decode_inner_method(#__decoder) } + quote_spanned! { field_span=> #decode_inner_method(#__decoder) } } pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { @@ -133,7 +133,7 @@ pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: }; let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound }); + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound }); s.add_bounds(synstructure::AddBounds::Fields); s.underscore_const(true); @@ -142,7 +142,7 @@ pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") { - s.add_impl_generic(parse_quote! {'tcx}); + s.add_impl_generic(parse_quote! { 'tcx }); } s.add_impl_generic(parse_quote! { '__a }); let encoder_ty = quote! { EncodeContext<'__a, 'tcx> }; @@ -154,7 +154,7 @@ pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2: pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder}); + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); @@ -163,7 +163,7 @@ pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke pub fn encodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { let encoder_ty = quote! { __E }; - s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder}); + s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder }); s.add_bounds(synstructure::AddBounds::Generics); s.underscore_const(true); From 220f3ab825d135c82243fc83ebf6286d94cdab96 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 21 May 2024 15:12:50 +1000 Subject: [PATCH 28/31] Add a useful comment. For something that wasn't obvious to me. --- compiler/rustc_span/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index f83bacdcebe77..62209e3795fa1 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -33,6 +33,9 @@ #![feature(rustdoc_internals)] // tidy-alphabetical-end +// The code produced by the `Encodable`/`Decodable` derive macros refer to +// `rustc_span::Span{Encoder,Decoder}`. That's fine outside this crate, but doesn't work inside +// this crate without this line making `rustc_span` available. extern crate self as rustc_span; #[macro_use] From e60c1916e0d0ff0cad9d8fce049c3bc31eeb34f0 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 22 May 2024 07:51:27 +1000 Subject: [PATCH 29/31] Remove `#[macro_use]` extern crate tracing` from `rustc_span`. Because explicit macro imports are better than implicit macro imports. --- compiler/rustc_span/src/hygiene.rs | 1 + compiler/rustc_span/src/lib.rs | 4 +--- compiler/rustc_span/src/source_map.rs | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index aa4bcefab9391..00ef17d630c5d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -40,6 +40,7 @@ use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fmt; use std::hash::Hash; +use tracing::{debug, trace}; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 62209e3795fa1..b2ca01fe3b94c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -38,13 +38,11 @@ // this crate without this line making `rustc_span` available. extern crate self as rustc_span; -#[macro_use] -extern crate tracing; - use rustc_data_structures::{outline, AtomicRef}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use tracing::debug; mod caching_source_map_view; pub mod source_map; diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 2093dcf0e8315..fb212d67997a7 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -16,6 +16,7 @@ use rustc_macros::{Decodable, Encodable}; use std::fs; use std::io::{self, BorrowedBuf, Read}; use std::path; +use tracing::{debug, instrument, trace}; #[cfg(test)] mod tests; From 8ee3d29cd93325db6f973358115f40ee1c785ef0 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Wed, 22 May 2024 00:56:57 -0700 Subject: [PATCH 30/31] Stop using `to_hir_binop` in codegen --- .../rustc_codegen_gcc/src/intrinsic/simd.rs | 13 ++++---- compiler/rustc_codegen_llvm/src/intrinsic.rs | 13 ++++---- compiler/rustc_codegen_ssa/src/base.rs | 32 +++++++++---------- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 13 ++++---- compiler/rustc_middle/src/mir/tcx.rs | 2 +- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 0c7cffbe7308b..1625e6ecdb701 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -14,6 +14,7 @@ use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; use rustc_hir as hir; +use rustc_middle::mir::BinOp; use rustc_middle::span_bug; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; @@ -122,12 +123,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let in_ty = arg_tys[0]; let comparison = match name { - sym::simd_eq => Some(hir::BinOpKind::Eq), - sym::simd_ne => Some(hir::BinOpKind::Ne), - sym::simd_lt => Some(hir::BinOpKind::Lt), - sym::simd_le => Some(hir::BinOpKind::Le), - sym::simd_gt => Some(hir::BinOpKind::Gt), - sym::simd_ge => Some(hir::BinOpKind::Ge), + sym::simd_eq => Some(BinOp::Eq), + sym::simd_ne => Some(BinOp::Ne), + sym::simd_lt => Some(BinOp::Lt), + sym::simd_le => Some(BinOp::Le), + sym::simd_gt => Some(BinOp::Gt), + sym::simd_ge => Some(BinOp::Ge), _ => None, }; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 80e863af893e0..897132a8e9ccc 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -14,6 +14,7 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_hir as hir; +use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf}; use rustc_middle::ty::{self, GenericArgsRef, Ty}; use rustc_middle::{bug, span_bug}; @@ -1104,12 +1105,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let in_ty = arg_tys[0]; let comparison = match name { - sym::simd_eq => Some(hir::BinOpKind::Eq), - sym::simd_ne => Some(hir::BinOpKind::Ne), - sym::simd_lt => Some(hir::BinOpKind::Lt), - sym::simd_le => Some(hir::BinOpKind::Le), - sym::simd_gt => Some(hir::BinOpKind::Gt), - sym::simd_ge => Some(hir::BinOpKind::Ge), + sym::simd_eq => Some(BinOp::Eq), + sym::simd_ne => Some(BinOp::Ne), + sym::simd_lt => Some(BinOp::Lt), + sym::simd_le => Some(BinOp::Le), + sym::simd_gt => Some(BinOp::Gt), + sym::simd_ge => Some(BinOp::Ge), _ => None, }; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 877e5b75912ea..66bc5b6606ded 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -20,7 +20,6 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::par_map; use rustc_data_structures::unord::UnordMap; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_metadata::EncodedMetadata; @@ -30,6 +29,7 @@ use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, Debugger use rustc_middle::middle::exported_symbols; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::lang_items; +use rustc_middle::mir::BinOp; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; @@ -46,32 +46,32 @@ use std::time::{Duration, Instant}; use itertools::Itertools; -pub fn bin_op_to_icmp_predicate(op: hir::BinOpKind, signed: bool) -> IntPredicate { +pub fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { match op { - hir::BinOpKind::Eq => IntPredicate::IntEQ, - hir::BinOpKind::Ne => IntPredicate::IntNE, - hir::BinOpKind::Lt => { + BinOp::Eq => IntPredicate::IntEQ, + BinOp::Ne => IntPredicate::IntNE, + BinOp::Lt => { if signed { IntPredicate::IntSLT } else { IntPredicate::IntULT } } - hir::BinOpKind::Le => { + BinOp::Le => { if signed { IntPredicate::IntSLE } else { IntPredicate::IntULE } } - hir::BinOpKind::Gt => { + BinOp::Gt => { if signed { IntPredicate::IntSGT } else { IntPredicate::IntUGT } } - hir::BinOpKind::Ge => { + BinOp::Ge => { if signed { IntPredicate::IntSGE } else { @@ -86,14 +86,14 @@ pub fn bin_op_to_icmp_predicate(op: hir::BinOpKind, signed: bool) -> IntPredicat } } -pub fn bin_op_to_fcmp_predicate(op: hir::BinOpKind) -> RealPredicate { +pub fn bin_op_to_fcmp_predicate(op: BinOp) -> RealPredicate { match op { - hir::BinOpKind::Eq => RealPredicate::RealOEQ, - hir::BinOpKind::Ne => RealPredicate::RealUNE, - hir::BinOpKind::Lt => RealPredicate::RealOLT, - hir::BinOpKind::Le => RealPredicate::RealOLE, - hir::BinOpKind::Gt => RealPredicate::RealOGT, - hir::BinOpKind::Ge => RealPredicate::RealOGE, + BinOp::Eq => RealPredicate::RealOEQ, + BinOp::Ne => RealPredicate::RealUNE, + BinOp::Lt => RealPredicate::RealOLT, + BinOp::Le => RealPredicate::RealOLE, + BinOp::Gt => RealPredicate::RealOGT, + BinOp::Ge => RealPredicate::RealOGE, op => { bug!( "comparison_op_to_fcmp_predicate: expected comparison operator, \ @@ -110,7 +110,7 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( rhs: Bx::Value, t: Ty<'tcx>, ret_ty: Bx::Type, - op: hir::BinOpKind, + op: BinOp, ) -> Bx::Value { let signed = match t.kind() { ty::Float(_) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 00b28cbd0c68a..f9085f502d422 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -7,7 +7,6 @@ use crate::common::IntPredicate; use crate::traits::*; use crate::MemFlags; -use rustc_hir as hir; use rustc_middle::mir; use rustc_middle::ty::cast::{CastTy, IntTy}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; @@ -896,9 +895,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | mir::BinOp::Le | mir::BinOp::Ge => { if is_float { - bx.fcmp(base::bin_op_to_fcmp_predicate(op.to_hir_binop()), lhs, rhs) + bx.fcmp(base::bin_op_to_fcmp_predicate(op), lhs, rhs) } else { - bx.icmp(base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), lhs, rhs) + bx.icmp(base::bin_op_to_icmp_predicate(op, is_signed), lhs, rhs) } } mir::BinOp::Cmp => { @@ -912,16 +911,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it // better (see ), it'll // be worth trying it in optimized builds as well. - let is_gt = bx.icmp(pred(hir::BinOpKind::Gt), lhs, rhs); + let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs); let gtext = bx.zext(is_gt, bx.type_i8()); - let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); + let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); let ltext = bx.zext(is_lt, bx.type_i8()); bx.unchecked_ssub(gtext, ltext) } else { // These operations are those expected by `tests/codegen/integer-cmp.rs`, // from . - let is_lt = bx.icmp(pred(hir::BinOpKind::Lt), lhs, rhs); - let is_ne = bx.icmp(pred(hir::BinOpKind::Ne), lhs, rhs); + let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs); let ge = bx.select( is_ne, bx.cx().const_i8(Ordering::Greater as i8), diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index e1ae2e0866675..c421c937dc36d 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -295,7 +295,7 @@ impl BorrowKind { } impl BinOp { - pub fn to_hir_binop(self) -> hir::BinOpKind { + pub(crate) fn to_hir_binop(self) -> hir::BinOpKind { match self { BinOp::Add => hir::BinOpKind::Add, BinOp::Sub => hir::BinOpKind::Sub, From 96968350e17efdbb9958dbeaec4982d8cca0019d Mon Sep 17 00:00:00 2001 From: r0cky Date: Thu, 23 May 2024 09:06:43 +0800 Subject: [PATCH 31/31] Detect unused structs which implement private traits --- compiler/rustc_passes/src/dead.rs | 49 +++++++++++++------ .../lint/dead-code/unused-adt-impls-trait.rs | 34 +++++++++++++ .../dead-code/unused-adt-impls-trait.stderr | 14 ++++++ .../ui/rust-2021/inherent-dyn-collision.fixed | 1 + tests/ui/rust-2021/inherent-dyn-collision.rs | 1 + .../rust-2021/inherent-dyn-collision.stderr | 2 +- 6 files changed, 84 insertions(+), 17 deletions(-) create mode 100644 tests/ui/lint/dead-code/unused-adt-impls-trait.rs create mode 100644 tests/ui/lint/dead-code/unused-adt-impls-trait.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 933412f677ce8..ddc50e2b811a2 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -425,10 +425,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let ItemKind::Impl(impl_ref) = self.tcx.hir().expect_item(local_impl_id).kind { - if self.tcx.visibility(trait_id).is_public() - && matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) + if matches!(trait_item.kind, hir::TraitItemKind::Fn(..)) && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) { + // skip methods of private ty, + // they would be solved in `solve_rest_impl_items` continue; } @@ -485,32 +486,46 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) { let mut ready; - (ready, unsolved_impl_items) = unsolved_impl_items - .into_iter() - .partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id)); + (ready, unsolved_impl_items) = + unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { + self.impl_item_with_used_self(impl_id, impl_item_id) + }); while !ready.is_empty() { self.worklist = ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); self.mark_live_symbols(); - (ready, unsolved_impl_items) = unsolved_impl_items - .into_iter() - .partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id)); + (ready, unsolved_impl_items) = + unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { + self.impl_item_with_used_self(impl_id, impl_item_id) + }); } } - fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId) -> bool { + fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { if let TyKind::Path(hir::QPath::Resolved(_, path)) = self.tcx.hir().item(impl_id).expect_impl().self_ty.kind && let Res::Def(def_kind, def_id) = path.res && let Some(local_def_id) = def_id.as_local() && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { - self.live_symbols.contains(&local_def_id) - } else { - false + if self.tcx.visibility(impl_item_id).is_public() { + // for the public method, we don't know the trait item is used or not, + // so we mark the method live if the self is used + return self.live_symbols.contains(&local_def_id); + } + + if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id + && let Some(local_id) = trait_item_id.as_local() + { + // for the private method, we can know the trait item is used or not, + // so we mark the method live if the self is used and the trait item is used + return self.live_symbols.contains(&local_id) + && self.live_symbols.contains(&local_def_id); + } } + false } } @@ -745,20 +760,22 @@ fn check_item<'tcx>( matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None); } - // for impl trait blocks, mark associate functions live if the trait is public + // for trait impl blocks, + // mark the method live if the self_ty is public, + // or the method is public and may construct self if of_trait && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) || tcx.visibility(local_def_id).is_public() && (ty_is_pub || may_construct_self)) { worklist.push((local_def_id, ComesFromAllowExpect::No)); - } else if of_trait && tcx.visibility(local_def_id).is_public() { - // pub method && private ty & methods not construct self - unsolved_impl_items.push((id, local_def_id)); } else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); + } else if of_trait { + // private method || public method not constructs self + unsolved_impl_items.push((id, local_def_id)); } } } diff --git a/tests/ui/lint/dead-code/unused-adt-impls-trait.rs b/tests/ui/lint/dead-code/unused-adt-impls-trait.rs new file mode 100644 index 0000000000000..4714859afac4b --- /dev/null +++ b/tests/ui/lint/dead-code/unused-adt-impls-trait.rs @@ -0,0 +1,34 @@ +#![deny(dead_code)] + +struct Used; +struct Unused; //~ ERROR struct `Unused` is never constructed + +pub trait PubTrait { + fn foo(&self) -> Self; +} + +impl PubTrait for Used { + fn foo(&self) -> Self { Used } +} + +impl PubTrait for Unused { + fn foo(&self) -> Self { Unused } +} + +trait PriTrait { + fn foo(&self) -> Self; +} + +impl PriTrait for Used { + fn foo(&self) -> Self { Used } +} + +impl PriTrait for Unused { + fn foo(&self) -> Self { Unused } +} + +fn main() { + let t = Used; + let _t = ::foo(&t); + let _t = ::foo(&t); +} diff --git a/tests/ui/lint/dead-code/unused-adt-impls-trait.stderr b/tests/ui/lint/dead-code/unused-adt-impls-trait.stderr new file mode 100644 index 0000000000000..28bae5c2af09c --- /dev/null +++ b/tests/ui/lint/dead-code/unused-adt-impls-trait.stderr @@ -0,0 +1,14 @@ +error: struct `Unused` is never constructed + --> $DIR/unused-adt-impls-trait.rs:4:8 + | +LL | struct Unused; + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-adt-impls-trait.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/rust-2021/inherent-dyn-collision.fixed b/tests/ui/rust-2021/inherent-dyn-collision.fixed index f5702613af032..8fe1bf811dbf3 100644 --- a/tests/ui/rust-2021/inherent-dyn-collision.fixed +++ b/tests/ui/rust-2021/inherent-dyn-collision.fixed @@ -25,6 +25,7 @@ mod inner { // having a struct of the same name as the trait in-scope, while *also* // implementing the trait for that struct but **without** importing the // trait itself into scope + #[allow(dead_code)] struct TryIntoU32; impl super::TryIntoU32 for TryIntoU32 { diff --git a/tests/ui/rust-2021/inherent-dyn-collision.rs b/tests/ui/rust-2021/inherent-dyn-collision.rs index 0bcb34e5708bb..47c8446241988 100644 --- a/tests/ui/rust-2021/inherent-dyn-collision.rs +++ b/tests/ui/rust-2021/inherent-dyn-collision.rs @@ -25,6 +25,7 @@ mod inner { // having a struct of the same name as the trait in-scope, while *also* // implementing the trait for that struct but **without** importing the // trait itself into scope + #[allow(dead_code)] struct TryIntoU32; impl super::TryIntoU32 for TryIntoU32 { diff --git a/tests/ui/rust-2021/inherent-dyn-collision.stderr b/tests/ui/rust-2021/inherent-dyn-collision.stderr index f5905574ac39d..d9e720dd9af8f 100644 --- a/tests/ui/rust-2021/inherent-dyn-collision.stderr +++ b/tests/ui/rust-2021/inherent-dyn-collision.stderr @@ -1,5 +1,5 @@ warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/inherent-dyn-collision.rs:41:9 + --> $DIR/inherent-dyn-collision.rs:42:9 | LL | get_dyn_trait().try_into().unwrap() | ^^^^^^^^^^^^^^^ help: disambiguate the method call: `(&*get_dyn_trait())`