1
1
use rustc_hir:: def:: DefKind ;
2
+ use rustc_hir:: LangItem ;
2
3
use rustc_middle:: mir;
4
+ use rustc_middle:: mir:: interpret:: PointerArithmetic ;
5
+ use rustc_middle:: ty:: layout:: FnAbiOf ;
3
6
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
4
7
use std:: borrow:: Borrow ;
5
8
use std:: hash:: Hash ;
9
+ use std:: ops:: ControlFlow ;
6
10
7
11
use rustc_data_structures:: fx:: FxIndexMap ;
8
12
use rustc_data_structures:: fx:: IndexEntry ;
@@ -17,58 +21,12 @@ use rustc_target::abi::{Align, Size};
17
21
use rustc_target:: spec:: abi:: Abi as CallAbi ;
18
22
19
23
use crate :: interpret:: {
20
- self , compile_time_machine, AllocId , ConstAllocation , Frame , ImmTy , InterpCx , InterpResult ,
21
- OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
24
+ self , compile_time_machine, AllocId , ConstAllocation , FnVal , Frame , ImmTy , InterpCx ,
25
+ InterpResult , OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
22
26
} ;
23
27
24
28
use super :: error:: * ;
25
29
26
- impl < ' mir , ' tcx > InterpCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > {
27
- /// "Intercept" a function call to a panic-related function
28
- /// because we have something special to do for it.
29
- /// If this returns successfully (`Ok`), the function should just be evaluated normally.
30
- fn hook_special_const_fn (
31
- & mut self ,
32
- instance : ty:: Instance < ' tcx > ,
33
- args : & [ OpTy < ' tcx > ] ,
34
- ) -> InterpResult < ' tcx , Option < ty:: Instance < ' tcx > > > {
35
- // All `#[rustc_do_not_const_check]` functions should be hooked here.
36
- let def_id = instance. def_id ( ) ;
37
-
38
- if Some ( def_id) == self . tcx . lang_items ( ) . panic_display ( )
39
- || Some ( def_id) == self . tcx . lang_items ( ) . begin_panic_fn ( )
40
- {
41
- // &str or &&str
42
- assert ! ( args. len( ) == 1 ) ;
43
-
44
- let mut msg_place = self . deref_operand ( & args[ 0 ] ) ?;
45
- while msg_place. layout . ty . is_ref ( ) {
46
- msg_place = self . deref_operand ( & msg_place. into ( ) ) ?;
47
- }
48
-
49
- let msg = Symbol :: intern ( self . read_str ( & msg_place) ?) ;
50
- let span = self . find_closest_untracked_caller_location ( ) ;
51
- let ( file, line, col) = self . location_triple_for_span ( span) ;
52
- return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
53
- } else if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
54
- // For panic_fmt, call const_panic_fmt instead.
55
- if let Some ( const_panic_fmt) = self . tcx . lang_items ( ) . const_panic_fmt ( ) {
56
- return Ok ( Some (
57
- ty:: Instance :: resolve (
58
- * self . tcx ,
59
- ty:: ParamEnv :: reveal_all ( ) ,
60
- const_panic_fmt,
61
- self . tcx . intern_substs ( & [ ] ) ,
62
- )
63
- . unwrap ( )
64
- . unwrap ( ) ,
65
- ) ) ;
66
- }
67
- }
68
- Ok ( None )
69
- }
70
- }
71
-
72
30
/// Extra machine state for CTFE, and the Machine instance
73
31
pub struct CompileTimeInterpreter < ' mir , ' tcx > {
74
32
/// For now, the number of terminators that can be evaluated before we throw a resource
@@ -191,6 +149,125 @@ impl interpret::MayLeak for ! {
191
149
}
192
150
193
151
impl < ' mir , ' tcx : ' mir > CompileTimeEvalContext < ' mir , ' tcx > {
152
+ /// "Intercept" a function call, because we have something special to do for it.
153
+ /// All `#[rustc_do_not_const_check]` functions should be hooked here.
154
+ /// If this returns `Some` function, which may be `instance` or a different function with
155
+ /// compatible arguments, then evaluation should continue with that function.
156
+ /// If this returns `None`, the function call has been handled and the function has returned.
157
+ fn hook_special_const_fn (
158
+ & mut self ,
159
+ instance : ty:: Instance < ' tcx > ,
160
+ args : & [ OpTy < ' tcx > ] ,
161
+ dest : & PlaceTy < ' tcx > ,
162
+ ret : Option < mir:: BasicBlock > ,
163
+ ) -> InterpResult < ' tcx , Option < ty:: Instance < ' tcx > > > {
164
+ let def_id = instance. def_id ( ) ;
165
+
166
+ if Some ( def_id) == self . tcx . lang_items ( ) . panic_display ( )
167
+ || Some ( def_id) == self . tcx . lang_items ( ) . begin_panic_fn ( )
168
+ {
169
+ // &str or &&str
170
+ assert ! ( args. len( ) == 1 ) ;
171
+
172
+ let mut msg_place = self . deref_operand ( & args[ 0 ] ) ?;
173
+ while msg_place. layout . ty . is_ref ( ) {
174
+ msg_place = self . deref_operand ( & msg_place. into ( ) ) ?;
175
+ }
176
+
177
+ let msg = Symbol :: intern ( self . read_str ( & msg_place) ?) ;
178
+ let span = self . find_closest_untracked_caller_location ( ) ;
179
+ let ( file, line, col) = self . location_triple_for_span ( span) ;
180
+ return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
181
+ } else if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
182
+ // For panic_fmt, call const_panic_fmt instead.
183
+ let const_def_id = self . tcx . require_lang_item ( LangItem :: ConstPanicFmt , None ) ;
184
+ let new_instance = ty:: Instance :: resolve (
185
+ * self . tcx ,
186
+ ty:: ParamEnv :: reveal_all ( ) ,
187
+ const_def_id,
188
+ instance. substs ,
189
+ )
190
+ . unwrap ( )
191
+ . unwrap ( ) ;
192
+
193
+ return Ok ( Some ( new_instance) ) ;
194
+ } else if Some ( def_id) == self . tcx . lang_items ( ) . align_offset_fn ( ) {
195
+ // For align_offset, we replace the function call if the pointer has no address.
196
+ match self . align_offset ( instance, args, dest, ret) ? {
197
+ ControlFlow :: Continue ( ( ) ) => return Ok ( Some ( instance) ) ,
198
+ ControlFlow :: Break ( ( ) ) => return Ok ( None ) ,
199
+ }
200
+ }
201
+ Ok ( Some ( instance) )
202
+ }
203
+
204
+ /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
205
+ /// may not have an address.
206
+ ///
207
+ /// If `ptr` does have a known address, then we return `CONTINUE` and the function call should
208
+ /// proceed as normal.
209
+ ///
210
+ /// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most
211
+ /// `target_align`, then we call the function again with an dummy address relative to the
212
+ /// allocation.
213
+ ///
214
+ /// If `ptr` doesn't have an address and `target_align` is stricter than the underlying
215
+ /// allocation's alignment, then we return `usize::MAX` immediately.
216
+ fn align_offset (
217
+ & mut self ,
218
+ instance : ty:: Instance < ' tcx > ,
219
+ args : & [ OpTy < ' tcx > ] ,
220
+ dest : & PlaceTy < ' tcx > ,
221
+ ret : Option < mir:: BasicBlock > ,
222
+ ) -> InterpResult < ' tcx , ControlFlow < ( ) > > {
223
+ assert_eq ! ( args. len( ) , 2 ) ;
224
+
225
+ let ptr = self . read_pointer ( & args[ 0 ] ) ?;
226
+ let target_align = self . read_scalar ( & args[ 1 ] ) ?. to_machine_usize ( self ) ?;
227
+
228
+ if !target_align. is_power_of_two ( ) {
229
+ throw_ub_format ! ( "`align_offset` called with non-power-of-two align: {}" , target_align) ;
230
+ }
231
+
232
+ match self . ptr_try_get_alloc_id ( ptr) {
233
+ Ok ( ( alloc_id, offset, _extra) ) => {
234
+ let ( _size, alloc_align, _kind) = self . get_alloc_info ( alloc_id) ;
235
+
236
+ if target_align <= alloc_align. bytes ( ) {
237
+ // Extract the address relative to the allocation base that is definitely
238
+ // sufficiently aligned and call `align_offset` again.
239
+ let addr = ImmTy :: from_uint ( offset. bytes ( ) , args[ 0 ] . layout ) . into ( ) ;
240
+ let align = ImmTy :: from_uint ( target_align, args[ 1 ] . layout ) . into ( ) ;
241
+ let fn_abi = self . fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ?;
242
+
243
+ // We replace the entire entire function call with a "tail call".
244
+ // Note that this happens before the frame of the original function
245
+ // is pushed on the stack.
246
+ self . eval_fn_call (
247
+ FnVal :: Instance ( instance) ,
248
+ ( CallAbi :: Rust , fn_abi) ,
249
+ & [ addr, align] ,
250
+ /* with_caller_location = */ false ,
251
+ dest,
252
+ ret,
253
+ StackPopUnwind :: NotAllowed ,
254
+ ) ?;
255
+ Ok ( ControlFlow :: BREAK )
256
+ } else {
257
+ // Not alignable in const, return `usize::MAX`.
258
+ let usize_max = Scalar :: from_machine_usize ( self . machine_usize_max ( ) , self ) ;
259
+ self . write_scalar ( usize_max, dest) ?;
260
+ self . return_to_block ( ret) ?;
261
+ Ok ( ControlFlow :: BREAK )
262
+ }
263
+ }
264
+ Err ( _addr) => {
265
+ // The pointer has an address, continue with function call.
266
+ Ok ( ControlFlow :: CONTINUE )
267
+ }
268
+ }
269
+ }
270
+
194
271
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
195
272
fn guaranteed_cmp ( & mut self , a : Scalar , b : Scalar ) -> InterpResult < ' tcx , u8 > {
196
273
Ok ( match ( a, b) {
@@ -271,8 +348,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
271
348
instance : ty:: Instance < ' tcx > ,
272
349
_abi : CallAbi ,
273
350
args : & [ OpTy < ' tcx > ] ,
274
- _dest : & PlaceTy < ' tcx > ,
275
- _ret : Option < mir:: BasicBlock > ,
351
+ dest : & PlaceTy < ' tcx > ,
352
+ ret : Option < mir:: BasicBlock > ,
276
353
_unwind : StackPopUnwind , // unwinding is not supported in consts
277
354
) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty:: Instance < ' tcx > ) > > {
278
355
debug ! ( "find_mir_or_eval_fn: {:?}" , instance) ;
@@ -291,7 +368,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
291
368
}
292
369
}
293
370
294
- if let Some ( new_instance) = ecx. hook_special_const_fn ( instance, args) ? {
371
+ let Some ( new_instance) = ecx. hook_special_const_fn ( instance, args, dest, ret) ? else {
372
+ return Ok ( None ) ;
373
+ } ;
374
+
375
+ if new_instance != instance {
295
376
// We call another const fn instead.
296
377
// However, we return the *original* instance to make backtraces work out
297
378
// (and we hope this does not confuse the FnAbi checks too much).
@@ -300,13 +381,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
300
381
new_instance,
301
382
_abi,
302
383
args,
303
- _dest ,
304
- _ret ,
384
+ dest ,
385
+ ret ,
305
386
_unwind,
306
387
) ?
307
388
. map ( |( body, _instance) | ( body, instance) ) ) ;
308
389
}
309
390
}
391
+
310
392
// This is a const fn. Call it.
311
393
Ok ( Some ( ( ecx. load_mir ( instance. def , None ) ?, instance) ) )
312
394
}
0 commit comments