From 1632f624fbcda1a3134ca4946af75e116c143261 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 30 Mar 2024 16:00:28 +0100 Subject: [PATCH 1/9] Add `#[rustc_no_implicit_autorefs]` and apply it to std methods --- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++++ compiler/rustc_passes/src/check_attr.rs | 3 +++ compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/string.rs | 2 ++ library/core/src/ops/index.rs | 2 ++ library/core/src/slice/mod.rs | 6 ++++++ library/core/src/str/mod.rs | 2 ++ 7 files changed, 20 insertions(+) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3b441729d7552..8993108b53c54 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -931,6 +931,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ EncodeCrossCrate::Yes, "#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers." ), + rustc_attr!( + rustc_no_implicit_autorefs, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, + "`#[rustc_no_implicit_autorefs]` is used to mark functions for which an autoref to the dereference of a raw pointer should not be used as an argument." + ), rustc_attr!( rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, "#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`." diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cbe5058b55191..016837b8b8531 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -179,6 +179,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_as_ptr, ..] => { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } + [sym::rustc_no_implicit_autorefs, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } [sym::rustc_never_returns_null_ptr, ..] => { self.check_applied_to_fn_or_method(hir_id, attr, span, target) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index ac7efaffefb8f..1bc08e60098ff 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1830,6 +1830,7 @@ symbols! { rustc_must_implement_one_of, rustc_never_returns_null_ptr, rustc_never_type_options, + rustc_no_implicit_autorefs, rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 9a161d057db09..cd9e04a915aa2 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1832,6 +1832,7 @@ impl String { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] #[rustc_confusables("length", "size")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] pub const fn len(&self) -> usize { self.vec.len() } @@ -1851,6 +1852,7 @@ impl String { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] pub const fn is_empty(&self) -> bool { self.len() == 0 } diff --git a/library/core/src/ops/index.rs b/library/core/src/ops/index.rs index 37d9a28fb99c0..8106c088f0ba2 100644 --- a/library/core/src/ops/index.rs +++ b/library/core/src/ops/index.rs @@ -67,6 +67,7 @@ pub trait Index { /// /// May panic if the index is out of bounds. #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[track_caller] fn index(&self, index: Idx) -> &Self::Output; } @@ -171,6 +172,7 @@ pub trait IndexMut: Index { /// /// May panic if the index is out of bounds. #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[track_caller] fn index_mut(&mut self, index: Idx) -> &mut Self::Output; } diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 7b2a3fac38a32..1e2c943c14d3c 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -109,6 +109,7 @@ impl [T] { #[lang = "slice_len_fn"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_len", since = "1.39.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[inline] #[must_use] pub const fn len(&self) -> usize { @@ -128,6 +129,7 @@ impl [T] { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_slice_is_empty", since = "1.39.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[inline] #[must_use] pub const fn is_empty(&self) -> bool { @@ -562,6 +564,7 @@ impl [T] { /// assert_eq!(None, v.get(0..4)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[inline] #[must_use] pub fn get(&self, index: I) -> Option<&I::Output> @@ -587,6 +590,7 @@ impl [T] { /// assert_eq!(x, &[0, 42, 2]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[inline] #[must_use] pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> @@ -624,6 +628,7 @@ impl [T] { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[inline] #[must_use] pub unsafe fn get_unchecked(&self, index: I) -> &I::Output @@ -666,6 +671,7 @@ impl [T] { /// assert_eq!(x, &[1, 13, 4]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[inline] #[must_use] pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 79b4953fcc11a..2099327d2f14a 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -134,6 +134,7 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_len", since = "1.39.0")] #[rustc_diagnostic_item = "str_len"] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[must_use] #[inline] pub const fn len(&self) -> usize { @@ -153,6 +154,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_is_empty", since = "1.39.0")] + #[cfg_attr(not(bootstrap), rustc_no_implicit_autorefs)] #[must_use] #[inline] pub const fn is_empty(&self) -> bool { From 40ba47d3b0d53374c9170871f82a410e632fe1e3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 10 Feb 2024 17:55:17 +0100 Subject: [PATCH 2/9] Implement lint against dangerous implicit autorefs --- compiler/rustc_lint/messages.ftl | 4 + compiler/rustc_lint/src/autorefs.rs | 153 +++++++++++++++++ compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 20 +++ library/alloc/src/vec/mod.rs | 2 +- tests/ui/lint/implicit_autorefs.fixed | 99 +++++++++++ tests/ui/lint/implicit_autorefs.rs | 99 +++++++++++ tests/ui/lint/implicit_autorefs.stderr | 219 +++++++++++++++++++++++++ 8 files changed, 598 insertions(+), 1 deletion(-) create mode 100644 compiler/rustc_lint/src/autorefs.rs create mode 100644 tests/ui/lint/implicit_autorefs.fixed create mode 100644 tests/ui/lint/implicit_autorefs.rs create mode 100644 tests/ui/lint/implicit_autorefs.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 60c183bd56b1f..a6499b07fe26c 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -360,6 +360,10 @@ lint_impl_trait_overcaptures = `{$self_ty}` will capture more lifetimes than pos lint_impl_trait_redundant_captures = all possible in-scope parameters are already captured, so `use<...>` syntax is redundant .suggestion = remove the `use<...>` syntax +lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dereference of a raw pointer + .note = creating a reference requires the pointer target to be valid and imposes aliasing requirements + .suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit + lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe .label = not FFI-safe .note = the type is defined here diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs new file mode 100644 index 0000000000000..5dd26854c9570 --- /dev/null +++ b/compiler/rustc_lint/src/autorefs.rs @@ -0,0 +1,153 @@ +use rustc_ast::{BorrowKind, UnOp}; +use rustc_hir::{Expr, ExprKind, Mutability}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::sym; + +use crate::lints::{ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsSuggestion}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `dangerous_implicit_autorefs` lint checks for implicitly taken references + /// to dereferences of raw pointers. + /// + /// ### Example + /// + /// ```rust + /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] { + /// &raw mut (*ptr)[..16] + /// // ^^^^^^ this calls `IndexMut::index_mut(&mut ..., ..16)`, + /// // implicitly creating a reference + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When working with raw pointers it's usually undesirable to create references, + /// since they inflict additional safety requirements. Unfortunately, it's possible + /// to take a reference to the dereference of a raw pointer implicitly, which inflicts + /// the usual reference requirements. + /// + /// If you are sure that you can soundly take a reference, then you can take it explicitly: + /// + /// ```rust + /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] { + /// &raw mut (&mut *ptr)[..16] + /// } + /// ``` + /// + /// Otherwise try to find an alternative way to achive your goals using only raw pointers: + /// + /// ```rust + /// use std::slice; + /// + /// unsafe fn fun(ptr: *mut [u8]) -> *mut [u8] { + /// slice::from_raw_parts_mut(ptr.cast(), 16) + /// } + /// ``` + pub DANGEROUS_IMPLICIT_AUTOREFS, + Warn, + "implicit reference to a dereference of a raw pointer", + report_in_external_macro +} + +declare_lint_pass!(ImplicitAutorefs => [DANGEROUS_IMPLICIT_AUTOREFS]); + +impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // This logic has mostly been taken from + // + + // 5. Either of the following: + // a. A deref followed by any non-deref place projection (that intermediate + // deref will typically be auto-inserted). + // b. A method call annotated with `#[rustc_no_implicit_refs]`. + // c. A deref followed by a `&raw const` or `&raw mut`. + let mut is_coming_from_deref = false; + let inner = match expr.kind { + ExprKind::AddrOf(BorrowKind::Raw, _, inner) => match inner.kind { + ExprKind::Unary(UnOp::Deref, inner) => { + is_coming_from_deref = true; + inner + } + _ => return, + }, + ExprKind::Index(base, _, _) => base, + ExprKind::MethodCall(_, inner, _, _) + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && cx.tcx.has_attr(def_id, sym::rustc_no_implicit_autorefs) => + { + inner + } + ExprKind::Field(inner, _) => inner, + _ => return, + }; + + let typeck = cx.typeck_results(); + let adjustments_table = typeck.adjustments(); + + if let Some(adjustments) = adjustments_table.get(inner.hir_id) + // 4. Any number of automatically inserted deref/derefmut calls. + && let adjustments = peel_derefs_adjustments(&**adjustments) + // 3. An automatically inserted reference (might come from a deref). + && let [adjustment] = adjustments + && let Some(borrow_mutbl) = has_implicit_borrow(adjustment) + && let ExprKind::Unary(UnOp::Deref, dereferenced) = + // 2. Any number of place projections. + peel_place_mappers(inner).kind + // 1. Deref of a raw pointer. + && typeck.expr_ty(dereferenced).is_raw_ptr() + { + cx.emit_span_lint( + DANGEROUS_IMPLICIT_AUTOREFS, + expr.span.source_callsite(), + ImplicitUnsafeAutorefsDiag { + suggestion: ImplicitUnsafeAutorefsSuggestion { + mutbl: borrow_mutbl.ref_prefix_str(), + deref: if is_coming_from_deref { "*" } else { "" }, + start_span: inner.span.shrink_to_lo(), + end_span: inner.span.shrink_to_hi(), + }, + }, + ) + } + } +} + +/// Peels expressions from `expr` that can map a place. +fn peel_place_mappers<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { + loop { + match expr.kind { + ExprKind::Index(base, _idx, _) => expr = &base, + ExprKind::Field(e, _) => expr = &e, + _ => break expr, + } + } +} + +/// Peel derefs adjustments until the last last element. +fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustment<'a>] { + while let [Adjustment { kind: Adjust::Deref(_), .. }, end @ ..] = adjs + && !end.is_empty() + { + adjs = end; + } + adjs +} + +/// Test if some adjustment has some implicit borrow. +/// +/// Returns `Some(mutability)` if the argument adjustment has implicit borrow in it. +fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option { + match kind { + &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some(mutbl), + &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some(mutbl.into()), + Adjust::NeverToAny + | Adjust::Pointer(..) + | Adjust::ReborrowPin(..) + | Adjust::Deref(None) + | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 212368bea8266..79c984cb634fc 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -36,6 +36,7 @@ mod async_closures; mod async_fn_in_trait; +mod autorefs; pub mod builtin; mod context; mod dangling; @@ -83,6 +84,7 @@ mod utils; use async_closures::AsyncClosureUsage; use async_fn_in_trait::AsyncFnInTrait; +use autorefs::*; use builtin::*; use dangling::*; use default_could_be_derived::DefaultCouldBeDerived; @@ -200,6 +202,7 @@ late_lint_methods!( PathStatements: PathStatements, LetUnderscore: LetUnderscore, InvalidReferenceCasting: InvalidReferenceCasting, + ImplicitAutorefs: ImplicitAutorefs, // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, UnitBindings: UnitBindings, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 8ab64fbd127af..487184b836a43 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -55,6 +55,26 @@ pub(crate) enum ShadowedIntoIterDiagSub { }, } +// autorefs.rs +#[derive(LintDiagnostic)] +#[diag(lint_implicit_unsafe_autorefs)] +#[note] +pub(crate) struct ImplicitUnsafeAutorefsDiag { + #[subdiagnostic] + pub suggestion: ImplicitUnsafeAutorefsSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] +pub(crate) struct ImplicitUnsafeAutorefsSuggestion { + pub mutbl: &'static str, + pub deref: &'static str, + #[suggestion_part(code = "({mutbl}{deref}")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + // builtin.rs #[derive(LintDiagnostic)] #[diag(lint_builtin_while_true)] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 68e4add30e5d0..65a83cb98ba6a 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2588,7 +2588,7 @@ impl Vec { #[inline] #[track_caller] unsafe fn append_elements(&mut self, other: *const [T]) { - let count = unsafe { (*other).len() }; + let count = other.len(); self.reserve(count); let len = self.len(); unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; diff --git a/tests/ui/lint/implicit_autorefs.fixed b/tests/ui/lint/implicit_autorefs.fixed new file mode 100644 index 0000000000000..96a617b20c918 --- /dev/null +++ b/tests/ui/lint/implicit_autorefs.fixed @@ -0,0 +1,99 @@ +//@ check-pass +//@ run-rustfix + +#![allow(dead_code)] // For the rustfix-ed code. + +use std::mem::ManuallyDrop; +use std::ops::Deref; + +unsafe fn test_const(ptr: *const [u8]) { + let _ = (&(*ptr))[..16]; + //~^ WARN implicit autoref +} + +struct Test { + field: [u8], +} + +unsafe fn test_field(ptr: *const Test) -> *const [u8] { + let l = (&(*ptr).field).len(); + //~^ WARN implicit autoref + + &raw const (&(*ptr).field)[..l - 1] + //~^ WARN implicit autoref +} + +unsafe fn test_builtin_index(a: *mut [String]) { + _ = (&(*a)[0]).len(); + //~^ WARN implicit autoref + + _ = (&(&(*a))[..1][0]).len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_const(ptr: *const ManuallyDrop) { + let _ = (&(*ptr)).field; + //~^ WARN implicit autoref + let _ = &raw const (&(*ptr)).field; + //~^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_mut(ptr: *mut ManuallyDrop) { + let _ = (&(*ptr)).field; + //~^ WARN implicit autoref +} + +unsafe fn test_double_overloaded_deref_const(ptr: *const ManuallyDrop>) { + let _ = (&(*ptr)).field; + //~^ WARN implicit autoref +} + +unsafe fn test_manually_overloaded_deref() { + struct W(T); + + impl Deref for W { + type Target = T; + fn deref(&self) -> &T { &self.0 } + } + + let w: W = W(5); + let w = &raw const w; + let _p: *const i32 = &raw const *(&**w); + //~^ WARN implicit autoref +} + +struct Test2 { + // Derefs to `[u8]`. + field: &'static [u8] +} + +fn test_more_manual_deref(ptr: *const Test2) -> usize { + unsafe { (&(*ptr).field).len() } + //~^ WARN implicit autoref +} + +unsafe fn test_no_attr(ptr: *mut ManuallyDrop) { + ptr.write(ManuallyDrop::new(1)); // Should not warn, as `ManuallyDrop::write` is not + // annotated with `#[rustc_no_implicit_auto_ref]` +} + +unsafe fn test_vec_get(ptr: *mut Vec) { + let _ = (&(*ptr)).get(0); + //~^ WARN implicit autoref + let _ = (&(*ptr)).get_unchecked(0); + //~^ WARN implicit autoref + let _ = (&mut (*ptr)).get_mut(0); + //~^ WARN implicit autoref + let _ = (&mut (*ptr)).get_unchecked_mut(0); + //~^ WARN implicit autoref +} + +unsafe fn test_string(ptr: *mut String) { + let _ = (&(*ptr)).len(); + //~^ WARN implicit autoref + let _ = (&(*ptr)).is_empty(); + //~^ WARN implicit autoref +} + +fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.rs b/tests/ui/lint/implicit_autorefs.rs new file mode 100644 index 0000000000000..61dd0ac50ce75 --- /dev/null +++ b/tests/ui/lint/implicit_autorefs.rs @@ -0,0 +1,99 @@ +//@ check-pass +//@ run-rustfix + +#![allow(dead_code)] // For the rustfix-ed code. + +use std::mem::ManuallyDrop; +use std::ops::Deref; + +unsafe fn test_const(ptr: *const [u8]) { + let _ = (*ptr)[..16]; + //~^ WARN implicit autoref +} + +struct Test { + field: [u8], +} + +unsafe fn test_field(ptr: *const Test) -> *const [u8] { + let l = (*ptr).field.len(); + //~^ WARN implicit autoref + + &raw const (*ptr).field[..l - 1] + //~^ WARN implicit autoref +} + +unsafe fn test_builtin_index(a: *mut [String]) { + _ = (*a)[0].len(); + //~^ WARN implicit autoref + + _ = (*a)[..1][0].len(); + //~^ WARN implicit autoref + //~^^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_const(ptr: *const ManuallyDrop) { + let _ = (*ptr).field; + //~^ WARN implicit autoref + let _ = &raw const (*ptr).field; + //~^ WARN implicit autoref +} + +unsafe fn test_overloaded_deref_mut(ptr: *mut ManuallyDrop) { + let _ = (*ptr).field; + //~^ WARN implicit autoref +} + +unsafe fn test_double_overloaded_deref_const(ptr: *const ManuallyDrop>) { + let _ = (*ptr).field; + //~^ WARN implicit autoref +} + +unsafe fn test_manually_overloaded_deref() { + struct W(T); + + impl Deref for W { + type Target = T; + fn deref(&self) -> &T { &self.0 } + } + + let w: W = W(5); + let w = &raw const w; + let _p: *const i32 = &raw const **w; + //~^ WARN implicit autoref +} + +struct Test2 { + // Derefs to `[u8]`. + field: &'static [u8] +} + +fn test_more_manual_deref(ptr: *const Test2) -> usize { + unsafe { (*ptr).field.len() } + //~^ WARN implicit autoref +} + +unsafe fn test_no_attr(ptr: *mut ManuallyDrop) { + ptr.write(ManuallyDrop::new(1)); // Should not warn, as `ManuallyDrop::write` is not + // annotated with `#[rustc_no_implicit_auto_ref]` +} + +unsafe fn test_vec_get(ptr: *mut Vec) { + let _ = (*ptr).get(0); + //~^ WARN implicit autoref + let _ = (*ptr).get_unchecked(0); + //~^ WARN implicit autoref + let _ = (*ptr).get_mut(0); + //~^ WARN implicit autoref + let _ = (*ptr).get_unchecked_mut(0); + //~^ WARN implicit autoref +} + +unsafe fn test_string(ptr: *mut String) { + let _ = (*ptr).len(); + //~^ WARN implicit autoref + let _ = (*ptr).is_empty(); + //~^ WARN implicit autoref +} + +fn main() {} diff --git a/tests/ui/lint/implicit_autorefs.stderr b/tests/ui/lint/implicit_autorefs.stderr new file mode 100644 index 0000000000000..6dd1ac65ada98 --- /dev/null +++ b/tests/ui/lint/implicit_autorefs.stderr @@ -0,0 +1,219 @@ +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:10:13 + | +LL | let _ = (*ptr)[..16]; + | ^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements + = note: `#[warn(dangerous_implicit_autorefs)]` on by default +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr))[..16]; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:19:13 + | +LL | let l = (*ptr).field.len(); + | ^^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let l = (&(*ptr).field).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:22:16 + | +LL | &raw const (*ptr).field[..l - 1] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | &raw const (&(*ptr).field)[..l - 1] + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:27:9 + | +LL | _ = (*a)[0].len(); + | ^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | _ = (&(*a)[0]).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | _ = (&(*a)[..1][0]).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:30:9 + | +LL | _ = (*a)[..1][0].len(); + | ^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | _ = (&(*a))[..1][0].len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:36:13 + | +LL | let _ = (*ptr).field; + | ^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:38:24 + | +LL | let _ = &raw const (*ptr).field; + | ^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = &raw const (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:43:13 + | +LL | let _ = (*ptr).field; + | ^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:48:13 + | +LL | let _ = (*ptr).field; + | ^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).field; + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:62:26 + | +LL | let _p: *const i32 = &raw const **w; + | ^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _p: *const i32 = &raw const *(&**w); + | +++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:72:14 + | +LL | unsafe { (*ptr).field.len() } + | ^^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | unsafe { (&(*ptr).field).len() } + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:82:13 + | +LL | let _ = (*ptr).get(0); + | ^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).get(0); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:84:13 + | +LL | let _ = (*ptr).get_unchecked(0); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).get_unchecked(0); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:86:13 + | +LL | let _ = (*ptr).get_mut(0); + | ^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&mut (*ptr)).get_mut(0); + | +++++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:88:13 + | +LL | let _ = (*ptr).get_unchecked_mut(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&mut (*ptr)).get_unchecked_mut(0); + | +++++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:93:13 + | +LL | let _ = (*ptr).len(); + | ^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).len(); + | ++ + + +warning: implicit autoref creates a reference to the dereference of a raw pointer + --> $DIR/implicit_autorefs.rs:95:13 + | +LL | let _ = (*ptr).is_empty(); + | ^^^^^^^^^^^^^^^^^ + | + = note: creating a reference requires the pointer target to be valid and imposes aliasing requirements +help: try using a raw pointer method instead; or if this reference is intentional, make it explicit + | +LL | let _ = (&(*ptr)).is_empty(); + | ++ + + +warning: 18 warnings emitted + From e8a6c175c51fbc347ea0b4479cc1b2de0932ddab Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 30 Mar 2024 16:40:24 +0100 Subject: [PATCH 3/9] Allow `dangerous_implicit_autorefs` lint in some tests --- library/core/src/pin.rs | 2 +- src/tools/miri/tests/pass/dst-raw.rs | 2 ++ .../miri/tests/pass/stacked-borrows/interior_mutability.rs | 2 ++ tests/ui/dynamically-sized-types/dst-raw.rs | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 48ed5ae451e40..39c11572e4757 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -676,7 +676,7 @@ //! let data_ptr = unpinned_src.data.as_ptr() as *const u8; //! let slice_ptr = unpinned_src.slice.as_ptr() as *const u8; //! let offset = slice_ptr.offset_from(data_ptr) as usize; -//! let len = (*unpinned_src.slice.as_ptr()).len(); +//! let len = unpinned_src.slice.as_ptr().len(); //! //! unpinned_self.slice = NonNull::from(&mut unpinned_self.data[offset..offset+len]); //! } diff --git a/src/tools/miri/tests/pass/dst-raw.rs b/src/tools/miri/tests/pass/dst-raw.rs index f26191a1d5998..3d0b843b3da22 100644 --- a/src/tools/miri/tests/pass/dst-raw.rs +++ b/src/tools/miri/tests/pass/dst-raw.rs @@ -1,5 +1,7 @@ // Test DST raw pointers +#![allow(dangerous_implicit_autorefs)] + trait Trait { fn foo(&self) -> isize; } diff --git a/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs b/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs index 830e9c33847cf..e86cb3711acb0 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs @@ -1,3 +1,5 @@ +#![allow(dangerous_implicit_autorefs)] + use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; use std::mem::{self, MaybeUninit}; diff --git a/tests/ui/dynamically-sized-types/dst-raw.rs b/tests/ui/dynamically-sized-types/dst-raw.rs index 111848c5a7f05..935f0f11ca61f 100644 --- a/tests/ui/dynamically-sized-types/dst-raw.rs +++ b/tests/ui/dynamically-sized-types/dst-raw.rs @@ -1,6 +1,8 @@ //@ run-pass // Test DST raw pointers +#![allow(dangerous_implicit_autorefs)] + trait Trait { fn foo(&self) -> isize; } From 5b5f55bc44075a0e08b253fad140e325ee6c3c93 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 23 Apr 2025 14:37:17 -0700 Subject: [PATCH 4/9] Update wasm-component-ld to 0.5.13 This commit updates the vendored `wasm-component-ld` binary to 0.5.13 which includes some various bug fixes and new feature updates for upcoming component model features coming down the pike. Not expected to break any existing workflows, just a normal update. --- Cargo.lock | 87 ++++++++------------------ src/tools/wasm-component-ld/Cargo.toml | 2 +- 2 files changed, 27 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49c1eb5b45f77..602690671ba6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4956,15 +4956,6 @@ dependencies = [ "color-eyre", ] -[[package]] -name = "spdx" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b69356da67e2fc1f542c71ea7e654a361a79c938e4424392ecf4fa065d2193" -dependencies = [ - "smallvec", -] - [[package]] name = "spdx-expression" version = "0.5.2" @@ -5784,9 +5775,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "29.0.1" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcd9f21bbde82ba59e415a8725e6ad0d0d7e9e460b1a3ccbca5bdee952c1a324" +checksum = "86fabda09a0d89ffd1615b297b4a5d4b4d99df9598aeb24685837e63019e927b" [[package]] name = "wasm-bindgen" @@ -5848,9 +5839,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580305a8e3f1b7a79859a8db897de643533b2851c5eb080fe5800233f16dec88" +checksum = "a60a07a994a3538b57d8c5f8caba19f4793fb4c7156276e5e90e90acbb829e20" dependencies = [ "anyhow", "clap", @@ -5858,7 +5849,7 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.223.1", + "wasmparser 0.229.0", "wat", "windows-sys 0.59.0", "winsplit", @@ -5885,39 +5876,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.223.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0a96fdeaee8fbeb4bd917fb8157d48c0d61c3b1f4ee4c363c8e8d68b2f4fe8" -dependencies = [ - "leb128", - "wasmparser 0.223.1", -] - -[[package]] -name = "wasm-encoder" -version = "0.228.0" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d30290541f2d4242a162bbda76b8f2d8b1ac59eab3568ed6f2327d52c9b2c4" +checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2" dependencies = [ "leb128fmt", - "wasmparser 0.228.0", + "wasmparser 0.229.0", ] [[package]] name = "wasm-metadata" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7e37883181704d96b89dbd8f1291be13694c71cd0663aebb94b46d235a377" +checksum = "78fdb7d29a79191ab363dc90c1ddd3a1e880ffd5348d92d48482393a9e6c5f4d" dependencies = [ "anyhow", "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "url", - "wasm-encoder 0.223.1", - "wasmparser 0.223.1", + "wasm-encoder 0.229.0", + "wasmparser 0.229.0", ] [[package]] @@ -5941,9 +5917,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664b980991ed9a8c834eb528a8979ab1109edcf52dc05dd5751e2cc3fb31035d" +checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c" dependencies = [ "bitflags", "hashbrown", @@ -5952,35 +5928,24 @@ dependencies = [ "serde", ] -[[package]] -name = "wasmparser" -version = "0.228.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3" -dependencies = [ - "bitflags", - "indexmap", - "semver", -] - [[package]] name = "wast" -version = "228.0.0" +version = "229.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5aae124478cb51439f6587f074a3a5e835afd22751c59a87b2e2a882727c97" +checksum = "63fcaff613c12225696bb163f79ca38ffb40e9300eff0ff4b8aa8b2f7eadf0d9" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.228.0", + "wasm-encoder 0.229.0", ] [[package]] name = "wat" -version = "1.228.0" +version = "1.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec29c89a8d055df988de7236483bf569988ac3d6905899f6af5ef920f9385ad" +checksum = "4189bad08b70455a9e9e67dc126d2dcf91fac143a80f1046747a5dde6d4c33e0" dependencies = [ "wast", ] @@ -6439,9 +6404,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc2fcc52a79f6f010a89c867e53e06d4227f86c58984add3e37f32b8e7af5f0" +checksum = "7f550067740e223bfe6c4878998e81cdbe2529dd9a793dc49248dd6613394e8b" dependencies = [ "anyhow", "bitflags", @@ -6450,17 +6415,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.223.1", + "wasm-encoder 0.229.0", "wasm-metadata", - "wasmparser 0.223.1", + "wasmparser 0.229.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "263fde17f1fbe55a413f16eb59094bf751795c6da469a05c3d45ea6c77df6b40" +checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6" dependencies = [ "anyhow", "id-arena", @@ -6471,7 +6436,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.223.1", + "wasmparser 0.229.0", ] [[package]] diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index f8ae91f9e6f1b..642d48b9952ed 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.12" +wasm-component-ld = "0.5.13" From f769f428b50a69722b292ee0abfb057ae4a89ef5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 25 Apr 2025 16:14:24 +0000 Subject: [PATCH 5/9] Move inline_asm to typeck, properly handle aliases --- Cargo.lock | 2 +- compiler/rustc_hir_analysis/Cargo.toml | 1 - compiler/rustc_hir_analysis/messages.ftl | 3 - compiler/rustc_hir_analysis/src/check/mod.rs | 1 - compiler/rustc_hir_analysis/src/errors.rs | 8 -- compiler/rustc_hir_typeck/Cargo.toml | 1 + compiler/rustc_hir_typeck/messages.ftl | 3 + compiler/rustc_hir_typeck/src/errors.rs | 8 ++ .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 10 +- .../src/inline_asm.rs} | 102 +++++++++--------- compiler/rustc_hir_typeck/src/lib.rs | 1 + tests/ui/asm/named_const_simd_vec_len.rs | 3 + tests/ui/asm/normalizable-asm-ty.rs | 15 +++ 13 files changed, 85 insertions(+), 73 deletions(-) rename compiler/{rustc_hir_analysis/src/check/intrinsicck.rs => rustc_hir_typeck/src/inline_asm.rs} (89%) create mode 100644 tests/ui/asm/normalizable-asm-ty.rs diff --git a/Cargo.lock b/Cargo.lock index 49c1eb5b45f77..b6b6451c9319c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3794,7 +3794,6 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", - "rustc_target", "rustc_trait_selection", "smallvec", "tracing", @@ -3833,6 +3832,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", + "rustc_target", "rustc_trait_selection", "smallvec", "tracing", diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index e5017794d8f29..58213c4f4e462 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -26,7 +26,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 92701e3328e92..277bb7bd3e15c 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -450,9 +450,6 @@ hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` -hir_analysis_register_type_unstable = - type `{$ty}` cannot be used with this register class in stable - hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_return_type_notation_equality_bound = diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 5fbd771976bbb..fad8abf5fae85 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -67,7 +67,6 @@ mod check; mod compare_impl_item; mod entry; pub mod intrinsic; -pub mod intrinsicck; mod region; pub mod wfcheck; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 508970cf2554d..2b1661aaac8f0 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1675,14 +1675,6 @@ pub(crate) struct CmseEntryGeneric { pub span: Span, } -#[derive(Diagnostic)] -#[diag(hir_analysis_register_type_unstable)] -pub(crate) struct RegisterTypeUnstable<'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'a>, -} - #[derive(LintDiagnostic)] #[diag(hir_analysis_supertrait_item_shadowing)] pub(crate) struct SupertraitItemShadowing { diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index b2b90cb29e36f..f00125c3e090a 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -22,6 +22,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 9e1b70f5767b4..23309102c4da5 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -179,6 +179,9 @@ hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len -> .help = use `transmute` if you're sure this is sound .label = unsupported cast +hir_typeck_register_type_unstable = + type `{$ty}` cannot be used with this register class in stable + hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 9e7305430e5f5..732795535087e 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -963,3 +963,11 @@ pub(crate) enum SupertraitItemShadowee { traits: DiagSymbolList, }, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_register_type_unstable)] +pub(crate) struct RegisterTypeUnstable<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index da0e8e362d663..5637de2222c2a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -8,7 +8,6 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Node, QPath}; -use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_index::IndexVec; @@ -33,6 +32,7 @@ use crate::errors::SuggestPtrNullMut; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; use crate::fn_ctxt::infer::FnCall; use crate::gather_locals::Declaration; +use crate::inline_asm::InlineAsmCtxt; use crate::method::MethodCallee; use crate::method::probe::IsSuggestion; use crate::method::probe::Mode::MethodCall; @@ -98,13 +98,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); for (asm, hir_id) in deferred_asm_checks.drain(..) { let enclosing_id = self.tcx.hir_enclosing_body_owner(hir_id); - InlineAsmCtxt::new( - enclosing_id, - &self.infcx, - self.typing_env(self.param_env), - &*self.typeck_results.borrow(), - ) - .check_asm(asm); + InlineAsmCtxt::new(self, enclosing_id).check_asm(asm); } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs similarity index 89% rename from compiler/rustc_hir_analysis/src/check/intrinsicck.rs rename to compiler/rustc_hir_typeck/src/inline_asm.rs index 32a582aadc1ca..6399f0a78ae2f 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -3,25 +3,22 @@ use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; -use rustc_infer::infer::InferCtxt; use rustc_middle::bug; -use rustc_middle::ty::{ - self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, TypeckResults, UintTy, -}; +use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use rustc_target::asm::{ InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, }; +use rustc_trait_selection::infer::InferCtxtExt; +use crate::FnCtxt; use crate::errors::RegisterTypeUnstable; -pub struct InlineAsmCtxt<'a, 'tcx> { - typing_env: ty::TypingEnv<'tcx>, +pub(crate) struct InlineAsmCtxt<'a, 'tcx> { target_features: &'tcx FxIndexSet, - infcx: &'a InferCtxt<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, + fcx: &'a FnCtxt<'a, 'tcx>, } enum NonAsmTypeReason<'tcx> { @@ -33,27 +30,17 @@ enum NonAsmTypeReason<'tcx> { } impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { - pub fn new( - def_id: LocalDefId, - infcx: &'a InferCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, - ) -> Self { - InlineAsmCtxt { - typing_env, - target_features: infcx.tcx.asm_target_features(def_id), - infcx, - typeck_results, - } + pub(crate) fn new(fcx: &'a FnCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { + InlineAsmCtxt { target_features: fcx.tcx.asm_target_features(def_id), fcx } } fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx + self.fcx.tcx } fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> { - let ty = self.typeck_results.expr_ty_adjusted(expr); - let ty = self.infcx.resolve_vars_if_possible(ty); + let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted(expr); + let ty = self.fcx.try_structurally_resolve_type(expr.span, ty); if ty.has_non_region_infer() { Ty::new_misc_error(self.tcx()) } else { @@ -62,19 +49,23 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` - fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { + fn is_thin_ptr_ty(&self, span: Span, ty: Ty<'tcx>) -> bool { // Type still may have region variables, but `Sized` does not depend // on those, so just erase them before querying. - if ty.is_sized(self.tcx(), self.typing_env) { + if self.fcx.type_is_sized_modulo_regions(self.fcx.param_env, ty) { return true; } - if let ty::Foreign(..) = ty.kind() { + if let ty::Foreign(..) = self.fcx.try_structurally_resolve_type(span, ty).kind() { return true; } false } - fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result> { + fn get_asm_ty( + &self, + span: Span, + ty: Ty<'tcx>, + ) -> Result> { let asm_ty_isize = match self.tcx().sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, @@ -95,7 +86,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128), ty::FnPtr(..) => Ok(asm_ty_isize), ty::RawPtr(elem_ty, _) => { - if self.is_thin_ptr_ty(elem_ty) { + if self.is_thin_ptr_ty(span, elem_ty) { Ok(asm_ty_isize) } else { Err(NonAsmTypeReason::NotSizedPtr(ty)) @@ -109,11 +100,20 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let field = &fields[FieldIdx::ZERO]; let elem_ty = field.ty(self.tcx(), args); - let (size, ty) = match elem_ty.kind() { + let (size, ty) = match *elem_ty.kind() { ty::Array(ty, len) => { - let len = self.tcx().normalize_erasing_regions(self.typing_env, *len); + // FIXME: `try_structurally_resolve_const` doesn't eval consts + // in the old solver. + let len = if self.fcx.next_trait_solver() { + self.fcx.try_structurally_resolve_const(span, len) + } else { + self.fcx.tcx.normalize_erasing_regions( + self.fcx.typing_env(self.fcx.param_env), + len, + ) + }; if let Some(len) = len.try_to_target_usize(self.tcx()) { - (len, *ty) + (len, ty) } else { return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength( field.did, len, @@ -183,9 +183,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ); let fields = &ty.non_enum_variant().fields; let ty = fields[FieldIdx::ZERO].ty(self.tcx(), args); - self.get_asm_ty(ty) + self.get_asm_ty(expr.span, ty) } - _ => self.get_asm_ty(ty), + _ => self.get_asm_ty(expr.span, ty), }; let asm_ty = match asm_ty { Ok(asm_ty) => asm_ty, @@ -193,7 +193,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { match reason { NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => { let msg = format!("cannot evaluate SIMD vector length `{len}`"); - self.infcx + self.fcx .dcx() .struct_span_err(self.tcx().def_span(did), msg) .with_span_note( @@ -204,7 +204,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } NonAsmTypeReason::Invalid(ty) => { let msg = format!("cannot use value of type `{ty}` for inline assembly"); - self.infcx.dcx().struct_span_err(expr.span, msg).with_note( + self.fcx.dcx().struct_span_err(expr.span, msg).with_note( "only integers, floats, SIMD vectors, pointers and function pointers \ can be used as arguments for inline assembly", ).emit(); @@ -213,7 +213,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let msg = format!( "cannot use value of unsized pointer type `{ty}` for inline assembly" ); - self.infcx + self.fcx .dcx() .struct_span_err(expr.span, msg) .with_note("only sized pointers can be used in inline assembly") @@ -223,7 +223,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let msg = format!( "cannot use SIMD vector with element type `{ty}` for inline assembly" ); - self.infcx.dcx() + self.fcx.dcx() .struct_span_err(self.tcx().def_span(did), msg).with_span_note( expr.span, "only integers, floats, SIMD vectors, pointers and function pointers \ @@ -232,7 +232,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } NonAsmTypeReason::EmptySIMDArray(ty) => { let msg = format!("use of empty SIMD vector `{ty}`"); - self.infcx.dcx().struct_span_err(expr.span, msg).emit(); + self.fcx.dcx().struct_span_err(expr.span, msg).emit(); } } return None; @@ -241,9 +241,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !self.tcx().type_is_copy_modulo_regions(self.typing_env, ty) { + if !self.fcx.type_is_copy_modulo_regions(self.fcx.param_env, ty) { let msg = "arguments for inline assembly must be copyable"; - self.infcx + self.fcx .dcx() .struct_span_err(expr.span, msg) .with_note(format!("`{ty}` does not implement the Copy trait")) @@ -263,7 +263,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if in_asm_ty != asm_ty { let msg = "incompatible types for asm inout argument"; let in_expr_ty = self.expr_ty(in_expr); - self.infcx + self.fcx .dcx() .struct_span_err(vec![in_expr.span, expr.span], msg) .with_span_label(in_expr.span, format!("type `{in_expr_ty}`")) @@ -296,7 +296,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ) } else { let msg = format!("type `{ty}` cannot be used with this register class"); - let mut err = self.infcx.dcx().struct_span_err(expr.span, msg); + let mut err = self.fcx.dcx().struct_span_err(expr.span, msg); let supported_tys: Vec<_> = supported_tys.iter().map(|(t, _)| t.to_string()).collect(); err.note(format!( @@ -326,7 +326,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if let Some(feature) = feature { if !self.target_features.contains(feature) { let msg = format!("`{feature}` target feature is not enabled"); - self.infcx + self.fcx .dcx() .struct_span_err(expr.span, msg) .with_note(format!( @@ -384,9 +384,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { Some(asm_ty) } - pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { + pub(crate) fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { let Some(asm_arch) = self.tcx().sess.asm_arch else { - self.infcx.dcx().delayed_bug("target architecture does not support asm"); + self.fcx.dcx().delayed_bug("target architecture does not support asm"); return; }; let allow_experimental_reg = self.tcx().features().asm_experimental_reg(); @@ -418,7 +418,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { op.is_clobber(), ) { let msg = format!("cannot use register `{}`: {}", reg.name(), msg); - self.infcx.dcx().span_err(op_sp, msg); + self.fcx.dcx().span_err(op_sp, msg); continue; } } @@ -458,7 +458,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { reg_class.name(), feature ); - self.infcx.dcx().span_err(op_sp, msg); + self.fcx.dcx().span_err(op_sp, msg); // register isn't enabled, don't do more checks continue; } @@ -472,7 +472,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { .intersperse(", ") .collect::(), ); - self.infcx.dcx().span_err(op_sp, msg); + self.fcx.dcx().span_err(op_sp, msg); // register isn't enabled, don't do more checks continue; } @@ -512,7 +512,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Error(_) => {} _ if ty.is_integral() => {} _ => { - self.infcx + self.fcx .dcx() .struct_span_err(op_sp, "invalid type for `const` operand") .with_span_label( @@ -531,7 +531,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::FnDef(..) => {} ty::Error(_) => {} _ => { - self.infcx + self.fcx .dcx() .struct_span_err(op_sp, "invalid `sym` operand") .with_span_label( diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index af8ec37393472..d11799c6c4286 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -23,6 +23,7 @@ mod diverges; mod errors; mod expectation; mod expr; +mod inline_asm; // Used by clippy; pub mod expr_use_visitor; mod fallback; diff --git a/tests/ui/asm/named_const_simd_vec_len.rs b/tests/ui/asm/named_const_simd_vec_len.rs index 7df4d922d5c91..7fedeb7d4d122 100644 --- a/tests/ui/asm/named_const_simd_vec_len.rs +++ b/tests/ui/asm/named_const_simd_vec_len.rs @@ -3,6 +3,9 @@ //@ only-x86_64 //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver #![feature(repr_simd)] diff --git a/tests/ui/asm/normalizable-asm-ty.rs b/tests/ui/asm/normalizable-asm-ty.rs new file mode 100644 index 0000000000000..ad76fb0f4298a --- /dev/null +++ b/tests/ui/asm/normalizable-asm-ty.rs @@ -0,0 +1,15 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +fn invoke(pc_section: &[usize]) { + unsafe { + std::arch::asm!( + "/* {} */", + in(reg) pc_section[0] + ); + } +} + +fn main() {} From ae0c2fe3d8173f03878671e0bba38a03efa44c93 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 22 Apr 2025 15:43:34 -0700 Subject: [PATCH 6/9] transmutability: Support char, NonZeroXxx Note that `NonZero` support is not wired up, as the author encountered bugs while attempting this. A future commit will wire up `NonZero` support. --- compiler/rustc_transmute/src/layout/mod.rs | 7 +- compiler/rustc_transmute/src/layout/tree.rs | 146 +++++++++++++++--- .../rustc_transmute/src/layout/tree/tests.rs | 24 +-- .../src/maybe_transmutable/tests.rs | 82 +++++++++- tests/ui/transmutability/char.rs | 41 +++++ tests/ui/transmutability/char.stderr | 48 ++++++ 6 files changed, 302 insertions(+), 46 deletions(-) create mode 100644 tests/ui/transmutability/char.rs create mode 100644 tests/ui/transmutability/char.stderr diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index d555ea702a9fe..4d5f630ae229e 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -65,7 +65,12 @@ impl fmt::Debug for Byte { } } -#[cfg(test)] +impl From> for Byte { + fn from(src: RangeInclusive) -> Self { + Self::new(src) + } +} + impl From for Byte { fn from(src: u8) -> Self { Self::from_val(src) diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 6a09be18ef944..7cf712ce9e977 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -1,4 +1,4 @@ -use std::ops::ControlFlow; +use std::ops::{ControlFlow, RangeInclusive}; use super::{Byte, Def, Ref}; @@ -32,6 +32,22 @@ where Byte(Byte), } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum Endian { + Little, + Big, +} + +#[cfg(feature = "rustc")] +impl From for Endian { + fn from(order: rustc_abi::Endian) -> Endian { + match order { + rustc_abi::Endian::Little => Endian::Little, + rustc_abi::Endian::Big => Endian::Big, + } + } +} + impl Tree where D: Def, @@ -59,22 +75,60 @@ where /// A `Tree` representing the layout of `bool`. pub(crate) fn bool() -> Self { - Self::Byte(Byte::new(0x00..=0x01)) + Self::byte(0x00..=0x01) } /// A `Tree` whose layout matches that of a `u8`. pub(crate) fn u8() -> Self { - Self::Byte(Byte::new(0x00..=0xFF)) + Self::byte(0x00..=0xFF) + } + + /// A `Tree` whose layout matches that of a `char`. + pub(crate) fn char(order: Endian) -> Self { + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // All other `char` values are illegal. We can thus represent a `char` + // as a union of three possible layouts: + // - 00 00 [00, D7] XX + // - 00 00 [E0, FF] XX + // - 00 [01, 10] XX XX + + const _0: RangeInclusive = 0..=0; + const BYTE: RangeInclusive = 0x00..=0xFF; + let x = Self::from_big_endian(order, [_0, _0, 0x00..=0xD7, BYTE]); + let y = Self::from_big_endian(order, [_0, _0, 0xE0..=0xFF, BYTE]); + let z = Self::from_big_endian(order, [_0, 0x01..=0x10, BYTE, BYTE]); + Self::alt([x, y, z]) } - /// A `Tree` whose layout accepts exactly the given bit pattern. - pub(crate) fn from_bits(bits: u8) -> Self { - Self::Byte(Byte::from_val(bits)) + /// A `Tree` whose layout matches `std::num::NonZeroXxx`. + #[allow(dead_code)] + pub(crate) fn nonzero(width_in_bytes: u64) -> Self { + const BYTE: RangeInclusive = 0x00..=0xFF; + const NONZERO: RangeInclusive = 0x01..=0xFF; + + (0..width_in_bytes) + .map(|nz_idx| { + (0..width_in_bytes) + .map(|pos| Self::byte(if pos == nz_idx { NONZERO } else { BYTE })) + .fold(Self::unit(), Self::then) + }) + .fold(Self::uninhabited(), Self::or) + } + + pub(crate) fn bytes>(bytes: [B; N]) -> Self { + Self::seq(bytes.map(B::into).map(Self::Byte)) + } + + pub(crate) fn byte(byte: impl Into) -> Self { + Self::Byte(byte.into()) } /// A `Tree` whose layout is a number of the given width. - pub(crate) fn number(width_in_bytes: usize) -> Self { - Self::Seq(vec![Self::u8(); width_in_bytes]) + pub(crate) fn number(width_in_bytes: u64) -> Self { + Self::Seq(vec![Self::u8(); width_in_bytes.try_into().unwrap()]) } /// A `Tree` whose layout is entirely padding of the given width. @@ -125,13 +179,35 @@ where Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true, } } -} -impl Tree -where - D: Def, - R: Ref, -{ + /// Produces a `Tree` which represents a sequence of bytes stored in + /// `order`. + /// + /// `bytes` is taken to be in big-endian byte order, and its order will be + /// swapped if `order == Endian::Little`. + pub(crate) fn from_big_endian>( + order: Endian, + mut bytes: [B; N], + ) -> Self { + if order == Endian::Little { + (&mut bytes[..]).reverse(); + } + + Self::bytes(bytes) + } + + /// Produces a `Tree` where each of the trees in `trees` are sequenced one + /// after another. + pub(crate) fn seq(trees: [Tree; N]) -> Self { + trees.into_iter().fold(Tree::unit(), Self::then) + } + + /// Produces a `Tree` where each of the trees in `trees` are accepted as + /// alternative layouts. + pub(crate) fn alt(trees: [Tree; N]) -> Self { + trees.into_iter().fold(Tree::uninhabited(), Self::or) + } + /// Produces a new `Tree` where `other` is sequenced after `self`. pub(crate) fn then(self, other: Self) -> Self { match (self, other) { @@ -222,17 +298,17 @@ pub(crate) mod rustc { ty::Float(nty) => { let width = nty.bit_width() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Int(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Uint(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx), @@ -249,11 +325,33 @@ pub(crate) mod rustc { .fold(Tree::unit(), |tree, elt| tree.then(elt))) } - ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() { - AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx), - AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx), - AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx), - }, + ty::Adt(adt_def, _args_ref) if !ty.is_box() => { + let (lo, hi) = cx.tcx().layout_scalar_valid_range(adt_def.did()); + + use core::ops::Bound::*; + let is_transparent = adt_def.repr().transparent(); + match (adt_def.adt_kind(), lo, hi) { + (AdtKind::Struct, Unbounded, Unbounded) => { + Self::from_struct((ty, layout), *adt_def, cx) + } + (AdtKind::Struct, Included(1), Included(_hi)) if is_transparent => { + // FIXME(@joshlf): Support `NonZero` types: + // - Check to make sure that the first field is + // numerical + // - Check to make sure that the upper bound is the + // maximum value for the field's type + // - Construct `Self::nonzero` + Err(Err::NotYetSupported) + } + (AdtKind::Enum, Unbounded, Unbounded) => { + Self::from_enum((ty, layout), *adt_def, cx) + } + (AdtKind::Union, Unbounded, Unbounded) => { + Self::from_union((ty, layout), *adt_def, cx) + } + _ => Err(Err::NotYetSupported), + } + } ty::Ref(lifetime, ty, mutability) => { let layout = layout_of(cx, *ty)?; @@ -268,6 +366,8 @@ pub(crate) mod rustc { })) } + ty::Char => Ok(Self::char(cx.tcx().data_layout.endian.into())), + _ => Err(Err::NotYetSupported), } } @@ -450,7 +550,7 @@ pub(crate) mod rustc { &bytes[bytes.len() - size.bytes_usize()..] } }; - Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) + Self::Seq(bytes.iter().map(|&b| Self::byte(b)).collect()) } /// Constructs a `Tree` from a union. diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs index 44f50a25c939a..8c3dbbe37ab21 100644 --- a/compiler/rustc_transmute/src/layout/tree/tests.rs +++ b/compiler/rustc_transmute/src/layout/tree/tests.rs @@ -20,23 +20,18 @@ mod prune { #[test] fn seq_1() { - let layout: Tree = - Tree::def(Def::NoSafetyInvariants).then(Tree::from_bits(0x00)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + let layout: Tree = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00)); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } #[test] fn seq_2() { - let layout: Tree = Tree::from_bits(0x00) - .then(Tree::def(Def::NoSafetyInvariants)) - .then(Tree::from_bits(0x01)); + let layout: Tree = + Tree::byte(0x00).then(Tree::def(Def::NoSafetyInvariants)).then(Tree::byte(0x01)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00).then(Tree::from_bits(0x01)) + Tree::byte(0x00).then(Tree::byte(0x01)) ); } } @@ -66,7 +61,7 @@ mod prune { #[test] fn invisible_def_in_seq_len_3() { let layout: Tree = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::HasSafetyInvariants)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), @@ -94,12 +89,9 @@ mod prune { #[test] fn visible_def_in_seq_len_3() { let layout: Tree = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::NoSafetyInvariants)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 24e2a1acadd68..992fcb7cc4c81 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -177,9 +177,9 @@ mod bool { #[test] fn should_permit_validity_expansion_and_reject_contraction() { - let b0 = layout::Tree::::from_bits(0); - let b1 = layout::Tree::::from_bits(1); - let b2 = layout::Tree::::from_bits(2); + let b0 = layout::Tree::::byte(0); + let b1 = layout::Tree::::byte(1); + let b2 = layout::Tree::::byte(2); let alts = [b0, b1, b2]; @@ -279,8 +279,8 @@ mod alt { fn should_permit_identity_transmutation() { type Tree = layout::Tree; - let x = Tree::Seq(vec![Tree::from_bits(0), Tree::from_bits(0)]); - let y = Tree::Seq(vec![Tree::bool(), Tree::from_bits(1)]); + let x = Tree::Seq(vec![Tree::byte(0), Tree::byte(0)]); + let y = Tree::Seq(vec![Tree::bool(), Tree::byte(1)]); let layout = Tree::Alt(vec![x, y]); let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( @@ -323,6 +323,76 @@ mod union { } } +mod char { + use super::*; + use crate::layout::tree::Endian; + + #[test] + fn should_permit_valid_transmutation() { + for order in [Endian::Big, Endian::Little] { + use Answer::*; + let char_layout = layout::Tree::::char(order); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // This loop synthesizes a singleton-validity type for the extremes + // of each range, and for one past the end of the extremes of each + // range. + let no = No(Reason::DstIsBitIncompatible); + for (src, answer) in [ + (0u32, Yes), + (0xD7FF, Yes), + (0xD800, no.clone()), + (0xDFFF, no.clone()), + (0xE000, Yes), + (0x10FFFF, Yes), + (0x110000, no.clone()), + (0xFFFF0000, no.clone()), + (0xFFFFFFFF, no), + ] { + let src_layout = + layout::tree::Tree::::from_big_endian(order, src.to_be_bytes()); + + let a = is_transmutable(&src_layout, &char_layout, Assume::default()); + assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}"); + } + } + } +} + +mod nonzero { + use super::*; + use crate::{Answer, Reason}; + + const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16]; + + #[test] + fn should_permit_identity_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + let layout = layout::Tree::::nonzero(width); + assert_eq!(is_transmutable(&layout, &layout, Assume::default()), Answer::Yes); + } + } + + #[test] + fn should_permit_valid_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + use Answer::*; + + let num = layout::Tree::::number(width); + let nz = layout::Tree::::nonzero(width); + + let a = is_transmutable(&num, &nz, Assume::default()); + assert_eq!(a, No(Reason::DstIsBitIncompatible), "width:{width}"); + + let a = is_transmutable(&nz, &num, Assume::default()); + assert_eq!(a, Yes, "width:{width}"); + } + } +} + mod r#ref { use super::*; @@ -330,7 +400,7 @@ mod r#ref { fn should_permit_identity_transmutation() { type Tree = crate::layout::Tree; - let layout = Tree::Seq(vec![Tree::from_bits(0), Tree::Ref([()])]); + let layout = Tree::Seq(vec![Tree::byte(0x00), Tree::Ref([()])]); let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( layout.clone(), diff --git a/tests/ui/transmutability/char.rs b/tests/ui/transmutability/char.rs new file mode 100644 index 0000000000000..55a6153732955 --- /dev/null +++ b/tests/ui/transmutability/char.rs @@ -0,0 +1,41 @@ +#![feature(never_type)] +#![feature(transmutability)] + +use std::mem::{Assume, TransmuteFrom}; + +pub fn is_transmutable() +where + Dst: TransmuteFrom, +{ +} + +fn main() { + is_transmutable::(); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // `Char` has variants whose tags are in the top and bottom of each range. + // It also has generic variants which are out of bounds of these ranges, but + // are generic on types which may be set to `!` to "disable" them in the + // transmutability analysis. + #[repr(u32)] + enum Char { + A = 0, + B = 0xD7FF, + OverB(B) = 0xD800, + UnderC(C) = 0xDFFF, + C = 0xE000, + D = 0x10FFFF, + OverD(D) = 0x110000, + } + + is_transmutable::, char>(); + is_transmutable::, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::, char>(); + //~^ ERROR cannot be safely transmuted into `char` +} diff --git a/tests/ui/transmutability/char.stderr b/tests/ui/transmutability/char.stderr new file mode 100644 index 0000000000000..98e7ae9c0d49c --- /dev/null +++ b/tests/ui/transmutability/char.stderr @@ -0,0 +1,48 @@ +error[E0277]: `main::Char<(), !, !>` cannot be safely transmuted into `char` + --> $DIR/char.rs:35:39 + | +LL | is_transmutable::, char>(); + | ^^^^ at least one value of `main::Char<(), !, !>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char` cannot be safely transmuted into `char` + --> $DIR/char.rs:37:39 + | +LL | is_transmutable::, char>(); + | ^^^^ at least one value of `main::Char` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char` cannot be safely transmuted into `char` + --> $DIR/char.rs:39:39 + | +LL | is_transmutable::, char>(); + | ^^^^ at least one value of `main::Char` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. From 1ec3e626c2a0c1635d06cc9f8721d97a05e61433 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sat, 26 Apr 2025 01:57:08 +0200 Subject: [PATCH 7/9] Remove redundant check --- compiler/rustc_hir_analysis/src/check/check.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4f338c6a19e69..f92b2aea160a1 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -730,7 +730,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { .is_ok() { check_impl_items_against_trait(tcx, def_id, impl_trait_header); - check_on_unimplemented(tcx, def_id); } } } From 4b309c67c6fc3c5d6ff51a417af453380348c2eb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 26 Apr 2025 01:42:48 +0000 Subject: [PATCH 8/9] Simply try to unpeel AsyncFnKindHelper goal in emit_specialized_closure_kind_error --- .../traits/fulfillment_errors.rs | 41 +++++-------------- .../kind-due-to-arg-with-box-wrap.rs | 22 ++++++++++ .../kind-due-to-arg-with-box-wrap.stderr | 21 ++++++++++ 3 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs create mode 100644 tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5648021f6134e..4c407cd71333d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -813,38 +813,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, mut trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option { - // If `AsyncFnKindHelper` is not implemented, that means that the closure kind - // doesn't extend the goal kind. This is worth reporting, but we can only do so - // if we actually know which closure this goal comes from, so look at the cause - // to see if we can extract that information. - if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) - && let Some(found_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(0).to_opt_closure_kind() - && let Some(expected_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind() - && !found_kind.extends(expected_kind) - { - if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() { - // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + // If we end up on an `AsyncFnKindHelper` goal, try to unwrap the parent + // `AsyncFn*` goal. + if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) { + let mut code = obligation.cause.code(); + // Unwrap a `FunctionArg` cause, which has been refined from a derived obligation. + if let ObligationCauseCode::FunctionArg { parent_code, .. } = code { + code = &**parent_code; + } + // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + if let Some((_, Some(parent))) = code.parent_with_predicate() { trait_pred = parent; - } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } = - obligation.cause.code() - && let Some(typeck_results) = &self.typeck_results - && let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) = - *typeck_results.node_type(arg_hir_id).kind() - { - // Otherwise, extract the closure kind from the obligation, - // but only if we actually have an argument to deduce the - // closure type from... - let mut err = self.report_closure_error( - &obligation, - closure_def_id, - found_kind, - expected_kind, - "Async", - ); - self.note_obligation_cause(&mut err, &obligation); - return Some(err.emit()); } } diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs new file mode 100644 index 0000000000000..650fb10d94be0 --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs @@ -0,0 +1,22 @@ +//@ edition: 2024 + +// Regression test for . + +struct Test; + +impl Test { + async fn an_async_fn(&mut self) { + todo!() + } + + pub async fn uses_takes_asyncfn(&mut self) { + takes_asyncfn(Box::new(async || self.an_async_fn().await)); + //~^ ERROR expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + } +} + +async fn takes_asyncfn(_: impl AsyncFn()) { + todo!() +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr new file mode 100644 index 0000000000000..e79f95c197b63 --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr @@ -0,0 +1,21 @@ +error[E0525]: expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:13:32 + | +LL | takes_asyncfn(Box::new(async || self.an_async_fn().await)); + | ------------- ---------^^^^^^^^-------------------------- + | | | | | + | | | | closure is `AsyncFnMut` because it mutates the variable `*self` here + | | | this closure implements `AsyncFnMut`, not `AsyncFn` + | | the requirement to implement `AsyncFn` derives from here + | required by a bound introduced by this call + | + = note: required for `Box<{async closure@$DIR/kind-due-to-arg-with-box-wrap.rs:13:32: 13:40}>` to implement `AsyncFn()` +note: required by a bound in `takes_asyncfn` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:18:32 + | +LL | async fn takes_asyncfn(_: impl AsyncFn()) { + | ^^^^^^^^^ required by this bound in `takes_asyncfn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0525`. From 0084862cd3c739cd850d1e757b7059e154c1fb08 Mon Sep 17 00:00:00 2001 From: Ethan Wu Date: Fri, 25 Apr 2025 22:21:40 -0700 Subject: [PATCH 9/9] Grammar fixes for BufRead::has_data_left docs --- library/std/src/io/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 5242261cf93f6..96fac4f6bde68 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2319,9 +2319,9 @@ pub trait BufRead: Read { /// Checks if there is any data left to be `read`. /// /// This function may fill the buffer to check for data, - /// so this functions returns `Result`, not `bool`. + /// so this function returns `Result`, not `bool`. /// - /// Default implementation calls `fill_buf` and checks that + /// The default implementation calls `fill_buf` and checks that the /// returned slice is empty (which means that there is no data left, /// since EOF is reached). ///