Ternary type inference failure #5571
Replies: 13 comments 7 replies
-
The compiler is following the spec here:
There's an implicit conversion from |
Beta Was this translation helpful? Give feedback.
-
Good catch. But if you didn't know that, and you looked at the code itself, you can easily tell that the intent of the code as-written is not what the spec says. .NET takes a lot of pride in the concept of "the pit of success," the idea that the system is designed well enough that code will accidentally end up doing the right thing without the developer having to be a language lawyer and know all the fiddly little details to make it turn out that way. This result violates that principle. Perhaps the spec could benefit from an update? It seems unlikely that doing so would break anything, as any existing code following this pattern is currently almost certainly doing the wrong thing anyway. |
Beta Was this translation helpful? Give feedback.
-
But, to look at it a slightly different way, would you expect your code to differ from: object GetValue(bool shouldBeLong)
{
long value = 3;
var result = shouldBeLong ? value : (int)value;
return result;
} I'd expect them to both do the same thing, but you don't? If you're proposing a spec update, I think step 1 is to figure out what the update should be. |
Beta Was this translation helpful? Give feedback.
-
I don't. The second version has less information available as input to the type inference mechanism, so it's not surprising that it would yield a different result. |
Beta Was this translation helpful? Give feedback.
-
Right, but to come to that conclusion you had to reason about how the type inference mechanism works and what information is available to it, which is hardly the Pit of Success. |
Beta Was this translation helpful? Give feedback.
-
I look at it as reasoning about treating each Statement as a discrete piece of work. Version 1: Statement says "compute ternary and return as function return type, which is If instead of |
Beta Was this translation helpful? Give feedback.
-
Closing out as the compiler is working as per the language spec. If you want the language to change here, please open a discussion at csharplang. Note: a change like what you are asking is for is highly unlikely as it would definitely be a breaking change and it would be certain to affect existing codebases. We know this for certain as we have tried to tweak rules in this space, and every time we have tried we have found it breaks real code. |
Beta Was this translation helpful? Give feedback.
-
It's reasonable for you to want this. However, until the last release, the language didn't work this way at all. Instead, we would find the common type of the ternary and convert burn branches to that. Types only flowed out of the expression, not into it. I'm the last release we made it so that if that process failed (e.g. no common type found), then we can look at surrounding context to inform the type. However, that must only happen in the failure case to prevent code from changing meaning and now breaking. |
Beta Was this translation helpful? Give feedback.
-
Note: that's not at all how i read it. I look at a ternary and as a human I determine what it returns independent of what context it appears in. That's a virtuous part of the language for me. That a ternary with compatible 'arm' types can have its type known independent of where it appears. Your perspective is understandable though. It's just not what the language has ever done. This would, in effect, be relitigating subjective choices in the lang design here made over 20 years ago and which has become baked deeply into the entire ecosystem. Such relitigation is extremely rare and is almost never done when the following is true:
In this case, both of the above are true. Which would make this sort of change one of the worst types of breaks imaginable. -- Note: if we do create a new language, this would likely be something we'd consider up front as to whether we wanted the "inside out" type semantics we have today, or if we'd want "outside in" where an outer type could contextually feed into things (or some hybrid of both). But for C#, it would be nigh impossible to change this. Def feel free to continue the discussion over at csharplang though if you want! |
Beta Was this translation helpful? Give feedback.
-
I find point 2 doubtful. As I noted above, it seems clear to me that any code where this pattern exists is almost certainly not doing what its writer intended. (It definitely was not in the code where I found this! I'm not the one who wrote it, but it's clear from the context that it was meant to return a boxed long in one case and a boxed int in the other, and in an IL code generator, that's a potentially significant mistake.) Therefore, it feels to me that such a change would be more likely to accidentally fix existing code than to accidentally break it. Can you give examples of where real-world code might be intentionally depending on the current semantics? |
Beta Was this translation helpful? Give feedback.
-
This is absolutely incorrect. This has been the logic for c# for 20 years and we have ample experience playing in this space and seeing that if we change this it breaks things immediately. |
Beta Was this translation helpful? Give feedback.
-
Then you must have plenty of real-world examples you could share to support your point... |
Beta Was this translation helpful? Give feedback.
-
Sure, but that's an issue with the naked |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Version Used:
Visual Studio 2022 / SharpLab Main (8 Dec 2021)
Steps to Reproduce:
Expected Behavior:
A human being looking at the code can clearly see that the intent of it is for
GetValue
to return a boxedInt64
whenshouldBeLong
istrue
, and a boxedInt32
when it isfalse
.Actual Behavior:
GetValue
unconditionally returns a boxedInt64
. Visual Studio shows a Message-level diagnostic stating that the cast toint
is redundant.Beta Was this translation helpful? Give feedback.
All reactions