diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index b9474f869ee29..e69719806a81d 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -701,7 +701,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { self.tcx().mk_const( ty::Const { val: ConstValue::Infer(InferConst::Canonical(self.binder_index, var.into())), - ty: const_var.ty, + ty: self.fold_ty(const_var.ty), } ) } diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index 60b1b192d10db..8ed06cbdc7623 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -202,11 +202,7 @@ impl<'a> LintLevelsBuilder<'a> { let meta = unwrap_or!(attr.meta(), continue); attr::mark_used(attr); - let mut metas = if let Some(metas) = meta.meta_item_list() { - metas - } else { - continue; - }; + let mut metas = unwrap_or!(meta.meta_item_list(), continue); if metas.is_empty() { // FIXME (#55112): issue unused-attributes lint for `#[level()]` diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 780b49cd9db0d..108c6c9786b2a 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -431,7 +431,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { place_layout: TyLayout<'tcx>, source_info: SourceInfo, place: &Place<'tcx>, - ) -> Option> { + ) -> Option<()> { let span = source_info.span; let overflow_check = self.tcx.sess.overflow_checks(); @@ -540,20 +540,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - // Work around: avoid extra unnecessary locals. FIXME(wesleywiser) - // Const eval will turn this into a `const Scalar()` that - // `SimplifyLocals` doesn't know it can remove. - Rvalue::Aggregate(_, operands) if operands.len() == 0 => { - return None; - } - _ => { } } self.use_ecx(source_info, |this| { trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place); this.ecx.eval_rvalue_into_place(rvalue, place)?; - this.ecx.eval_place_to_op(place, Some(place_layout)) + Ok(()) }) } @@ -717,16 +710,15 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { base: PlaceBase::Local(local), projection: box [], } = *place { - if let Some(value) = self.const_prop(rval, - place_layout, - statement.source_info, - place) { - trace!("checking whether {:?} can be stored to {:?}", value, local); + let source = statement.source_info; + if let Some(()) = self.const_prop(rval, place_layout, source, place) { if self.can_const_prop[local] { - trace!("stored {:?} to {:?}", value, local); - assert_eq!(self.get_const(local), Some(value)); + trace!("propagated into {:?}", local); if self.should_const_prop() { + let value = + self.get_const(local).expect("local was dead/uninitialized"); + trace!("replacing {:?} with {:?}", rval, value); self.replace_with_const( rval, value, @@ -734,7 +726,7 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> { ); } } else { - trace!("can't propagate {:?} to {:?}", value, local); + trace!("can't propagate into {:?}", local); self.remove_const(local); } } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 606c1a3a1cc09..e41b4678dbd0d 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -31,7 +31,7 @@ use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; use rustc::ty::TyCtxt; use rustc::mir::*; -use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext}; +use rustc::mir::visit::{MutVisitor, Visitor, PlaceContext, MutatingUseContext}; use rustc::session::config::DebugInfo; use std::borrow::Cow; use crate::transform::{MirPass, MirSource}; @@ -293,23 +293,31 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) { pub struct SimplifyLocals; impl<'tcx> MirPass<'tcx> for SimplifyLocals { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { - let mut marker = DeclMarker { locals: BitSet::new_empty(body.local_decls.len()) }; - marker.visit_body(body); - // Return pointer and arguments are always live - marker.locals.insert(RETURN_PLACE); - for arg in body.args_iter() { - marker.locals.insert(arg); - } + fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + trace!("running SimplifyLocals on {:?}", source); + let locals = { + let mut marker = DeclMarker { + locals: BitSet::new_empty(body.local_decls.len()), + body, + }; + marker.visit_body(body); + // Return pointer and arguments are always live + marker.locals.insert(RETURN_PLACE); + for arg in body.args_iter() { + marker.locals.insert(arg); + } - // We may need to keep dead user variables live for debuginfo. - if tcx.sess.opts.debuginfo == DebugInfo::Full { - for local in body.vars_iter() { - marker.locals.insert(local); + // We may need to keep dead user variables live for debuginfo. + if tcx.sess.opts.debuginfo == DebugInfo::Full { + for local in body.vars_iter() { + marker.locals.insert(local); + } } - } - let map = make_local_map(&mut body.local_decls, marker.locals); + marker.locals + }; + + let map = make_local_map(&mut body.local_decls, locals); // Update references to all vars and tmps now LocalUpdater { map }.visit_body(body); body.local_decls.shrink_to_fit(); @@ -334,18 +342,35 @@ fn make_local_map( map } -struct DeclMarker { +struct DeclMarker<'a, 'tcx> { pub locals: BitSet, + pub body: &'a Body<'tcx>, } -impl<'tcx> Visitor<'tcx> for DeclMarker { - fn visit_local(&mut self, local: &Local, ctx: PlaceContext, _: Location) { +impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { + fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) { // Ignore storage markers altogether, they get removed along with their otherwise unused // decls. // FIXME: Extend this to all non-uses. - if !ctx.is_storage_marker() { - self.locals.insert(*local); + if ctx.is_storage_marker() { + return; } + + // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many + // of these locals. However, if the local is still needed, then it will be referenced in + // another place and we'll mark it as being used there. + if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store) { + let stmt = + &self.body.basic_blocks()[location.block].statements[location.statement_index]; + if let StatementKind::Assign(box (p, Rvalue::Use(Operand::Constant(c)))) = &stmt.kind { + if p.as_local().is_some() { + trace!("skipping store of const value {:?} to {:?}", c, local); + return; + } + } + } + + self.locals.insert(*local); } } @@ -357,9 +382,16 @@ impl<'tcx> MutVisitor<'tcx> for LocalUpdater { fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { // Remove unnecessary StorageLive and StorageDead annotations. data.statements.retain(|stmt| { - match stmt.kind { + match &stmt.kind { StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { - self.map[l].is_some() + self.map[*l].is_some() + } + StatementKind::Assign(box (place, _)) => { + if let Some(local) = place.as_local() { + self.map[local].is_some() + } else { + true + } } _ => true } diff --git a/src/librustc_resolve/error_codes.rs b/src/librustc_resolve/error_codes.rs index cd6189c681da1..b82cba8c83dc4 100644 --- a/src/librustc_resolve/error_codes.rs +++ b/src/librustc_resolve/error_codes.rs @@ -1682,7 +1682,7 @@ enum Wizard { } trait Isengard { - fn wizard(w: Wizard) { // error! + fn wizard(w: Wizard) { // ok! match w { Wizard::Saruman => { // do something diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index a8dfe924fdf06..ab8a55660cb0c 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -33,9 +33,72 @@ mod as_keyword { } // /// Exit early from a loop. /// -/// The documentation for this keyword is [not yet complete]. Pull requests welcome! +/// When `break` is encountered, execution of the associated loop body is +/// immediately terminated. +/// +/// ```rust +/// let mut last = 0; +/// +/// for x in 1..100 { +/// if x > 12 { +/// break; +/// } +/// last = x; +/// } +/// +/// assert_eq!(last, 12); +/// println!("{}", last); +/// ``` +/// +/// A break expression is normally associated with the innermost loop enclosing the +/// `break` but a label can be used to specify which enclosing loop is affected. +/// +///```rust +/// 'outer: for i in 1..=5 { +/// println!("outer iteration (i): {}", i); +/// +/// 'inner: for j in 1..=200 { +/// println!(" inner iteration (j): {}", j); +/// if j >= 3 { +/// // breaks from inner loop, let's outer loop continue. +/// break; +/// } +/// if i >= 2 { +/// // breaks from outer loop, and directly to "Bye". +/// break 'outer; +/// } +/// } +/// } +/// println!("Bye."); +///``` +/// +/// When associated with `loop`, a break expression may be used to return a value from that loop. +/// This is only valid with `loop` and not with any other type of loop. +/// If no value is specified, `break;` returns `()`. +/// Every `break` within a loop must return the same type. +/// +/// ```rust +/// let (mut a, mut b) = (1, 1); +/// let result = loop { +/// if b > 10 { +/// break b; +/// } +/// let c = a + b; +/// a = b; +/// b = c; +/// }; +/// // first number in Fibonacci sequence over 10: +/// assert_eq!(result, 13); +/// println!("{}", result); +/// ``` +/// +/// For more details consult the [Reference on "break expression"] and the [Reference on "break and +/// loop values"]. +/// +/// [Reference on "break expression"]: ../reference/expressions/loop-expr.html#break-expressions +/// [Reference on "break and loop values"]: +/// ../reference/expressions/loop-expr.html#break-and-loop-values /// -/// [not yet complete]: https://github.com/rust-lang/rust/issues/34601 mod break_keyword { } #[doc(keyword = "const")] diff --git a/src/test/incremental/const-generics/issue-61338.rs b/src/test/incremental/const-generics/issue-61338.rs new file mode 100644 index 0000000000000..00b3b29698bed --- /dev/null +++ b/src/test/incremental/const-generics/issue-61338.rs @@ -0,0 +1,14 @@ +// revisions:rpass1 + +#![feature(const_generics)] + +struct Struct(T); + +impl Struct<[T; N]> { + fn f() {} + fn g() { Self::f(); } +} + +fn main() { + Struct::<[u32; 3]>::g(); +} diff --git a/src/test/incremental/const-generics/issue-61516.rs b/src/test/incremental/const-generics/issue-61516.rs new file mode 100644 index 0000000000000..a7465b77267a5 --- /dev/null +++ b/src/test/incremental/const-generics/issue-61516.rs @@ -0,0 +1,16 @@ +// revisions:rpass1 + +#![feature(const_generics)] + +struct FakeArray(T); + +impl FakeArray { + fn len(&self) -> usize { + N + } +} + +fn main() { + let fa = FakeArray::(1); + assert_eq!(fa.len(), 32); +} diff --git a/src/test/incremental/const-generics/issue-62536.rs b/src/test/incremental/const-generics/issue-62536.rs new file mode 100644 index 0000000000000..90e279bfc7433 --- /dev/null +++ b/src/test/incremental/const-generics/issue-62536.rs @@ -0,0 +1,12 @@ +// revisions:cfail1 +#![feature(const_generics)] +//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +struct S([T; N]); + +fn f(x: T) -> S { panic!() } + +fn main() { + f(0u8); + //[cfail1]~^ ERROR type annotations needed +} diff --git a/src/test/incremental/const-generics/issue-64087.rs b/src/test/incremental/const-generics/issue-64087.rs new file mode 100644 index 0000000000000..b3c12fbb6e813 --- /dev/null +++ b/src/test/incremental/const-generics/issue-64087.rs @@ -0,0 +1,11 @@ +// revisions:cfail1 +#![feature(const_generics)] +//[cfail1]~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn combinator() -> [T; S] {} +//[cfail1]~^ ERROR mismatched types + +fn main() { + combinator().into_iter(); + //[cfail1]~^ ERROR type annotations needed +} diff --git a/src/test/incremental/const-generics/issue-65623.rs b/src/test/incremental/const-generics/issue-65623.rs new file mode 100644 index 0000000000000..353e323e67b44 --- /dev/null +++ b/src/test/incremental/const-generics/issue-65623.rs @@ -0,0 +1,14 @@ +// revisions:rpass1 +#![feature(const_generics)] + +pub struct Foo([T; 0]); + +impl Foo { + pub fn new() -> Self { + Foo([]) + } +} + +fn main() { + let _: Foo = Foo::new(); +} diff --git a/src/test/incremental/hashes/for_loops.rs b/src/test/incremental/hashes/for_loops.rs index 70820dfaea4a0..8e134ad14fc14 100644 --- a/src/test/incremental/hashes/for_loops.rs +++ b/src/test/incremental/hashes/for_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/incremental/hashes/let_expressions.rs b/src/test/incremental/hashes/let_expressions.rs index 68545b7daaa5c..4e8ba5a209df8 100644 --- a/src/test/incremental/hashes/let_expressions.rs +++ b/src/test/incremental/hashes/let_expressions.rs @@ -22,7 +22,7 @@ pub fn change_name() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="HirBody,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_name() { let _y = 2u64; @@ -86,7 +86,7 @@ pub fn change_mutability_of_slot() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="HirBody,typeck_tables_of,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_mutability_of_slot() { let _x: u64 = 0; @@ -182,7 +182,7 @@ pub fn add_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,typeck_tables_of,mir_built,optimized_mir")] + except="HirBody,typeck_tables_of,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn add_initializer() { let _x: i16 = 3i16; @@ -198,7 +198,7 @@ pub fn change_initializer() { #[cfg(not(cfail1))] #[rustc_clean(cfg="cfail2", - except="HirBody,mir_built,optimized_mir")] + except="HirBody,mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_initializer() { let _x = 5u16; diff --git a/src/test/incremental/hashes/loop_expressions.rs b/src/test/incremental/hashes/loop_expressions.rs index a2222db4c59ad..ca85ee39e3671 100644 --- a/src/test/incremental/hashes/loop_expressions.rs +++ b/src/test/incremental/hashes/loop_expressions.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_let_loops.rs b/src/test/incremental/hashes/while_let_loops.rs index da3c957741fb2..1e628d019196b 100644 --- a/src/test/incremental/hashes/while_let_loops.rs +++ b/src/test/incremental/hashes/while_let_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs index 3be42e7a4ee7a..295c2244879f8 100644 --- a/src/test/incremental/hashes/while_loops.rs +++ b/src/test/incremental/hashes/while_loops.rs @@ -25,7 +25,7 @@ pub fn change_loop_body() { } #[cfg(not(cfail1))] -#[rustc_clean(cfg="cfail2", except="HirBody, mir_built, optimized_mir")] +#[rustc_clean(cfg="cfail2", except="HirBody, mir_built")] #[rustc_clean(cfg="cfail3")] pub fn change_loop_body() { let mut _x = 0; diff --git a/src/test/mir-opt/simplify-locals-removes-unused-consts.rs b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs new file mode 100644 index 0000000000000..6f03438ff7234 --- /dev/null +++ b/src/test/mir-opt/simplify-locals-removes-unused-consts.rs @@ -0,0 +1,89 @@ +// compile-flags: -C overflow-checks=no + +fn use_zst(_: ((), ())) { } + +struct Temp { + x: u8 +} + +fn use_u8(_: u8) { } + +fn main() { + let ((), ()) = ((), ()); + use_zst(((), ())); + + use_u8((Temp { x : 40 }).x + 2); +} + +// END RUST SOURCE + +// START rustc.main.SimplifyLocals.before.mir +// let mut _0: (); +// let mut _1: ((), ()); +// let mut _2: (); +// let mut _3: (); +// let _4: (); +// let mut _5: ((), ()); +// let mut _6: (); +// let mut _7: (); +// let _8: (); +// let mut _9: u8; +// let mut _10: u8; +// let mut _11: Temp; +// scope 1 { +// } +// bb0: { +// StorageLive(_1); +// StorageLive(_2); +// _2 = const Scalar() : (); +// StorageLive(_3); +// _3 = const Scalar() : (); +// _1 = const Scalar() : ((), ()); +// StorageDead(_3); +// StorageDead(_2); +// StorageDead(_1); +// StorageLive(_4); +// StorageLive(_6); +// _6 = const Scalar() : (); +// StorageLive(_7); +// _7 = const Scalar() : (); +// StorageDead(_7); +// StorageDead(_6); +// _4 = const use_zst(const Scalar() : ((), ())) -> bb1; +// } +// bb1: { +// StorageDead(_4); +// StorageLive(_8); +// StorageLive(_10); +// StorageLive(_11); +// _11 = const Scalar(0x28) : Temp; +// _10 = const 40u8; +// StorageDead(_10); +// _8 = const use_u8(const 42u8) -> bb2; +// } +// bb2: { +// StorageDead(_11); +// StorageDead(_8); +// return; +// } +// END rustc.main.SimplifyLocals.before.mir +// START rustc.main.SimplifyLocals.after.mir +// let mut _0: (); +// let _1: (); +// let _2: (); +// scope 1 { +// } +// bb0: { +// StorageLive(_1); +// _1 = const use_zst(const Scalar() : ((), ())) -> bb1; +// } +// bb1: { +// StorageDead(_1); +// StorageLive(_2); +// _2 = const use_u8(const 42u8) -> bb2; +// } +// bb2: { +// StorageDead(_2); +// return; +// } +// END rustc.main.SimplifyLocals.after.mir diff --git a/src/test/mir-opt/slice-drop-shim.rs b/src/test/mir-opt/slice-drop-shim.rs index 754fad51b21e7..f270dec5fe232 100644 --- a/src/test/mir-opt/slice-drop-shim.rs +++ b/src/test/mir-opt/slice-drop-shim.rs @@ -1,5 +1,7 @@ +// compile-flags: -Zmir-opt-level=0 + fn main() { - std::ptr::drop_in_place::<[String]> as unsafe fn(_); + let _fn = std::ptr::drop_in_place::<[String]> as unsafe fn(_); } // END RUST SOURCE