csharplang
csharplang copied to clipboard
Collection expression loophole: ability to invoke .Add() method with the parameter type not matching the collection expression element type
Version Used: main branch
Steps to Reproduce:
using System.Collections;
using System.Collections.Generic;
BadCandidateForCollectionExpression xs = [123]; // no .Add(int)
BadCandidateForCollectionExpression ys = ["abc"]; // not int element type
BadCandidateForCollectionExpression zs = [default]; // ????
class BadCandidateForCollectionExpression : IEnumerable<int>
{
public void Add(string s) { }
IEnumerator<int> IEnumerable<int>.GetEnumerator() { yield break; }
IEnumerator IEnumerable.GetEnumerator() { yield break; }
}
Expected Behavior:
Impossible to fill the collection expression, no Add method can accept the value of int type - the element type inferred as a collection expression element type.
Actual Behavior:
If the expression is both convertible to inferred element type and can be accepted by some non-int Add method overload - it compiles successfully.
As the result you have a really weird language context - you can only pass expressions that are implicitly convertible to int, but the invoked method accepts string value.
I guess this is the poorly written check in frontend, I guess instead of checking the element expression to be convertible to int you probably must check the parameter type of invoked .Add() method to have type convertible to int.
@cston @333fred to see if this is by design based on our last meetings
I don't imagine this is an interesting case to support given that it doesn't behave like any normal collection does.
This doesn't seem like it should compile. The type of default should be inferred to be the element type.
I believe this is by-design from current spec. There exists a conversion from default to the iteration type int and default can also be used to invoke .Add. Those two checks are independent (the types don't have to match).
feels like a spec bug :)
Moving to csharplang to close the spec bug.
This should not be supported unless we can find a reasonable use case. Otherwise, It shouldn't be supported for collection expressions.
A behavior I found a bit amusing here is that if you change the parameter into a float, and you try to pass a float to that method it will complain that it will only allow an int, not a float! If you give it an int however, it will silently pass a float instead.
There should probably be a check at compile time. It would simply check if the .Add() method has the same type as the IEnumerable. If not, it can't be used in collections expressions.
There should probably be a check at compile time.
Perhaps a diagnostic could be reported if the element as converted for the Add call is not implicitly convertible to the collection iteration type.
BadCandidateForCollectionExpression zs = [default]; // warning: converting 'default' to 'string' for Add(string)