11use rustc_hir:: def:: DefKind ;
22use rustc_middle:: mir;
3+ use rustc_middle:: mir:: interpret:: PointerArithmetic ;
4+ use rustc_middle:: ty:: layout:: { FnAbiOf , LayoutOf } ;
35use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
46use std:: borrow:: Borrow ;
57use std:: collections:: hash_map:: Entry ;
@@ -17,58 +19,12 @@ use rustc_target::abi::{Align, Size};
1719use rustc_target:: spec:: abi:: Abi as CallAbi ;
1820
1921use 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 ,
2224} ;
2325
2426use super :: error:: * ;
2527
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-
7228/// Extra machine state for CTFE, and the Machine instance
7329pub struct CompileTimeInterpreter < ' mir , ' tcx > {
7430 /// For now, the number of terminators that can be evaluated before we throw a resource
@@ -191,6 +147,120 @@ impl interpret::MayLeak for ! {
191147}
192148
193149impl < ' 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+
194264 /// See documentation on the `ptr_guaranteed_cmp` intrinsic.
195265 fn guaranteed_cmp ( & mut self , a : Scalar , b : Scalar ) -> InterpResult < ' tcx , u8 > {
196266 Ok ( match ( a, b) {
@@ -271,8 +341,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
271341 instance : ty:: Instance < ' tcx > ,
272342 _abi : CallAbi ,
273343 args : & [ OpTy < ' tcx > ] ,
274- _dest : & PlaceTy < ' tcx > ,
275- _ret : Option < mir:: BasicBlock > ,
344+ dest : & PlaceTy < ' tcx > ,
345+ ret : Option < mir:: BasicBlock > ,
276346 _unwind : StackPopUnwind , // unwinding is not supported in consts
277347 ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty:: Instance < ' tcx > ) > > {
278348 debug ! ( "find_mir_or_eval_fn: {:?}" , instance) ;
@@ -291,7 +361,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
291361 }
292362 }
293363
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 {
295369 // We call another const fn instead.
296370 // However, we return the *original* instance to make backtraces work out
297371 // (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,
300374 new_instance,
301375 _abi,
302376 args,
303- _dest ,
304- _ret ,
377+ dest ,
378+ ret ,
305379 _unwind,
306380 ) ?
307381 . map ( |( body, _instance) | ( body, instance) ) ) ;
308382 }
309383 }
384+
310385 // This is a const fn. Call it.
311386 Ok ( Some ( ( ecx. load_mir ( instance. def , None ) ?, instance) ) )
312387 }
0 commit comments