diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 7e298a9a63c79..bd8f428ee9b63 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -1,8 +1,9 @@ use rustc_ast::visit::{visit_opt, walk_list}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; -use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem}; -use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl}; +use rustc_middle::ty::Ty; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::Span; use rustc_span::symbol::sym; @@ -132,7 +133,8 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind && is_temporary_rvalue(receiver) && let ty = cx.typeck_results().expr_ty(receiver) - && owns_allocation(cx.tcx, ty) + && let adjs = cx.typeck_results().expr_adjustments(receiver) + && is_inner_allocation_owned(ty, adjs) && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && cx.tcx.has_attr(fn_id, sym::rustc_as_ptr) { @@ -151,6 +153,34 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { } } +/// Determine if a `#[rustc_as_ptr]` receiver type (with adjustments) is only applied +/// to owned allocations. +fn is_inner_allocation_owned(input_ty: Ty<'_>, adjs: &[Adjustment<'_>]) -> bool { + // 1. Exclude any pointer types (as they cannot be owned) + // + // - `&[u8]`: NOT OWNED + // - `*const u8`: NOT OWNED + // - `Box`: MAYBE OWNED + // - `Box<&u8>`: MAYBE OWNED + !input_ty.is_any_ptr() + // 2. Look at all the deref-ed types and exclude any pointer types + // + // - `Box` -> `u8`: OWNED + // - `Box<&u8>` -> `&u8`: NOT OWNED + // - `Box>` -> `Box` -> `u8`: OWNED + // - `MaybeUninit>`: OWNED + // - `Box>` -> `MaybeUninit<&u8>`: OWNED + && adjs.iter() + .filter_map(|adj| match adj.kind { + Adjust::Deref(_) => Some(adj.target), + Adjust::NeverToAny + | Adjust::Borrow(_) + | Adjust::Pointer(_) + | Adjust::ReborrowPin(_) => None, + }) + .all(|deref_ty| !deref_ty.is_any_ptr()) +} + fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { match expr.kind { // Const is not temporary. @@ -196,27 +226,3 @@ fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { ExprKind::Type(..) | ExprKind::Err(..) => false, } } - -// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box, Box, UnsafeCell, -// SyncUnsafeCell, or any of the above in arbitrary many nested Box'es. -fn owns_allocation(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { - if ty.is_array() { - true - } else if let Some(inner) = ty.boxed_ty() { - inner.is_slice() - || inner.is_str() - || inner.ty_adt_def().is_some_and(|def| tcx.is_lang_item(def.did(), LangItem::CStr)) - || owns_allocation(tcx, inner) - } else if let Some(def) = ty.ty_adt_def() { - for lang_item in [LangItem::String, LangItem::MaybeUninit, LangItem::UnsafeCell] { - if tcx.is_lang_item(def.did(), lang_item) { - return true; - } - } - tcx.get_diagnostic_name(def.did()).is_some_and(|name| { - matches!(name, sym::cstring_type | sym::Vec | sym::Cell | sym::SyncUnsafeCell) - }) - } else { - false - } -} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5252c446e1d38..882828584ab52 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -171,7 +171,6 @@ symbols! { CallOnceFuture, CallRefFuture, Capture, - Cell, Center, Cleanup, Clone, @@ -316,7 +315,6 @@ symbols! { SubdiagMessage, Subdiagnostic, Sync, - SyncUnsafeCell, T, Target, ToOwned, diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index bfd2a71f97b2c..2abfe1fae549d 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -304,7 +304,6 @@ pub use once::OnceCell; /// ``` /// /// See the [module-level documentation](self) for more. -#[cfg_attr(not(test), rustc_diagnostic_item = "Cell")] #[stable(feature = "rust1", since = "1.0.0")] #[repr(transparent)] #[rustc_pub_transparent] @@ -2274,7 +2273,6 @@ impl, U> DispatchFromDyn> for UnsafeCell /// See [`UnsafeCell`] for details. #[unstable(feature = "sync_unsafe_cell", issue = "95439")] #[repr(transparent)] -#[rustc_diagnostic_item = "SyncUnsafeCell"] #[rustc_pub_transparent] pub struct SyncUnsafeCell { value: UnsafeCell, diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.rs b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs index 17c3eca89e273..c22aeb0cae954 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/types.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs @@ -52,6 +52,17 @@ fn main() { //~^ ERROR a dangling pointer will be produced because the temporary `UnsafeCell` will be dropped declval::>().get(); //~^ ERROR a dangling pointer will be produced because the temporary `SyncUnsafeCell` will be dropped - declval::>().as_ptr(); + declval::>>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `MaybeUninit>` will be dropped + declval::>>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `Box>` will be dropped + declval::>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `MaybeUninit<&u8>` will be dropped + declval::<[&u8; 10]>().as_ptr(); + //~^ ERROR a dangling pointer will be produced because the temporary `[&u8; 10]` will be dropped + + // should not lint + declval::<&[u8]>().as_ptr(); declval::().as_ptr(); + declval::>().as_ptr(); } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr index 250ed6dc9e379..2dad3f3688535 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr @@ -190,5 +190,49 @@ LL | declval::>().get(); = note: pointers do not have a lifetime; when calling `get` the `SyncUnsafeCell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned = help: for more information, see -error: aborting due to 17 previous errors +error: a dangling pointer will be produced because the temporary `MaybeUninit>` will be dropped + --> $DIR/types.rs:55:48 + | +LL | declval::>>().as_ptr(); + | ------------------------------------------ ^^^^^^ this pointer will immediately be invalid + | | + | this `MaybeUninit>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `MaybeUninit>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `Box>` will be dropped + --> $DIR/types.rs:57:40 + | +LL | declval::>>().as_ptr(); + | ---------------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `Box>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `MaybeUninit<&u8>` will be dropped + --> $DIR/types.rs:59:35 + | +LL | declval::>().as_ptr(); + | ----------------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `MaybeUninit<&u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `MaybeUninit<&u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: a dangling pointer will be produced because the temporary `[&u8; 10]` will be dropped + --> $DIR/types.rs:61:28 + | +LL | declval::<[&u8; 10]>().as_ptr(); + | ---------------------- ^^^^^^ this pointer will immediately be invalid + | | + | this `[&u8; 10]` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | + = note: pointers do not have a lifetime; when calling `as_ptr` the `[&u8; 10]` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned + = help: for more information, see + +error: aborting due to 21 previous errors