From 33e72af843250efcb4179c3068c5c15b8c1bb8c9 Mon Sep 17 00:00:00 2001 From: Martin Molinero Date: Fri, 15 Aug 2025 11:46:31 -0300 Subject: [PATCH] Improve numeric implicit conversion handling --- src/embed_tests/TestMethodBinder.cs | 6 ++-- src/runtime/MethodBinder.cs | 50 ++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/embed_tests/TestMethodBinder.cs b/src/embed_tests/TestMethodBinder.cs index 2e20870f3..979592492 100644 --- a/src/embed_tests/TestMethodBinder.cs +++ b/src/embed_tests/TestMethodBinder.cs @@ -1448,7 +1448,7 @@ public bool SomeMethod() return true; } - public virtual string OnlyClass(TestImplicitConversion data) + public virtual string OnlyClass(TestImplicitConversion data, double anotherArgument = 0) { return "OnlyClass impl"; } @@ -1458,12 +1458,12 @@ public virtual string OnlyString(string data) return "OnlyString impl: " + data; } - public virtual string InvokeModel(string data) + public virtual string InvokeModel(string data, double anotherArgument = 0) { return "string impl: " + data; } - public virtual string InvokeModel(TestImplicitConversion data) + public virtual string InvokeModel(TestImplicitConversion data, double anotherArgument = 0) { return "TestImplicitConversion impl"; } diff --git a/src/runtime/MethodBinder.cs b/src/runtime/MethodBinder.cs index d567ced0c..54fd33ff4 100644 --- a/src/runtime/MethodBinder.cs +++ b/src/runtime/MethodBinder.cs @@ -546,7 +546,7 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe var margs = new object[clrArgCount]; int paramsArrayIndex = paramsArray ? pi.Length - 1 : -1; // -1 indicates no paramsArray - var usedImplicitConversion = false; + int implicitConversions = 0; var kwargsMatched = 0; // Conversion loop for each parameter @@ -658,10 +658,14 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe else if (matches.Count == 0) { // accepts non-decimal numbers in decimal parameters - if (underlyingType == typeof(decimal)) + if (underlyingType == typeof(decimal) || underlyingType == typeof(double)) { clrtype = parameter.ParameterType; - usedImplicitConversion |= typematch = Converter.ToManaged(op, clrtype, out arg, false); + typematch = Converter.ToManaged(op, clrtype, out arg, false); + if (typematch) + { + implicitConversions++; + } } if (!typematch) { @@ -669,7 +673,11 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe var opImplicit = parameter.ParameterType.GetMethod("op_Implicit", new[] { clrtype }); if (opImplicit != null) { - usedImplicitConversion |= typematch = opImplicit.ReturnType == parameter.ParameterType; + typematch = opImplicit.ReturnType == parameter.ParameterType; + if (typematch) + { + implicitConversions++; + } clrtype = parameter.ParameterType; } } @@ -739,13 +747,10 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe } } - var match = new MatchedMethod(kwargsMatched, margs, outs, methodInformation); - if (usedImplicitConversion) + var match = new MatchedMethod(kwargsMatched, margs, outs, methodInformation, implicitConversions); + if (implicitConversions > 0) { - if (matchesUsingImplicitConversion == null) - { - matchesUsingImplicitConversion = new List(); - } + matchesUsingImplicitConversion ??= new List(); matchesUsingImplicitConversion.Add(match); } else @@ -767,11 +772,22 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe // we solve the ambiguity by taking the one with the highest precedence but only // considering the actual arguments passed, ignoring the optional arguments for // which the default values were used - var bestMatch = matchesTouse - .GroupBy(x => x.KwargsMatched) - .OrderByDescending(x => x.Key) - .First() - .MinBy(x => GetMatchedArgumentsPrecedence(x.MethodInformation, pyArgCount, kwArgDict?.Keys)); + MatchedMethod bestMatch; + if (matchesTouse.Count == 1) + { + bestMatch = matchesTouse[0]; + } + else + { + bestMatch = matchesTouse + .GroupBy(x => x.KwargsMatched) + .OrderByDescending(x => x.Key) + .First() + .GroupBy(x => x.ImplicitOperations) + .OrderBy(x => x.Key) + .First() + .MinBy(x => GetMatchedArgumentsPrecedence(x.MethodInformation, pyArgCount, kwArgDict?.Keys)); + } var margs = bestMatch.ManagedArgs; var outs = bestMatch.Outs; @@ -1135,15 +1151,17 @@ private readonly struct MatchedMethod public int KwargsMatched { get; } public object?[] ManagedArgs { get; } public int Outs { get; } + public int ImplicitOperations { get; } public MethodInformation MethodInformation { get; } public MethodBase Method => MethodInformation.MethodBase; - public MatchedMethod(int kwargsMatched, object?[] margs, int outs, MethodInformation methodInformation) + public MatchedMethod(int kwargsMatched, object?[] margs, int outs, MethodInformation methodInformation, int implicitOperations) { KwargsMatched = kwargsMatched; ManagedArgs = margs; Outs = outs; MethodInformation = methodInformation; + ImplicitOperations = implicitOperations; } }