0

I'm trying to understand why implicit conversion in the following code causes StackOverflowException. I think it is a covariance/contravariance issue, but I cannot explain why at the moment.

Crashes NUnit:

private List<T[]> _NodesContent = new List<T[]>();
private UnrolledLinkedListBuilder<T> AddNodes(IEnumerable<T[]> nodes)
{
    _NodesContent.AddRange(nodes);
    return this;
}

public UnrolledLinkedListBuilder<T> AddNodes(params T[][] nodes)
{
    return AddNodes(nodes);
}

Works:

private List<T[]> _NodesContent = new List<T[]>();
private UnrolledLinkedListBuilder<T> AddNodes(IEnumerable<T[]> nodes)
{
    _NodesContent.AddRange(nodes);
    return this;
}

public UnrolledLinkedListBuilder<T> AddNodes(params T[][] nodes)
{
    return AddNodes((IEnumerable<T[])nodes);
}

As I understand from this answer https://stackoverflow.com/a/275107/761755 conversion should be performed implicitly. Moreover, if in the first example you directly call _NodesContent.AddRange(nodes) from AddNodes(T[][]) there will be no exception.

GKalnytskyi
  • 579
  • 7
  • 16
  • 1
    Because when you call `AddNodes` passing `T[][]` - best fitting overload is of course one that accepts `T[][]`, not one that accepts `IEnumerable`. It doesn't matter if it's possible to convert `T[][]` to `IEnumerable` or not. – Evk Feb 15 '18 at 10:50
  • 1
    Why you expect that you don't get a `StackOverflowException`? You are passing the argument directly to the method in which you are. It would be a compiler bug if you would call a different method (overload) then. – Tim Schmelter Feb 15 '18 at 10:50
  • 1
    "As I understand conversion should be performed implicitly." Sure, but a conversion will only happen **if a conversion is necessary.** But a conversion is not necessary. `nodes` is a `T[][]`. There is a method overload that takes a `T[][]` as parameter, so that one will be chosen, resulting in a stack overflow. – Dennis_E Feb 15 '18 at 10:53
  • @Evk Thanks a lot. Now I see my mistake. – GKalnytskyi Feb 15 '18 at 11:03

1 Answers1

1

I'm trying to understand why implicit conversion in the following code causes StackOverflowException.

Well, there is no implicit conversion necessary. But isn't it obvious? Your first method calls itself because you pass the argument as it is:

public UnrolledLinkedListBuilder<T> AddNodes(params T[][] nodes)
{
    return AddNodes(nodes);
}

It was a compiler bug if that would not cause a StackOverflowException in any case:

public SomeReturnType AnyMethod(AnyType x)
{
    return AnyMethod(x); // StackOverflowException
}

It doesn't matter that the parameter is using params because it's not possible to have another method with the same parameter type without params because that was ambiguous. So this will always be the best candidate.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • Thank you. I see my mistake now. – GKalnytskyi Feb 15 '18 at 11:04
  • *It was a compiler bug if that would not cause a `StackOverflowException` in any case* Are you saying that compiler do not allowed to do tail call optimization here? – user4003407 Feb 15 '18 at 11:13
  • @PetSerAl: no, i just meant that it's not possible that a different method-overload is called from within a method when you pass the argument as it is(without casting). Otherwise the question arises why this method was called in the first place. – Tim Schmelter Feb 15 '18 at 11:17