@@ -3,6 +3,7 @@ use std::fmt;
33use std:: hash:: Hash ;
44use std:: ops:: ControlFlow ;
55
6+ use either:: Either ;
67use rustc_ast:: Mutability ;
78use rustc_data_structures:: fx:: FxIndexMap ;
89use rustc_data_structures:: fx:: IndexEntry ;
@@ -14,6 +15,7 @@ use rustc_middle::mir::AssertMessage;
1415use rustc_middle:: query:: TyCtxtAt ;
1516use rustc_middle:: ty;
1617use rustc_middle:: ty:: layout:: { FnAbiOf , TyAndLayout } ;
18+ use rustc_middle:: ty:: Ty ;
1719use rustc_session:: lint:: builtin:: WRITES_THROUGH_IMMUTABLE_POINTER ;
1820use rustc_span:: symbol:: { sym, Symbol } ;
1921use rustc_span:: Span ;
@@ -191,6 +193,16 @@ impl interpret::MayLeak for ! {
191193 }
192194}
193195
196+ #[ derive( Debug , Copy , Clone ) ]
197+ pub enum ExtraFnVal < ' tcx > {
198+ /// `#[rustc_const_panic_str]` or `#[lang = "begin_panic"]`
199+ BeginPanic ,
200+ /// `#[lang = "panic_fmt"]`
201+ PanicFmt ( ty:: Instance < ' tcx > ) ,
202+ /// `#[lang = "align_offset"]`
203+ AlignOffset ( ty:: Instance < ' tcx > ) ,
204+ }
205+
194206impl < ' mir , ' tcx : ' mir > CompileTimeEvalContext < ' mir , ' tcx > {
195207 fn location_triple_for_span ( & self , span : Span ) -> ( Symbol , u32 , u32 ) {
196208 let topmost = span. ctxt ( ) . outer_expn ( ) . expansion_cause ( ) . unwrap_or ( span) ;
@@ -212,56 +224,29 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
212224
213225 /// "Intercept" a function call, because we have something special to do for it.
214226 /// All `#[rustc_do_not_const_check]` functions should be hooked here.
215- /// If this returns `Some` function, which may be `instance` or a different function with
216- /// compatible arguments, then evaluation should continue with that function.
217- /// If this returns `None`, the function call has been handled and the function has returned.
218- fn hook_special_const_fn (
219- & mut self ,
220- instance : ty:: Instance < ' tcx > ,
221- args : & [ FnArg < ' tcx > ] ,
222- dest : & PlaceTy < ' tcx > ,
223- ret : Option < mir:: BasicBlock > ,
224- ) -> InterpResult < ' tcx , Option < ty:: Instance < ' tcx > > > {
227+ ///
228+ /// If this returns `Some`, the function should be executed via [`call_extra_fn`].
229+ /// If this returns `None`, the function should be executed as normal.
230+ ///
231+ /// [`call_extra_fn`]: interpret::Machine::call_extra_fn
232+ fn hook_special_const_fn ( & mut self , instance : ty:: Instance < ' tcx > ) -> Option < ExtraFnVal < ' tcx > > {
225233 let def_id = instance. def_id ( ) ;
226234
227235 if self . tcx . has_attr ( def_id, sym:: rustc_const_panic_str)
228236 || Some ( def_id) == self . tcx . lang_items ( ) . begin_panic_fn ( )
229237 {
230- let args = self . copy_fn_args ( args) ?;
231- // &str or &&str
232- assert ! ( args. len( ) == 1 ) ;
238+ return Some ( ExtraFnVal :: BeginPanic ) ;
239+ }
233240
234- let mut msg_place = self . deref_pointer ( & args[ 0 ] ) ?;
235- while msg_place. layout . ty . is_ref ( ) {
236- msg_place = self . deref_pointer ( & msg_place) ?;
237- }
241+ if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
242+ return Some ( ExtraFnVal :: PanicFmt ( instance) ) ;
243+ }
238244
239- let msg = Symbol :: intern ( self . read_str ( & msg_place) ?) ;
240- let span = self . find_closest_untracked_caller_location ( ) ;
241- let ( file, line, col) = self . location_triple_for_span ( span) ;
242- return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
243- } else if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
244- // For panic_fmt, call const_panic_fmt instead.
245- let const_def_id = self . tcx . require_lang_item ( LangItem :: ConstPanicFmt , None ) ;
246- let new_instance = ty:: Instance :: resolve (
247- * self . tcx ,
248- ty:: ParamEnv :: reveal_all ( ) ,
249- const_def_id,
250- instance. args ,
251- )
252- . unwrap ( )
253- . unwrap ( ) ;
254-
255- return Ok ( Some ( new_instance) ) ;
256- } else if Some ( def_id) == self . tcx . lang_items ( ) . align_offset_fn ( ) {
257- let args = self . copy_fn_args ( args) ?;
258- // For align_offset, we replace the function call if the pointer has no address.
259- match self . align_offset ( instance, & args, dest, ret) ? {
260- ControlFlow :: Continue ( ( ) ) => return Ok ( Some ( instance) ) ,
261- ControlFlow :: Break ( ( ) ) => return Ok ( None ) ,
262- }
245+ if Some ( def_id) == self . tcx . lang_items ( ) . align_offset_fn ( ) {
246+ return Some ( ExtraFnVal :: AlignOffset ( instance) ) ;
263247 }
264- Ok ( Some ( instance) )
248+
249+ None
265250 }
266251
267252 /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
@@ -371,6 +356,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
371356 compile_time_machine ! ( <' mir, ' tcx>) ;
372357
373358 type MemoryKind = MemoryKind ;
359+ type ExtraFnVal = ExtraFnVal < ' tcx > ;
374360
375361 const PANIC_ON_ALLOC_FAIL : bool = false ; // will be raised as a proper error
376362
@@ -399,7 +385,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
399385 . delayed_bug ( "This is likely a const item that is missing from its impl" ) ;
400386 throw_inval ! ( AlreadyReported ( guar. into( ) ) ) ;
401387 } else {
402- // `find_mir_or_eval_fn ` checks that this is a const fn before even calling us,
388+ // `find_mir_or_extra_fn ` checks that this is a const fn before even calling us,
403389 // so this should be unreachable.
404390 let path = ecx. tcx . def_path_str ( def) ;
405391 bug ! ( "trying to call extern function `{path}` at compile-time" ) ;
@@ -409,22 +395,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
409395 }
410396 }
411397
412- fn find_mir_or_eval_fn (
398+ fn find_mir_or_extra_fn (
413399 ecx : & mut InterpCx < ' mir , ' tcx , Self > ,
414- orig_instance : ty:: Instance < ' tcx > ,
400+ instance : ty:: Instance < ' tcx > ,
415401 _abi : CallAbi ,
416- args : & [ FnArg < ' tcx > ] ,
417- dest : & PlaceTy < ' tcx > ,
418- ret : Option < mir:: BasicBlock > ,
419- _unwind : mir:: UnwindAction , // unwinding is not supported in consts
420- ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty:: Instance < ' tcx > ) > > {
421- debug ! ( "find_mir_or_eval_fn: {:?}" , orig_instance) ;
402+ ) -> InterpResult < ' tcx , Either < & ' mir mir:: Body < ' tcx > , Self :: ExtraFnVal > > {
403+ debug ! ( "find_mir_or_extra_fn: {:?}" , instance) ;
422404
423405 // Replace some functions.
424- let Some ( instance) = ecx. hook_special_const_fn ( orig_instance, args, dest, ret) ? else {
425- // Call has already been handled.
426- return Ok ( None ) ;
427- } ;
406+ if let Some ( extra) = ecx. hook_special_const_fn ( instance) {
407+ return Ok ( Either :: Right ( extra) ) ;
408+ }
428409
429410 // Only check non-glue functions
430411 if let ty:: InstanceDef :: Item ( def) = instance. def {
@@ -442,10 +423,75 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
442423 }
443424 }
444425
445- // This is a const fn. Call it.
446- // In case of replacement, we return the *original* instance to make backtraces work out
447- // (and we hope this does not confuse the FnAbi checks too much).
448- Ok ( Some ( ( ecx. load_mir ( instance. def , None ) ?, orig_instance) ) )
426+ // This is a const fn. Return its mir to be called.
427+ ecx. load_mir ( instance. def , None ) . map ( Either :: Left )
428+ }
429+
430+ #[ inline( always) ]
431+ fn call_extra_fn (
432+ ecx : & mut InterpCx < ' mir , ' tcx , Self > ,
433+ fn_val : Self :: ExtraFnVal ,
434+ abis : ( CallAbi , & rustc_target:: abi:: call:: FnAbi < ' tcx , Ty < ' tcx > > ) ,
435+ args : & [ FnArg < ' tcx > ] ,
436+ destination : & PlaceTy < ' tcx , Self :: Provenance > ,
437+ target : Option < mir:: BasicBlock > ,
438+ unwind : mir:: UnwindAction ,
439+ ) -> InterpResult < ' tcx > {
440+ match fn_val {
441+ ExtraFnVal :: BeginPanic => {
442+ let args = ecx. copy_fn_args ( args) ?;
443+ // &str or &&str
444+ assert ! ( args. len( ) == 1 ) ;
445+
446+ let mut msg_place = ecx. deref_pointer ( & args[ 0 ] ) ?;
447+ while msg_place. layout . ty . is_ref ( ) {
448+ msg_place = ecx. deref_pointer ( & msg_place) ?;
449+ }
450+
451+ let msg = Symbol :: intern ( ecx. read_str ( & msg_place) ?) ;
452+ let span = ecx. find_closest_untracked_caller_location ( ) ;
453+ let ( file, line, col) = ecx. location_triple_for_span ( span) ;
454+ return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
455+ }
456+ ExtraFnVal :: PanicFmt ( instance) => {
457+ // For panic_fmt, call const_panic_fmt instead.
458+ let const_def_id = ecx. tcx . require_lang_item ( LangItem :: ConstPanicFmt , None ) ;
459+ let new_instance = ty:: Instance :: resolve (
460+ * ecx. tcx ,
461+ ty:: ParamEnv :: reveal_all ( ) ,
462+ const_def_id,
463+ instance. args ,
464+ )
465+ . unwrap ( )
466+ . unwrap ( ) ;
467+
468+ ecx. eval_fn_call (
469+ FnVal :: Instance ( new_instance) ,
470+ abis,
471+ args,
472+ true ,
473+ destination,
474+ target,
475+ unwind,
476+ )
477+ }
478+ ExtraFnVal :: AlignOffset ( instance) => {
479+ let args2 = ecx. copy_fn_args ( args) ?;
480+ // For align_offset, we replace the function call if the pointer has no address.
481+ match ecx. align_offset ( instance, & args2, destination, target) ? {
482+ ControlFlow :: Continue ( ( ) ) => ecx. eval_fn_call (
483+ FnVal :: Instance ( instance) ,
484+ abis,
485+ args,
486+ false ,
487+ destination,
488+ target,
489+ unwind,
490+ ) ,
491+ ControlFlow :: Break ( ( ) ) => Ok ( ( ) ) ,
492+ }
493+ }
494+ }
449495 }
450496
451497 fn panic_nounwind ( ecx : & mut InterpCx < ' mir , ' tcx , Self > , msg : & str ) -> InterpResult < ' tcx > {
0 commit comments