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