@@ -10,6 +10,7 @@ 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} ;
15
16
use rustc_span:: symbol:: { Ident , Symbol } ;
@@ -303,6 +304,12 @@ impl ConstantSource {
303
304
}
304
305
}
305
306
307
+ /// Attempts to check whether the expression is a constant representing an empty slice, str, array,
308
+ /// etc…
309
+ pub fn constant_is_empty ( lcx : & LateContext < ' _ > , e : & Expr < ' _ > ) -> Option < bool > {
310
+ ConstEvalLateContext :: new ( lcx, lcx. typeck_results ( ) ) . expr_is_empty ( e)
311
+ }
312
+
306
313
/// Attempts to evaluate the expression as a constant.
307
314
pub fn constant < ' tcx > (
308
315
lcx : & LateContext < ' tcx > ,
@@ -402,7 +409,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
402
409
match e. kind {
403
410
ExprKind :: ConstBlock ( ConstBlock { body, .. } ) => self . expr ( self . lcx . tcx . hir ( ) . body ( body) . value ) ,
404
411
ExprKind :: DropTemps ( e) => self . expr ( e) ,
405
- ExprKind :: Path ( ref qpath) => self . fetch_path ( qpath, e. hir_id , self . typeck_results . expr_ty ( e) ) ,
412
+ ExprKind :: Path ( ref qpath) => {
413
+ self . fetch_path_and_apply ( qpath, e. hir_id , self . typeck_results . expr_ty ( e) , |this, result| {
414
+ let result = mir_to_const ( this. lcx , result) ?;
415
+ this. source = ConstantSource :: Constant ;
416
+ Some ( result)
417
+ } )
418
+ } ,
406
419
ExprKind :: Block ( block, _) => self . block ( block) ,
407
420
ExprKind :: Lit ( lit) => {
408
421
if is_direct_expn_of ( e. span , "cfg" ) . is_some ( ) {
@@ -468,6 +481,39 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
468
481
}
469
482
}
470
483
484
+ /// Simple constant folding to determine if an expression is an empty slice, str, array, …
485
+ pub fn expr_is_empty ( & mut self , e : & Expr < ' _ > ) -> Option < bool > {
486
+ match e. kind {
487
+ ExprKind :: ConstBlock ( ConstBlock { body, .. } ) => self . expr_is_empty ( self . lcx . tcx . hir ( ) . body ( body) . value ) ,
488
+ ExprKind :: DropTemps ( e) => self . expr_is_empty ( e) ,
489
+ ExprKind :: Path ( ref qpath) => {
490
+ self . fetch_path_and_apply ( qpath, e. hir_id , self . typeck_results . expr_ty ( e) , |this, result| {
491
+ mir_is_empty ( this. lcx , result)
492
+ } )
493
+ } ,
494
+ ExprKind :: Lit ( lit) => {
495
+ if is_direct_expn_of ( e. span , "cfg" ) . is_some ( ) {
496
+ None
497
+ } else {
498
+ match & lit. node {
499
+ LitKind :: Str ( is, _) => Some ( is. is_empty ( ) ) ,
500
+ LitKind :: ByteStr ( s, _) | LitKind :: CStr ( s, _) => Some ( s. is_empty ( ) ) ,
501
+ _ => None ,
502
+ }
503
+ }
504
+ } ,
505
+ ExprKind :: Array ( vec) => self . multi ( vec) . map ( |v| v. is_empty ( ) ) ,
506
+ ExprKind :: Repeat ( ..) => {
507
+ if let ty:: Array ( _, n) = self . typeck_results . expr_ty ( e) . kind ( ) {
508
+ Some ( n. try_eval_target_usize ( self . lcx . tcx , self . lcx . param_env ) ? == 0 )
509
+ } else {
510
+ span_bug ! ( e. span, "typeck error" ) ;
511
+ }
512
+ } ,
513
+ _ => None ,
514
+ }
515
+ }
516
+
471
517
#[ expect( clippy:: cast_possible_wrap) ]
472
518
fn constant_not ( & self , o : & Constant < ' tcx > , ty : Ty < ' _ > ) -> Option < Constant < ' tcx > > {
473
519
use self :: Constant :: { Bool , Int } ;
@@ -515,8 +561,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
515
561
vec. iter ( ) . map ( |elem| self . expr ( elem) ) . collect :: < Option < _ > > ( )
516
562
}
517
563
518
- /// Lookup a possibly constant expression from an `ExprKind::Path`.
519
- fn fetch_path ( & mut self , qpath : & QPath < ' _ > , id : HirId , ty : Ty < ' tcx > ) -> Option < Constant < ' tcx > > {
564
+ /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it.
565
+ fn fetch_path_and_apply < T , F > ( & mut self , qpath : & QPath < ' _ > , id : HirId , ty : Ty < ' tcx > , f : F ) -> Option < T >
566
+ where
567
+ F : FnOnce ( & mut Self , rustc_middle:: mir:: Const < ' tcx > ) -> Option < T > ,
568
+ {
520
569
let res = self . typeck_results . qpath_res ( qpath, id) ;
521
570
match res {
522
571
Res :: Def ( DefKind :: Const | DefKind :: AssocConst , def_id) => {
@@ -549,9 +598,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
549
598
. const_eval_resolve ( self . param_env , mir:: UnevaluatedConst :: new ( def_id, args) , None )
550
599
. ok ( )
551
600
. map ( |val| rustc_middle:: mir:: Const :: from_value ( val, ty) ) ?;
552
- let result = mir_to_const ( self . lcx , result) ?;
553
- self . source = ConstantSource :: Constant ;
554
- Some ( result)
601
+ f ( self , result)
555
602
} ,
556
603
_ => None ,
557
604
}
@@ -742,7 +789,6 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
742
789
}
743
790
744
791
pub fn mir_to_const < ' tcx > ( lcx : & LateContext < ' tcx > , result : mir:: Const < ' tcx > ) -> Option < Constant < ' tcx > > {
745
- use rustc_middle:: mir:: ConstValue ;
746
792
let mir:: Const :: Val ( val, _) = result else {
747
793
// We only work on evaluated consts.
748
794
return None ;
@@ -788,6 +834,40 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
788
834
}
789
835
}
790
836
837
+ fn mir_is_empty < ' tcx > ( lcx : & LateContext < ' tcx > , result : mir:: Const < ' tcx > ) -> Option < bool > {
838
+ let mir:: Const :: Val ( val, _) = result else {
839
+ // We only work on evaluated consts.
840
+ return None ;
841
+ } ;
842
+ match ( val, result. ty ( ) . kind ( ) ) {
843
+ ( _, ty:: Ref ( _, inner_ty, _) ) => match inner_ty. kind ( ) {
844
+ ty:: Str | ty:: Slice ( _) => {
845
+ if let ConstValue :: Indirect { alloc_id, offset } = val {
846
+ let a = lcx. tcx . global_alloc ( alloc_id) . unwrap_memory ( ) . inner ( ) ;
847
+ let ptr_size = lcx. tcx . data_layout . pointer_size ;
848
+ if a. size ( ) < offset + 2 * ptr_size {
849
+ // (partially) dangling reference
850
+ return None ;
851
+ }
852
+ let len = a
853
+ . read_scalar ( & lcx. tcx , alloc_range ( offset + ptr_size, ptr_size) , false )
854
+ . ok ( ) ?
855
+ . to_target_usize ( & lcx. tcx )
856
+ . ok ( ) ?;
857
+ Some ( len == 0 )
858
+ } else {
859
+ None
860
+ }
861
+ } ,
862
+ ty:: Array ( _, len) => Some ( len. try_to_target_usize ( lcx. tcx ) ? == 0 ) ,
863
+ _ => None ,
864
+ } ,
865
+ ( ConstValue :: Indirect { .. } , ty:: Array ( _, len) ) => Some ( len. try_to_target_usize ( lcx. tcx ) ? == 0 ) ,
866
+ ( ConstValue :: ZeroSized , _) => Some ( true ) ,
867
+ _ => None ,
868
+ }
869
+ }
870
+
791
871
fn field_of_struct < ' tcx > (
792
872
adt_def : ty:: AdtDef < ' tcx > ,
793
873
lcx : & LateContext < ' tcx > ,
0 commit comments