Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fabb22c

Browse files
committed
improved support for generic method overloading
Prior to this change if method had multiple generic overloads, only 1 of them could be matched (whichever one reflection would return first) Now MethodBinder.MatchParameters returns all matching generic overloads, not just the first one. fixes #1522
1 parent c4238d9 commit fabb22c

File tree

5 files changed

+47
-22
lines changed

5 files changed

+47
-22
lines changed

src/runtime/methodbinder.cs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,17 @@ internal void AddMethod(MethodBase m)
8686

8787
/// <summary>
8888
/// Given a sequence of MethodInfo and a sequence of type parameters,
89-
/// return the MethodInfo that represents the matching closed generic.
89+
/// return the MethodInfo(s) that represents the matching closed generic.
9090
/// If unsuccessful, returns null and may set a Python error.
9191
/// </summary>
92-
internal static MethodInfo? MatchParameters(MethodBase[] mi, Type[]? tp)
92+
internal static MethodInfo[] MatchParameters(MethodBase[] mi, Type[]? tp)
9393
{
9494
if (tp == null)
9595
{
96-
return null;
96+
return Array.Empty<MethodInfo>();
9797
}
9898
int count = tp.Length;
99+
var result = new List<MethodInfo>();
99100
foreach (MethodInfo t in mi)
100101
{
101102
if (!t.IsGenericMethodDefinition)
@@ -111,16 +112,14 @@ internal void AddMethod(MethodBase m)
111112
{
112113
// MakeGenericMethod can throw ArgumentException if the type parameters do not obey the constraints.
113114
MethodInfo method = t.MakeGenericMethod(tp);
114-
Exceptions.Clear();
115-
return method;
115+
result.Add(method);
116116
}
117-
catch (ArgumentException e)
117+
catch (ArgumentException)
118118
{
119-
Exceptions.SetError(e);
120119
// The error will remain set until cleared by a successful match.
121120
}
122121
}
123-
return null;
122+
return result.ToArray();
124123
}
125124

126125

@@ -381,9 +380,6 @@ public MismatchedMethod(Exception exception, MethodBase mb)
381380
}
382381
}
383382

384-
var pynargs = (int)Runtime.PyTuple_Size(args);
385-
var isGeneric = false;
386-
387383
MethodBase[] _methods;
388384
if (info != null)
389385
{
@@ -395,11 +391,19 @@ public MismatchedMethod(Exception exception, MethodBase mb)
395391
_methods = GetMethods();
396392
}
397393

398-
var argMatchedMethods = new List<MatchedMethod>(_methods.Length);
394+
return Bind(inst, args, kwargDict, _methods, matchGenerics: true);
395+
}
396+
397+
static Binding? Bind(BorrowedReference inst, BorrowedReference args, Dictionary<string, PyObject> kwargDict, MethodBase[] methods, bool matchGenerics)
398+
{
399+
var pynargs = (int)Runtime.PyTuple_Size(args);
400+
var isGeneric = false;
401+
402+
var argMatchedMethods = new List<MatchedMethod>(methods.Length);
399403
var mismatchedMethods = new List<MismatchedMethod>();
400404

401405
// TODO: Clean up
402-
foreach (MethodBase mi in _methods)
406+
foreach (MethodBase mi in methods)
403407
{
404408
if (mi.IsGenericMethod)
405409
{
@@ -535,17 +539,17 @@ public MismatchedMethod(Exception exception, MethodBase mb)
535539

536540
return new Binding(mi, target, margs, outs);
537541
}
538-
else if (isGeneric && info == null && methodinfo != null)
542+
else if (matchGenerics && isGeneric)
539543
{
540544
// We weren't able to find a matching method but at least one
541545
// is a generic method and info is null. That happens when a generic
542546
// method was not called using the [] syntax. Let's introspect the
543547
// type of the arguments and use it to construct the correct method.
544548
Type[]? types = Runtime.PythonArgsToTypeArray(args, true);
545-
MethodInfo? mi = MatchParameters(methodinfo, types);
546-
if (mi != null)
549+
MethodInfo[] overloads = MatchParameters(methods, types);
550+
if (overloads.Length != 0)
547551
{
548-
return Bind(inst, args, kw, mi, null);
552+
return Bind(inst, args, kwargDict, overloads, matchGenerics: false);
549553
}
550554
}
551555
if (mismatchedMethods.Count > 0)

src/runtime/methodbinding.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,18 @@ public static NewReference mp_subscript(BorrowedReference tp, BorrowedReference
4343
return Exceptions.RaiseTypeError("type(s) expected");
4444
}
4545

46-
MethodBase? mi = self.m.IsInstanceConstructor
47-
? self.m.type.Value.GetConstructor(types)
46+
MethodBase[] overloads = self.m.IsInstanceConstructor
47+
? self.m.type.Value.GetConstructor(types) is { } ctor
48+
? new[] { ctor }
49+
: Array.Empty<MethodBase>()
4850
: MethodBinder.MatchParameters(self.m.info, types);
49-
if (mi == null)
51+
if (overloads.Length == 0)
5052
{
5153
return Exceptions.RaiseTypeError("No match found for given type params");
5254
}
5355

54-
var mb = new MethodBinding(self.m, self.target, self.targetType) { info = mi };
56+
MethodObject overloaded = self.m.WithOverloads(overloads);
57+
var mb = new MethodBinding(overloaded, self.target, self.targetType);
5558
return mb.Alloc();
5659
}
5760

src/runtime/methodobject.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ internal class MethodObject : ExtensionType
2727
internal PyString? doc;
2828
internal MaybeType type;
2929

30-
public MethodObject(Type type, string name, MethodBase[] info, bool allow_threads = MethodBinder.DefaultAllowThreads)
30+
public MethodObject(MaybeType type, string name, MethodBase[] info, bool allow_threads = MethodBinder.DefaultAllowThreads)
3131
{
3232
this.type = type;
3333
this.name = name;
@@ -47,6 +47,9 @@ public MethodObject(Type type, string name, MethodBase[] info, bool allow_thread
4747

4848
public bool IsInstanceConstructor => name == "__init__";
4949

50+
public MethodObject WithOverloads(MethodBase[] overloads)
51+
=> new(type, name, overloads, allow_threads: binder.allow_threads);
52+
5053
internal MethodBase[] info
5154
{
5255
get

src/testing/methodtest.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,9 @@ public static int Overloaded(int i, string s)
646646
return i;
647647
}
648648

649+
public virtual void OverloadedConstrainedGeneric<T>(T generic) where T : MethodTest { }
650+
public virtual void OverloadedConstrainedGeneric<T>(T generic, string str) where T: MethodTest { }
651+
649652
public static string CaseSensitive()
650653
{
651654
return "CaseSensitive";

tests/test_generic.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,18 @@ def test_missing_generic_type():
762762
with pytest.raises(TypeError):
763763
IList[bool]
764764

765+
# https://github.com/pythonnet/pythonnet/issues/1522
766+
def test_overload_generic_parameter():
767+
from Python.Test import MethodTest, MethodTestSub
768+
769+
inst = MethodTest()
770+
generic = MethodTestSub()
771+
inst.OverloadedConstrainedGeneric(generic)
772+
inst.OverloadedConstrainedGeneric[MethodTestSub](generic)
773+
774+
inst.OverloadedConstrainedGeneric[MethodTestSub](generic, '42')
775+
inst.OverloadedConstrainedGeneric[MethodTestSub](generic, System.String('42'))
776+
765777
def test_invalid_generic_type_parameter():
766778
from Python.Test import GenericTypeWithConstraint
767779
with pytest.raises(TypeError):

0 commit comments

Comments
 (0)