Skip to content

Commit 635c4a5

Browse files
committed
Auto merge of #114494 - est31:extend_useless_ptr_null_checks, r=jackh726
Make useless_ptr_null_checks smarter about some std functions This teaches the `useless_ptr_null_checks` lint that some std functions can't ever return null pointers, because they need to point to valid data, get references as input, etc. This is achieved by introducing an `#[rustc_never_returns_null_ptr]` attribute and adding it to these std functions (gated behind bootstrap `cfg_attr`). Later on, the attribute could maybe be used to tell LLVM that the returned pointer is never null. I don't expect much impact of that though, as the functions are pretty shallow and usually the input data is already never null. Follow-up of PR #113657 Fixes #114442
2 parents e81f85f + 4b1bc27 commit 635c4a5

File tree

18 files changed

+94
-46
lines changed

18 files changed

+94
-46
lines changed

compiler/rustc_feature/src/builtin_attrs.rs

+4
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
699699
rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
700700
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
701701
),
702+
rustc_attr!(
703+
rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing,
704+
"#[rustc_never_returns_null_ptr] is used to mark functions returning non-null pointers."
705+
),
702706
rustc_attr!(
703707
rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, @only_local: true,
704708
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."

compiler/rustc_lint/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ lint_ptr_null_checks_fn_ptr = function pointers are not nullable, so checking th
453453
.help = wrap the function pointer inside an `Option` and use `Option::is_none` to check for null pointer value
454454
.label = expression has type `{$orig_ty}`
455455
456+
lint_ptr_null_checks_fn_ret = returned pointer of `{$fn_name}` call is never null, so checking it for null will always return false
457+
456458
lint_ptr_null_checks_ref = references are not nullable, so checking them for null will always return false
457459
.label = expression has type `{$orig_ty}`
458460

compiler/rustc_lint/src/lints.rs

+2
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,8 @@ pub enum PtrNullChecksDiag<'a> {
635635
#[label]
636636
label: Span,
637637
},
638+
#[diag(lint_ptr_null_checks_fn_ret)]
639+
FnRet { fn_name: Ident },
638640
}
639641

640642
// for_loops_over_fallibles.rs

compiler/rustc_lint/src/ptr_nulls.rs

+29-23
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,30 @@ declare_lint! {
3131

3232
declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
3333

34-
/// This function detects and returns the original expression from a series of consecutive casts,
35-
/// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`.
36-
fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
34+
/// This function checks if the expression is from a series of consecutive casts,
35+
/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
36+
/// a fn ptr, a reference, or a function call whose definition is
37+
/// annotated with `#![rustc_never_returns_null_ptr]`.
38+
/// If this situation is present, the function returns the appropriate diagnostic.
39+
fn incorrect_check<'a, 'tcx: 'a>(
40+
cx: &'a LateContext<'tcx>,
41+
mut e: &'a Expr<'a>,
42+
) -> Option<PtrNullChecksDiag<'tcx>> {
3743
let mut had_at_least_one_cast = false;
3844
loop {
3945
e = e.peel_blocks();
46+
if let ExprKind::MethodCall(_, _expr, [], _) = e.kind
47+
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
48+
&& cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
49+
&& let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
50+
return Some(PtrNullChecksDiag::FnRet { fn_name });
51+
} else if let ExprKind::Call(path, _args) = e.kind
52+
&& let ExprKind::Path(ref qpath) = path.kind
53+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
54+
&& cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
55+
&& let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
56+
return Some(PtrNullChecksDiag::FnRet { fn_name });
57+
}
4058
e = if let ExprKind::Cast(expr, t) = e.kind
4159
&& let TyKind::Ptr(_) = t.kind {
4260
had_at_least_one_cast = true;
@@ -46,33 +64,21 @@ fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'
4664
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) {
4765
had_at_least_one_cast = true;
4866
expr
49-
} else if let ExprKind::Call(path, [arg]) = e.kind
50-
&& let ExprKind::Path(ref qpath) = path.kind
51-
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
52-
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) {
53-
had_at_least_one_cast = true;
54-
arg
5567
} else if had_at_least_one_cast {
56-
return Some(e);
68+
let orig_ty = cx.typeck_results().expr_ty(e);
69+
return if orig_ty.is_fn() {
70+
Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span })
71+
} else if orig_ty.is_ref() {
72+
Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span })
73+
} else {
74+
None
75+
};
5776
} else {
5877
return None;
5978
};
6079
}
6180
}
6281

