Skip to content

Commit 509cbf3

Browse files
committed
Auto merge of rust-lang#52498 - oli-obk:const_prop, r=nikomatsakis
Const propagate casts fixes rust-lang#49760 So... This fixes the original issue about the missing warnings. But our test suite contains fun things like ```rust fn foo() {} assert_eq!(foo as i16, foo as usize as i16); ``` Which, will result in > a raw memory access tried to access part of a pointer value as raw bytes on both sides of the assertion. Because well... that's exactly what's going on! We're ripping out 16 bits of a pointer.
2 parents 3bea4d1 + 9329957 commit 509cbf3

File tree

6 files changed

+207
-148
lines changed

6 files changed

+207
-148
lines changed

src/librustc_mir/interpret/cast.rs

+143-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,153 @@
1-
use rustc::ty::Ty;
2-
use rustc::ty::layout::LayoutOf;
1+
use rustc::ty::{self, Ty};
2+
use rustc::ty::layout::{self, LayoutOf};
33
use syntax::ast::{FloatTy, IntTy, UintTy};
44

55
use rustc_apfloat::ieee::{Single, Double};
66
use super::{EvalContext, Machine};
7-
use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic};
7+
use rustc::mir::interpret::{Scalar, EvalResult, Pointer, PointerArithmetic, Value, EvalErrorKind};
8+
use rustc::mir::CastKind;
89
use rustc_apfloat::Float;
10+
use interpret::eval_context::ValTy;
11+
use interpret::Place;
912

1013
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
14+
crate fn cast(
15+
&mut self,
16+
src: ValTy<'tcx>,
17+
kind: CastKind,
18+
dest_ty: Ty<'tcx>,
19+
dest: Place,
20+
) -> EvalResult<'tcx> {
21+
use rustc::mir::CastKind::*;
22+
match kind {
23+
Unsize => {
24+
let src_layout = self.layout_of(src.ty)?;
25+
let dst_layout = self.layout_of(dest_ty)?;
26+
self.unsize_into(src.value, src_layout, dest, dst_layout)?;
27+
}
28+
29+
Misc => {
30+
if self.type_is_fat_ptr(src.ty) {
31+
match (src.value, self.type_is_fat_ptr(dest_ty)) {
32+
(Value::ByRef { .. }, _) |
33+
// pointers to extern types
34+
(Value::Scalar(_),_) |
35+
// slices and trait objects to other slices/trait objects
36+
(Value::ScalarPair(..), true) => {
37+
let valty = ValTy {
38+
value: src.value,
39+
ty: dest_ty,
40+
};
41+
self.write_value(valty, dest)?;
42+
}
43+
// slices and trait objects to thin pointers (dropping the metadata)
44+
(Value::ScalarPair(data, _), false) => {
45+
let valty = ValTy {
46+
value: Value::Scalar(data),
47+
ty: dest_ty,
48+
};
49+
self.write_value(valty, dest)?;
50+
}
51+
}
52+
} else {
53+
let src_layout = self.layout_of(src.ty)?;
54+
match src_layout.variants {
55+
layout::Variants::Single { index } => {
56+
if let Some(def) = src.ty.ty_adt_def() {
57+
let discr_val = def
58+
.discriminant_for_variant(*self.tcx, index)
59+
.val;
60+
let defined = self
61+
.layout_of(dest_ty)
62+
.unwrap()
63+
.size
64+
.bits() as u8;
65+
return self.write_scalar(
66+
dest,
67+
Scalar::Bits {
68+
bits: discr_val,
69+
defined,
70+
},
71+
dest_ty);
72+
}
73+
}
74+
layout::Variants::Tagged { .. } |
75+
layout::Variants::NicheFilling { .. } => {},
76+
}
77+
78+
let src_val = self.value_to_scalar(src)?;
79+
let dest_val = self.cast_scalar(src_val, src.ty, dest_ty)?;
80+
let valty = ValTy {
81+
value: Value::Scalar(dest_val),
82+
ty: dest_ty,
83+
};
84+
self.write_value(valty, dest)?;
85+
}
86+
}
87+
88+
ReifyFnPointer => {
89+
match src.ty.sty {
90+
ty::TyFnDef(def_id, substs) => {
91+
if self.tcx.has_attr(def_id, "rustc_args_required_const") {
92+
bug!("reifying a fn ptr that requires \
93+
const arguments");
94+
}
95+
let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
96+
*self.tcx,
97+
self.param_env,
98+
def_id,
99+
substs,
100+
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
101+
let fn_ptr = self.memory.create_fn_alloc(instance?);
102+
let valty = ValTy {
103+
value: Value::Scalar(fn_ptr.into()),
104+
ty: dest_ty,
105+
};
106+
self.write_value(valty, dest)?;
107+
}
108+
ref other => bug!("reify fn pointer on {:?}", other),
109+
}
110+
}
111+
112+
UnsafeFnPointer => {
113+
match dest_ty.sty {
114+
ty::TyFnPtr(_) => {
115+
let mut src = src;
116+
src.ty = dest_ty;
117+
self.write_value(src, dest)?;
118+
}
119+
ref other => bug!("fn to unsafe fn cast on {:?}", other),
120+
}
121+
}
122+
123+
ClosureFnPointer => {
124+
match src.ty.sty {
125+
ty::TyClosure(def_id, substs) => {
126+
let substs = self.tcx.subst_and_normalize_erasing_regions(
127+
self.substs(),
128+
ty::ParamEnv::reveal_all(),
129+
&substs,
130+
);
131+
let instance = ty::Instance::resolve_closure(
132+
*self.tcx,
133+
def_id,
134+
substs,
135+
ty::ClosureKind::FnOnce,
136+
);
137+
let fn_ptr = self.memory.create_fn_alloc(instance);
138+
let valty = ValTy {
139+
value: Value::Scalar(fn_ptr.into()),
140+
ty: dest_ty,
141+
};
142+
self.write_value(valty, dest)?;
143+
}
144+
ref other => bug!("closure fn pointer on {:?}", other),
145+
}
146+
}
147+
}
148+
Ok(())
149+
}
150+
11151
pub(super) fn cast_scalar(
12152
&self,
13153
val: Scalar,

src/librustc_mir/interpret/eval_context.rs

+3-130
Original file line numberDiff line numberDiff line change
@@ -770,135 +770,8 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
770770

771771
Cast(kind, ref operand, cast_ty) => {
772772
debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest_ty);
773-
use rustc::mir::CastKind::*;
774-
match kind {
775-
Unsize => {
776-
let src = self.eval_operand(operand)?;
777-
let src_layout = self.layout_of(src.ty)?;
778-
let dst_layout = self.layout_of(dest_ty)?;
779-
self.unsize_into(src.value, src_layout, dest, dst_layout)?;
780-
}
781-
782-
Misc => {
783-
let src = self.eval_operand(operand)?;
784-
if self.type_is_fat_ptr(src.ty) {
785-
match (src.value, self.type_is_fat_ptr(dest_ty)) {
786-
(Value::ByRef { .. }, _) |
787-
// pointers to extern types
788-
(Value::Scalar(_),_) |
789-
// slices and trait objects to other slices/trait objects
790-
(Value::ScalarPair(..), true) => {
791-
let valty = ValTy {
792-
value: src.value,
793-
ty: dest_ty,
794-
};
795-
self.write_value(valty, dest)?;
796-
}
797-
// slices and trait objects to thin pointers (dropping the metadata)
798-
(Value::ScalarPair(data, _), false) => {
799-
let valty = ValTy {
800-
value: Value::Scalar(data),
801-
ty: dest_ty,
802-
};
803-
self.write_value(valty, dest)?;
804-
}
805-
}
806-
} else {
807-
let src_layout = self.layout_of(src.ty)?;
808-
match src_layout.variants {
809-
layout::Variants::Single { index } => {
810-
if let Some(def) = src.ty.ty_adt_def() {
811-
let discr_val = def
812-
.discriminant_for_variant(*self.tcx, index)
813-
.val;
814-
let defined = self
815-
.layout_of(dest_ty)
816-
.unwrap()
817-
.size
818-
.bits() as u8;
819-
return self.write_scalar(
820-
dest,
821-
Scalar::Bits {
822-
bits: discr_val,
823-
defined,
824-
},
825-
dest_ty);
826-
}
827-
}
828-
layout::Variants::Tagged { .. } |
829-
layout::Variants::NicheFilling { .. } => {},
830-
}
831-
832-
let src_val = self.value_to_scalar(src)?;
833-
let dest_val = self.cast_scalar(src_val, src.ty, dest_ty)?;
834-
let valty = ValTy {
835-
value: Value::Scalar(dest_val),
836-
ty: dest_ty,
837-
};
838-
self.write_value(valty, dest)?;
839-
}
840-
}
841-
842-
ReifyFnPointer => {
843-
match self.eval_operand(operand)?.ty.sty {
844-
ty::TyFnDef(def_id, substs) => {
845-
if self.tcx.has_attr(def_id, "rustc_args_required_const") {
846-
bug!("reifying a fn ptr that requires \
847-
const arguments");
848-
}
849-
let instance: EvalResult<'tcx, _> = ty::Instance::resolve(
850-
*self.tcx,
851-
self.param_env,
852-
def_id,
853-
substs,
854-
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
855-
let fn_ptr = self.memory.create_fn_alloc(instance?);
856-
let valty = ValTy {
857-
value: Value::Scalar(fn_ptr.into()),
858-
ty: dest_ty,
859-
};
860-
self.write_value(valty, dest)?;
861-
}
862-
ref other => bug!("reify fn pointer on {:?}", other),
863-
}
864-
}
865-
866-
UnsafeFnPointer => {
867-
match dest_ty.sty {
868-
ty::TyFnPtr(_) => {
869-
let mut src = self.eval_operand(operand)?;
870-
src.ty = dest_ty;
871-
self.write_value(src, dest)?;
872-
}
873-
ref other => bug!("fn to unsafe fn cast on {:?}", other),
874-
}
875-
}
876-
877-
ClosureFnPointer => {
878-
match self.eval_operand(operand)?.ty.sty {
879-
ty::TyClosure(def_id, substs) => {
880-
let substs = self.tcx.subst_and_normalize_erasing_regions(
881-
self.substs(),
882-
ty::ParamEnv::reveal_all(),
883-
&substs,
884-
);
885-
let instance = ty::Instance::resolve_closure(
886-
*self.tcx,
887-
def_id,
888-
substs,
889-
ty::ClosureKind::FnOnce,
890-
);
891-
let fn_ptr = self.memory.create_fn_alloc(instance);
892-
let valty = ValTy {
893-
value: Value::Scalar(fn_ptr.into()),
894-
ty: dest_ty,
895-
};
896-
self.write_value(valty, dest)?;
897-
}
898-
ref other => bug!("closure fn pointer on {:?}", other),
899-
}
900-
}
901-
}
773+
let src = self.eval_operand(operand)?;
774+
self.cast(src, kind, dest_ty, dest)?;
902775
}
903776

904777
Discriminant(ref place) => {
@@ -1565,7 +1438,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
15651438
}
15661439
}
15671440

