(f *FlowState, flow *ast.FlowNode)
| 1302 | } |
| 1303 | |
| 1304 | func (c *Checker) getTypeAtFlowLoopLabel(f *FlowState, flow *ast.FlowNode) FlowType { |
| 1305 | if f.refKey.IsZero() { |
| 1306 | f.refKey = c.getFlowReferenceKey(f) |
| 1307 | } |
| 1308 | if f.refKey == nonDottedNameCacheKey { |
| 1309 | // No cache key is generated when binding patterns are in unnarrowable situations |
| 1310 | return FlowType{t: f.declaredType} |
| 1311 | } |
| 1312 | key := FlowLoopKey{flowNode: flow, refKey: f.refKey} |
| 1313 | // If we have previously computed the control flow type for the reference at |
| 1314 | // this flow loop junction, return the cached type. |
| 1315 | if cached := c.flowLoopCache[key]; cached != nil { |
| 1316 | return FlowType{t: cached} |
| 1317 | } |
| 1318 | // If this flow loop junction and reference are already being processed, return |
| 1319 | // the union of the types computed for each branch so far, marked as incomplete. |
| 1320 | // It is possible to see an empty array in cases where loops are nested and the |
| 1321 | // back edge of the outer loop reaches an inner loop that is already being analyzed. |
| 1322 | // In such cases we restart the analysis of the inner loop, which will then see |
| 1323 | // a non-empty in-process array for the outer loop and eventually terminate because |
| 1324 | // the first antecedent of a loop junction is always the non-looping control flow |
| 1325 | // path that leads to the top. |
| 1326 | for _, loopInfo := range c.flowLoopStack { |
| 1327 | if loopInfo.key == key && len(loopInfo.types) != 0 { |
| 1328 | return c.newFlowType(c.getUnionOrEvolvingArrayType(f, loopInfo.types, UnionReductionLiteral), true /*incomplete*/) |
| 1329 | } |
| 1330 | } |
| 1331 | // Add the flow loop junction and reference to the in-process stack and analyze |
| 1332 | // each antecedent code path. |
| 1333 | antecedentTypes := make([]*Type, 0, 4) |
| 1334 | subtypeReduction := false |
| 1335 | var firstAntecedentType FlowType |
| 1336 | for list := flow.Antecedents; list != nil; list = list.Next { |
| 1337 | var flowType FlowType |
| 1338 | if firstAntecedentType.isNil() { |
| 1339 | // The first antecedent of a loop junction is always the non-looping control |
| 1340 | // flow path that leads to the top. |
| 1341 | firstAntecedentType = c.getTypeAtFlowNode(f, list.Flow) |
| 1342 | flowType = firstAntecedentType |
| 1343 | } else { |
| 1344 | // All but the first antecedent are the looping control flow paths that lead |
| 1345 | // back to the loop junction. We track these on the flow loop stack. |
| 1346 | c.flowLoopStack = append(c.flowLoopStack, FlowLoopInfo{key: key, types: antecedentTypes}) |
| 1347 | saveFlowTypeCache := c.flowTypeCache |
| 1348 | c.flowTypeCache = nil |
| 1349 | flowType = c.getTypeAtFlowNode(f, list.Flow) |
| 1350 | c.flowTypeCache = saveFlowTypeCache |
| 1351 | c.flowLoopStack = c.flowLoopStack[:len(c.flowLoopStack)-1] |
| 1352 | // If we see a value appear in the cache it is a sign that control flow analysis |
| 1353 | // was restarted and completed by checkExpressionCached. We can simply pick up |
| 1354 | // the resulting type and bail out. |
| 1355 | if cached := c.flowLoopCache[key]; cached != nil { |
| 1356 | return FlowType{t: cached} |
| 1357 | } |
| 1358 | } |
| 1359 | antecedentTypes = core.AppendIfUnique(antecedentTypes, flowType.t) |
| 1360 | // If an antecedent type is not a subset of the declared type, we need to perform |
| 1361 | // subtype reduction. This happens when a "foreign" type is injected into the control |
no test coverage detected