-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generated GetHashCode implementation falls foul of BCL nullability of IEqualityComparer<>.GetHashCode #37913
Comments
I think it's a feature bug. we should be generating |
I have no idea. @333fred, @jcouv or @gafter what's the intent here? I was similarly confused here: https://github.com/dotnet/roslyn/pull/37881/files#diff-48ce97c41d3c9a5bf28ef1718c0ade67R76 Since equality comparers historically have accepted null no matter what...? |
Is IEqualityComparer (or |
That's what I'd expect. Otherwise this means any existing generated code would have to be updated. |
So looking at this further, So this asks the question: what do we do for the users trying to use nullability in .NET framework targeting code? It's not a fully supported experience, but what do we want to do? We can have it add suppressions or surround with |
@jasonmalinowski for this particular issue, wouldn't this be solved if we emit as |
Yes, which at least in some sense is "writing ugly code that you wouldn't write if targeted a newer framework". I'd say that's the path of least pain, but still not fun. 😢 |
The one thing I can think of why we might want to emit |
(that's not a strong argument in my opinion but at least tips scales a bit) |
Note: i don't see anything wrong with That's waht we would have gotten if we just had real nullability on ITypeSymbol. it's also what we would do for something like a nullable custom struct. I think that would be fine here. |
|
Oh I might have misread the scenario. My bad. Boo me. |
Could you reopen, please? struct Foo
{
public string? Bar { get; }
public override int GetHashCode()
{
// CS8604 Possible null reference argument for parameter 'obj' in
// 'int EqualityComparer<string?>.GetHashCode(string? obj)'. ↓
return 1739646154 + EqualityComparer<string?>.Default.GetHashCode(Bar);
}
} .NET Core 3.0 definition: public interface IEqualityComparer<in T>
{
bool Equals([AllowNull] T x, [AllowNull] T y);
int GetHashCode([DisallowNull] T obj);
} Would the best way to handle this be to special-case knowledge of the EqualityComparer class and emit an extra I also filed https://github.com/dotnet/corefx/issues/41405 to ask if |
@jnm2 We would have to special case the behavior here for |
We don't need to do that. Rather. |
This is the extent of the relevant documentation I can see from Sam's link:
I understand this to be how the docs usually say, "you might get such and such an exception. If you do, this is why." |
Ok. So if you might get an exceptoin, but you won't always, then this function should be allowed to take in null. it shoudln't say "do not pass in null here" since that's super common and expected and doesn't have any problem with likelythe most commonly used EqualityComparer out there (i.e. |
@CyrusNajmabadi Derived types are allowed to expand on the set of supported inputs, but not restrict it. If the base implementation says null is allowed, then derived implementations can't implement that method while disallowing null (i.e. allowing null is a widening operation). |
If that's the case, and the most common use case is one that accepts null, then the base type should not restrict null as you end up in just this scenario where the user is told there's a problem, despite there being no problem. |
Again, who is helped by this type being annotated like this? Literally all code out there that specifically uses this exact API for this exact purpose (which is something we've communicated) are now told there's a problem that must be dealt with even though this type is idiomatically the way to easily do equality/gethashcode for values that may be null. |
Note: this is not in reference to |
@CyrusNajmabadi I can reopen https://github.com/dotnet/corefx/issues/41405 if you want to make the case there. I'm certainly not going to complain if this changes. |
Yeah, i would reopen that. It def seems wrong IMO. |
@jnm2 @CyrusNajmabadi So how do we feel about the overall status of this bug? We'll now use System.HashCode directly which removes the problematic code. Do we feel comfortable calling that the 'real' fix here? |
That won't fix it for me. I'm targeting .NET Standard and .NET Framework (where HashCode is not available) and using @sharwell's excellent https://github.com/tunnelvisionlabs/ReferenceAssemblyAnnotator to borrow .NET Core's annotations into the matching APIs in the .NET Standard and .NET Framework reference assemblies. So effectively for this question it is the same as if I was using a .NET Core 3.0 that didn't have HashCode. |
I'm not sure there's much else we can do from the Roslyn side. It appears unlikely that the reference assemblies will change in order to allow users to make these safe calls without warnings. Instead of forcing a smaller number of users who implement |
So the original discussion of just having this consume |
This does bring up an interesting problem with @sharwell's tool though: if we write any code under the assumption that annotated APIs means you're on a newer framework that has certain additional APIs to address other concerns (like I did in my comment), that our solutions won't work. It puts you in a fairly "interesting" framework surface area. It's not bad given it is doing what sometimes needs to be done, it's just a strange case for us doing testing. |
I thought we could still generate a hashcode (without System.HashCode) that would at least not fall afoul of the nullable rules. Shouldn't we still do that as well? |
Is a warning suppression still on the table? |
So for somebody who is using nullable down-level without @sharwell's tool, then |
Fair. Is it possible for suppression to be implemented by authoring an analyzer? |
This was fixed by tunnelvisionlabs/ReferenceAssemblyAnnotator#47. We rewrite |
Version Used: Visual Studio 16.2.1,
<LangVersion>preview</LangVersion>
,<Nullable>enable</Nullable>
Generate equality members for:
Result:
This can be fixed by hand to get rid of the warning by changing
EqualityComparer<string>
toEqualityComparer<string?>
:The text was updated successfully, but these errors were encountered: