Skip to content

Commit 44d6439

Browse files
committed
Auto merge of #6025 - thomcc:compare_exchange_atomics, r=flip1995
Extend invalid_atomic_ordering for compare_exchange{,_weak} and fetch_update changelog: The invalid_atomic_ordering lint can now detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`. --- I was surprised not to find an issue or existing support here, since these are the functions which are always hardest to get the ordering right on for me (as the allowed orderings for `fail` depend on the `success` parameter). I believe this lint now covers all atomic methods which care about their ordering now, but I could be wrong. Hopefully I didn't forget to do anything for the PR!
2 parents 5e60497 + 09f7a37 commit 44d6439

8 files changed

+631
-7
lines changed

clippy_lints/src/atomic_ordering.rs

+100-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
88

99
declare_clippy_lint! {
1010
/// **What it does:** Checks for usage of invalid atomic
11-
/// ordering in atomic loads/stores and memory fences.
11+
/// ordering in atomic loads/stores/exchanges/updates and
12+
/// memory fences.
1213
///
1314
/// **Why is this bad?** Using an invalid atomic ordering
1415
/// will cause a panic at run-time.
@@ -17,22 +18,35 @@ declare_clippy_lint! {
1718
///
1819
/// **Example:**
1920
/// ```rust,no_run
20-
/// # use std::sync::atomic::{self, AtomicBool, Ordering};
21+
/// # use std::sync::atomic::{self, AtomicU8, Ordering};
2122
///
22-
/// let x = AtomicBool::new(true);
23+
/// let x = AtomicU8::new(0);
2324
///
25+
/// // Bad: `Release` and `AcqRel` cannot be used for `load`.
2426
/// let _ = x.load(Ordering::Release);
2527
/// let _ = x.load(Ordering::AcqRel);
2628
///
27-
/// x.store(false, Ordering::Acquire);
28-
/// x.store(false, Ordering::AcqRel);
29+
/// // Bad: `Acquire` and `AcqRel` cannot be used for `store`.
30+
/// x.store(1, Ordering::Acquire);
31+
/// x.store(2, Ordering::AcqRel);
2932
///
33+
/// // Bad: `Relaxed` cannot be used as a fence's ordering.
3034
/// atomic::fence(Ordering::Relaxed);
3135
/// atomic::compiler_fence(Ordering::Relaxed);
36+
///
37+
/// // Bad: `Release` and `AcqRel` are both always invalid
38+
/// // for the failure ordering (the last arg).
39+
/// let _ = x.compare_exchange(1, 2, Ordering::SeqCst, Ordering::Release);
40+
/// let _ = x.compare_exchange_weak(2, 3, Ordering::AcqRel, Ordering::AcqRel);
41+
///
42+
/// // Bad: The failure ordering is not allowed to be
43+
/// // stronger than the success order, and `SeqCst` is
44+
/// // stronger than `Relaxed`.
45+
/// let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |val| Some(val + val));
3246
/// ```
3347
pub INVALID_ATOMIC_ORDERING,
3448
correctness,
35-
"usage of invalid atomic ordering in atomic loads/stores and memory fences"
49+
"usage of invalid atomic ordering in atomic operations and memory fences"
3650
}
3751

3852
declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
@@ -127,9 +141,89 @@ fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
127141
}
128142
}
129143

