@@ -3,6 +3,7 @@ use std::fmt;
3
3
use std:: hash:: Hash ;
4
4
use std:: ops:: ControlFlow ;
5
5
6
+ use either:: Either ;
6
7
use rustc_ast:: Mutability ;
7
8
use rustc_data_structures:: fx:: FxIndexMap ;
8
9
use rustc_data_structures:: fx:: IndexEntry ;
@@ -14,6 +15,7 @@ use rustc_middle::mir::AssertMessage;
14
15
use rustc_middle:: query:: TyCtxtAt ;
15
16
use rustc_middle:: ty;
16
17
use rustc_middle:: ty:: layout:: { FnAbiOf , TyAndLayout } ;
18
+ use rustc_middle:: ty:: Ty ;
17
19
use rustc_session:: lint:: builtin:: WRITES_THROUGH_IMMUTABLE_POINTER ;
18
20
use rustc_span:: symbol:: { sym, Symbol } ;
19
21
use rustc_span:: Span ;
@@ -191,6 +193,16 @@ impl interpret::MayLeak for ! {
191
193
}
192
194
}
193
195
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
+
194
206
impl < ' mir , ' tcx : ' mir > CompileTimeEvalContext < ' mir , ' tcx > {
195
207
fn location_triple_for_span ( & self , span : Span ) -> ( Symbol , u32 , u32 ) {
196
208
let topmost = span. ctxt ( ) . outer_expn ( ) . expansion_cause ( ) . unwrap_or ( span) ;
@@ -212,56 +224,29 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
212
224
213
225
/// "Intercept" a function call, because we have something special to do for it.
214
226
/// 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 > > {
225
233
let def_id = instance. def_id ( ) ;
226
234
227
235
if self . tcx . has_attr ( def_id, sym:: rustc_const_panic_str)
228
236
|| Some ( def_id) == self . tcx . lang_items ( ) . begin_panic_fn ( )
229
237
{
230
- let args = self . copy_fn_args ( args) ?;
231
- // &str or &&str
232
- assert ! ( args. len( ) == 1 ) ;
238
+ return Some ( ExtraFnVal :: BeginPanic ) ;
239
+ }
233
240
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
+ }
238
244
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) ) ;
263
247
}
264
- Ok ( Some ( instance) )
248
+
249
+ None
265
250
}
266
251
267
252
/// `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,
371
356
compile_time_machine ! ( <' mir, ' tcx>) ;
372
357
373
358
type MemoryKind = MemoryKind ;
359
+ type ExtraFnVal = ExtraFnVal < ' tcx > ;
374
360
375
361
const PANIC_ON_ALLOC_FAIL : bool = false ; // will be raised as a proper error
376
362
@@ -399,7 +385,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
399
385
. delayed_bug ( "This is likely a const item that is missing from its impl" ) ;
400
386
throw_inval ! ( AlreadyReported ( guar. into( ) ) ) ;
401
387
} 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,
403
389
// so this should be unreachable.
404
390
let path = ecx. tcx . def_path_str ( def) ;
405
391
bug ! ( "trying to call extern function `{path}` at compile-time" ) ;
@@ -409,22 +395,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
409
395
}
410
396
}
411
397
412
- fn find_mir_or_eval_fn (
398
+ fn find_mir_or_extra_fn (
413
399
ecx : & mut InterpCx < ' mir , ' tcx , Self > ,
414
- orig_instance : ty:: Instance < ' tcx > ,
400
+ instance : ty:: Instance < ' tcx > ,
415
401
_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) ;
422
404
423
405
// 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
+ }
428
409
429
410
// Only check non-glue functions
430
411
if let ty:: InstanceDef :: Item ( def) = instance. def {
@@ -442,10 +423,75 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
442
423
}
443
424
}
444
425
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
+ }
449
495
}
450
496
451
497
fn panic_nounwind ( ecx : & mut InterpCx < ' mir , ' tcx , Self > , msg : & str ) -> InterpResult < ' tcx > {
0 commit comments