Importing rustc-1.53.0
Bug: 194400612
Change-Id: Id2f38eeabc8325fff960e46b89b1cc7216f5227c
diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs
index a18c1f7..ffeaaf6 100644
--- a/compiler/rustc_mir/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs
@@ -540,12 +540,17 @@
pub struct UnsizingCast;
impl NonConstOp for UnsizingCast {
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
- mcf_status_in_item(ccx)
+ if ccx.const_kind() != hir::ConstContext::ConstFn {
+ Status::Allowed
+ } else {
+ Status::Unstable(sym::const_fn_unsize)
+ }
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
- mcf_build_error(
- ccx,
+ feature_err(
+ &ccx.tcx.sess.parse_sess,
+ sym::const_fn_unsize,
span,
"unsizing casts to types besides slices are not allowed in const fn",
)
@@ -642,12 +647,17 @@
}
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
- mcf_status_in_item(ccx)
+ if ccx.const_kind() != hir::ConstContext::ConstFn {
+ Status::Allowed
+ } else {
+ Status::Unstable(sym::const_fn_trait_bound)
+ }
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
- mcf_build_error(
- ccx,
+ feature_err(
+ &ccx.tcx.sess.parse_sess,
+ sym::const_fn_trait_bound,
span,
"trait bounds other than `Sized` on const fn parameters are unstable",
)
@@ -672,21 +682,3 @@
}
}
}
-
-fn mcf_status_in_item(ccx: &ConstCx<'_, '_>) -> Status {
- if ccx.const_kind() != hir::ConstContext::ConstFn {
- Status::Allowed
- } else {
- Status::Unstable(sym::const_fn)
- }
-}
-
-fn mcf_build_error(ccx: &ConstCx<'_, 'tcx>, span: Span, msg: &str) -> DiagnosticBuilder<'tcx> {
- let mut err = struct_span_err!(ccx.tcx.sess, span, E0723, "{}", msg);
- err.note(
- "see issue #57563 <https://github.com/rust-lang/rust/issues/57563> \
- for more information",
- );
- err.help("add `#![feature(const_fn)]` to the crate attributes to enable");
- err
-}
diff --git a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs
index 1a2d932..057092b 100644
--- a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs
@@ -79,7 +79,9 @@
mir::TerminatorKind::Drop { place: dropped_place, .. } => {
let dropped_ty = dropped_place.ty(self.body, self.tcx).ty;
if !NeedsDrop::in_any_value_of_ty(self.ccx, dropped_ty) {
- return;
+ bug!(
+ "Drop elaboration left behind a Drop for a type that does not need dropping"
+ );
}
if dropped_place.is_indirect() {
@@ -87,6 +89,10 @@
return;
}
+ // Drop elaboration is not precise enough to accept code like
+ // `src/test/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is
+ // initialized with `None` and never changed, it still emits drop glue.
+ // Hence we additionally check the qualifs here to allow more code to pass.
if self.qualifs.needs_drop(self.ccx, dropped_place.local, location) {
// Use the span where the dropped local was declared for the error.
let span = self.body.local_decls[dropped_place.local].source_info.span;
diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
index 748f65c..ac8c748 100644
--- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
@@ -29,11 +29,11 @@
/// Normally, we would determine what qualifications apply to each type and error when an illegal
/// operation is performed on such a type. However, this was found to be too imprecise, especially
/// in the presence of `enum`s. If only a single variant of an enum has a certain qualification, we
-/// needn't reject code unless it actually constructs and operates on the qualifed variant.
+/// needn't reject code unless it actually constructs and operates on the qualified variant.
///
/// To accomplish this, const-checking and promotion use a value-based analysis (as opposed to a
/// type-based one). Qualifications propagate structurally across variables: If a local (or a
-/// projection of a local) is assigned a qualifed value, that local itself becomes qualifed.
+/// projection of a local) is assigned a qualified value, that local itself becomes qualified.
pub trait Qualif {
/// The name of the file used to debug the dataflow analysis that computes this qualif.
const ANALYSIS_NAME: &'static str;
@@ -247,7 +247,7 @@
// Check the qualifs of the value of `const` items.
if let Some(ct) = constant.literal.const_for_ty() {
- if let ty::ConstKind::Unevaluated(def, _, promoted) = ct.val {
+ if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) = ct.val {
assert!(promoted.is_none());
// Don't peek inside trait associated constants.
if cx.tcx.trait_of_item(def.did).is_none() {
diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs
index 1ad7b8f..63fc66f 100644
--- a/compiler/rustc_mir/src/transform/check_consts/validation.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs
@@ -1,6 +1,6 @@
//! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
+use rustc_errors::{Applicability, Diagnostic, ErrorReported};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, HirId, LangItem};
use rustc_index::bit_set::BitSet;
@@ -234,13 +234,11 @@
if self.is_const_stable_const_fn() {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
if crate::const_eval::is_parent_const_impl_raw(tcx, hir_id) {
- struct_span_err!(
- self.ccx.tcx.sess,
- self.span,
- E0723,
- "trait methods cannot be stable const fn"
- )
- .emit();
+ self.ccx
+ .tcx
+ .sess
+ .struct_span_err(self.span, "trait methods cannot be stable const fn")
+ .emit();
}
}
@@ -428,7 +426,7 @@
ty::PredicateKind::Subtype(_) => {
bug!("subtype predicate on function: {:#?}", predicate)
}
- ty::PredicateKind::Trait(pred, constness) => {
+ ty::PredicateKind::Trait(pred, _constness) => {
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
continue;
}
@@ -442,16 +440,7 @@
// arguments when determining importance.
let kind = LocalKind::Arg;
- if constness == hir::Constness::Const {
- self.check_op_spanned(ops::ty::TraitBound(kind), span);
- } else if !tcx.features().const_fn
- || self.ccx.is_const_stable_const_fn()
- {
- // HACK: We shouldn't need the conditional above, but trait
- // bounds on containing impl blocks are wrongly being marked as
- // "not-const".
- self.check_op_spanned(ops::ty::TraitBound(kind), span);
- }
+ self.check_op_spanned(ops::ty::TraitBound(kind), span);
}
// other kinds of bounds are either tautologies
// or cause errors in other passes
@@ -850,9 +839,12 @@
let obligation = Obligation::new(
ObligationCause::dummy(),
param_env,
- Binder::bind(TraitPredicate {
- trait_ref: TraitRef::from_method(tcx, trait_id, substs),
- }),
+ Binder::bind(
+ TraitPredicate {
+ trait_ref: TraitRef::from_method(tcx, trait_id, substs),
+ },
+ tcx,
+ ),
);
let implsrc = tcx.infer_ctxt().enter(|infcx| {
diff --git a/compiler/rustc_mir/src/transform/check_packed_ref.rs b/compiler/rustc_mir/src/transform/check_packed_ref.rs
index ee88daa..13b7221 100644
--- a/compiler/rustc_mir/src/transform/check_packed_ref.rs
+++ b/compiler/rustc_mir/src/transform/check_packed_ref.rs
@@ -1,11 +1,18 @@
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::*;
+use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::builtin::UNALIGNED_REFERENCES;
+use rustc_span::symbol::sym;
use crate::transform::MirPass;
use crate::util;
+pub(crate) fn provide(providers: &mut Providers) {
+ *providers = Providers { unsafe_derive_on_repr_packed, ..*providers };
+}
+
pub struct CheckPackedRef;
impl<'tcx> MirPass<'tcx> for CheckPackedRef {
@@ -24,6 +31,41 @@
source_info: SourceInfo,
}
+fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+ let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+
+ tcx.struct_span_lint_hir(UNALIGNED_REFERENCES, lint_hir_id, tcx.def_span(def_id), |lint| {
+ // FIXME: when we make this a hard error, this should have its
+ // own error code.
+ let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
+ "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
+ type or const parameters (error E0133)"
+ .to_string()
+ } else {
+ "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
+ does not derive Copy (error E0133)"
+ .to_string()
+ };
+ lint.build(&message).emit()
+ });
+}
+
+fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
+ debug!("builtin_derive_def_id({:?})", def_id);
+ if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
+ if tcx.has_attr(impl_def_id, sym::automatically_derived) {
+ debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
+ Some(impl_def_id)
+ } else {
+ debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
+ None
+ }
+ } else {
+ debug!("builtin_derive_def_id({:?}) - not a method", def_id);
+ None
+ }
+}
+
impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> {
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
// Make sure we know where in the MIR we are.
@@ -40,26 +82,33 @@
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
if context.is_borrow() {
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
- let source_info = self.source_info;
- let lint_root = self.body.source_scopes[source_info.scope]
- .local_data
- .as_ref()
- .assert_crate_local()
- .lint_root;
- self.tcx.struct_span_lint_hir(
- UNALIGNED_REFERENCES,
- lint_root,
- source_info.span,
- |lint| {
- lint.build("reference to packed field is unaligned")
- .note(
- "fields of packed structs are not properly aligned, and creating \
- a misaligned reference is undefined behavior (even if that \
- reference is never dereferenced)",
- )
- .emit()
- },
- );
+ let def_id = self.body.source.instance.def_id();
+ if let Some(impl_def_id) = builtin_derive_def_id(self.tcx, def_id) {
+ // If a method is defined in the local crate,
+ // the impl containing that method should also be.
+ self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
+ } else {
+ let source_info = self.source_info;
+ let lint_root = self.body.source_scopes[source_info.scope]
+ .local_data
+ .as_ref()
+ .assert_crate_local()
+ .lint_root;
+ self.tcx.struct_span_lint_hir(
+ UNALIGNED_REFERENCES,
+ lint_root,
+ source_info.span,
+ |lint| {
+ lint.build("reference to packed field is unaligned")
+ .note(
+ "fields of packed structs are not properly aligned, and creating \
+ a misaligned reference is undefined behavior (even if that \
+ reference is never dereferenced)",
+ )
+ .emit()
+ },
+ );
+ }
}
}
}
diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs
index 532d201..955be8c 100644
--- a/compiler/rustc_mir/src/transform/check_unsafety.rs
+++ b/compiler/rustc_mir/src/transform/check_unsafety.rs
@@ -10,20 +10,15 @@
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
+use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
use rustc_session::lint::Level;
-use rustc_span::symbol::sym;
use std::ops::Bound;
-use crate::const_eval::is_min_const_fn;
-use crate::util;
-
pub struct UnsafetyChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
body_did: LocalDefId,
const_context: bool,
- min_const_fn: bool,
violations: Vec<UnsafetyViolation>,
source_info: SourceInfo,
tcx: TyCtxt<'tcx>,
@@ -36,21 +31,15 @@
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn new(
const_context: bool,
- min_const_fn: bool,
body: &'a Body<'tcx>,
body_did: LocalDefId,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Self {
- // sanity check
- if min_const_fn {
- assert!(const_context);
- }
Self {
body,
body_did,
const_context,
- min_const_fn,
violations: vec![],
source_info: SourceInfo::outermost(body.span),
tcx,
@@ -86,7 +75,7 @@
let sig = func_ty.fn_sig(self.tcx);
if let hir::Unsafety::Unsafe = sig.unsafety() {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToUnsafeFunction,
)
}
@@ -136,7 +125,7 @@
match self.tcx.layout_scalar_valid_range(def.did) {
(Bound::Unbounded, Bound::Unbounded) => {}
_ => self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::InitializingTypeWith,
),
}
@@ -182,18 +171,6 @@
self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use());
}
- // Check for borrows to packed fields.
- // `is_disaligned` already traverses the place to consider all projections after the last
- // `Deref`, so this only needs to be called once at the top level.
- if context.is_borrow() {
- if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
- self.require_unsafe(
- UnsafetyViolationKind::BorrowPacked,
- UnsafetyViolationDetails::BorrowOfPackedField,
- );
- }
- }
-
// Some checks below need the extra metainfo of the local declaration.
let decl = &self.body.local_decls[place.local];
@@ -227,7 +204,7 @@
let base_ty = base.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::DerefOfRawPointer,
)
}
@@ -272,7 +249,7 @@
);
if !nodrop {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::AssignToDroppingUnionField,
);
} else {
@@ -280,7 +257,7 @@
}
} else {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::AccessToUnionField,
)
}
@@ -291,6 +268,9 @@
impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
+ // Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
+ assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
+
let source_info = self.source_info;
let lint_root = self.body.source_scopes[self.source_info.scope]
.local_data
@@ -317,25 +297,14 @@
// `unsafe` blocks are required in safe code
Safety::Safe => {
for violation in violations {
- let mut violation = *violation;
match violation.kind {
- UnsafetyViolationKind::GeneralAndConstFn
- | UnsafetyViolationKind::General => {}
- UnsafetyViolationKind::BorrowPacked => {
- if self.min_const_fn {
- // const fns don't need to be backwards compatible and can
- // emit these violations as a hard error instead of a backwards
- // compat lint
- violation.kind = UnsafetyViolationKind::General;
- }
- }
- UnsafetyViolationKind::UnsafeFn
- | UnsafetyViolationKind::UnsafeFnBorrowPacked => {
+ UnsafetyViolationKind::General => {}
+ UnsafetyViolationKind::UnsafeFn => {
bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context")
}
}
- if !self.violations.contains(&violation) {
- self.violations.push(violation)
+ if !self.violations.contains(violation) {
+ self.violations.push(*violation)
}
}
false
@@ -345,11 +314,7 @@
for violation in violations {
let mut violation = *violation;
- if violation.kind == UnsafetyViolationKind::BorrowPacked {
- violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked;
- } else {
- violation.kind = UnsafetyViolationKind::UnsafeFn;
- }
+ violation.kind = UnsafetyViolationKind::UnsafeFn;
if !self.violations.contains(&violation) {
self.violations.push(violation)
}
@@ -362,31 +327,6 @@
if !violations.is_empty() {
self.used_unsafe.insert(hir_id);
}
- // only some unsafety is allowed in const fn
- if self.min_const_fn {
- for violation in violations {
- match violation.kind {
- // these unsafe things are stable in const fn
- UnsafetyViolationKind::GeneralAndConstFn => {}
- // these things are forbidden in const fns
- UnsafetyViolationKind::General
- | UnsafetyViolationKind::BorrowPacked => {
- let mut violation = *violation;
- // const fns don't need to be backwards compatible and can
- // emit these violations as a hard error instead of a backwards
- // compat lint
- violation.kind = UnsafetyViolationKind::General;
- if !self.violations.contains(&violation) {
- self.violations.push(violation)
- }
- }
- UnsafetyViolationKind::UnsafeFn
- | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!(
- "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context"
- ),
- }
- }
- }
true
}
};
@@ -424,7 +364,7 @@
} else {
continue;
};
- self.require_unsafe(UnsafetyViolationKind::GeneralAndConstFn, details);
+ self.require_unsafe(UnsafetyViolationKind::General, details);
}
}
}
@@ -442,7 +382,7 @@
// Is `callee_features` a subset of `calling_features`?
if !callee_features.iter().all(|feature| self_features.contains(feature)) {
self.require_unsafe(
- UnsafetyViolationKind::GeneralAndConstFn,
+ UnsafetyViolationKind::General,
UnsafetyViolationDetails::CallToFunctionWith,
)
}
@@ -464,7 +404,6 @@
ty::WithOptConstParam { did, const_param_did: Some(param_did) },
)
},
- unsafe_derive_on_repr_packed,
..*providers
};
}
@@ -525,15 +464,12 @@
let param_env = tcx.param_env(def.did);
let id = tcx.hir().local_def_id_to_hir_id(def.did);
- let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) {
- hir::BodyOwnerKind::Closure => (false, false),
- hir::BodyOwnerKind::Fn => {
- (tcx.is_const_fn_raw(def.did.to_def_id()), is_min_const_fn(tcx, def.did.to_def_id()))
- }
- hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false),
+ let const_context = match tcx.hir().body_owner_kind(id) {
+ hir::BodyOwnerKind::Closure => false,
+ hir::BodyOwnerKind::Fn => tcx.is_const_fn_raw(def.did.to_def_id()),
+ hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => true,
};
- let mut checker =
- UnsafetyChecker::new(const_context, min_const_fn, body, def.did, tcx, param_env);
+ let mut checker = UnsafetyChecker::new(const_context, body, def.did, tcx, param_env);
checker.visit_body(&body);
check_unused_unsafe(tcx, def.did, &checker.used_unsafe, &mut checker.inherited_blocks);
@@ -544,25 +480,6 @@
})
}
-fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
- let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
-
- tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| {
- // FIXME: when we make this a hard error, this should have its
- // own error code.
- let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
- "`#[derive]` can't be used on a `#[repr(packed)]` struct with \
- type or const parameters (error E0133)"
- .to_string()
- } else {
- "`#[derive]` can't be used on a `#[repr(packed)]` struct that \
- does not derive Copy (error E0133)"
- .to_string()
- };
- lint.build(&message).emit()
- });
-}
-
/// Returns the `HirId` for an enclosing scope that is also `unsafe`.
fn is_enclosed(
tcx: TyCtxt<'_>,
@@ -609,22 +526,6 @@
});
}
-fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
- debug!("builtin_derive_def_id({:?})", def_id);
- if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
- if tcx.has_attr(impl_def_id, sym::automatically_derived) {
- debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
- Some(impl_def_id)
- } else {
- debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
- None
- }
- } else {
- debug!("builtin_derive_def_id({:?}) - not a method", def_id);
- None
- }
-}
-
pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
debug!("check_unsafety({:?})", def_id);
@@ -643,7 +544,7 @@
if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" };
match kind {
- UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => {
+ UnsafetyViolationKind::General => {
// once
struct_span_err!(
tcx.sess,
@@ -657,27 +558,6 @@
.note(note)
.emit();
}
- UnsafetyViolationKind::BorrowPacked => {
- if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id.to_def_id()) {
- // If a method is defined in the local crate,
- // the impl containing that method should also be.
- tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
- } else {
- tcx.struct_span_lint_hir(
- SAFE_PACKED_BORROWS,
- lint_root,
- source_info.span,
- |lint| {
- lint.build(&format!(
- "{} is unsafe and requires unsafe{} block (error E0133)",
- description, unsafe_fn_msg,
- ))
- .note(note)
- .emit()
- },
- )
- }
- }
UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir(
UNSAFE_OP_IN_UNSAFE_FN,
lint_root,
@@ -692,35 +572,6 @@
.emit();
},
),
- UnsafetyViolationKind::UnsafeFnBorrowPacked => {
- // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions
- // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`,
- // a safe packed borrow should emit a warning *but not an error* in an unsafe function,
- // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`.
- //
- // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with
- // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow
- // should only issue a warning for the sake of backwards compatibility.
- //
- // The solution those 2 expectations is to always take the minimum of both lints.
- // This prevent any new errors (unless both lints are explicitly set to `deny`).
- let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0
- <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0
- {
- SAFE_PACKED_BORROWS
- } else {
- UNSAFE_OP_IN_UNSAFE_FN
- };
- tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| {
- lint.build(&format!(
- "{} is unsafe and requires unsafe block (error E0133)",
- description,
- ))
- .span_label(source_info.span, description)
- .note(note)
- .emit();
- })
- }
}
}
diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs
index cc8669d..5968bbb 100644
--- a/compiler/rustc_mir/src/transform/const_prop.rs
+++ b/compiler/rustc_mir/src/transform/const_prop.rs
@@ -13,9 +13,9 @@
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
};
use rustc_middle::mir::{
- AssertKind, BasicBlock, BinOp, Body, ClearCrossCrate, Constant, ConstantKind, Local, LocalDecl,
- LocalKind, Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData,
- Statement, StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
+ AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
+ Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
+ StatementKind, Terminator, TerminatorKind, UnOp, RETURN_PLACE,
};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout};
use rustc_middle::ty::subst::{InternalSubsts, Subst};
@@ -440,18 +440,7 @@
}
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>
@@ -491,7 +480,11 @@
let lint_only = match c.literal {
ConstantKind::Ty(ct) => match ct.val {
// Promoteds must lint and not error as the user didn't ask for them
- ConstKind::Unevaluated(_, _, Some(_)) => true,
+ ConstKind::Unevaluated(ty::Unevaluated {
+ def: _,
+ substs: _,
+ promoted: Some(_),
+ }) => true,
// Out of backwards compatibility we cannot report hard errors in unused
// generic functions using associated constants of the generic parameters.
_ => c.literal.needs_subst(),
diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs
index aabfee5..4836148 100644
--- a/compiler/rustc_mir/src/transform/coverage/debug.rs
+++ b/compiler/rustc_mir/src/transform/coverage/debug.rs
@@ -121,6 +121,7 @@
use rustc_middle::mir::{self, BasicBlock, TerminatorKind};
use rustc_middle::ty::TyCtxt;
+use std::iter;
use std::lazy::SyncOnceCell;
pub const NESTED_INDENT: &str = " ";
@@ -703,9 +704,7 @@
let edge_counters = from_terminator
.successors()
.map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb));
- edge_labels
- .iter()
- .zip(edge_counters)
+ iter::zip(&edge_labels, edge_counters)
.map(|(label, some_counter)| {
if let Some(counter) = some_counter {
format!("{}\n{}", label, debug_counters.format_counter(counter))
@@ -817,7 +816,7 @@
sections
}
-/// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any
+/// Returns a simple string representation of a `TerminatorKind` variant, independent of any
/// values it might hold.
pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str {
match kind {
diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs
index 93133e9..eaeb442 100644
--- a/compiler/rustc_mir/src/transform/coverage/mod.rs
+++ b/compiler/rustc_mir/src/transform/coverage/mod.rs
@@ -23,6 +23,7 @@
use rustc_middle::hir;
use rustc_middle::hir::map::blocks::FnLikeNode;
use rustc_middle::ich::StableHashingContext;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::coverage::*;
use rustc_middle::mir::{
self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
@@ -87,6 +88,11 @@
_ => {}
}
+ let codegen_fn_attrs = tcx.codegen_fn_attrs(mir_source.def_id());
+ if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
+ return;
+ }
+
trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
@@ -111,7 +117,8 @@
let body_span = hir_body.value.span;
let source_file = source_map.lookup_source_file(body_span.lo());
let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
- Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.hi()))
+ fn_sig.span.ctxt() == body_span.ctxt()
+ && Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.lo()))
}) {
Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()),
None => body_span.shrink_to_lo(),
diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs
index de8447f..2ba9d1b 100644
--- a/compiler/rustc_mir/src/transform/coverage/query.rs
+++ b/compiler/rustc_mir/src/transform/coverage/query.rs
@@ -6,10 +6,9 @@
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::DefId;
-/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each
-/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR).
+/// A `query` provider for retrieving coverage information injected into MIR.
pub(crate) fn provide(providers: &mut Providers) {
- providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id);
+ providers.coverageinfo = |tcx, def_id| coverageinfo(tcx, def_id);
providers.covered_file_name = |tcx, def_id| covered_file_name(tcx, def_id);
providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id);
}
@@ -121,7 +120,7 @@
}
}
-fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {
+fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo {
let mir_body = mir_body(tcx, def_id);
let mut coverage_visitor = CoverageVisitor {
@@ -139,29 +138,22 @@
}
fn covered_file_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Symbol> {
- let body = mir_body(tcx, def_id);
- for bb_data in body.basic_blocks().iter() {
- for statement in bb_data.statements.iter() {
- if let StatementKind::Coverage(box ref coverage) = statement.kind {
- if let Some(code_region) = coverage.code_region.as_ref() {
- if is_inlined(body, statement) {
- continue;
+ if tcx.is_mir_available(def_id) {
+ let body = mir_body(tcx, def_id);
+ for bb_data in body.basic_blocks().iter() {
+ for statement in bb_data.statements.iter() {
+ if let StatementKind::Coverage(box ref coverage) = statement.kind {
+ if let Some(code_region) = coverage.code_region.as_ref() {
+ if is_inlined(body, statement) {
+ continue;
+ }
+ return Some(code_region.file_name);
}
- return Some(code_region.file_name);
}
}
}
}
- None
-}
-
-/// This function ensures we obtain the correct MIR for the given item irrespective of
-/// whether that means const mir or runtime mir. For `const fn` this opts for runtime
-/// mir.
-fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> {
- let id = ty::WithOptConstParam::unknown(def_id);
- let def = ty::InstanceDef::Item(id);
- tcx.instance_mir(def)
+ return None;
}
fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> {
@@ -188,3 +180,12 @@
let scope_data = &body.source_scopes[statement.source_info.scope];
scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some()
}
+
+/// This function ensures we obtain the correct MIR for the given item irrespective of
+/// whether that means const mir or runtime mir. For `const fn` this opts for runtime
+/// mir.
+fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> {
+ let id = ty::WithOptConstParam::unknown(def_id);
+ let def = ty::InstanceDef::Item(id);
+ tcx.instance_mir(def)
+}
diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs
index e7097ce..2041109 100644
--- a/compiler/rustc_mir/src/transform/coverage/spans.rs
+++ b/compiler/rustc_mir/src/transform/coverage/spans.rs
@@ -11,7 +11,7 @@
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::original_sp;
-use rustc_span::{BytePos, Span, SyntaxContext};
+use rustc_span::{BytePos, Span};
use std::cmp::Ordering;
@@ -240,7 +240,7 @@
/// to be).
pub(super) fn generate_coverage_spans(
mir_body: &'a mir::Body<'tcx>,
- fn_sig_span: Span,
+ fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span`
body_span: Span,
basic_coverage_blocks: &'a CoverageGraph,
) -> Vec<CoverageSpan> {
@@ -683,10 +683,10 @@
// and `_1` is the `Place` for `somenum`.
//
// If and when the Issue is resolved, remove this special case match pattern:
- StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None,
+ StatementKind::FakeRead(box (cause, _)) if cause == FakeReadCause::ForGuardBinding => None,
// Retain spans from all other statements
- StatementKind::FakeRead(_, _) // Not including `ForGuardBinding`
+ StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding`
| StatementKind::CopyNonOverlapping(..)
| StatementKind::Assign(_)
| StatementKind::SetDiscriminant { .. }
@@ -717,11 +717,21 @@
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::Goto { .. } => None,
+ // Call `func` operand can have a more specific span when part of a chain of calls
+ | TerminatorKind::Call { ref func, .. } => {
+ let mut span = terminator.source_info.span;
+ if let mir::Operand::Constant(box constant) = func {
+ if constant.span.lo() > span.lo() {
+ span = span.with_lo(constant.span.lo());
+ }
+ }
+ Some(function_source_span(span, body_span))
+ }
+
// Retain spans from all other terminators
TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
- | TerminatorKind::Call { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseUnwind { .. }
@@ -733,6 +743,6 @@
#[inline]
fn function_source_span(span: Span, body_span: Span) -> Span {
- let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root());
+ let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
if body_span.contains(span) { span } else { body_span }
}
diff --git a/compiler/rustc_mir/src/transform/coverage/tests.rs b/compiler/rustc_mir/src/transform/coverage/tests.rs
index 7a9bfaa..dee1124 100644
--- a/compiler/rustc_mir/src/transform/coverage/tests.rs
+++ b/compiler/rustc_mir/src/transform/coverage/tests.rs
@@ -17,7 +17,7 @@
//! Also note, some basic features of `Span` also rely on the `Span`s own "session globals", which
//! are unrelated to the `TyCtxt` global. Without initializing the `Span` session globals, some
//! basic, coverage-specific features would be impossible to test, but thankfully initializing these
-//! globals is comparitively simpler. The easiest way is to wrap the test in a closure argument
+//! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument
//! to: `rustc_span::with_default_session_globals(|| { test_here(); })`.
use super::counters;
diff --git a/compiler/rustc_mir/src/transform/deduplicate_blocks.rs b/compiler/rustc_mir/src/transform/deduplicate_blocks.rs
index e102512..c41e71e 100644
--- a/compiler/rustc_mir/src/transform/deduplicate_blocks.rs
+++ b/compiler/rustc_mir/src/transform/deduplicate_blocks.rs
@@ -1,7 +1,7 @@
//! This pass finds basic blocks that are completely equal,
//! and replaces all uses with just one of them.
-use std::{collections::hash_map::Entry, hash::Hash, hash::Hasher};
+use std::{collections::hash_map::Entry, hash::Hash, hash::Hasher, iter};
use crate::transform::MirPass;
@@ -115,11 +115,7 @@
fn eq(&self, other: &Self) -> bool {
self.basic_block_data.statements.len() == other.basic_block_data.statements.len()
&& &self.basic_block_data.terminator().kind == &other.basic_block_data.terminator().kind
- && self
- .basic_block_data
- .statements
- .iter()
- .zip(&other.basic_block_data.statements)
+ && iter::zip(&self.basic_block_data.statements, &other.basic_block_data.statements)
.all(|(x, y)| statement_eq(&x.kind, &y.kind))
}
}
diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs
index 6656dea..29df86c 100644
--- a/compiler/rustc_mir/src/transform/dest_prop.rs
+++ b/compiler/rustc_mir/src/transform/dest_prop.rs
@@ -720,9 +720,6 @@
}
}
}
- InlineAsmOperand::Const { value } => {
- assert!(value.place().is_none());
- }
InlineAsmOperand::InOut {
reg: _,
late: _,
@@ -731,6 +728,7 @@
}
| InlineAsmOperand::In { reg: _, value: _ }
| InlineAsmOperand::Out { reg: _, late: _, place: None }
+ | InlineAsmOperand::Const { value: _ }
| InlineAsmOperand::SymFn { value: _ }
| InlineAsmOperand::SymStatic { def_id: _ } => {}
}
diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs
index c85e9b9..003003a 100644
--- a/compiler/rustc_mir/src/transform/generator.rs
+++ b/compiler/rustc_mir/src/transform/generator.rs
@@ -751,9 +751,10 @@
span_bug!(
body.span,
"Broken MIR: generator contains type {} in MIR, \
- but typeck only knows about {}",
- decl.ty,
- witness,
+ but typeck only knows about {} and {:?}",
+ decl_ty,
+ allowed,
+ allowed_upvars
);
}
}
diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs
index 12fdbd6..b6f8076 100644
--- a/compiler/rustc_mir/src/transform/inline.rs
+++ b/compiler/rustc_mir/src/transform/inline.rs
@@ -630,7 +630,7 @@
caller_body.required_consts.extend(
callee_body.required_consts.iter().copied().filter(|&ct| {
match ct.literal.const_for_ty() {
- Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_, _, _)),
+ Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_)),
None => true,
}
}),
diff --git a/compiler/rustc_mir/src/transform/match_branches.rs b/compiler/rustc_mir/src/transform/match_branches.rs
index d04a701..f7a9835 100644
--- a/compiler/rustc_mir/src/transform/match_branches.rs
+++ b/compiler/rustc_mir/src/transform/match_branches.rs
@@ -1,6 +1,7 @@
use crate::transform::MirPass;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
+use std::iter;
use super::simplify::simplify_cfg;
@@ -83,7 +84,7 @@
if first_stmts.len() != scnd_stmts.len() {
continue;
}
- for (f, s) in first_stmts.iter().zip(scnd_stmts.iter()) {
+ for (f, s) in iter::zip(first_stmts, scnd_stmts) {
match (&f.kind, &s.kind) {
// If two statements are exactly the same, we can optimize.
(f_s, s_s) if f_s == s_s => {}
@@ -113,7 +114,7 @@
// and bb_idx has a different terminator from both of them.
let (from, first, second) = bbs.pick3_mut(bb_idx, first, second);
- let new_stmts = first.statements.iter().zip(second.statements.iter()).map(|(f, s)| {
+ let new_stmts = iter::zip(&first.statements, &second.statements).map(|(f, s)| {
match (&f.kind, &s.kind) {
(f_s, s_s) if f_s == s_s => (*f).clone(),
diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs
index 1354644..5c49ee6 100644
--- a/compiler/rustc_mir/src/transform/mod.rs
+++ b/compiler/rustc_mir/src/transform/mod.rs
@@ -44,6 +44,7 @@
pub mod remove_noop_landing_pads;
pub mod remove_storage_markers;
pub mod remove_unneeded_drops;
+pub mod remove_zsts;
pub mod required_consts;
pub mod rustc_peek;
pub mod simplify;
@@ -58,6 +59,7 @@
pub(crate) fn provide(providers: &mut Providers) {
self::check_unsafety::provide(providers);
+ self::check_packed_ref::provide(providers);
*providers = Providers {
mir_keys,
mir_const,
@@ -313,11 +315,8 @@
&simplify::SimplifyCfg::new("promote-consts"),
];
- let opt_coverage: &[&dyn MirPass<'tcx>] = if tcx.sess.opts.debugging_opts.instrument_coverage {
- &[&coverage::InstrumentCoverage]
- } else {
- &[]
- };
+ let opt_coverage: &[&dyn MirPass<'tcx>] =
+ if tcx.sess.instrument_coverage() { &[&coverage::InstrumentCoverage] } else { &[] };
run_passes(tcx, &mut body, MirPhase::ConstPromotion, &[promote, opt_coverage]);
@@ -494,6 +493,7 @@
// The main optimizations that we do on MIR.
let optimizations: &[&dyn MirPass<'tcx>] = &[
&remove_storage_markers::RemoveStorageMarkers,
+ &remove_zsts::RemoveZsts,
&const_goto::ConstGoto,
&remove_unneeded_drops::RemoveUnneededDrops,
&match_branches::MatchBranchSimplification,
diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs
index 7db790c..1bbaf83 100644
--- a/compiler/rustc_mir/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir/src/transform/promote_consts.rs
@@ -108,9 +108,6 @@
/// the attribute currently provides the semantic requirement that arguments
/// must be constant.
Argument { bb: BasicBlock, index: usize },
-
- /// `const` operand in asm!.
- InlineAsm { bb: BasicBlock, index: usize },
}
impl Candidate {
@@ -118,16 +115,14 @@
fn forces_explicit_promotion(&self) -> bool {
match self {
Candidate::Ref(_) => false,
- Candidate::Argument { .. } | Candidate::InlineAsm { .. } => true,
+ Candidate::Argument { .. } => true,
}
}
fn source_info(&self, body: &Body<'_>) -> SourceInfo {
match self {
Candidate::Ref(location) => *body.source_info(*location),
- Candidate::Argument { bb, .. } | Candidate::InlineAsm { bb, .. } => {
- *body.source_info(body.terminator_loc(*bb))
- }
+ Candidate::Argument { bb, .. } => *body.source_info(body.terminator_loc(*bb)),
}
}
}
@@ -217,36 +212,25 @@
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
self.super_terminator(terminator, location);
- match terminator.kind {
- TerminatorKind::Call { ref func, .. } => {
- if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() {
- let fn_sig = self.ccx.tcx.fn_sig(def_id);
- if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
- let name = self.ccx.tcx.item_name(def_id);
- // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles.
- if name.as_str().starts_with("simd_shuffle") {
- self.candidates
- .push(Candidate::Argument { bb: location.block, index: 2 });
+ if let TerminatorKind::Call { ref func, .. } = terminator.kind {
+ if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() {
+ let fn_sig = self.ccx.tcx.fn_sig(def_id);
+ if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
+ let name = self.ccx.tcx.item_name(def_id);
+ // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles.
+ if name.as_str().starts_with("simd_shuffle") {
+ self.candidates.push(Candidate::Argument { bb: location.block, index: 2 });
- return; // Don't double count `simd_shuffle` candidates
- }
+ return; // Don't double count `simd_shuffle` candidates
}
+ }
- if let Some(constant_args) = args_required_const(self.ccx.tcx, def_id) {
- for index in constant_args {
- self.candidates.push(Candidate::Argument { bb: location.block, index });
- }
+ if let Some(constant_args) = args_required_const(self.ccx.tcx, def_id) {
+ for index in constant_args {
+ self.candidates.push(Candidate::Argument { bb: location.block, index });
}
}
}
- TerminatorKind::InlineAsm { ref operands, .. } => {
- for (index, op) in operands.iter().enumerate() {
- if let InlineAsmOperand::Const { .. } = op {
- self.candidates.push(Candidate::InlineAsm { bb: location.block, index })
- }
- }
- }
- _ => {}
}
}
}
@@ -335,18 +319,6 @@
_ => bug!(),
}
}
- Candidate::InlineAsm { bb, index } => {
- assert!(self.explicit);
-
- let terminator = self.body[bb].terminator();
- match &terminator.kind {
- TerminatorKind::InlineAsm { operands, .. } => match &operands[index] {
- InlineAsmOperand::Const { value } => self.validate_operand(value),
- _ => bug!(),
- },
- _ => bug!(),
- }
- }
}
}
@@ -818,9 +790,7 @@
}
match candidate {
- Candidate::Argument { bb, index } | Candidate::InlineAsm { bb, index }
- if !is_promotable =>
- {
+ Candidate::Argument { bb, index } if !is_promotable => {
let span = ccx.body[bb].terminator().source_info.span;
let msg = format!("argument {} is required to be a constant", index + 1);
ccx.tcx.sess.span_err(span, &msg);
@@ -1001,17 +971,17 @@
literal: tcx
.mk_const(ty::Const {
ty,
- val: ty::ConstKind::Unevaluated(
+ val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def,
- InternalSubsts::for_item(tcx, def.did, |param, _| {
+ substs: InternalSubsts::for_item(tcx, def.did, |param, _| {
if let ty::GenericParamDefKind::Lifetime = param.kind {
tcx.lifetimes.re_erased.into()
} else {
tcx.mk_param_from_def(param)
}
}),
- Some(promoted_id),
- ),
+ promoted: Some(promoted_id),
+ }),
})
.into(),
}))
@@ -1089,24 +1059,6 @@
_ => bug!(),
}
}
- Candidate::InlineAsm { bb, index } => {
- let terminator = blocks[bb].terminator_mut();
- match terminator.kind {
- TerminatorKind::InlineAsm { ref mut operands, .. } => {
- match &mut operands[index] {
- InlineAsmOperand::Const { ref mut value } => {
- let ty = value.ty(local_decls, self.tcx);
- let span = terminator.source_info.span;
-
- Rvalue::Use(mem::replace(value, promoted_operand(ty, span)))
- }
- _ => bug!(),
- }
- }
-
- _ => bug!(),
- }
- }
}
};
@@ -1161,7 +1113,7 @@
}
}
}
- Candidate::Argument { .. } | Candidate::InlineAsm { .. } => {}
+ Candidate::Argument { .. } => {}
}
// Declare return place local so that `mir::Body::new` doesn't complain.
diff --git a/compiler/rustc_mir/src/transform/remove_zsts.rs b/compiler/rustc_mir/src/transform/remove_zsts.rs
new file mode 100644
index 0000000..70f7538
--- /dev/null
+++ b/compiler/rustc_mir/src/transform/remove_zsts.rs
@@ -0,0 +1,89 @@
+//! Removes assignments to ZST places.
+
+use crate::transform::MirPass;
+use rustc_middle::mir::tcx::PlaceTy;
+use rustc_middle::mir::{Body, LocalDecls, Place, StatementKind};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+pub struct RemoveZsts;
+
+impl<'tcx> MirPass<'tcx> for RemoveZsts {
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ if tcx.sess.mir_opt_level() < 3 {
+ return;
+ }
+ let param_env = tcx.param_env(body.source.def_id());
+ let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
+ for block in basic_blocks.iter_mut() {
+ for statement in block.statements.iter_mut() {
+ match statement.kind {
+ StatementKind::Assign(box (place, _)) => {
+ let place_ty = place.ty(local_decls, tcx).ty;
+ if !maybe_zst(place_ty) {
+ continue;
+ }
+ let layout = match tcx.layout_of(param_env.and(place_ty)) {
+ Ok(layout) => layout,
+ Err(_) => continue,
+ };
+ if !layout.is_zst() {
+ continue;
+ }
+ if involves_a_union(place, local_decls, tcx) {
+ continue;
+ }
+ if tcx.consider_optimizing(|| {
+ format!(
+ "RemoveZsts - Place: {:?} SourceInfo: {:?}",
+ place, statement.source_info
+ )
+ }) {
+ statement.make_nop();
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+}
+
+/// A cheap, approximate check to avoid unnecessary `layout_of` calls.
+fn maybe_zst(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ // maybe ZST (could be more precise)
+ ty::Adt(..) | ty::Array(..) | ty::Closure(..) | ty::Tuple(..) | ty::Opaque(..) => true,
+ // definitely ZST
+ ty::FnDef(..) | ty::Never => true,
+ // unreachable or can't be ZST
+ _ => false,
+ }
+}
+
+/// Miri lazily allocates memory for locals on assignment,
+/// so we must preserve writes to unions and union fields,
+/// or it will ICE on reads of those fields.
+fn involves_a_union<'tcx>(
+ place: Place<'tcx>,
+ local_decls: &LocalDecls<'tcx>,
+ tcx: TyCtxt<'tcx>,
+) -> bool {
+ let mut place_ty = PlaceTy::from_ty(local_decls[place.local].ty);
+ if is_union(place_ty.ty) {
+ return true;
+ }
+ for elem in place.projection {
+ place_ty = place_ty.projection_ty(tcx, elem);
+ if is_union(place_ty.ty) {
+ return true;
+ }
+ }
+ return false;
+}
+
+fn is_union(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ ty::Adt(def, _) if def.is_union() => true,
+ _ => false,
+ }
+}
diff --git a/compiler/rustc_mir/src/transform/required_consts.rs b/compiler/rustc_mir/src/transform/required_consts.rs
index 2b518bd..8b64ad6 100644
--- a/compiler/rustc_mir/src/transform/required_consts.rs
+++ b/compiler/rustc_mir/src/transform/required_consts.rs
@@ -15,7 +15,7 @@
impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
if let Some(ct) = constant.literal.const_for_ty() {
- if let ConstKind::Unevaluated(_, _, _) = ct.val {
+ if let ConstKind::Unevaluated(_) = ct.val {
self.required_consts.push(*constant);
}
}
diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs
index d2314a9..65e2d09 100644
--- a/compiler/rustc_mir/src/transform/simplify.rs
+++ b/compiler/rustc_mir/src/transform/simplify.rs
@@ -31,10 +31,10 @@
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
-use rustc_middle::ty::ParamEnv;
use rustc_middle::ty::TyCtxt;
use smallvec::SmallVec;
-use std::{borrow::Cow, convert::TryInto};
+use std::borrow::Cow;
+use std::convert::TryInto;
pub struct SimplifyCfg {
label: String,
@@ -326,7 +326,7 @@
pub fn simplify_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) {
// First, we're going to get a count of *actual* uses for every `Local`.
- let mut used_locals = UsedLocals::new(body, tcx);
+ let mut used_locals = UsedLocals::new(body);
// Next, we're going to remove any `Local` with zero actual uses. When we remove those
// `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
@@ -336,8 +336,7 @@
remove_unused_definitions(&mut used_locals, body);
// Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
- let arg_count = body.arg_count.try_into().unwrap();
- let map = make_local_map(&mut body.local_decls, &used_locals, arg_count);
+ let map = make_local_map(&mut body.local_decls, &used_locals);
// Only bother running the `LocalUpdater` if we actually found locals to remove.
if map.iter().any(Option::is_none) {
@@ -350,61 +349,54 @@
}
/// Construct the mapping while swapping out unused stuff out from the `vec`.
-fn make_local_map<'tcx, V>(
+fn make_local_map<V>(
local_decls: &mut IndexVec<Local, V>,
- used_locals: &UsedLocals<'tcx>,
- arg_count: u32,
+ used_locals: &UsedLocals,
) -> IndexVec<Local, Option<Local>> {
- let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, local_decls);
+ let mut map: IndexVec<Local, Option<Local>> = IndexVec::from_elem(None, &*local_decls);
let mut used = Local::new(0);
for alive_index in local_decls.indices() {
- // When creating the local map treat the `RETURN_PLACE` and arguments as used.
- if alive_index.as_u32() <= arg_count || used_locals.is_used(alive_index) {
- map[alive_index] = Some(used);
- if alive_index != used {
- local_decls.swap(alive_index, used);
- }
- used.increment_by(1);
+ // `is_used` treats the `RETURN_PLACE` and arguments as used.
+ if !used_locals.is_used(alive_index) {
+ continue;
}
+
+ map[alive_index] = Some(used);
+ if alive_index != used {
+ local_decls.swap(alive_index, used);
+ }
+ used.increment_by(1);
}
local_decls.truncate(used.index());
map
}
/// Keeps track of used & unused locals.
-struct UsedLocals<'tcx> {
+struct UsedLocals {
increment: bool,
+ arg_count: u32,
use_count: IndexVec<Local, u32>,
- is_static: bool,
- local_decls: IndexVec<Local, LocalDecl<'tcx>>,
- param_env: ParamEnv<'tcx>,
- tcx: TyCtxt<'tcx>,
}
-impl UsedLocals<'tcx> {
+impl UsedLocals {
/// Determines which locals are used & unused in the given body.
- fn new(body: &Body<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
- let def_id = body.source.def_id();
- let is_static = tcx.is_static(def_id);
- let param_env = tcx.param_env(def_id);
- let local_decls = body.local_decls.clone();
+ fn new(body: &Body<'_>) -> Self {
let mut this = Self {
increment: true,
+ arg_count: body.arg_count.try_into().unwrap(),
use_count: IndexVec::from_elem(0, &body.local_decls),
- is_static,
- local_decls,
- param_env,
- tcx,
};
this.visit_body(body);
this
}
/// Checks if local is used.
+ ///
+ /// Return place and arguments are always considered used.
fn is_used(&self, local: Local) -> bool {
trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]);
- self.use_count[local] != 0
+ local.as_u32() <= self.arg_count || self.use_count[local] != 0
}
/// Updates the use counts to reflect the removal of given statement.
@@ -422,7 +414,9 @@
// A use, not a definition.
self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
} else {
- // A definition. Although, it still might use other locals for indexing.
+ // A definition. The base local itself is not visited, so this occurrence is not counted
+ // toward its use count. There might be other locals still, used in an indexing
+ // projection.
self.super_projection(
place.as_ref(),
PlaceContext::MutatingUse(MutatingUseContext::Projection),
@@ -432,7 +426,7 @@
}
}
-impl Visitor<'tcx> for UsedLocals<'tcx> {
+impl Visitor<'_> for UsedLocals {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
match statement.kind {
StatementKind::LlvmInlineAsm(..)
@@ -459,21 +453,7 @@
}
}
- fn visit_local(&mut self, local: &Local, ctx: PlaceContext, _location: Location) {
- debug!("local: {:?} is_static: {:?}, ctx: {:?}", local, self.is_static, ctx);
- // Do not count _0 as a used in `return;` if it is a ZST.
- let return_place = *local == RETURN_PLACE
- && matches!(ctx, PlaceContext::NonMutatingUse(visit::NonMutatingUseContext::Move));
- if !self.is_static && return_place {
- let ty = self.local_decls[*local].ty;
- let param_env_and = self.param_env.and(ty);
- if let Ok(layout) = self.tcx.layout_of(param_env_and) {
- debug!("layout.is_zst: {:?}", layout.is_zst());
- if layout.is_zst() {
- return;
- }
- }
- }
+ fn visit_local(&mut self, local: &Local, _ctx: PlaceContext, _location: Location) {
if self.increment {
self.use_count[*local] += 1;
} else {
@@ -484,10 +464,7 @@
}
/// Removes unused definitions. Updates the used locals to reflect the changes made.
-fn remove_unused_definitions<'a, 'tcx>(
- used_locals: &'a mut UsedLocals<'tcx>,
- body: &mut Body<'tcx>,
-) {
+fn remove_unused_definitions<'a, 'tcx>(used_locals: &'a mut UsedLocals, body: &mut Body<'tcx>) {
// The use counts are updated as we remove the statements. A local might become unused
// during the retain operation, leading to a temporary inconsistency (storage statements or
// definitions referencing the local might remain). For correctness it is crucial that this