Skip to content

Reject oversized lambda signatures before delegate creation - #586

Open
jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/reject-excessive-lambda-arity
Open

jogibear9988 wants to merge 1 commit into
dadhi:masterfrom
jogibear9988:fix/reject-excessive-lambda-arity

Conversation

@jogibear9988

@jogibear9988 jogibear9988 commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

A lambda with 65,535 explicit parameters plus FEC's hidden closure parameter exceeds the runtime method-signature limit, but compilation can currently return an unusable delegate.

Reject the excessive count before allocating its signature array. The check is confined to the existing unpooled large-signature branch: zero-argument and small pooled signatures retain their current paths. No new traversal or context metadata.

Validation

Covers early rejection of excessive signatures, including the strict/throw flags, and supported 255/256/300-argument delegates with correct argument indices, compared with System.

  • The unfixed master regression fails in both APIs; the same fixture passes after the patch on .NET 8, .NET 10 and .NET Framework 4.7.2. Strict FEC compilation is used.
  • Full Release solution build passes with zero warnings/errors.
  • Legacy suites pass: 1,756 on .NET 10 and 1,755 on .NET 8.
  • Framework runner passes 249 TestX + 1,639 legacy cases (en-US culture for existing message assertions).
  • Full modern runners retain only the upstream nullable MultiplyChecked parity failure for (1,1)/(2,1), in both APIs. Unmodified master was rebuilt and checked separately. No assertions were skipped or weakened; legacy suites ran separately because that existing TestX failure gates the normal runner.

Performance checks

Compared with master at f8f2a240abd650dda738162957606aac2659873f using already-working expressions, for both Standard and LightExpression. The broken regression establishes correctness and is not used as a before/after speed baseline. All selected working controls retain identical decoded emitted IL and the same measured compilation/invocation allocation counts.

Measurement setup and full results

Windows ARM64, SDK 10.0.301, .NET 8.0.28/10.0.9, Release. Prebuilt trees; Light conversion outside the timed section; strict CompileFast without System fallback; fixed CPU affinity, tiering disabled, 300 compilation warmups and 100,000 invocation warmups. Two fresh processes with opposite load order, nine alternating samples per side, GC before samples and a 256 MiB no-GC region where available. Table values are geometric means of process medians; compilation uses the longer approximately 100 ms paired-calibration run and invocation uses approximately 24 ms. Framework is correctness-tested, not timed.

Runtime / API / workload Compile ns, master → fix Change Compile B/op, both Invoke ns, master → fix Invoke B/op, both
net10.0 / Standard / add_control 4292.4 → 2293.6 -46.6% 872 1.88 → 1.91 0
net10.0 / Standard / reference_constant 1671.9 → 1752.6 +4.8% 984 1.87 → 1.90 0
net10.0 / Standard / arguments_16 8489.6 → 7002.1 -17.5% 1168 2.19 → 2.14 0
net10.0 / Light / add_control 1505.4 → 1296.7 -13.9% 872 1.90 → 1.86 0
net10.0 / Light / reference_constant 1671.0 → 1861.0 +11.4% 984 1.87 → 1.87 0
net10.0 / Light / arguments_16 3185.7 → 3369.5 +5.8% 1168 2.14 → 2.13 0
net8.0 / Standard / add_control 1115.5 → 1121.2 +0.5% 872 2.03 → 1.94 0
net8.0 / Standard / reference_constant 949.2 → 905.4 -4.6% 984 1.90 → 1.93 0
net8.0 / Standard / arguments_16 2104.7 → 2126.9 +1.1% 1168 2.25 → 2.25 0
net8.0 / Light / add_control 951.5 → 939.6 -1.2% 872 1.91 → 1.88 0
net8.0 / Light / reference_constant 903.1 → 900.7 -0.3% 984 1.87 → 1.94 0
net8.0 / Light / arguments_16 2033.9 → 2003.5 -1.5% 1168 2.21 → 2.18 0

The full short run covers add, reference constant, root/nested constructors, a 16-argument delegate, flat field assignment and nested field reads. The longer run focuses on this fix's affected working paths plus unchanged controls. Slower observations are included above. Identical-binary master/master compilation variation in the same setup was net10.0: -11.6% to +10.4%; net8.0: -21.3% to +14.0%. These timings do not prove zero overhead for every input/machine, and no speedup claim is made. Runtime timing differences above are observations of identical emitted control IL.

All-process compilation timing ranges

These ranges include the less stable observations, not just the longer-run aggregate in the table. They are individual process-median changes across working controls; they are not speedup claims.

Runtime Short-run range Longer-run range Unchanged master/master longer-run range
net10.0 -26.8% to +31.2% -73.3% to +14.5% -13.9% to +10.7%
net8.0 -14.1% to +34.0% -8.1% to +4.5% -29.5% to +25.0%

A lambda with 65,535 explicit parameters plus FEC's hidden closure parameter exceeds the runtime method-signature limit, but compilation can currently return an unusable delegate. Reject the excessive count before allocating its signature array. The check is confined to the existing unpooled large-signature branch: zero-argument and small pooled signatures retain their current paths. No new traversal or context metadata.

Focused Standard/Light regressions pass on .NET 8, .NET 10 and .NET Framework 4.7.2. Full solution and legacy suites pass. Modern full runners retain only the reproduced upstream nullable MultiplyChecked parity failure. Working benchmark controls retain allocation counts and emitted IL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant