Skip to content
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

Merged
merged 10 commits into from
May 8, 2017
Merged

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Apr 4, 2017

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

Copy link
Member

@VSadov VSadov left a 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))
Copy link
Member

@VSadov VSadov Apr 5, 2017

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

@jcouv
Copy link
Member Author

jcouv commented Apr 5, 2017

@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()
Copy link
Member

@cston cston Apr 5, 2017

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

Copy link
Member Author

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();
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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();
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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.
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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

Copy link
Member Author

@jcouv jcouv Apr 5, 2017

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

Copy link
Member

@gafter gafter Apr 6, 2017

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()
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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()
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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()
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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(
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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))
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.


In reply to: 109989611 [](ancestors = 109989611)

case BoundKind.TupleLiteral:
if ((object)operand.Type == null)
{
Error(diagnostics, ErrorCode.ERR_TypelessTupleInAs, node);
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 5, 2017

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

Copy link
Member

@gafter gafter Apr 6, 2017

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

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Apr 5, 2017

Done with review pass. #Closed

@gafter gafter removed the Feature - Tuples Tuples label Apr 11, 2017
@jcouv
Copy link
Member Author

jcouv commented Apr 11, 2017

@AlekseyTs @cston @VSadov for review. I addressed the feedback. #Resolved

@gafter gafter added the Feature - Tuples Tuples label Apr 11, 2017
@cston
Copy link
Member

cston commented Apr 11, 2017

LGTM

</compilation>, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe)

comp.AssertTheseDiagnostics(<errors>
BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'.
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 12, 2017

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

Copy link
Member Author

@jcouv jcouv May 4, 2017

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #19269 for broader issue.


In reply to: 114696375 [](ancestors = 114696375)

{
static void Main()
{
C? x = (1, null);
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 12, 2017

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)
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 12, 2017

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

@cston
Copy link
Member

cston commented May 3, 2017

        }

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?)
~~~~~~~~~~~~
Copy link
Member Author

@jcouv jcouv May 4, 2017

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

Copy link
Member Author

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)

@jcouv
Copy link
Member Author

jcouv commented May 8, 2017

@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)>
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the link below: #16697


In reply to: 115317743 [](ancestors = 115317743)

Copy link
Member Author

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.

@AlekseyTs
Copy link
Contributor

LGTM

@jcouv
Copy link
Member Author

jcouv commented May 8, 2017

Customer scenario
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

Bugs this fixes:
Fixes #17962

Workarounds, if any
Don't use typeless tuples in "as".

Risk
Performance impact
Low. A check in conversions was previously checking for implicit nullable conversion, but now checks for nullable conversion.

How was the bug found?
Reshetnikov ;-)

@MeiChin-Tsai @jaredpar for ask-mode approval.

@MeiChin-Tsai
Copy link

approved. thx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Visual Studio crashes with NullReferenceException on certain applications of the as operator to tuples
7 participants