1568-
fn unsize_into(
1441+
crate fn unsize_into(
15691442
&mut self,
15701443
src: Value,
15711444
src_layout: TyLayout<'tcx>,

src/librustc_mir/transform/const_prop.rs

+34-14
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc::mir::{Constant, Literal, Location, Place, Mir, Operand, Rvalue, Local
1717
use rustc::mir::{NullOp, StatementKind, Statement, BasicBlock, LocalKind};
1818
use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem};
1919
use rustc::mir::visit::{Visitor, PlaceContext};
20-
use rustc::mir::interpret::ConstEvalErr;
20+
use rustc::mir::interpret::{ConstEvalErr, EvalErrorKind};
2121
use rustc::ty::{TyCtxt, self, Instance};
2222
use rustc::mir::interpret::{Value, Scalar, GlobalId, EvalResult};
2323
use interpret::EvalContext;
@@ -145,17 +145,23 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
145145
let r = match f(self) {
146146
Ok(val) => Some(val),
147147
Err(err) => {
148-
let (frames, span) = self.ecx.generate_stacktrace(None);
149-
let err = ConstEvalErr {
150-
span,
151-
error: err,
152-
stacktrace: frames,
153-
};
154-
err.report_as_lint(
155-
self.ecx.tcx,
156-
"this expression will panic at runtime",
157-
lint_root,
158-
);
148+
match err.kind {
149+
// don't report these, they make no sense in a const prop context
150+
EvalErrorKind::MachineError(_) => {},
151+
_ => {
152+
let (frames, span) = self.ecx.generate_stacktrace(None);
153+
let err = ConstEvalErr {
154+
span,
155+
error: err,
156+
stacktrace: frames,
157+
};
158+
err.report_as_lint(
159+
self.ecx.tcx,
160+
"this expression will panic at runtime",
161+
lint_root,
162+
);
163+
}
164+
}
159165
None
160166
},
161167
};
@@ -257,10 +263,25 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
257263
},
258264
Rvalue::Repeat(..) |
259265
Rvalue::Ref(..) |
260-
Rvalue::Cast(..) |
261266
Rvalue::Aggregate(..) |
262267
Rvalue::NullaryOp(NullOp::Box, _) |
263268
Rvalue::Discriminant(..) => None,
269+
270+
Rvalue::Cast(kind, ref operand, _) => {
271+
let (value, ty, span) = self.eval_operand(operand, source_info)?;
272+
self.use_ecx(source_info, |this| {
273+
let dest_ptr = this.ecx.alloc_ptr(place_ty)?;
274+
let place_align = this.ecx.layout_of(place_ty)?.align;
275+
let dest = ::interpret::Place::from_ptr(dest_ptr, place_align);
276+
this.ecx.cast(ValTy { value, ty }, kind, place_ty, dest)?;
277+
Ok((
278+
Value::ByRef(dest_ptr.into(), place_align),
279+
place_ty,
280+
span,
281+
))
282+
})
283+
}
284+
264285
// FIXME(oli-obk): evaluate static/constant slice lengths
265286
Rvalue::Len(_) => None,
266287
Rvalue::NullaryOp(NullOp::SizeOf, ty) => {
@@ -354,7 +375,6 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
354375
)
355376
} else {
356377
if overflow {
357-
use rustc::mir::interpret::EvalErrorKind;
358378
let err = EvalErrorKind::Overflow(op).into();
359379
let _: Option<()> = self.use_ecx(source_info, |_| Err(err));
360380
return None;

src/test/run-pass/cast-rfc0401.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,12 @@ fn main()
166166

167167
assert!(foo as usize != bar as usize);
168168

169-
assert_eq!(foo as i16, foo as usize as i16);
169+
// Taking a few bits of a function's address is totally pointless and we detect that
170+
// Disabling the lint to ensure that the assertion can still be run
171+
#[allow(const_err)]
172+
{
173+
assert_eq!(foo as i16, foo as usize as i16);
174+
}
170175

171176
// fptr-ptr-cast
172177

0 commit comments

Comments
 (0)