@@ -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 ;
@@ -187,6 +189,16 @@ impl interpret::MayLeak for ! {
187189 }
188190}
189191
192+ #[ derive( Debug , Copy , Clone ) ]
193+ pub enum ExtraFnVal < ' tcx > {
194+ /// `#[rustc_const_panic_str]` or `#[lang = "begin_panic"]`
195+ BeginPanic ,
196+ /// `#[lang = "panic_fmt"]`
197+ PanicFmt ( ty:: Instance < ' tcx > ) ,
198+ /// `#[lang = "align_offset"]`
199+ AlignOffset ( ty:: Instance < ' tcx > ) ,
200+ }
201+
190202impl < ' mir , ' tcx : ' mir > CompileTimeEvalContext < ' mir , ' tcx > {
191203 fn location_triple_for_span ( & self , span : Span ) -> ( Symbol , u32 , u32 ) {
192204 let topmost = span. ctxt ( ) . outer_expn ( ) . expansion_cause ( ) . unwrap_or ( span) ;
@@ -208,56 +220,29 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
208220
209221 /// "Intercept" a function call, because we have something special to do for it.
210222 /// All `#[rustc_do_not_const_check]` functions should be hooked here.
211- /// If this returns `Some` function, which may be `instance` or a different function with
212- /// compatible arguments, then evaluation should continue with that function.
213- /// If this returns `None`, the function call has been handled and the function has returned.
214- fn hook_special_const_fn (
215- & mut self ,
216- instance : ty:: Instance < ' tcx > ,
217- args : & [ FnArg < ' tcx > ] ,
218- dest : & PlaceTy < ' tcx > ,
219- ret : Option < mir:: BasicBlock > ,
220- ) -> InterpResult < ' tcx , Option < ty:: Instance < ' tcx > > > {
223+ ///
224+ /// If this returns `Some`, the function should be executed via [`call_extra_fn`].
225+ /// If this returns `None`, the function should be executed as normal.
226+ ///
227+ /// [`call_extra_fn`]: interpret::Machine::call_extra_fn
228+ fn hook_special_const_fn ( & mut self , instance : ty:: Instance < ' tcx > ) -> Option < ExtraFnVal < ' tcx > > {
221229 let def_id = instance. def_id ( ) ;
222230
223231 if self . tcx . has_attr ( def_id, sym:: rustc_const_panic_str)
224232 || Some ( def_id) == self . tcx . lang_items ( ) . begin_panic_fn ( )
225233 {
226- let args = self . copy_fn_args ( args) ?;
227- // &str or &&str
228- assert ! ( args. len( ) == 1 ) ;
234+ return Some ( ExtraFnVal :: BeginPanic ) ;
235+ }
229236
230- let mut msg_place = self . deref_pointer ( & args[ 0 ] ) ?;
231- while msg_place. layout . ty . is_ref ( ) {
232- msg_place = self . deref_pointer ( & msg_place) ?;
233- }
237+ if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
238+ return Some ( ExtraFnVal :: PanicFmt ( instance) ) ;
239+ }
234240
235- let msg = Symbol :: intern ( self . read_str ( & msg_place) ?) ;
236- let span = self . find_closest_untracked_caller_location ( ) ;
237- let ( file, line, col) = self . location_triple_for_span ( span) ;
238- return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
239- } else if Some ( def_id) == self . tcx . lang_items ( ) . panic_fmt ( ) {
240- // For panic_fmt, call const_panic_fmt instead.
241- let const_def_id = self . tcx . require_lang_item ( LangItem :: ConstPanicFmt , None ) ;
242- let new_instance = ty:: Instance :: resolve (
243- * self . tcx ,
244- ty:: ParamEnv :: reveal_all ( ) ,
245- const_def_id,
246- instance. args ,
247- )
248- . unwrap ( )
249- . unwrap ( ) ;
250-
251- return Ok ( Some ( new_instance) ) ;
252- } else if Some ( def_id) == self . tcx . lang_items ( ) . align_offset_fn ( ) {
253- let args = self . copy_fn_args ( args) ?;
254- // For align_offset, we replace the function call if the pointer has no address.
255- match self . align_offset ( instance, & args, dest, ret) ? {
256- ControlFlow :: Continue ( ( ) ) => return Ok ( Some ( instance) ) ,
257- ControlFlow :: Break ( ( ) ) => return Ok ( None ) ,
258- }
241+ if Some ( def_id) == self . tcx . lang_items ( ) . align_offset_fn ( ) {
242+ return Some ( ExtraFnVal :: AlignOffset ( instance) ) ;
259243 }
260- Ok ( Some ( instance) )
244+
245+ None
261246 }
262247
263248 /// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
@@ -367,6 +352,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
367352 compile_time_machine ! ( <' mir, ' tcx>) ;
368353
369354 type MemoryKind = MemoryKind ;
355+ type ExtraFnVal = ExtraFnVal < ' tcx > ;
370356
371357 const PANIC_ON_ALLOC_FAIL : bool = false ; // will be raised as a proper error
372358
@@ -395,7 +381,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
395381 . delayed_bug ( "This is likely a const item that is missing from its impl" ) ;
396382 throw_inval ! ( AlreadyReported ( guar. into( ) ) ) ;
397383 } else {
398- // `find_mir_or_eval_fn ` checks that this is a const fn before even calling us,
384+ // `find_mir_or_extra_fn ` checks that this is a const fn before even calling us,
399385 // so this should be unreachable.
400386 let path = ecx. tcx . def_path_str ( def) ;
401387 bug ! ( "trying to call extern function `{path}` at compile-time" ) ;
@@ -405,22 +391,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
405391 }
406392 }
407393
408- fn find_mir_or_eval_fn (
394+ fn find_mir_or_extra_fn (
409395 ecx : & mut InterpCx < ' mir , ' tcx , Self > ,
410- orig_instance : ty:: Instance < ' tcx > ,
396+ instance : ty:: Instance < ' tcx > ,
411397 _abi : CallAbi ,
412- args : & [ FnArg < ' tcx > ] ,
413- dest : & PlaceTy < ' tcx > ,
414- ret : Option < mir:: BasicBlock > ,
415- _unwind : mir:: UnwindAction , // unwinding is not supported in consts
416- ) -> InterpResult < ' tcx , Option < ( & ' mir mir:: Body < ' tcx > , ty:: Instance < ' tcx > ) > > {
417- debug ! ( "find_mir_or_eval_fn: {:?}" , orig_instance) ;
398+ ) -> InterpResult < ' tcx , Either < & ' mir mir:: Body < ' tcx > , Self :: ExtraFnVal > > {
399+ debug ! ( "find_mir_or_extra_fn: {:?}" , instance) ;
418400
419401 // Replace some functions.
420- let Some ( instance) = ecx. hook_special_const_fn ( orig_instance, args, dest, ret) ? else {
421- // Call has already been handled.
422- return Ok ( None ) ;
423- } ;
402+ if let Some ( extra) = ecx. hook_special_const_fn ( instance) {
403+ return Ok ( Either :: Right ( extra) ) ;
404+ }
424405
425406 // Only check non-glue functions
426407 if let ty:: InstanceDef :: Item ( def) = instance. def {
@@ -438,10 +419,75 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
438419 }
439420 }
440421
441- // This is a const fn. Call it.
442- // In case of replacement, we return the *original* instance to make backtraces work out
443- // (and we hope this does not confuse the FnAbi checks too much).
444- Ok ( Some ( ( ecx. load_mir ( instance. def , None ) ?, orig_instance) ) )
422+ // This is a const fn. Return its mir to be called.
423+ ecx. load_mir ( instance. def , None ) . map ( Either :: Left )
424+ }
425+
426+ #[ inline( always) ]
427+ fn call_extra_fn (
428+ ecx : & mut InterpCx < ' mir , ' tcx , Self > ,
429+ fn_val : Self :: ExtraFnVal ,
430+ abis : ( CallAbi , & rustc_target:: abi:: call:: FnAbi < ' tcx , Ty < ' tcx > > ) ,
431+ args : & [ FnArg < ' tcx > ] ,
432+ destination : & PlaceTy < ' tcx , Self :: Provenance > ,
433+ target : Option < mir:: BasicBlock > ,
434+ unwind : mir:: UnwindAction ,
435+ ) -> InterpResult < ' tcx > {
436+ match fn_val {
437+ ExtraFnVal :: BeginPanic => {
438+ let args = ecx. copy_fn_args ( args) ?;
439+ // &str or &&str
440+ assert ! ( args. len( ) == 1 ) ;
441+
442+ let mut msg_place = ecx. deref_pointer ( & args[ 0 ] ) ?;
443+ while msg_place. layout . ty . is_ref ( ) {
444+ msg_place = ecx. deref_pointer ( & msg_place) ?;
445+ }
446+
447+ let msg = Symbol :: intern ( ecx. read_str ( & msg_place) ?) ;
448+ let span = ecx. find_closest_untracked_caller_location ( ) ;
449+ let ( file, line, col) = ecx. location_triple_for_span ( span) ;
450+ return Err ( ConstEvalErrKind :: Panic { msg, file, line, col } . into ( ) ) ;
451+ }
452+ ExtraFnVal :: PanicFmt ( instance) => {
453+ // For panic_fmt, call const_panic_fmt instead.
454+ let const_def_id = ecx. tcx . require_lang_item ( LangItem :: ConstPanicFmt , None ) ;
455+ let new_instance = ty:: Instance :: resolve (
456+ * ecx. tcx ,
457+ ty:: ParamEnv :: reveal_all ( ) ,
458+ const_def_id,
459+ instance. args ,
460+ )
461+ . unwrap ( )
462+ . unwrap ( ) ;
463+
464+ ecx. eval_fn_call (
465+ FnVal :: Instance ( new_instance) ,
466+ abis,
467+ args,
468+ true ,
469+ destination,
470+ target,
471+ unwind,
472+ )
473+ }
474+ ExtraFnVal :: AlignOffset ( instance) => {
475+ let args2 = ecx. copy_fn_args ( args) ?;
476+ // For align_offset, we replace the function call if the pointer has no address.
477+ match ecx. align_offset ( instance, & args2, destination, target) ? {
478+ ControlFlow :: Continue ( ( ) ) => ecx. eval_fn_call (
479+ FnVal :: Instance ( instance) ,
480+ abis,
481+ args,
482+ false ,
483+ destination,
484+ target,
485+ unwind,
486+ ) ,
487+ ControlFlow :: Break ( ( ) ) => Ok ( ( ) ) ,
488+ }
489+ }
490+ }
445491 }
446492
447493 fn panic_nounwind ( ecx : & mut InterpCx < ' mir , ' tcx , Self > , msg : & str ) -> InterpResult < ' tcx > {
0 commit comments