Skip to content

Commit ae919d0

Browse files
Ariel Ben-Yehudaarielb1
Ariel Ben-Yehuda
authored andcommitted
type-check lvalues
1 parent 880b6c2 commit ae919d0

File tree

2 files changed

+218
-10
lines changed

2 files changed

+218
-10
lines changed

src/librustc_mir/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
2020
#![cfg_attr(not(stage0), deny(warnings))]
2121
#![unstable(feature = "rustc_private", issue = "27812")]
2222

23+
#![feature(box_patterns)]
2324
#![feature(rustc_private)]
2425
#![feature(staged_api)]
2526

src/librustc_mir/transform/type_check.rs

+217-10
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
#![allow(unreachable_code)]
1313

1414
use rustc::middle::infer;
15+
use rustc::middle::traits;
1516
use rustc::middle::ty::{self, Ty};
1617
use rustc::middle::ty::fold::TypeFoldable;
1718
use rustc::mir::repr::*;
19+
use rustc::mir::tcx::LvalueTy;
1820
use rustc::mir::transform::MirPass;
1921
use rustc::mir::visit::{self, Visitor};
2022

@@ -25,11 +27,27 @@ macro_rules! span_mirbug {
2527
($context:expr, $elem:expr, $($message:tt)*) => ({
2628
$context.tcx().sess.span_warn(
2729
$context.last_span,
28-
&format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*))
30+
&format!("broken MIR ({:?}): {}", $elem, format!($($message)*))
2931
)
3032
})
3133
}
3234

35+
macro_rules! span_mirbug_and_err {
36+
($context:expr, $elem:expr, $($message:tt)*) => ({
37+
{
38+
$context.tcx().sess.span_bug(
39+
$context.last_span,
40+
&format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*))
41+
);
42+
$context.error()
43+
}
44+
})
45+
}
46+
47+
enum FieldAccessError {
48+
OutOfRange { field_count: usize }
49+
}
50+
3351
/// Verifies that MIR types are sane to not crash further
3452
/// checks.
3553
struct TypeVerifier<'a, 'tcx: 'a> {
@@ -46,11 +64,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
4664
}
4765
}
4866

49-
fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: visit::LvalueContext) {
50-
self.super_lvalue(lvalue, context);
51-
debug!("visiting lvalue {:?}", lvalue);
52-
let lv_ty = self.mir.lvalue_ty(self.tcx(), lvalue).to_ty(self.tcx());
53-
self.sanitize_type(lvalue, lv_ty);
67+
fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: visit::LvalueContext) {
68+
self.sanitize_lvalue(lvalue);
5469
}
5570

5671
fn visit_constant(&mut self, constant: &Constant<'tcx>) {
@@ -78,6 +93,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
7893
for (n, tmp_decl) in mir.temp_decls.iter().enumerate() {
7994
self.sanitize_type(&(n, tmp_decl), tmp_decl.ty);
8095
}
96+
if self.errors_reported {
97+
return;
98+
}
8199
self.super_mir(mir);
82100
}
83101
}
@@ -96,12 +114,201 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> {
96114
self.infcx.tcx
97115
}
98116

