Fix: PeelWhereChain rebases onto innermost (narrowest) predicate param#480
Merged
Conversation
When aggregate fusion peels a chain of Where calls with mixed parameter types (e.g. Where<Derived>(p).Where<Base>(q).Sum(...) produced by IQueryable<out T> covariance), rebasing every peeled body onto the outer widest parameter threw ArgumentException on any member that only exists on a narrower type. Rebasing onto the innermost parameter is always safe because every derived type contains all base/interface members. Made-with: Cursor
…ate peel The non-fusable Sum/Min/Max/Avg peel path rebuilt the Where call with the predicate unconditionally wrapped in Expression.Quote. That is correct for Queryable.Where (parameter type is Expression<Func<>>) but throws ArgumentException for Enumerable.Where (parameter type is Func<>), which is what g.Where(p) on an IGrouping resolves to. Quote only when the captured innermost Where method expects an Expression-derived parameter. Made-with: Cursor
Seed the AndAlso accumulator from peeled[^1].Body (already typed against the rebase parameter) and iterate only the outer predicates that need rebasing. Drops the per-iteration null check and reduces FastExpression.Lambda allocations from one-per-predicate to one total. Made-with: Cursor
… place source is already a ref Expression; the local copy and final write-back were redundant. The bail-out branches naturally leave source at the unconsumed remainder. Made-with: Cursor
SergeiPavlov
approved these changes
Apr 25, 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.
When aggregate fusion peels a chain of
Wherecalls with mixed parameter types (e.g.Where<Derived>(p).Where<Base>(q).Sum(...)produced byIQueryable<out T>covariance), rebasing every peeled body onto the outer widest parameter threwArgumentExceptionon any member that only exists on a narrower type. Rebasing onto the innermost parameter is always safe because every derived type contains all base/interface members.The non-fusable
Sum/Min/Max/Avgpeel path rebuilt theWherecall with the predicate unconditionally wrapped inExpression.Quote. That is correct forQueryable.Where(parameter type isExpression<Func<>>) but throwsArgumentExceptionforEnumerable.Where(parameter type isFunc<>), which is whatg.Where(p)on anIGroupingresolves to. Quote only when the captured innermostWheremethod expects an Expression-derived parameter.Made-with: Cursor