@@ -106,21 +106,35 @@ func (pa *paramsAnalyzer) setResults(fp *FuncProps) {
106
106
fp .ParamFlags = pa .values
107
107
}
108
108
109
+ func (pa * paramsAnalyzer ) findParamIdx (n * ir.Name ) int {
110
+ if n == nil {
111
+ panic ("bad" )
112
+ }
113
+ for i := range pa .params {
114
+ if pa .params [i ] == n {
115
+ return i
116
+ }
117
+ }
118
+ return - 1
119
+ }
120
+
121
+ type testfType func (x ir.Node , param * ir.Name , idx int ) (bool , bool )
122
+
109
123
// paramsAnalyzer invokes function 'testf' on the specified expression
110
124
// 'x' for each parameter, and if the result is TRUE, or's 'flag' into
111
125
// the flags for that param.
112
- func (pa * paramsAnalyzer ) checkParams (x ir.Node , flag ParamPropBits , mayflag ParamPropBits , testf func ( x ir. Node , param * ir. Name ) bool ) {
126
+ func (pa * paramsAnalyzer ) checkParams (x ir.Node , flag ParamPropBits , mayflag ParamPropBits , testf testfType ) {
113
127
for idx , p := range pa .params {
114
128
if ! pa .top [idx ] && pa .values [idx ] == ParamNoInfo {
115
129
continue
116
130
}
117
- result := testf (x , p )
131
+ result , may := testf (x , p , idx )
118
132
if debugTrace & debugTraceParams != 0 {
119
133
fmt .Fprintf (os .Stderr , "=-= test expr %v param %s result=%v flag=%s\n " , x , p .Sym ().Name , result , flag .String ())
120
134
}
121
135
if result {
122
136
v := flag
123
- if pa .condLevel != 0 {
137
+ if pa .condLevel != 0 || may {
124
138
v = mayflag
125
139
}
126
140
pa .values [idx ] |= v
@@ -134,8 +148,8 @@ func (pa *paramsAnalyzer) checkParams(x ir.Node, flag ParamPropBits, mayflag Par
134
148
// specific parameter had a constant value.
135
149
func (pa * paramsAnalyzer ) foldCheckParams (x ir.Node ) {
136
150
pa .checkParams (x , ParamFeedsIfOrSwitch , ParamMayFeedIfOrSwitch ,
137
- func (x ir.Node , p * ir.Name ) bool {
138
- return ShouldFoldIfNameConstant (x , []* ir.Name {p })
151
+ func (x ir.Node , p * ir.Name , idx int ) ( bool , bool ) {
152
+ return ShouldFoldIfNameConstant (x , []* ir.Name {p }), false
139
153
})
140
154
}
141
155
@@ -158,9 +172,9 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) {
158
172
}
159
173
pa .checkParams (r , ParamFeedsInterfaceMethodCall ,
160
174
ParamMayFeedInterfaceMethodCall ,
161
- func (x ir.Node , p * ir.Name ) bool {
175
+ func (x ir.Node , p * ir.Name , idx int ) ( bool , bool ) {
162
176
name := x .(* ir.Name )
163
- return name == p
177
+ return name == p , false
164
178
})
165
179
case ir .OCALLFUNC :
166
180
if ce .X .Op () != ir .ONAME {
@@ -171,15 +185,89 @@ func (pa *paramsAnalyzer) callCheckParams(ce *ir.CallExpr) {
171
185
return
172
186
}
173
187
name := called .(* ir.Name )
188
+ if name .Class == ir .PPARAM {
189
+ pa .checkParams (called , ParamFeedsIndirectCall ,
190
+ ParamMayFeedIndirectCall ,
191
+ func (x ir.Node , p * ir.Name , idx int ) (bool , bool ) {
192
+ name := x .(* ir.Name )
193
+ return name == p , false
194
+ })
195
+ } else {
196
+ cname , isFunc , _ := isFuncName (called )
197
+ if isFunc {
198
+ pa .deriveFlagsFromCallee (ce , cname .Func )
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ // deriveFlagsFromCallee tries to derive flags for the current
205
+ // function based on a call this function makes to some other
206
+ // function. Example:
207
+ //
208
+ // /* Simple */ /* Derived from callee */
209
+ // func foo(f func(int)) { func foo(f func(int)) {
210
+ // f(2) bar(32, f)
211
+ // } }
212
+ // func bar(x int, f func()) {
213
+ // f(x)
214
+ // }
215
+ //
216
+ // Here we can set the "param feeds indirect call" flag for
217
+ // foo's param 'f' since we know that bar has that flag set for
218
+ // its second param, and we're passing that param a function.
219
+ func (pa * paramsAnalyzer ) deriveFlagsFromCallee (ce * ir.CallExpr , callee * ir.Func ) {
220
+ calleeProps := propsForFunc (callee )
221
+ if calleeProps == nil {
222
+ return
223
+ }
224
+ if debugTrace & debugTraceParams != 0 {
225
+ fmt .Fprintf (os .Stderr , "=-= callee props for %v:\n %s" ,
226
+ callee .Sym ().Name , calleeProps .String ())
227
+ }
228
+
229
+ must := []ParamPropBits {ParamFeedsInterfaceMethodCall , ParamFeedsIndirectCall , ParamFeedsIfOrSwitch }
230
+ may := []ParamPropBits {ParamMayFeedInterfaceMethodCall , ParamMayFeedIndirectCall , ParamMayFeedIfOrSwitch }
231
+
232
+ for pidx , arg := range ce .Args {
233
+ // Does the callee param have any interesting properties?
234
+ // If not we can skip this one.
235
+ pflag := calleeProps .ParamFlags [pidx ]
236
+ if pflag == 0 {
237
+ continue
238
+ }
239
+ // See if one of the caller's parameters is flowing unmodified
240
+ // into this actual expression.
241
+ r := ir .StaticValue (arg )
242
+ if r .Op () != ir .ONAME {
243
+ return
244
+ }
245
+ name := r .(* ir.Name )
174
246
if name .Class != ir .PPARAM {
175
247
return
176
248
}
177
- pa .checkParams (called , ParamFeedsIndirectCall ,
178
- ParamMayFeedIndirectCall ,
179
- func (x ir.Node , p * ir.Name ) bool {
180
- name := x .(* ir.Name )
181
- return name == p
182
- })
249
+ callerParamIdx := pa .findParamIdx (name )
250
+ if callerParamIdx == - 1 || pa .params [callerParamIdx ] == nil {
251
+ panic ("something went wrong" )
252
+ }
253
+ if ! pa .top [callerParamIdx ] &&
254
+ pa .values [callerParamIdx ] == ParamNoInfo {
255
+ continue
256
+ }
257
+ if debugTrace & debugTraceParams != 0 {
258
+ fmt .Fprintf (os .Stderr , "=-= pflag for arg %d is %s\n " ,
259
+ pidx , pflag .String ())
260
+ }
261
+ for i := range must {
262
+ mayv := may [i ]
263
+ mustv := must [i ]
264
+ if pflag & mustv != 0 && pa .condLevel == 0 {
265
+ pa .values [callerParamIdx ] |= mustv
266
+ } else if pflag & (mustv | mayv ) != 0 {
267
+ pa .values [callerParamIdx ] |= mayv
268
+ }
269
+ }
270
+ pa .top [callerParamIdx ] = false
183
271
}
184
272
}
185
273
0 commit comments