144+
fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
145+
if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
146+
cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
147+
} else {
148+
None
149+
}
150+
}
151+
152+
fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
153+
if_chain! {
154+
if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
155+
let method = method_path.ident.name.as_str();
156+
if type_is_atomic(cx, &args[0]);
157+
if method == "compare_exchange" || method == "compare_exchange_weak" || method == "fetch_update";
158+
let (success_order_arg, failure_order_arg) = if method == "fetch_update" {
159+
(&args[1], &args[2])
160+
} else {
161+
(&args[3], &args[4])
162+
};
163+
if let Some(fail_ordering_def_id) = opt_ordering_defid(cx, failure_order_arg);
164+
then {
165+
// Helper type holding on to some checking and error reporting data. Has
166+
// - (success ordering name,
167+
// - list of failure orderings forbidden by the success order,
168+
// - suggestion message)
169+
type OrdLintInfo = (&'static str, &'static [&'static str], &'static str);
170+
let relaxed: OrdLintInfo = ("Relaxed", &["SeqCst", "Acquire"], "ordering mode `Relaxed`");
171+
let acquire: OrdLintInfo = ("Acquire", &["SeqCst"], "ordering modes `Acquire` or `Relaxed`");
172+
let seq_cst: OrdLintInfo = ("SeqCst", &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
173+
let release = ("Release", relaxed.1, relaxed.2);
174+
let acqrel = ("AcqRel", acquire.1, acquire.2);
175+
let search = [relaxed, acquire, seq_cst, release, acqrel];
176+
177+
let success_lint_info = opt_ordering_defid(cx, success_order_arg)
178+
.and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
179+
search
180+
.iter()
181+
.find(|(ordering, ..)| {
182+
match_def_path(cx, success_ord_def_id,
183+
&["core", "sync", "atomic", "Ordering", ordering])
184+
})
185+
.copied()
186+
});
187+
188+
if match_ordering_def_path(cx, fail_ordering_def_id, &["Release", "AcqRel"]) {
189+
// If we don't know the success order is, use what we'd suggest
190+
// if it were maximally permissive.
191+
let suggested = success_lint_info.unwrap_or(seq_cst).2;
192+
span_lint_and_help(
193+
cx,
194+
INVALID_ATOMIC_ORDERING,
195+
failure_order_arg.span,
196+
&format!(
197+
"{}'s failure ordering may not be `Release` or `AcqRel`",
198+
method,
199+
),
200+
None,
201+
&format!("consider using {} instead", suggested),
202+
);
203+
} else if let Some((success_ord_name, bad_ords_given_success, suggested)) = success_lint_info {
204+
if match_ordering_def_path(cx, fail_ordering_def_id, bad_ords_given_success) {
205+
span_lint_and_help(
206+
cx,
207+
INVALID_ATOMIC_ORDERING,
208+
failure_order_arg.span,
209+
&format!(
210+
"{}'s failure ordering may not be stronger than the success ordering of `{}`",
211+
method,
212+
success_ord_name,
213+
),
214+
None,
215+
&format!("consider using {} instead", suggested),
216+
);
217+
}
218+
}
219+
}
220+
}
221+
}
222+
130223
impl<'tcx> LateLintPass<'tcx> for AtomicOrdering {
131224
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
132225
check_atomic_load_store(cx, expr);
133226
check_memory_fence(cx, expr);
227+
check_atomic_compare_exchange(cx, expr);
134228
}
135229
}

src/lintlist/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,7 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
930930
Lint {
931931
name: "invalid_atomic_ordering",
932932
group: "correctness",
933-
desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences",
933+
desc: "usage of invalid atomic ordering in atomic operations and memory fences",
934934
deprecation: None,
935935
module: "atomic_ordering",
936936
},

