Skip to content

Conversation

@rstam
Copy link
Contributor

@rstam rstam commented Oct 23, 2025

Handle C# 14 now binding some overloads to MemoryExtensions extension methods by rewriting the Expression tree to use the original Enumerable extension methods that C# used to bind to.

@rstam rstam requested a review from a team as a code owner October 23, 2025 17:12
@rstam rstam added feature Adds new user-facing functionality. improvement Optimizations or refactoring (no new features or fixes). labels Oct 23, 2025
@adelinowona adelinowona requested a review from Copilot November 10, 2025 16:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses C# 14 compiler changes that now bind array-based extension method calls (like Contains and SequenceEqual) to MemoryExtensions instead of Enumerable. The driver's LINQ provider cannot translate these MemoryExtensions methods, so the solution rewrites expression trees to use the original Enumerable methods before translation.

Key changes:

  • Introduced LinqExpressionPreprocessor as a centralized preprocessing step that performs CLR compatibility rewrites and partial evaluation
  • Added ClrCompatExpressionRewriter to detect and rewrite MemoryExtensions calls back to Enumerable equivalents
  • Replaced all direct PartialEvaluator.EvaluatePartially calls with LinqExpressionPreprocessor.Preprocess

Reviewed Changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
LinqExpressionPreprocessor.cs New preprocessor that chains CLR compatibility rewrites with partial evaluation
ClrCompatExpressionRewriter.cs New rewriter that converts MemoryExtensions method calls back to Enumerable equivalents
MemoryExtensionsMethod.cs Reflection helpers for identifying MemoryExtensions methods
EnumerableMethod.cs Added references to Enumerable.Contains and SequenceEqual overloads with comparers
TypeExtensions.cs Added IsSpanOf and IsReadOnlySpanOf helper methods
MethodInfoExtensions.cs Added parameter and generic argument checking helpers
LinqProviderAdapter.cs Updated to use new preprocessor instead of direct PartialEvaluator calls
ExpressionToExecutableQueryTranslator.cs Updated to use new preprocessor
ExpressionToSetStageTranslator.cs Updated to use new preprocessor
LookupMethodToPipelineTranslator.cs Updated to use new preprocessor
GroupingWithOutputExpressionStageDefinitions.cs Updated to use new preprocessor
PredicateTranslatorTests.cs Updated test to use new preprocessor
LegacyPredicateTranslatorTests.cs Updated test to use new preprocessor
CSharp5749Tests.cs New test file validating the MemoryExtensions to Enumerable rewriting works correctly
Comments suppressed due to low confidence (1)

src/MongoDB.Driver/Linq/Linq3Implementation/Misc/ClrCompatExpressionRewriter.cs:1

  • Incorrect method reference: should use SequenceEqualWithReadOnlySpanAndReadOnlySpanAndComparer since this branch handles the 3-parameter overload with a comparer, not the 2-parameter overload.
/* Copyright 2010-present MongoDB Inc.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


var result = collection.AsQueryable().Single(Rewrite((C x) => ratings.SequenceEqual(x.Ratings)));

result.Id.Should().Be(3);
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation: this line uses 7 spaces instead of 8 spaces like the surrounding code.

Suggested change
result.Id.Should().Be(3);
result.Id.Should().Be(3);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@rstam rstam requested a review from damieng November 10, 2025 17:50
namespace MongoDB.Driver.Linq.Linq3Implementation.Misc;

/// <summary>
/// This visitor rewrites expressions where new features of .NET CLR or
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please refer to particular features instead of "new", as the term "new" will be outdated pretty soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just removing the word "new" from the comment.

Listing features doesn't feel appropriate for the one sentence summary.

The code itself documents in detail what this class does.

using MongoDB.Driver.TestHelpers;
using Xunit;

namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add unit tests for ClrCompatExpressionRewriter? That will have expressions as an input and output.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be redundant, we already have integration tests that indirectly test this. This is common for LINQ tests, where we rely on the integration tests to also tests functionality lower down.

Otherwise we end up testing things more than once.

];
}

private Expression<Func<T, bool>> Rewrite<T>(Expression<Func<T, bool>> predicate)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I right to think that we have this rewriter only because C# 14 is not released yet and we cannot bump our yet? Should we hold this PR until the release and actually use the features in the tests?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to get this out ahead of switching to c# 14. At some point in tje future we can remove the test rewriter.

namespace MongoDB.Driver.Linq.Linq3Implementation.Misc
{
internal static class MethodInfoExtensions
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of Has can we use TryGet given that this has out parameters that it tries to get? It would follow many other .net methods like TryGetValue on Dictionary.

E g. TryGetSingleGenericArgument

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep the existing names.

The Linq processor has many existing methods that are named IsXyz, HasXyz, ImplementsXyz etc.. many of which also have out parameters.

Naming a method TryXyz puts the emphasis on the operation and whether it succeeds or fails.

In the LINQ processor we aren't thinking in terms of success or failure, we are thinking in terms of asking questions like "is", "has", "implements" etc... the out parameters are less important than the question being asked.

];
}

private Expression<Func<T, bool>> Rewrite<T>(Expression<Func<T, bool>> predicate)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to get this out ahead of switching to c# 14. At some point in tje future we can remove the test rewriter.

@rstam rstam requested review from damieng and sanych-sun November 10, 2025 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Adds new user-facing functionality. improvement Optimizations or refactoring (no new features or fixes).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants