11use rustc_hir:: def:: DefKind ;
22use rustc_middle:: mir;
3+ use rustc_middle:: mir:: interpret:: PointerArithmetic ;
4+ use rustc_middle:: ty:: layout:: FnAbiOf ;
35use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
46use std:: borrow:: Borrow ;
57use std:: hash:: Hash ;
8+ use std:: ops:: ControlFlow ;
69
710use rustc_data_structures:: fx:: FxIndexMap ;
811use rustc_data_structures:: fx:: IndexEntry ;
@@ -17,8 +20,8 @@ use rustc_target::abi::{Align, Size};
1720use rustc_target:: spec:: abi:: Abi as CallAbi ;
1821
1922use 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 ,
2225} ;
2326
2427use super :: error:: * ;
@@ -145,15 +148,19 @@ impl interpret::MayLeak for ! {
145148}
146149
147150impl < ' 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.
151156 fn hook_special_const_fn (
152157 & mut self ,
153158 instance : ty:: Instance < ' tcx > ,
159+ _abi : CallAbi ,
154160 args : & [ OpTy < ' tcx > ] ,
161+ dest : & PlaceTy < ' tcx > ,
162+ ret : Option < mir:: BasicBlock > ,
155163 ) -> InterpResult < ' tcx , Option < ty:: Instance < ' tcx > > > {
156- // All `#[rustc_do_not_const_check]` functions should be hooked here.
157164 let def_id = instance. def_id ( ) ;
158165
159166 if Some ( def_id) == self . tcx . lang_items ( ) . panic_display ( )
@@ -173,20 +180,91 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
173180 return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
174181 } else if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
175182 // 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 )
187266 }
188267 }
189- Ok ( None )
190268 }
191269
192270 /// See documentation on the `ptr_guaranteed_cmp` intrinsic.
@@ -269,8 +347,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
269347 instance : ty:: Instance < ' tcx > ,
270348 _abi : CallAbi ,
271349 args : & [ OpTy < ' tcx > ] ,
272- _dest : & PlaceTy < ' tcx > ,
273- _ret : Option < mir:: BasicBlock > ,
350+ dest : & PlaceTy < ' tcx > ,
351+ ret : Option < mir:: BasicBlock > ,
274352 _unwind : StackPopUnwind , // unwinding is not supported in consts
275353 ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty:: Instance < ' tcx > ) > > {
276354 debug ! ( "find_mir_or_eval_fn: {:?}" , instance) ;
@@ -289,7 +367,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
289367 }
290368 }
291369
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 {
293375 // We call another const fn instead.
294376 // However, we return the *original* instance to make backtraces work out
295377 // (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,
298380 new_instance,
299381 _abi,
300382 args,
301- _dest ,
302- _ret ,
383+ dest ,
384+ ret ,
303385 _unwind,
304386 ) ?
305387 . map ( |( body, _instance) | ( body, instance) ) ) ;
306388 }
307389 }
390+
308391 // This is a const fn. Call it.
309392 Ok ( Some ( ( ecx. load_mir ( instance. def , None ) ?, instance) ) )
310393 }
0 commit comments