trans::mir::constant - fix assignment error recovery
We used to not store anything when the RHS of an assignment returned an error, which caused ICEs downstream. Fixes #43197.
This commit is contained in:
parent
1057a728f5
commit
93db1f9923
@ -222,15 +222,24 @@ struct MirConstContext<'a, 'tcx: 'a> {
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
|
||||
/// Values of locals in a constant or const fn.
|
||||
locals: IndexVec<mir::Local, Option<Const<'tcx>>>
|
||||
locals: IndexVec<mir::Local, Option<Result<Const<'tcx>, ConstEvalErr<'tcx>>>>
|
||||
}
|
||||
|
||||
fn add_err<'tcx, U, V>(failure: &mut Result<U, ConstEvalErr<'tcx>>,
|
||||
value: &Result<V, ConstEvalErr<'tcx>>)
|
||||
{
|
||||
if let &Err(ref err) = value {
|
||||
if failure.is_ok() {
|
||||
*failure = Err(err.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
||||
fn new(ccx: &'a CrateContext<'a, 'tcx>,
|
||||
mir: &'a mir::Mir<'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
args: IndexVec<mir::Local, Const<'tcx>>)
|
||||
args: IndexVec<mir::Local, Result<Const<'tcx>, ConstEvalErr<'tcx>>>)
|
||||
-> MirConstContext<'a, 'tcx> {
|
||||
let mut context = MirConstContext {
|
||||
ccx: ccx,
|
||||
@ -249,7 +258,7 @@ fn new(ccx: &'a CrateContext<'a, 'tcx>,
|
||||
fn trans_def(ccx: &'a CrateContext<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
args: IndexVec<mir::Local, Const<'tcx>>)
|
||||
args: IndexVec<mir::Local, Result<Const<'tcx>, ConstEvalErr<'tcx>>>)
|
||||
-> Result<Const<'tcx>, ConstEvalErr<'tcx>> {
|
||||
let instance = monomorphize::resolve(ccx.shared(), def_id, substs);
|
||||
let mir = ccx.tcx().instance_mir(instance.def);
|
||||
@ -278,10 +287,9 @@ fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalErr<'tcx>> {
|
||||
mir::StatementKind::Assign(ref dest, ref rvalue) => {
|
||||
let ty = dest.ty(self.mir, tcx);
|
||||
let ty = self.monomorphize(&ty).to_ty(tcx);
|
||||
match self.const_rvalue(rvalue, ty, span) {
|
||||
Ok(value) => self.store(dest, value, span),
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
let value = self.const_rvalue(rvalue, ty, span);
|
||||
add_err(&mut failure, &value);
|
||||
self.store(dest, value, span);
|
||||
}
|
||||
mir::StatementKind::StorageLive(_) |
|
||||
mir::StatementKind::StorageDead(_) |
|
||||
@ -301,9 +309,9 @@ fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalErr<'tcx>> {
|
||||
mir::TerminatorKind::Goto { target } => target,
|
||||
mir::TerminatorKind::Return => {
|
||||
failure?;
|
||||
return Ok(self.locals[mir::RETURN_POINTER].unwrap_or_else(|| {
|
||||
return self.locals[mir::RETURN_POINTER].clone().unwrap_or_else(|| {
|
||||
span_bug!(span, "no returned value in constant");
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, .. } => {
|
||||
@ -342,33 +350,30 @@ fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalErr<'tcx>> {
|
||||
|
||||
let mut arg_vals = IndexVec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
match self.const_operand(arg, span) {
|
||||
Ok(arg) => { arg_vals.push(arg); },
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
let arg_val = self.const_operand(arg, span);
|
||||
add_err(&mut failure, &arg_val);
|
||||
arg_vals.push(arg_val);
|
||||
}
|
||||
if let Some((ref dest, target)) = *destination {
|
||||
if fn_ty.fn_sig(tcx).abi() == Abi::RustIntrinsic {
|
||||
let value = match &tcx.item_name(def_id).as_str()[..] {
|
||||
let result = if fn_ty.fn_sig(tcx).abi() == Abi::RustIntrinsic {
|
||||
match &tcx.item_name(def_id).as_str()[..] {
|
||||
"size_of" => {
|
||||
let llval = C_uint(self.ccx,
|
||||
self.ccx.size_of(substs.type_at(0)));
|
||||
Const::new(llval, tcx.types.usize)
|
||||
Ok(Const::new(llval, tcx.types.usize))
|
||||
}
|
||||
"min_align_of" => {
|
||||
let llval = C_uint(self.ccx,
|
||||
self.ccx.align_of(substs.type_at(0)));
|
||||
Const::new(llval, tcx.types.usize)
|
||||
Ok(Const::new(llval, tcx.types.usize))
|
||||
}
|
||||
_ => span_bug!(span, "{:?} in constant", terminator.kind)
|
||||
};
|
||||
self.store(dest, value, span);
|
||||
} else {
|
||||
match MirConstContext::trans_def(self.ccx, def_id, substs, arg_vals) {
|
||||
Ok(value) => self.store(dest, value, span),
|
||||
Err(err) => if failure.is_ok() { failure = Err(err); }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MirConstContext::trans_def(self.ccx, def_id, substs, arg_vals)
|
||||
};
|
||||
add_err(&mut failure, &result);
|
||||
self.store(dest, result, span);
|
||||
target
|
||||
} else {
|
||||
span_bug!(span, "diverging {:?} in constant", terminator.kind);
|
||||
@ -379,7 +384,10 @@ fn trans(&mut self) -> Result<Const<'tcx>, ConstEvalErr<'tcx>> {
|
||||
}
|
||||
}
|
||||
|
||||
fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) {
|
||||
fn store(&mut self,
|
||||
dest: &mir::Lvalue<'tcx>,
|
||||
value: Result<Const<'tcx>, ConstEvalErr<'tcx>>,
|
||||
span: Span) {
|
||||
if let mir::Lvalue::Local(index) = *dest {
|
||||
self.locals[index] = Some(value);
|
||||
} else {
|
||||
@ -392,9 +400,9 @@ fn const_lvalue(&self, lvalue: &mir::Lvalue<'tcx>, span: Span)
|
||||
let tcx = self.ccx.tcx();
|
||||
|
||||
if let mir::Lvalue::Local(index) = *lvalue {
|
||||
return Ok(self.locals[index].unwrap_or_else(|| {
|
||||
return self.locals[index].clone().unwrap_or_else(|| {
|
||||
span_bug!(span, "{:?} not initialized", lvalue)
|
||||
}).as_lvalue());
|
||||
}).map(|v| v.as_lvalue());
|
||||
}
|
||||
|
||||
let lvalue = match *lvalue {
|
||||
|
21
src/test/ui/const-eval/issue-43197.rs
Normal file
21
src/test/ui/const-eval/issue-43197.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// 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 <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(const_fn)]
|
||||
|
||||
const fn foo(x: u32) -> u32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
const X: u32 = 0-1;
|
||||
const Y: u32 = foo(0-1);
|
||||
println!("{} {}", X, Y);
|
||||
}
|
28
src/test/ui/const-eval/issue-43197.stderr
Normal file
28
src/test/ui/const-eval/issue-43197.stderr
Normal file
@ -0,0 +1,28 @@
|
||||
warning: constant evaluation error: attempt to subtract with overflow. This will become a HARD ERROR in the future
|
||||
--> $DIR/issue-43197.rs:18:20
|
||||
|
|
||||
18 | const X: u32 = 0-1;
|
||||
| ^^^
|
||||
|
|
||||
= note: #[warn(const_err)] on by default
|
||||
|
||||
warning: constant evaluation error: attempt to subtract with overflow. This will become a HARD ERROR in the future
|
||||
--> $DIR/issue-43197.rs:19:20
|
||||
|
|
||||
19 | const Y: u32 = foo(0-1);
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0080]: constant evaluation error
|
||||
--> $DIR/issue-43197.rs:18:20
|
||||
|
|
||||
18 | const X: u32 = 0-1;
|
||||
| ^^^ attempt to subtract with overflow
|
||||
|
||||
error[E0080]: constant evaluation error
|
||||
--> $DIR/issue-43197.rs:19:24
|
||||
|
|
||||
19 | const Y: u32 = foo(0-1);
|
||||
| ^^^ attempt to subtract with overflow
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user