]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/validation.rs
we need to normalize associated types also deep in the hierarchy (89)
[rust.git] / src / librustc_mir / interpret / validation.rs
1 use rustc::hir::Mutability;
2 use rustc::mir::{self, ValidationOp};
3 use rustc::ty::{self, Ty, TypeFoldable};
4 use rustc::traits::Reveal;
5 use rustc::infer::TransNormalize;
6 use rustc::middle::region::CodeExtent;
7
8 use error::{EvalError, EvalResult};
9 use eval_context::{EvalContext};
10 use memory::AccessKind;
11 use value::Value;
12 use lvalue::{Lvalue, LvalueExtra};
13
14 // Validity checks
15 #[derive(Copy, Clone, Debug)]
16 pub struct ValidationCtx {
17     op: ValidationOp,
18     region: Option<CodeExtent>,
19     mutbl: Mutability,
20 }
21
22 impl ValidationCtx {
23     pub fn new(op: ValidationOp) -> Self {
24         ValidationCtx {
25             op, region: None, mutbl: Mutability::MutMutable,
26         }
27     }
28 }
29
30 impl<'a, 'tcx> EvalContext<'a, 'tcx> {
31     fn validate_variant(
32         &mut self,
33         lvalue: Lvalue<'tcx>,
34         ty: Ty<'tcx>,
35         variant: &ty::VariantDef,
36         subst: &ty::subst::Substs<'tcx>,
37         vctx: ValidationCtx,
38     ) -> EvalResult<'tcx> {
39         // TODO: Take visibility/privacy into account.
40         for (idx, field) in variant.fields.iter().enumerate() {
41             let field_ty = field.ty(self.tcx, subst);
42             let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
43             self.validate(field_lvalue, field_ty, vctx)?;
44         }
45         Ok(())
46     }
47
48     fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, vctx: ValidationCtx) -> EvalResult<'tcx> {
49         // Check alignment and non-NULLness
50         let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
51         let ptr = val.into_ptr(&mut self.memory)?;
52         self.memory.check_align(ptr, align)?;
53
54         // Recurse
55         let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?;
56         self.validate(pointee_lvalue, pointee_ty, vctx)
57     }
58
59     /// Validate the lvalue at the given type. If `release` is true, just do a release of all write locks
60     pub(super) fn validate(&mut self, lvalue: Lvalue<'tcx>, mut ty: Ty<'tcx>, mut vctx: ValidationCtx) -> EvalResult<'tcx>
61     {
62         use rustc::ty::TypeVariants::*;
63         use rustc::ty::RegionKind::*;
64         use rustc::ty::AdtKind;
65         use self::Mutability::*;
66
67         // This is essentially a copy of normalize_associated_type, but without erasure
68         if ty.has_projection_types() {
69             let param_env = ty::ParamEnv::empty(Reveal::All);
70             ty = self.tcx.infer_ctxt().enter(move |infcx| {
71                 ty.trans_normalize(&infcx, param_env)
72             })
73         }
74         let ty = ty; // no more mutation
75         trace!("Validating {:?} at type {}, context {:?}", lvalue, ty, vctx);
76
77         // Decide whether this type *owns* the memory it covers (like integers), or whether it
78         // just assembles pieces (that each own their memory) together to a larger whole.
79         // TODO: Currently, we don't acquire locks for padding and discriminants. We should.
80         let is_owning = match ty.sty {
81             TyInt(_) | TyUint(_) | TyRawPtr(_) |
82             TyBool | TyFloat(_) | TyChar | TyStr |
83             TyRef(..) | TyFnPtr(..) | TyNever => true,
84             TyAdt(adt, _) if adt.is_box() => true,
85             TySlice(_) | TyAdt(_, _) | TyTuple(..) | TyClosure(..) | TyArray(..) | TyDynamic(..) => false,
86             TyParam(_) | TyInfer(_) => bug!("I got an incomplete type for validation"),
87             _ => return Err(EvalError::Unimplemented(format!("Unimplemented type encountered when checking validity."))),
88         };
89         if is_owning {
90             match lvalue {
91                 Lvalue::Ptr { ptr, extra, aligned: _ } => {
92                     // Determine the size
93                     // FIXME: Can we reuse size_and_align_of_dst for Lvalues?
94                     let len = match self.type_size(ty)? {
95                         Some(size) => {
96                             assert_eq!(extra, LvalueExtra::None, "Got a fat ptr to a sized type");
97                             size
98                         }
99                         None => {
100                             // The only unsized typ we concider "owning" is TyStr.
101                             assert_eq!(ty.sty, TyStr, "Found a surprising unsized owning type");
102                             // The extra must be the length, in bytes.
103                             match extra {
104                                 LvalueExtra::Length(len) => len,
105                                 _ => bug!("TyStr must have a length as extra"),
106                             }
107                         }
108                     };
109                     // Handle locking
110                     if len > 0 {
111                         let ptr = ptr.to_ptr()?;
112                         let access = match vctx.mutbl { MutMutable => AccessKind::Write, MutImmutable => AccessKind::Read };
113                         match vctx.op {
114                             ValidationOp::Acquire => self.memory.acquire_lock(ptr, len, vctx.region, access)?,
115                             ValidationOp::Release => self.memory.release_write_lock_until(ptr, len, None)?,
116                             ValidationOp::Suspend(region) => self.memory.release_write_lock_until(ptr, len, Some(region))?,
117                         }
118                     }
119                 }
120                 Lvalue::Local { .. } | Lvalue::Global(..) => {
121                     // These are not backed by memory, so we have nothing to do.
122                 }
123             }
124         }
125
126         match ty.sty {
127             TyInt(_) | TyUint(_) | TyRawPtr(_) => {
128                 // TODO: Make sure these are not undef.
129                 // We could do a bounds-check and other sanity checks on the lvalue, but it would be a bug in miri for this to ever fail.
130                 Ok(())
131             }
132             TyBool | TyFloat(_) | TyChar | TyStr => {
133                 // TODO: Check if these are valid bool/float/codepoint/UTF-8, respectively (and in particular, not undef).
134                 Ok(())
135             }
136             TyNever => {
137                 Err(EvalError::ValidationFailure(format!("The empty type is never valid.")))
138             }
139             TyRef(region, ty::TypeAndMut { ty: pointee_ty, mutbl }) => {
140                 let val = self.read_lvalue(lvalue)?;
141                 // Sharing restricts our context
142                 if mutbl == MutImmutable {
143                     // Actually, in case of releasing-validation, this means we are done.
144                     if vctx.op != ValidationOp::Acquire {
145                         return Ok(());
146                     }
147                     vctx.mutbl = MutImmutable;
148                 }
149                 // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet,
150                 // we record the region of this borrow to the context.
151                 if vctx.region == None {
152                     match *region {
153                         ReScope(ce) => vctx.region = Some(ce),
154                         // It is possible for us to encode erased lifetimes here because the lifetimes in
155                         // this functions' Subst will be erased.
156                         _ => {},
157                     }
158                 }
159                 self.validate_ptr(val, pointee_ty, vctx)
160             }
161             TyAdt(adt, _) if adt.is_box() => {
162                 let val = self.read_lvalue(lvalue)?;
163                 self.validate_ptr(val, ty.boxed_ty(), vctx)
164             }
165             TyFnPtr(_sig) => {
166                 // TODO: The function names here could need some improvement.
167                 let ptr = self.read_lvalue(lvalue)?.into_ptr(&mut self.memory)?.to_ptr()?;
168                 self.memory.get_fn(ptr)?;
169                 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
170                 Ok(())
171             }
172
173             // Compound types
174             TySlice(elem_ty) => {
175                 let len = match lvalue {
176                     Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len,
177                     _ => bug!("acquire_valid of a TySlice given non-slice lvalue: {:?}", lvalue),
178                 };
179                 for i in 0..len {
180                     let inner_lvalue = self.lvalue_index(lvalue, ty, i)?;
181                     self.validate(inner_lvalue, elem_ty, vctx)?;
182                 }
183                 Ok(())
184             }
185             TyArray(elem_ty, len) => {
186                 for i in 0..len {
187                     let inner_lvalue = self.lvalue_index(lvalue, ty, i as u64)?;
188                     self.validate(inner_lvalue, elem_ty, vctx)?;
189                 }
190                 Ok(())
191             }
192             TyDynamic(_data, _region) => {
193                 // Check that this is a valid vtable
194                 let vtable = match lvalue {
195                     Lvalue::Ptr { extra: LvalueExtra::Vtable(vtable), .. } => vtable,
196                     _ => bug!("acquire_valid of a TyDynamic given non-trait-object lvalue: {:?}", lvalue),
197                 };
198                 self.read_size_and_align_from_vtable(vtable)?;
199                 // TODO: Check that the vtable contains all the function pointers we expect it to have.
200                 // TODO: Is there anything we can/should validate here?  Trait objects cannot have any operations performed
201                 // on them directly.  We cannot, in general, even acquire any locks as the trait object *could*
202                 // contain an UnsafeCell.  If we call functions to get access to data, we will validate
203                 // their return values.  So, it doesn't seem like there's anything to do.
204                 Ok(())
205             }
206             TyAdt(adt, subst) => {
207                 if Some(adt.did) == self.tcx.lang_items.unsafe_cell_type() {
208                     // No locks for unsafe cells.  Also no other validation, the only field is private anyway.
209                     return Ok(());
210                 }
211
212                 match adt.adt_kind() {
213                     AdtKind::Enum => {
214                         // TODO: Can we get the discriminant without forcing an allocation?
215                         let ptr = self.force_allocation(lvalue)?.to_ptr()?;
216                         let discr = self.read_discriminant_value(ptr, ty)?;
217
218                         // Get variant index for discriminant
219                         let variant_idx = adt.discriminants(self.tcx)
220                             .position(|variant_discr| variant_discr.to_u128_unchecked() == discr)
221                             .ok_or(EvalError::InvalidDiscriminant)?;
222                         let variant = &adt.variants[variant_idx];
223
224                         if variant.fields.len() > 0 {
225                             // Downcast to this variant, if needed
226                             let lvalue = if adt.variants.len() > 1 {
227                                 self.eval_lvalue_projection(lvalue, ty, &mir::ProjectionElem::Downcast(adt, variant_idx))?
228                             } else {
229                                 lvalue
230                             };
231
232                             // Recursively validate the fields
233                             self.validate_variant(lvalue, ty, variant, subst, vctx)
234                         } else {
235                             // No fields, nothing left to check.  Downcasting may fail, e.g. in case of a CEnum.
236                             Ok(())
237                         }
238                     }
239                     AdtKind::Struct => {
240                         self.validate_variant(lvalue, ty, adt.struct_variant(), subst, vctx)
241                     }
242                     AdtKind::Union => {
243                         // No guarantees are provided for union types.
244                         // TODO: Make sure that all access to union fields is unsafe; otherwise, we may have some checking to do (but what exactly?)
245                         Ok(())
246                     }
247                 }
248             }
249             TyTuple(ref types, _) => {
250                 for (idx, field_ty) in types.iter().enumerate() {
251                     let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
252                     self.validate(field_lvalue, field_ty, vctx)?;
253                 }
254                 Ok(())
255             }
256             TyClosure(def_id, ref closure_substs) => {
257                 for (idx, field_ty) in closure_substs.upvar_tys(def_id, self.tcx).enumerate() {
258                     let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?;
259                     self.validate(field_lvalue, field_ty, vctx)?;
260                 }
261                 // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
262                 // Is there other things we can/should check?  Like vtable pointers?
263                 Ok(())
264             }
265             _ => bug!("We already establishd that this is a type we support.")
266         }
267     }
268 }