fix: handle InvocationExpression pattern for MemoryExtensions.Contains (.NET 10)#80
Merged
korchak-aleksandr merged 11 commits intomainfrom Mar 24, 2026
Conversation
…s in .NET 10 In .NET 10, array.Contains(value) in LINQ expression trees may generate two patterns: 1. MethodCallExpression(op_Implicit, [array]) — for simple local captures 2. InvocationExpression(ConstantExpression(pre_compiled_delegate), []) — when array is captured in a nested closure (e.g. passed as a method parameter) The original 10.8.0 fix only handled pattern 1. This fix adds: - TryExtractArrayFromSpanExpression() helper that handles both patterns - For pattern 2: extracts the array from the delegate's closure target via reflection - Broader condition check: mc.Arguments[0].Type is ReadOnlySpan<T> (not just MethodCallExpression) New test ArrayContains_PassedAsParameter_TranslatesToSqlIn reproduces pattern 2.
efimovvo
approved these changes
Mar 24, 2026
efimovvo
approved these changes
Mar 24, 2026
The compiled delegate from Expression.Lambda(...).Compile() may have a two-level closure: outer Closure.Constants contains another compiled delegate whose Closure.Constants contains the actual array. Added FindArrayInClosure() that recursively searches delegate closures (up to depth 3) to handle all nesting patterns produced by the DLR expression compiler. Also updated ArrayContains_InvocationExpressionPattern_TranslatesToSqlIn test to directly construct the InvocationExpression using Expression.Lambda().Compile() so it genuinely exercises Pattern 2 (RED without fix, GREEN with fix).
…s, remove unused test - FindArrayInClosure: iterative BFS instead of recursion — no depth limit needed, works for any nesting produced by DLR; no risk of stack overflow - TryExtractArrayFromSpanExpression: added braces on all return paths - Removed ArrayContains_PassedAsParameter_TranslatesToSqlIn — it did not reproduce Pattern 2 (was identical to ArrayContains_TranslatesToSqlIn)
efimovvo
approved these changes
Mar 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
10.8.0(PR #76) fixedMemoryExtensions.Containsfor Pattern 1, but missed Pattern 2.In .NET 10,
array.Contains(value)in LINQ expression trees generates two patterns for the implicitT[]→ReadOnlySpan<T>conversion:MethodCallExpression(op_Implicit, [array])— simple local capture ✅ fixed in 10.8.0InvocationExpression(ConstantExpression(pre_compiled_delegate), [])— closure compiled by DLR (e.g. afterExpandExpressions()) ❌ crashedThe
VisitInvocationhandler triedDynamicInvokeon the delegate, butReadOnlySpan<T>is a ref struct — causingNotSupportedException.Fix
Added
FindArrayInClosure()— recursive search over the delegate's closure (depth ≤ 3, no heap allocation). Handles:Closurefrom compiled expression lambdas), including nested delegatesTryExtractArrayFromSpanExpression()now delegates toFindArrayInClosure()for Pattern 2.New test
ArrayContains_InvocationExpressionPattern_TranslatesToSqlIn— explicitly constructs Pattern 2 viaExpression.Lambda(...).Compile(), was RED before this fix.Test plan
dotnet build— 0 errors, 0 warningsdotnet test— 63 passed, 17 skipped (net8.0 + net10.0)