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 in is operator used with tuples #19413

Merged
merged 10 commits into from
May 19, 2017
Merged

Conversation

gafter
Copy link
Member

@gafter gafter commented May 10, 2017

Customer scenario

Certain patterns of erroneous pattern-matching were not previously diagnosed. Specifically, when you match a nullable of one integral type with a declaration pattern of another integral type, there was no error. We introduce this error. This is a breaking change in a scenario related to the recently-released pattern-matching feature, so we would like to take the break as soon as possible so that customers do not rely on being able to write the erroneous code. We do not believe the pattern of code is very likely to arise in practice.

This PR also fixes a crash in the existing is operator when used with tuple types.

Bugs this fixes:

Workarounds, if any

N/A

Risk

The purpose of the change is to reduce future risk. Also, because we now combine what were previously three separate implementations, we have one third fewer places there could be bugs.

Performance impact

None expected, as there are only small changes to the code paths taken during compilation.

Is this a regression from a previous update?

No.

Root cause analysis:

For the crash, we did not test scenarios involving mixing tuples with the existing is operator.

How was the bug found?

I found the crash reviewing the existing code while preparing the fix for the other two issues.

* Fixes crash in `is` operator used with tuples
Fixes dotnet#19310

* Unify subsumption implementations
Fixes dotnet#19273

* Matching an expression of type `byte?` with a pattern of type `int` should not be allowed
Fixes dotnet#19151

* Add documentation for the breaking change matching `int?` against the pattern `long x`
@gafter gafter added 4 - In Review A fix for the issue is submitted for review. Area-Compilers Bug Feature - Pattern Matching Pattern Matching Feature - Tuples Tuples labels May 10, 2017
@gafter gafter added this to the 15.3 milestone May 10, 2017
@gafter gafter self-assigned this May 10, 2017
@gafter
Copy link
Member Author

gafter commented May 11, 2017

Also fixes #16129.

@gafter
Copy link
Member Author

gafter commented May 11, 2017

That ubuntu_14_debug_prtest failure is a big WTF.

21:41:56 
   Microsoft.CodeAnalysis.CSharp.UnitTests.Declarations.SourcePlusMetadataTests.InterfaceName [FAIL]
21:41:56       Assert.Equal() Failure
21:41:56       Expected: System.IComparable
21:41:56       Actual:   System.IComparable
21:41:56       Stack Trace:
21:41:56             at Microsoft.CodeAnalysis.CSharp.UnitTests.Declarations.SourcePlusMetadataTests.InterfaceName()

It passed on ubuntu_16.

@gafter
Copy link
Member Author

gafter commented May 11, 2017

@dotnet-bot please test ubuntu_14_debug_prtest

@gafter gafter changed the title Refactor subsumption implementation Fix crash in is operator used with tuples May 11, 2017
@@ -14,3 +14,8 @@ For instance, `var t = (a, b.c, this.d);` will produce a tuple with element name
Consider the case where the type of `a` is `System.Func<bool>` and you write `var local = t.a();`. This will now find the first element of the tuple and invoke it, whereas previously it could only mean "invoke an extension method named 'a'".

- https://github.com/dotnet/roslyn/issues/16870 In C# 7.0 and before C# 7.1, the compiler accepted self-assignments in deconstruction-assignment. The compiler now produces a warning for that. For instance, in `(x, y) = (x, 2);`.

- The compiler is now more precise in detecting erroneous pattern-matching operations because the expression could not possibly match the pattern. The following situations now cause an error:
Copy link
Member

@jcouv jcouv May 11, 2017

Choose a reason for hiding this comment

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

Consider adding a link to one representative issue, so that it is easier to pull for more information/details. #Closed

Copy link
Member Author

@gafter gafter May 15, 2017

Choose a reason for hiding this comment

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

There is only one issue, and it doesn't have more information, but I'll add a link. #Closed

