Skip to content

Rollup of 7 pull requests #107472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jan 30, 2023
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
70f9d52
Add and use expect methods to hir.
WaffleLapkin Jan 20, 2023
ee9e8cd
This function appears to be unused
thanatos Jan 21, 2023
fa214c3
Fix typo in comment
thanatos Jan 21, 2023
ba80c66
slice: Add a specialization for clone_into when T is Copy
bpeel Jan 28, 2023
a34f11c
vec: Use SpecCloneIntoVec::clone_into to implement Vec::clone_from
bpeel Jan 28, 2023
a9aed86
Reimplement NormalizeArrayLen.
cjgillot Jan 21, 2023
b456307
Remove obsolete comment.
cjgillot Jan 29, 2023
f7cc20a
use a more descriptive name
tshepang Jan 30, 2023
a4aebf0
Improve ICE messages for `*::expect_*`
WaffleLapkin Jan 30, 2023
b2ef837
Use `expect_{use,fn}` in a couple of places
WaffleLapkin Jan 30, 2023
883145f
fix `TraitItemKind::expect_type` docs
WaffleLapkin Jan 30, 2023
727a1fd
Keep all theme-updating logic together
thanatos Jan 21, 2023
d458540
Split `has_allow_dead_code_or_lang_attr` into sub functions
WaffleLapkin Jan 30, 2023
48af3a9
Consider `#[allow(dead_code)]` before lang items
WaffleLapkin Jan 30, 2023
8348f25
Update bastion-of-the-turbofish.rs
LLBlumire Jan 30, 2023
4ac1796
Rollup merge of #107125 - WaffleLapkin:expect_an_item_in_your_hir_by_…
matthiaskrgr Jan 30, 2023
db97749
Rollup merge of #107172 - cjgillot:no-nal, r=nagisa
matthiaskrgr Jan 30, 2023
d1320a5
Rollup merge of #107177 - thanatos:fix-doc-errant-light-theme, r=notr…
matthiaskrgr Jan 30, 2023
b3b9383
Rollup merge of #107424 - bpeel:clone-into-from-share-code, r=scottmcm
matthiaskrgr Jan 30, 2023
c3b1f54
Rollup merge of #107455 - tshepang:better-name, r=wesleywiser
matthiaskrgr Jan 30, 2023
3c155dc
Rollup merge of #107465 - WaffleLapkin:has_allow_dead_code_or_lang_at…
matthiaskrgr Jan 30, 2023
d79a40d
Rollup merge of #107469 - LLBlumire:master, r=jyn514
matthiaskrgr Jan 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
363 changes: 360 additions & 3 deletions compiler/rustc_hir/src/hir.rs

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
@@ -3140,8 +3140,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
hir.get(fn_hir_id) else { return None };
let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
hir.get_parent(fn_hir_id) else { bug!("ImplItem should have Impl parent") };
let i = hir.get_parent(fn_hir_id).expect_item().expect_impl();

let trait_ref = self.instantiate_mono_trait_ref(
i.of_trait.as_ref()?,
18 changes: 9 additions & 9 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ use rustc_errors::{
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit;
use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
use rustc_hir::{GenericParamKind, ImplItemKind};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
@@ -918,7 +918,7 @@ fn report_trait_method_mismatch<'tcx>(
// When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
// span points only at the type `Box<Self`>, but we want to cover the whole
// argument pattern and type.
let ImplItemKind::Fn(ref sig, body) = tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{impl_m:?} is not a method") };
let (sig, body) = tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).expect_fn();
let span = tcx
.hir()
.body_param_names(body)
@@ -1080,12 +1080,12 @@ fn extract_spans_for_error_reporting<'tcx>(
) -> (Span, Option<Span>) {
let tcx = infcx.tcx;
let mut impl_args = {
let ImplItemKind::Fn(sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{:?} is not a method", impl_m) };
let (sig, _) = tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).expect_fn();
sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
};

let trait_args = trait_m.def_id.as_local().map(|def_id| {
let TraitItemKind::Fn(sig, _) = &tcx.hir().expect_trait_item(def_id).kind else { bug!("{:?} is not a TraitItemKind::Fn", trait_m) };
let (sig, _) = tcx.hir().expect_trait_item(def_id).expect_fn();
sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
});

@@ -1358,7 +1358,7 @@ fn compare_number_of_method_arguments<'tcx>(
.def_id
.as_local()
.and_then(|def_id| {
let TraitItemKind::Fn(trait_m_sig, _) = &tcx.hir().expect_trait_item(def_id).kind else { bug!("{:?} is not a method", impl_m) };
let (trait_m_sig, _) = &tcx.hir().expect_trait_item(def_id).expect_fn();
let pos = trait_number_args.saturating_sub(1);
trait_m_sig.decl.inputs.get(pos).map(|arg| {
if pos == 0 {
@@ -1370,7 +1370,7 @@ fn compare_number_of_method_arguments<'tcx>(
})
.or(trait_item_span);

let ImplItemKind::Fn(impl_m_sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{:?} is not a method", impl_m) };
let (impl_m_sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).expect_fn();
let pos = impl_number_args.saturating_sub(1);
let impl_span = impl_m_sig
.decl
@@ -1506,7 +1506,7 @@ fn compare_synthetic_generics<'tcx>(
let _: Option<_> = try {
let impl_m = impl_m.def_id.as_local()?;
let impl_m = tcx.hir().expect_impl_item(impl_m);
let hir::ImplItemKind::Fn(sig, _) = &impl_m.kind else { unreachable!() };
let (sig, _) = impl_m.expect_fn();
let input_tys = sig.decl.inputs;

struct Visitor(Option<Span>, hir::def_id::LocalDefId);
@@ -1704,7 +1704,7 @@ pub(super) fn compare_impl_const_raw(
);

// Locate the Span containing just the type of the offending impl
let ImplItemKind::Const(ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).kind else { bug!("{impl_const_item:?} is not a impl const") };
let (ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).expect_const();
cause.span = ty.span;

let mut diag = struct_span_err!(
@@ -1717,7 +1717,7 @@ pub(super) fn compare_impl_const_raw(

let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
// Add a label to the Span containing just the type of the const
let TraitItemKind::Const(ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).kind else { bug!("{trait_const_item:?} is not a trait const") };
let (ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).expect_const();
ty.span
});

8 changes: 4 additions & 4 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -1072,8 +1072,8 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
// All field types must be well-formed.
for field in &variant.fields {
let field_id = field.did.expect_local();
let hir::Node::Field(hir::FieldDef { ty: hir_ty, .. }) = tcx.hir().get_by_def_id(field_id)
else { bug!() };
let hir::FieldDef { ty: hir_ty, .. } =
tcx.hir().get_by_def_id(field_id).expect_field();
let ty = wfcx.normalize(hir_ty.span, None, tcx.type_of(field.did));
wfcx.register_wf_obligation(
hir_ty.span,
@@ -1106,8 +1106,8 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
{
let last = idx == variant.fields.len() - 1;
let field_id = field.did.expect_local();
let hir::Node::Field(hir::FieldDef { ty: hir_ty, .. }) = tcx.hir().get_by_def_id(field_id)
else { bug!() };
let hir::FieldDef { ty: hir_ty, .. } =
tcx.hir().get_by_def_id(field_id).expect_field();
let ty = wfcx.normalize(hir_ty.span, None, tcx.type_of(field.did));
wfcx.register_bound(
traits::ObligationCause::new(
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check_unused.rs
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
if item.span.is_dummy() {
continue;
}
let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() };
let (path, _) = item.expect_use();
let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) {
format!("unused import: `{}`", snippet)
} else {
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/coherence/builtin.rs
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
_ => {}
}

let ItemKind::Impl(impl_) = tcx.hir().expect_item(impl_did).kind else { bug!("expected Drop impl item") };
let impl_ = tcx.hir().expect_item(impl_did).expect_impl();

tcx.sess.emit_err(DropImplOnWrongItem { span: impl_.self_ty.span });
}
4 changes: 1 addition & 3 deletions compiler/rustc_hir_analysis/src/coherence/unsafety.rs
Original file line number Diff line number Diff line change
@@ -3,15 +3,13 @@
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::Unsafety;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LocalDefId;

pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl));
let item = tcx.hir().expect_item(def_id);
let hir::ItemKind::Impl(impl_) = item.kind else { bug!() };
let impl_ = item.expect_impl();

if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) {
let trait_ref = trait_ref.subst_identity();
3 changes: 1 addition & 2 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
@@ -1348,8 +1348,7 @@ fn suggest_impl_trait<'tcx>(

fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::EarlyBinder<ty::TraitRef<'_>>> {
let icx = ItemCtxt::new(tcx, def_id);
let item = tcx.hir().expect_item(def_id.expect_local());
let hir::ItemKind::Impl(impl_) = item.kind else { bug!() };
let impl_ = tcx.hir().expect_item(def_id.expect_local()).expect_impl();
impl_
.of_trait
.as_ref()
9 changes: 6 additions & 3 deletions compiler/rustc_middle/src/mir/mono.rs
Original file line number Diff line number Diff line change
@@ -318,16 +318,19 @@ impl<'tcx> CodegenUnit<'tcx> {
base_n::encode(hash, base_n::CASE_INSENSITIVE)
}

pub fn estimate_size(&mut self, tcx: TyCtxt<'tcx>) {
pub fn create_size_estimate(&mut self, tcx: TyCtxt<'tcx>) {
// Estimate the size of a codegen unit as (approximately) the number of MIR
// statements it corresponds to.
self.size_estimate = Some(self.items.keys().map(|mi| mi.size_estimate(tcx)).sum());
}

#[inline]
/// Should only be called if [`create_size_estimate`] has previously been called.
///
/// [`create_size_estimate`]: Self::create_size_estimate
pub fn size_estimate(&self) -> usize {
// Should only be called if `estimate_size` has previously been called.
self.size_estimate.expect("estimate_size must be called before getting a size_estimate")
self.size_estimate
.expect("create_size_estimate must be called before getting a size_estimate")
}

pub fn modify_size_estimate(&mut self, delta: usize) {
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
@@ -548,13 +548,13 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&[
&reveal_all::RevealAll, // has to be done before inlining, since inlined code is in RevealAll mode.
&lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first
&normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering
&unreachable_prop::UnreachablePropagation,
&uninhabited_enum_branching::UninhabitedEnumBranching,
&o1(simplify::SimplifyCfg::new("after-uninhabited-enum-branching")),
&inline::Inline,
&remove_storage_markers::RemoveStorageMarkers,
&remove_zsts::RemoveZsts,
&normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering
&const_goto::ConstGoto,
&remove_unneeded_drops::RemoveUnneededDrops,
&sroa::ScalarReplacementOfAggregates,
322 changes: 69 additions & 253 deletions compiler/rustc_mir_transform/src/normalize_array_len.rs
Original file line number Diff line number Diff line change
@@ -1,288 +1,104 @@
//! This pass eliminates casting of arrays into slices when their length
//! is taken using `.len()` method. Handy to preserve information in MIR for const prop
use crate::ssa::SsaLocals;
use crate::MirPass;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::intern::Interned;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, ReErased, Region, TyCtxt};

const MAX_NUM_BLOCKS: usize = 800;
const MAX_NUM_LOCALS: usize = 3000;
use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::impls::borrowed_locals;

pub struct NormalizeArrayLen;

impl<'tcx> MirPass<'tcx> for NormalizeArrayLen {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
// See #105929
sess.mir_opt_level() >= 4 && sess.opts.unstable_opts.unsound_mir_opts
sess.mir_opt_level() >= 3
}

#[instrument(level = "trace", skip(self, tcx, body))]
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// early returns for edge cases of highly unrolled functions
if body.basic_blocks.len() > MAX_NUM_BLOCKS {
return;
}
if body.local_decls.len() > MAX_NUM_LOCALS {
return;
}
debug!(def_id = ?body.source.def_id());
normalize_array_len_calls(tcx, body)
}
}

pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// We don't ever touch terminators, so no need to invalidate the CFG cache
let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
let local_decls = &mut body.local_decls;
fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
let borrowed_locals = borrowed_locals(body);
let ssa = SsaLocals::new(tcx, param_env, body, &borrowed_locals);

// do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]`
let mut interesting_locals = BitSet::new_empty(local_decls.len());
for (local, decl) in local_decls.iter_enumerated() {
match decl.ty.kind() {
ty::Array(..) => {
interesting_locals.insert(local);
}
ty::Ref(.., ty, Mutability::Not) => match ty.kind() {
ty::Array(..) => {
interesting_locals.insert(local);
}
_ => {}
},
_ => {}
}
}
if interesting_locals.is_empty() {
// we have found nothing to analyze
return;
}
let num_intesting_locals = interesting_locals.count();
let mut state = FxIndexMap::with_capacity_and_hasher(num_intesting_locals, Default::default());
let mut patches_scratchpad =
FxIndexMap::with_capacity_and_hasher(num_intesting_locals, Default::default());
let mut replacements_scratchpad =
FxIndexMap::with_capacity_and_hasher(num_intesting_locals, Default::default());
for block in basic_blocks {
// make length calls for arrays [T; N] not to decay into length calls for &[T]
// that forbids constant propagation
normalize_array_len_call(
tcx,
block,
local_decls,
&interesting_locals,
&mut state,
&mut patches_scratchpad,
&mut replacements_scratchpad,
);
state.clear();
patches_scratchpad.clear();
replacements_scratchpad.clear();
}
}
let slice_lengths = compute_slice_length(tcx, &ssa, body);
debug!(?slice_lengths);

struct Patcher<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
patches_scratchpad: &'a FxIndexMap<usize, usize>,
replacements_scratchpad: &'a mut FxIndexMap<usize, Local>,
local_decls: &'a mut IndexVec<Local, LocalDecl<'tcx>>,
statement_idx: usize,
Replacer { tcx, slice_lengths }.visit_body_preserves_cfg(body);
}

impl<'tcx> Patcher<'_, 'tcx> {
fn patch_expand_statement(
&mut self,
statement: &mut Statement<'tcx>,
) -> Option<std::vec::IntoIter<Statement<'tcx>>> {
let idx = self.statement_idx;
if let Some(len_statemnt_idx) = self.patches_scratchpad.get(&idx).copied() {
let mut statements = Vec::with_capacity(2);

// we are at statement that performs a cast. The only sound way is
// to create another local that performs a similar copy without a cast and then
// use this copy in the Len operation

match &statement.kind {
StatementKind::Assign(box (
..,
Rvalue::Cast(
CastKind::Pointer(ty::adjustment::PointerCast::Unsize),
operand,
_,
),
)) => {
match operand {
Operand::Copy(place) | Operand::Move(place) => {
// create new local
let ty = operand.ty(self.local_decls, self.tcx);
let local_decl = LocalDecl::with_source_info(ty, statement.source_info);
let local = self.local_decls.push(local_decl);
// make it live
let mut make_live_statement = statement.clone();
make_live_statement.kind = StatementKind::StorageLive(local);
statements.push(make_live_statement);
// copy into it

let operand = Operand::Copy(*place);
let mut make_copy_statement = statement.clone();
let assign_to = Place::from(local);
let rvalue = Rvalue::Use(operand);
make_copy_statement.kind =
StatementKind::Assign(Box::new((assign_to, rvalue)));
statements.push(make_copy_statement);

// to reorder we have to copy and make NOP
statements.push(statement.clone());
statement.make_nop();

self.replacements_scratchpad.insert(len_statemnt_idx, local);
}
_ => {
unreachable!("it's a bug in the implementation")
}
}
}
_ => {
unreachable!("it's a bug in the implementation")
fn compute_slice_length<'tcx>(
tcx: TyCtxt<'tcx>,
ssa: &SsaLocals,
body: &Body<'tcx>,
) -> IndexVec<Local, Option<ty::Const<'tcx>>> {
let mut slice_lengths = IndexVec::from_elem(None, &body.local_decls);

for (local, rvalue) in ssa.assignments(body) {
match rvalue {
Rvalue::Cast(
CastKind::Pointer(ty::adjustment::PointerCast::Unsize),
operand,
cast_ty,
) => {
let operand_ty = operand.ty(body, tcx);
debug!(?operand_ty);
if let Some(operand_ty) = operand_ty.builtin_deref(true)
&& let ty::Array(_, len) = operand_ty.ty.kind()
&& let Some(cast_ty) = cast_ty.builtin_deref(true)
&& let ty::Slice(..) = cast_ty.ty.kind()
{
slice_lengths[local] = Some(*len);
}
}

self.statement_idx += 1;

Some(statements.into_iter())
} else if let Some(local) = self.replacements_scratchpad.get(&idx).copied() {
let mut statements = Vec::with_capacity(2);

match &statement.kind {
StatementKind::Assign(box (into, Rvalue::Len(place))) => {
let add_deref = if let Some(..) = place.as_local() {
false
} else if let Some(..) = place.local_or_deref_local() {
true
} else {
unreachable!("it's a bug in the implementation")
};
// replace len statement
let mut len_statement = statement.clone();
let mut place = Place::from(local);
if add_deref {
place = self.tcx.mk_place_deref(place);
}
len_statement.kind =
StatementKind::Assign(Box::new((*into, Rvalue::Len(place))));
statements.push(len_statement);

// make temporary dead
let mut make_dead_statement = statement.clone();
make_dead_statement.kind = StatementKind::StorageDead(local);
statements.push(make_dead_statement);

// make original statement NOP
statement.make_nop();
// The length information is stored in the fat pointer, so we treat `operand` as a value.
Rvalue::Use(operand) => {
if let Some(rhs) = operand.place() && let Some(rhs) = rhs.as_local() {
slice_lengths[local] = slice_lengths[rhs];
}
_ => {
unreachable!("it's a bug in the implementation")
}
// The length information is stored in the fat pointer.
// Reborrowing copies length information from one pointer to the other.
Rvalue::Ref(_, _, rhs) | Rvalue::AddressOf(_, rhs) => {
if let [PlaceElem::Deref] = rhs.projection[..] {
slice_lengths[local] = slice_lengths[rhs.local];
}
}

self.statement_idx += 1;

Some(statements.into_iter())
} else {
self.statement_idx += 1;
None
_ => {}
}
}

slice_lengths
}

fn normalize_array_len_call<'tcx>(
struct Replacer<'tcx> {
tcx: TyCtxt<'tcx>,
block: &mut BasicBlockData<'tcx>,
local_decls: &mut IndexVec<Local, LocalDecl<'tcx>>,
interesting_locals: &BitSet<Local>,
state: &mut FxIndexMap<Local, usize>,
patches_scratchpad: &mut FxIndexMap<usize, usize>,
replacements_scratchpad: &mut FxIndexMap<usize, Local>,
) {
for (statement_idx, statement) in block.statements.iter_mut().enumerate() {
match &mut statement.kind {
StatementKind::Assign(box (place, rvalue)) => {
match rvalue {
Rvalue::Cast(
CastKind::Pointer(ty::adjustment::PointerCast::Unsize),
operand,
cast_ty,
) => {
let Some(local) = place.as_local() else { return };
match operand {
Operand::Copy(place) | Operand::Move(place) => {
let Some(operand_local) = place.local_or_deref_local() else { return; };
if !interesting_locals.contains(operand_local) {
return;
}
let operand_ty = local_decls[operand_local].ty;
match (operand_ty.kind(), cast_ty.kind()) {
(ty::Array(of_ty_src, ..), ty::Slice(of_ty_dst)) => {
if of_ty_src == of_ty_dst {
// this is a cast from [T; N] into [T], so we are good
state.insert(local, statement_idx);
}
}
// current way of patching doesn't allow to work with `mut`
(
ty::Ref(
Region(Interned(ReErased, _)),
operand_ty,
Mutability::Not,
),
ty::Ref(
Region(Interned(ReErased, _)),
cast_ty,
Mutability::Not,
),
) => {
match (operand_ty.kind(), cast_ty.kind()) {
// current way of patching doesn't allow to work with `mut`
(ty::Array(of_ty_src, ..), ty::Slice(of_ty_dst)) => {
if of_ty_src == of_ty_dst {
// this is a cast from [T; N] into [T], so we are good
state.insert(local, statement_idx);
}
}
_ => {}
}
}
_ => {}
}
}
_ => {}
}
}
Rvalue::Len(place) => {
let Some(local) = place.local_or_deref_local() else {
return;
};
if let Some(cast_statement_idx) = state.get(&local).copied() {
patches_scratchpad.insert(cast_statement_idx, statement_idx);
}
}
_ => {
// invalidate
state.remove(&place.local);
}
}
}
_ => {}
}
}
slice_lengths: IndexVec<Local, Option<ty::Const<'tcx>>>,
}

let mut patcher = Patcher {
tcx,
patches_scratchpad: &*patches_scratchpad,
replacements_scratchpad,
local_decls,
statement_idx: 0,
};
impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

block.expand_statements(|st| patcher.patch_expand_statement(st));
fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, loc: Location) {
if let Rvalue::Len(place) = rvalue
&& let [PlaceElem::Deref] = &place.projection[..]
&& let Some(len) = self.slice_lengths[place.local]
{
*rvalue = Rvalue::Use(Operand::Constant(Box::new(Constant {
span: rustc_span::DUMMY_SP,
user_ty: None,
literal: ConstantKind::from_const(len, self.tcx),
})));
}
self.super_rvalue(rvalue, loc);
}
}
4 changes: 2 additions & 2 deletions compiler/rustc_monomorphize/src/partitioning/mod.rs
Original file line number Diff line number Diff line change
@@ -180,7 +180,7 @@ pub fn partition<'tcx>(
partitioner.place_root_mono_items(cx, mono_items)
};

initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.create_size_estimate(tcx));

debug_dump(tcx, "INITIAL PARTITIONING:", initial_partitioning.codegen_units.iter());

@@ -200,7 +200,7 @@ pub fn partition<'tcx>(
partitioner.place_inlined_mono_items(cx, initial_partitioning)
};

post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(tcx));
post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.create_size_estimate(tcx));