63-
fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option<PtrNullChecksDiag<'a>> {
64-
let expr = ptr_cast_chain(cx, expr)?;
65-
66-
let orig_ty = cx.typeck_results().expr_ty(expr);
67-
if orig_ty.is_fn() {
68-
Some(PtrNullChecksDiag::FnPtr { orig_ty, label: expr.span })
69-
} else if orig_ty.is_ref() {
70-
Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span })
71-
} else {
72-
None
73-
}
74-
}
75-
7682
impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
7783
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
7884
match expr.kind {

compiler/rustc_passes/src/check_attr.rs

+3
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ impl CheckAttrVisitor<'_> {
139139
self.check_rustc_std_internal_symbol(&attr, span, target)
140140
}
141141
sym::naked => self.check_naked(hir_id, attr, span, target),
142+
sym::rustc_never_returns_null_ptr => {
143+
self.check_applied_to_fn_or_method(hir_id, attr, span, target)
144+
}
142145
sym::rustc_legacy_const_generics => {
143146
self.check_rustc_legacy_const_generics(hir_id, &attr, span, target, item)
144147
}

compiler/rustc_span/src/symbol.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,6 @@ symbols! {
11781178
ptr_cast_const,
11791179
ptr_cast_mut,
11801180
ptr_const_is_null,
1181-
ptr_from_mut,
11821181
ptr_from_ref,
11831182
ptr_guaranteed_cmp,
11841183
ptr_is_null,
@@ -1337,6 +1336,7 @@ symbols! {
13371336
rustc_main,
13381337
rustc_mir,
13391338
rustc_must_implement_one_of,
1339+
rustc_never_returns_null_ptr,
13401340
rustc_nonnull_optimization_guaranteed,
13411341
rustc_nounwind,
13421342
rustc_object_lifetime_default,

library/alloc/src/rc.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,7 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> {
13041304
/// assert_eq!(unsafe { &*x_ptr }, "hello");
13051305
/// ```
13061306
#[stable(feature = "rc_raw", since = "1.17.0")]
1307+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
13071308
pub fn into_raw(this: Self) -> *const T {
13081309
let ptr = Self::as_ptr(&this);
13091310
mem::forget(this);
@@ -1327,6 +1328,7 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> {
13271328
/// assert_eq!(unsafe { &*x_ptr }, "hello");
13281329
/// ```
13291330
#[stable(feature = "weak_into_raw", since = "1.45.0")]
1331+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
13301332
pub fn as_ptr(this: &Self) -> *const T {
13311333
let ptr: *mut RcBox<T> = NonNull::as_ptr(this.ptr);
13321334

library/alloc/src/sync.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
14541454
/// ```
14551455
#[must_use = "losing the pointer will leak memory"]
14561456
#[stable(feature = "rc_raw", since = "1.17.0")]
1457+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
14571458
pub fn into_raw(this: Self) -> *const T {
14581459
let ptr = Self::as_ptr(&this);
14591460
mem::forget(this);
@@ -1478,6 +1479,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> {
14781479
/// ```
14791480
#[must_use]
14801481
#[stable(feature = "rc_as_ptr", since = "1.45.0")]
1482+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
14811483
pub fn as_ptr(this: &Self) -> *const T {
14821484
let ptr: *mut ArcInner<T> = NonNull::as_ptr(this.ptr);
14831485

library/alloc/src/vec/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1258,6 +1258,7 @@ impl<T, A: Allocator> Vec<T, A> {
12581258
/// [`as_mut_ptr`]: Vec::as_mut_ptr
12591259
/// [`as_ptr`]: Vec::as_ptr
12601260
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
1261+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
12611262
#[inline]
12621263
pub fn as_ptr(&self) -> *const T {
12631264
// We shadow the slice method of the same name to avoid going through
@@ -1317,6 +1318,7 @@ impl<T, A: Allocator> Vec<T, A> {
13171318
/// [`as_mut_ptr`]: Vec::as_mut_ptr
13181319
/// [`as_ptr`]: Vec::as_ptr
13191320
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
1321+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
13201322
#[inline]
13211323
pub fn as_mut_ptr(&mut self) -> *mut T {
13221324
// We shadow the slice method of the same name to avoid going through

library/core/src/cell.rs

+4
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ impl<T: ?Sized> Cell<T> {
556556
#[inline]
557557
#[stable(feature = "cell_as_ptr", since = "1.12.0")]
558558
#[rustc_const_stable(feature = "const_cell_as_ptr", since = "1.32.0")]
559+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
559560
pub const fn as_ptr(&self) -> *mut T {
560561
self.value.get()
561562
}
@@ -1111,6 +1112,7 @@ impl<T: ?Sized> RefCell<T> {
11111112
/// ```
11121113
#[inline]
11131114
#[stable(feature = "cell_as_ptr", since = "1.12.0")]
1115+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
11141116
pub fn as_ptr(&self) -> *mut T {
11151117
self.value.get()
11161118
}
@@ -2105,6 +2107,7 @@ impl<T: ?Sized> UnsafeCell<T> {
21052107
#[inline(always)]
21062108
#[stable(feature = "rust1", since = "1.0.0")]
21072109
#[rustc_const_stable(feature = "const_unsafecell_get", since = "1.32.0")]
2110+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
21082111
pub const fn get(&self) -> *mut T {
21092112
// We can just cast the pointer from `UnsafeCell<T>` to `T` because of
21102113
// #[repr(transparent)]. This exploits std's special status, there is
@@ -2248,6 +2251,7 @@ impl<T: ?Sized> SyncUnsafeCell<T> {
22482251
/// when casting to `&mut T`, and ensure that there are no mutations
22492252
/// or mutable aliases going on when casting to `&T`
22502253
#[inline]
2254+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
22512255
pub const fn get(&self) -> *mut T {
22522256
self.value.get()
22532257
}

library/core/src/ffi/c_str.rs

+1
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ impl CStr {
511511
#[must_use]
512512
#[stable(feature = "rust1", since = "1.0.0")]
513513
#[rustc_const_stable(feature = "const_str_as_ptr", since = "1.32.0")]
514+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
514515
pub const fn as_ptr(&self) -> *const c_char {
515516
self.inner.as_ptr()
516517
}

library/core/src/ptr/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ where
698698
#[inline(always)]
699699
#[must_use]
700700
#[unstable(feature = "ptr_from_ref", issue = "106116")]
701+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
701702
#[rustc_diagnostic_item = "ptr_from_ref"]
702703
pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
703704
r
@@ -710,7 +711,7 @@ pub const fn from_ref<T: ?Sized>(r: &T) -> *const T {
710711
#[inline(always)]
711712
#[must_use]
712713
#[unstable(feature = "ptr_from_ref", issue = "106116")]
713-
#[rustc_diagnostic_item = "ptr_from_mut"]
714+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
714715
pub const fn from_mut<T: ?Sized>(r: &mut T) -> *mut T {
715716
r
716717
}

library/core/src/ptr/non_null.rs

+2
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ impl<T: ?Sized> NonNull<T> {
338338
/// ```
339339
#[stable(feature = "nonnull", since = "1.25.0")]
340340
#[rustc_const_stable(feature = "const_nonnull_as_ptr", since = "1.32.0")]
341+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
341342
#[must_use]
342343
#[inline(always)]
343344
pub const fn as_ptr(self) -> *mut T {
@@ -597,6 +598,7 @@ impl<T> NonNull<[T]> {
597598
#[must_use]
598599
#[unstable(feature = "slice_ptr_get", issue = "74265")]
599600
#[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
601+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
600602
pub const fn as_mut_ptr(self) -> *mut T {
601603
self.as_non_null_ptr().as_ptr()
602604
}

library/core/src/slice/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,7 @@ impl<T> [T] {
730730
/// [`as_mut_ptr`]: slice::as_mut_ptr
731731
#[stable(feature = "rust1", since = "1.0.0")]
732732
#[rustc_const_stable(feature = "const_slice_as_ptr", since = "1.32.0")]
733+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
733734
#[inline(always)]
734735
#[must_use]
735736
pub const fn as_ptr(&self) -> *const T {
@@ -760,6 +761,7 @@ impl<T> [T] {
760761
#[stable(feature = "rust1", since = "1.0.0")]
761762
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
762763
#[rustc_allow_const_fn_unstable(const_mut_refs)]
764+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
763765
#[inline(always)]
764766
#[must_use]
765767
pub const fn as_mut_ptr(&mut self) -> *mut T {

library/core/src/str/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ impl str {
386386
/// ```
387387
#[stable(feature = "rust1", since = "1.0.0")]
388388
#[rustc_const_stable(feature = "rustc_str_as_ptr", since = "1.32.0")]
389+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
389390
#[must_use]
390391
#[inline(always)]
391392
pub const fn as_ptr(&self) -> *const u8 {
@@ -401,6 +402,7 @@ impl str {
401402
/// It is your responsibility to make sure that the string slice only gets
402403
/// modified in a way that it remains valid UTF-8.
403404
#[stable(feature = "str_as_mut_ptr", since = "1.36.0")]
405+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
404406
#[must_use]
405407
#[inline(always)]
406408
pub fn as_mut_ptr(&mut self) -> *mut u8 {

library/core/src/sync/atomic.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,7 @@ impl AtomicBool {
10181018
#[inline]
10191019
#[stable(feature = "atomic_as_ptr", since = "1.70.0")]
10201020
#[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
1021+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
10211022
pub const fn as_ptr(&self) -> *mut bool {
10221023
self.v.get().cast()
10231024
}
@@ -1953,6 +1954,7 @@ impl<T> AtomicPtr<T> {
19531954
#[inline]
19541955
#[stable(feature = "atomic_as_ptr", since = "1.70.0")]
19551956
#[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
1957+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
19561958
pub const fn as_ptr(&self) -> *mut *mut T {
19571959
self.p.get()
19581960
}
@@ -2891,6 +2893,7 @@ macro_rules! atomic_int {
28912893
#[inline]
28922894
#[stable(feature = "atomic_as_ptr", since = "1.70.0")]
28932895
#[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")]
2896+
#[cfg_attr(not(bootstrap), rustc_never_returns_null_ptr)]
28942897
pub const fn as_ptr(&self) -> *mut $int_type {
28952898
self.v.get()
28962899
}

tests/ui/lint/ptr_null_checks.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ fn main() {
3838
if (&mut 8 as *mut i32).is_null() {}
3939
//~^ WARN references are not nullable
4040
if ptr::from_mut(&mut 8).is_null() {}
41-
//~^ WARN references are not nullable
41+
//~^ WARN call is never null
4242
if (&8 as *const i32).is_null() {}
4343
//~^ WARN references are not nullable
4444
if ptr::from_ref(&8).is_null() {}
45-
//~^ WARN references are not nullable
45+
//~^ WARN call is never null
4646
if ptr::from_ref(&8).cast_mut().is_null() {}
47-
//~^ WARN references are not nullable
47+
//~^ WARN call is never null
4848
if (ptr::from_ref(&8).cast_mut() as *mut i32).is_null() {}
49-
//~^ WARN references are not nullable
49+
//~^ WARN call is never null
5050
if (&8 as *const i32) == std::ptr::null() {}
5151
//~^ WARN references are not nullable
5252
let ref_num = &8;
@@ -65,6 +65,12 @@ fn main() {
6565
if (&*{ static_i32() } as *const i32).is_null() {}
6666
//~^ WARN references are not nullable
6767

68+
// ---------------- Functions -------------------
69+
if ptr::NonNull::new(&mut 8).unwrap().as_ptr().is_null() {}
70+
//~^ WARN call is never null
71+
if ptr::NonNull::<u8>::dangling().as_ptr().is_null() {}
72+
//~^ WARN call is never null
73+
6874
// ----------------------------------------------
6975
const ZPTR: *const () = 0 as *const _;
7076
const NOT_ZPTR: *const () = 1 as *const _;

0 commit comments

Comments
 (0)