From b4993ec8635a48d4467a69482f77c6370a9709df Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Thu, 5 Jan 2017 23:17:12 +0000 Subject: [PATCH 01/10] suggest doubling recursion limit in more situations --- src/librustc_typeck/check/autoderef.rs | 4 ++ src/libsyntax/ext/base.rs | 14 +++-- src/test/compile-fail/recursion_limit.rs | 57 ----------------- src/test/ui/did_you_mean/recursion_limit.rs | 45 ++++++++++++++ .../ui/did_you_mean/recursion_limit.stderr | 21 +++++++ .../ui/did_you_mean/recursion_limit_deref.rs | 62 +++++++++++++++++++ .../did_you_mean/recursion_limit_deref.stderr | 23 +++++++ .../ui/did_you_mean/recursion_limit_macro.rs | 26 ++++++++ .../did_you_mean/recursion_limit_macro.stderr | 11 ++++ 9 files changed, 202 insertions(+), 61 deletions(-) delete mode 100644 src/test/compile-fail/recursion_limit.rs create mode 100644 src/test/ui/did_you_mean/recursion_limit.rs create mode 100644 src/test/ui/did_you_mean/recursion_limit.stderr create mode 100644 src/test/ui/did_you_mean/recursion_limit_deref.rs create mode 100644 src/test/ui/did_you_mean/recursion_limit_deref.stderr create mode 100644 src/test/ui/did_you_mean/recursion_limit_macro.rs create mode 100644 src/test/ui/did_you_mean/recursion_limit_macro.stderr diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index b4647df3f4f0a..ca0ab8f1e8c77 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -54,12 +54,16 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> { if self.steps.len() == tcx.sess.recursion_limit.get() { // We've reached the recursion limit, error gracefully. + let suggested_limit = tcx.sess.recursion_limit.get() * 2; struct_span_err!(tcx.sess, self.span, E0055, "reached the recursion limit while auto-dereferencing {:?}", self.cur_ty) .span_label(self.span, &format!("deref recursion limit reached")) + .help(&format!( + "consider adding a `#[recursion_limit=\"{}\"]` attribute to your crate", + suggested_limit)) .emit(); return None; } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 9a717b86d091e..e7f794328b893 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -14,7 +14,7 @@ use ast::{self, Attribute, Name, PatKind, MetaItem}; use attr::HasAttrs; use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; -use errors::DiagnosticBuilder; +use errors::{DiagnosticBuilder, FatalError}; use ext::expand::{self, Expansion}; use ext::hygiene::Mark; use fold::{self, Folder}; @@ -674,9 +674,15 @@ impl<'a> ExtCtxt<'a> { pub fn bt_push(&mut self, ei: ExpnInfo) { if self.current_expansion.depth > self.ecfg.recursion_limit { - self.span_fatal(ei.call_site, - &format!("recursion limit reached while expanding the macro `{}`", - ei.callee.name())); + let suggested_limit = self.ecfg.recursion_limit * 2; + let mut err = self.struct_span_fatal(ei.call_site, + &format!("recursion limit reached while expanding the macro `{}`", + ei.callee.name())); + err.note(&format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", + suggested_limit)); + err.emit(); + panic!(FatalError); } let mut call_site = ei.call_site; diff --git a/src/test/compile-fail/recursion_limit.rs b/src/test/compile-fail/recursion_limit.rs deleted file mode 100644 index 226a6d57ddbf0..0000000000000 --- a/src/test/compile-fail/recursion_limit.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2014 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. - -// Test that the recursion limit can be changed. In this case, we have -// deeply nested types that will fail the `Send` check by overflow -// when the recursion limit is set very low. - -#![allow(dead_code)] -#![recursion_limit="10"] - -macro_rules! link { - ($id:ident, $t:ty) => { - enum $id { $id($t) } - } -} - -link! { A, B } -link! { B, C } -link! { C, D } -link! { D, E } -link! { E, F } -link! { F, G } -link! { G, H } -link! { H, I } -link! { I, J } -link! { J, K } -link! { K, L } -link! { L, M } -link! { M, N } - -enum N { N(usize) } - -fn is_send() { } - -fn main() { - is_send::(); - //~^ ERROR overflow evaluating - //~| NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate - //~| NOTE required because it appears within the type `A` - //~| NOTE required because it appears within the type `B` - //~| NOTE required because it appears within the type `C` - //~| NOTE required because it appears within the type `D` - //~| NOTE required because it appears within the type `E` - //~| NOTE required because it appears within the type `F` - //~| NOTE required because it appears within the type `G` - //~| NOTE required because it appears within the type `H` - //~| NOTE required because it appears within the type `I` - //~| NOTE required because it appears within the type `J` - //~| NOTE required by `is_send` -} diff --git a/src/test/ui/did_you_mean/recursion_limit.rs b/src/test/ui/did_you_mean/recursion_limit.rs new file mode 100644 index 0000000000000..becb81b1fff7e --- /dev/null +++ b/src/test/ui/did_you_mean/recursion_limit.rs @@ -0,0 +1,45 @@ +// Copyright 2014 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. + +// Test that the recursion limit can be changed and that the compiler +// suggests a fix. In this case, we have deeply nested types that will +// fail the `Send` check by overflow when the recursion limit is set +// very low. + +#![allow(dead_code)] +#![recursion_limit="10"] + +macro_rules! link { + ($id:ident, $t:ty) => { + enum $id { $id($t) } + } +} + +link! { A, B } +link! { B, C } +link! { C, D } +link! { D, E } +link! { E, F } +link! { F, G } +link! { G, H } +link! { H, I } +link! { I, J } +link! { J, K } +link! { K, L } +link! { L, M } +link! { M, N } + +enum N { N(usize) } + +fn is_send() { } + +fn main() { + is_send::(); +} diff --git a/src/test/ui/did_you_mean/recursion_limit.stderr b/src/test/ui/did_you_mean/recursion_limit.stderr new file mode 100644 index 0000000000000..524aab87ccfe2 --- /dev/null +++ b/src/test/ui/did_you_mean/recursion_limit.stderr @@ -0,0 +1,21 @@ +error[E0275]: overflow evaluating the requirement `K: std::marker::Send` + --> $DIR/recursion_limit.rs:44:5 + | +44 | is_send::(); + | ^^^^^^^^^^^^ + | + = note: consider adding a `#![recursion_limit="20"]` attribute to your crate + = note: required because it appears within the type `J` + = note: required because it appears within the type `I` + = note: required because it appears within the type `H` + = note: required because it appears within the type `G` + = note: required because it appears within the type `F` + = note: required because it appears within the type `E` + = note: required because it appears within the type `D` + = note: required because it appears within the type `C` + = note: required because it appears within the type `B` + = note: required because it appears within the type `A` + = note: required by `is_send` + +error: aborting due to previous error + diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.rs b/src/test/ui/did_you_mean/recursion_limit_deref.rs new file mode 100644 index 0000000000000..ebc56c94adf84 --- /dev/null +++ b/src/test/ui/did_you_mean/recursion_limit_deref.rs @@ -0,0 +1,62 @@ +// Copyright 2017 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. + +// Test that the recursion limit can be changed and that the compiler +// suggests a fix. In this case, we have a long chain of Deref impls +// which will cause an overflow during the autoderef loop. + +#![allow(dead_code)] +#![recursion_limit="10"] + +macro_rules! link { + ($outer:ident, $inner:ident) => { + struct $outer($inner); + + impl $outer { + fn new() -> $outer { + $outer($inner::new()) + } + } + + impl std::ops::Deref for $outer { + type Target = $inner; + + fn deref(&self) -> &$inner { + &self.0 + } + } + } +} + +struct Bottom; +impl Bottom { + fn new() -> Bottom { + Bottom + } +} + +link!(Top, A); +link!(A, B); +link!(B, C); +link!(C, D); +link!(D, E); +link!(E, F); +link!(F, G); +link!(G, H); +link!(H, I); +link!(I, J); +link!(J, K); +link!(K, Bottom); + +fn main() { + let t = Top::new(); + let x: &Bottom = &t; +} + diff --git a/src/test/ui/did_you_mean/recursion_limit_deref.stderr b/src/test/ui/did_you_mean/recursion_limit_deref.stderr new file mode 100644 index 0000000000000..57b28d0373622 --- /dev/null +++ b/src/test/ui/did_you_mean/recursion_limit_deref.stderr @@ -0,0 +1,23 @@ +error[E0055]: reached the recursion limit while auto-dereferencing I + --> $DIR/recursion_limit_deref.rs:60:22 + | +60 | let x: &Bottom = &t; + | ^^ deref recursion limit reached + | + = help: consider adding a `#[recursion_limit="20"]` attribute to your crate + +error[E0055]: reached the recursion limit while auto-dereferencing I + | + = help: consider adding a `#[recursion_limit="20"]` attribute to your crate + +error[E0308]: mismatched types + --> $DIR/recursion_limit_deref.rs:60:22 + | +60 | let x: &Bottom = &t; + | ^^ expected struct `Bottom`, found struct `Top` + | + = note: expected type `&Bottom` + found type `&Top` + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/did_you_mean/recursion_limit_macro.rs b/src/test/ui/did_you_mean/recursion_limit_macro.rs new file mode 100644 index 0000000000000..9fb82b730c9b3 --- /dev/null +++ b/src/test/ui/did_you_mean/recursion_limit_macro.rs @@ -0,0 +1,26 @@ +// Copyright 2017 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. + +// Test that the recursion limit can be changed and that the compiler +// suggests a fix. In this case, we have a recursing macro that will +// overflow if the number of arguments surpasses the recursion limit. + +#![allow(dead_code)] +#![recursion_limit="10"] + +macro_rules! recurse { + () => { }; + ($t:tt $($tail:tt)*) => { recurse!($($tail)*) }; +} + +fn main() { + recurse!(0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9); +} + diff --git a/src/test/ui/did_you_mean/recursion_limit_macro.stderr b/src/test/ui/did_you_mean/recursion_limit_macro.stderr new file mode 100644 index 0000000000000..6bed78a3badc8 --- /dev/null +++ b/src/test/ui/did_you_mean/recursion_limit_macro.stderr @@ -0,0 +1,11 @@ +error: recursion limit reached while expanding the macro `recurse` + --> $DIR/recursion_limit_macro.rs:20:31 + | +20 | ($t:tt $($tail:tt)*) => { recurse!($($tail)*) }; + | ^^^^^^^^^^^^^^^^^^^ +... +24 | recurse!(0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9); + | -------------------------------------------------- in this macro invocation + | + = note: consider adding a `#![recursion_limit="20"]` attribute to your crate + From 53f8a84fb4ba9f03540131e2ccc90742df45c1e3 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 26 Feb 2017 16:21:26 +0200 Subject: [PATCH 02/10] schedule drops on bindings only after initializing them This reduces the number of dynamic drops in libstd from 1141 to 899. However, without this change, the next patch would have created much more dynamic drops. A basic merge unswitching hack reduced the number of dynamic drops to 644, with no effect on stack usage. I should be writing a more dedicated drop unswitching pass. No performance measurements. --- .../borrowck/mir/elaborate_drops.rs | 1 + src/librustc_mir/build/block.rs | 5 +- src/librustc_mir/build/matches/mod.rs | 120 ++++++++---------- src/test/mir-opt/storage_ranges.rs | 2 +- src/test/run-pass/mir_drop_order.rs | 42 ++++++ 5 files changed, 98 insertions(+), 72 deletions(-) create mode 100644 src/test/run-pass/mir_drop_order.rs diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 5899c9f31d14d..e5b2757c3d20a 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -161,6 +161,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn create_drop_flag(&mut self, index: MovePathIndex) { let tcx = self.tcx; let patch = &mut self.patch; + debug!("create_drop_flag({:?})", self.mir.span); self.drop_flags.entry(index).or_insert_with(|| { patch.new_temp(tcx.types.bool) }); diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 121d592da0316..3305cfc0dfe1a 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -67,7 +67,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { this.expr_into_pattern(block, pattern, init) })); } else { - this.storage_live_for_bindings(block, &pattern); + this.visit_bindings(&pattern, &mut |this, _, _, node, span, _| { + this.storage_live_binding(block, node, span); + this.schedule_drop_for_binding(node, span); + }) } // Enter the visibility scope, after evaluating the initializer. diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index a28bc5d6ce36d..6da1bf5023a07 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -123,16 +123,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { - self.storage_live_for_bindings(block, &irrefutable_pat); - let lvalue = Lvalue::Local(self.var_indices[&var]); - return self.into(&lvalue, block, initializer); + let lvalue = self.storage_live_binding(block, var, irrefutable_pat.span); + unpack!(block = self.into(&lvalue, block, initializer)); + self.schedule_drop_for_binding(var, irrefutable_pat.span); + block.unit() + } + _ => { + let lvalue = unpack!(block = self.as_lvalue(block, initializer)); + self.lvalue_into_pattern(block, irrefutable_pat, &lvalue) } - _ => {} } - let lvalue = unpack!(block = self.as_lvalue(block, initializer)); - self.lvalue_into_pattern(block, - irrefutable_pat, - &lvalue) } pub fn lvalue_into_pattern(&mut self, @@ -174,79 +174,70 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { scope_span: Span, pattern: &Pattern<'tcx>) -> Option { - match *pattern.kind { - PatternKind::Binding { mutability, name, mode: _, var, ty, ref subpattern } => { - if var_scope.is_none() { - var_scope = Some(self.new_visibility_scope(scope_span)); - } - let source_info = SourceInfo { - span: pattern.span, - scope: var_scope.unwrap() - }; - self.declare_binding(source_info, mutability, name, var, ty); - if let Some(subpattern) = subpattern.as_ref() { - var_scope = self.declare_bindings(var_scope, scope_span, subpattern); - } - } - PatternKind::Array { ref prefix, ref slice, ref suffix } | - PatternKind::Slice { ref prefix, ref slice, ref suffix } => { - for subpattern in prefix.iter().chain(slice).chain(suffix) { - var_scope = self.declare_bindings(var_scope, scope_span, subpattern); - } - } - PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => { - } - PatternKind::Deref { ref subpattern } => { - var_scope = self.declare_bindings(var_scope, scope_span, subpattern); - } - PatternKind::Leaf { ref subpatterns } | - PatternKind::Variant { ref subpatterns, .. } => { - for subpattern in subpatterns { - var_scope = self.declare_bindings(var_scope, scope_span, &subpattern.pattern); - } + self.visit_bindings(pattern, &mut |this, mutability, name, var, span, ty| { + if var_scope.is_none() { + var_scope = Some(this.new_visibility_scope(scope_span)); } - } + let source_info = SourceInfo { + span: span, + scope: var_scope.unwrap() + }; + this.declare_binding(source_info, mutability, name, var, ty); + }); var_scope } - /// Emit `StorageLive` for every binding in the pattern. - pub fn storage_live_for_bindings(&mut self, - block: BasicBlock, - pattern: &Pattern<'tcx>) { - match *pattern.kind { - PatternKind::Binding { var, ref subpattern, .. } => { - let lvalue = Lvalue::Local(self.var_indices[&var]); - let source_info = self.source_info(pattern.span); - self.cfg.push(block, Statement { - source_info: source_info, - kind: StatementKind::StorageLive(lvalue) - }); + pub fn storage_live_binding(&mut self, block: BasicBlock, var: NodeId, span: Span) + -> Lvalue<'tcx> + { + let local_id = self.var_indices[&var]; + let source_info = self.source_info(span); + self.cfg.push(block, Statement { + source_info: source_info, + kind: StatementKind::StorageLive(Lvalue::Local(local_id)) + }); + Lvalue::Local(local_id) + } + pub fn schedule_drop_for_binding(&mut self, var: NodeId, span: Span) { + let local_id = self.var_indices[&var]; + let var_ty = self.local_decls[local_id].ty; + let extent = self.hir.tcx().region_maps.var_scope(var); + self.schedule_drop(span, extent, &Lvalue::Local(local_id), var_ty); + } + + pub fn visit_bindings(&mut self, pattern: &Pattern<'tcx>, mut f: &mut F) + where F: FnMut(&mut Self, Mutability, Name, NodeId, Span, Ty<'tcx>) + { + match *pattern.kind { + PatternKind::Binding { mutability, name, var, ty, ref subpattern, .. } => { + f(self, mutability, name, var, pattern.span, ty); if let Some(subpattern) = subpattern.as_ref() { - self.storage_live_for_bindings(block, subpattern); + self.visit_bindings(subpattern, f); } } PatternKind::Array { ref prefix, ref slice, ref suffix } | PatternKind::Slice { ref prefix, ref slice, ref suffix } => { for subpattern in prefix.iter().chain(slice).chain(suffix) { - self.storage_live_for_bindings(block, subpattern); + self.visit_bindings(subpattern, f); } } PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => { } PatternKind::Deref { ref subpattern } => { - self.storage_live_for_bindings(block, subpattern); + self.visit_bindings(subpattern, f); } PatternKind::Leaf { ref subpatterns } | PatternKind::Variant { ref subpatterns, .. } => { for subpattern in subpatterns { - self.storage_live_for_bindings(block, &subpattern.pattern); + self.visit_bindings(&subpattern.pattern, f); } } } } } + /// List of blocks for each arm (and potentially other metadata in the /// future). struct ArmBlocks { @@ -691,25 +682,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Assign each of the bindings. This may trigger moves out of the candidate. for binding in bindings { - // Find the variable for the `var_id` being bound. It - // should have been created by a previous call to - // `declare_bindings`. - let var_index = self.var_indices[&binding.var_id]; - + let source_info = self.source_info(binding.span); + let local = self.storage_live_binding(block, binding.var_id, binding.span); + self.schedule_drop_for_binding(binding.var_id, binding.span); let rvalue = match binding.binding_mode { BindingMode::ByValue => Rvalue::Use(Operand::Consume(binding.source)), BindingMode::ByRef(region, borrow_kind) => Rvalue::Ref(region, borrow_kind, binding.source), }; - - let source_info = self.source_info(binding.span); - self.cfg.push(block, Statement { - source_info: source_info, - kind: StatementKind::StorageLive(Lvalue::Local(var_index)) - }); - self.cfg.push_assign(block, source_info, - &Lvalue::Local(var_index), rvalue); + self.cfg.push_assign(block, source_info, &local, rvalue); } } @@ -730,8 +712,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { name: Some(name), source_info: Some(source_info), }); - let extent = self.hir.tcx().region_maps.var_scope(var_id); - self.schedule_drop(source_info.span, extent, &Lvalue::Local(var), var_ty); self.var_indices.insert(var_id, var); debug!("declare_binding: var={:?}", var); diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs index 933bfa8df2ec2..c63db741e5ae8 100644 --- a/src/test/mir-opt/storage_ranges.rs +++ b/src/test/mir-opt/storage_ranges.rs @@ -31,8 +31,8 @@ fn main() { // _3 = &_4; // StorageDead(_5); // _2 = (); -// StorageDead(_4); // StorageDead(_3); +// StorageDead(_4); // StorageLive(_6); // _6 = const 1i32; // _0 = (); diff --git a/src/test/run-pass/mir_drop_order.rs b/src/test/run-pass/mir_drop_order.rs new file mode 100644 index 0000000000000..55ac5ca067bf8 --- /dev/null +++ b/src/test/run-pass/mir_drop_order.rs @@ -0,0 +1,42 @@ +// Copyright 2017 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 std::cell::RefCell; + +pub struct DropLogger<'a> { + id: usize, + log: &'a RefCell> +} + +impl<'a> Drop for DropLogger<'a> { + fn drop(&mut self) { + self.log.borrow_mut().push(self.id); + } +} + +fn main() { + let log = RefCell::new(vec![]); + let d = |id| DropLogger { id: id, log: &log }; + let get = || -> Vec<_> { + let mut m = log.borrow_mut(); + let n = m.drain(..); + n.collect() + }; + + { + let _x = (d(0), &d(1), d(2), &d(3)); + // all borrows are extended - nothing has been dropped yet + assert_eq!(get(), vec![]); + } + // in a let-statement, extended lvalues are dropped + // *after* the let result (tho they have the same scope + // as far as scope-based borrowck goes). + assert_eq!(get(), vec![0, 2, 3, 1]); +} From 7f8529d5f60ad84a439f3a15d16edcbe8fdf16da Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 26 Feb 2017 16:21:08 +0200 Subject: [PATCH 03/10] make operands live to the end of their containing expression In MIR construction, operands need to live exactly until they are used, which is during the (sub)expression that made the call to `as_operand`. Before this PR, operands lived until the end of the temporary scope, which was sometimes unnecessarily longer and sometimes too short. Fixes #38669. --- src/librustc_mir/build/expr/as_lvalue.rs | 8 ++- src/librustc_mir/build/expr/as_operand.rs | 32 +++++++-- src/librustc_mir/build/expr/as_rvalue.rs | 53 ++++++++------ src/librustc_mir/build/expr/as_temp.rs | 21 ++++-- src/librustc_mir/build/expr/into.rs | 16 ++--- src/librustc_mir/build/expr/stmt.rs | 8 +-- src/librustc_mir/build/matches/mod.rs | 2 +- src/librustc_mir/build/scope.rs | 10 ++- src/test/mir-opt/basic_assignment.rs | 85 +++++++++++++++++++++++ src/test/mir-opt/issue-38669.rs | 52 ++++++++++++++ src/test/mir-opt/storage_ranges.rs | 5 +- src/test/run-pass/mir_drop_order.rs | 21 ++++-- 12 files changed, 257 insertions(+), 56 deletions(-) create mode 100644 src/test/mir-opt/basic_assignment.rs create mode 100644 src/test/mir-opt/issue-38669.rs diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 5abfe084f2258..8886a310429ea 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -56,8 +56,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty()); let slice = unpack!(block = this.as_lvalue(block, lhs)); - - let idx = unpack!(block = this.as_operand(block, index)); + // extent=None so lvalue indexes live forever. They are scalars so they + // do not need storage annotations, and they are often copied between + // places. + let idx = unpack!(block = this.as_operand(block, None, index)); // bounds check: let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty)); @@ -121,7 +123,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Some(Category::Lvalue) => false, _ => true, }); - this.as_temp(block, expr) + this.as_temp(block, expr.temp_lifetime, expr) } } } diff --git a/src/librustc_mir/build/expr/as_operand.rs b/src/librustc_mir/build/expr/as_operand.rs index 09cdcc74ef63e..8d79e755685d0 100644 --- a/src/librustc_mir/build/expr/as_operand.rs +++ b/src/librustc_mir/build/expr/as_operand.rs @@ -13,29 +13,52 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::Category; use hair::*; +use rustc::middle::region::CodeExtent; use rustc::mir::*; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { + /// Returns an operand suitable for use until the end of the current + /// scope expression. + /// + /// The operand returned from this function will *not be valid* after + /// an ExprKind::Scope is passed, so please do *not* return it from + /// functions to avoid bad miscompiles. + pub fn as_local_operand(&mut self, block: BasicBlock, expr: M) + -> BlockAnd> + where M: Mirror<'tcx, Output = Expr<'tcx>> + { + let topmost_scope = self.topmost_scope(); // FIXME(#6393) + self.as_operand(block, Some(topmost_scope), expr) + } + /// Compile `expr` into a value that can be used as an operand. /// If `expr` is an lvalue like `x`, this will introduce a /// temporary `tmp = x`, so that we capture the value of `x` at /// this time. - pub fn as_operand(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + /// + /// The operand is known to be live until the end of `scope`. + pub fn as_operand(&mut self, + block: BasicBlock, + scope: Option, + expr: M) -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>> { let expr = self.hir.mirror(expr); - self.expr_as_operand(block, expr) + self.expr_as_operand(block, scope, expr) } fn expr_as_operand(&mut self, mut block: BasicBlock, + scope: Option, expr: Expr<'tcx>) -> BlockAnd> { debug!("expr_as_operand(block={:?}, expr={:?})", block, expr); let this = self; if let ExprKind::Scope { extent, value } = expr.kind { - return this.in_scope(extent, block, |this| this.as_operand(block, value)); + return this.in_scope(extent, block, |this| { + this.as_operand(block, scope, value) + }); } let category = Category::of(&expr.kind).unwrap(); @@ -47,7 +70,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } Category::Lvalue | Category::Rvalue(..) => { - let operand = unpack!(block = this.as_temp(block, expr)); + let operand = + unpack!(block = this.as_temp(block, scope, expr)); block.and(Operand::Consume(operand)) } } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 7f5d9c36ecedf..961713bcfe6d5 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -21,22 +21,34 @@ use build::expr::category::{Category, RvalueFunc}; use hair::*; use rustc_const_math::{ConstInt, ConstIsize}; use rustc::middle::const_val::ConstVal; +use rustc::middle::region::CodeExtent; use rustc::ty; use rustc::mir::*; use syntax::ast; use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { + /// See comment on `as_local_operand` + pub fn as_local_rvalue(&mut self, block: BasicBlock, expr: M) + -> BlockAnd> + where M: Mirror<'tcx, Output = Expr<'tcx>> + { + let topmost_scope = self.topmost_scope(); // FIXME(#6393) + self.as_rvalue(block, Some(topmost_scope), expr) + } + /// Compile `expr`, yielding an rvalue. - pub fn as_rvalue(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + pub fn as_rvalue(&mut self, block: BasicBlock, scope: Option, expr: M) + -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>> { let expr = self.hir.mirror(expr); - self.expr_as_rvalue(block, expr) + self.expr_as_rvalue(block, scope, expr) } fn expr_as_rvalue(&mut self, mut block: BasicBlock, + scope: Option, expr: Expr<'tcx>) -> BlockAnd> { debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr); @@ -47,10 +59,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { match expr.kind { ExprKind::Scope { extent, value } => { - this.in_scope(extent, block, |this| this.as_rvalue(block, value)) + this.in_scope(extent, block, |this| this.as_rvalue(block, scope, value)) } ExprKind::Repeat { value, count } => { - let value_operand = unpack!(block = this.as_operand(block, value)); + let value_operand = unpack!(block = this.as_operand(block, scope, value)); block.and(Rvalue::Repeat(value_operand, count)) } ExprKind::Borrow { region, borrow_kind, arg } => { @@ -58,13 +70,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block.and(Rvalue::Ref(region, borrow_kind, arg_lvalue)) } ExprKind::Binary { op, lhs, rhs } => { - let lhs = unpack!(block = this.as_operand(block, lhs)); - let rhs = unpack!(block = this.as_operand(block, rhs)); + let lhs = unpack!(block = this.as_operand(block, scope, lhs)); + let rhs = unpack!(block = this.as_operand(block, scope, rhs)); this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs) } ExprKind::Unary { op, arg } => { - let arg = unpack!(block = this.as_operand(block, arg)); + let arg = unpack!(block = this.as_operand(block, scope, arg)); // Check for -MIN on signed integers if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() { let bool_ty = this.hir.bool_ty(); @@ -97,27 +109,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Cast { source } => { let source = this.hir.mirror(source); - let source = unpack!(block = this.as_operand(block, source)); + let source = unpack!(block = this.as_operand(block, scope, source)); block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty)) } ExprKind::Use { source } => { - let source = unpack!(block = this.as_operand(block, source)); + let source = unpack!(block = this.as_operand(block, scope, source)); block.and(Rvalue::Use(source)) } ExprKind::ReifyFnPointer { source } => { - let source = unpack!(block = this.as_operand(block, source)); + let source = unpack!(block = this.as_operand(block, scope, source)); block.and(Rvalue::Cast(CastKind::ReifyFnPointer, source, expr.ty)) } ExprKind::UnsafeFnPointer { source } => { - let source = unpack!(block = this.as_operand(block, source)); + let source = unpack!(block = this.as_operand(block, scope, source)); block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty)) } ExprKind::ClosureFnPointer { source } => { - let source = unpack!(block = this.as_operand(block, source)); + let source = unpack!(block = this.as_operand(block, scope, source)); block.and(Rvalue::Cast(CastKind::ClosureFnPointer, source, expr.ty)) } ExprKind::Unsize { source } => { - let source = unpack!(block = this.as_operand(block, source)); + let source = unpack!(block = this.as_operand(block, scope, source)); block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty)) } ExprKind::Array { fields } => { @@ -150,7 +162,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // first process the set of fields let fields: Vec<_> = fields.into_iter() - .map(|f| unpack!(block = this.as_operand(block, f))) + .map(|f| unpack!(block = this.as_operand(block, scope, f))) .collect(); block.and(Rvalue::Aggregate(AggregateKind::Array, fields)) @@ -159,7 +171,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // first process the set of fields let fields: Vec<_> = fields.into_iter() - .map(|f| unpack!(block = this.as_operand(block, f))) + .map(|f| unpack!(block = this.as_operand(block, scope, f))) .collect(); block.and(Rvalue::Aggregate(AggregateKind::Tuple, fields)) @@ -167,7 +179,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Closure { closure_id, substs, upvars } => { // see (*) above let upvars = upvars.into_iter() - .map(|upvar| unpack!(block = this.as_operand(block, upvar))) + .map(|upvar| unpack!(block = this.as_operand(block, scope, upvar))) .collect(); block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars)) } @@ -179,10 +191,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // first process the set of fields that were provided // (evaluating them in order given by user) - let fields_map: FxHashMap<_, _> = - fields.into_iter() - .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr)))) - .collect(); + let fields_map: FxHashMap<_, _> = fields.into_iter() + .map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr)))) + .collect(); let field_names = this.hir.all_fields(adt_def, variant_index); @@ -235,7 +246,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false, _ => true, }); - let operand = unpack!(block = this.as_operand(block, expr)); + let operand = unpack!(block = this.as_operand(block, scope, expr)); block.and(Rvalue::Use(operand)) } } diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index 0ae4bcc4205d4..69b9570200921 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -13,29 +13,38 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use build::expr::category::Category; use hair::*; +use rustc::middle::region::CodeExtent; use rustc::mir::*; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// Compile `expr` into a fresh temporary. This is used when building /// up rvalues so as to freeze the value that will be consumed. - pub fn as_temp(&mut self, block: BasicBlock, expr: M) -> BlockAnd> + pub fn as_temp(&mut self, + block: BasicBlock, + temp_lifetime: Option, + expr: M) + -> BlockAnd> where M: Mirror<'tcx, Output = Expr<'tcx>> { let expr = self.hir.mirror(expr); - self.expr_as_temp(block, expr) + self.expr_as_temp(block, temp_lifetime, expr) } - fn expr_as_temp(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd> { + fn expr_as_temp(&mut self, + mut block: BasicBlock, + temp_lifetime: Option, + expr: Expr<'tcx>) + -> BlockAnd> { debug!("expr_as_temp(block={:?}, expr={:?})", block, expr); let this = self; - if let ExprKind::Scope { extent, value } = expr.kind { - return this.in_scope(extent, block, |this| this.as_temp(block, value)); + if let ExprKind::Scope { .. } = expr.kind { + span_bug!(expr.span, "unexpected scope expression in as_temp: {:?}", + expr); } let expr_ty = expr.ty.clone(); let temp = this.temp(expr_ty.clone()); - let temp_lifetime = expr.temp_lifetime; let expr_span = expr.span; let source_info = this.source_info(expr_span); diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index d9f71e36e2118..d6880c8a84bf7 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { _ => false, }; - unpack!(block = this.as_rvalue(block, source)); + unpack!(block = this.as_local_rvalue(block, source)); // This is an optimization. If the expression was a call then we already have an // unreachable block. Don't bother to terminate it and create a new one. @@ -65,7 +65,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => { - let operand = unpack!(block = this.as_operand(block, cond_expr)); + let operand = unpack!(block = this.as_local_operand(block, cond_expr)); let mut then_block = this.cfg.start_new_block(); let mut else_block = this.cfg.start_new_block(); @@ -107,7 +107,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { (this.cfg.start_new_block(), this.cfg.start_new_block(), this.cfg.start_new_block(), this.cfg.start_new_block()); - let lhs = unpack!(block = this.as_operand(block, lhs)); + let lhs = unpack!(block = this.as_local_operand(block, lhs)); let blocks = match op { LogicalOp::And => (else_block, false_block), LogicalOp::Or => (true_block, else_block), @@ -115,7 +115,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1); this.cfg.terminate(block, source_info, term); - let rhs = unpack!(else_block = this.as_operand(else_block, rhs)); + let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs)); let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block); this.cfg.terminate(else_block, source_info, term); @@ -173,7 +173,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if let Some(cond_expr) = opt_cond_expr { let loop_block_end; let cond = unpack!( - loop_block_end = this.as_operand(loop_block, cond_expr)); + loop_block_end = this.as_local_operand(loop_block, cond_expr)); body_block = this.cfg.start_new_block(); let term = TerminatorKind::if_(this.hir.tcx(), cond, body_block, exit_block); @@ -206,10 +206,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } _ => false }; - let fun = unpack!(block = this.as_operand(block, fun)); + let fun = unpack!(block = this.as_local_operand(block, fun)); let args: Vec<_> = args.into_iter() - .map(|arg| unpack!(block = this.as_operand(block, arg))) + .map(|arg| unpack!(block = this.as_local_operand(block, arg))) .collect(); let success = this.cfg.start_new_block(); @@ -265,7 +265,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { _ => true, }); - let rvalue = unpack!(block = this.as_rvalue(block, expr)); + let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); this.cfg.push_assign(block, source_info, destination, rvalue); block.unit() } diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs index c577aab40dbeb..be39dcbf6d08d 100644 --- a/src/librustc_mir/build/expr/stmt.rs +++ b/src/librustc_mir/build/expr/stmt.rs @@ -38,14 +38,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Generate better code for things that don't need to be // dropped. if this.hir.needs_drop(lhs.ty) { - let rhs = unpack!(block = this.as_operand(block, rhs)); + let rhs = unpack!(block = this.as_local_operand(block, rhs)); let lhs = unpack!(block = this.as_lvalue(block, lhs)); unpack!(block = this.build_drop_and_replace( block, lhs_span, lhs, rhs )); block.unit() } else { - let rhs = unpack!(block = this.as_rvalue(block, rhs)); + let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); let lhs = unpack!(block = this.as_lvalue(block, lhs)); this.cfg.push_assign(block, source_info, &lhs, rhs); block.unit() @@ -64,7 +64,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let lhs_ty = lhs.ty; // As above, RTL. - let rhs = unpack!(block = this.as_operand(block, rhs)); + let rhs = unpack!(block = this.as_local_operand(block, rhs)); let lhs = unpack!(block = this.as_lvalue(block, lhs)); // we don't have to drop prior contents or anything @@ -122,7 +122,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { unpack!(block = this.as_lvalue(block, output)) }).collect(); let inputs = inputs.into_iter().map(|input| { - unpack!(block = this.as_operand(block, input)) + unpack!(block = this.as_local_operand(block, input)) }).collect(); this.cfg.push(block, Statement { source_info: source_info, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 6da1bf5023a07..15afc58e6a471 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -661,7 +661,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // guard, this block is simply unreachable let guard = self.hir.mirror(guard); let source_info = self.source_info(guard.span); - let cond = unpack!(block = self.as_operand(block, guard)); + let cond = unpack!(block = self.as_local_operand(block, guard)); let otherwise = self.cfg.start_new_block(); self.cfg.terminate(block, source_info, TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise)); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 282361fc13e27..3dab1717f6b2e 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -253,9 +253,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { f: F) where F: FnOnce(&mut Builder<'a, 'gcx, 'tcx>) { - let extent = self.scopes.last().map(|scope| scope.extent).unwrap(); + let extent = self.topmost_scope(); let loop_scope = LoopScope { - extent: extent.clone(), + extent: extent, continue_block: loop_block, break_block: break_block, break_destination: break_destination, @@ -416,6 +416,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { self.scopes[1].extent } + /// Returns the topmost active scope, which is known to be alive until + /// the next scope expression. + pub fn topmost_scope(&self) -> CodeExtent { + self.scopes.last().expect("topmost_scope: no scopes present").extent + } + // Scheduling drops // ================ /// Indicates that `lvalue` should be dropped on exit from diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs new file mode 100644 index 0000000000000..9c924a23903f9 --- /dev/null +++ b/src/test/mir-opt/basic_assignment.rs @@ -0,0 +1,85 @@ +// Copyright 2017 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. + +// this tests move up progration, which is not yet implemented +// Copyright 2017 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. + +// check that codegen of assignment expressions is sane. Assignments +// tend to be absent in simple code, so subtle breakage in them can +// leave a quite hard-to-find trail of destruction. + +fn main() { + let nodrop_x = false; + let nodrop_y; + + nodrop_y = nodrop_x; + + let drop_x : Option> = None; + let drop_y; + + drop_y = drop_x; +} + +// END RUST SOURCE +// START rustc.node4.SimplifyCfg.initial-after.mir +// bb0: { +// StorageLive(_1); +// _1 = const false; +// StorageLive(_2); +// StorageLive(_3); +// _3 = _1; +// _2 = _3; +// StorageDead(_3); +// StorageLive(_4); +// _4 = std::option::Option>::None; +// StorageLive(_6); +// StorageLive(_7); +// _7 = _4; +// replace(_6 <- _7) -> [return: bb5, unwind: bb4]; +// } +// bb1: { +// resume; +// } +// bb2: { +// drop(_4) -> bb1; +// } +// bb3: { +// drop(_6) -> bb2; +// } +// bb4: { +// drop(_7) -> bb3; +// } +// bb5: { +// drop(_7) -> [return: bb6, unwind: bb3]; +// } +// bb6: { +// StorageDead(_7); +// _0 = (); +// drop(_6) -> [return: bb7, unwind: bb2]; +// } +// bb7: { +// StorageDead(_6); +// drop(_4) -> bb8; +// } +// bb8: { +// StorageDead(_4); +// StorageDead(_2); +// StorageDead(_1); +// return; +// } +// END rustc.node4.SimplifyCfg.initial-after.mir diff --git a/src/test/mir-opt/issue-38669.rs b/src/test/mir-opt/issue-38669.rs new file mode 100644 index 0000000000000..d1e515c4f2b15 --- /dev/null +++ b/src/test/mir-opt/issue-38669.rs @@ -0,0 +1,52 @@ +// Copyright 2017 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. + +// check that we don't StorageDead booleans before they are used + +fn main() { + let mut should_break = false; + loop { + if should_break { + break; + } + should_break = true; + } +} + +// END RUST SOURCE +// START rustc.node4.SimplifyCfg.initial-after.mir +// bb0: { +// StorageLive(_1); +// _1 = const false; +// goto -> bb1; +// } +// +// bb1: { +// StorageLive(_4); +// _4 = _1; +// switchInt(_4) -> [0: bb3, otherwise: bb2]; +// } +// +// bb2: { +// StorageLive(_6); +// _0 = (); +// StorageDead(_4); +// StorageDead(_1); +// return; +// } +// +// bb3: { +// _3 = (); +// StorageDead(_4); +// _1 = const true; +// _2 = (); +// goto -> bb1; +// } +// END rustc.node4.SimplifyCfg.initial-after.mir diff --git a/src/test/mir-opt/storage_ranges.rs b/src/test/mir-opt/storage_ranges.rs index c63db741e5ae8..3fbd1a36f2f16 100644 --- a/src/test/mir-opt/storage_ranges.rs +++ b/src/test/mir-opt/storage_ranges.rs @@ -28,8 +28,8 @@ fn main() { // StorageLive(_5); // _5 = _1; // _4 = std::option::Option::Some(_5,); -// _3 = &_4; // StorageDead(_5); +// _3 = &_4; // _2 = (); // StorageDead(_3); // StorageDead(_4); @@ -38,6 +38,5 @@ fn main() { // _0 = (); // StorageDead(_6); // StorageDead(_1); -// return; -// } +// } // END rustc.node4.TypeckMir.before.mir diff --git a/src/test/run-pass/mir_drop_order.rs b/src/test/run-pass/mir_drop_order.rs index 55ac5ca067bf8..e7da43597f169 100644 --- a/src/test/run-pass/mir_drop_order.rs +++ b/src/test/run-pass/mir_drop_order.rs @@ -9,23 +9,27 @@ // except according to those terms. use std::cell::RefCell; +use std::panic; pub struct DropLogger<'a> { id: usize, - log: &'a RefCell> + log: &'a panic::AssertUnwindSafe>> } impl<'a> Drop for DropLogger<'a> { fn drop(&mut self) { - self.log.borrow_mut().push(self.id); + self.log.0.borrow_mut().push(self.id); } } +struct InjectedFailure; + +#[allow(unreachable_code)] fn main() { - let log = RefCell::new(vec![]); + let log = panic::AssertUnwindSafe(RefCell::new(vec![])); let d = |id| DropLogger { id: id, log: &log }; let get = || -> Vec<_> { - let mut m = log.borrow_mut(); + let mut m = log.0.borrow_mut(); let n = m.drain(..); n.collect() }; @@ -39,4 +43,13 @@ fn main() { // *after* the let result (tho they have the same scope // as far as scope-based borrowck goes). assert_eq!(get(), vec![0, 2, 3, 1]); + + let _ = std::panic::catch_unwind(|| { + (d(4), &d(5), d(6), &d(7), panic!(InjectedFailure)); + }); + + // here, the temporaries (5/7) live until the end of the + // containing statement, which is destroyed after the operands + // (4/6) on a panic. + assert_eq!(get(), vec![6, 4, 7, 5]); } From e4458dee7e80197b0bd150c682cd012bd7b2af12 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 28 Feb 2017 13:15:15 +0200 Subject: [PATCH 04/10] fix codegen test --- src/test/codegen/lifetime_start_end.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/test/codegen/lifetime_start_end.rs b/src/test/codegen/lifetime_start_end.rs index e3b35cf355250..5c1f1f8f2bb20 100644 --- a/src/test/codegen/lifetime_start_end.rs +++ b/src/test/codegen/lifetime_start_end.rs @@ -11,11 +11,9 @@ // compile-flags: -O -C no-prepopulate-passes #![crate_type = "lib"] -#![feature(rustc_attrs)] // CHECK-LABEL: @test #[no_mangle] -#[rustc_mir] // FIXME #27840 MIR has different codegen. pub fn test() { let a = 0; &a; // keep variable in an alloca @@ -33,11 +31,11 @@ pub fn test() { // CHECK: [[S__5:%[0-9]+]] = bitcast %"core::option::Option"* %_5 to i8* // CHECK: call void @llvm.lifetime.start(i{{[0-9 ]+}}, i8* [[S__5]]) -// CHECK: [[E__5:%[0-9]+]] = bitcast %"core::option::Option"* %_5 to i8* -// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E__5]]) - // CHECK: [[E_b:%[0-9]+]] = bitcast %"core::option::Option"** %b to i8* // CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E_b]]) + +// CHECK: [[E__5:%[0-9]+]] = bitcast %"core::option::Option"* %_5 to i8* +// CHECK: call void @llvm.lifetime.end(i{{[0-9 ]+}}, i8* [[E__5]]) } let c = 1; From 3a14e9e745460e55b149dad0d21cdb221545f184 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 26 Feb 2017 00:32:14 +0200 Subject: [PATCH 05/10] Make Rvalue::ty infallible --- src/librustc/mir/mod.rs | 5 ++- src/librustc/mir/tcx.rs | 42 +++++++++----------- src/librustc/mir/visit.rs | 3 +- src/librustc_mir/build/expr/as_rvalue.rs | 3 +- src/librustc_mir/transform/qualify_consts.rs | 2 +- src/librustc_mir/transform/type_check.rs | 15 +++---- src/librustc_passes/mir_stats.rs | 2 +- src/librustc_trans/mir/constant.rs | 2 +- src/librustc_trans/mir/rvalue.rs | 2 +- 9 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 40ebc97a78a6c..ae60be7aa4b0e 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1038,7 +1038,8 @@ pub enum CastKind { #[derive(Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum AggregateKind<'tcx> { - Array, + /// The type is of the element + Array(Ty<'tcx>), Tuple, /// The second field is variant number (discriminant), it's equal to 0 /// for struct and union expressions. The fourth field is active field @@ -1135,7 +1136,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { } match *kind { - AggregateKind::Array => write!(fmt, "{:?}", lvs), + AggregateKind::Array(_) => write!(fmt, "{:?}", lvs), AggregateKind::Tuple => { match lvs.len() { diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 50a80305bee27..cfd53cfcddcd3 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -134,46 +134,45 @@ impl<'tcx> Lvalue<'tcx> { } impl<'tcx> Rvalue<'tcx> { - pub fn ty<'a, 'gcx>(&self, mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option> + pub fn ty<'a, 'gcx>(&self, mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { match *self { - Rvalue::Use(ref operand) => Some(operand.ty(mir, tcx)), + Rvalue::Use(ref operand) => operand.ty(mir, tcx), Rvalue::Repeat(ref operand, ref count) => { let op_ty = operand.ty(mir, tcx); let count = count.value.as_u64(tcx.sess.target.uint_type); assert_eq!(count as usize as u64, count); - Some(tcx.mk_array(op_ty, count as usize)) + tcx.mk_array(op_ty, count as usize) } Rvalue::Ref(reg, bk, ref lv) => { let lv_ty = lv.ty(mir, tcx).to_ty(tcx); - Some(tcx.mk_ref(reg, + tcx.mk_ref(reg, ty::TypeAndMut { ty: lv_ty, mutbl: bk.to_mutbl_lossy() } - )) + ) } - Rvalue::Len(..) => Some(tcx.types.usize), - Rvalue::Cast(.., ty) => Some(ty), + Rvalue::Len(..) => tcx.types.usize, + Rvalue::Cast(.., ty) => ty, Rvalue::BinaryOp(op, ref lhs, ref rhs) => { let lhs_ty = lhs.ty(mir, tcx); let rhs_ty = rhs.ty(mir, tcx); - Some(op.ty(tcx, lhs_ty, rhs_ty)) + op.ty(tcx, lhs_ty, rhs_ty) } Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { let lhs_ty = lhs.ty(mir, tcx); let rhs_ty = rhs.ty(mir, tcx); let ty = op.ty(tcx, lhs_ty, rhs_ty); - let ty = tcx.intern_tup(&[ty, tcx.types.bool], false); - Some(ty) + tcx.intern_tup(&[ty, tcx.types.bool], false) } Rvalue::UnaryOp(_, ref operand) => { - Some(operand.ty(mir, tcx)) + operand.ty(mir, tcx) } Rvalue::Discriminant(ref lval) => { let ty = lval.ty(mir, tcx).to_ty(tcx); if let ty::TyAdt(adt_def, _) = ty.sty { - Some(adt_def.repr.discr_type().to_ty(tcx)) + adt_def.repr.discr_type().to_ty(tcx) } else { // Undefined behaviour, bug for now; may want to return something for // the `discriminant` intrinsic later. @@ -181,29 +180,24 @@ impl<'tcx> Rvalue<'tcx> { } } Rvalue::Box(t) => { - Some(tcx.mk_box(t)) + tcx.mk_box(t) } Rvalue::Aggregate(ref ak, ref ops) => { match *ak { - AggregateKind::Array => { - if let Some(operand) = ops.get(0) { - let ty = operand.ty(mir, tcx); - Some(tcx.mk_array(ty, ops.len())) - } else { - None - } + AggregateKind::Array(ty) => { + tcx.mk_array(ty, ops.len()) } AggregateKind::Tuple => { - Some(tcx.mk_tup( + tcx.mk_tup( ops.iter().map(|op| op.ty(mir, tcx)), false - )) + ) } AggregateKind::Adt(def, _, substs, _) => { - Some(tcx.item_type(def.did).subst(tcx, substs)) + tcx.item_type(def.did).subst(tcx, substs) } AggregateKind::Closure(did, substs) => { - Some(tcx.mk_closure_from_closure_substs(did, substs)) + tcx.mk_closure_from_closure_substs(did, substs) } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 7cdbd5cae061f..34c69f5c2f7f4 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -515,7 +515,8 @@ macro_rules! make_mir_visitor { Rvalue::Aggregate(ref $($mutability)* kind, ref $($mutability)* operands) => { match *kind { - AggregateKind::Array => { + AggregateKind::Array(ref $($mutability)* ty) => { + self.visit_ty(ty); } AggregateKind::Tuple => { } diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 7f5d9c36ecedf..7c3807a5edca5 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -148,12 +148,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // to the same MIR as `let x = ();`. // first process the set of fields + let el_ty = expr.ty.sequence_element_type(this.hir.tcx()); let fields: Vec<_> = fields.into_iter() .map(|f| unpack!(block = this.as_operand(block, f))) .collect(); - block.and(Rvalue::Aggregate(AggregateKind::Array, fields)) + block.and(Rvalue::Aggregate(AggregateKind::Array(el_ty), fields)) } ExprKind::Tuple { fields } => { // see (*) above // first process the set of fields diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 441a9add883dd..e998665e03536 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -752,7 +752,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { } if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() { - let ty = rvalue.ty(self.mir, self.tcx).unwrap(); + let ty = rvalue.ty(self.mir, self.tcx); self.add_type(ty); assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR)); // Even if the value inside may not need dropping, diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index af4a4a53905eb..c99c4323bb8a1 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -83,9 +83,8 @@ impl<'a, 'b, 'gcx, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'gcx, 'tcx> { fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); - if let Some(ty) = rvalue.ty(self.mir, self.tcx()) { - self.sanitize_type(rvalue, ty); - } + let rval_ty = rvalue.ty(self.mir, self.tcx()); + self.sanitize_type(rvalue, rval_ty); } fn visit_mir(&mut self, mir: &Mir<'tcx>) { @@ -356,14 +355,10 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { StatementKind::Assign(ref lv, ref rv) => { let lv_ty = lv.ty(mir, tcx).to_ty(tcx); let rv_ty = rv.ty(mir, tcx); - if let Some(rv_ty) = rv_ty { - if let Err(terr) = self.sub_types(rv_ty, lv_ty) { - span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", - lv_ty, rv_ty, terr); - } + if let Err(terr) = self.sub_types(rv_ty, lv_ty) { + span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", + lv_ty, rv_ty, terr); } - // FIXME: rvalue with undeterminable type - e.g. AggregateKind::Array branch that - // returns `None`. } StatementKind::SetDiscriminant{ ref lvalue, variant_index } => { let lvalue_type = lvalue.ty(mir, tcx).to_ty(tcx); diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index ad20c535decbb..88b0694560832 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -191,7 +191,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { // AggregateKind is not distinguished by visit API, so // record it. (`super_rvalue` handles `_operands`.) self.record(match *kind { - AggregateKind::Array => "AggregateKind::Array", + AggregateKind::Array(_) => "AggregateKind::Array", AggregateKind::Tuple => "AggregateKind::Tuple", AggregateKind::Adt(..) => "AggregateKind::Adt", AggregateKind::Closure(..) => "AggregateKind::Closure", diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 771a5b7f366a1..d6c1b3f1a874a 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -548,7 +548,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { failure?; match *kind { - mir::AggregateKind::Array => { + mir::AggregateKind::Array(_) => { self.const_array(dest_ty, &fields) } mir::AggregateKind::Adt(..) | diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 037c771c97b06..9a20683e8715f 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -435,7 +435,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::Rvalue::Discriminant(ref lvalue) => { let discr_lvalue = self.trans_lvalue(&bcx, lvalue); let enum_ty = discr_lvalue.ty.to_ty(bcx.tcx()); - let discr_ty = rvalue.ty(&*self.mir, bcx.tcx()).unwrap(); + let discr_ty = rvalue.ty(&*self.mir, bcx.tcx()); let discr_type = type_of::immediate_type_of(bcx.ccx, discr_ty); let discr = adt::trans_get_discr(&bcx, enum_ty, discr_lvalue.llval, discr_lvalue.alignment, Some(discr_type), true); From 21c61336bb9e327b90f4cb8e87a948be40eeafe5 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 26 Feb 2017 03:05:02 +0200 Subject: [PATCH 06/10] Remove the TypedConstVal Replace it with ConstUsize instead, which is more appropriate; we are not using the rest of the TypedConstVal anyway --- src/librustc/mir/mod.rs | 15 +-------------- src/librustc/mir/tcx.rs | 2 +- src/librustc/mir/visit.rs | 24 ++---------------------- src/librustc_mir/hair/cx/expr.rs | 6 +----- src/librustc_mir/hair/mod.rs | 5 +++-- src/librustc_passes/mir_stats.rs | 9 +-------- src/librustc_trans/mir/constant.rs | 2 +- src/librustc_trans/mir/rvalue.rs | 2 +- 8 files changed, 11 insertions(+), 54 deletions(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index ae60be7aa4b0e..10761a03bec0c 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -983,7 +983,7 @@ pub enum Rvalue<'tcx> { Use(Operand<'tcx>), /// [x; 32] - Repeat(Operand<'tcx>, TypedConstVal<'tcx>), + Repeat(Operand<'tcx>, ConstUsize), /// &x or &mut x Ref(&'tcx Region, BorrowKind, Lvalue<'tcx>), @@ -1203,19 +1203,6 @@ pub struct Constant<'tcx> { pub literal: Literal<'tcx>, } -#[derive(Clone, RustcEncodable, RustcDecodable)] -pub struct TypedConstVal<'tcx> { - pub ty: Ty<'tcx>, - pub span: Span, - pub value: ConstUsize, -} - -impl<'tcx> Debug for TypedConstVal<'tcx> { - fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - write!(fmt, "const {}", ConstInt::Usize(self.value)) - } -} - newtype_index!(Promoted, "promoted"); #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index cfd53cfcddcd3..14d3876a66e55 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -140,7 +140,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::Use(ref operand) => operand.ty(mir, tcx), Rvalue::Repeat(ref operand, ref count) => { let op_ty = operand.ty(mir, tcx); - let count = count.value.as_u64(tcx.sess.target.uint_type); + let count = count.as_u64(tcx.sess.target.uint_type); assert_eq!(count as usize as u64, count); tcx.mk_array(op_ty, count as usize) } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 34c69f5c2f7f4..980d1806e78f8 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -235,12 +235,6 @@ macro_rules! make_mir_visitor { self.super_const_usize(const_usize); } - fn visit_typed_const_val(&mut self, - val: & $($mutability)* TypedConstVal<'tcx>, - location: Location) { - self.super_typed_const_val(val, location); - } - fn visit_local_decl(&mut self, local_decl: & $($mutability)* LocalDecl<'tcx>) { self.super_local_decl(local_decl); @@ -467,9 +461,9 @@ macro_rules! make_mir_visitor { } Rvalue::Repeat(ref $($mutability)* value, - ref $($mutability)* typed_const_val) => { + ref $($mutability)* length) => { self.visit_operand(value, location); - self.visit_typed_const_val(typed_const_val, location); + self.visit_const_usize(length, location); } Rvalue::Ref(r, bk, ref $($mutability)* path) => { @@ -648,20 +642,6 @@ macro_rules! make_mir_visitor { self.visit_literal(literal, location); } - fn super_typed_const_val(&mut self, - constant: & $($mutability)* TypedConstVal<'tcx>, - location: Location) { - let TypedConstVal { - ref $($mutability)* span, - ref $($mutability)* ty, - ref $($mutability)* value, - } = *constant; - - self.visit_span(span); - self.visit_ty(ty); - self.visit_const_usize(value, location); - } - fn super_literal(&mut self, literal: & $($mutability)* Literal<'tcx>, location: Location) { diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index f2b89309e4ab1..c67bb8ec6c585 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -602,11 +602,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, ExprKind::Repeat { value: v.to_ref(), - count: TypedConstVal { - ty: cx.tcx.types.usize, - span: c.span, - value: count - } + count: count, } } hir::ExprRet(ref v) => ExprKind::Return { value: v.to_ref() }, diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 9c7ee6a9ce883..2ee375dee08ac 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -14,7 +14,8 @@ //! unit-tested and separated from the Rust source and compiler data //! structures. -use rustc::mir::{BinOp, BorrowKind, Field, Literal, UnOp, TypedConstVal}; +use rustc_const_math::ConstUsize; +use rustc::mir::{BinOp, BorrowKind, Field, Literal, UnOp}; use rustc::hir::def_id::DefId; use rustc::middle::region::CodeExtent; use rustc::ty::subst::Substs; @@ -219,7 +220,7 @@ pub enum ExprKind<'tcx> { }, Repeat { value: ExprRef<'tcx>, - count: TypedConstVal<'tcx>, + count: ConstUsize, }, Array { fields: Vec>, diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 88b0694560832..ce02cb0e83643 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -19,7 +19,7 @@ use rustc::mir::{Constant, Literal, Location, LocalDecl}; use rustc::mir::{Lvalue, LvalueElem, LvalueProjection}; use rustc::mir::{Mir, Operand, ProjectionElem}; use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind}; -use rustc::mir::{Terminator, TerminatorKind, TypedConstVal, VisibilityScope, VisibilityScopeData}; +use rustc::mir::{Terminator, TerminatorKind, VisibilityScope, VisibilityScopeData}; use rustc::mir::visit as mir_visit; use rustc::mir::visit::Visitor; use rustc::ty::{ClosureSubsts, TyCtxt}; @@ -297,13 +297,6 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> { self.super_const_usize(const_usize); } - fn visit_typed_const_val(&mut self, - val: &TypedConstVal<'tcx>, - location: Location) { - self.record("TypedConstVal", val); - self.super_typed_const_val(val, location); - } - fn visit_local_decl(&mut self, local_decl: &LocalDecl<'tcx>) { self.record("LocalDecl", local_decl); diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index d6c1b3f1a874a..c524d8351e003 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -529,7 +529,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::Rvalue::Repeat(ref elem, ref count) => { let elem = self.const_operand(elem, span)?; - let size = count.value.as_u64(tcx.sess.target.uint_type); + let size = count.as_u64(tcx.sess.target.uint_type); let fields = vec![elem.llval; size as usize]; self.const_array(dest_ty, &fields) } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 9a20683e8715f..b6af4e52e820b 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -95,7 +95,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { mir::Rvalue::Repeat(ref elem, ref count) => { let tr_elem = self.trans_operand(&bcx, elem); - let size = count.value.as_u64(bcx.tcx().sess.target.uint_type); + let size = count.as_u64(bcx.tcx().sess.target.uint_type); let size = C_uint(bcx.ccx, size); let base = base::get_dataptr(&bcx, dest.llval); tvec::slice_for_each(&bcx, base, tr_elem.ty, size, |bcx, llslot| { From 2fcbb48c727e82ea8751d6476d86fd3c6fe16b42 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Mon, 27 Feb 2017 12:03:19 -0800 Subject: [PATCH 07/10] Implement function-like procedural macros ( `#[proc_macro]`) --- src/libproc_macro/lib.rs | 4 ++ src/librustc_metadata/creader.rs | 11 +++- src/libsyntax/feature_gate.rs | 5 ++ src/libsyntax_ext/proc_macro_impl.rs | 35 ++++++++++ src/libsyntax_ext/proc_macro_registrar.rs | 66 ++++++++++++++++--- .../proc-macro/auxiliary/bang_proc_macro.rs | 23 +++++++ .../proc-macro/macro-use-bang.rs | 21 ++++++ .../proc-macro/resolve-error.rs | 12 ++++ .../proc-macro/auxiliary/bang-macro.rs | 26 ++++++++ .../proc-macro/bang-macro.rs | 20 ++++++ 10 files changed, 213 insertions(+), 10 deletions(-) create mode 100644 src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs create mode 100644 src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs create mode 100644 src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs create mode 100644 src/test/run-pass-fulldeps/proc-macro/bang-macro.rs diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index f962c888f42cc..867fa48330de2 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -125,6 +125,10 @@ pub mod __internal { fn register_attr_proc_macro(&mut self, name: &str, expand: fn(TokenStream, TokenStream) -> TokenStream); + + fn register_bang_proc_macro(&mut self, + name: &str, + expand: fn(TokenStream) -> TokenStream); } // Emulate scoped_thread_local!() here essentially diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 4477488f6cb38..49dcffb4830a1 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -586,7 +586,7 @@ impl<'a> CrateLoader<'a> { use proc_macro::__internal::Registry; use rustc_back::dynamic_lib::DynamicLibrary; use syntax_ext::deriving::custom::ProcMacroDerive; - use syntax_ext::proc_macro_impl::AttrProcMacro; + use syntax_ext::proc_macro_impl::{AttrProcMacro, BangProcMacro}; let path = match dylib { Some(dylib) => dylib, @@ -630,6 +630,15 @@ impl<'a> CrateLoader<'a> { ); self.0.push((Symbol::intern(name), Rc::new(expand))); } + + fn register_bang_proc_macro(&mut self, + name: &str, + expand: fn(TokenStream) -> TokenStream) { + let expand = SyntaxExtension::ProcMacro( + Box::new(BangProcMacro { inner: expand }) + ); + self.0.push((Symbol::intern(name), Rc::new(expand))); + } } let mut my_registrar = MyRegistrar(Vec::new()); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c2b72edb66c6c..7de50430184f6 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -763,6 +763,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "attribute proc macros are currently unstable", cfg_fn!(proc_macro))), + ("proc_macro", Normal, Gated(Stability::Unstable, + "proc_macro", + "function-like proc macros are currently unstable", + cfg_fn!(proc_macro))), + ("rustc_derive_registrar", Normal, Gated(Stability::Unstable, "rustc_derive_registrar", "used internally by rustc", diff --git a/src/libsyntax_ext/proc_macro_impl.rs b/src/libsyntax_ext/proc_macro_impl.rs index b454628acb1c0..f60e5824db962 100644 --- a/src/libsyntax_ext/proc_macro_impl.rs +++ b/src/libsyntax_ext/proc_macro_impl.rs @@ -56,3 +56,38 @@ impl base::AttrProcMacro for AttrProcMacro { } } } + +pub struct BangProcMacro { + pub inner: fn(TsShim) -> TsShim, +} + +impl base::ProcMacro for BangProcMacro { + fn expand<'cx>(&self, + ecx: &'cx mut ExtCtxt, + span: Span, + input: TokenStream) + -> TokenStream { + let input = __internal::token_stream_wrap(input); + + let res = __internal::set_parse_sess(&ecx.parse_sess, || { + panic::catch_unwind(panic::AssertUnwindSafe(|| (self.inner)(input))) + }); + + match res { + Ok(stream) => __internal::token_stream_inner(stream), + Err(e) => { + let msg = "proc macro panicked"; + let mut err = ecx.struct_span_fatal(span, msg); + if let Some(s) = e.downcast_ref::() { + err.help(&format!("message: {}", s)); + } + if let Some(s) = e.downcast_ref::<&'static str>() { + err.help(&format!("message: {}", s)); + } + + err.emit(); + panic!(FatalError); + } + } + } +} diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index 325f09a83ddab..9c96ad547e1ae 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -27,6 +27,9 @@ use syntax_pos::{Span, DUMMY_SP}; use deriving; +const PROC_MACRO_KINDS: [&'static str; 3] = + ["proc_macro_derive", "proc_macro_attribute", "proc_macro"]; + struct ProcMacroDerive { trait_name: ast::Name, function_name: Ident, @@ -34,14 +37,15 @@ struct ProcMacroDerive { attrs: Vec, } -struct AttrProcMacro { +struct ProcMacroDef { function_name: Ident, span: Span, } struct CollectProcMacros<'a> { derives: Vec, - attr_macros: Vec, + attr_macros: Vec, + bang_macros: Vec, in_root: bool, handler: &'a errors::Handler, is_proc_macro_crate: bool, @@ -58,17 +62,18 @@ pub fn modify(sess: &ParseSess, let ecfg = ExpansionConfig::default("proc_macro".to_string()); let mut cx = ExtCtxt::new(sess, ecfg, resolver); - let (derives, attr_macros) = { + let (derives, attr_macros, bang_macros) = { let mut collect = CollectProcMacros { derives: Vec::new(), attr_macros: Vec::new(), + bang_macros: Vec::new(), in_root: true, handler: handler, is_proc_macro_crate: is_proc_macro_crate, is_test_crate: is_test_crate, }; visit::walk_crate(&mut collect, &krate); - (collect.derives, collect.attr_macros) + (collect.derives, collect.attr_macros, collect.bang_macros) }; if !is_proc_macro_crate { @@ -83,7 +88,7 @@ pub fn modify(sess: &ParseSess, return krate; } - krate.module.items.push(mk_registrar(&mut cx, &derives, &attr_macros)); + krate.module.items.push(mk_registrar(&mut cx, &derives, &attr_macros, &bang_macros)); if krate.exported_macros.len() > 0 { handler.err("cannot export macro_rules! macros from a `proc-macro` \ @@ -93,6 +98,10 @@ pub fn modify(sess: &ParseSess, return krate } +fn is_proc_macro_attr(attr: &ast::Attribute) -> bool { + PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(kind)) +} + impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { if self.is_proc_macro_crate && @@ -196,12 +205,12 @@ impl<'a> CollectProcMacros<'a> { fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { if let Some(_) = attr.meta_item_list() { self.handler.span_err(attr.span, "`#[proc_macro_attribute]` attribute - cannot contain any meta items"); + does not take any arguments"); return; } if self.in_root && item.vis == ast::Visibility::Public { - self.attr_macros.push(AttrProcMacro { + self.attr_macros.push(ProcMacroDef { span: item.span, function_name: item.ident, }); @@ -215,6 +224,29 @@ impl<'a> CollectProcMacros<'a> { self.handler.span_err(item.span, msg); } } + + fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { + if let Some(_) = attr.meta_item_list() { + self.handler.span_err(attr.span, "`#[proc_macro]` attribute + does not take any arguments"); + return; + } + + if self.in_root && item.vis == ast::Visibility::Public { + self.bang_macros.push(ProcMacroDef { + span: item.span, + function_name: item.ident, + }); + } else { + let msg = if !self.in_root { + "functions tagged with `#[proc_macro]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro]` must be `pub`" + }; + self.handler.span_err(item.span, msg); + } + } } impl<'a> Visitor<'a> for CollectProcMacros<'a> { @@ -232,7 +264,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { let mut found_attr: Option<&'a ast::Attribute> = None; for attr in &item.attrs { - if attr.check_name("proc_macro_derive") || attr.check_name("proc_macro_attribute") { + if is_proc_macro_attr(&attr) { if let Some(prev_attr) = found_attr { let msg = if attr.name() == prev_attr.name() { format!("Only one `#[{}]` attribute is allowed on any given function", @@ -285,6 +317,8 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { self.collect_custom_derive(item, attr); } else if attr.check_name("proc_macro_attribute") { self.collect_attr_proc_macro(item, attr); + } else if attr.check_name("proc_macro") { + self.collect_bang_proc_macro(item, attr); }; visit::walk_item(self, item); @@ -320,7 +354,8 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { // } fn mk_registrar(cx: &mut ExtCtxt, custom_derives: &[ProcMacroDerive], - custom_attrs: &[AttrProcMacro]) -> P { + custom_attrs: &[ProcMacroDef], + custom_macros: &[ProcMacroDef]) -> P { let eid = cx.codemap().record_expansion(ExpnInfo { call_site: DUMMY_SP, callee: NameAndSpan { @@ -342,6 +377,7 @@ fn mk_registrar(cx: &mut ExtCtxt, let registrar = Ident::from_str("registrar"); let register_custom_derive = Ident::from_str("register_custom_derive"); let register_attr_proc_macro = Ident::from_str("register_attr_proc_macro"); + let register_bang_proc_macro = Ident::from_str("register_bang_proc_macro"); let mut stmts = custom_derives.iter().map(|cd| { let path = cx.path_global(cd.span, vec![cd.function_name]); @@ -371,6 +407,18 @@ fn mk_registrar(cx: &mut ExtCtxt, vec![registrar, name, cx.expr_path(path)])) })); + stmts.extend(custom_macros.iter().map(|cm| { + let name = cx.expr_str(cm.span, cm.function_name.name); + let path = cx.path_global(cm.span, vec![cm.function_name]); + let registrar = cx.expr_ident(cm.span, registrar); + + let ufcs_path = cx.path(span, + vec![proc_macro, __internal, registry, register_bang_proc_macro]); + + cx.stmt_expr(cx.expr_call(span, cx.expr_path(ufcs_path), + vec![registrar, name, cx.expr_path(path)])) + })); + let path = cx.path(span, vec![proc_macro, __internal, registry]); let registrar_path = cx.ty_path(path); let arg_ty = cx.ty_rptr(span, registrar_path, None, ast::Mutability::Mutable); diff --git a/src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs new file mode 100644 index 0000000000000..89ac11b309d75 --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/auxiliary/bang_proc_macro.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +// force-host +// no-prefer-dynamic +#![feature(proc_macro)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn bang_proc_macro(input: TokenStream) -> TokenStream { + input +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs b/src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs new file mode 100644 index 0000000000000..7ecc685357ee6 --- /dev/null +++ b/src/test/compile-fail-fulldeps/proc-macro/macro-use-bang.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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. + +// aux-build:bang_proc_macro.rs + +#![feature(proc_macro)] + +#[macro_use] +extern crate bang_proc_macro; + +fn main() { + bang_proc_macro!(println!("Hello, world!")); + //~^ ERROR: procedural macros cannot be imported with `#[macro_use]` +} diff --git a/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs b/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs index eac0be6f84874..e0066dd43be89 100644 --- a/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs +++ b/src/test/compile-fail-fulldeps/proc-macro/resolve-error.rs @@ -11,6 +11,7 @@ // aux-build:derive-foo.rs // aux-build:derive-clona.rs // aux-build:attr_proc_macro.rs +// aux-build:bang_proc_macro.rs #![feature(proc_macro)] @@ -19,13 +20,19 @@ extern crate derive_foo; #[macro_use] extern crate derive_clona; extern crate attr_proc_macro; +extern crate bang_proc_macro; use attr_proc_macro::attr_proc_macro; +use bang_proc_macro::bang_proc_macro; macro_rules! FooWithLongNam { () => {} } +macro_rules! attr_proc_mac { + () => {} +} + #[derive(FooWithLongNan)] //~^ ERROR cannot find derive macro `FooWithLongNan` in this scope //~^^ HELP did you mean `FooWithLongName`? @@ -61,7 +68,12 @@ fn main() { attr_proc_macra!(); //~^ ERROR cannot find macro `attr_proc_macra!` in this scope + //~^^ HELP did you mean `attr_proc_mac!`? Dlona!(); //~^ ERROR cannot find macro `Dlona!` in this scope + + bang_proc_macrp!(); + //~^ ERROR cannot find macro `bang_proc_macrp!` in this scope + //~^^ HELP did you mean `bang_proc_macro!`? } diff --git a/src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs b/src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs new file mode 100644 index 0000000000000..122a47aff7198 --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/auxiliary/bang-macro.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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. + +// no-prefer-dynamic +#![feature(proc_macro)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn rewrite(input: TokenStream) -> TokenStream { + let input = input.to_string(); + + assert_eq!(input, r#""Hello, world!""#); + + r#""NOT Hello, world!""#.parse().unwrap() +} diff --git a/src/test/run-pass-fulldeps/proc-macro/bang-macro.rs b/src/test/run-pass-fulldeps/proc-macro/bang-macro.rs new file mode 100644 index 0000000000000..531bd0dd3569d --- /dev/null +++ b/src/test/run-pass-fulldeps/proc-macro/bang-macro.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +// aux-build:bang-macro.rs + +#![feature(proc_macro)] + +extern crate bang_macro; +use bang_macro::rewrite; + +fn main() { + assert_eq!(rewrite!("Hello, world!"), "NOT Hello, world!"); +} From f2017f4561852bf65c39fc498cf035a5f385a065 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sun, 26 Feb 2017 19:42:11 +0100 Subject: [PATCH 08/10] Panic on errors in `format!` or `::to_string` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … instead of silently ignoring a result. `fmt::Write for String` never returns `Err`, so implementations of `Display` (or other traits of that family) never should either. Fixes #40103 --- src/libcollections/fmt.rs | 3 ++- src/libcollections/macros.rs | 6 ++++++ src/libcollections/string.rs | 9 ++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/libcollections/fmt.rs b/src/libcollections/fmt.rs index 079541235a25e..dfd292176d2f9 100644 --- a/src/libcollections/fmt.rs +++ b/src/libcollections/fmt.rs @@ -524,6 +524,7 @@ use string; pub fn format(args: Arguments) -> string::String { let capacity = args.estimated_capacity(); let mut output = string::String::with_capacity(capacity); - let _ = output.write_fmt(args); + output.write_fmt(args) + .expect("a formatting trait implementation returned an error"); output } diff --git a/src/libcollections/macros.rs b/src/libcollections/macros.rs index 3115be00a4d72..396a917dfde26 100644 --- a/src/libcollections/macros.rs +++ b/src/libcollections/macros.rs @@ -72,6 +72,12 @@ macro_rules! vec { /// /// [fmt]: ../std/fmt/index.html /// +/// # Panics +/// +/// `format!` panics if a formatting trait implementation returns an error. +/// This indicates an incorrect implementation +/// since `fmt::Write for String` never returns an error itself. +/// /// # Examples /// /// ``` diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 10bc5ab88c93f..a8942619cea16 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -1888,13 +1888,20 @@ pub trait ToString { fn to_string(&self) -> String; } +/// # Panics +/// +/// In this implementation, the `to_string` method panics +/// if the `Display` implementation returns an error. +/// This indicates an incorrect `Display` implementation +/// since `fmt::Write for String` never returns an error itself. #[stable(feature = "rust1", since = "1.0.0")] impl ToString for T { #[inline] default fn to_string(&self) -> String { use core::fmt::Write; let mut buf = String::new(); - let _ = buf.write_fmt(format_args!("{}", self)); + buf.write_fmt(format_args!("{}", self)) + .expect("a Display implementation return an error unexpectedly"); buf.shrink_to_fit(); buf } From 6e259dc77870d278a37bbc42bcbc5fad64b75930 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Tue, 28 Feb 2017 18:18:54 +0000 Subject: [PATCH 09/10] note -> help --- src/librustc/traits/error_reporting.rs | 2 +- src/libsyntax/ext/base.rs | 2 +- src/test/ui/did_you_mean/recursion_limit.stderr | 2 +- src/test/ui/did_you_mean/recursion_limit_macro.stderr | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 661d47199df13..6ba593da51e8f 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -995,7 +995,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder) { let current_limit = self.tcx.sess.recursion_limit.get(); let suggested_limit = current_limit * 2; - err.note(&format!( + err.help(&format!( "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", suggested_limit)); } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index e7f794328b893..7085d0440d3c7 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -678,7 +678,7 @@ impl<'a> ExtCtxt<'a> { let mut err = self.struct_span_fatal(ei.call_site, &format!("recursion limit reached while expanding the macro `{}`", ei.callee.name())); - err.note(&format!( + err.help(&format!( "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate", suggested_limit)); err.emit(); diff --git a/src/test/ui/did_you_mean/recursion_limit.stderr b/src/test/ui/did_you_mean/recursion_limit.stderr index 524aab87ccfe2..d157c5de6c7f5 100644 --- a/src/test/ui/did_you_mean/recursion_limit.stderr +++ b/src/test/ui/did_you_mean/recursion_limit.stderr @@ -4,7 +4,7 @@ error[E0275]: overflow evaluating the requirement `K: std::marker::Send` 44 | is_send::(); | ^^^^^^^^^^^^ | - = note: consider adding a `#![recursion_limit="20"]` attribute to your crate + = help: consider adding a `#![recursion_limit="20"]` attribute to your crate = note: required because it appears within the type `J` = note: required because it appears within the type `I` = note: required because it appears within the type `H` diff --git a/src/test/ui/did_you_mean/recursion_limit_macro.stderr b/src/test/ui/did_you_mean/recursion_limit_macro.stderr index 6bed78a3badc8..19aac1f77e7c1 100644 --- a/src/test/ui/did_you_mean/recursion_limit_macro.stderr +++ b/src/test/ui/did_you_mean/recursion_limit_macro.stderr @@ -7,5 +7,5 @@ error: recursion limit reached while expanding the macro `recurse` 24 | recurse!(0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9); | -------------------------------------------------- in this macro invocation | - = note: consider adding a `#![recursion_limit="20"]` attribute to your crate + = help: consider adding a `#![recursion_limit="20"]` attribute to your crate From accc7f4c6a8af93ce93f7f15d0ce700d8952d50a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 2 Mar 2017 11:28:09 +0100 Subject: [PATCH 10/10] LLVM: Update submodule to include x86-interrupt ABI patches --- src/llvm | 2 +- src/rustllvm/llvm-auto-clean-trigger | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm b/src/llvm index ceb177eeefa7d..50ab09fb43f03 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit ceb177eeefa7d67ca29230d2e7e8584f97d4fdad +Subproject commit 50ab09fb43f038e4f824eea6cb278f560d3e8621 diff --git a/src/rustllvm/llvm-auto-clean-trigger b/src/rustllvm/llvm-auto-clean-trigger index ab36e9a2c2b20..57f37ea050c1b 100644 --- a/src/rustllvm/llvm-auto-clean-trigger +++ b/src/rustllvm/llvm-auto-clean-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be forcibly cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2017-02-15 +2017-03-02