tests/ui/atomic_ordering_exchange.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![warn(clippy::invalid_atomic_ordering)]
2+
3+
use std::sync::atomic::{AtomicUsize, Ordering};
4+
5+
fn main() {
6+
// `compare_exchange` (not weak) testing
7+
let x = AtomicUsize::new(0);
8+
9+
// Allowed ordering combos
10+
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Relaxed);
11+
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Acquire);
12+
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Relaxed);
13+
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Relaxed);
14+
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Acquire);
15+
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Relaxed);
16+
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Relaxed);
17+
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Acquire);
18+
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::SeqCst);
19+
20+
// AcqRel is always forbidden as a failure ordering
21+
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
22+
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
23+
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
24+
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
25+
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
26+
27+
// Release is always forbidden as a failure ordering
28+
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
29+
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
30+
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
31+
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
32+
let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
33+
34+
// Release success order forbids failure order of Acquire or SeqCst
35+
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
36+
let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
37+
38+
// Relaxed success order also forbids failure order of Acquire or SeqCst
39+
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
40+
let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
41+
42+
// Acquire/AcqRel forbids failure order of SeqCst
43+
let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
44+
let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
45+
}
+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
2+
--> $DIR/atomic_ordering_exchange.rs:21:57
3+
|
4+
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
5+
| ^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::invalid-atomic-ordering` implied by `-D warnings`
8+
= help: consider using ordering mode `Relaxed` instead
9+
10+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
11+
--> $DIR/atomic_ordering_exchange.rs:22:57
12+
|
13+
LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
14+
| ^^^^^^^^^^^^^^^^
15+
|
16+
= help: consider using ordering modes `Acquire` or `Relaxed` instead
17+
18+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
19+
--> $DIR/atomic_ordering_exchange.rs:23:57
20+
|
21+
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
22+
| ^^^^^^^^^^^^^^^^
23+
|
24+
= help: consider using ordering mode `Relaxed` instead
25+
26+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
27+
--> $DIR/atomic_ordering_exchange.rs:24:56
28+
|
29+
LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
30+
| ^^^^^^^^^^^^^^^^
31+
|
32+
= help: consider using ordering modes `Acquire` or `Relaxed` instead
33+
34+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
35+
--> $DIR/atomic_ordering_exchange.rs:25:56
36+
|
37+
LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
38+
| ^^^^^^^^^^^^^^^^
39+
|
40+
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
41+
42+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
43+
--> $DIR/atomic_ordering_exchange.rs:28:57
44+
|
45+
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
46+
| ^^^^^^^^^^^^^^^^^
47+
|
48+
= help: consider using ordering mode `Relaxed` instead
49+
50+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
51+
--> $DIR/atomic_ordering_exchange.rs:29:57
52+
|
53+
LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
54+
| ^^^^^^^^^^^^^^^^^
55+
|
56+
= help: consider using ordering modes `Acquire` or `Relaxed` instead
57+
58+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
59+
--> $DIR/atomic_ordering_exchange.rs:30:57
60+
|
61+
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
62+
| ^^^^^^^^^^^^^^^^^
63+
|
64+
= help: consider using ordering mode `Relaxed` instead
65+
66+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
67+
--> $DIR/atomic_ordering_exchange.rs:31:56
68+
|
69+
LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
70+
| ^^^^^^^^^^^^^^^^^
71+
|
72+
= help: consider using ordering modes `Acquire` or `Relaxed` instead
73+
74+
error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
75+
--> $DIR/atomic_ordering_exchange.rs:32:56
76+
|
77+
LL | let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
78+
| ^^^^^^^^^^^^^^^^^
79+
|
80+
= help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
81+
82+
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
83+
--> $DIR/atomic_ordering_exchange.rs:35:57
84+
|
85+
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
86+
| ^^^^^^^^^^^^^^^^^
87+
|
88+
= help: consider using ordering mode `Relaxed` instead
89+
90+
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
91+
--> $DIR/atomic_ordering_exchange.rs:36:57
92+
|
93+
LL | let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
94+
| ^^^^^^^^^^^^^^^^
95+
|
96+
= help: consider using ordering mode `Relaxed` instead
97+
98+
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
99+
--> $DIR/atomic_ordering_exchange.rs:39:57
100+
|
101+
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
102+
| ^^^^^^^^^^^^^^^^
103+
|
104+
= help: consider using ordering mode `Relaxed` instead
105+
106+
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
107+
--> $DIR/atomic_ordering_exchange.rs:40:57
108+
|
109+
LL | let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
110+
| ^^^^^^^^^^^^^^^^^
111+
|
112+
= help: consider using ordering mode `Relaxed` instead
113+
114+
error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire`
115+
--> $DIR/atomic_ordering_exchange.rs:43:57
116+
|
117+
LL | let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
118+
| ^^^^^^^^^^^^^^^^
119+
|
120+
= help: consider using ordering modes `Acquire` or `Relaxed` instead
121+
122+
error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel`
123+
--> $DIR/atomic_ordering_exchange.rs:44:56
124+
|
125+
LL | let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
126+
| ^^^^^^^^^^^^^^^^
127+
|
128+
= help: consider using ordering modes `Acquire` or `Relaxed` instead
129+
130+
error: aborting due to 16 previous errors
131+
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#![warn(clippy::invalid_atomic_ordering)]
2+
3+
use std::sync::atomic::{AtomicPtr, Ordering};
4+
5+
fn main() {
6+
let ptr = &mut 5;
7+
let ptr2 = &mut 10;
8+
// `compare_exchange_weak` testing
9+
let x = AtomicPtr::new(ptr);
10+
11+
// Allowed ordering combos
12+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Relaxed);
13+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Acquire);
14+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Relaxed);
15+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Relaxed);
16+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Acquire);
17+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Relaxed);
18+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Relaxed);
19+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Acquire);
20+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::SeqCst);
21+
22+
// AcqRel is always forbidden as a failure ordering
23+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel);
24+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel);
25+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel);
26+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel);
27+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel);
28+
29+
// Release is always forbidden as a failure ordering
30+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release);
31+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release);
32+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release);
33+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release);
34+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release);
35+
36+
// Release success order forbids failure order of Acquire or SeqCst
37+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire);
38+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst);
39+
40+
// Relaxed success order also forbids failure order of Acquire or SeqCst
41+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst);
42+
let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire);
43+
44+
// Acquire/AcqRel forbids failure order of SeqCst
45+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst);
46+
let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst);
47+
}

0 commit comments

Comments
 (0)