@@ -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 ;
@@ -187,6 +189,16 @@ impl interpret::MayLeak for ! {
187
189
}
188
190
}
189
191
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
+
190
202
impl < ' mir , ' tcx : ' mir > CompileTimeEvalContext < ' mir , ' tcx > {
191
203
fn location_triple_for_span ( & self , span : Span ) -> ( Symbol , u32 , u32 ) {
192
204
let topmost = span. ctxt ( ) . outer_expn ( ) . expansion_cause ( ) . unwrap_or ( span) ;
@@ -208,56 +220,29 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
208
220
209
221
/// "Intercept" a function call, because we have something special to do for it.
210
222
/// 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 > > {
221
229
let def_id = instance. def_id ( ) ;
222
230
223
231
if self . tcx . has_attr ( def_id, sym:: rustc_const_panic_str)
224
232
|| Some ( def_id) == self . tcx . lang_items ( ) . begin_panic_fn ( )
225
233
{
226
- let args = self . copy_fn_args ( args) ?;
227
- // &str or &&str
228
- assert ! ( args. len( ) == 1 ) ;
234
+ return Some ( ExtraFnVal :: BeginPanic ) ;
235
+ }
229
236
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
+ }
234
240
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) ) ;
259
243
}
260
- Ok ( Some ( instance) )
244
+
245
+ None
261
246
}
262
247
263
248
/// `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,
367
352
compile_time_machine ! ( <' mir, ' tcx>) ;
368
353
369
354
type MemoryKind = MemoryKind ;
355
+ type ExtraFnVal = ExtraFnVal < ' tcx > ;
370
356
371
357
const PANIC_ON_ALLOC_FAIL : bool = false ; // will be raised as a proper error
372
358
@@ -395,7 +381,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
395
381
. delayed_bug ( "This is likely a const item that is missing from its impl" ) ;
396
382
throw_inval ! ( AlreadyReported ( guar. into( ) ) ) ;
397
383
} 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,
399
385
// so this should be unreachable.
400
386
let path = ecx. tcx . def_path_str ( def) ;
401
387
bug ! ( "trying to call extern function `{path}` at compile-time" ) ;
@@ -405,22 +391,17 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
405
391
}
406
392
}
407
393
408
- fn find_mir_or_eval_fn (
394
+ fn find_mir_or_extra_fn (
409
395
ecx : & mut InterpCx < ' mir , ' tcx , Self > ,
410
- orig_instance : ty:: Instance < ' tcx > ,
396
+ instance : ty:: Instance < ' tcx > ,
411
397
_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) ;
418
400
419
401
// 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
+ }
424
405
425
406
// Only check non-glue functions
426
407
if let ty:: InstanceDef :: Item ( def) = instance. def {
@@ -438,10 +419,75 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
438
419
}
439
420
}
440
421
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
+ }
445
491
}
446
492
447
493
fn panic_nounwind ( ecx : & mut InterpCx < ' mir , ' tcx , Self > , msg : & str ) -> InterpResult < ' tcx > {
0 commit comments