debug_dump(tcx, "POST INLINING:", post_inlining.codegen_units.iter());

34 changes: 18 additions & 16 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
@@ -459,30 +459,32 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
}

fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
if tcx.has_attr(def_id.to_def_id(), sym::lang) {
return true;
fn has_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
tcx.has_attr(def_id.to_def_id(), sym::lang)
// Stable attribute for #[lang = "panic_impl"]
|| tcx.has_attr(def_id.to_def_id(), sym::panic_handler)
}

// Stable attribute for #[lang = "panic_impl"]
if tcx.has_attr(def_id.to_def_id(), sym::panic_handler) {
return true;
fn has_allow_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0 == lint::Allow
}

if tcx.def_kind(def_id).has_codegen_attrs() {
let cg_attrs = tcx.codegen_fn_attrs(def_id);
fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
tcx.def_kind(def_id).has_codegen_attrs() && {
let cg_attrs = tcx.codegen_fn_attrs(def_id);

// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
// forcefully, e.g., for placing it in a specific section.
if cg_attrs.contains_extern_indicator()
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED)
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
{
return true;
// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
// forcefully, e.g., for placing it in a specific section.
cg_attrs.contains_extern_indicator()
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED)
|| cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
}
}

let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0 == lint::Allow
has_allow_dead_code(tcx, def_id)
|| has_used_like_attr(tcx, def_id)
|| has_lang_attr(tcx, def_id)
}

