77
88use rustc_hir:: def_id:: LocalDefId ;
99use rustc_index:: IndexVec ;
10- use rustc_middle:: middle:: deduced_param_attrs:: { DeducedParamAttrs , DeducedReadOnlyParam } ;
10+ use rustc_middle:: middle:: deduced_param_attrs:: { DeducedParamAttrs , DeducedParamFlags } ;
1111use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
1212use rustc_middle:: mir:: * ;
1313use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
1414use rustc_session:: config:: OptLevel ;
1515
1616/// A visitor that determines which arguments have been mutated. We can't use the mutability field
1717/// on LocalDecl for this because it has no meaning post-optimization.
18- struct DeduceReadOnly {
18+ struct DeduceParamAttrs {
1919 /// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl
2020 /// 1). The bit is false if the argument may have been mutated or true if we know it hasn't
2121 /// been up to the point we're at.
22- read_only : IndexVec < usize , DeducedReadOnlyParam > ,
22+ flags : IndexVec < usize , DeducedParamFlags > ,
2323}
2424
25- impl DeduceReadOnly {
26- /// Returns a new DeduceReadOnly instance.
27- fn new ( arg_count : usize ) -> Self {
28- Self { read_only : IndexVec :: from_elem_n ( DeducedReadOnlyParam :: empty ( ) , arg_count) }
25+ impl DeduceParamAttrs {
26+ /// Returns a new DeduceParamAttrs instance.
27+ fn new ( body : & Body < ' tcx > ) -> Self {
28+ let mut this =
29+ Self { flags : IndexVec :: from_elem_n ( DeducedParamFlags :: empty ( ) , body. arg_count + 1 ) } ;
30+ // Mark the return place as mutated.
31+ this. flags [ 0 ] |= DeducedParamFlags :: MUTATED ;
32+ this
2933 }
3034
31- /// Returns whether the given local is a parameter and its index.
35+ /// Returns whether the given local is a return place parameter and its index.
3236 fn as_param ( & self , local : Local ) -> Option < usize > {
33- // Locals and parameters are shifted by `RETURN_PLACE`.
34- let param_index = local. as_usize ( ) . checked_sub ( 1 ) ?;
35- if param_index < self . read_only . len ( ) { Some ( param_index) } else { None }
37+ let i = local. as_usize ( ) ;
38+ if i < self . flags . len ( ) { Some ( i) } else { None }
3639 }
3740}
3841
39- impl < ' tcx > Visitor < ' tcx > for DeduceReadOnly {
42+ impl < ' tcx > Visitor < ' tcx > for DeduceParamAttrs {
4043 fn visit_place ( & mut self , place : & Place < ' tcx > , context : PlaceContext , _location : Location ) {
41- // We're only interested in arguments .
42- let Some ( param_index ) = self . as_param ( place. local ) else { return } ;
44+ // We're only interested in the return place or an argument .
45+ let Some ( i ) = self . as_param ( place. local ) else { return } ;
4346
4447 match context {
45- // Not mutating, so it's fine .
48+ // Not actually using the local .
4649 PlaceContext :: NonUse ( ..) => { }
47- // Dereference is not a mutation .
50+ // Neither mutated nor captured .
4851 _ if place. is_indirect_first_projection ( ) => { }
4952 // This is a `Drop`. It could disappear at monomorphization, so mark it specially.
5053 PlaceContext :: MutatingUse ( MutatingUseContext :: Drop )
5154 // Projection changes the place's type, so `needs_drop(local.ty)` is not
5255 // `needs_drop(place.ty)`.
5356 if place. projection . is_empty ( ) => {
54- self . read_only [ param_index] |= DeducedReadOnlyParam :: IF_NO_DROP ;
57+ self . flags [ i] |= DeducedParamFlags :: IF_NO_DROP ;
58+ }
59+ PlaceContext :: MutatingUse (
60+ MutatingUseContext :: Call
61+ | MutatingUseContext :: Yield
62+ | MutatingUseContext :: Drop
63+ | MutatingUseContext :: Borrow
64+ | MutatingUseContext :: RawBorrow ) => {
65+ self . flags [ i] |= DeducedParamFlags :: MUTATED ;
66+ self . flags [ i] |= DeducedParamFlags :: CAPTURED ;
67+ }
68+ PlaceContext :: MutatingUse (
69+ MutatingUseContext :: Store
70+ | MutatingUseContext :: SetDiscriminant
71+ | MutatingUseContext :: AsmOutput
72+ | MutatingUseContext :: Projection
73+ | MutatingUseContext :: Retag ) => {
74+ self . flags [ i] |= DeducedParamFlags :: MUTATED ;
5575 }
56- // This is a mutation, so mark it as such.
57- PlaceContext :: MutatingUse ( ..)
58- // Whether mutating though a `&raw const` is allowed is still undecided, so we
59- // disable any sketchy `readonly` optimizations for now.
6076 | PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: RawBorrow ) => {
61- self . read_only [ param_index] |= DeducedReadOnlyParam :: MUTATED ;
77+ // Whether mutating though a `&raw const` is allowed is still undecided, so we
78+ // disable any sketchy `readonly` optimizations for now.
79+ self . flags [ i] |= DeducedParamFlags :: MUTATED ;
80+ self . flags [ i] |= DeducedParamFlags :: CAPTURED ;
6281 }
63- // Not mutating if the parameter is `Freeze`.
6482 PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: SharedBorrow ) => {
65- self . read_only [ param_index] |= DeducedReadOnlyParam :: IF_FREEZE ;
83+ // Not mutating if the parameter is `Freeze`.
84+ self . flags [ i] |= DeducedParamFlags :: IF_FREEZE ;
85+ self . flags [ i] |= DeducedParamFlags :: CAPTURED ;
6686 }
6787 // Not mutating, so it's fine.
68- PlaceContext :: NonMutatingUse ( ..) => { }
88+ PlaceContext :: NonMutatingUse (
89+ NonMutatingUseContext :: Inspect
90+ | NonMutatingUseContext :: Copy
91+ | NonMutatingUseContext :: Move
92+ | NonMutatingUseContext :: FakeBorrow
93+ | NonMutatingUseContext :: PlaceMention
94+ | NonMutatingUseContext :: Projection ) => { }
6995 }
7096 }
7197
7298 fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
73- // OK, this is subtle. Suppose that we're trying to deduce whether `x` in `f` is read-only
74- // and we have the following:
75- //
76- // fn f(x: BigStruct) { g(x) }
77- // fn g(mut y: BigStruct) { y.foo = 1 }
78- //
79- // If, at the generated MIR level, `f` turned into something like:
80- //
81- // fn f(_1: BigStruct) -> () {
82- // let mut _0: ();
83- // bb0: {
84- // _0 = g(move _1) -> bb1;
85- // }
86- // ...
87- // }
88- //
89- // then it would be incorrect to mark `x` (i.e. `_1`) as `readonly`, because `g`'s write to
90- // its copy of the indirect parameter would actually be a write directly to the pointer that
91- // `f` passes. Note that function arguments are the only situation in which this problem can
92- // arise: every other use of `move` in MIR doesn't actually write to the value it moves
93- // from.
9499 if let TerminatorKind :: Call { ref args, .. } = terminator. kind {
95100 for arg in args {
96101 if let Operand :: Move ( place) = arg. node
97- // We're only interested in arguments.
98- && let Some ( param_index) = self . as_param ( place. local )
99102 && !place. is_indirect_first_projection ( )
103+ && let Some ( i) = self . as_param ( place. local )
100104 {
101- self . read_only [ param_index] |= DeducedReadOnlyParam :: MUTATED ;
105+ // Move arguments might be passed in-place allowing callee to both mutate the
106+ // local and capture its address.
107+ self . flags [ i] |= DeducedParamFlags :: MUTATED ;
108+ self . flags [ i] |= DeducedParamFlags :: CAPTURED ;
102109 }
103110 }
104111 } ;
@@ -150,10 +157,9 @@ pub(super) fn deduced_param_attrs<'tcx>(
150157 if matches ! ( fn_ty. kind( ) , ty:: FnDef ( ..) )
151158 && fn_ty
152159 . fn_sig ( tcx)
153- . inputs ( )
160+ . inputs_and_output ( )
154161 . skip_binder ( )
155162 . iter ( )
156- . cloned ( )
157163 . all ( type_will_always_be_passed_directly)
158164 {
159165 return & [ ] ;
@@ -166,13 +172,13 @@ pub(super) fn deduced_param_attrs<'tcx>(
166172
167173 // Grab the optimized MIR. Analyze it to determine which arguments have been mutated.
168174 let body: & Body < ' tcx > = tcx. optimized_mir ( def_id) ;
169- let mut deduce_read_only = DeduceReadOnly :: new ( body. arg_count ) ;
170- deduce_read_only . visit_body ( body) ;
171- tracing:: trace!( ?deduce_read_only . read_only ) ;
175+ let mut deduce = DeduceParamAttrs :: new ( body) ;
176+ deduce . visit_body ( body) ;
177+ tracing:: trace!( ?deduce . flags ) ;
172178
173- let mut deduced_param_attrs: & [ _ ] = tcx. arena . alloc_from_iter (
174- deduce_read_only . read_only . into_iter ( ) . map ( |read_only| DeducedParamAttrs { read_only } ) ,
175- ) ;
179+ let mut deduced_param_attrs: & [ _ ] = tcx
180+ . arena
181+ . alloc_from_iter ( deduce . flags . into_iter ( ) . map ( |flags| DeducedParamAttrs { flags } ) ) ;
176182
177183 // Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the
178184 // default set of attributes, so we don't have to store them explicitly. Pop them off to save a
0 commit comments