Rollup merge of #129248 - compiler-errors:raw-ref-deref, r=nnethercote
Taking a raw ref (`&raw (const|mut)`) of a deref of pointer (`*ptr`) is always safe T-opsem decided in https://github.com/rust-lang/reference/pull/1387 that `*ptr` is only unsafe if the place is accessed. This means that taking a raw ref of a deref expr is always safe, since it doesn't constitute a read. This also relaxes the `DEREF_NULLPTR` lint to stop warning in the case of raw ref of a deref'd nullptr, and updates its docs to reflect that change in the UB specification. This does not change the behavior of `addr_of!((*ptr).field)`, since field projections still require the projection is in-bounds. I'm on the fence whether this requires an FCP, since it's something that is guaranteed by the reference you could ostensibly call this a bugfix since we were counting truly safe operations as unsafe. Perhaps someone on opsem has a strong opinion? cc `@rust-lang/opsem`
This commit is contained in:
commit
93bf791e8b
8 changed files with 46 additions and 52 deletions
|
@ -2657,8 +2657,8 @@ declare_lint! {
|
|||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Dereferencing a null pointer causes [undefined behavior] even as a place expression,
|
||||
/// like `&*(0 as *const i32)` or `addr_of!(*(0 as *const i32))`.
|
||||
/// Dereferencing a null pointer causes [undefined behavior] if it is accessed
|
||||
/// (loaded from or stored to).
|
||||
///
|
||||
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
|
||||
pub DEREF_NULLPTR,
|
||||
|
@ -2673,14 +2673,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
|
|||
/// test if expression is a null ptr
|
||||
fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
match &expr.kind {
|
||||
rustc_hir::ExprKind::Cast(expr, ty) => {
|
||||
if let rustc_hir::TyKind::Ptr(_) = ty.kind {
|
||||
hir::ExprKind::Cast(expr, ty) => {
|
||||
if let hir::TyKind::Ptr(_) = ty.kind {
|
||||
return is_zero(expr) || is_null_ptr(cx, expr);
|
||||
}
|
||||
}
|
||||
// check for call to `core::ptr::null` or `core::ptr::null_mut`
|
||||
rustc_hir::ExprKind::Call(path, _) => {
|
||||
if let rustc_hir::ExprKind::Path(ref qpath) = path.kind {
|
||||
hir::ExprKind::Call(path, _) => {
|
||||
if let hir::ExprKind::Path(ref qpath) = path.kind {
|
||||
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() {
|
||||
return matches!(
|
||||
cx.tcx.get_diagnostic_name(def_id),
|
||||
|
@ -2697,7 +2697,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
|
|||
/// test if expression is the literal `0`
|
||||
fn is_zero(expr: &hir::Expr<'_>) -> bool {
|
||||
match &expr.kind {
|
||||
rustc_hir::ExprKind::Lit(lit) => {
|
||||
hir::ExprKind::Lit(lit) => {
|
||||
if let LitKind::Int(a, _) = lit.node {
|
||||
return a == 0;
|
||||
}
|
||||
|
@ -2707,8 +2707,16 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
|
|||
false
|
||||
}
|
||||
|
||||
if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind {
|
||||
if is_null_ptr(cx, expr_deref) {
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, expr_deref) = expr.kind
|
||||
&& is_null_ptr(cx, expr_deref)
|
||||
{
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::AddrOf(hir::BorrowKind::Raw, ..),
|
||||
..
|
||||
}) = cx.tcx.parent_hir_node(expr.hir_id)
|
||||
{
|
||||
// `&raw *NULL` is ok.
|
||||
} else {
|
||||
cx.emit_span_lint(DEREF_NULLPTR, expr.span, BuiltinDerefNullptr {
|
||||
label: expr.span,
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue