@@ -18,18 +18,18 @@ use crate::errors;
1818use crate :: errors:: ConstEvalError ;
1919use crate :: interpret:: eval_nullary_intrinsic;
2020use crate :: interpret:: {
21- create_static_alloc, intern_const_alloc_recursive, take_static_root_alloc , CtfeValidationMode ,
22- GlobalId , Immediate , InternKind , InterpCx , InterpError , InterpResult , MPlaceTy , MemoryKind ,
23- OpTy , RefTracking , StackPopCleanup ,
21+ create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode , GlobalId , Immediate ,
22+ InternKind , InterpCx , InterpError , InterpResult , MPlaceTy , MemoryKind , OpTy , RefTracking ,
23+ StackPopCleanup ,
2424} ;
2525
2626// Returns a pointer to where the result lives
27- #[ instrument( level = "trace" , skip( ecx, body) , ret ) ]
28- fn eval_body_using_ecx < ' mir , ' tcx > (
27+ #[ instrument( level = "trace" , skip( ecx, body) ) ]
28+ fn eval_body_using_ecx < ' mir , ' tcx , R : InterpretationResult < ' tcx > > (
2929 ecx : & mut CompileTimeEvalContext < ' mir , ' tcx > ,
3030 cid : GlobalId < ' tcx > ,
3131 body : & ' mir mir:: Body < ' tcx > ,
32- ) -> InterpResult < ' tcx , MPlaceTy < ' tcx > > {
32+ ) -> InterpResult < ' tcx , R > {
3333 trace ! ( ?ecx. param_env) ;
3434 let tcx = * ecx. tcx ;
3535 assert ! (
@@ -84,7 +84,10 @@ fn eval_body_using_ecx<'mir, 'tcx>(
8484 // Intern the result
8585 intern_const_alloc_recursive ( ecx, intern_kind, & ret) ?;
8686
87- Ok ( ret)
87+ // Since evaluation had no errors, validate the resulting constant.
88+ const_validate_mplace ( & ecx, & ret, cid) ?;
89+
90+ Ok ( R :: make_result ( ret, ecx) )
8891}
8992
9093/// The `InterpCx` is only meant to be used to do field and index projections into constants for
@@ -282,18 +285,26 @@ pub fn eval_static_initializer_provider<'tcx>(
282285
283286 let instance = ty:: Instance :: mono ( tcx, def_id. to_def_id ( ) ) ;
284287 let cid = rustc_middle:: mir:: interpret:: GlobalId { instance, promoted : None } ;
285- let mut ecx = InterpCx :: new (
286- tcx,
287- tcx. def_span ( def_id) ,
288- ty:: ParamEnv :: reveal_all ( ) ,
289- // Statics (and promoteds inside statics) may access other statics, because unlike consts
290- // they do not have to behave "as if" they were evaluated at runtime.
291- CompileTimeInterpreter :: new ( CanAccessMutGlobal :: Yes , CheckAlignment :: Error ) ,
292- ) ;
293- let alloc_id = eval_in_interpreter ( & mut ecx, cid, true ) ?. alloc_id ;
294- let alloc = take_static_root_alloc ( & mut ecx, alloc_id) ;
295- let alloc = tcx. mk_const_alloc ( alloc) ;
296- Ok ( alloc)
288+ eval_in_interpreter ( tcx, cid, ty:: ParamEnv :: reveal_all ( ) )
289+ }
290+
291+ pub trait InterpretationResult < ' tcx > {
292+ /// This function takes the place where the result of the evaluation is stored
293+ /// and prepares it for returning it in the appropriate format needed by the specific
294+ /// evaluation query.
295+ fn make_result < ' mir > (
296+ mplace : MPlaceTy < ' tcx > ,
297+ ecx : & mut InterpCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > ,
298+ ) -> Self ;
299+ }
300+
301+ impl < ' tcx > InterpretationResult < ' tcx > for ConstAlloc < ' tcx > {
302+ fn make_result < ' mir > (
303+ mplace : MPlaceTy < ' tcx > ,
304+ _ecx : & mut InterpCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > ,
305+ ) -> Self {
306+ ConstAlloc { alloc_id : mplace. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) , ty : mplace. layout . ty }
307+ }
297308}
298309
299310#[ instrument( skip( tcx) , level = "debug" ) ]
@@ -319,92 +330,64 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
319330 trace ! ( "const eval: {:?} ({})" , key, instance) ;
320331 }
321332
322- let cid = key. value ;
333+ eval_in_interpreter ( tcx, key. value , key. param_env )
334+ }
335+
336+ fn eval_in_interpreter < ' tcx , R : InterpretationResult < ' tcx > > (
337+ tcx : TyCtxt < ' tcx > ,
338+ cid : GlobalId < ' tcx > ,
339+ param_env : ty:: ParamEnv < ' tcx > ,
340+ ) -> Result < R , ErrorHandled > {
323341 let def = cid. instance . def . def_id ( ) ;
324342 let is_static = tcx. is_static ( def) ;
325343
326344 let mut ecx = InterpCx :: new (
327345 tcx,
328346 tcx. def_span ( def) ,
329- key . param_env ,
347+ param_env,
330348 // Statics (and promoteds inside statics) may access mutable global memory, because unlike consts
331349 // they do not have to behave "as if" they were evaluated at runtime.
332350 // For consts however we want to ensure they behave "as if" they were evaluated at runtime,
333351 // so we have to reject reading mutable global memory.
334352 CompileTimeInterpreter :: new ( CanAccessMutGlobal :: from ( is_static) , CheckAlignment :: Error ) ,
335353 ) ;
336- eval_in_interpreter ( & mut ecx, cid, is_static)
337- }
338-
339- pub fn eval_in_interpreter < ' mir , ' tcx > (
340- ecx : & mut InterpCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > ,
341- cid : GlobalId < ' tcx > ,
342- is_static : bool ,
343- ) -> :: rustc_middle:: mir:: interpret:: EvalToAllocationRawResult < ' tcx > {
344- // `is_static` just means "in static", it could still be a promoted!
345- debug_assert_eq ! ( is_static, ecx. tcx. static_mutability( cid. instance. def_id( ) ) . is_some( ) ) ;
346-
347354 let res = ecx. load_mir ( cid. instance . def , cid. promoted ) ;
348- match res. and_then ( |body| eval_body_using_ecx ( ecx, cid, body) ) {
349- Err ( error) => {
350- let ( error, backtrace) = error. into_parts ( ) ;
351- backtrace. print_backtrace ( ) ;
352-
353- let ( kind, instance) = if is_static {
354- ( "static" , String :: new ( ) )
355- } else {
356- // If the current item has generics, we'd like to enrich the message with the
357- // instance and its args: to show the actual compile-time values, in addition to
358- // the expression, leading to the const eval error.
359- let instance = & cid. instance ;
360- if !instance. args . is_empty ( ) {
361- let instance = with_no_trimmed_paths ! ( instance. to_string( ) ) ;
362- ( "const_with_path" , instance)
363- } else {
364- ( "const" , String :: new ( ) )
365- }
366- } ;
367-
368- Err ( super :: report (
369- * ecx. tcx ,
370- error,
371- None ,
372- || super :: get_span_and_frames ( ecx. tcx , & ecx. machine ) ,
373- |span, frames| ConstEvalError {
374- span,
375- error_kind : kind,
376- instance,
377- frame_notes : frames,
378- } ,
379- ) )
380- }
381- Ok ( mplace) => {
382- // Since evaluation had no errors, validate the resulting constant.
383-
384- // Temporarily allow access to the static_root_ids for the purpose of validation.
385- let static_root_ids = ecx. machine . static_root_ids . take ( ) ;
386- let res = const_validate_mplace ( & ecx, & mplace, cid) ;
387- ecx. machine . static_root_ids = static_root_ids;
388-
389- let alloc_id = mplace. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) ;
390-
391- // Validation failed, report an error.
392- if let Err ( error) = res {
393- Err ( const_report_error ( & ecx, error, alloc_id) )
355+ res. and_then ( |body| eval_body_using_ecx ( & mut ecx, cid, body) ) . map_err ( |error| {
356+ let ( error, backtrace) = error. into_parts ( ) ;
357+ backtrace. print_backtrace ( ) ;
358+
359+ let ( kind, instance) = if ecx. tcx . is_static ( cid. instance . def_id ( ) ) {
360+ ( "static" , String :: new ( ) )
361+ } else {
362+ // If the current item has generics, we'd like to enrich the message with the
363+ // instance and its args: to show the actual compile-time values, in addition to
364+ // the expression, leading to the const eval error.
365+ let instance = & cid. instance ;
366+ if !instance. args . is_empty ( ) {
367+ let instance = with_no_trimmed_paths ! ( instance. to_string( ) ) ;
368+ ( "const_with_path" , instance)
394369 } else {
395- // Convert to raw constant
396- Ok ( ConstAlloc { alloc_id, ty : mplace. layout . ty } )
370+ ( "const" , String :: new ( ) )
397371 }
398- }
399- }
372+ } ;
373+
374+ super :: report (
375+ * ecx. tcx ,
376+ error,
377+ None ,
378+ || super :: get_span_and_frames ( ecx. tcx , ecx. stack ( ) ) ,
379+ |span, frames| ConstEvalError { span, error_kind : kind, instance, frame_notes : frames } ,
380+ )
381+ } )
400382}
401383
402384#[ inline( always) ]
403- pub fn const_validate_mplace < ' mir , ' tcx > (
385+ fn const_validate_mplace < ' mir , ' tcx > (
404386 ecx : & InterpCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > ,
405387 mplace : & MPlaceTy < ' tcx > ,
406388 cid : GlobalId < ' tcx > ,
407- ) -> InterpResult < ' tcx > {
389+ ) -> Result < ( ) , ErrorHandled > {
390+ let alloc_id = mplace. ptr ( ) . provenance . unwrap ( ) . alloc_id ( ) ;
408391 let mut ref_tracking = RefTracking :: new ( mplace. clone ( ) ) ;
409392 let mut inner = false ;
410393 while let Some ( ( mplace, path) ) = ref_tracking. todo . pop ( ) {
@@ -418,15 +401,18 @@ pub fn const_validate_mplace<'mir, 'tcx>(
418401 CtfeValidationMode :: Const { allow_immutable_unsafe_cell : !inner }
419402 }
420403 } ;
421- ecx. const_validate_operand ( & mplace. into ( ) , path, & mut ref_tracking, mode) ?;
404+ ecx. const_validate_operand ( & mplace. into ( ) , path, & mut ref_tracking, mode)
405+ // Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted
406+ // error about the validation failure.
407+ . map_err ( |error| report_validation_error ( & ecx, error, alloc_id) ) ?;
422408 inner = true ;
423409 }
424410
425411 Ok ( ( ) )
426412}
427413
428414#[ inline( always) ]
429- pub fn const_report_error < ' mir , ' tcx > (
415+ fn report_validation_error < ' mir , ' tcx > (
430416 ecx : & InterpCx < ' mir , ' tcx , CompileTimeInterpreter < ' mir , ' tcx > > ,
431417 error : InterpErrorInfo < ' tcx > ,
432418 alloc_id : AllocId ,
@@ -444,7 +430,7 @@ pub fn const_report_error<'mir, 'tcx>(
444430 * ecx. tcx ,
445431 error,
446432 None ,
447- || crate :: const_eval:: get_span_and_frames ( ecx. tcx , & ecx. machine ) ,
448- move |span, frames| errors:: UndefinedBehavior { span, ub_note, frames, raw_bytes } ,
433+ || crate :: const_eval:: get_span_and_frames ( ecx. tcx , ecx. stack ( ) ) ,
434+ move |span, frames| errors:: ValidationFailure { span, ub_note, frames, raw_bytes } ,
449435 )
450436}
0 commit comments