Improve invalid memory ordering diagnostics.
This commit is contained in:
parent
acb5c16fa8
commit
47080eacf8
1 changed files with 60 additions and 81 deletions
|
@ -4,7 +4,6 @@ use rustc_attr as attr;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
|
||||||
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
|
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
|
||||||
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
|
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
|
||||||
use rustc_middle::ty::subst::SubstsRef;
|
use rustc_middle::ty::subst::SubstsRef;
|
||||||
|
@ -1483,39 +1482,32 @@ impl InvalidAtomicOrdering {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_ordering(cx: &LateContext<'_>, did: DefId, orderings: &[Symbol]) -> bool {
|
fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
|
||||||
|
let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
|
||||||
|
let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
|
||||||
let tcx = cx.tcx;
|
let tcx = cx.tcx;
|
||||||
let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
|
let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
|
||||||
orderings.iter().any(|ordering| {
|
let name = tcx.item_name(did);
|
||||||
tcx.item_name(did) == *ordering && {
|
let parent = tcx.parent(did);
|
||||||
let parent = tcx.parent(did);
|
[sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
|
||||||
Some(parent) == atomic_ordering
|
|&ordering| {
|
||||||
// needed in case this is a ctor, not a variant
|
name == ordering
|
||||||
|| tcx.opt_parent(parent) == atomic_ordering
|
&& (Some(parent) == atomic_ordering
|
||||||
}
|
// needed in case this is a ctor, not a variant
|
||||||
})
|
|| tcx.opt_parent(parent) == atomic_ordering)
|
||||||
}
|
},
|
||||||
|
)
|
||||||
fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
|
|
||||||
if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
|
|
||||||
cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
use rustc_hir::def::{DefKind, Res};
|
|
||||||
use rustc_hir::QPath;
|
|
||||||
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
|
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
|
||||||
&& let Some((ordering_arg, invalid_ordering)) = match method {
|
&& let Some((ordering_arg, invalid_ordering)) = match method {
|
||||||
sym::load => Some((&args[1], sym::Release)),
|
sym::load => Some((&args[1], sym::Release)),
|
||||||
sym::store => Some((&args[2], sym::Acquire)),
|
sym::store => Some((&args[2], sym::Acquire)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = ordering_arg.kind
|
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg)
|
||||||
&& let Res::Def(DefKind::Ctor(..), ctor_id) = path.res
|
&& (ordering == invalid_ordering || ordering == sym::AcqRel)
|
||||||
&& Self::matches_ordering(cx, ctor_id, &[invalid_ordering, sym::AcqRel])
|
|
||||||
{
|
{
|
||||||
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| {
|
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| {
|
||||||
if method == sym::load {
|
if method == sym::load {
|
||||||
|
@ -1537,9 +1529,7 @@ impl InvalidAtomicOrdering {
|
||||||
&& let ExprKind::Path(ref func_qpath) = func.kind
|
&& let ExprKind::Path(ref func_qpath) = func.kind
|
||||||
&& let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
|
&& let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
|
||||||
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
|
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
|
||||||
&& let ExprKind::Path(ref ordering_qpath) = &args[0].kind
|
&& Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
|
||||||
&& let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id()
|
|
||||||
&& Self::matches_ordering(cx, ordering_def_id, &[sym::Relaxed])
|
|
||||||
{
|
{
|
||||||
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| {
|
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| {
|
||||||
diag.build("memory fences cannot have `Relaxed` ordering")
|
diag.build("memory fences cannot have `Relaxed` ordering")
|
||||||
|
@ -1550,62 +1540,51 @@ impl InvalidAtomicOrdering {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
|
let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
|
||||||
&& let Some((success_order_arg, failure_order_arg)) = match method {
|
else {return };
|
||||||
sym::fetch_update => Some((&args[1], &args[2])),
|
|
||||||
sym::compare_exchange | sym::compare_exchange_weak => Some((&args[3], &args[4])),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
&& let Some(fail_ordering_def_id) = Self::opt_ordering_defid(cx, failure_order_arg)
|
|
||||||
{
|
|
||||||
// Helper type holding on to some checking and error reporting data. Has
|
|
||||||
// - (success ordering,
|
|
||||||
// - list of failure orderings forbidden by the success order,
|
|
||||||
// - suggestion message)
|
|
||||||
type OrdLintInfo = (Symbol, &'static [Symbol], &'static str);
|
|
||||||
const RELAXED: OrdLintInfo = (sym::Relaxed, &[sym::SeqCst, sym::Acquire], "ordering mode `Relaxed`");
|
|
||||||
const ACQUIRE: OrdLintInfo = (sym::Acquire, &[sym::SeqCst], "ordering modes `Acquire` or `Relaxed`");
|
|
||||||
const SEQ_CST: OrdLintInfo = (sym::SeqCst, &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
|
|
||||||
const RELEASE: OrdLintInfo = (sym::Release, RELAXED.1, RELAXED.2);
|
|
||||||
const ACQREL: OrdLintInfo = (sym::AcqRel, ACQUIRE.1, ACQUIRE.2);
|
|
||||||
const SEARCH: [OrdLintInfo; 5] = [RELAXED, ACQUIRE, SEQ_CST, RELEASE, ACQREL];
|
|
||||||
|
|
||||||
let success_lint_info = Self::opt_ordering_defid(cx, success_order_arg)
|
let (success_order_arg, fail_order_arg) = match method {
|
||||||
.and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
|
sym::fetch_update => (&args[1], &args[2]),
|
||||||
SEARCH
|
sym::compare_exchange | sym::compare_exchange_weak => (&args[3], &args[4]),
|
||||||
.iter()
|
_ => return,
|
||||||
.copied()
|
};
|
||||||
.find(|(ordering, ..)| {
|
|
||||||
Self::matches_ordering(cx, success_ord_def_id, &[*ordering])
|
let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
|
||||||
})
|
|
||||||
});
|
if matches!(fail_ordering, sym::Release | sym::AcqRel) {
|
||||||
if Self::matches_ordering(cx, fail_ordering_def_id, &[sym::Release, sym::AcqRel]) {
|
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg.span, |diag| {
|
||||||
// If we don't know the success order is, use what we'd suggest
|
diag.build(&format!(
|
||||||
// if it were maximally permissive.
|
"{method}'s failure ordering may not be `Release` or `AcqRel`, \
|
||||||
let suggested = success_lint_info.unwrap_or(SEQ_CST).2;
|
since a failed {method} does not result in a write",
|
||||||
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
|
))
|
||||||
let msg = format!(
|
.span_label(fail_order_arg.span, "invalid failure ordering")
|
||||||
"{}'s failure ordering may not be `Release` or `AcqRel`",
|
.help("consider using Acquire or Relaxed failure ordering instead")
|
||||||
method,
|
.emit();
|
||||||
);
|
});
|
||||||
diag.build(&msg)
|
}
|
||||||
.help(&format!("consider using {} instead", suggested))
|
|
||||||
.emit();
|
let Some(success_ordering) = Self::match_ordering(cx, success_order_arg) else { return };
|
||||||
});
|
|
||||||
} else if let Some((success_ord, bad_ords_given_success, suggested)) = success_lint_info {
|
if matches!(
|
||||||
if Self::matches_ordering(cx, fail_ordering_def_id, bad_ords_given_success) {
|
(success_ordering, fail_ordering),
|
||||||
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
|
(sym::Relaxed | sym::Release, sym::Acquire)
|
||||||
let msg = format!(
|
| (sym::Relaxed | sym::Release | sym::Acquire | sym::AcqRel, sym::SeqCst)
|
||||||
"{}'s failure ordering may not be stronger than the success ordering of `{}`",
|
) {
|
||||||
method,
|
let success_suggestion =
|
||||||
success_ord,
|
if success_ordering == sym::Release && fail_ordering == sym::Acquire {
|
||||||
);
|
sym::AcqRel
|
||||||
diag.build(&msg)
|
} else {
|
||||||
.help(&format!("consider using {} instead", suggested))
|
fail_ordering
|
||||||
.emit();
|
};
|
||||||
});
|
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, success_order_arg.span, |diag| {
|
||||||
}
|
diag.build(&format!(
|
||||||
}
|
"{method}'s success ordering must be at least as strong as its failure ordering"
|
||||||
|
))
|
||||||
|
.span_label(fail_order_arg.span, format!("{fail_ordering} failure ordering"))
|
||||||
|
.span_label(success_order_arg.span, format!("{success_ordering} success ordering"))
|
||||||
|
.help(format!("consider using {success_suggestion} success ordering instead"))
|
||||||
|
.emit();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue