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 ;
3
5
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
4
6
use std:: borrow:: Borrow ;
5
7
use std:: collections:: hash_map:: Entry ;
6
8
use std:: hash:: Hash ;
9
+ use std:: ops:: ControlFlow ;
7
10
8
11
use rustc_data_structures:: fx:: FxHashMap ;
9
12
use std:: fmt;
@@ -17,8 +20,8 @@ use rustc_target::abi::{Align, Size};
17
20
use rustc_target:: spec:: abi:: Abi as CallAbi ;
18
21
19
22
use crate :: interpret:: {
20
- self , compile_time_machine, AllocId , ConstAllocation , Frame , ImmTy , InterpCx , InterpResult ,
21
- OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
23
+ self , compile_time_machine, AllocId , ConstAllocation , FnVal , Frame , ImmTy , InterpCx ,
24
+ InterpResult , OpTy , PlaceTy , Pointer , Scalar , StackPopUnwind ,
22
25
} ;
23
26
24
27
use super :: error:: * ;
@@ -145,15 +148,19 @@ impl interpret::MayLeak for ! {
145
148
}
146
149
147
150
impl < ' mir , ' tcx : ' mir > CompileTimeEvalContext < ' mir , ' tcx > {
148
- /// "Intercept" a function call to a panic-related function
149
- /// because we have something special to do for it.
150
- /// If this returns successfully (`Ok`), the function should just be evaluated normally.
151
+ /// "Intercept" a function call, because we have something special to do for it.
152
+ /// All `#[rustc_do_not_const_check]` functions should be hooked here.
153
+ /// If this returns `Some` function, which may be `instance` or a different function with
154
+ /// compatible arguments, then evaluation should continue with that function.
155
+ /// If this returns `None`, the function call has been handled and the function has returned.
151
156
fn hook_special_const_fn (
152
157
& mut self ,
153
158
instance : ty:: Instance < ' tcx > ,
159
+ _abi : CallAbi ,
154
160
args : & [ OpTy < ' tcx > ] ,
161
+ dest : & PlaceTy < ' tcx > ,
162
+ ret : Option < mir:: BasicBlock > ,
155
163
) -> InterpResult < ' tcx , Option < ty:: Instance < ' tcx > > > {
156
- // All `#[rustc_do_not_const_check]` functions should be hooked here.
157
164
let def_id = instance. def_id ( ) ;
158
165
159
166
if Some ( def_id) == self . tcx . lang_items ( ) . panic_display ( )
@@ -173,20 +180,91 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
173
180
return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
174
181
} else if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
175
182
// For panic_fmt, call const_panic_fmt instead.
176
- if let Some ( const_panic_fmt) = self . tcx . lang_items ( ) . const_panic_fmt ( ) {
177
- return Ok ( Some (
178
- ty:: Instance :: resolve (
179
- * self . tcx ,
180
- ty:: ParamEnv :: reveal_all ( ) ,
181
- const_panic_fmt,
182
- self . tcx . intern_substs ( & [ ] ) ,
183
- )
184
- . unwrap ( )
185
- . unwrap ( ) ,
186
- ) ) ;
183
+ let Some ( const_def_id) = self . tcx . lang_items ( ) . const_panic_fmt ( ) else {
184
+ bug ! ( "`const_panic_fmt` must be defined to call `panic_fmt` in const eval" )
185
+ } ;
186
+ let new_instance = ty:: Instance :: resolve (
187
+ * self . tcx ,
188
+ ty:: ParamEnv :: reveal_all ( ) ,
189
+ const_def_id,
190
+ instance. substs ,
191
+ )
192
+ . unwrap ( )
193
+ . unwrap ( ) ;
194
+
195
+ return Ok ( Some ( new_instance) ) ;
196
+ } else if Some ( def_id) == self . tcx . lang_items ( ) . align_offset_fn ( ) {
197
+ // For align_offset, we replace the function call if the pointer has no address.
198
+ match self . align_offset ( instance, args, dest, ret) ? {
199
+ ControlFlow :: Continue ( ( ) ) => return Ok ( Some ( instance) ) ,
200
+ ControlFlow :: Break ( ( ) ) => return Ok ( None ) ,
201
+ }
202
+ }
203
+ Ok ( Some ( instance) )
204
+ }
205
+
206
+ /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
207
+ /// may not have an address.
208
+ ///
209
+ /// If `ptr` does have a known address, then we return `CONTINUE` and the function call should
210
+ /// proceed as normal.
211
+ ///
212
+ /// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most
213
+ /// `target_align`, then we call the function again with an dummy address relative to the
214
+ /// allocation.
215
+ ///
216
+ /// If `ptr` doesn't have an address and `target_align` is stricter than the underlying
217
+ /// allocation's alignment, then we return `usize::MAX` immediately.
218
+ fn align_offset (
219
+ & mut self ,
220
+ instance : ty:: Instance < ' tcx > ,
221
+ args : & [ OpTy < ' tcx > ] ,
222
+ dest : & PlaceTy < ' tcx > ,
223
+ ret : Option < mir:: BasicBlock > ,
224
+ ) -> InterpResult < ' tcx , ControlFlow < ( ) > > {
225
+ assert_eq ! ( args. len( ) , 2 ) ;
226
+
227
+ let ptr = self . read_pointer ( & args[ 0 ] ) ?;
228
+ let target_align = self . read_scalar ( & args[ 1 ] ) ?. to_machine_usize ( self ) ?;
229
+
230
+ if !target_align. is_power_of_two ( ) {
231
+ throw_ub_format ! ( "`align_offset` called with non-power-of-two align: {}" , target_align) ;
232
+ }
233
+
234
+ match self . ptr_try_get_alloc_id ( ptr) {
235
+ Ok ( ( alloc_id, offset, _extra) ) => {
236
+ let ( _size, alloc_align, _kind) = self . get_alloc_info ( alloc_id) ;
237
+
238
+ if target_align <= alloc_align. bytes ( ) {
239
+ // Extract the address relative to the allocation base that is definitely
240
+ // sufficiently aligned and call `align_offset` again.
241
+ let addr = ImmTy :: from_uint ( offset. bytes ( ) , args[ 0 ] . layout ) . into ( ) ;
242
+ let align = ImmTy :: from_uint ( target_align, args[ 1 ] . layout ) . into ( ) ;
243
+
244
+ let fn_abi = self . fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ?;
245
+ self . eval_fn_call (
246
+ FnVal :: Instance ( instance) ,
247
+ ( CallAbi :: Rust , fn_abi) ,
248
+ & [ addr, align] ,
249
+ false ,
250
+ dest,
251
+ ret,
252
+ StackPopUnwind :: NotAllowed ,
253
+ ) ?;
254
+ Ok ( ControlFlow :: BREAK )
255
+ } else {
256
+ // Not alignable in const, return `usize::MAX`.
257
+ let usize_max = Scalar :: from_machine_usize ( self . machine_usize_max ( ) , self ) ;
258
+ self . write_scalar ( usize_max, dest) ?;
259
+ self . return_to_block ( ret) ?;
260
+ Ok ( ControlFlow :: BREAK )
261
+ }
262
+ }
263
+ Err ( _addr) => {
264
+ // The pointer has an address, continue with function call.
265
+ Ok ( ControlFlow :: CONTINUE )
187
266
}
188
267
}
189
- Ok ( None )
190
268
}
191
269
192
270
/// See documentation on the `ptr_guaranteed_cmp` intrinsic.
@@ -269,8 +347,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
269
347
instance : ty:: Instance < ' tcx > ,
270
348
_abi : CallAbi ,
271
349
args : & [ OpTy < ' tcx > ] ,
272
- _dest : & PlaceTy < ' tcx > ,
273
- _ret : Option < mir:: BasicBlock > ,
350
+ dest : & PlaceTy < ' tcx > ,
351
+ ret : Option < mir:: BasicBlock > ,
274
352
_unwind : StackPopUnwind , // unwinding is not supported in consts
275
353
) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty:: Instance < ' tcx > ) > > {
276
354
debug ! ( "find_mir_or_eval_fn: {:?}" , instance) ;
@@ -289,7 +367,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
289
367
}
290
368
}
291
369
292
- if let Some ( new_instance) = ecx. hook_special_const_fn ( instance, args) ? {
370
+ let Some ( new_instance) = ecx. hook_special_const_fn ( instance, _abi, args, dest, ret) ? else {
371
+ return Ok ( None ) ;
372
+ } ;
373
+
374
+ if new_instance != instance {
293
375
// We call another const fn instead.
294
376
// However, we return the *original* instance to make backtraces work out
295
377
// (and we hope this does not confuse the FnAbi checks too much).
@@ -298,13 +380,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
298
380
new_instance,
299
381
_abi,
300
382
args,
301
- _dest ,
302
- _ret ,
383
+ dest ,
384
+ ret ,
303
385
_unwind,
304
386
) ?
305
387
. map ( |( body, _instance) | ( body, instance) ) ) ;
306
388
}
307
389
}
390
+
308
391
// This is a const fn. Call it.
309
392
Ok ( Some ( ( ecx. load_mir ( instance. def , None ) ?, instance) ) )
310
393
}
0 commit comments