Skip to content

Commit 6beb06e

Browse files
committed
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 #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.
2 parents 7d6e5b9 + adcb37e commit 6beb06e

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

Diff for: src/librustc_mir/build/block.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
143143
if let Some(expr) = expr {
144144
unpack!(block = this.into(destination, block, expr));
145145
} else {
146-
this.cfg.push_assign_unit(block, source_info, destination);
146+
// If a block has no trailing expression, then it is given an implicit return type.
147+
// This return type is usually `()`, unless the block is diverging, in which case the
148+
// return type is `!`. For the unit type, we need to actually return the unit, but in
149+
// the case of `!`, no return value is required, as the block will never return.
150+
let tcx = this.hir.tcx();
151+
let ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
152+
if ty.is_nil() {
153+
// We only want to assign an implicit `()` as the return value of the block if the
154+
// block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
155+
this.cfg.push_assign_unit(block, source_info, destination);
156+
}
147157
}
148158
// Finally, we pop all the let scopes before exiting out from the scope of block
149159
// itself.

Diff for: src/librustc_mir/build/expr/into.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
272272
ExprKind::Continue { .. } |
273273
ExprKind::Break { .. } |
274274
ExprKind::InlineAsm { .. } |
275-
ExprKind::Return {.. } => {
275+
ExprKind::Return { .. } => {
276276
unpack!(block = this.stmt_expr(block, expr));
277277
this.cfg.push_assign_unit(block, source_info, destination);
278278
block.unit()

Diff for: src/test/run-pass/never-type-rvalues.rs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(never_type)]
12+
#![allow(dead_code)]
13+
#![allow(path_statements)]
14+
#![allow(unreachable_patterns)]
15+
16+
fn never_direct(x: !) {
17+
x;
18+
}
19+
20+
fn never_ref_pat(ref x: !) {
21+
*x;
22+
}
23+
24+
fn never_ref(x: &!) {
25+
let &y = x;
26+
y;
27+
}
28+
29+
fn never_pointer(x: *const !) {
30+
unsafe {
31+
*x;
32+
}
33+
}
34+
35+
fn never_slice(x: &[!]) {
36+
x[0];
37+
}
38+
39+
fn never_match(x: Result<(), !>) {
40+
match x {
41+
Ok(_) => {},
42+
Err(_) => {},
43+
}
44+
}
45+
46+
pub fn main() { }

0 commit comments

Comments
 (0)