-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Fix crash with typeless tuple in "as" operator #18416
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -93,7 +93,7 @@ internal partial class Binder | |||
} | |||
|
|||
if (conversion.IsTupleLiteralConversion || | |||
(conversion.Kind == ConversionKind.ImplicitNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) | |||
(conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, just missed the check for explicit cast.. #Resolved
@VSadov Some tests were broken by this change. I updated them. I'll stop by to discuss whether they are correct. #Resolved |
]]></file> | ||
</compilation>, additionalRefs:=s_valueTupleRefs) | ||
|
||
comp.AssertNoDiagnostics() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following results in an assert failure in TypeSymbolExtensions.IsSameType
:
Class C(Of T, U)
End Class
Class C
Shared Sub M(Of T)()
Dim x = TryCast((0, Nothing), C(Of Integer, T))
End Sub
End Class
``` #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cston I didn't manage to hit this assertion. Just getting an error:
BC30311: Value of type '(Integer, Object)' cannot be converted to 'C(Of Integer, T)'.
Dim x = TryCast((0, Nothing), C(Of Integer, T))
~~~~~~~~~~~~
I'll try a few things to see if I can hit this again.
In reply to: 109945098 [](ancestors = 109945098)
"; | ||
|
||
var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); | ||
comp.VerifyEmitDiagnostics(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comp.VerifyEmitDiagnostics(); [](start = 12, length = 29)
Please run the executable and verify the behavior. #Closed
"; | ||
|
||
var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); | ||
comp.VerifyEmitDiagnostics(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comp.VerifyEmitDiagnostics(); [](start = 11, length = 30)
Please run the executable and verify the behavior. #Closed
|
||
var comp = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); | ||
comp.VerifyDiagnostics( | ||
// (6,17): error CS8304: The first operand of an 'as' operator may not be a tuple literal without a natural type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// (6,17): error CS8304: The first operand of an 'as' operator may not be a tuple literal without a natural type. [](start = 16, length = 113)
I am not sure why we are not using target typing in this scenario. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't do target-typing for lambdas in this situation either.
I'll forward you the thread with compat council (basically, it is ok to block because doesn't seem useful, could be enabled later). #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure why we are not using target typing in this scenario.
The set of conversions permitted are those that don't change the representation of the runtime type of the object on the left-hand-side. See https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#the-as-operator . We do target type, but only the null
value. Permitting target-typing of typeless tuples would (like e.g. the target typing of lambdas, method groups, numeric literals, formattable string literals, etc) contradict that intended design.
#Resolved
]]></file> | ||
</compilation>, additionalRefs:=s_valueTupleRefs) | ||
|
||
comp.AssertNoDiagnostics() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comp.AssertNoDiagnostics() [](start = 12, length = 26)
Please run the executable and verify the behavior. Also, please test SematicModel as in C# tests. #Closed
]]></file> | ||
</compilation>, additionalRefs:=s_valueTupleRefs) | ||
|
||
comp.AssertNoDiagnostics() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comp.AssertNoDiagnostics() [](start = 12, length = 26)
Please run the executable and verify the behavior. Also, please test SematicModel as in C# tests. #Closed
]]></file> | ||
</compilation>, additionalRefs:=s_valueTupleRefs) | ||
|
||
comp.AssertNoDiagnostics() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comp.AssertNoDiagnostics() [](start = 12, length = 26)
Please run the executable and verify the behavior. Also, please test SematicModel as in C# tests. #Closed
@@ -7459,6 +7459,62 @@ BC30512: Option Strict On disallows implicit conversions from 'Double' to 'Strin | |||
End Sub | |||
|
|||
<Fact> | |||
Public Sub TupleCTypeNullableConversionWithTypelessTuple() | |||
Dim comp = CreateCompilationWithMscorlibAndVBRuntime( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CreateCompilationWithMscorlibAndVBRuntime [](start = 23, length = 41)
It looks like we are missing a test for TryCast conversion. #Closed
@@ -93,7 +93,7 @@ internal partial class Binder | |||
} | |||
|
|||
if (conversion.IsTupleLiteralConversion || | |||
(conversion.Kind == ConversionKind.ImplicitNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) | |||
(conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) [](start = 16, length = 88)
Do we have tests for nullable conversions that involve user-defined conversions? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
case BoundKind.TupleLiteral: | ||
if ((object)operand.Type == null) | ||
{ | ||
Error(diagnostics, ErrorCode.ERR_TypelessTupleInAs, node); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error(diagnostics, ErrorCode.ERR_TypelessTupleInAs, node); [](start = 24, length = 58)
I think we should simply target type the tuple as we do for other conversions. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not target type the left operand of as
, though I believe there is a special case for null
. #Resolved
Done with review pass. #Closed |
@AlekseyTs @cston @VSadov for review. I addressed the feedback. #Resolved |
LGTM |
</compilation>, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) | ||
|
||
comp.AssertTheseDiagnostics(<errors> | ||
BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This error feels unexpected. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Investigated this. The tuple has type (Integer, Object)
and the conversion method has input type (Integer, String)
. So the input would have to go through a narrowing tuple conversion, which gets rejected by DetermineMostSpecificWideningConversion
because it is not widening.
On the other side, DetermineMostSpecificNarrowingConversion
rejects this method because the out conversion would be widening (widening nullable). See comment below.
From the comment, I think the logic needs to account for tuples in some new way. I'm not sure how. I'll stop by tomorrow to discuss.
' Note that {Narrowing in, Widening (non-identity) out} operator is not considered as an applicable candidate.
' In fact, an operator like this cannot exist unless nullable in/out conversions are involved.
' Basically we would be dealing with an operator that converts from type derived from source to type derived from destination,
' it would have to be defined in one of those types. When we collect operators we only visit source, destination and their
' bases. So, in order for such an operator to be found, there must be an inheritance relationship between source and
' destination and the operator must be defined in a type that is in between of them in the inheritance hierarchy. Thus,
' there would be an inheritance relationship between parameter type ant return type of the operator, which makes the operator
' inapplicable - there would be a predefined conversion between the types.
' Ignoring an operator like this even when nullable conversions are involved, allows us to consider it as a candidate for a
' lifting, otherwise it would be treated as a not-lifted narrowing.
``` #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | ||
static void Main() | ||
{ | ||
C? x = (1, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C? x = (1, null); [](start = 8, length = 17)
Consider testing explicit casts, including as
cast. #Resolved
Structure C | ||
Shared Sub Main() | ||
Dim x As C = (1, Nothing) | ||
Dim y As C? = (2, Nothing) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dim y As C? = (2, Nothing) [](start = 8, length = 26)
Consider also testing all three flavors of explicit casts. #Resolved
Can we remove this case? It looks like this is handled at line 3004. #Resolved Refers to: src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs:3078 in bd51b3c. [](commit_id = bd51b3c5a66c68417b72eb86000d1325c40d2a88, deletion_comment = False) |
comp.AssertTheseDiagnostics(<errors> | ||
BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. | ||
Dim y = CType((2, Nothing), C?) | ||
~~~~~~~~~~~~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems strange. I'll investigate as well. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This behavior is same as discussed above (test ImplicitConversionOnTypelessTupleWithUserConversion
).
In reply to: 114701536 [](ancestors = 114701536)
@AlekseyTs Could you take another look? Thanks |
@@ -4401,5 +4401,28 @@ End Class | |||
Await TestInRegularAndScriptAsync(code, expected, ignoreTrivia:=False) | |||
End Function | |||
|
|||
<Fact(Skip:="InvalidCastException"), Trait(Traits.Feature, Traits.Features.CodeActionsInlineTemporary)> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InvalidCastException [](start = 21, length = 20)
This should probably be a link to an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As precaution, I also added a note in #16697 to find all tests to un-skip, including this one.
LGTM |
Customer scenario Bugs this fixes: Workarounds, if any Risk How was the bug found? @MeiChin-Tsai @jaredpar for ask-mode approval. |
approved. thx. |
There are two crashes with typeless tuples in nullable conversions:
var x = (1, null) as (int, string)? // this will now produce an error
(int, string)? y = (1, null); // this is fixed to work
Fixes #17962
@VSadov @dotnet/roslyn-compiler for review