Merge from rustc
This commit is contained in:
commit
06ca3abc5a
496 changed files with 5453 additions and 2575 deletions
|
|
@ -149,7 +149,7 @@ static TARGETS: &[&str] = &[
|
|||
"wasm32-unknown-unknown",
|
||||
"wasm32-wasi",
|
||||
"wasm32-wasip1",
|
||||
"wasm32-wasi-preview1-threads",
|
||||
"wasm32-wasip1-threads",
|
||||
"x86_64-apple-darwin",
|
||||
"x86_64-apple-ios",
|
||||
"x86_64-fortanix-unknown-sgx",
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit a4c63fe5388beaa09e5f91196c86addab0a03580
|
||||
Subproject commit 7065f0ef4aa267a7455e1c478b5ccacb7baea59c
|
||||
|
|
@ -52,7 +52,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
|
|||
.as_ref()
|
||||
.map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
|
||||
|stmt| match &stmt.kind {
|
||||
StmtKind::Local(_) => true,
|
||||
StmtKind::Let(_) => true,
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
|
||||
StmtKind::Item(_) => false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ impl BlockEq {
|
|||
|
||||
/// If the statement is a local, checks if the bound names match the expected list of names.
|
||||
fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
|
||||
if let StmtKind::Local(l) = s.kind {
|
||||
if let StmtKind::Let(l) = s.kind {
|
||||
let mut i = 0usize;
|
||||
let mut res = true;
|
||||
l.pat.each_binding_or_first(&mut |_, _, _, name| {
|
||||
|
|
@ -389,7 +389,7 @@ fn eq_stmts(
|
|||
eq: &mut HirEqInterExpr<'_, '_, '_>,
|
||||
moved_bindings: &mut Vec<(HirId, Symbol)>,
|
||||
) -> bool {
|
||||
(if let StmtKind::Local(l) = stmt.kind {
|
||||
(if let StmtKind::Let(l) = stmt.kind {
|
||||
let old_count = moved_bindings.len();
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, name| {
|
||||
moved_bindings.push((id, name.name));
|
||||
|
|
@ -432,7 +432,7 @@ fn scan_block_for_eq<'tcx>(
|
|||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(i, stmt)| {
|
||||
if let StmtKind::Local(l) = stmt.kind
|
||||
if let StmtKind::Let(l) = stmt.kind
|
||||
&& needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id))
|
||||
{
|
||||
local_needs_ordered_drop = true;
|
||||
|
|
@ -509,7 +509,7 @@ fn scan_block_for_eq<'tcx>(
|
|||
// Clear out all locals seen at the end so far. None of them can be moved.
|
||||
let stmts = &blocks[0].stmts;
|
||||
for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] {
|
||||
if let StmtKind::Local(l) = stmt.kind {
|
||||
if let StmtKind::Let(l) = stmt.kind {
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, _| {
|
||||
// FIXME(rust/#120456) - is `swap_remove` correct?
|
||||
eq.locals.swap_remove(&id);
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
|||
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
|
||||
// `default` method of the `Default` trait, and store statement index in current block being
|
||||
// checked and the name of the bound variable
|
||||
let (local, variant, binding_name, binding_type, span) = if let StmtKind::Local(local) = stmt.kind
|
||||
let (local, variant, binding_name, binding_type, span) = if let StmtKind::Let(local) = stmt.kind
|
||||
// only take `let ...` statements
|
||||
&& let Some(expr) = local.init
|
||||
&& !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
|||
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
// we cannot check the exact type since it's a hir::Ty which does not implement `is_numeric`
|
||||
StmtKind::Local(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())),
|
||||
StmtKind::Let(local) => self.ty_bounds.push(ExplicitTyBound(local.ty.is_some())),
|
||||
|
||||
_ => self.ty_bounds.push(ExplicitTyBound(false)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
|||
}
|
||||
},
|
||||
StmtKind::Expr(e) => self.visit_expr(e),
|
||||
StmtKind::Local(l) => {
|
||||
StmtKind::Let(l) => {
|
||||
self.visit_pat(l.pat);
|
||||
if let Some(e) = l.init {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> {
|
||||
if let ExprKind::Block(block, _label @ None) = kind
|
||||
&& let Block {
|
||||
stmts: [Stmt { kind: StmtKind::Local(local), .. }],
|
||||
stmts: [Stmt { kind: StmtKind::Let(local), .. }],
|
||||
expr: Some(expr_end_of_block),
|
||||
rules: BlockCheckMode::DefaultBlock,
|
||||
..
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
|||
let mut it = block.stmts.iter().peekable();
|
||||
while let Some(stmt) = it.next() {
|
||||
if let Some(expr) = it.peek()
|
||||
&& let hir::StmtKind::Local(local) = stmt.kind
|
||||
&& let hir::StmtKind::Let(local) = stmt.kind
|
||||
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
|
||||
&& let hir::StmtKind::Expr(if_) = expr.kind
|
||||
&& let hir::ExprKind::If(
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@ fn get_assignments<'a, 'tcx>(
|
|||
stmts
|
||||
.iter()
|
||||
.filter_map(move |stmt| match stmt.kind {
|
||||
StmtKind::Local(..) | StmtKind::Item(..) => None,
|
||||
StmtKind::Let(..) | StmtKind::Item(..) => None,
|
||||
StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e),
|
||||
})
|
||||
.chain(*expr)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr
|
|||
}
|
||||
|
||||
fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) {
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& is_vec_pop_unwrap(cx, init, is_empty_recv)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
|||
}
|
||||
return false; // no need to walk further *on the variable*
|
||||
},
|
||||
Res::Def(DefKind::Static(_) | DefKind::Const, ..) => {
|
||||
Res::Def(DefKind::Static{..} | DefKind::Const, ..) => {
|
||||
if index_used_directly {
|
||||
self.indexed_directly.insert(
|
||||
seqvar.segments[0].ident.name,
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t
|
|||
match stmt.kind {
|
||||
StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)),
|
||||
// add the let...else expression (if present)
|
||||
StmtKind::Local(local) => local.init.map(|init| (init, local.els)),
|
||||
StmtKind::Let(local) => local.init.map(|init| (init, local.els)),
|
||||
StmtKind::Item(..) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
|
|||
Res::Local(hir_id) => {
|
||||
self.ids.insert(hir_id);
|
||||
},
|
||||
Res::Def(DefKind::Static(_), def_id) => {
|
||||
Res::Def(DefKind::Static{..}, def_id) => {
|
||||
let mutable = self.cx.tcx.is_mutable_static(def_id);
|
||||
self.def_ids.insert(def_id, mutable);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_lint::LateContext;
|
|||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
|
||||
([stmt, stmts @ ..], expr) => {
|
||||
if let StmtKind::Local(&Local {
|
||||
if let StmtKind::Let(&Local {
|
||||
init: Some(e),
|
||||
els: None,
|
||||
..
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ impl<'tcx> QuestionMark {
|
|||
return;
|
||||
}
|
||||
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& local.els.is_none()
|
||||
&& local.ty.is_none()
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<
|
|||
// If block only contains statements,
|
||||
// reduce `{ X; }` to `X` or `X;`
|
||||
match inner_stmt.kind {
|
||||
hir::StmtKind::Local(local) => Some(local.span),
|
||||
hir::StmtKind::Let(local) => Some(local.span),
|
||||
hir::StmtKind::Expr(e) => Some(e.span),
|
||||
hir::StmtKind::Semi(..) => Some(inner_stmt.span),
|
||||
hir::StmtKind::Item(..) => None,
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ pub(super) fn check<'tcx>(
|
|||
},
|
||||
hir::ExprKind::Path(ref p) => matches!(
|
||||
cx.qpath_res(p, arg.hir_id),
|
||||
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static(_), _)
|
||||
hir::def::Res::Def(hir::def::DefKind::Const | hir::def::DefKind::Static{..}, _)
|
||||
),
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>
|
|||
match stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
|
||||
StmtKind::Item(..) => None,
|
||||
StmtKind::Local(Local { init, pat, .. }) => {
|
||||
StmtKind::Let(Local { init, pat, .. }) => {
|
||||
if let PatKind::Binding(_, hir_id, ..) = pat.kind {
|
||||
init.map(|init_expr| (init_expr, Some(hir_id)))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ fn indirect_usage<'tcx>(
|
|||
binding: HirId,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<IndirectUsage<'tcx>> {
|
||||
if let StmtKind::Local(&Local {
|
||||
if let StmtKind::Let(&Local {
|
||||
pat: Pat {
|
||||
kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
|
||||
..
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &E
|
|||
|
||||
fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
|
||||
for stmt in statements {
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
|
||||
&& let hir::def::Res::Local(local_hir_id) = path.res
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
|||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if !in_external_macro(cx.tcx.sess, stmt.span)
|
||||
&& let StmtKind::Local(local) = stmt.kind
|
||||
&& let StmtKind::Let(local) = stmt.kind
|
||||
&& let PatKind::Binding(BindingAnnotation(ByRef::Yes, mutabl), .., name, None) = local.pat.kind
|
||||
&& let Some(init) = local.init
|
||||
// Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue.
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
|
|||
}
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Let(local) => {
|
||||
if let Local { init: Some(e), .. } = local {
|
||||
DivergenceVisitor { cx }.visit_expr(e);
|
||||
}
|
||||
|
|
@ -291,7 +291,7 @@ fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> St
|
|||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
|
||||
// If the declaration is of a local variable, check its initializer
|
||||
// expression if it has one. Otherwise, keep going.
|
||||
StmtKind::Local(local) => local
|
||||
StmtKind::Let(local) => local
|
||||
.init
|
||||
.as_ref()
|
||||
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ fn collect_unsafe_exprs<'tcx>(
|
|||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
hir::Path {
|
||||
res: Res::Def(DefKind::Static(Mutability::Mut), _),
|
||||
res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _),
|
||||
..
|
||||
},
|
||||
)) => {
|
||||
|
|
@ -149,7 +149,7 @@ fn collect_unsafe_exprs<'tcx>(
|
|||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
hir::Path {
|
||||
res: Res::Def(DefKind::Static(Mutability::Mut), _),
|
||||
res: Res::Def(DefKind::Static{mutability:Mutability::Mut, ..}, _),
|
||||
..
|
||||
}
|
||||
))
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ fn contains_let(cond: &Expr<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
let StmtKind::Local(local) = stmt.kind else {
|
||||
let StmtKind::Let(local) = stmt.kind else {
|
||||
return false;
|
||||
};
|
||||
!local.pat.walk_short(|pat| {
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ impl NoEffect {
|
|||
);
|
||||
return true;
|
||||
}
|
||||
} else if let StmtKind::Local(local) = stmt.kind {
|
||||
} else if let StmtKind::Let(local) = stmt.kind {
|
||||
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id)
|
||||
&& !matches!(local.source, LocalSource::AsyncFn)
|
||||
&& let Some(init) = local.init
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if let StmtKind::Local(local) = stmt.kind {
|
||||
if let StmtKind::Let(local) = stmt.kind {
|
||||
if in_external_macro(cx.sess(), local.pat.span) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir
|
|||
}
|
||||
|
||||
fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
if let StmtKind::Local(Local {
|
||||
if let StmtKind::Let(Local {
|
||||
pat,
|
||||
init: Some(init_expr),
|
||||
els: Some(els),
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
|||
return;
|
||||
}
|
||||
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let Local {
|
||||
pat, init: Some(init), ..
|
||||
} = local
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
|||
}
|
||||
|
||||
for w in block.stmts.windows(2) {
|
||||
if let hir::StmtKind::Local(local) = w[0].kind
|
||||
if let hir::StmtKind::Let(local) = w[0].kind
|
||||
&& let Option::Some(t) = local.init
|
||||
&& let hir::ExprKind::Closure { .. } = t.kind
|
||||
&& let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
|||
// we need both a let-binding stmt and an expr
|
||||
if let Some(retexpr) = block.expr
|
||||
&& let Some(stmt) = block.stmts.iter().last()
|
||||
&& let StmtKind::Local(local) = &stmt.kind
|
||||
&& let StmtKind::Let(local) = &stmt.kind
|
||||
&& local.ty.is_none()
|
||||
&& cx.tcx.hir().attrs(local.hir_id).is_empty()
|
||||
&& let Some(initexpr) = &local.init
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx
|
|||
fn manage_has_expensive_expr_after_last_attr(&mut self) {
|
||||
let has_expensive_stmt = match self.ap.curr_stmt.kind {
|
||||
hir::StmtKind::Expr(expr) if is_inexpensive_expr(expr) => false,
|
||||
hir::StmtKind::Local(local)
|
||||
hir::StmtKind::Let(local)
|
||||
if let Some(expr) = local.init
|
||||
&& let hir::ExprKind::Path(_) = expr.kind =>
|
||||
{
|
||||
|
|
@ -290,7 +290,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
|
|||
};
|
||||
let mut ac = AttrChecker::new(self.cx, self.seen_types, self.type_cache);
|
||||
if ac.has_sig_drop_attr(self.cx.typeck_results().expr_ty(expr)) {
|
||||
if let hir::StmtKind::Local(local) = self.ap.curr_stmt.kind
|
||||
if let hir::StmtKind::Let(local) = self.ap.curr_stmt.kind
|
||||
&& let hir::PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
|
||||
&& !self.ap.apas.contains_key(&hir_id)
|
||||
&& {
|
||||
|
|
@ -326,7 +326,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'o
|
|||
return;
|
||||
};
|
||||
match self.ap.curr_stmt.kind {
|
||||
hir::StmtKind::Local(local) => {
|
||||
hir::StmtKind::Let(local) => {
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind {
|
||||
apa.last_bind_ident = ident;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
|
|||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
// Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
|
||||
// or `Vec::new()`
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Let(local) = stmt.kind
|
||||
&& let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind
|
||||
&& let Some(init) = local.init
|
||||
&& let Some(size_expr) = Self::as_vec_initializer(cx, init)
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
|
|||
}
|
||||
|
||||
for [s1, s2, s3] in block.stmts.array_windows::<3>() {
|
||||
if let StmtKind::Local(tmp) = s1.kind
|
||||
if let StmtKind::Let(tmp) = s1.kind
|
||||
// let t = foo();
|
||||
&& let Some(tmp_init) = tmp.init
|
||||
&& let PatKind::Binding(.., ident, None) = tmp.pat.kind
|
||||
|
|
@ -243,7 +243,7 @@ fn parse<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(ExprOrIdent<'hir>, &'a Expr<
|
|||
if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
|
||||
return Some((ExprOrIdent::Expr(lhs), rhs));
|
||||
}
|
||||
} else if let StmtKind::Local(expr) = stmt.kind {
|
||||
} else if let StmtKind::Let(expr) = stmt.kind {
|
||||
if let Some(rhs) = expr.init {
|
||||
if let PatKind::Binding(_, _, ident_l, _) = expr.pat.kind {
|
||||
return Some((ExprOrIdent::Ident(ident_l), rhs));
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
|
|||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) {
|
||||
let (hir::StmtKind::Local(&hir::Local { init: Some(expr), .. })
|
||||
let (hir::StmtKind::Let(&hir::Local { init: Some(expr), .. })
|
||||
| hir::StmtKind::Expr(expr)
|
||||
| hir::StmtKind::Semi(expr)) = stmt.kind
|
||||
else {
|
||||
|
|
@ -358,7 +358,7 @@ fn block_parents_have_safety_comment(
|
|||
},
|
||||
Node::Stmt(hir::Stmt {
|
||||
kind:
|
||||
hir::StmtKind::Local(hir::Local { span, hir_id, .. })
|
||||
hir::StmtKind::Let(hir::Local { span, hir_id, .. })
|
||||
| hir::StmtKind::Expr(hir::Expr { span, hir_id, .. })
|
||||
| hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }),
|
||||
..
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ impl<'tcx> VecLocation<'tcx> {
|
|||
/// or `self` expression for `Vec::reserve()`.
|
||||
fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option<TargetVec<'tcx>> {
|
||||
match stmt.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Let(local) => {
|
||||
if let Some(init_expr) = local.init
|
||||
&& let PatKind::Binding(_, hir_id, _, None) = local.pat.kind
|
||||
&& let Some(init_kind) = get_vec_init_kind(cx, init_expr)
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
|
|||
/// we need to check them at `check_expr` or `check_block` as they are not stmts
|
||||
/// but we can't check them at `check_expr` because we need the broader context
|
||||
/// because we should do this only for the final expression of the block, and not for
|
||||
/// `StmtKind::Local` which binds values => the io amount is used.
|
||||
/// `StmtKind::Let` which binds values => the io amount is used.
|
||||
///
|
||||
/// To check for unused io amount in stmts, we only consider `StmtKind::Semi`.
|
||||
/// `StmtKind::Local` is not considered because it binds values => the io amount is used.
|
||||
/// `StmtKind::Let` is not considered because it binds values => the io amount is used.
|
||||
/// `StmtKind::Expr` is not considered because requires unit type => the io amount is used.
|
||||
/// `StmtKind::Item` is not considered because it's not an expression.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
|
|||
|
||||
for (idx, stmt) in block.stmts.iter().enumerate() {
|
||||
if !stmt.span.from_expansion()
|
||||
&& let StmtKind::Local(local) = stmt.kind
|
||||
&& let StmtKind::Let(local) = stmt.kind
|
||||
&& let PatKind::Binding(_, binding, ident, _) = local.pat.kind
|
||||
&& let Some(init) = local.init
|
||||
&& !init.span.from_expansion()
|
||||
|
|
@ -197,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
|
|||
},
|
||||
Node::Stmt(stmt) => {
|
||||
match stmt.kind {
|
||||
StmtKind::Local(_) | StmtKind::Item(_) => self.found_peek_call = true,
|
||||
StmtKind::Let(_) | StmtKind::Item(_) => self.found_peek_call = true,
|
||||
StmtKind::Expr(_) | StmtKind::Semi(_) => {},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -724,7 +724,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
match stmt.value.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Let(local) => {
|
||||
bind!(self, local);
|
||||
kind!("Local({local})");
|
||||
self.option(field!(local.init), "init", |init| {
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ pub fn eq_block(l: &Block, r: &Block) -> bool {
|
|||
pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
|
||||
use StmtKind::*;
|
||||
match (&l.kind, &r.kind) {
|
||||
(Local(l), Local(r)) => {
|
||||
(Let(l), Let(r)) => {
|
||||
eq_pat(&l.pat, &r.pat)
|
||||
&& both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
|
||||
&& eq_local_kind(&l.kind, &r.kind)
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> {
|
|||
impl HirEqInterExpr<'_, '_, '_> {
|
||||
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
|
||||
match (&left.kind, &right.kind) {
|
||||
(&StmtKind::Local(l), &StmtKind::Local(r)) => {
|
||||
(&StmtKind::Let(l), &StmtKind::Let(r)) => {
|
||||
// This additional check ensures that the type of the locals are equivalent even if the init
|
||||
// expression or type have some inferred parts.
|
||||
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
|
||||
|
|
@ -1030,7 +1030,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
std::mem::discriminant(&b.kind).hash(&mut self.s);
|
||||
|
||||
match &b.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Let(local) => {
|
||||
self.hash_pat(local.pat);
|
||||
if let Some(init) = local.init {
|
||||
self.hash_expr(init);
|
||||
|
|
|
|||
|
|
@ -2161,7 +2161,7 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
|||
Node::Stmt(Stmt {
|
||||
kind: StmtKind::Expr(_)
|
||||
| StmtKind::Semi(_)
|
||||
| StmtKind::Local(Local {
|
||||
| StmtKind::Let(Local {
|
||||
pat: Pat {
|
||||
kind: PatKind::Wild,
|
||||
..
|
||||
|
|
|
|||
|
|
@ -694,7 +694,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"check-stdout",
|
||||
"check-test-line-numbers-match",
|
||||
"compile-flags",
|
||||
"count",
|
||||
"dont-check-compiler-stderr",
|
||||
"dont-check-compiler-stdout",
|
||||
"dont-check-failure-status",
|
||||
|
|
@ -853,6 +852,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"only-sparc64",
|
||||
"only-stable",
|
||||
"only-thumb",
|
||||
"only-unix",
|
||||
"only-wasm32",
|
||||
"only-wasm32-bare",
|
||||
"only-windows",
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ fn ignore_target() {
|
|||
assert!(check_ignore(&config, "//@ ignore-x86_64-unknown-linux-gnu"));
|
||||
assert!(check_ignore(&config, "//@ ignore-x86_64"));
|
||||
assert!(check_ignore(&config, "//@ ignore-linux"));
|
||||
assert!(check_ignore(&config, "//@ ignore-unix"));
|
||||
assert!(check_ignore(&config, "//@ ignore-gnu"));
|
||||
assert!(check_ignore(&config, "//@ ignore-64bit"));
|
||||
|
||||
|
|
@ -300,6 +301,7 @@ fn only_target() {
|
|||
|
||||
assert!(check_ignore(&config, "//@ only-x86"));
|
||||
assert!(check_ignore(&config, "//@ only-linux"));
|
||||
assert!(check_ignore(&config, "//@ only-unix"));
|
||||
assert!(check_ignore(&config, "//@ only-msvc"));
|
||||
assert!(check_ignore(&config, "//@ only-32bit"));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
//! This module is responsible for managing the absolute addresses that allocations are located at,
|
||||
//! and for casting between pointers and integers based on those addresses.
|
||||
|
||||
mod reuse_pool;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::max;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
|
@ -6,9 +11,10 @@ use rand::Rng;
|
|||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::{HasDataLayout, Size};
|
||||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
|
||||
use crate::*;
|
||||
use reuse_pool::ReusePool;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ProvenanceMode {
|
||||
|
|
@ -23,7 +29,7 @@ pub enum ProvenanceMode {
|
|||
|
||||
pub type GlobalState = RefCell<GlobalStateInner>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalStateInner {
|
||||
/// This is used as a map between the address of each allocation and its `AllocId`. It is always
|
||||
/// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset
|
||||
|
|
@ -35,6 +41,8 @@ pub struct GlobalStateInner {
|
|||
/// they do not have an `AllocExtra`.
|
||||
/// This is the inverse of `int_to_ptr_map`.
|
||||
base_addr: FxHashMap<AllocId, u64>,
|
||||
/// A pool of addresses we can reuse for future allocations.
|
||||
reuse: ReusePool,
|
||||
/// Whether an allocation has been exposed or not. This cannot be put
|
||||
/// into `AllocExtra` for the same reason as `base_addr`.
|
||||
exposed: FxHashSet<AllocId>,
|
||||
|
|
@ -50,6 +58,7 @@ impl VisitProvenance for GlobalStateInner {
|
|||
let GlobalStateInner {
|
||||
int_to_ptr_map: _,
|
||||
base_addr: _,
|
||||
reuse: _,
|
||||
exposed: _,
|
||||
next_base_addr: _,
|
||||
provenance_mode: _,
|
||||
|
|
@ -68,6 +77,7 @@ impl GlobalStateInner {
|
|||
GlobalStateInner {
|
||||
int_to_ptr_map: Vec::default(),
|
||||
base_addr: FxHashMap::default(),
|
||||
reuse: ReusePool::new(),
|
||||
exposed: FxHashSet::default(),
|
||||
next_base_addr: stack_addr,
|
||||
provenance_mode: config.provenance_mode,
|
||||
|
|
@ -96,7 +106,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
// or `None` if the addr is out of bounds
|
||||
fn alloc_id_from_addr(&self, addr: u64) -> Option<AllocId> {
|
||||
let ecx = self.eval_context_ref();
|
||||
let global_state = ecx.machine.intptrcast.borrow();
|
||||
let global_state = ecx.machine.alloc_addresses.borrow();
|
||||
assert!(global_state.provenance_mode != ProvenanceMode::Strict);
|
||||
|
||||
let pos = global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr);
|
||||
|
|
@ -133,12 +143,13 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
|
||||
fn addr_from_alloc_id(&self, alloc_id: AllocId) -> InterpResult<'tcx, u64> {
|
||||
let ecx = self.eval_context_ref();
|
||||
let mut global_state = ecx.machine.intptrcast.borrow_mut();
|
||||
let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
|
||||
let global_state = &mut *global_state;
|
||||
|
||||
Ok(match global_state.base_addr.entry(alloc_id) {
|
||||
Entry::Occupied(entry) => *entry.get(),
|
||||
Entry::Vacant(entry) => {
|
||||
let mut rng = ecx.machine.rng.borrow_mut();
|
||||
let (size, align, kind) = ecx.get_alloc_info(alloc_id);
|
||||
// This is either called immediately after allocation (and then cached), or when
|
||||
// adjusting `tcx` pointers (which never get freed). So assert that we are looking
|
||||
|
|
@ -147,44 +158,63 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
// information was removed.
|
||||
assert!(!matches!(kind, AllocKind::Dead));
|
||||
|
||||
// This allocation does not have a base address yet, pick one.
|
||||
// Leave some space to the previous allocation, to give it some chance to be less aligned.
|
||||
let slack = {
|
||||
let mut rng = ecx.machine.rng.borrow_mut();
|
||||
// This means that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
|
||||
rng.gen_range(0..16)
|
||||
// This allocation does not have a base address yet, pick or reuse one.
|
||||
let base_addr = if let Some(reuse_addr) =
|
||||
global_state.reuse.take_addr(&mut *rng, size, align)
|
||||
{
|
||||
reuse_addr
|
||||
} else {
|
||||
// We have to pick a fresh address.
|
||||
// Leave some space to the previous allocation, to give it some chance to be less aligned.
|
||||
// We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
|
||||
let slack = rng.gen_range(0..16);
|
||||
// From next_base_addr + slack, round up to adjust for alignment.
|
||||
let base_addr = global_state
|
||||
.next_base_addr
|
||||
.checked_add(slack)
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
let base_addr = align_addr(base_addr, align.bytes());
|
||||
|
||||
// Remember next base address. If this allocation is zero-sized, leave a gap
|
||||
// of at least 1 to avoid two allocations having the same base address.
|
||||
// (The logic in `alloc_id_from_addr` assumes unique addresses, and different
|
||||
// function/vtable pointers need to be distinguishable!)
|
||||
global_state.next_base_addr = base_addr
|
||||
.checked_add(max(size.bytes(), 1))
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
// Even if `Size` didn't overflow, we might still have filled up the address space.
|
||||
if global_state.next_base_addr > ecx.target_usize_max() {
|
||||
throw_exhaust!(AddressSpaceFull);
|
||||
}
|
||||
|
||||
base_addr
|
||||
};
|
||||
// From next_base_addr + slack, round up to adjust for alignment.
|
||||
let base_addr = global_state
|
||||
.next_base_addr
|
||||
.checked_add(slack)
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
let base_addr = align_addr(base_addr, align.bytes());
|
||||
entry.insert(base_addr);
|
||||
trace!(
|
||||
"Assigning base address {:#x} to allocation {:?} (size: {}, align: {}, slack: {})",
|
||||
"Assigning base address {:#x} to allocation {:?} (size: {}, align: {})",
|
||||
base_addr,
|
||||
alloc_id,
|
||||
size.bytes(),
|
||||
align.bytes(),
|
||||
slack,
|
||||
);
|
||||
|
||||
// Remember next base address. If this allocation is zero-sized, leave a gap
|
||||
// of at least 1 to avoid two allocations having the same base address.
|
||||
// (The logic in `alloc_id_from_addr` assumes unique addresses, and different
|
||||
// function/vtable pointers need to be distinguishable!)
|
||||
global_state.next_base_addr = base_addr
|
||||
.checked_add(max(size.bytes(), 1))
|
||||
.ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
|
||||
// Even if `Size` didn't overflow, we might still have filled up the address space.
|
||||
if global_state.next_base_addr > ecx.target_usize_max() {
|
||||
throw_exhaust!(AddressSpaceFull);
|
||||
}
|
||||
// Also maintain the opposite mapping in `int_to_ptr_map`.
|
||||
// Given that `next_base_addr` increases in each allocation, pushing the
|
||||
// corresponding tuple keeps `int_to_ptr_map` sorted
|
||||
global_state.int_to_ptr_map.push((base_addr, alloc_id));
|
||||
// Store address in cache.
|
||||
entry.insert(base_addr);
|
||||
|
||||
// Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted.
|
||||
// We have a fast-path for the common case that this address is bigger than all previous ones.
|
||||
let pos = if global_state
|
||||
.int_to_ptr_map
|
||||
.last()
|
||||
.is_some_and(|(last_addr, _)| *last_addr < base_addr)
|
||||
{
|
||||
global_state.int_to_ptr_map.len()
|
||||
} else {
|
||||
global_state
|
||||
.int_to_ptr_map
|
||||
.binary_search_by_key(&base_addr, |(addr, _)| *addr)
|
||||
.unwrap_err()
|
||||
};
|
||||
global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id));
|
||||
|
||||
base_addr
|
||||
}
|
||||
|
|
@ -196,7 +226,7 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir,
|
|||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
fn expose_ptr(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
|
||||
let ecx = self.eval_context_mut();
|
||||
let global_state = ecx.machine.intptrcast.get_mut();
|
||||
let global_state = ecx.machine.alloc_addresses.get_mut();
|
||||
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
|
||||
if global_state.provenance_mode == ProvenanceMode::Strict {
|
||||
return Ok(());
|
||||
|
|
@ -207,7 +237,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
return Ok(());
|
||||
}
|
||||
trace!("Exposing allocation id {alloc_id:?}");
|
||||
let global_state = ecx.machine.intptrcast.get_mut();
|
||||
let global_state = ecx.machine.alloc_addresses.get_mut();
|
||||
global_state.exposed.insert(alloc_id);
|
||||
if ecx.machine.borrow_tracker.is_some() {
|
||||
ecx.expose_tag(alloc_id, tag)?;
|
||||
|
|
@ -219,7 +249,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
trace!("Casting {:#x} to a pointer", addr);
|
||||
|
||||
let ecx = self.eval_context_ref();
|
||||
let global_state = ecx.machine.intptrcast.borrow();
|
||||
let global_state = ecx.machine.alloc_addresses.borrow();
|
||||
|
||||
// Potentially emit a warning.
|
||||
match global_state.provenance_mode {
|
||||
|
|
@ -299,7 +329,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
}
|
||||
|
||||
impl GlobalStateInner {
|
||||
pub fn free_alloc_id(&mut self, dead_id: AllocId) {
|
||||
pub fn free_alloc_id(
|
||||
&mut self,
|
||||
rng: &mut impl Rng,
|
||||
dead_id: AllocId,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) {
|
||||
// We can *not* remove this from `base_addr`, since the interpreter design requires that we
|
||||
// be able to retrieve an AllocId + offset for any memory access *before* we check if the
|
||||
// access is valid. Specifically, `ptr_get_alloc` is called on each attempt at a memory
|
||||
|
|
@ -319,6 +355,8 @@ impl GlobalStateInner {
|
|||
// We can also remove it from `exposed`, since this allocation can anyway not be returned by
|
||||
// `alloc_id_from_addr` any more.
|
||||
self.exposed.remove(&dead_id);
|
||||
// Also remember this address for future reuse.
|
||||
self.reuse.add_addr(rng, addr, size, align)
|
||||
}
|
||||
}
|
||||
|
||||
87
src/tools/miri/src/alloc_addresses/reuse_pool.rs
Normal file
87
src/tools/miri/src/alloc_addresses/reuse_pool.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
//! Manages a pool of addresses that can be reused.
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
use rustc_target::abi::{Align, Size};
|
||||
|
||||
const MAX_POOL_SIZE: usize = 64;
|
||||
|
||||
// Just use fair coins, until we have evidence that other numbers are better.
|
||||
const ADDR_REMEMBER_CHANCE: f64 = 0.5;
|
||||
const ADDR_TAKE_CHANCE: f64 = 0.5;
|
||||
|
||||
/// The pool strikes a balance between exploring more possible executions and making it more likely
|
||||
/// to find bugs. The hypothesis is that bugs are more likely to occur when reuse happens for
|
||||
/// allocations with the same layout, since that can trigger e.g. ABA issues in a concurrent data
|
||||
/// structure. Therefore we only reuse allocations when size and alignment match exactly.
|
||||
#[derive(Debug)]
|
||||
pub struct ReusePool {
|
||||
/// The i-th element in `pool` stores allocations of alignment `2^i`. We store these reusable
|
||||
/// allocations as address-size pairs, the list must be sorted by the size.
|
||||
///
|
||||
/// Each of these maps has at most MAX_POOL_SIZE elements, and since alignment is limited to
|
||||
/// less than 64 different possible value, that bounds the overall size of the pool.
|
||||
pool: Vec<Vec<(u64, Size)>>,
|
||||
}
|
||||
|
||||
impl ReusePool {
|
||||
pub fn new() -> Self {
|
||||
ReusePool { pool: vec![] }
|
||||
}
|
||||
|
||||
fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size)> {
|
||||
let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap();
|
||||
if self.pool.len() <= pool_idx {
|
||||
self.pool.resize(pool_idx + 1, Vec::new());
|
||||
}
|
||||
&mut self.pool[pool_idx]
|
||||
}
|
||||
|
||||
pub fn add_addr(&mut self, rng: &mut impl Rng, addr: u64, size: Size, align: Align) {
|
||||
// Let's see if we even want to remember this address.
|
||||
if !rng.gen_bool(ADDR_REMEMBER_CHANCE) {
|
||||
return;
|
||||
}
|
||||
// Determine the pool to add this to, and where in the pool to put it.
|
||||
let subpool = self.subpool(align);
|
||||
let pos = subpool.partition_point(|(_addr, other_size)| *other_size < size);
|
||||
// Make sure the pool does not grow too big.
|
||||
if subpool.len() >= MAX_POOL_SIZE {
|
||||
// Pool full. Replace existing element, or last one if this would be even bigger.
|
||||
let clamped_pos = pos.min(subpool.len() - 1);
|
||||
subpool[clamped_pos] = (addr, size);
|
||||
return;
|
||||
}
|
||||
// Add address to pool, at the right position.
|
||||
subpool.insert(pos, (addr, size));
|
||||
}
|
||||
|
||||
pub fn take_addr(&mut self, rng: &mut impl Rng, size: Size, align: Align) -> Option<u64> {
|
||||
// Determine whether we'll even attempt a reuse.
|
||||
if !rng.gen_bool(ADDR_TAKE_CHANCE) {
|
||||
return None;
|
||||
}
|
||||
// Determine the pool to take this from.
|
||||
let subpool = self.subpool(align);
|
||||
// Let's see if we can find something of the right size. We want to find the full range of
|
||||
// such items, beginning with the first, so we can't use `binary_search_by_key`.
|
||||
let begin = subpool.partition_point(|(_addr, other_size)| *other_size < size);
|
||||
let mut end = begin;
|
||||
while let Some((_addr, other_size)) = subpool.get(end) {
|
||||
if *other_size != size {
|
||||
break;
|
||||
}
|
||||
end += 1;
|
||||
}
|
||||
if end == begin {
|
||||
// Could not find any item of the right size.
|
||||
return None;
|
||||
}
|
||||
// Pick a random element with the desired size.
|
||||
let idx = rng.gen_range(begin..end);
|
||||
// Remove it from the pool and return.
|
||||
let (chosen_addr, chosen_size) = subpool.remove(idx);
|
||||
debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0);
|
||||
Some(chosen_addr)
|
||||
}
|
||||
}
|
||||
|
|
@ -485,14 +485,14 @@ impl AllocState {
|
|||
&mut self,
|
||||
alloc_id: AllocId,
|
||||
prov_extra: ProvenanceExtra,
|
||||
range: AllocRange,
|
||||
size: Size,
|
||||
machine: &MiriMachine<'_, 'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
match self {
|
||||
AllocState::StackedBorrows(sb) =>
|
||||
sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, range, machine),
|
||||
sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
|
||||
AllocState::TreeBorrows(tb) =>
|
||||
tb.get_mut().before_memory_deallocation(alloc_id, prov_extra, range, machine),
|
||||
tb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -574,13 +574,13 @@ impl Stacks {
|
|||
&mut self,
|
||||
alloc_id: AllocId,
|
||||
tag: ProvenanceExtra,
|
||||
range: AllocRange,
|
||||
size: Size,
|
||||
machine: &MiriMachine<'_, 'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
|
||||
trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, size.bytes());
|
||||
let dcx = DiagnosticCxBuilder::dealloc(machine, tag);
|
||||
let state = machine.borrow_tracker.as_ref().unwrap().borrow();
|
||||
self.for_each(range, dcx, |stack, dcx, exposed_tags| {
|
||||
self.for_each(alloc_range(Size::ZERO, size), dcx, |stack, dcx, exposed_tags| {
|
||||
stack.dealloc(tag, &state, dcx, exposed_tags)
|
||||
})?;
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ impl<'tcx> Tree {
|
|||
&mut self,
|
||||
alloc_id: AllocId,
|
||||
prov: ProvenanceExtra,
|
||||
range: AllocRange,
|
||||
size: Size,
|
||||
machine: &MiriMachine<'_, 'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// TODO: for now we bail out on wildcard pointers. Eventually we should
|
||||
|
|
@ -91,7 +91,7 @@ impl<'tcx> Tree {
|
|||
};
|
||||
let global = machine.borrow_tracker.as_ref().unwrap();
|
||||
let span = machine.current_span();
|
||||
self.dealloc(tag, range, global, alloc_id, span)
|
||||
self.dealloc(tag, alloc_range(Size::ZERO, size), global, alloc_id, span)
|
||||
}
|
||||
|
||||
pub fn expose_tag(&mut self, _tag: BorTag) {
|
||||
|
|
|
|||
|
|
@ -1071,10 +1071,10 @@ impl VClockAlloc {
|
|||
pub fn deallocate<'tcx>(
|
||||
&mut self,
|
||||
alloc_id: AllocId,
|
||||
range: AllocRange,
|
||||
size: Size,
|
||||
machine: &mut MiriMachine<'_, '_>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.unique_access(alloc_id, range, NaWriteType::Deallocate, machine)
|
||||
self.unique_access(alloc_id, alloc_range(Size::ZERO, size), NaWriteType::Deallocate, machine)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ use std::num::NonZero;
|
|||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use rand::RngCore;
|
||||
|
||||
use rustc_apfloat::ieee::{Double, Single};
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_hir::def::{DefKind, Namespace};
|
||||
|
|
@ -20,8 +22,6 @@ use rustc_span::{def_id::CrateNum, sym, Span, Symbol};
|
|||
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::*;
|
||||
|
||||
/// Indicates which kind of access is being performed.
|
||||
|
|
|
|||
|
|
@ -72,13 +72,13 @@ extern crate rustc_target;
|
|||
#[allow(unused_extern_crates)]
|
||||
extern crate rustc_driver;
|
||||
|
||||
mod alloc_addresses;
|
||||
mod borrow_tracker;
|
||||
mod clock;
|
||||
mod concurrency;
|
||||
mod diagnostics;
|
||||
mod eval;
|
||||
mod helpers;
|
||||
mod intptrcast;
|
||||
mod machine;
|
||||
mod mono_hash_map;
|
||||
mod operator;
|
||||
|
|
@ -101,6 +101,7 @@ pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
|
|||
pub use crate::shims::time::EvalContextExt as _;
|
||||
pub use crate::shims::tls::TlsData;
|
||||
|
||||
pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode};
|
||||
pub use crate::borrow_tracker::stacked_borrows::{
|
||||
EvalContextExt as _, Item, Permission, Stack, Stacks,
|
||||
};
|
||||
|
|
@ -122,7 +123,6 @@ pub use crate::eval::{
|
|||
create_ecx, eval_entry, AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith,
|
||||
};
|
||||
pub use crate::helpers::{AccessKind, EvalContextExt as _};
|
||||
pub use crate::intptrcast::{EvalContextExt as _, ProvenanceMode};
|
||||
pub use crate::machine::{
|
||||
AllocExtra, FrameExtra, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,
|
||||
PrimitiveLayouts, Provenance, ProvenanceExtra,
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ pub struct MiriMachine<'mir, 'tcx> {
|
|||
pub data_race: Option<data_race::GlobalState>,
|
||||
|
||||
/// Ptr-int-cast module global data.
|
||||
pub intptrcast: intptrcast::GlobalState,
|
||||
pub alloc_addresses: alloc_addresses::GlobalState,
|
||||
|
||||
/// Environment variables set by `setenv`.
|
||||
/// Miri does not expose env vars from the host to the emulated program.
|
||||
|
|
@ -630,7 +630,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
|||
tcx,
|
||||
borrow_tracker,
|
||||
data_race,
|
||||
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config, stack_addr)),
|
||||
alloc_addresses: RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr)),
|
||||
// `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
|
||||
env_vars: EnvVars::default(),
|
||||
main_fn_ret_place: None,
|
||||
|
|
@ -777,7 +777,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
|
|||
dir_handler,
|
||||
borrow_tracker,
|
||||
data_race,
|
||||
intptrcast,
|
||||
alloc_addresses,
|
||||
file_handler,
|
||||
tcx: _,
|
||||
isolated_op: _,
|
||||
|
|
@ -821,7 +821,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
|
|||
file_handler.visit_provenance(visit);
|
||||
data_race.visit_provenance(visit);
|
||||
borrow_tracker.visit_provenance(visit);
|
||||
intptrcast.visit_provenance(visit);
|
||||
alloc_addresses.visit_provenance(visit);
|
||||
main_fn_ret_place.visit_provenance(visit);
|
||||
argc.visit_provenance(visit);
|
||||
argv.visit_provenance(visit);
|
||||
|
|
@ -1282,22 +1282,28 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
|
|||
machine: &mut Self,
|
||||
alloc_extra: &mut AllocExtra<'tcx>,
|
||||
(alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
|
||||
range: AllocRange,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx> {
|
||||
if machine.tracked_alloc_ids.contains(&alloc_id) {
|
||||
machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
|
||||
}
|
||||
if let Some(data_race) = &mut alloc_extra.data_race {
|
||||
data_race.deallocate(alloc_id, range, machine)?;
|
||||
data_race.deallocate(alloc_id, size, machine)?;
|
||||
}
|
||||
if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
|
||||
borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, range, machine)?;
|
||||
borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
|
||||
}
|
||||
if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
|
||||
{
|
||||
*deallocated_at = Some(machine.current_span());
|
||||
}
|
||||
machine.intptrcast.get_mut().free_alloc_id(alloc_id);
|
||||
machine.alloc_addresses.get_mut().free_alloc_id(
|
||||
machine.rng.get_mut(),
|
||||
alloc_id,
|
||||
size,
|
||||
align,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
|||
let allocs = LiveAllocs { ecx: this, collected: allocs };
|
||||
this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
|
||||
this.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
|
||||
this.machine.intptrcast.borrow_mut().remove_unreachable_allocs(&allocs);
|
||||
this.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
|
||||
if let Some(borrow_tracker) = &this.machine.borrow_tracker {
|
||||
borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
|
||||
}
|
||||
|
|
|
|||
16
src/tools/miri/tests/pass/address-reuse.rs
Normal file
16
src/tools/miri/tests/pass/address-reuse.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//! Check that we do sometimes reuse addresses.
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn main() {
|
||||
let count = 100;
|
||||
let mut addrs = HashSet::<usize>::new();
|
||||
for _ in 0..count {
|
||||
// We make a `Box` with a layout that's hopefully not used by tons of things inside the
|
||||
// allocator itself, so that we are more likely to get reuse. (With `i32` or `usize`, on
|
||||
// Windows the reuse chances are very low.)
|
||||
let b = Box::new([42usize; 4]);
|
||||
addrs.insert(&*b as *const [usize; 4] as usize);
|
||||
}
|
||||
// dbg!(addrs.len());
|
||||
assert!(addrs.len() > 1 && addrs.len() < count);
|
||||
}
|
||||
|
|
@ -67,8 +67,8 @@ fn ptr_eq_dangling() {
|
|||
drop(b);
|
||||
let b = Box::new(0);
|
||||
let y = &*b as *const i32; // different allocation
|
||||
// They *could* be equal if memory was reused, but probably are not.
|
||||
assert!(x != y);
|
||||
// They *could* be equal if memory is reused...
|
||||
assert!(x != y || x == y);
|
||||
}
|
||||
|
||||
fn ptr_eq_out_of_bounds() {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub(crate) fn get_attrs_from_stmt(stmt: &ast::Stmt) -> &[ast::Attribute] {
|
|||
|
||||
pub(crate) fn get_span_without_attrs(stmt: &ast::Stmt) -> Span {
|
||||
match stmt.kind {
|
||||
ast::StmtKind::Local(ref local) => local.span,
|
||||
ast::StmtKind::Let(ref local) => local.span,
|
||||
ast::StmtKind::Item(ref item) => item.span,
|
||||
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => expr.span,
|
||||
ast::StmtKind::MacCall(ref mac_stmt) => mac_stmt.mac.span(),
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ implement_spanned!(ast::Local);
|
|||
impl Spanned for ast::Stmt {
|
||||
fn span(&self) -> Span {
|
||||
match self.kind {
|
||||
ast::StmtKind::Local(ref local) => mk_sp(local.span().lo(), self.span.hi()),
|
||||
ast::StmtKind::Let(ref local) => mk_sp(local.span().lo(), self.span.hi()),
|
||||
ast::StmtKind::Item(ref item) => mk_sp(item.span().lo(), self.span.hi()),
|
||||
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
|
||||
mk_sp(expr.span().lo(), self.span.hi())
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ fn format_stmt(
|
|||
skip_out_of_file_lines_range!(context, stmt.span());
|
||||
|
||||
let result = match stmt.kind {
|
||||
ast::StmtKind::Local(ref local) => local.rewrite(context, shape),
|
||||
ast::StmtKind::Let(ref local) => local.rewrite(context, shape),
|
||||
ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => {
|
||||
let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) {
|
||||
";"
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
|
|||
self.visit_item(item);
|
||||
self.last_pos = stmt.span().hi();
|
||||
}
|
||||
ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
|
||||
ast::StmtKind::Let(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
|
||||
let attrs = get_attrs_from_stmt(stmt.as_ast_node());
|
||||
if contains_skip(attrs) {
|
||||
self.push_skipped_with_span(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue