summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-01-28 00:40:10 +0000
committerbors <bors@rust-lang.org>2018-01-28 00:40:10 +0000
commit6beb06ee5ef6f031aab7586211f1818d346033d6 (patch)
treeba541694b09ecbc9a955e0256e240770d70d0d5f
parent7d6e5b9da0865fbc9fa54edb324fefe80f358da7 (diff)
parentadcb37e275ee9b06114d4debdfe453cbc89b7716 (diff)
downloadrust-6beb06ee5ef6f031aab7586211f1818d346033d6.tar.gz
Auto merge of #47746 - varkor:never-type-ice, r=nikomatsakis
Fix never-type rvalue ICE This fixes #43061. r? @nikomatsakis A small post-mortem as a follow-up to our investigations in https://github.com/rust-lang/rust/pull/47291: The problem as I understand it is that when `NeverToAny` coercions are made, the expression/statement that is coerced may be enclosed in a block. In our case, the statement `x;` was being transformed to something like: `NeverToAny( {x;} )`. Then, `NeverToAny` is transformed into an expression: https://github.com/rust-lang/rust/blob/000fbbc9b8f88adc6a417f1caef41161f104250f/src/librustc_mir/build/expr/into.rs#L52-L59 Which ends up calling `ast_block_stmts` on the block `{x;}`, which triggers this condition: https://github.com/rust-lang/rust/blob/000fbbc9b8f88adc6a417f1caef41161f104250f/src/librustc_mir/build/block.rs#L141-L147 In our case, there is no return expression, so `push_assign_unit` is called. But the block has already been recorded as _diverging_, meaning the result of the block will be assigned to a location of type `!`, rather than `()`. This causes the MIR error. I'm assuming the `NeverToAny` coercion code is doing what it's supposed to (there don't seem to be any other problems), so fixing the issue simply consists of checking that the destination for the return value actually _is_ supposed to be a unit. (If no return value is given, the only other possible type for the return value is `!`, which can just be ignored, as it will be unreachable anyway.) I checked the other cases of `push_assign_unit`, and it didn't look like they could be affected by the divergence issue (blocks are kind of special-cased in this regard as far as I can tell), so this should be sufficient to fix the issue.
-rw-r--r--src/librustc_mir/build/block.rs12
-rw-r--r--src/librustc_mir/build/expr/into.rs2
-rw-r--r--src/test/run-pass/never-type-rvalues.rs46
3 files changed, 58 insertions, 2 deletions
diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs
index b2b615d29a5..ef30b1e4522 100644
--- a/src/librustc_mir/build/block.rs
+++ b/src/librustc_mir/build/block.rs
@@ -143,7 +143,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
if let Some(expr) = expr {
unpack!(block = this.into(destination, block, expr));
} else {
- this.cfg.push_assign_unit(block, source_info, destination);
+ // If a block has no trailing expression, then it is given an implicit return type.
+ // This return type is usually `()`, unless the block is diverging, in which case the
+ // return type is `!`. For the unit type, we need to actually return the unit, but in
+ // the case of `!`, no return value is required, as the block will never return.
+ let tcx = this.hir.tcx();
+ let ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
+ if ty.is_nil() {
+ // We only want to assign an implicit `()` as the return value of the block if the
+ // block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
+ this.cfg.push_assign_unit(block, source_info, destination);
+ }
}
// Finally, we pop all the let scopes before exiting out from the scope of block
// itself.
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index 3e0ccc7d072..68b23d1ae17 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -272,7 +272,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
ExprKind::Continue { .. } |
ExprKind::Break { .. } |
ExprKind::InlineAsm { .. } |
- ExprKind::Return {.. } => {
+ ExprKind::Return { .. } => {
unpack!(block = this.stmt_expr(block, expr));
this.cfg.push_assign_unit(block, source_info, destination);
block.unit()
diff --git a/src/test/run-pass/never-type-rvalues.rs b/src/test/run-pass/never-type-rvalues.rs
new file mode 100644
index 00000000000..bda288f4086
--- /dev/null
+++ b/src/test/run-pass/never-type-rvalues.rs
@@ -0,0 +1,46 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(never_type)]
+#![allow(dead_code)]
+#![allow(path_statements)]
+#![allow(unreachable_patterns)]
+
+fn never_direct(x: !) {
+ x;
+}
+
+fn never_ref_pat(ref x: !) {
+ *x;
+}
+
+fn never_ref(x: &!) {
+ let &y = x;
+ y;
+}
+
+fn never_pointer(x: *const !) {
+ unsafe {
+ *x;
+ }
+}
+
+fn never_slice(x: &[!]) {
+ x[0];
+}
+
+fn never_match(x: Result<(), !>) {
+ match x {
+ Ok(_) => {},
+ Err(_) => {},
+ }
+}
+
+pub fn main() { }