Add avoidable loop allocation triage shapes#2047
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Independent verification — shape is correct and clean; one value-precision refinementBuilt the branch and verified against the corpus. Correct and additive:
One refinement (recommend before/with ship): the detection is precise on the Verdict: solid, correct, favorability-clean shape. Ready once (a) the two |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Suppress pre-sized collection growth rows, skip provable no-op Substring calls, and keep finally handlers warm for cold-path demotion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add strict loop membership for new shapes, fail closed on ambiguous collection constructors, and narrow throw-helper cold demotion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adversarial review reconciliation — 6 findings, all resolved and independently verifiedTwo-family false-positive review: GPT-5.5 and Gemini 3.1 Pro (both non-Claude, since the build agent is Claude-family). Six real precision issues; none dismissed. GPT-5.5 (fixed in
|
Status update: confirming re-review found 2 more issues (1 critical) — NOT ready to mergeThe confirming re-review round (GPT-5.5 + Gemini 3.1 Pro on the fixed branch) verified the original 6 findings are resolved, but each reviewer found one NEW issue introduced by the fixes:
Both are being fixed on this branch with regression tests. I'll re-verify and run a second confirming round before this is ready. Holding merge. |
Replace recursive Tarjan traversal with an explicit frame stack so deep CFGs cannot overflow the process stack. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Do not suppress unsized collection growth rows for provable no-op EnsureCapacity(0) or Capacity = 0 calls. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Confirming-round findings resolved + independently verifiedBoth issues from the first confirming round are fixed and I re-verified each on the built branch:
286 analysis tests pass; CI re-running on the new HEAD. A second confirming re-review round (GPT-5.5 + Gemini, focused on iterative-Tarjan semantic equivalence + the zero-cap fix) is running before this is called ready. Still holding merge. |
Two-clean reached — second confirming round is CLEAN from both reviewersThe second confirming re-review round (on HEAD
Full adversarial trail (8 findings, all resolved + independently verified)
Every actionable finding maps to a resolution commit; none dismissed. This is ready to merge on your nod. |
Summary
Adds avoidable-loop-allocation family v1 to Performance Triage:
unsized-collection-grown: no-capacity growable collection stored to a local and grown in a loop, linked by reaching definitions.string-slice-in-loop: trustedSystem.StringSubstring/Split/Trim*calls inside loop regions.Evidence
dotnet build src/dotnet-inspect -c Release✅dotnet run --project src/ILInspector.Analysis.Tests -c Release✅ (275 passed)Unsized collection canaries
JsonHelpers.TraverseGraphWithTopologicalSort: 4 rows, all medium.StringBuilderrows: low (cold demotion), yielding STJ split 6 low / 5 medium.String slice canaries
DcpLogParser.FormatSystemLog:Split+Trimrows.SqlMapper.GenerateValueTupleDeserializer: 2Substringrows.TypeDetails.PossibleNames:Substringrow.string-slice-in-looprows.Fixtures
unsized-collection-grown: no-capacity+loop fires; capacity ctor does not;EnsureCapacitybefore loop does not;Capacitysetter before loop does not; ternary sized/unsized constructor merge does not; lexical-but-not-natural loop does not; non-loop growth does not; staticAddhelper does not; throw-helper path demotes low; infinite-loop work with conditional throw stays medium.string-slice-in-loop: Substring-in-loop fires; Substring outside loop does not; no-opSubstring(0)/Substring(0, s.Length)in loop does not; lexical-but-not-natural loop does not; Substring-in-loop throw helper demotes low; Substring-in-finallystays medium.Corpus favorability
Re-verified after GPT-5.5 and Gemini precision fixes: existing rows remain byte-identical to the 0.16.0 baseline after removing the two new shapes. The only baseline deltas are the two new shapes. The strict natural-loop gate intentionally suppresses a few previously reported new-shape rows that could not be confirmed as natural-loop sites (fail-closed precision).
string-slice-in-loopnew rowsAspire before/after demo
dnx dotnet-inspect@0.16.0unsized-collection-grownResourceLoggerService.GetLogger(List<ILogger>::Add),DrainAppHostStartupEvents(List<AppHostStartupEvent>::Add)string-slice-in-loopDcpLogParser.FormatSystemLog(Split,Trim),ContainerDirectory.GetFileSystemItemsFromPath(Substring,Split)Adversarial review
Addand instance-callstack receiver hardening issues; fixed infcd5d68f. Latest family review found no significant issues.d9823aa8by suppressing dominating pre-loop sizing (EnsureCapacity/Capacitysetter), suppressing provable no-opSubstring, and keepingfinallyhandlers warm.finallyissue plus lexical loop attribution, branchy constructor-def aliasing, and infinite-loop throw-helper over-demotion; fixed in042cbd1awith a new-shapes-only natural-loop gate, constructor/store domination and all-reaching-def fail-closed behavior, and throw-helper-name-gated cold demotion. Confirming re-review found recursive Tarjan stack-overflow risk; fixed ineb2c572bby making SCC discovery iterative.0e045ccfsoEnsureCapacity(0)/Capacity = 0still report while non-zero/unknown sizing suppresses.Latest local build/tests passed after
0e045ccf; CI/review still pending, so not marking Ready to merge yet.Fixes #2042.