@@ -10,8 +10,10 @@ use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item
10
10
use rustc_lexer:: tokenize;
11
11
use rustc_lint:: LateContext ;
12
12
use rustc_middle:: mir:: interpret:: { alloc_range, Scalar } ;
13
+ use rustc_middle:: mir:: ConstValue ;
13
14
use rustc_middle:: ty:: { self , EarlyBinder , FloatTy , GenericArgsRef , IntTy , List , ScalarInt , Ty , TyCtxt , UintTy } ;
14
15
use rustc_middle:: { bug, mir, span_bug} ;
16
+ use rustc_span:: def_id:: DefId ;
15
17
use rustc_span:: symbol:: { Ident , Symbol } ;
16
18
use rustc_span:: SyntaxContext ;
17
19
use rustc_target:: abi:: Size ;
@@ -307,6 +309,12 @@ impl ConstantSource {
307
309
}
308
310
}
309
311
312
+ /// Attempts to check whether the expression is a constant representing an empty slice, str, array,
313
+ /// etc…
314
+ pub fn constant_is_empty ( lcx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> Option < bool > {
315
+ ConstEvalLateContext :: new ( lcx, lcx. typeck_results ( ) ) . expr_is_empty ( e)
316
+ }
317
+
310
318
/// Attempts to evaluate the expression as a constant.
311
319
pub fn constant < ' tcx > (
312
320
lcx : & LateContext < ' tcx > ,
@@ -406,7 +414,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
406
414
match e. kind {
407
415
ExprKind :: ConstBlock ( ConstBlock { body, .. } ) => self . expr ( self . lcx . tcx . hir ( ) . body ( body) . value ) ,
408
416
ExprKind :: DropTemps ( e) => self . expr ( e) ,
409
- ExprKind :: Path ( ref qpath) => self . fetch_path ( qpath, e. hir_id , self . typeck_results . expr_ty ( e) ) ,
417
+ ExprKind :: Path ( ref qpath) => {
418
+ self . fetch_path_and_apply ( qpath, e. hir_id , self . typeck_results . expr_ty ( e) , |this, result| {
419
+ let result = mir_to_const ( this. lcx , result) ?;
420
+ this. source = ConstantSource :: Constant ;
421
+ Some ( result)
422
+ } )
423
+ } ,
410
424
ExprKind :: Block ( block, _) => self . block ( block) ,
411
425
ExprKind :: Lit ( lit) => {
412
426
if is_direct_expn_of ( e. span , "cfg" ) . is_some ( ) {
@@ -472,6 +486,49 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
472
486
}
473
487
}
474
488
489
+ /// Simple constant folding to determine if an expression is an empty slice, str, array, …
490
+ /// `None` will be returned if the constness cannot be determined, or if the resolution
491
+ /// leaves the local crate.
492
+ pub fn expr_is_empty ( & mut self , e : & Expr < ' _ > ) -> Option < bool > {
493
+ match e. kind {
494
+ ExprKind :: ConstBlock ( ConstBlock { body, .. } ) => self . expr_is_empty ( self . lcx . tcx . hir ( ) . body ( body) . value ) ,
495
+ ExprKind :: DropTemps ( e) => self . expr_is_empty ( e) ,
496
+ ExprKind :: Path ( ref qpath) => {
497
+ if !self
498
+ . typeck_results
499
+ . qpath_res ( qpath, e. hir_id )
500
+ . opt_def_id ( )
501
+ . is_some_and ( DefId :: is_local)
502
+ {
503
+ return None ;
504
+ }
505
+ self . fetch_path_and_apply ( qpath, e. hir_id , self . typeck_results . expr_ty ( e) , |this, result| {
506
+ mir_is_empty ( this. lcx , result)
507
+ } )
508
+ } ,
509
+ ExprKind :: Lit ( lit) => {
510
+ if is_direct_expn_of ( e. span , "cfg" ) . is_some ( ) {
511
+ None
512
+ } else {
513
+ match & lit. node {
514
+ LitKind :: Str ( is, _) => Some ( is. is_empty ( ) ) ,
515
+ LitKind :: ByteStr ( s, _) | LitKind :: CStr ( s, _) => Some ( s. is_empty ( ) ) ,
516
+ _ => None ,
517
+ }
518
+ }
519
+ } ,
520
+ ExprKind :: Array ( vec) => self . multi ( vec) . map ( |v| v. is_empty ( ) ) ,
521
+ ExprKind :: Repeat ( ..) => {
522
+ if let ty:: Array ( _, n) = self . typeck_results . expr_ty ( e) . kind ( ) {
523
+ Some ( n. try_eval_target_usize ( self . lcx . tcx , self . lcx . param_env ) ? == 0 )
524
+ } else {
525
+ span_bug ! ( e. span, "typeck error" ) ;
526
+ }
527
+ } ,
528
+ _ => None ,
529
+ }
530
+ }
531
+
475
532
#[ expect( clippy:: cast_possible_wrap) ]
476
533
fn constant_not ( & self , o : & Constant < ' tcx > , ty : Ty < ' _ > ) -> Option < Constant < ' tcx > > {
477
534
use self :: Constant :: { Bool , Int } ;
@@ -519,8 +576,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
519
576
vec. iter ( ) . map ( |elem| self . expr ( elem) ) . collect :: < Option < _ > > ( )
520
577
}
521
578
522
- /// Lookup a possibly constant expression from an `ExprKind::Path`.
523
- fn fetch_path ( & mut self , qpath : & QPath < ' _ > , id : HirId , ty : Ty < ' tcx > ) -> Option < Constant < ' tcx > > {
579
+ /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it.
580
+ fn fetch_path_and_apply < T , F > ( & mut self , qpath : & QPath < ' _ > , id : HirId , ty : Ty < ' tcx > , f : F ) -> Option < T >
581
+ where
582
+ F : FnOnce ( & mut Self , rustc_middle:: mir:: Const < ' tcx > ) -> Option < T > ,
583
+ {
524
584
let res = self . typeck_results . qpath_res ( qpath, id) ;
525
585
match res {
526
586
Res :: Def ( DefKind :: Const | DefKind :: AssocConst , def_id) => {
@@ -553,9 +613,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
553
613
. const_eval_resolve ( self . param_env , mir:: UnevaluatedConst :: new ( def_id, args) , None )
554
614
. ok ( )
555
615
. map ( |val| rustc_middle:: mir:: Const :: from_value ( val, ty) ) ?;
556
- let result = mir_to_const ( self . lcx , result) ?;
557
- self . source = ConstantSource :: Constant ;
558
- Some ( result)
616
+ f ( self , result)
559
617
} ,
560
618
_ => None ,
561
619
}
@@ -746,7 +804,6 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
746
804
}
747
805
748
806
pub fn mir_to_const < ' tcx > ( lcx : & LateContext < ' tcx > , result : mir:: Const < ' tcx > ) -> Option < Constant < ' tcx > > {
749
- use rustc_middle:: mir:: ConstValue ;
750
807
let mir:: Const :: Val ( val, _) = result else {
751
808
// We only work on evaluated consts.
752
809
return None ;
@@ -794,6 +851,42 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
794
851
}
795
852
}
796
853
854
+ fn mir_is_empty < ' tcx > ( lcx : & LateContext < ' tcx > , result : mir:: Const < ' tcx > ) -> Option < bool > {
855
+ let mir:: Const :: Val ( val, _) = result else {
856
+ // We only work on evaluated consts.
857
+ return None ;
858
+ } ;
859
+ match ( val, result. ty ( ) . kind ( ) ) {
860
+ ( _, ty:: Ref ( _, inner_ty, _) ) => match inner_ty. kind ( ) {
861
+ ty:: Str | ty:: Slice ( _) => {
862
+ if let ConstValue :: Indirect { alloc_id, offset } = val {
863
+ // Get the length from the slice, using the same formula as
864
+ // [`ConstValue::try_get_slice_bytes_for_diagnostics`].
865
+ let a = lcx. tcx . global_alloc ( alloc_id) . unwrap_memory ( ) . inner ( ) ;
866
+ let ptr_size = lcx. tcx . data_layout . pointer_size ;
867
+ if a. size ( ) < offset + 2 * ptr_size {
868
+ // (partially) dangling reference
869
+ return None ;
870
+ }
871
+ let len = a
872
+ . read_scalar ( & lcx. tcx , alloc_range ( offset + ptr_size, ptr_size) , false )
873
+ . ok ( ) ?
874
+ . to_target_usize ( & lcx. tcx )
875
+ . ok ( ) ?;
876
+ Some ( len == 0 )
877
+ } else {
878
+ None
879
+ }
880
+ } ,
881
+ ty:: Array ( _, len) => Some ( len. try_to_target_usize ( lcx. tcx ) ? == 0 ) ,
882
+ _ => None ,
883
+ } ,
884
+ ( ConstValue :: Indirect { .. } , ty:: Array ( _, len) ) => Some ( len. try_to_target_usize ( lcx. tcx ) ? == 0 ) ,
885
+ ( ConstValue :: ZeroSized , _) => Some ( true ) ,
886
+ _ => None ,
887
+ }
888
+ }
889
+
797
890
fn field_of_struct < ' tcx > (
798
891
adt_def : ty:: AdtDef < ' tcx > ,
799
892
lcx : & LateContext < ' tcx > ,
0 commit comments