return _factory.Sequence(
ImmutableArray.Create(s, i),
ImmutableArray.Create<BoundExpression>(
_factory.AssignmentExpression(_factory.Local(i), _factory.Convert(tmpType, loweredInput)),
_factory.AssignmentExpression(_factory.Local(i), _factory.Convert(objectType, loweredInput)),
_factory.AssignmentExpression(loweredTarget, _factory.Conditional(
Copy link
Member

@alrz alrz May 11, 2017

Choose a reason for hiding this comment

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

What would it take to remove the need for the boolean local s? I mean, ideally o is S ? (S)o : e and o is S s ? s : e should produce the same code as long as CanChangeValueBetweenReads for o is false.

@@ -1468,6 +1468,8 @@ internal enum ErrorCode
ERR_VoidInTuple = 8210,
#endregion more stragglers for C# 7

#region diagnostics introduced for C# 7.1
Copy link
Member

@jcouv jcouv May 15, 2017

Choose a reason for hiding this comment

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

Although I did some compaction on bottom of the list, I just noticed the gap from 8210 to 8300.
Opened #19523 to compact more. #WontFix

@gafter
Copy link
Member Author

gafter commented May 15, 2017

This appears to fix #19122 as well.

@gafter gafter requested a review from AlekseyTs May 15, 2017 23:59
@gafter
Copy link
Member Author

gafter commented May 16, 2017

@AlekseyTs @dotnet/roslyn-compiler May I please have a couple of reviews for this fix for 7.1?

@gafter
Copy link
Member Author

gafter commented May 16, 2017

@AlekseyTs @dotnet/roslyn-compiler May I please have a couple of reviews of this fix for 7.1?

@@ -392,7 +392,8 @@ private void LowerConstantValueDecision(DecisionTree.ByValue byValue)
{
var loweredRight = kv.Key;
var loweredLeft = kv.Value;
loweredRight = _factory.Convert(loweredLeft.Type, loweredRight);
//loweredRight = _factory.Convert(loweredLeft.Type, loweredRight);
Debug.Assert(loweredLeft.Type.Equals(loweredRight.Type, TypeCompareKind.IgnoreDynamicAndTupleNames));
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

IgnoreDynamicAndTupleNames [](start = 96, length = 26)

I think we should use AllIgnoreOptions here. #Closed

@@ -392,7 +392,8 @@ private void LowerConstantValueDecision(DecisionTree.ByValue byValue)
{
var loweredRight = kv.Key;
var loweredLeft = kv.Value;
loweredRight = _factory.Convert(loweredLeft.Type, loweredRight);
//loweredRight = _factory.Convert(loweredLeft.Type, loweredRight);
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

//loweredRight = _factory.Convert(loweredLeft.Type, loweredRight); [](start = 24, length = 66)

Please remove obsolete code. #Closed

@@ -31,16 +31,7 @@ static class C {
compilation.GetDiagnostics().Verify();
compilation.GetEmitDiagnostics().Verify(
// warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options.
Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1),
// (9,48): error CS0518: Predefined type 'System.Nullable`1' is not defined or imported
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

// (9,48): error CS0518: Predefined type 'System.Nullable`1' is not defined or imported [](start = 16, length = 87)

It looks like we lost tests for error scenarios like this one, none of them are hitting the interesting code paths. We should come up with new scenarios that do. #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.

We aren't hitting them because those code paths don't exist any longer. We use a different lowering strategy for this code now.

// (9,48): error CS0518: Predefined type 'System.Nullable`1' is not defined or imported
// public static bool M() => ((object)123) is int i;
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int i").WithArguments("System.Nullable`1").WithLocation(9, 48),
// (9,48): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault'
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

// (9,48): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault' [](start = 16, length = 95)

It looks like we lost tests for error scenarios like this one, none of them are hitting the interesting code paths. We should come up with new scenarios that do. #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.

We aren't hitting them because those code paths don't exist any longer. We use a different lowering strategy for this code now.

// (9,48): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault'
// public static bool M() => ((object)123) is int i;
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "GetValueOrDefault").WithLocation(9, 48),
// (9,48): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue'
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

// (9,48): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue' [](start = 16, length = 90)

It looks like we lost tests for error scenarios like this one, none of them are hitting the interesting code paths. We should come up with new scenarios that do. #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.

We aren't hitting them because those code paths don't exist any longer. We use a different lowering strategy for this code now.

@gafter
Copy link
Member Author

gafter commented May 17, 2017

Regarding the risk, how confident that we feel? Reading from the template, reducing future risk does not imply risk of fix is low. thx.

@MeiChin-Tsai This fixes a few reported crashes. I expect that the compiler after this fix will be much more stable in this area than before the fix. The most significant part of this change replaces recently written code with slightly modified code that had been in production use for a long time.

}"
);
}

[Fact, WorkItem(19280, "https://github.com/dotnet/roslyn/issues/19280")]
public void ShareLikeKindedTemps_01()
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

ShareLikeKindedTemps_01 [](start = 20, length = 23)

I am not sure what happened with this test, it looks like it was deleted. Intentional? #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.

I think this was a merge error. Adding it again (and the next test).

@@ -4780,10 +4800,13 @@ public static void Main(string[] args)
CreateCompilationWithMscorlib45(program).VerifyDiagnostics(
// (10,18): error CS0266: Cannot implicitly convert type 'double' to 'int?'. An explicit conversion exists (are you missing a cast?)
// case double.NaN:
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "double.NaN").WithArguments("double", "int?").WithLocation(10, 18),
// (13,18): error CS8121: An expression of type int? cannot be handled by a pattern of type string.
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "double.NaN").WithArguments("double", "int?"),
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "double.NaN").WithArguments("double", "int?"), [](start = 16, length = 91)

Location is lost, please restore. #Closed

// case string _:
Diagnostic(ErrorCode.ERR_PatternWrongType, "string").WithArguments("int?", "string").WithLocation(13, 18)
Diagnostic(ErrorCode.ERR_PatternWrongType, "string").WithArguments("int?", "string"),
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

Diagnostic(ErrorCode.ERR_PatternWrongType, "string").WithArguments("int?", "string"), [](start = 16, length = 85)

Location is lost, please restore. #Closed

@@ -332,7 +350,7 @@ public void foo2(char i)
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "97.0f").WithArguments("float", "char").WithLocation(34, 18),
// (38,18): error CS0266: Cannot implicitly convert type 'int' to 'char'. An explicit conversion exists (are you missing a cast?)
// case 97:
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "97").WithArguments("int", "char").WithLocation(38, 18)
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "97").WithArguments("int", "char")
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2017

Choose a reason for hiding this comment

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

Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "97").WithArguments("int", "char") [](start = 16, length = 79)

Location is lost, please restore. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented May 17, 2017

Done with review pass. #Closed

@MeiChin-Tsai
Copy link

approved.

@gafter
Copy link
Member Author

gafter commented May 17, 2017

@AlekseyTs The PR has been modified in response to your review comments. Do you have anything else?

@dotnet/roslyn-compiler May I please have a second reviewer?

@gafter
Copy link
Member Author

gafter commented May 17, 2017

retest windows_debug_vs-integration_prtest please

@AlekseyTs
Copy link
Contributor

AlekseyTs commented May 18, 2017

The PR has been modified in response to your review comments. Do you have anything else?

In order to answer this question or to sign-off, I need to do another review pass. It is on my list of things to do. #Closed

requiresNullTest = requiresNullTest && MatchConstantValue(loweredInput, type, true) != true;
if (loweredInput.Type.IsNullableType())
{
var getValueOrDefault = _factory.SpecialMethod(SpecialMember.System_Nullable_T_GetValueOrDefault).AsMember((NamedTypeSymbol)loweredInput.Type);
Copy link
Contributor

Choose a reason for hiding this comment

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

var getValueOrDefault = _factory.SpecialMethod(SpecialMember.System_Nullable_T_GetValueOrDefault).AsMember((NamedTypeSymbol)loweredInput.Type); [](start = 20, length = 143)

Do we have a test that hits this code path with missing SpecialMember.System_Nullable_T_GetValueOrDefault?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes (see MissingNullable_03).

//}

var input = _factory.SynthesizedLocal(loweredInput.Type, syntax); // we copy the input to avoid double evaluation
var getHasValue = _factory.SpecialMethod(SpecialMember.System_Nullable_T_get_HasValue).AsMember((NamedTypeSymbol)loweredInput.Type);
Copy link
Contributor

Choose a reason for hiding this comment

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

var getHasValue = _factory.SpecialMethod(SpecialMember.System_Nullable_T_get_HasValue).AsMember((NamedTypeSymbol)loweredInput.Type); [](start = 24, length = 132)

Do we have a test that hits this code path with missing SpecialMember.System_Nullable_T_get_HasValue?

Copy link
Member Author

Choose a reason for hiding this comment

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

_factory.SpecialMethod handles this one the same way it handles the one you asked about a few lines earlier. Every call to that helper method is handled the same way in LocalRewriter (and causes the rest of the calling method to be skipped).

Copy link
Member Author

Choose a reason for hiding this comment

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

Also see 9e05b04 which adds a test that triggers the requested condition.

@AlekseyTs
Copy link
Contributor

Please answer a new question #19413 (comment), the thread appears as "outdated".

@AlekseyTs
Copy link
Contributor

Done with review pass. Just have three questions around test coverage above.

@gafter
Copy link
Member Author

gafter commented May 18, 2017

The question was

@gafter I see that you deleted the code. Could you please confirm that you added a test that would fail without this fix?

That is not possible. There are two call sites for this method, one of which passes parameters such that the extra conditions later do not make a difference in the returned value. The other call site treats true and null return values the same, so it would not care.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

Thanks. LGTM

@gafter
Copy link
Member Author

gafter commented May 19, 2017

@dotnet/roslyn-compiler May I please have a second review?

1 similar comment
@gafter
Copy link
Member Author

gafter commented May 19, 2017

@dotnet/roslyn-compiler May I please have a second review?

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM
Thanks for the code walkthrough.

@gafter gafter merged commit b057348 into dotnet:master May 19, 2017
@gafter gafter removed the 4 - In Review A fix for the issue is submitted for review. label May 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment