mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-10 14:57:14 +00:00
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:000fbbc9b8/src/librustc_mir/build/expr/into.rs (L52-L59)
Which ends up calling `ast_block_stmts` on the block `{x;}`, which triggers this condition:000fbbc9b8/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.
This commit is contained in:
commit
6beb06ee5e
@ -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.
|
||||
|
@ -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()
|
||||
|
46
src/test/run-pass/never-type-rvalues.rs
Normal file
46
src/test/run-pass/never-type-rvalues.rs
Normal file
@ -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() { }
|
Loading…
Reference in New Issue
Block a user