|
| 1 | +use crate::ty::needs_ordered_drop; |
1 | 2 | use crate::{get_enclosing_block, path_to_local_id};
|
2 | 3 | use core::ops::ControlFlow;
|
3 | 4 | use rustc_hir as hir;
|
4 |
| -use rustc_hir::def::{DefKind, Res}; |
| 5 | +use rustc_hir::def::{CtorKind, DefKind, Res}; |
5 | 6 | use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
|
6 | 7 | use rustc_hir::{
|
7 |
| - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource, |
8 |
| - Unsafety, |
| 8 | + Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, QPath, Stmt, UnOp, |
| 9 | + UnsafeSource, Unsafety, |
9 | 10 | };
|
10 | 11 | use rustc_lint::LateContext;
|
11 | 12 | use rustc_middle::hir::map::Map;
|
12 | 13 | use rustc_middle::hir::nested_filter;
|
13 |
| -use rustc_middle::ty; |
| 14 | +use rustc_middle::ty::adjustment::Adjust; |
| 15 | +use rustc_middle::ty::{self, Ty, TypeckResults}; |
14 | 16 |
|
15 | 17 | /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
|
16 | 18 | /// bodies (i.e. closures) are visited.
|
@@ -494,3 +496,124 @@ pub fn for_each_local_use_after_expr<'tcx, B>(
|
494 | 496 | ControlFlow::Continue(())
|
495 | 497 | }
|
496 | 498 | }
|
| 499 | + |
| 500 | +// Calls the given function for every unconsumed temporary created by the expression. Note the |
| 501 | +// function is only guaranteed to be called for types which need to be dropped, but it may be called |
| 502 | +// for other types. |
| 503 | +pub fn for_each_unconsumed_temporary<'tcx, B>( |
| 504 | + cx: &LateContext<'tcx>, |
| 505 | + e: &'tcx Expr<'tcx>, |
| 506 | + mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>, |
| 507 | +) -> ControlFlow<B> { |
| 508 | + // Todo: Handle partially consumed values. |
| 509 | + fn helper<'tcx, B>( |
| 510 | + typeck: &'tcx TypeckResults<'tcx>, |
| 511 | + consume: bool, |
| 512 | + e: &'tcx Expr<'tcx>, |
| 513 | + f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>, |
| 514 | + ) -> ControlFlow<B> { |
| 515 | + if !consume |
| 516 | + || matches!( |
| 517 | + typeck.expr_adjustments(e), |
| 518 | + [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_)) |
| 519 | + ) |
| 520 | + { |
| 521 | + match e.kind { |
| 522 | + ExprKind::Path(QPath::Resolved(None, p)) |
| 523 | + if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) => |
| 524 | + { |
| 525 | + f(typeck.expr_ty(e))?; |
| 526 | + }, |
| 527 | + ExprKind::Path(_) |
| 528 | + | ExprKind::Unary(UnOp::Deref, _) |
| 529 | + | ExprKind::Index(..) |
| 530 | + | ExprKind::Field(..) |
| 531 | + | ExprKind::AddrOf(..) => (), |
| 532 | + _ => f(typeck.expr_ty(e))?, |
| 533 | + } |
| 534 | + } |
| 535 | + match e.kind { |
| 536 | + ExprKind::AddrOf(_, _, e) |
| 537 | + | ExprKind::Field(e, _) |
| 538 | + | ExprKind::Unary(UnOp::Deref, e) |
| 539 | + | ExprKind::Match(e, ..) |
| 540 | + | ExprKind::Let(&Let { init: e, .. }) => { |
| 541 | + helper(typeck, false, e, f)?; |
| 542 | + }, |
| 543 | + ExprKind::Block(&Block { expr: Some(e), .. }, _) |
| 544 | + | ExprKind::Box(e) |
| 545 | + | ExprKind::Cast(e, _) |
| 546 | + | ExprKind::Unary(_, e) => { |
| 547 | + helper(typeck, true, e, f)?; |
| 548 | + }, |
| 549 | + ExprKind::Call(callee, args) => { |
| 550 | + helper(typeck, true, callee, f)?; |
| 551 | + for arg in args { |
| 552 | + helper(typeck, true, arg, f)?; |
| 553 | + } |
| 554 | + }, |
| 555 | + ExprKind::MethodCall(_, args, _) | ExprKind::Tup(args) | ExprKind::Array(args) => { |
| 556 | + for arg in args { |
| 557 | + helper(typeck, true, arg, f)?; |
| 558 | + } |
| 559 | + }, |
| 560 | + ExprKind::Index(borrowed, consumed) |
| 561 | + | ExprKind::Assign(borrowed, consumed, _) |
| 562 | + | ExprKind::AssignOp(_, borrowed, consumed) => { |
| 563 | + helper(typeck, false, borrowed, f)?; |
| 564 | + helper(typeck, true, consumed, f)?; |
| 565 | + }, |
| 566 | + ExprKind::Binary(_, lhs, rhs) => { |
| 567 | + helper(typeck, true, lhs, f)?; |
| 568 | + helper(typeck, true, rhs, f)?; |
| 569 | + }, |
| 570 | + ExprKind::Struct(_, fields, default) => { |
| 571 | + for field in fields { |
| 572 | + helper(typeck, true, field.expr, f)?; |
| 573 | + } |
| 574 | + if let Some(default) = default { |
| 575 | + helper(typeck, false, default, f)?; |
| 576 | + } |
| 577 | + }, |
| 578 | + ExprKind::If(cond, then, else_expr) => { |
| 579 | + helper(typeck, true, cond, f)?; |
| 580 | + helper(typeck, true, then, f)?; |
| 581 | + if let Some(else_expr) = else_expr { |
| 582 | + helper(typeck, true, else_expr, f)?; |
| 583 | + } |
| 584 | + }, |
| 585 | + ExprKind::Type(e, _) => { |
| 586 | + helper(typeck, consume, e, f)?; |
| 587 | + }, |
| 588 | + |
| 589 | + // Either drops temporaries, jumps out of the current expression, or has no sub expression. |
| 590 | + ExprKind::DropTemps(_) |
| 591 | + | ExprKind::Ret(_) |
| 592 | + | ExprKind::Break(..) |
| 593 | + | ExprKind::Yield(..) |
| 594 | + | ExprKind::Block(..) |
| 595 | + | ExprKind::Loop(..) |
| 596 | + | ExprKind::Repeat(..) |
| 597 | + | ExprKind::Lit(_) |
| 598 | + | ExprKind::ConstBlock(_) |
| 599 | + | ExprKind::Closure { .. } |
| 600 | + | ExprKind::Path(_) |
| 601 | + | ExprKind::Continue(_) |
| 602 | + | ExprKind::InlineAsm(_) |
| 603 | + | ExprKind::Err => (), |
| 604 | + } |
| 605 | + ControlFlow::Continue(()) |
| 606 | + } |
| 607 | + helper(cx.typeck_results(), true, e, &mut f) |
| 608 | +} |
| 609 | + |
| 610 | +pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { |
| 611 | + for_each_unconsumed_temporary(cx, e, |ty| { |
| 612 | + if needs_ordered_drop(cx, ty) { |
| 613 | + ControlFlow::Break(()) |
| 614 | + } else { |
| 615 | + ControlFlow::Continue(()) |
| 616 | + } |
| 617 | + }) |
| 618 | + .is_break() |
| 619 | +} |
0 commit comments