1
1
use std:: fmt:: Write ;
2
2
3
+ use syntax_pos:: symbol:: Symbol ;
3
4
use rustc:: ty:: layout:: { self , Size , Primitive } ;
4
5
use rustc:: ty:: { self , Ty } ;
5
6
use rustc_data_structures:: fx:: FxHashSet ;
@@ -13,21 +14,23 @@ use super::{
13
14
14
15
macro_rules! validation_failure{
15
16
( $what: expr, $where: expr, $details: expr) => { {
16
- let where_ = if $where. is_empty( ) {
17
+ let where_ = path_format( $where) ;
18
+ let where_ = if where_. is_empty( ) {
17
19
String :: new( )
18
20
} else {
19
- format!( " at {}" , $where )
21
+ format!( " at {}" , where_ )
20
22
} ;
21
23
err!( ValidationFailure ( format!(
22
24
"encountered {}{}, but expected {}" ,
23
25
$what, where_, $details,
24
26
) ) )
25
27
} } ;
26
28
( $what: expr, $where: expr) => { {
27
- let where_ = if $where. is_empty( ) {
29
+ let where_ = path_format( $where) ;
30
+ let where_ = if where_. is_empty( ) {
28
31
String :: new( )
29
32
} else {
30
- format!( " at {}" , $where )
33
+ format!( " at {}" , where_ )
31
34
} ;
32
35
err!( ValidationFailure ( format!(
33
36
"encountered {}{}" ,
@@ -36,13 +39,59 @@ macro_rules! validation_failure{
36
39
} } ;
37
40
}
38
41
42
+ /// We want to show a nice path to the invalid field for diagnotsics,
43
+ /// but avoid string operations in the happy case where no error happens.
44
+ /// So we track a `Vec<PathElem>` where `PathElem` contains all the data we
45
+ /// need to later print something for the user.
46
+ #[ derive( Copy , Clone , Debug ) ]
47
+ pub enum PathElem {
48
+ Field ( Symbol ) ,
49
+ ClosureVar ( Symbol ) ,
50
+ ArrayElem ( usize ) ,
51
+ TupleElem ( usize ) ,
52
+ Deref ,
53
+ Tag ,
54
+ }
55
+
56
+ // Adding a Deref and making a copy of the path to be put into the queue
57
+ // always go together. This one does it with only new allocation.
58
+ fn path_clone_and_deref ( path : & Vec < PathElem > ) -> Vec < PathElem > {
59
+ let mut new_path = Vec :: with_capacity ( path. len ( ) +1 ) ;
60
+ new_path. clone_from ( path) ;
61
+ new_path. push ( PathElem :: Deref ) ;
62
+ new_path
63
+ }
64
+
65
+ /// Format a path
66
+ fn path_format ( path : & Vec < PathElem > ) -> String {
67
+ use self :: PathElem :: * ;
68
+
69
+ let mut out = String :: new ( ) ;
70
+ for elem in path. iter ( ) {
71
+ match elem {
72
+ Field ( name) => write ! ( out, ".{}" , name) . unwrap ( ) ,
73
+ ClosureVar ( name) => write ! ( out, ".<closure-var({})>" , name) . unwrap ( ) ,
74
+ TupleElem ( idx) => write ! ( out, ".{}" , idx) . unwrap ( ) ,
75
+ ArrayElem ( idx) => write ! ( out, "[{}]" , idx) . unwrap ( ) ,
76
+ Deref =>
77
+ // This does not match Rust syntax, but it is more readable for long paths -- and
78
+ // some of the other items here also are not Rust syntax. Actually we can't
79
+ // even use the usual syntax because we are just showing the projections,
80
+ // not the root.
81
+ write ! ( out, ".<deref>" ) . unwrap ( ) ,
82
+ Tag => write ! ( out, ".<enum-tag>" ) . unwrap ( ) ,
83
+ }
84
+ }
85
+ out
86
+ }
87
+
39
88
impl < ' a , ' mir , ' tcx , M : Machine < ' mir , ' tcx > > EvalContext < ' a , ' mir , ' tcx , M > {
40
89
fn validate_scalar (
41
90
& self ,
42
91
value : ScalarMaybeUndef ,
43
92
size : Size ,
44
93
scalar : & layout:: Scalar ,
45
- path : & str ,
94
+ path : & Vec < PathElem > ,
46
95
ty : Ty ,
47
96
) -> EvalResult < ' tcx > {
48
97
trace ! ( "validate scalar: {:#?}, {:#?}, {:#?}, {}" , value, size, scalar, ty) ;
@@ -93,7 +142,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
93
142
ty:: TyChar => {
94
143
debug_assert_eq ! ( size. bytes( ) , 4 ) ;
95
144
if :: std:: char:: from_u32 ( bits as u32 ) . is_none ( ) {
96
- return err ! ( InvalidChar ( bits) ) ;
145
+ return validation_failure ! (
146
+ "character" ,
147
+ path,
148
+ "a valid unicode codepoint"
149
+ ) ;
97
150
}
98
151
}
99
152
_ => { } ,
@@ -127,46 +180,57 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
127
180
/// This function checks the memory where `dest` points to. The place must be sized
128
181
/// (i.e., dest.extra == PlaceExtra::None).
129
182
/// It will error if the bits at the destination do not match the ones described by the layout.
183
+ /// The `path` may be pushed to, but the part that is present when the function
184
+ /// starts must not be changed!
130
185
pub fn validate_mplace (
131
186
& self ,
132
187
dest : MPlaceTy < ' tcx > ,
133
- path : String ,
188
+ path : & mut Vec < PathElem > ,
134
189
seen : & mut FxHashSet < ( MPlaceTy < ' tcx > ) > ,
135
- todo : & mut Vec < ( MPlaceTy < ' tcx > , String ) > ,
190
+ todo : & mut Vec < ( MPlaceTy < ' tcx > , Vec < PathElem > ) > ,
136
191
) -> EvalResult < ' tcx > {
137
192
self . memory . dump_alloc ( dest. to_ptr ( ) ?. alloc_id ) ;
138
193
trace ! ( "validate_mplace: {:?}, {:#?}" , * dest, dest. layout) ;
139
194
140
- // Find the right variant
195
+ // Find the right variant. We have to handle this as a prelude, not via
196
+ // proper recursion with the new inner layout, to be able to later nicely
197
+ // print the field names of the enum field that is being accessed.
141
198
let ( variant, dest) = match dest. layout . variants {
142
199
layout:: Variants :: NicheFilling { niche : ref tag, .. } |
143
200
layout:: Variants :: Tagged { ref tag, .. } => {
144
201
let size = tag. value . size ( self ) ;
145
202
// we first read the tag value as scalar, to be able to validate it
146
203
let tag_mplace = self . mplace_field ( dest, 0 ) ?;
147
204
let tag_value = self . read_scalar ( tag_mplace. into ( ) ) ?;
148
- let path = format ! ( "{}.TAG" , path ) ;
205
+ path. push ( PathElem :: Tag ) ;
149
206
self . validate_scalar (
150
207
tag_value, size, tag, & path, tag_mplace. layout . ty
151
208
) ?;
209
+ path. pop ( ) ; // remove the element again
152
210
// then we read it again to get the index, to continue
153
211
let variant = self . read_discriminant_as_variant_index ( dest. into ( ) ) ?;
154
- let dest = self . mplace_downcast ( dest, variant) ?;
212
+ let inner_dest = self . mplace_downcast ( dest, variant) ?;
213
+ // Put the variant projection onto the path, as a field
214
+ path. push ( PathElem :: Field ( dest. layout . ty . ty_adt_def ( ) . unwrap ( ) . variants [ variant] . name ) ) ;
155
215
trace ! ( "variant layout: {:#?}" , dest. layout) ;
156
- ( variant, dest )
216
+ ( variant, inner_dest )
157
217
} ,
158
218
layout:: Variants :: Single { index } => {
159
219
( index, dest)
160
220
}
161
221
} ;
162
222
223
+ // Remember the length, in case we need to truncate
224
+ let path_len = path. len ( ) ;
225
+
163
226
// Validate all fields
164
227
match dest. layout . fields {
165
228
// primitives are unions with zero fields
166
229
layout:: FieldPlacement :: Union ( 0 ) => {
167
230
match dest. layout . abi {
168
231
// nothing to do, whatever the pointer points to, it is never going to be read
169
- layout:: Abi :: Uninhabited => validation_failure ! ( "a value of an uninhabited type" , path) ,
232
+ layout:: Abi :: Uninhabited =>
233
+ return validation_failure ! ( "a value of an uninhabited type" , path) ,
170
234
// check that the scalar is a valid pointer or that its bit range matches the
171
235
// expectation.
172
236
layout:: Abi :: Scalar ( ref scalar_layout) => {
@@ -179,8 +243,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
179
243
if let Scalar :: Ptr ( ptr) = scalar. not_undef ( ) ? {
180
244
let alloc_kind = self . tcx . alloc_map . lock ( ) . get ( ptr. alloc_id ) ;
181
245
if let Some ( AllocType :: Static ( did) ) = alloc_kind {
182
- // statics from other crates are already checked
183
- // extern statics should not be validated as they have no body
246
+ // statics from other crates are already checked.
247
+ // extern statics should not be validated as they have no body.
184
248
if !did. is_local ( ) || self . tcx . is_foreign_item ( did) {
185
249
return Ok ( ( ) ) ;
186
250
}
@@ -190,12 +254,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
190
254
let ptr_place = self . ref_to_mplace ( value) ?;
191
255
// we have not encountered this pointer+layout combination before
192
256
if seen. insert ( ptr_place) {
193
- todo. push ( ( ptr_place, format ! ( "(*{})" , path) ) )
257
+ todo. push ( ( ptr_place, path_clone_and_deref ( path) ) ) ;
194
258
}
195
259
}
196
260
}
197
261
}
198
- Ok ( ( ) )
199
262
} ,
200
263
_ => bug ! ( "bad abi for FieldPlacement::Union(0): {:#?}" , dest. layout. abi) ,
201
264
}
@@ -204,16 +267,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
204
267
// We can't check unions, their bits are allowed to be anything.
205
268
// The fields don't need to correspond to any bit pattern of the union's fields.
206
269
// See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
207
- Ok ( ( ) )
208
270
} ,
209
271
layout:: FieldPlacement :: Array { .. } => {
210
272
for ( i, field) in self . mplace_array_fields ( dest) ?. enumerate ( ) {
211
273
let field = field?;
212
- let mut path = path. clone ( ) ;
213
- self . dump_field_name ( & mut path, dest. layout . ty , i as usize , variant) . unwrap ( ) ;
274
+ path. push ( PathElem :: ArrayElem ( i) ) ;
214
275
self . validate_mplace ( field, path, seen, todo) ?;
276
+ path. truncate ( path_len) ;
215
277
}
216
- Ok ( ( ) )
217
278
} ,
218
279
layout:: FieldPlacement :: Arbitrary { ref offsets, .. } => {
219
280
// fat pointers need special treatment
@@ -232,9 +293,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
232
293
assert_eq ! ( ptr. extra, PlaceExtra :: Length ( len) ) ;
233
294
let unpacked_ptr = self . unpack_unsized_mplace ( ptr) ?;
234
295
if seen. insert ( unpacked_ptr) {
235
- let mut path = path. clone ( ) ;
236
- self . dump_field_name ( & mut path, dest. layout . ty , 0 , 0 ) . unwrap ( ) ;
237
- todo. push ( ( unpacked_ptr, path) )
296
+ todo. push ( ( unpacked_ptr, path_clone_and_deref ( path) ) ) ;
238
297
}
239
298
} ,
240
299
Some ( ty:: TyDynamic ( ..) ) => {
@@ -249,9 +308,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
249
308
assert_eq ! ( ptr. extra, PlaceExtra :: Vtable ( vtable) ) ;
250
309
let unpacked_ptr = self . unpack_unsized_mplace ( ptr) ?;
251
310
if seen. insert ( unpacked_ptr) {
252
- let mut path = path. clone ( ) ;
253
- self . dump_field_name ( & mut path, dest. layout . ty , 0 , 0 ) . unwrap ( ) ;
254
- todo. push ( ( unpacked_ptr, path) )
311
+ todo. push ( ( unpacked_ptr, path_clone_and_deref ( path) ) ) ;
255
312
}
256
313
// FIXME: More checks for the vtable... making sure it is exactly
257
314
// the one one would expect for this type.
@@ -261,84 +318,42 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
261
318
None => {
262
319
// Not a pointer, perform regular aggregate handling below
263
320
for i in 0 ..offsets. len ( ) {
264
- let mut path = path. clone ( ) ;
265
- self . dump_field_name ( & mut path, dest. layout . ty , i, variant) . unwrap ( ) ;
266
321
let field = self . mplace_field ( dest, i as u64 ) ?;
322
+ path. push ( self . aggregate_field_path_elem ( dest. layout . ty , variant, i) ) ;
267
323
self . validate_mplace ( field, path, seen, todo) ?;
324
+ path. truncate ( path_len) ;
268
325
}
269
326
// FIXME: For a TyStr, check that this is valid UTF-8.
270
327
} ,
271
328
}
272
-
273
- Ok ( ( ) )
274
329
}
275
330
}
331
+ Ok ( ( ) )
276
332
}
277
333
278
- fn dump_field_name ( & self , s : & mut String , ty : Ty < ' tcx > , i : usize , variant : usize ) -> :: std :: fmt :: Result {
334
+ fn aggregate_field_path_elem ( & self , ty : Ty < ' tcx > , variant : usize , field : usize ) -> PathElem {
279
335
match ty. sty {
280
- ty:: TyBool |
281
- ty:: TyChar |
282
- ty:: TyInt ( _) |
283
- ty:: TyUint ( _) |
284
- ty:: TyFloat ( _) |
285
- ty:: TyFnPtr ( _) |
286
- ty:: TyNever |
287
- ty:: TyFnDef ( ..) |
288
- ty:: TyGeneratorWitness ( ..) |
289
- ty:: TyForeign ( ..) |
290
- ty:: TyDynamic ( ..) => {
291
- bug ! ( "field_name({:?}): not applicable" , ty)
292
- }
293
-
294
- // Potentially-fat pointers.
295
- ty:: TyRef ( _, pointee, _) |
296
- ty:: TyRawPtr ( ty:: TypeAndMut { ty : pointee, .. } ) => {
297
- assert ! ( i < 2 ) ;
298
-
299
- // Reuse the fat *T type as its own thin pointer data field.
300
- // This provides information about e.g. DST struct pointees
301
- // (which may have no non-DST form), and will work as long
302
- // as the `Abi` or `FieldPlacement` is checked by users.
303
- if i == 0 {
304
- return write ! ( s, ".data_ptr" ) ;
305
- }
306
-
307
- match self . tcx . struct_tail ( pointee) . sty {
308
- ty:: TySlice ( _) |
309
- ty:: TyStr => write ! ( s, ".len" ) ,
310
- ty:: TyDynamic ( ..) => write ! ( s, ".vtable_ptr" ) ,
311
- _ => bug ! ( "field_name({:?}): not applicable" , ty)
312
- }
313
- }
314
-
315
- // Arrays and slices.
316
- ty:: TyArray ( _, _) |
317
- ty:: TySlice ( _) |
318
- ty:: TyStr => write ! ( s, "[{}]" , i) ,
319
-
320
336
// generators and closures.
321
337
ty:: TyClosure ( def_id, _) | ty:: TyGenerator ( def_id, _, _) => {
322
338
let node_id = self . tcx . hir . as_local_node_id ( def_id) . unwrap ( ) ;
323
- let freevar = self . tcx . with_freevars ( node_id, |fv| fv[ i ] ) ;
324
- write ! ( s , ".upvar({})" , self . tcx. hir. name( freevar. var_id( ) ) )
339
+ let freevar = self . tcx . with_freevars ( node_id, |fv| fv[ field ] ) ;
340
+ PathElem :: ClosureVar ( self . tcx . hir . name ( freevar. var_id ( ) ) )
325
341
}
326
342
327
- ty:: TyTuple ( _) => write ! ( s, ".{}" , i) ,
343
+ // tuples
344
+ ty:: TyTuple ( _) => PathElem :: TupleElem ( field) ,
328
345
329
346
// enums
330
347
ty:: TyAdt ( def, ..) if def. is_enum ( ) => {
331
348
let variant = & def. variants [ variant] ;
332
- write ! ( s , ".{}::{}" , variant. name , variant . fields[ i ] . ident)
349
+ PathElem :: Field ( variant. fields [ field ] . ident . name )
333
350
}
334
351
335
- // other ADTs.
336
- ty:: TyAdt ( def, _) => write ! ( s , ".{}" , def. non_enum_variant( ) . fields[ i ] . ident) ,
352
+ // other ADTs
353
+ ty:: TyAdt ( def, _) => PathElem :: Field ( def. non_enum_variant ( ) . fields [ field ] . ident . name ) ,
337
354
338
- ty:: TyProjection ( _) | ty:: TyAnon ( ..) | ty:: TyParam ( _) |
339
- ty:: TyInfer ( _) | ty:: TyError => {
340
- bug ! ( "dump_field_name: unexpected type `{}`" , ty)
341
- }
355
+ // nothing else has an aggregate layout
356
+ _ => bug ! ( "aggregate_field_path_elem: got non-aggregate type {:?}" , ty) ,
342
357
}
343
358
}
344
359
}
0 commit comments