// These check_* functions seeds items that
43 changes: 33 additions & 10 deletions library/alloc/src/slice.rs
Original file line number Diff line number Diff line change
@@ -782,6 +782,38 @@ impl<T, A: Allocator> BorrowMut<[T]> for Vec<T, A> {
}
}

// Specializable trait for implementing ToOwned::clone_into. This is
// public in the crate and has the Allocator parameter so that
// vec::clone_from use it too.
#[cfg(not(no_global_oom_handling))]
pub(crate) trait SpecCloneIntoVec<T, A: Allocator> {
fn clone_into(&self, target: &mut Vec<T, A>);
}

#[cfg(not(no_global_oom_handling))]
impl<T: Clone, A: Allocator> SpecCloneIntoVec<T, A> for [T] {
default fn clone_into(&self, target: &mut Vec<T, A>) {
// drop anything in target that will not be overwritten
target.truncate(self.len());

// target.len <= self.len due to the truncate above, so the
// slices here are always in-bounds.
let (init, tail) = self.split_at(target.len());

// reuse the contained values' allocations/resources.
target.clone_from_slice(init);
target.extend_from_slice(tail);
}
}

#[cfg(not(no_global_oom_handling))]
impl<T: Copy, A: Allocator> SpecCloneIntoVec<T, A> for [T] {
fn clone_into(&self, target: &mut Vec<T, A>) {
target.clear();
target.extend_from_slice(self);
}
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Clone> ToOwned for [T] {
@@ -797,16 +829,7 @@ impl<T: Clone> ToOwned for [T] {
}

fn clone_into(&self, target: &mut Vec<T>) {
// drop anything in target that will not be overwritten
target.truncate(self.len());

// target.len <= self.len due to the truncate above, so the
// slices here are always in-bounds.
let (init, tail) = self.split_at(target.len());

// reuse the contained values' allocations/resources.
target.clone_from_slice(init);
target.extend_from_slice(tail);
SpecCloneIntoVec::clone_into(self, target);
}
}

31 changes: 1 addition & 30 deletions library/alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
@@ -2646,35 +2646,6 @@ impl<T, A: Allocator> ops::DerefMut for Vec<T, A> {
}
}

