@@ -9,7 +9,7 @@ use std::convert::TryInto;
9
9
10
10
use rustc:: hir:: def:: DefKind ;
11
11
use rustc:: hir:: def_id:: DefId ;
12
- use rustc:: mir:: interpret:: { ConstEvalErr , ErrorHandled } ;
12
+ use rustc:: mir:: interpret:: { ConstEvalErr , ErrorHandled , ScalarMaybeUndef } ;
13
13
use rustc:: mir;
14
14
use rustc:: ty:: { self , TyCtxt , query:: TyCtxtAt } ;
15
15
use rustc:: ty:: layout:: { self , LayoutOf , VariantIdx } ;
@@ -18,15 +18,14 @@ use rustc::traits::Reveal;
18
18
use rustc:: util:: common:: ErrorReported ;
19
19
use rustc_data_structures:: fx:: FxHashMap ;
20
20
21
- use syntax:: ast:: Mutability ;
22
21
use syntax:: source_map:: { Span , DUMMY_SP } ;
23
22
24
23
use crate :: interpret:: { self ,
25
- PlaceTy , MPlaceTy , MemPlace , OpTy , ImmTy , Immediate , Scalar ,
24
+ PlaceTy , MPlaceTy , OpTy , ImmTy , Immediate , Scalar ,
26
25
RawConst , ConstValue ,
27
26
InterpResult , InterpErrorInfo , InterpError , GlobalId , InterpretCx , StackPopCleanup ,
28
27
Allocation , AllocId , MemoryKind ,
29
- snapshot, RefTracking ,
28
+ snapshot, RefTracking , intern_const_alloc_recursive ,
30
29
} ;
31
30
32
31
/// Number of steps until the detector even starts doing anything.
@@ -63,33 +62,19 @@ pub(crate) fn eval_promoted<'mir, 'tcx>(
63
62
eval_body_using_ecx ( & mut ecx, cid, body, param_env)
64
63
}
65
64
66
- fn mplace_to_const < ' tcx > (
67
- ecx : & CompileTimeEvalContext < ' _ , ' tcx > ,
68
- mplace : MPlaceTy < ' tcx > ,
69
- ) -> & ' tcx ty:: Const < ' tcx > {
70
- let MemPlace { ptr, align, meta } = * mplace;
71
- // extract alloc-offset pair
72
- assert ! ( meta. is_none( ) ) ;
73
- let ptr = ptr. to_ptr ( ) . unwrap ( ) ;
74
- let alloc = ecx. memory . get ( ptr. alloc_id ) . unwrap ( ) ;
75
- assert ! ( alloc. align >= align) ;
76
- assert ! ( alloc. bytes. len( ) as u64 - ptr. offset. bytes( ) >= mplace. layout. size. bytes( ) ) ;
77
- let mut alloc = alloc. clone ( ) ;
78
- alloc. align = align;
79
- // FIXME shouldn't it be the case that `mark_static_initialized` has already
80
- // interned this? I thought that is the entire point of that `FinishStatic` stuff?
81
- let alloc = ecx. tcx . intern_const_alloc ( alloc) ;
82
- let val = ConstValue :: ByRef ( ptr, alloc) ;
83
- ecx. tcx . mk_const ( ty:: Const { val, ty : mplace. layout . ty } )
84
- }
85
-
86
65
fn op_to_const < ' tcx > (
87
66
ecx : & CompileTimeEvalContext < ' _ , ' tcx > ,
88
67
op : OpTy < ' tcx > ,
89
68
) -> & ' tcx ty:: Const < ' tcx > {
90
- // We do not normalize just any data. Only non-union scalars and slices.
91
- let normalize = match op. layout . abi {
92
- layout:: Abi :: Scalar ( ..) => op. layout . ty . ty_adt_def ( ) . map_or ( true , |adt| !adt. is_union ( ) ) ,
69
+ // We do not have value optmizations for everything.
70
+ // Only scalars and slices, since they are very common.
71
+ // Note that further down we turn scalars of undefined bits back to `ByRef`. These can result
72
+ // from scalar unions that are initialized with one of their zero sized variants. We could
73
+ // instead allow `ConstValue::Scalar` to store `ScalarMaybeUndef`, but that would affect all
74
+ // the usual cases of extracting e.g. a `usize`, without there being a real use case for the
75
+ // `Undef` situation.
76
+ let try_as_immediate = match op. layout . abi {
77
+ layout:: Abi :: Scalar ( ..) => true ,
93
78
layout:: Abi :: ScalarPair ( ..) => match op. layout . ty . sty {
94
79
ty:: Ref ( _, inner, _) => match inner. sty {
95
80
ty:: Slice ( elem) => elem == ecx. tcx . types . u8 ,
@@ -100,16 +85,38 @@ fn op_to_const<'tcx>(
100
85
} ,
101
86
_ => false ,
102
87
} ;
103
- let normalized_op = if normalize {
104
- Err ( * ecx. read_immediate ( op) . expect ( "normalization works on validated constants" ) )
88
+ let immediate = if try_as_immediate {
89
+ Err ( ecx. read_immediate ( op) . expect ( "normalization works on validated constants" ) )
105
90
} else {
91
+ // It is guaranteed that any non-slice scalar pair is actually ByRef here.
92
+ // When we come back from raw const eval, we are always by-ref. The only way our op here is
93
+ // by-val is if we are in const_field, i.e., if this is (a field of) something that we
94
+ // "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
95
+ // structs containing such.
106
96
op. try_as_mplace ( )
107
97
} ;
108
- let val = match normalized_op {
109
- Ok ( mplace) => return mplace_to_const ( ecx, mplace) ,
110
- Err ( Immediate :: Scalar ( x) ) =>
111
- ConstValue :: Scalar ( x. not_undef ( ) . unwrap ( ) ) ,
112
- Err ( Immediate :: ScalarPair ( a, b) ) => {
98
+ let val = match immediate {
99
+ Ok ( mplace) => {
100
+ let ptr = mplace. ptr . to_ptr ( ) . unwrap ( ) ;
101
+ let alloc = ecx. tcx . alloc_map . lock ( ) . unwrap_memory ( ptr. alloc_id ) ;
102
+ ConstValue :: ByRef ( ptr, mplace. align , alloc)
103
+ } ,
104
+ // see comment on `let try_as_immediate` above
105
+ Err ( ImmTy { imm : Immediate :: Scalar ( x) , .. } ) => match x {
106
+ ScalarMaybeUndef :: Scalar ( s) => ConstValue :: Scalar ( s) ,
107
+ ScalarMaybeUndef :: Undef => {
108
+ // When coming out of "normal CTFE", we'll always have an `Indirect` operand as
109
+ // argument and we will not need this. The only way we can already have an
110
+ // `Immediate` is when we are called from `const_field`, and that `Immediate`
111
+ // comes from a constant so it can happen have `Undef`, because the indirect
112
+ // memory that was read had undefined bytes.
113
+ let mplace = op. to_mem_place ( ) ;
114
+ let ptr = mplace. ptr . to_ptr ( ) . unwrap ( ) ;
115
+ let alloc = ecx. tcx . alloc_map . lock ( ) . unwrap_memory ( ptr. alloc_id ) ;
116
+ ConstValue :: ByRef ( ptr, mplace. align , alloc)
117
+ } ,
118
+ } ,
119
+ Err ( ImmTy { imm : Immediate :: ScalarPair ( a, b) , .. } ) => {
113
120
let ( data, start) = match a. not_undef ( ) . unwrap ( ) {
114
121
Scalar :: Ptr ( ptr) => (
115
122
ecx. tcx . alloc_map . lock ( ) . unwrap_memory ( ptr. alloc_id ) ,
@@ -164,13 +171,12 @@ fn eval_body_using_ecx<'mir, 'tcx>(
164
171
ecx. run ( ) ?;
165
172
166
173
// Intern the result
167
- let mutability = if tcx. is_mutable_static ( cid. instance . def_id ( ) ) ||
168
- !layout. ty . is_freeze ( tcx, param_env, body. span ) {
169
- Mutability :: Mutable
170
- } else {
171
- Mutability :: Immutable
172
- } ;
173
- ecx. memory . intern_static ( ret. ptr . to_ptr ( ) ?. alloc_id , mutability) ?;
174
+ intern_const_alloc_recursive (
175
+ ecx,
176
+ cid. instance . def_id ( ) ,
177
+ ret,
178
+ param_env,
179
+ ) ?;
174
180
175
181
debug ! ( "eval_body_using_ecx done: {:?}" , * ret) ;
176
182
Ok ( ret)
@@ -297,7 +303,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
297
303
}
298
304
}
299
305
300
- type CompileTimeEvalContext < ' mir , ' tcx > =
306
+ crate type CompileTimeEvalContext < ' mir , ' tcx > =
301
307
InterpretCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > ;
302
308
303
309
impl interpret:: MayLeak for ! {
@@ -526,13 +532,22 @@ fn validate_and_turn_into_const<'tcx>(
526
532
mplace. into ( ) ,
527
533
path,
528
534
Some ( & mut ref_tracking) ,
529
- true , // const mode
530
535
) ?;
531
536
}
532
537
// Now that we validated, turn this into a proper constant.
538
+ // Statics/promoteds are always `ByRef`, for the rest `op_to_const` decides
539
+ // whether they become immediates.
533
540
let def_id = cid. instance . def . def_id ( ) ;
534
541
if tcx. is_static ( def_id) || cid. promoted . is_some ( ) {
535
- Ok ( mplace_to_const ( & ecx, mplace) )
542
+ let ptr = mplace. ptr . to_ptr ( ) ?;
543
+ Ok ( tcx. mk_const ( ty:: Const {
544
+ val : ConstValue :: ByRef (
545
+ ptr,
546
+ mplace. align ,
547
+ ecx. tcx . alloc_map . lock ( ) . unwrap_memory ( ptr. alloc_id ) ,
548
+ ) ,
549
+ ty : mplace. layout . ty ,
550
+ } ) )
536
551
} else {
537
552
Ok ( op_to_const ( & ecx, mplace. into ( ) ) )
538
553
}
0 commit comments