diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index b16c1436a1cbe..182b317075d72 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -45,6 +45,7 @@ pub mod generator; pub mod inline; pub mod lower_128bit; pub mod uniform_array_move_out; +pub mod split_local_fields; pub(crate) fn provide(providers: &mut Providers) { self::qualify_consts::provide(providers); @@ -265,6 +266,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx instcombine::InstCombine, deaggregator::Deaggregator, + split_local_fields::SplitLocalFields, copy_prop::CopyPropagation, remove_noop_landing_pads::RemoveNoopLandingPads, simplify::SimplifyCfg::new("final"), diff --git a/src/librustc_mir/transform/split_local_fields.rs b/src/librustc_mir/transform/split_local_fields.rs new file mode 100644 index 0000000000000..b0d596f81bdc4 --- /dev/null +++ b/src/librustc_mir/transform/split_local_fields.rs @@ -0,0 +1,429 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir; +use rustc::ty::{self, TyCtxt, Ty}; +use rustc::ty::util::IntTypeExt; +use rustc::middle::const_val::ConstVal; +use rustc::mir::*; +use rustc::mir::visit::{PlaceContext, MutVisitor, Visitor}; +use rustc::session::config::FullDebugInfo; +use rustc_const_math::ConstInt; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use std::collections::BTreeMap; +use syntax_pos::Span; +use transform::{MirPass, MirSource}; + +pub struct SplitLocalFields; + +impl MirPass for SplitLocalFields { + fn run_pass<'a, 'tcx>(&self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + source: MirSource, + mir: &mut Mir<'tcx>) { + // Don't run on constant MIR, because trans might not be able to + // evaluate the modified MIR. + // FIXME(eddyb) Remove check after miri is merged. + let id = tcx.hir.as_local_node_id(source.def_id).unwrap(); + match (tcx.hir.body_owner_kind(id), source.promoted) { + (_, Some(_)) | + (hir::BodyOwnerKind::Const, _) | + (hir::BodyOwnerKind::Static(_), _) => return, + + (hir::BodyOwnerKind::Fn, _) => { + if tcx.is_const_fn(source.def_id) { + // Don't run on const functions, as, again, trans might not be able to evaluate + // the optimized IR. + return + } + } + } + + let mut collector = LocalPathCollector { + locals: mir.local_decls.iter().map(|decl| { + LocalPath::new(decl.ty) + }).collect() + }; + + // Can't split return and arguments. + collector.locals[RETURN_PLACE].make_opaque(); + for arg in mir.args_iter() { + collector.locals[arg].make_opaque(); + } + + // We need to keep user variables intact for debuginfo. + if tcx.sess.opts.debuginfo == FullDebugInfo { + for local in mir.vars_iter() { + collector.locals[local].make_opaque(); + } + } + + collector.visit_mir(mir); + + let replacements = collector.locals.iter_enumerated_mut().map(|(local, root)| { + // Don't rename locals that are entirely opaque. + match root.interior { + LocalPathInterior::Opaque { .. } => local.index()..local.index()+1, + LocalPathInterior::Split { .. } => { + let orig_decl = mir.local_decls[local].clone(); + let first = mir.local_decls.len(); + root.split_into_locals(tcx, &mut mir.local_decls, &orig_decl); + first..mir.local_decls.len() + } + } + }).collect::>(); + + // Expand `Storage{Live,Dead}` statements to refer to the replacement locals. + for bb in mir.basic_blocks_mut() { + bb.expand_statements(|stmt| { + let (local, is_live) = match stmt.kind { + StatementKind::StorageLive(local) => (local, true), + StatementKind::StorageDead(local) => (local, false), + _ => return None + }; + let range = replacements[local].clone(); + let source_info = stmt.source_info; + Some(range.map(move |i| { + let new_local = Local::new(i); + Statement { + source_info, + kind: if is_live { + StatementKind::StorageLive(new_local) + } else { + StatementKind::StorageDead(new_local) + } + } + })) + }); + } + drop(replacements); + + // Lastly, replace all the opaque paths with their new locals. + let mut replacer = LocalPathReplacer { + tcx, + span: mir.span, + locals: collector.locals + }; + replacer.visit_mir(mir); + } +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +struct VariantField { + variant_index: usize, + field: u64 +} + +struct LocalPath<'tcx> { + ty: Ty<'tcx>, + interior: LocalPathInterior<'tcx> +} + +enum LocalPathInterior<'tcx> { + /// This path needs to remain self-contained, e.g. due to accesses / borrows. + Opaque { + replacement_local: Option + }, + + /// This path' can be split into separate locals for its fields. + Split { + discr_local: Option, + fields: BTreeMap> + } +} + +impl<'a, 'tcx> LocalPath<'tcx> { + fn new(ty: Ty<'tcx>) -> Self { + let mut path = LocalPath { + ty, + interior: LocalPathInterior::Split { + discr_local: None, + fields: BTreeMap::new() + } + }; + + if let ty::TyAdt(adt_def, _) = ty.sty { + // Unions have (observably) overlapping members, so don't split them. + if adt_def.is_union() { + path.make_opaque(); + } + } + + path + } + + fn make_opaque(&mut self) { + if let LocalPathInterior::Split { .. } = self.interior { + self.interior = LocalPathInterior::Opaque { + replacement_local: None + }; + } + } + + fn project(&mut self, elem: &PlaceElem<'tcx>, variant_index: usize) -> Option<&mut Self> { + match *elem { + ProjectionElem::Field(f, ty) => { + if let LocalPathInterior::Split { ref mut fields, .. } = self.interior { + let field = VariantField { + variant_index, + field: f.index() as u64 + }; + return Some(fields.entry(field).or_insert(LocalPath::new(ty))); + } + } + ProjectionElem::Downcast(..) => { + bug!("should be handled by the caller of `LocalPath::project`"); + } + // FIXME(eddyb) support indexing by constants. + ProjectionElem::ConstantIndex { .. } | + ProjectionElem::Subslice { .. } => {} + // Can't support without alias analysis. + ProjectionElem::Index(_) | + ProjectionElem::Deref => {} + } + + // If we can't project, we must be opaque. + self.make_opaque(); + None + } + + fn split_into_locals(&mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + local_decls: &mut IndexVec>, + base_decl: &LocalDecl<'tcx>) { + match self.interior { + LocalPathInterior::Opaque { ref mut replacement_local } => { + let mut decl = base_decl.clone(); + decl.ty = self.ty; + decl.name = None; + decl.is_user_variable = false; + *replacement_local = Some(local_decls.push(decl)); + } + LocalPathInterior::Split { + ref mut discr_local, + ref mut fields + } => { + if let ty::TyAdt(adt_def, _) = self.ty.sty { + if adt_def.is_enum() { + let discr_ty = adt_def.repr.discr_type().to_ty(tcx); + let mut decl = base_decl.clone(); + decl.ty = discr_ty; + decl.name = None; + decl.is_user_variable = false; + *discr_local = Some(local_decls.push(decl)); + } + } + for field in fields.values_mut() { + field.split_into_locals(tcx, local_decls, base_decl); + } + } + } + } +} + +struct LocalPathCollector<'tcx> { + locals: IndexVec> +} + +impl<'tcx> LocalPathCollector<'tcx> { + fn place_path(&mut self, place: &Place<'tcx>) -> Option<&mut LocalPath<'tcx>> { + match *place { + Place::Local(local) => Some(&mut self.locals[local]), + Place::Static(_) => None, + Place::Projection(box ref proj) => { + let (base, variant_index) = match proj.base { + Place::Projection(box Projection { + ref base, + elem: ProjectionElem::Downcast(_, variant_index) + }) => (base, variant_index), + ref base => (base, 0), + }; + + // Locals used as indices shouldn't be optimized. + if let ProjectionElem::Index(i) = proj.elem { + self.locals[i].make_opaque(); + } + + self.place_path(base)?.project(&proj.elem, variant_index) + } + } + } +} + +impl<'tcx> Visitor<'tcx> for LocalPathCollector<'tcx> { + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _: Location) { + if context.is_use() { + if let Some(path) = self.place_path(place) { + path.make_opaque(); + } + } + } + + // Special-case `(Set)Discriminant(place)` to not mark `place` as opaque. + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + if let Rvalue::Discriminant(ref place) = *rvalue { + self.place_path(place); + return; + } + self.super_rvalue(rvalue, location); + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &Statement<'tcx>, + location: Location) { + if let StatementKind::SetDiscriminant { ref place, .. } = statement.kind { + self.place_path(place); + return; + } + self.super_statement(block, statement, location); + } +} + +struct LocalPathReplacer<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + span: Span, + locals: IndexVec> +} + +impl<'a, 'tcx> LocalPathReplacer<'a, 'tcx> { + fn replace(&mut self, place: &mut Place<'tcx>) -> Option<&mut LocalPath<'tcx>> { + let path = match *place { + Place::Local(ref mut local) => { + let path = &mut self.locals[*local]; + match path.interior { + LocalPathInterior::Opaque { replacement_local } => { + *local = replacement_local.unwrap_or(*local); + } + _ => {} + } + return Some(path); + } + Place::Static(_) => return None, + Place::Projection(box ref mut proj) => { + let (base, variant_index) = match proj.base { + Place::Projection(box Projection { + ref mut base, + elem: ProjectionElem::Downcast(_, variant_index) + }) => (base, variant_index), + ref mut base => (base, 0) + }; + self.replace(base)?.project(&proj.elem, variant_index)? + } + }; + match path.interior { + LocalPathInterior::Opaque { replacement_local } => { + *place = Place::Local(replacement_local.expect("missing replacement")); + } + _ => {} + } + Some(path) + } +} + +impl<'a, 'tcx> MutVisitor<'tcx> for LocalPathReplacer<'a, 'tcx> { + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _: Location) { + self.replace(place); + } + + // Special-case `(Set)Discriminant(place)` to use `discr_local` for `place`. + fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { + let tcx = self.tcx; + let span = self.span; + + let mut replacement = None; + if let Rvalue::Discriminant(ref mut place) = rvalue { + if let Some(path) = self.replace(place) { + if let LocalPathInterior::Split { discr_local, .. } = path.interior { + if let ty::TyAdt(adt_def, _) = path.ty.sty { + if adt_def.is_enum() { + let discr_place = + Place::Local(discr_local.expect("missing discriminant")); + replacement = Some(Rvalue::Use(Operand::Copy(discr_place))); + } + } + + // Non-enums don't have discriminants other than `0u8`. + if replacement.is_none() { + let discr = tcx.mk_const(ty::Const { + val: ConstVal::Integral(ConstInt::U8(0)), + ty: tcx.types.u8 + }); + replacement = Some(Rvalue::Use(Operand::Constant(box Constant { + span, + ty: discr.ty, + literal: Literal::Value { + value: discr + }, + }))); + } + } + } + } + // HACK(eddyb) clean this double matching post-NLL. + if let Rvalue::Discriminant(_) = rvalue { + if let Some(replacement) = replacement { + *rvalue = replacement; + } + return; + } + self.super_rvalue(rvalue, location); + } + + fn visit_statement(&mut self, + block: BasicBlock, + statement: &mut Statement<'tcx>, + location: Location) { + self.span = statement.source_info.span; + + let tcx = self.tcx; + let span = self.span; + + let mut replacement = None; + if let StatementKind::SetDiscriminant { ref mut place, variant_index } = statement.kind { + if let Some(path) = self.replace(place) { + if let LocalPathInterior::Split { discr_local, .. } = path.interior { + if let ty::TyAdt(adt_def, _) = path.ty.sty { + if adt_def.is_enum() { + let discr_place = + Place::Local(discr_local.expect("missing discriminant")); + let discr = adt_def.discriminant_for_variant(tcx, variant_index); + let discr = tcx.mk_const(ty::Const { + val: ConstVal::Integral(discr), + ty: adt_def.repr.discr_type().to_ty(tcx) + }); + let discr = Rvalue::Use(Operand::Constant(box Constant { + span, + ty: discr.ty, + literal: Literal::Value { + value: discr + }, + })); + replacement = Some(StatementKind::Assign(discr_place, discr)); + } + } + + // Non-enums don't have discriminants to set. + if replacement.is_none() { + replacement = Some(StatementKind::Nop); + } + } + } + } + // HACK(eddyb) clean this double matching post-NLL. + if let StatementKind::SetDiscriminant { .. } = statement.kind { + if let Some(replacement) = replacement { + statement.kind = replacement; + } + return; + } + self.super_statement(block, statement, location); + } +} diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index f683703ce6d53..abeb1238e2573 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -12,6 +12,7 @@ //! which do not. use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::control_flow_graph::dominators::Dominators; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use rustc::middle::const_val::ConstVal; use rustc::mir::{self, Location, TerminatorKind, Literal}; @@ -22,7 +23,7 @@ use rustc::ty::layout::LayoutOf; use type_of::LayoutLlvmExt; use super::FunctionCx; -pub fn memory_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { +pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { let mir = fx.mir; let mut analyzer = LocalAnalyzer::new(fx); @@ -44,43 +45,60 @@ pub fn memory_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { // (e.g. structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways // (gep vs extractvalue etc). - analyzer.mark_as_memory(mir::Local::new(index)); + analyzer.not_ssa(mir::Local::new(index)); } } - analyzer.memory_locals + analyzer.non_ssa_locals } struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { fx: &'mir FunctionCx<'a, 'tcx>, - memory_locals: BitVector, - seen_assigned: BitVector + dominators: Dominators, + non_ssa_locals: BitVector, + // The location of the first visited direct assignment to each + // local, or an invalid location (out of bounds `block` index). + first_assignment: IndexVec } impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { fn new(fx: &'mir FunctionCx<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { + let invalid_location = + mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location(); let mut analyzer = LocalAnalyzer { fx, - memory_locals: BitVector::new(fx.mir.local_decls.len()), - seen_assigned: BitVector::new(fx.mir.local_decls.len()) + dominators: fx.mir.dominators(), + non_ssa_locals: BitVector::new(fx.mir.local_decls.len()), + first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls) }; // Arguments get assigned to by means of the function being called - for idx in 0..fx.mir.arg_count { - analyzer.seen_assigned.insert(idx + 1); + for arg in fx.mir.args_iter() { + analyzer.first_assignment[arg] = mir::START_BLOCK.start_location(); } analyzer } - fn mark_as_memory(&mut self, local: mir::Local) { - debug!("marking {:?} as memory", local); - self.memory_locals.insert(local.index()); + fn first_assignment(&self, local: mir::Local) -> Option { + let location = self.first_assignment[local]; + if location.block.index() < self.fx.mir.basic_blocks().len() { + Some(location) + } else { + None + } } - fn mark_assigned(&mut self, local: mir::Local) { - if !self.seen_assigned.insert(local.index()) { - self.mark_as_memory(local); + fn not_ssa(&mut self, local: mir::Local) { + debug!("marking {:?} as non-SSA", local); + self.non_ssa_locals.insert(local.index()); + } + + fn assign(&mut self, local: mir::Local, location: Location) { + if self.first_assignment(local).is_some() { + self.not_ssa(local); + } else { + self.first_assignment[local] = location; } } } @@ -94,9 +112,9 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); if let mir::Place::Local(index) = *place { - self.mark_assigned(index); + self.assign(index, location); if !self.fx.rvalue_creates_operand(rvalue) { - self.mark_as_memory(index); + self.not_ssa(index); } } else { self.visit_place(place, PlaceContext::Store, location); @@ -160,7 +178,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { // Recurse with the same context, instead of `Projection`, // potentially stopping at non-operand projections, - // which would trigger `mark_as_memory` on locals. + // which would trigger `not_ssa` on locals. self.visit_place(&proj.base, context, location); return; } @@ -177,35 +195,50 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { } fn visit_local(&mut self, - &index: &mir::Local, + &local: &mir::Local, context: PlaceContext<'tcx>, - _: Location) { + location: Location) { match context { PlaceContext::Call => { - self.mark_assigned(index); + self.assign(local, location); } PlaceContext::StorageLive | PlaceContext::StorageDead | - PlaceContext::Validate | + PlaceContext::Validate => {} + PlaceContext::Copy | - PlaceContext::Move => {} + PlaceContext::Move => { + // Reads from uninitialized variables (e.g. in dead code, after + // optimizations) require locals to be in (uninitialized) memory. + // NB: there can be uninitialized reads of a local visited after + // an assignment to that local, if they happen on disjoint paths. + let ssa_read = match self.first_assignment(local) { + Some(assignment_location) => { + assignment_location.dominates(&location, &self.dominators) + } + None => false + }; + if !ssa_read { + self.not_ssa(local); + } + } PlaceContext::Inspect | PlaceContext::Store | PlaceContext::AsmOutput | PlaceContext::Borrow { .. } | PlaceContext::Projection(..) => { - self.mark_as_memory(index); + self.not_ssa(local); } PlaceContext::Drop => { - let ty = mir::Place::Local(index).ty(self.fx.mir, self.fx.cx.tcx); + let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); // Only need the place if we're actually dropping it. if self.fx.cx.type_needs_drop(ty) { - self.mark_as_memory(index); + self.not_ssa(local); } } } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index da01592d9118a..9de70dfc0d002 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -244,7 +244,7 @@ pub fn trans_mir<'a, 'tcx: 'a>( }, }; - let memory_locals = analyze::memory_locals(&fx); + let memory_locals = analyze::non_ssa_locals(&fx); // Allocate variable and temp allocas fx.locals = { diff --git a/src/test/compile-fail/huge-enum.rs b/src/test/compile-fail/huge-enum.rs index 6e7c05370b99d..63478ec3136f9 100644 --- a/src/test/compile-fail/huge-enum.rs +++ b/src/test/compile-fail/huge-enum.rs @@ -14,10 +14,10 @@ #[cfg(target_pointer_width = "32")] fn main() { - let big: Option<[u32; (1<<29)-1]> = None; + let ref big: Option<[u32; (1<<29)-1]> = None; } #[cfg(target_pointer_width = "64")] fn main() { - let big: Option<[u32; (1<<45)-1]> = None; + let ref big: Option<[u32; (1<<45)-1]> = None; } diff --git a/src/test/compile-fail/huge-struct.rs b/src/test/compile-fail/huge-struct.rs index a10c61d6606d0..c9c077dfbbce9 100644 --- a/src/test/compile-fail/huge-struct.rs +++ b/src/test/compile-fail/huge-struct.rs @@ -50,5 +50,5 @@ struct S1k { val: S32> } struct S1M { val: S1k> } fn main() { - let fat: Option>>> = None; + let ref fat: Option>>> = None; } diff --git a/src/test/run-pass/mir_raw_fat_ptr.rs b/src/test/run-pass/mir_raw_fat_ptr.rs index 846318ec4fd34..fa8cd9e1c0c77 100644 --- a/src/test/run-pass/mir_raw_fat_ptr.rs +++ b/src/test/run-pass/mir_raw_fat_ptr.rs @@ -149,9 +149,9 @@ fn main() { assert_inorder(buf, compare_foo); // check ordering for structs containing arrays - let ss: (S<[u8; 2]>, - S<[u8; 3]>, - S<[u8; 2]>) = ( + let ref ss: (S<[u8; 2]>, + S<[u8; 3]>, + S<[u8; 2]>) = ( S(7, [8, 9]), S(10, [11, 12, 13]), S(4, [5, 6]) diff --git a/src/test/run-pass/raw-fat-ptr.rs b/src/test/run-pass/raw-fat-ptr.rs index b4572f4577133..b98fa41309fc9 100644 --- a/src/test/run-pass/raw-fat-ptr.rs +++ b/src/test/run-pass/raw-fat-ptr.rs @@ -112,9 +112,9 @@ fn main() { assert_inorder(buf); // check ordering for structs containing arrays - let ss: (S<[u8; 2]>, - S<[u8; 3]>, - S<[u8; 2]>) = ( + let ref ss: (S<[u8; 2]>, + S<[u8; 3]>, + S<[u8; 2]>) = ( S(7, [8, 9]), S(10, [11, 12, 13]), S(4, [5, 6]) diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs index 7f234e243e926..5810f27944dc9 100644 --- a/src/test/ui/print_type_sizes/niche-filling.rs +++ b/src/test/ui/print_type_sizes/niche-filling.rs @@ -86,8 +86,8 @@ fn start(_: isize, _: *const *const u8) -> isize { let _b: MyOption = Default::default(); let _c: MyOption = Default::default(); let _b: MyOption> = Default::default(); - let _e: Enum4<(), char, (), ()> = Enum4::One(()); - let _f: Enum4<(), (), bool, ()> = Enum4::One(()); - let _g: Enum4<(), (), (), MyOption> = Enum4::One(()); + let ref _e: Enum4<(), char, (), ()> = Enum4::One(()); + let ref _f: Enum4<(), (), bool, ()> = Enum4::One(()); + let ref _g: Enum4<(), (), (), MyOption> = Enum4::One(()); 0 } diff --git a/src/test/ui/print_type_sizes/no_duplicates.rs b/src/test/ui/print_type_sizes/no_duplicates.rs index d9b90260364b2..2fb1d39589b64 100644 --- a/src/test/ui/print_type_sizes/no_duplicates.rs +++ b/src/test/ui/print_type_sizes/no_duplicates.rs @@ -20,11 +20,11 @@ pub struct SevenBytes([u8; 7]); pub fn f1() { - let _s: SevenBytes = SevenBytes([0; 7]); + let ref _s: SevenBytes = SevenBytes([0; 7]); } #[start] fn start(_: isize, _: *const *const u8) -> isize { - let _s: SevenBytes = SevenBytes([0; 7]); + let ref _s: SevenBytes = SevenBytes([0; 7]); 0 } diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs index 4d0396903e555..9e53ae56d6800 100644 --- a/src/test/ui/print_type_sizes/uninhabited.rs +++ b/src/test/ui/print_type_sizes/uninhabited.rs @@ -16,7 +16,7 @@ #[start] fn start(_: isize, _: *const *const u8) -> isize { - let _x: Option = None; - let _y: Result = Ok(42); + let ref _x: Option = None; + let ref _y: Result = Ok(42); 0 }