#[cfg(not(no_global_oom_handling))]
trait SpecCloneFrom {
fn clone_from(this: &mut Self, other: &Self);
}

#[cfg(not(no_global_oom_handling))]
impl<T: Clone, A: Allocator> SpecCloneFrom for Vec<T, A> {
default fn clone_from(this: &mut Self, other: &Self) {
// drop anything that will not be overwritten
this.truncate(other.len());

// self.len <= other.len due to the truncate above, so the
// slices here are always in-bounds.
let (init, tail) = other.split_at(this.len());

// reuse the contained values' allocations/resources.
this.clone_from_slice(init);
this.extend_from_slice(tail);
}
}

#[cfg(not(no_global_oom_handling))]
impl<T: Copy, A: Allocator> SpecCloneFrom for Vec<T, A> {
fn clone_from(this: &mut Self, other: &Self) {
this.clear();
this.extend_from_slice(other);
}
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> {
@@ -2695,7 +2666,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> {
}

fn clone_from(&mut self, other: &Self) {
SpecCloneFrom::clone_from(self, other)
crate::slice::SpecCloneIntoVec::clone_into(other.as_slice(), self);
}
}

2 changes: 1 addition & 1 deletion src/librustdoc/config.rs
Original file line number Diff line number Diff line change
@@ -509,7 +509,7 @@ impl Options {
// these values up both in `dataset` and in the storage API, so it needs to be able
// to convert the names back and forth. Despite doing this kebab-case to
// StudlyCaps transformation automatically, the JS DOM API does not provide a
// mechanism for doing the just transformation on a string. So we want to avoid
// mechanism for doing just the transformation on a string. So we want to avoid
// the StudlyCaps representation in the `dataset` property.
//
// We solve this by replacing all the `-`s with `_`s. We do that here, when we
4 changes: 2 additions & 2 deletions src/librustdoc/html/static/js/settings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Local js definitions:
/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */
/* global getSettingValue, getVirtualKey, updateLocalStorage, updateTheme */
/* global addClass, removeClass, onEach, onEachLazy, blurHandler, elemIsInParent */
/* global MAIN_ID, getVar, getSettingsButton */

@@ -19,7 +19,7 @@
case "theme":
case "preferred-dark-theme":
case "preferred-light-theme":
updateSystemTheme();
updateTheme();
updateLightAndDark();
break;
case "line-numbers":
112 changes: 52 additions & 60 deletions src/librustdoc/html/static/js/storage.js
Original file line number Diff line number Diff line change
@@ -153,79 +153,74 @@ function switchTheme(styleElem, mainStyleElem, newThemeName, saveTheme) {
}
}

// This function is called from "main.js".
// eslint-disable-next-line no-unused-vars
function useSystemTheme(value) {
if (value === undefined) {
value = true;
}

updateLocalStorage("use-system-theme", value);

// update the toggle if we're on the settings page
const toggle = document.getElementById("use-system-theme");
if (toggle && toggle instanceof HTMLInputElement) {
toggle.checked = value;
}
}

const updateSystemTheme = (function() {
if (!window.matchMedia) {
// fallback to the CSS computed value
return () => {
const cssTheme = getComputedStyle(document.documentElement)
.getPropertyValue("content");

switchTheme(
window.currentTheme,
window.mainTheme,
JSON.parse(cssTheme) || "light",
true
);
const updateTheme = (function() {
/**
* Update the current theme to match whatever the current combination of
* * the preference for using the system theme
* (if this is the case, the value of preferred-light-theme, if the
* system theme is light, otherwise if dark, the value of
* preferred-dark-theme.)
* * the preferred theme
* … dictates that it should be.
*/
function updateTheme() {
const use = (theme, saveTheme) => {
switchTheme(window.currentTheme, window.mainTheme, theme, saveTheme);
};
}

// only listen to (prefers-color-scheme: dark) because light is the default
const mql = window.matchMedia("(prefers-color-scheme: dark)");

function handlePreferenceChange(mql) {
const use = theme => {
switchTheme(window.currentTheme, window.mainTheme, theme, true);
};
// maybe the user has disabled the setting in the meantime!
if (getSettingValue("use-system-theme") !== "false") {
const lightTheme = getSettingValue("preferred-light-theme") || "light";
const darkTheme = getSettingValue("preferred-dark-theme") || "dark";

if (mql.matches) {
use(darkTheme);
if (isDarkMode()) {
use(darkTheme, true);
} else {
// prefers a light theme, or has no preference
use(lightTheme);
use(lightTheme, true);
}
// note: we save the theme so that it doesn't suddenly change when
// the user disables "use-system-theme" and reloads the page or
// navigates to another page
} else {
use(getSettingValue("theme"));
use(getSettingValue("theme"), false);
}
}

mql.addListener(handlePreferenceChange);
// This is always updated below to a function () => bool.
let isDarkMode;

return () => {
handlePreferenceChange(mql);
};
})();
// Determine the function for isDarkMode, and if we have
// `window.matchMedia`, set up an event listener on the preferred color
// scheme.
//
// Otherwise, fall back to the prefers-color-scheme value CSS captured in
// the "content" property.
if (window.matchMedia) {
// only listen to (prefers-color-scheme: dark) because light is the default
const mql = window.matchMedia("(prefers-color-scheme: dark)");

function switchToSavedTheme() {
switchTheme(
window.currentTheme,
window.mainTheme,
getSettingValue("theme") || "light",
false
);
}
isDarkMode = () => mql.matches;

if (mql.addEventListener) {
mql.addEventListener("change", updateTheme);
} else {
// This is deprecated, see:
// https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener
mql.addListener(updateTheme);
}
} else {
// fallback to the CSS computed value
const cssContent = getComputedStyle(document.documentElement)
.getPropertyValue("content");
// (Note: the double-quotes come from that this is a CSS value, which
// might be a length, string, etc.)
const cssColorScheme = cssContent || "\"light\"";
isDarkMode = () => (cssColorScheme === "\"dark\"");
}

return updateTheme;
})();

if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
// update the preferred dark theme if the user is already using a dark theme
@@ -235,13 +230,10 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
&& darkThemes.indexOf(localStoredTheme) >= 0) {
updateLocalStorage("preferred-dark-theme", localStoredTheme);
}

// call the function to initialize the theme at least once!
updateSystemTheme();
} else {
switchToSavedTheme();
}

updateTheme();

if (getSettingValue("source-sidebar-show") === "true") {
// At this point in page load, `document.body` is not available yet.
// Set a class on the `<html>` element instead.
@@ -259,6 +251,6 @@ if (getSettingValue("source-sidebar-show") === "true") {
// specifically when talking to a remote website with no caching.
window.addEventListener("pageshow", ev => {
if (ev.persisted) {
setTimeout(switchToSavedTheme, 0);
setTimeout(updateTheme, 0);
}
});
Original file line number Diff line number Diff line change
@@ -33,11 +33,11 @@
StorageLive(_4); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:13: +3:15
StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
_5 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
_6 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
_6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
- _7 = Lt(_5, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ _7 = Lt(const 3_usize, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ _7 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
}

bb1: {
Original file line number Diff line number Diff line change
@@ -33,11 +33,11 @@
StorageLive(_4); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:13: +3:15
StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
_5 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
_6 = Len((*_1)); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
_6 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
- _7 = Lt(_5, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ _7 = Lt(const 3_usize, _6); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ _7 = const false; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
+ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> bb1; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:18: +3:25
}

bb1: {
1 change: 1 addition & 0 deletions tests/mir-opt/issue_76432.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// compile-flags: -Zmir-enable-passes=-NormalizeArrayLen
// Check that we do not insert StorageDead at each target if StorageDead was never seen

// EMIT_MIR issue_76432.test.SimplifyComparisonIntegral.diff
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@
let _8: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:15: +2:20
let mut _9: usize; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
let mut _10: bool; // in scope 0 at $DIR/lower_array_len.rs:+2:9: +2:21
+ let mut _11: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27

bb0: {
StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
@@ -23,13 +22,10 @@
StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
_7 = &(*_2); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ StorageLive(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ _11 = _7; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
_6 = move _7 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21
- _5 = Len((*_6)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ _5 = Len((*_11)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ StorageDead(_11); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
}

Original file line number Diff line number Diff line change
@@ -16,7 +16,6 @@
let _11: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:15: +4:16
let mut _12: usize; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
let mut _13: bool; // in scope 0 at $DIR/lower_array_len.rs:+4:9: +4:17
+ let mut _14: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27

bb0: {
StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:8: +1:27
@@ -26,13 +25,10 @@
StorageLive(_6); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
StorageLive(_7); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
_7 = &(*_2); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ StorageLive(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ _14 = _7; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
_6 = move _7 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+1:20: +1:21
- _5 = Len((*_6)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ _5 = Len((*_14)); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ StorageDead(_14); // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
+ _5 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:16: +1:27
}

Original file line number Diff line number Diff line change
@@ -6,19 +6,15 @@
let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:52: +0:57
let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14

bb0: {
StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
_3 = &(*_1); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ _4 = _3; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
_2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8
- _0 = Len((*_2)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ _0 = Len((*_4)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
}

Original file line number Diff line number Diff line change
@@ -6,19 +6,15 @@
let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:60: +0:65
let mut _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ let mut _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14

bb0: {
StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
_3 = &_1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ _4 = _3; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
_2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:7: +1:8
- _0 = Len((*_2)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ _0 = Len((*_4)); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
+ _0 = const N; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
goto -> bb1; // scope 0 at $DIR/lower_array_len.rs:+1:5: +1:14
}

50 changes: 50 additions & 0 deletions tests/mir-opt/lower_array_len.array_len_raw.NormalizeArrayLen.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
- // MIR for `array_len_raw` before NormalizeArrayLen
+ // MIR for `array_len_raw` after NormalizeArrayLen

fn array_len_raw(_1: [u8; N]) -> usize {
debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:38: +0:41
let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:55: +0:60
let _2: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12
let mut _3: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25
let _4: &[u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25
let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+3:5: +3:27
let _7: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+3:14: +3:19
scope 1 {
debug arr => _2; // in scope 1 at $DIR/lower_array_len.rs:+1:9: +1:12
let _5: *const [u8]; // in scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12
scope 2 {
debug arr => _5; // in scope 2 at $DIR/lower_array_len.rs:+2:9: +2:12
scope 3 {
}
}
}

bb0: {
StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12
StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25
StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25
_4 = &_1; // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25
_3 = &(*_4); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25
_2 = move _3 as &[u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:21: +1:25
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:24: +1:25
StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:26
StorageLive(_5); // scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12
_5 = &raw const (*_2); // scope 1 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
StorageLive(_6); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27
StorageLive(_7); // scope 2 at $DIR/lower_array_len.rs:+3:14: +3:19
_7 = &(*_5); // scope 3 at $DIR/lower_array_len.rs:+3:14: +3:19
_6 = &(*_7); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27
- _0 = Len((*_6)); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27
+ _0 = const N; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27
goto -> bb1; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:27
}

bb1: {
StorageDead(_6); // scope 2 at $DIR/lower_array_len.rs:+3:26: +3:27
StorageDead(_5); // scope 1 at $DIR/lower_array_len.rs:+4:1: +4:2
StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+4:1: +4:2
StorageDead(_7); // scope 0 at $DIR/lower_array_len.rs:+4:1: +4:2
return; // scope 0 at $DIR/lower_array_len.rs:+4:2: +4:2
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
- // MIR for `array_len_reborrow` before NormalizeArrayLen
+ // MIR for `array_len_reborrow` after NormalizeArrayLen

fn array_len_reborrow(_1: [u8; N]) -> usize {
debug arr => _1; // in scope 0 at $DIR/lower_array_len.rs:+0:43: +0:50
let mut _0: usize; // return place in scope 0 at $DIR/lower_array_len.rs:+0:64: +0:69
let _2: &mut [u8]; // in scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12
let mut _3: &mut [u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33
let mut _4: &mut [u8; N]; // in scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33
let mut _6: &[u8]; // in scope 0 at $DIR/lower_array_len.rs:+3:5: +3:14
scope 1 {
debug arr => _2; // in scope 1 at $DIR/lower_array_len.rs:+1:9: +1:12
let _5: &[u8]; // in scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12
scope 2 {
debug arr => _5; // in scope 2 at $DIR/lower_array_len.rs:+2:9: +2:12
}
}

bb0: {
StorageLive(_2); // scope 0 at $DIR/lower_array_len.rs:+1:9: +1:12
StorageLive(_3); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33
StorageLive(_4); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33
_4 = &mut _1; // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33
_3 = &mut (*_4); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33
_2 = move _3 as &mut [u8] (Pointer(Unsize)); // scope 0 at $DIR/lower_array_len.rs:+1:25: +1:33
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:+1:32: +1:33
StorageDead(_4); // scope 0 at $DIR/lower_array_len.rs:+1:33: +1:34
StorageLive(_5); // scope 1 at $DIR/lower_array_len.rs:+2:9: +2:12
_5 = &(*_2); // scope 1 at $DIR/lower_array_len.rs:+2:15: +2:20
StorageLive(_6); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14
_6 = &(*_5); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14
- _0 = Len((*_6)); // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14
+ _0 = const N; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14
goto -> bb1; // scope 2 at $DIR/lower_array_len.rs:+3:5: +3:14
}

bb1: {
StorageDead(_6); // scope 2 at $DIR/lower_array_len.rs:+3:13: +3:14
StorageDead(_5); // scope 1 at $DIR/lower_array_len.rs:+4:1: +4:2
StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:+4:1: +4:2
return; // scope 0 at $DIR/lower_array_len.rs:+4:2: +4:2
}
}

16 changes: 16 additions & 0 deletions tests/mir-opt/lower_array_len.rs
Original file line number Diff line number Diff line change
@@ -31,10 +31,26 @@ pub fn array_len_by_value<const N: usize>(arr: [u8; N]) -> usize {
arr.len()
}

// EMIT_MIR lower_array_len.array_len_reborrow.NormalizeArrayLen.diff
pub fn array_len_reborrow<const N: usize>(mut arr: [u8; N]) -> usize {
let arr: &mut [_] = &mut arr;
let arr = &*arr;
arr.len()
}

// EMIT_MIR lower_array_len.array_len_raw.NormalizeArrayLen.diff
pub fn array_len_raw<const N: usize>(arr: [u8; N]) -> usize {
let arr: &[_] = &arr;
let arr = std::ptr::addr_of!(*arr);
unsafe { &*arr }.len()
}

fn main() {
let _ = array_bound(3, &[0, 1, 2, 3]);
let mut tmp = [0, 1, 2, 3, 4];
let _ = array_bound_mut(3, &mut [0, 1, 2, 3]);
let _ = array_len(&[0]);
let _ = array_len_by_value([0, 2]);
let _ = array_len_reborrow([0, 2]);
let _ = array_len_raw([0, 2]);
}
41 changes: 0 additions & 41 deletions tests/mir-opt/lower_array_len_e2e.array_bound.PreCodegen.after.mir

This file was deleted.

This file was deleted.

11 changes: 0 additions & 11 deletions tests/mir-opt/lower_array_len_e2e.array_len.PreCodegen.after.mir

This file was deleted.

This file was deleted.

39 changes: 0 additions & 39 deletions tests/mir-opt/lower_array_len_e2e.rs

This file was deleted.

2 changes: 1 addition & 1 deletion tests/ui/parser/bastion-of-the-turbofish.rs
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@

// See https://github.com/rust-lang/rust/pull/53562
// and https://github.com/rust-lang/rfcs/pull/2527
// and https://twitter.com/garblefart/status/1393236602856611843
// and https://web.archive.org/web/20211010063452/https://twitter.com/garblefart/status/1393236602856611843
// for context.

fn main() {