1
1
use rustc_hir:: def:: DefKind ;
2
2
use rustc_middle:: mir;
3
+ use rustc_middle:: mir:: interpret:: PointerArithmetic ;
4
+ use rustc_middle:: ty:: layout:: { FnAbiOf , LayoutOf } ;
3
5
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
4
6
use std:: borrow:: Borrow ;
5
7
use std:: collections:: hash_map:: Entry ;
@@ -17,58 +19,12 @@ use rustc_target::abi::{Align, Size};
17
19
use rustc_target:: spec:: abi:: Abi as CallAbi ;
18
20
19
21
use crate :: interpret:: {
20
- self , compile_time_machine, AllocId , ConstAllocation , Frame , ImmTy , InterpCx , InterpResult ,
21
- OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
22
+ self , compile_time_machine, AllocId , ConstAllocation , FnVal , Frame , ImmTy , InterpCx ,
23
+ InterpResult , OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
22
24
} ;
23
25
24
26
use super :: error:: * ;
25
27
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
28
/// Extra machine state for CTFE, and the Machine instance
73
29
pub struct CompileTimeInterpreter < ' mir , ' tcx > {
74
30
/// For now, the number of terminators that can be evaluated before we throw a resource
@@ -191,6 +147,120 @@ impl interpret::MayLeak for ! {
191
147
}
192
148
193
149
impl < ' mir , ' tcx : ' mir > CompileTimeEvalContext < ' mir , ' tcx > {
150
+ /// "Intercept" a function call to a panic-related function
151
+ /// because we have something special to do for it.
152
+ /// If this returns successfully (`Ok`), the function should just be evaluated normally.
153
+ fn hook_special_const_fn (
154
+ & mut self ,
155
+ instance : ty:: Instance < ' tcx > ,
156
+ _abi : CallAbi ,
157
+ args : & [ OpTy < ' tcx > ] ,
158
+ dest : & PlaceTy < ' tcx > ,
159
+ ret : Option < mir:: BasicBlock > ,
160
+ ) -> InterpResult < ' tcx , Option < ty:: Instance < ' tcx > > > {
161
+ // All `#[rustc_do_not_const_check]` functions should be hooked here.
162
+ let def_id = instance. def_id ( ) ;
163
+
164
+ if Some ( def_id) == self . tcx . lang_items ( ) . panic_display ( )
165
+ || Some ( def_id) == self . tcx . lang_items ( ) . begin_panic_fn ( )
166
+ {
167
+ // &str or &&str
168
+ assert ! ( args. len( ) == 1 ) ;
169
+
170
+ let mut msg_place = self . deref_operand ( & args[ 0 ] ) ?;
171
+ while msg_place. layout . ty . is_ref ( ) {
172
+ msg_place = self . deref_operand ( & msg_place. into ( ) ) ?;
173
+ }
174
+
175
+ let msg = Symbol :: intern ( self . read_str ( & msg_place) ?) ;
176
+ let span = self . find_closest_untracked_caller_location ( ) ;
177
+ let ( file, line, col) = self . location_triple_for_span ( span) ;
178
+ return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
179
+ } else if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
180
+ // For panic_fmt, call const_panic_fmt instead.
181
+ let Some ( const_def_id) = self . tcx . lang_items ( ) . const_panic_fmt ( ) else {
182
+ bug ! ( "`const_panic_fmt` must be defined to call `panic_fmt` in const eval" )
183
+ } ;
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 either call const_align_offset or return usize::MAX directly.
196
+
197
+ let Some ( const_def_id) = self . tcx . lang_items ( ) . const_align_offset_fn ( ) else {
198
+ bug ! ( "`const_align_offset` must be defined to call `align_offset` in const eval" )
199
+ } ;
200
+ let const_instance = ty:: Instance :: resolve (
201
+ * self . tcx ,
202
+ ty:: ParamEnv :: reveal_all ( ) ,
203
+ const_def_id,
204
+ instance. substs ,
205
+ )
206
+ . unwrap ( )
207
+ . unwrap ( ) ;
208
+
209
+ self . align_offset ( const_instance, args, dest, ret) ?;
210
+
211
+ return Ok ( None ) ;
212
+ }
213
+ Ok ( Some ( instance) )
214
+ }
215
+
216
+ fn align_offset (
217
+ & mut self ,
218
+ const_instance : ty:: Instance < ' tcx > ,
219
+ args : & [ OpTy < ' tcx > ] ,
220
+ dest : & PlaceTy < ' tcx > ,
221
+ ret : Option < mir:: BasicBlock > ,
222
+ ) -> InterpResult < ' tcx > {
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
+ let addr = 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
+ let usize_max = Scalar :: from_machine_usize ( self . machine_usize_max ( ) , self ) ;
238
+ self . write_scalar ( usize_max, dest) ?;
239
+ self . return_to_block ( ret) ?;
240
+ return Ok ( ( ) ) ;
241
+ } else {
242
+ offset. bytes ( )
243
+ }
244
+ }
245
+ Err ( addr) => addr,
246
+ } ;
247
+
248
+ let usize_layout = self . layout_of ( self . tcx . types . usize ) ?;
249
+ let addr = ImmTy :: from_uint ( addr, usize_layout) . into ( ) ;
250
+ let align = ImmTy :: from_uint ( target_align, usize_layout) . into ( ) ;
251
+
252
+ let fn_abi = self . fn_abi_of_instance ( const_instance, ty:: List :: empty ( ) ) ?;
253
+ self . eval_fn_call (
254
+ FnVal :: Instance ( const_instance) ,
255
+ ( CallAbi :: Rust , fn_abi) ,
256
+ & [ addr, align] ,
257
+ false ,
258
+ dest,
259
+ ret,
260
+ StackPopUnwind :: NotAllowed ,
261
+ )
262
+ }
263
+
194
264
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
195
265
fn guaranteed_cmp ( & mut self , a : Scalar , b : Scalar ) -> InterpResult < ' tcx , u8 > {
196
266
Ok ( match ( a, b) {
@@ -271,8 +341,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
271
341
instance : ty:: Instance < ' tcx > ,
272
342
_abi : CallAbi ,
273
343
args : & [ OpTy < ' tcx > ] ,
274
- _dest : & PlaceTy < ' tcx > ,
275
- _ret : Option < mir:: BasicBlock > ,
344
+ dest : & PlaceTy < ' tcx > ,
345
+ ret : Option < mir:: BasicBlock > ,
276
346
_unwind : StackPopUnwind , // unwinding is not supported in consts
277
347
) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty:: Instance < ' tcx > ) > > {
278
348
debug ! ( "find_mir_or_eval_fn: {:?}" , instance) ;
@@ -291,7 +361,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
291
361
}
292
362
}
293
363
294
- if let Some ( new_instance) = ecx. hook_special_const_fn ( instance, args) ? {
364
+ let Some ( new_instance) = ecx. hook_special_const_fn ( instance, _abi, args, dest, ret) ? else {
365
+ return Ok ( None ) ;
366
+ } ;
367
+
368
+ if new_instance != instance {
295
369
// We call another const fn instead.
296
370
// However, we return the *original* instance to make backtraces work out
297
371
// (and we hope this does not confuse the FnAbi checks too much).
@@ -300,13 +374,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
300
374
new_instance,
301
375
_abi,
302
376
args,
303
- _dest ,
304
- _ret ,
377
+ dest ,
378
+ ret ,
305
379
_unwind,
306
380
) ?
307
381
. map ( |( body, _instance) | ( body, instance) ) ) ;
308
382
}
309
383
}
384
+
310
385
// This is a const fn. Call it.
311
386
Ok ( Some ( ( ecx. load_mir ( instance. def , None ) ?, instance) ) )
312
387
}
0 commit comments