99-
fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) {
100-
if !(ty.needs_infer() || ty.has_escaping_regions()) {
101-
return;
117+
fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
118+
if !(ty.needs_infer() || ty.has_escaping_regions() ||
119+
ty.references_error()) {
120+
return ty;
121+
}
122+
span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
123+
}
124+
125+
fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>) -> LvalueTy<'tcx> {
126+
debug!("sanitize_lvalue: {:?}", lvalue);
127+
match *lvalue {
128+
Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index as usize].ty },
129+
Lvalue::Temp(index) =>
130+
LvalueTy::Ty { ty: self.mir.temp_decls[index as usize].ty },
131+
Lvalue::Arg(index) =>
132+
LvalueTy::Ty { ty: self.mir.arg_decls[index as usize].ty },
133+
Lvalue::Static(def_id) =>
134+
LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
135+
Lvalue::ReturnPointer => {
136+
if let ty::FnConverging(return_ty) = self.mir.return_ty {
137+
LvalueTy::Ty { ty: return_ty }
138+
} else {
139+
LvalueTy::Ty {
140+
ty: span_mirbug_and_err!(
141+
self, lvalue, "return in diverging function")
142+
}
143+
}
144+
}
145+
Lvalue::Projection(ref proj) => {
146+
let base_ty = self.sanitize_lvalue(&proj.base);
147+
if let LvalueTy::Ty { ty } = base_ty {
148+
if ty.references_error() {
149+
assert!(self.errors_reported);
150+
return LvalueTy::Ty { ty: self.tcx().types.err };
151+
}
152+
}
153+
self.sanitize_projection(base_ty, &proj.elem, lvalue)
154+
}
155+
}
156+
}
157+
158+
fn sanitize_projection(&mut self,
159+
base: LvalueTy<'tcx>,
160+
pi: &LvalueElem<'tcx>,
161+
lvalue: &Lvalue<'tcx>)
162+
-> LvalueTy<'tcx> {
163+
debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue);
164+
let tcx = self.tcx();
165+
let base_ty = base.to_ty(tcx);
166+
match *pi {
167+
ProjectionElem::Deref => {
168+
let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
169+
LvalueTy::Ty {
170+
ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
171+
span_mirbug_and_err!(
172+
self, lvalue, "deref of non-pointer {:?}", base_ty)
173+
})
174+
}
175+
}
176+
ProjectionElem::Index(ref i) => {
177+
self.visit_operand(i);
178+
let index_ty = self.mir.operand_ty(tcx, i);
179+
if index_ty != tcx.types.usize {
180+
LvalueTy::Ty {
181+
ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i)
182+
}
183+
} else {
184+
LvalueTy::Ty {
185+
ty: base_ty.builtin_index().unwrap_or_else(|| {
186+
span_mirbug_and_err!(
187+
self, lvalue, "index of non-array {:?}", base_ty)
188+
})
189+
}
190+
}
191+
}
192+
ProjectionElem::ConstantIndex { .. } => {
193+
// consider verifying in-bounds
194+
LvalueTy::Ty {
195+
ty: base_ty.builtin_index().unwrap_or_else(|| {
196+
span_mirbug_and_err!(
197+
self, lvalue, "index of non-array {:?}", base_ty)
198+
})
199+
}
200+
}
201+
ProjectionElem::Downcast(adt_def1, index) =>
202+
match base_ty.sty {
203+
ty::TyEnum(adt_def, substs) if adt_def == adt_def1 => {
204+
if index >= adt_def.variants.len() {
205+
LvalueTy::Ty {
206+
ty: span_mirbug_and_err!(
207+
self,
208+
lvalue,
209+
"cast to variant #{:?} but enum only has {:?}",
210+
index,
211+
adt_def.variants.len())
212+
}
213+
} else {
214+
LvalueTy::Downcast {
215+
adt_def: adt_def,
216+
substs: substs,
217+
variant_index: index
218+
}
219+
}
220+
}
221+
_ => LvalueTy::Ty {
222+
ty: span_mirbug_and_err!(
223+
self, lvalue, "can't downcast {:?}", base_ty)
224+
}
225+
},
226+
ProjectionElem::Field(field, fty) => {
227+
let fty = self.sanitize_type(lvalue, fty);
228+
match self.field_ty(lvalue, base, field) {
229+
Ok(ty) => {
230+
if let Err(terr) = infer::can_mk_subty(self.infcx, ty, fty) {
231+
span_mirbug!(
232+
self, lvalue, "bad field access ({:?}: {:?}): {:?}",
233+
ty, fty, terr);
234+
}
235+
}
236+
Err(FieldAccessError::OutOfRange { field_count }) => {
237+
span_mirbug!(
238+
self, lvalue, "accessed field #{} but variant only has {}",
239+
field.index(), field_count)
240+
}
241+
}
242+
LvalueTy::Ty { ty: fty }
243+
}
102244
}
103-
span_mirbug!(self, parent, "bad type {:?}", ty);
245+
}
246+
247+
fn error(&mut self) -> Ty<'tcx> {
104248
self.errors_reported = true;
249+
self.tcx().types.err
250+
}
251+
252+
fn field_ty(&mut self,
253+
parent: &fmt::Debug,
254+
base_ty: LvalueTy<'tcx>,
255+
field: Field)
256+
-> Result<Ty<'tcx>, FieldAccessError>
257+
{
258+
let tcx = self.tcx();
259+
260+
let (variant, substs) = match base_ty {
261+
LvalueTy::Downcast { adt_def, substs, variant_index } => {
262+
(&adt_def.variants[variant_index], substs)
263+
}
264+
LvalueTy::Ty { ty } => match ty.sty {
265+
ty::TyStruct(adt_def, substs) | ty::TyEnum(adt_def, substs)
266+
if adt_def.is_univariant() => {
267+
(&adt_def.variants[0], substs)
268+
}
269+
ty::TyTuple(ref tys) | ty::TyClosure(_, box ty::ClosureSubsts {
270+
upvar_tys: ref tys, ..
271+
}) => {
272+
return match tys.get(field.index()) {
273+
Some(&ty) => Ok(ty),
274+
None => Err(FieldAccessError::OutOfRange {
275+
field_count: tys.len()
276+
})
277+
}
278+
}
279+
_ => return Ok(span_mirbug_and_err!(
280+
self, parent, "can't project out of {:?}", base_ty))
281+
}
282+
};
283+
284+
if let Some(field) = variant.fields.get(field.index()) {
285+
Ok(self.normalize(parent, field.ty(tcx, substs)))
286+
} else {
287+
Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
288+
}
289+
}
290+
291+
fn normalize(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
292+
let mut selcx = traits::SelectionContext::new(&self.infcx);
293+
let cause = traits::ObligationCause::misc(self.last_span, 0);
294+
let traits::Normalized { value: ty, obligations } =
295+
traits::normalize(&mut selcx, cause, &ty);
296+
297+
debug!("normalize: ty={:?} obligations={:?}",
298+
ty,
299+
obligations);
300+
301+
let mut fulfill_cx = self.infcx.fulfillment_cx.borrow_mut();
302+
for obligation in obligations {
303+
fulfill_cx.register_predicate_obligation(&self.infcx, obligation);
304+
}
305+
306+
match infer::drain_fulfillment_cx(&self.infcx, &mut fulfill_cx, &ty) {
307+
Ok(ty) => ty,
308+
Err(e) => {
309+
span_mirbug_and_err!(self, parent, "trait fulfillment failed: {:?}", e)
310+
}
311+
}
105312
}
106313
}
107314

0 commit comments

Comments
 (0)