};
}
+declare_lint! {
+ /// The `large_assigments` lint detects when objects of large
+ /// types are being moved around.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,ignore (can crash on some platforms)
+ /// let x = [0; 50000];
+ /// let y = x;
+ /// ```
+ ///
+ /// produces:
+ ///
+ /// ```text
+ /// warning: moving a large value
+ /// --> $DIR/move-large.rs:1:3
+ /// let y = x;
+ /// - Copied large value here
+ /// ```
+ ///
+ /// ### Explanation
+ ///
+ /// When using a large type in a plain assignment or in a function
+ /// argument, idiomatic code can be inefficient.
+ /// Ideally appropriate optimizations would resolve this, but such
+ /// optimizations are only done in a best-effort manner.
+ /// This lint will trigger on all sites of large moves and thus allow the
+ /// user to resolve them in code.
+ pub LARGE_ASSIGNMENTS,
+ Allow,
+ "detects large moves or copies",
+}
+
declare_lint_pass! {
/// Does nothing as a lint pass, but registers some `Lint`s
/// that are used by other parts of the compiler.
LEGACY_DERIVE_HELPERS,
PROC_MACRO_BACK_COMPAT,
OR_PATTERNS_BACK_COMPAT,
+ LARGE_ASSIGNMENTS,
]
}
use crate::ty::subst::{Subst, SubstsRef};
use crate::ty::{self, List, Ty, TyCtxt};
use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
-use rustc_hir as hir;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, GeneratorKind};
+use rustc_hir::{self as hir, HirId};
use rustc_target::abi::{Size, VariantIdx};
use polonius_engine::Atom;
}
}
+impl SourceScope {
+ /// Finds the original HirId this MIR item came from.
+ /// This is necessary after MIR optimizations, as otherwise we get a HirId
+ /// from the function that was inlined instead of the function call site.
+ pub fn lint_root(self, source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>) -> Option<HirId> {
+ let mut data = &source_scopes[self];
+ // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
+ // does not work as I thought it would. Needs more investigation and documentation.
+ while data.inlined.is_some() {
+ trace!(?data);
+ data = &source_scopes[data.parent_scope.unwrap()];
+ }
+ trace!(?data);
+ match &data.local_data {
+ ClearCrossCrate::Set(data) => Some(data.lint_root),
+ ClearCrossCrate::Clear => None,
+ }
+ }
+}
+
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)]
pub struct SourceScopeData<'tcx> {
pub span: Span,
use rustc_middle::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext};
use rustc_session::config::EntryFnType;
+use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
+use rustc_target::abi::Size;
use smallvec::SmallVec;
use std::iter;
use std::ops::Range;
self.super_terminator(terminator, location);
}
+ fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
+ self.super_operand(operand, location);
+ let ty = operand.ty(self.body, self.tcx);
+ let ty = self.monomorphize(ty);
+ let layout = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty));
+ if let Ok(layout) = layout {
+ if layout.size > Size::from_bytes(1000) {
+ debug!(?layout);
+ let source_info = self.body.source_info(location);
+ debug!(?source_info);
+ let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
+ debug!(?lint_root);
+ let lint_root = match lint_root {
+ Some(lint_root) => lint_root,
+ // This happens when the issue is in a function from a foreign crate that
+ // we monomorphized in the current crate. We can't get a `HirId` for things
+ // in other crates.
+ // FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
+ // but correct span? This would make the lint at least accept crate-level lint attributes.
+ None => return,
+ };
+ self.tcx.struct_span_lint_hir(
+ LARGE_ASSIGNMENTS,
+ lint_root,
+ source_info.span,
+ |lint| {
+ let mut err = lint.build(&format!("moving {} bytes", layout.size.bytes()));
+ err.span_label(source_info.span, "value moved from here");
+ err.emit()
+ },
+ );
+ }
+ }
+ }
+
fn visit_local(
&mut self,
_place_local: &Local,
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
use rustc_middle::mir::{
- AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
+ AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl,
LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
};
}
fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
- let mut data = &self.source_scopes[source_info.scope];
- // FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
- // does not work as I thought it would. Needs more investigation and documentation.
- while data.inlined.is_some() {
- trace!(?data);
- data = &self.source_scopes[data.parent_scope.unwrap()];
- }
- trace!(?data);
- match &data.local_data {
- ClearCrossCrate::Set(data) => Some(data.lint_root),
- ClearCrossCrate::Clear => None,
- }
+ source_info.scope.lint_root(&self.source_scopes)
}
fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
--- /dev/null
+#![deny(large_assignments)]
+// build-fail
+
+// edition:2018
+
+fn main() {
+ let x = async { //~ ERROR large_assignments
+ let y = [0; 9999];
+ dbg!(y);
+ thing(&y).await;
+ dbg!(y);
+ };
+ let z = (x, 42); //~ ERROR large_assignments
+ //~^ ERROR large_assignments
+ let a = z.0; //~ ERROR large_assignments
+ let b = z.1;
+}
+
+async fn thing(y: &[u8]) {
+ dbg!(y);
+}
\ No newline at end of file
--- /dev/null
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:7:13
+ |
+LL | let x = async {
+ | _____________^
+LL | | let y = [0; 9999];
+LL | | dbg!(y);
+LL | | thing(&y).await;
+LL | | dbg!(y);
+LL | | };
+ | |_____^ value moved from here
+ |
+note: the lint level is defined here
+ --> $DIR/large_moves.rs:1:9
+ |
+LL | #![deny(large_assignments)]
+ | ^^^^^^^^^^^^^^^^^
+
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:13:14
+ |
+LL | let z = (x, 42);
+ | ^ value moved from here
+
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:13:13
+ |
+LL | let z = (x, 42);
+ | ^^^^^^^ value moved from here
+
+error: moving 10024 bytes
+ --> $DIR/large_moves.rs:15:13
+ |
+LL | let a = z.0;
+ | ^^^ value moved from here
+
+error: aborting due to 4 previous errors
+