11use rustc_index:: IndexVec ;
2- use rustc_middle:: mir:: interpret :: Scalar ;
2+ use rustc_middle:: mir:: visit :: { MutatingUseContext , NonMutatingUseContext , PlaceContext } ;
33use rustc_middle:: mir:: * ;
44use rustc_middle:: ty:: { Ty , TyCtxt } ;
55use rustc_session:: Session ;
@@ -26,6 +26,7 @@ fn insert_null_check<'tcx>(
2626 tcx : TyCtxt < ' tcx > ,
2727 pointer : Place < ' tcx > ,
2828 pointee_ty : Ty < ' tcx > ,
29+ context : PlaceContext ,
2930 local_decls : & mut IndexVec < Local , LocalDecl < ' tcx > > ,
3031 stmts : & mut Vec < Statement < ' tcx > > ,
3132 source_info : SourceInfo ,
@@ -42,30 +43,51 @@ fn insert_null_check<'tcx>(
4243 let addr = local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
4344 stmts. push ( Statement { source_info, kind : StatementKind :: Assign ( Box :: new ( ( addr, rvalue) ) ) } ) ;
4445
45- // Get size of the pointee (zero-sized reads and writes are allowed).
46- let rvalue = Rvalue :: NullaryOp ( NullOp :: SizeOf , pointee_ty) ;
47- let sizeof_pointee =
48- local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
49- stmts. push ( Statement {
50- source_info,
51- kind : StatementKind :: Assign ( Box :: new ( ( sizeof_pointee, rvalue) ) ) ,
52- } ) ;
53-
54- // Check that the pointee is not a ZST.
5546 let zero = Operand :: Constant ( Box :: new ( ConstOperand {
5647 span : source_info. span ,
5748 user_ty : None ,
58- const_ : Const :: Val ( ConstValue :: Scalar ( Scalar :: from_target_usize ( 0 , & tcx) ) , tcx. types . usize ) ,
49+ const_ : Const :: Val ( ConstValue :: from_target_usize ( 0 , & tcx) , tcx. types . usize ) ,
5950 } ) ) ;
60- let is_pointee_no_zst =
61- local_decls. push ( LocalDecl :: with_source_info ( tcx. types . bool , source_info) ) . into ( ) ;
62- stmts. push ( Statement {
63- source_info,
64- kind : StatementKind :: Assign ( Box :: new ( (
65- is_pointee_no_zst,
66- Rvalue :: BinaryOp ( BinOp :: Ne , Box :: new ( ( Operand :: Copy ( sizeof_pointee) , zero. clone ( ) ) ) ) ,
67- ) ) ) ,
68- } ) ;
51+
52+ let pointee_should_be_checked = match context {
53+ // Borrows pointing to "null" are UB even if the pointee is a ZST.
54+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: SharedBorrow )
55+ | PlaceContext :: MutatingUse ( MutatingUseContext :: Borrow ) => {
56+ // Pointer should be checked unconditionally.
57+ Operand :: Constant ( Box :: new ( ConstOperand {
58+ span : source_info. span ,
59+ user_ty : None ,
60+ const_ : Const :: Val ( ConstValue :: from_bool ( true ) , tcx. types . bool ) ,
61+ } ) )
62+ }
63+ // Other usages of null pointers only are UB if the pointee is not a ZST.
64+ _ => {
65+ let rvalue = Rvalue :: NullaryOp ( NullOp :: SizeOf , pointee_ty) ;
66+ let sizeof_pointee =
67+ local_decls. push ( LocalDecl :: with_source_info ( tcx. types . usize , source_info) ) . into ( ) ;
68+ stmts. push ( Statement {
69+ source_info,
70+ kind : StatementKind :: Assign ( Box :: new ( ( sizeof_pointee, rvalue) ) ) ,
71+ } ) ;
72+
73+ // Check that the pointee is not a ZST.
74+ let is_pointee_not_zst =
75+ local_decls. push ( LocalDecl :: with_source_info ( tcx. types . bool , source_info) ) . into ( ) ;
76+ stmts. push ( Statement {
77+ source_info,
78+ kind : StatementKind :: Assign ( Box :: new ( (
79+ is_pointee_not_zst,
80+ Rvalue :: BinaryOp (
81+ BinOp :: Ne ,
82+ Box :: new ( ( Operand :: Copy ( sizeof_pointee) , zero. clone ( ) ) ) ,
83+ ) ,
84+ ) ) ) ,
85+ } ) ;
86+
87+ // Pointer needs to be checked only if pointee is not a ZST.
88+ Operand :: Copy ( is_pointee_not_zst)
89+ }
90+ } ;
6991
7092 // Check whether the pointer is null.
7193 let is_null = local_decls. push ( LocalDecl :: with_source_info ( tcx. types . bool , source_info) ) . into ( ) ;
@@ -77,7 +99,8 @@ fn insert_null_check<'tcx>(
7799 ) ) ) ,
78100 } ) ;
79101
80- // We want to throw an exception if the pointer is null and doesn't point to a ZST.
102+ // We want to throw an exception if the pointer is null and the pointee is not unconditionally
103+ // allowed (which for all non-borrow place uses, is when the pointee is ZST).
81104 let should_throw_exception =
82105 local_decls. push ( LocalDecl :: with_source_info ( tcx. types . bool , source_info) ) . into ( ) ;
83106 stmts. push ( Statement {
@@ -86,7 +109,7 @@ fn insert_null_check<'tcx>(
86109 should_throw_exception,
87110 Rvalue :: BinaryOp (
88111 BinOp :: BitAnd ,
89- Box :: new ( ( Operand :: Copy ( is_null) , Operand :: Copy ( is_pointee_no_zst ) ) ) ,
112+ Box :: new ( ( Operand :: Copy ( is_null) , pointee_should_be_checked ) ) ,
90113 ) ,
91114 ) ) ) ,
92115 } ) ;
0 commit comments