@@ -112,6 +112,9 @@ public class ExpressionTreeFuncletizer : ExpressionVisitor
112
112
private static readonly bool UseOldBehavior35656 =
113
113
AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue35656" , out var enabled35656 ) && enabled35656 ;
114
114
115
+ private static readonly bool UseOldBehavior35100 =
116
+ AppContext . TryGetSwitch ( "Microsoft.EntityFrameworkCore.Issue35100" , out var enabled35100 ) && enabled35100 ;
117
+
115
118
private static readonly MethodInfo ReadOnlyCollectionIndexerGetter = typeof ( ReadOnlyCollection < Expression > ) . GetProperties ( )
116
119
. Single ( p => p . GetIndexParameters ( ) is { Length : 1 } indexParameters && indexParameters [ 0 ] . ParameterType == typeof ( int ) ) . GetMethod ! ;
117
120
@@ -974,6 +977,51 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall)
974
977
}
975
978
}
976
979
980
+ // .NET 10 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans").
981
+ // Unfortunately, the LINQ interpreter does not support ref structs, so we rewrite e.g. MemoryExtensions.Contains to
982
+ // Enumerable.Contains here. See https://github.com/dotnet/runtime/issues/109757.
983
+ if ( method . DeclaringType == typeof ( MemoryExtensions ) && ! UseOldBehavior35100 )
984
+ {
985
+ switch ( method . Name )
986
+ {
987
+ case nameof ( MemoryExtensions . Contains )
988
+ when methodCall . Arguments is [ var arg0 , var arg1 ] && TryUnwrapSpanImplicitCast ( arg0 , out var unwrappedArg0 ) :
989
+ {
990
+ return Visit (
991
+ Call (
992
+ EnumerableMethods . Contains . MakeGenericMethod ( methodCall . Method . GetGenericArguments ( ) [ 0 ] ) ,
993
+ unwrappedArg0 , arg1 ) ) ;
994
+ }
995
+
996
+ case nameof ( MemoryExtensions . SequenceEqual )
997
+ when methodCall . Arguments is [ var arg0 , var arg1 ]
998
+ && TryUnwrapSpanImplicitCast ( arg0 , out var unwrappedArg0 )
999
+ && TryUnwrapSpanImplicitCast ( arg1 , out var unwrappedArg1 ) :
1000
+ return Visit (
1001
+ Call (
1002
+ EnumerableMethods . SequenceEqual . MakeGenericMethod ( methodCall . Method . GetGenericArguments ( ) [ 0 ] ) ,
1003
+ unwrappedArg0 , unwrappedArg1 ) ) ;
1004
+ }
1005
+
1006
+ static bool TryUnwrapSpanImplicitCast ( Expression expression , [ NotNullWhen ( true ) ] out Expression ? result )
1007
+ {
1008
+ if ( expression is MethodCallExpression
1009
+ {
1010
+ Method : { Name : "op_Implicit" , DeclaringType : { IsGenericType : true } implicitCastDeclaringType } ,
1011
+ Arguments : [ var unwrapped ]
1012
+ }
1013
+ && implicitCastDeclaringType . GetGenericTypeDefinition ( ) is var genericTypeDefinition
1014
+ && ( genericTypeDefinition == typeof ( Span < > ) || genericTypeDefinition == typeof ( ReadOnlySpan < > ) ) )
1015
+ {
1016
+ result = unwrapped ;
1017
+ return true ;
1018
+ }
1019
+
1020
+ result = null ;
1021
+ return false ;
1022
+ }
1023
+ }
1024
+
977
1025
// Regular/arbitrary method handling from here on
978
1026
979
1027
// First, visit the object and all arguments, saving states as well
0 commit comments