1111
1212use rustc_hir:: def_id:: LocalDefId ;
1313use rustc_index:: IndexVec ;
14- use rustc_middle:: middle:: deduced_param_attrs:: { DeducedParamAttrs , DeducedReadOnlyParam } ;
14+ use rustc_middle:: middle:: deduced_param_attrs:: { DeducedParamAttrs , UsageSummary } ;
1515use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
1616use rustc_middle:: mir:: * ;
1717use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
1818use rustc_session:: config:: OptLevel ;
1919
20- /// A visitor that determines which arguments have been mutated. We can't use the mutability field
21- /// on LocalDecl for this because it has no meaning post-optimization.
22- struct DeduceReadOnly {
23- /// Each bit is indexed by argument number, starting at zero (so 0 corresponds to local decl
24- /// 1). The bit is false if the argument may have been mutated or true if we know it hasn't
25- /// been up to the point we're at.
26- read_only : IndexVec < usize , DeducedReadOnlyParam > ,
20+ /// A visitor that determines how a return place and arguments are used inside MIR body.
21+ /// To determine whether a local is mutated we can't use the mutability field on LocalDecl
22+ /// because it has no meaning post-optimization.
23+ struct DeduceParamAttrs {
24+ /// Summarizes how a return place and arguments are used inside MIR body.
25+ usage : IndexVec < usize , UsageSummary > ,
2726}
2827
29- impl DeduceReadOnly {
30- /// Returns a new DeduceReadOnly instance.
31- fn new ( arg_count : usize ) -> Self {
32- Self { read_only : IndexVec :: from_elem_n ( DeducedReadOnlyParam :: empty ( ) , arg_count) }
28+ impl DeduceParamAttrs {
29+ /// Returns a new DeduceParamAttrs instance.
30+ fn new ( body : & Body < ' _ > ) -> Self {
31+ let mut this =
32+ Self { usage : IndexVec :: from_elem_n ( UsageSummary :: empty ( ) , body. arg_count + 1 ) } ;
33+ // Code generation indicates that a return place is writable. To avoid setting both
34+ // `readonly` and `writable` attributes, when return place is never written to, mark it as
35+ // mutated.
36+ this. usage [ 0 ] |= UsageSummary :: MUTATE ;
37+ this
3338 }
3439
35- /// Returns whether the given local is a parameter and its index.
40+ /// Returns whether a local is the return place or an argument and returns its index.
3641 fn as_param ( & self , local : Local ) -> Option < usize > {
37- // Locals and parameters are shifted by `RETURN_PLACE`.
38- let param_index = local. as_usize ( ) . checked_sub ( 1 ) ?;
39- if param_index < self . read_only . len ( ) { Some ( param_index) } else { None }
42+ let i = local. as_usize ( ) ;
43+ if i < self . usage . len ( ) { Some ( i) } else { None }
4044 }
4145}
4246
43- impl < ' tcx > Visitor < ' tcx > for DeduceReadOnly {
47+ impl < ' tcx > Visitor < ' tcx > for DeduceParamAttrs {
4448 fn visit_place ( & mut self , place : & Place < ' tcx > , context : PlaceContext , _location : Location ) {
45- // We're only interested in arguments .
46- let Some ( param_index ) = self . as_param ( place. local ) else { return } ;
49+ // We're only interested in the return place or an argument .
50+ let Some ( i ) = self . as_param ( place. local ) else { return } ;
4751
4852 match context {
49- // Not mutating, so it's fine .
53+ // Not actually using the local .
5054 PlaceContext :: NonUse ( ..) => { }
51- // Dereference is not a mutation .
55+ // Neither mutated nor captured .
5256 _ if place. is_indirect_first_projection ( ) => { }
5357 // This is a `Drop`. It could disappear at monomorphization, so mark it specially.
5458 PlaceContext :: MutatingUse ( MutatingUseContext :: Drop )
5559 // Projection changes the place's type, so `needs_drop(local.ty)` is not
5660 // `needs_drop(place.ty)`.
5761 if place. projection . is_empty ( ) => {
58- self . read_only [ param_index] |= DeducedReadOnlyParam :: IF_NO_DROP ;
62+ self . usage [ i] |= UsageSummary :: DROP ;
63+ }
64+ PlaceContext :: MutatingUse (
65+ MutatingUseContext :: Call
66+ | MutatingUseContext :: Yield
67+ | MutatingUseContext :: Drop
68+ | MutatingUseContext :: Borrow
69+ | MutatingUseContext :: RawBorrow ) => {
70+ self . usage [ i] |= UsageSummary :: MUTATE ;
71+ self . usage [ i] |= UsageSummary :: CAPTURE ;
72+ }
73+ PlaceContext :: MutatingUse (
74+ MutatingUseContext :: Store
75+ | MutatingUseContext :: SetDiscriminant
76+ | MutatingUseContext :: AsmOutput
77+ | MutatingUseContext :: Projection
78+ | MutatingUseContext :: Retag ) => {
79+ self . usage [ i] |= UsageSummary :: MUTATE ;
5980 }
60- // This is a mutation, so mark it as such.
61- PlaceContext :: MutatingUse ( ..)
62- // Whether mutating though a `&raw const` is allowed is still undecided, so we
63- // disable any sketchy `readonly` optimizations for now.
6481 | PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: RawBorrow ) => {
65- self . read_only [ param_index] |= DeducedReadOnlyParam :: MUTATED ;
82+ // Whether mutating though a `&raw const` is allowed is still undecided, so we
83+ // disable any sketchy `readonly` optimizations for now.
84+ self . usage [ i] |= UsageSummary :: MUTATE ;
85+ self . usage [ i] |= UsageSummary :: CAPTURE ;
6686 }
67- // Not mutating if the parameter is `Freeze`.
6887 PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: SharedBorrow ) => {
69- self . read_only [ param_index] |= DeducedReadOnlyParam :: IF_FREEZE ;
88+ // Not mutating if the parameter is `Freeze`.
89+ self . usage [ i] |= UsageSummary :: SHARED_BORROW ;
90+ self . usage [ i] |= UsageSummary :: CAPTURE ;
7091 }
7192 // Not mutating, so it's fine.
72- PlaceContext :: NonMutatingUse ( ..) => { }
93+ PlaceContext :: NonMutatingUse (
94+ NonMutatingUseContext :: Inspect
95+ | NonMutatingUseContext :: Copy
96+ | NonMutatingUseContext :: Move
97+ | NonMutatingUseContext :: FakeBorrow
98+ | NonMutatingUseContext :: PlaceMention
99+ | NonMutatingUseContext :: Projection ) => { }
73100 }
74101 }
75102
76103 fn visit_terminator ( & mut self , terminator : & Terminator < ' tcx > , location : Location ) {
77- // OK, this is subtle. Suppose that we're trying to deduce whether `x` in `f` is read-only
78- // and we have the following:
79- //
80- // fn f(x: BigStruct) { g(x) }
81- // fn g(mut y: BigStruct) { y.foo = 1 }
82- //
83- // If, at the generated MIR level, `f` turned into something like:
84- //
85- // fn f(_1: BigStruct) -> () {
86- // let mut _0: ();
87- // bb0: {
88- // _0 = g(move _1) -> bb1;
89- // }
90- // ...
91- // }
92- //
93- // then it would be incorrect to mark `x` (i.e. `_1`) as `readonly`, because `g`'s write to
94- // its copy of the indirect parameter would actually be a write directly to the pointer that
95- // `f` passes. Note that function arguments are the only situation in which this problem can
96- // arise: every other use of `move` in MIR doesn't actually write to the value it moves
97- // from.
98104 if let TerminatorKind :: Call { ref args, .. } = terminator. kind {
99105 for arg in args {
100106 if let Operand :: Move ( place) = arg. node
101- // We're only interested in arguments.
102- && let Some ( param_index) = self . as_param ( place. local )
103107 && !place. is_indirect_first_projection ( )
108+ && let Some ( i) = self . as_param ( place. local )
104109 {
105- self . read_only [ param_index] |= DeducedReadOnlyParam :: MUTATED ;
110+ // Moved arguments might be passed in-place, which allows callee to mutate them
111+ // or capture their address.
112+ self . usage [ i] |= UsageSummary :: MUTATE ;
113+ self . usage [ i] |= UsageSummary :: CAPTURE ;
106114 }
107115 }
108116 } ;
@@ -154,10 +162,9 @@ pub(super) fn deduced_param_attrs<'tcx>(
154162 if matches ! ( fn_ty. kind( ) , ty:: FnDef ( ..) )
155163 && fn_ty
156164 . fn_sig ( tcx)
157- . inputs ( )
165+ . inputs_and_output ( )
158166 . skip_binder ( )
159167 . iter ( )
160- . cloned ( )
161168 . all ( type_will_always_be_passed_directly)
162169 {
163170 return & [ ] ;
@@ -170,13 +177,13 @@ pub(super) fn deduced_param_attrs<'tcx>(
170177
171178 // Grab the optimized MIR. Analyze it to determine which arguments have been mutated.
172179 let body: & Body < ' tcx > = tcx. optimized_mir ( def_id) ;
173- let mut deduce_read_only = DeduceReadOnly :: new ( body. arg_count ) ;
174- deduce_read_only . visit_body ( body) ;
175- tracing:: trace!( ?deduce_read_only . read_only ) ;
180+ let mut deduce = DeduceParamAttrs :: new ( body) ;
181+ deduce . visit_body ( body) ;
182+ tracing:: trace!( ?deduce . usage ) ;
176183
177- let mut deduced_param_attrs: & [ _ ] = tcx. arena . alloc_from_iter (
178- deduce_read_only . read_only . into_iter ( ) . map ( |read_only| DeducedParamAttrs { read_only } ) ,
179- ) ;
184+ let mut deduced_param_attrs: & [ _ ] = tcx
185+ . arena
186+ . alloc_from_iter ( deduce . usage . into_iter ( ) . map ( |usage| DeducedParamAttrs { usage } ) ) ;
180187
181188 // Trailing parameters past the size of the `deduced_param_attrs` array are assumed to have the
182189 // default set of attributes, so we don't have to store them explicitly. Pop them off to save a
0 commit comments