X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Flibrustc_mir%2Ftransform%2Fconst_prop.rs;h=05e51c5430d7fe5aafd0f4b9741974e09722dc70;hb=aa1e6db70900cf5d11a843bc4234de0548677aba;hp=94f96d46996d831b34029b52db6d97557248d2e7;hpb=662fb069fd45d44c5828c335690598d712226325;p=rust.git diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 94f96d46996..05e51c5430d 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -17,7 +17,7 @@ use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind}; use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem}; use rustc::mir::visit::{Visitor, PlaceContext}; -use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind}; +use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind, ScalarMaybeUndef}; use rustc::ty::{TyCtxt, self, Instance}; use rustc::mir::interpret::{Value, Scalar, GlobalId, EvalResult}; use interpret::EvalContext; @@ -65,7 +65,7 @@ fn run_pass<'a, 'tcx>(&self, } } -type Const<'tcx> = (Value, ty::Ty<'tcx>, Span); +type Const<'tcx> = (Value, TyLayout<'tcx>, Span); /// Finds optimization opportunities on the MIR. struct ConstPropagator<'b, 'a, 'tcx:'a+'b> { @@ -144,18 +144,100 @@ fn use_ecx( }; let r = match f(self) { Ok(val) => Some(val), - Err(err) => { - match err.kind { + Err(error) => { + let (stacktrace, span) = self.ecx.generate_stacktrace(None); + let diagnostic = ConstEvalErr { span, error, stacktrace }; + use rustc::mir::interpret::EvalErrorKind::*; + match diagnostic.error.kind { // don't report these, they make no sense in a const prop context - EvalErrorKind::MachineError(_) => {}, - _ => { - let (frames, span) = self.ecx.generate_stacktrace(None); - let err = ConstEvalErr { - span, - error: err, - stacktrace: frames, - }; - err.report_as_lint( + | MachineError(_) + // at runtime these transformations might make sense + // FIXME: figure out the rules and start linting + | FunctionPointerTyMismatch(..) + // fine at runtime, might be a register address or sth + | ReadBytesAsPointer + // fine at runtime + | ReadForeignStatic + | Unimplemented(_) + // don't report const evaluator limits + | StackFrameLimitReached + | NoMirFor(..) + | InlineAsm + => {}, + + | InvalidMemoryAccess + | DanglingPointerDeref + | DoubleFree + | InvalidFunctionPointer + | InvalidBool + | InvalidDiscriminant + | PointerOutOfBounds { .. } + | InvalidNullPointerUsage + | MemoryLockViolation { .. } + | MemoryAcquireConflict { .. } + | ValidationFailure(..) + | InvalidMemoryLockRelease { .. } + | DeallocatedLockedMemory { .. } + | InvalidPointerMath + | ReadUndefBytes + | DeadLocal + | InvalidBoolOp(_) + | DerefFunctionPointer + | ExecuteMemory + | Intrinsic(..) + | InvalidChar(..) + | AbiViolation(_) + | AlignmentCheckFailed{..} + | CalledClosureAsFunction + | VtableForArgumentlessMethod + | ModifiedConstantMemory + | AssumptionNotHeld + // FIXME: should probably be removed and turned into a bug! call + | TypeNotPrimitive(_) + | ReallocatedWrongMemoryKind(_, _) + | DeallocatedWrongMemoryKind(_, _) + | ReallocateNonBasePtr + | DeallocateNonBasePtr + | IncorrectAllocationInformation(..) + | UnterminatedCString(_) + | HeapAllocZeroBytes + | HeapAllocNonPowerOfTwoAlignment(_) + | Unreachable + | ReadFromReturnPointer + | GeneratorResumedAfterReturn + | GeneratorResumedAfterPanic + | ReferencedConstant(_) + | InfiniteLoop + => { + // FIXME: report UB here + }, + + | OutOfTls + | TlsOutOfBounds + | PathNotFound(_) + => bug!("these should not be in rustc, but in miri's machine errors"), + + | Layout(_) + | UnimplementedTraitSelection + | TypeckError + | TooGeneric + | CheckMatchError + // these are just noise + => {}, + + // non deterministic + | ReadPointerAsBytes + // FIXME: implement + => {}, + + | Panic + | BoundsCheck{..} + | Overflow(_) + | OverflowNeg + | DivisionByZero + | RemainderByZero + => { + diagnostic.report_as_lint( self.ecx.tcx, "this expression will panic at runtime", lint_root, @@ -176,7 +258,10 @@ fn eval_constant( ) -> Option> { self.ecx.tcx.span = source_info.span; match self.ecx.const_to_value(c.literal.val) { - Ok(val) => Some((val, c.literal.ty, c.span)), + Ok(val) => { + let layout = self.tcx.layout_of(self.param_env.and(c.literal.ty)).ok()?; + Some((val, layout, c.span)) + }, Err(error) => { let (stacktrace, span) = self.ecx.generate_stacktrace(None); let err = ConstEvalErr { @@ -199,11 +284,11 @@ fn eval_place(&mut self, place: &Place<'tcx>, source_info: SourceInfo) -> Option Place::Projection(ref proj) => match proj.elem { ProjectionElem::Field(field, _) => { trace!("field proj on {:?}", proj.base); - let (base, ty, span) = self.eval_place(&proj.base, source_info)?; + let (base, layout, span) = self.eval_place(&proj.base, source_info)?; let valty = self.use_ecx(source_info, |this| { - this.ecx.read_field(base, None, field, ty) + this.ecx.read_field(base, None, field, layout) })?; - Some((valty.value, valty.ty, span)) + Some((valty.0, valty.1, span)) }, _ => None, }, @@ -243,14 +328,14 @@ fn eval_operand(&mut self, op: &Operand<'tcx>, source_info: SourceInfo) -> Optio fn const_prop( &mut self, rvalue: &Rvalue<'tcx>, - place_ty: ty::Ty<'tcx>, + place_layout: TyLayout<'tcx>, source_info: SourceInfo, ) -> Option> { let span = source_info.span; match *rvalue { // This branch exists for the sanity type check Rvalue::Use(Operand::Constant(ref c)) => { - assert_eq!(c.ty, place_ty); + assert_eq!(c.ty, place_layout.ty); self.eval_constant(c, source_info) }, Rvalue::Use(ref op) => { @@ -263,15 +348,15 @@ fn const_prop( Rvalue::Discriminant(..) => None, Rvalue::Cast(kind, ref operand, _) => { - let (value, ty, span) = self.eval_operand(operand, source_info)?; + let (value, layout, span) = self.eval_operand(operand, source_info)?; self.use_ecx(source_info, |this| { - let dest_ptr = this.ecx.alloc_ptr(place_ty)?; - let place_align = this.ecx.layout_of(place_ty)?.align; + let dest_ptr = this.ecx.alloc_ptr(place_layout)?; + let place_align = place_layout.align; let dest = ::interpret::Place::from_ptr(dest_ptr, place_align); - this.ecx.cast(ValTy { value, ty }, kind, place_ty, dest)?; + this.ecx.cast(ValTy { value, ty: layout.ty }, kind, place_layout.ty, dest)?; Ok(( Value::ByRef(dest_ptr.into(), place_align), - place_ty, + place_layout, span, )) }) @@ -280,15 +365,14 @@ fn const_prop( // FIXME(oli-obk): evaluate static/constant slice lengths Rvalue::Len(_) => None, Rvalue::NullaryOp(NullOp::SizeOf, ty) => { - let param_env = self.tcx.param_env(self.source.def_id); - type_size_of(self.tcx, param_env, ty).map(|n| ( + type_size_of(self.tcx, self.param_env, ty).and_then(|n| Some(( Value::Scalar(Scalar::Bits { bits: n as u128, - defined: self.tcx.data_layout.pointer_size.bits() as u8, - }), - self.tcx.types.usize, + size: self.tcx.data_layout.pointer_size.bytes() as u8, + }.into()), + self.tcx.layout_of(self.param_env.and(self.tcx.types.usize)).ok()?, span, - )) + ))) } Rvalue::UnaryOp(op, ref arg) => { let def_id = if self.tcx.is_closure(self.source.def_id) { @@ -304,10 +388,10 @@ fn const_prop( let val = self.eval_operand(arg, source_info)?; let prim = self.use_ecx(source_info, |this| { - this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1 }) + this.ecx.value_to_scalar(ValTy { value: val.0, ty: val.1.ty }) })?; let val = self.use_ecx(source_info, |this| this.ecx.unary_op(op, prim, val.1))?; - Some((Value::Scalar(val), place_ty, span)) + Some((Value::Scalar(val.into()), place_layout, span)) } Rvalue::CheckedBinaryOp(op, ref left, ref right) | Rvalue::BinaryOp(op, ref left, ref right) => { @@ -325,7 +409,7 @@ fn const_prop( } let r = self.use_ecx(source_info, |this| { - this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1 }) + this.ecx.value_to_scalar(ValTy { value: right.0, ty: right.1.ty }) })?; if op == BinOp::Shr || op == BinOp::Shl { let left_ty = left.ty(self.mir, self.tcx); @@ -335,7 +419,7 @@ fn const_prop( .unwrap() .size .bits(); - let right_size = self.tcx.layout_of(self.param_env.and(right.1)).unwrap().size; + let right_size = right.1.size; if r.to_bits(right_size).ok().map_or(false, |b| b >= left_bits as u128) { let source_scope_local_data = match self.mir.source_scope_local_data { ClearCrossCrate::Set(ref data) => data, @@ -357,16 +441,16 @@ fn const_prop( } let left = self.eval_operand(left, source_info)?; let l = self.use_ecx(source_info, |this| { - this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1 }) + this.ecx.value_to_scalar(ValTy { value: left.0, ty: left.1.ty }) })?; trace!("const evaluating {:?} for {:?} and {:?}", op, left, right); let (val, overflow) = self.use_ecx(source_info, |this| { - this.ecx.binary_op(op, l, left.1, r, right.1) + this.ecx.binary_op(op, l, left.1.ty, r, right.1.ty) })?; let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue { Value::ScalarPair( - val, - Scalar::from_bool(overflow), + val.into(), + Scalar::from_bool(overflow).into(), ) } else { if overflow { @@ -374,9 +458,9 @@ fn const_prop( let _: Option<()> = self.use_ecx(source_info, |_| Err(err)); return None; } - Value::Scalar(val) + Value::Scalar(val.into()) }; - Some((val, place_ty, span)) + Some((val, place_layout, span)) }, } } @@ -462,16 +546,18 @@ fn visit_statement( ) { trace!("visit_statement: {:?}", statement); if let StatementKind::Assign(ref place, ref rval) = statement.kind { - let place_ty = place + let place_ty: ty::Ty<'tcx> = place .ty(&self.mir.local_decls, self.tcx) .to_ty(self.tcx); - if let Some(value) = self.const_prop(rval, place_ty, statement.source_info) { - if let Place::Local(local) = *place { - trace!("checking whether {:?} can be stored to {:?}", value, local); - if self.can_const_prop[local] { - trace!("storing {:?} to {:?}", value, local); - assert!(self.places[local].is_none()); - self.places[local] = Some(value); + if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) { + if let Some(value) = self.const_prop(rval, place_layout, statement.source_info) { + if let Place::Local(local) = *place { + trace!("checking whether {:?} can be stored to {:?}", value, local); + if self.can_const_prop[local] { + trace!("storing {:?} to {:?}", value, local); + assert!(self.places[local].is_none()); + self.places[local] = Some(value); + } } } } @@ -490,7 +576,7 @@ fn visit_terminator_kind( if let TerminatorKind::Assert { expected, msg, cond, .. } = kind { if let Some(value) = self.eval_operand(cond, source_info) { trace!("assertion on {:?} should be {:?}", value, expected); - if Value::Scalar(Scalar::from_bool(*expected)) != value.0 { + if Value::Scalar(Scalar::from_bool(*expected).into()) != value.0 { // poison all places this operand references so that further code // doesn't use the invalid value match cond { @@ -527,14 +613,18 @@ fn visit_terminator_kind( .eval_operand(len, source_info) .expect("len must be const"); let len = match len.0 { - Value::Scalar(Scalar::Bits { bits, ..}) => bits, + Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { + bits, .. + })) => bits, _ => bug!("const len not primitive: {:?}", len), }; let index = self .eval_operand(index, source_info) .expect("index must be const"); let index = match index.0 { - Value::Scalar(Scalar::Bits { bits, .. }) => bits, + Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { + bits, .. + })) => bits, _ => bug!("const index not primitive: {:?}", index), }; format!(