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

List patterns: counterexample for non-exhaustive switch expressions #55327

Merged
merged 14 commits into from
Oct 27, 2021

Conversation

alrz
Copy link
Member

@alrz alrz commented Aug 1, 2021

Relates to test plan #51289

@alrz alrz changed the title List patterns: generate counterexample for non-exhaustive switch expressions List patterns: counterexample for non-exhaustive switch expressions Aug 2, 2021
@alrz alrz marked this pull request as ready for review October 20, 2021 19:24
@alrz alrz requested a review from a team as a code owner October 20, 2021 19:24
@jcouv jcouv self-assigned this Oct 20, 2021
int lengthValue = lengthValues.Sample.Int32Value;
if (slice != null && lengthValues.All(BinaryOperatorKind.Equal, lengthValue))
{
// Bail if there's a slice but only one length value is remained.
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we can support some of these cases, as this could be useful. Consider: null or { Length: 4 } => 0, [_, .. var middle, _] => 0 (that'd be a good test to add).
Drawing the distinction between cases we can correctly explain and those we can't may be tricky.
In draft #57210 I'd probably gone a bit too far in trying to explain more slice scenarios, but it may give you some idea.

We can also punt for now and file a follow-up issue to refine.

Copy link
Member Author

@alrz alrz Oct 22, 2021

Choose a reason for hiding this comment

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

I added a test but it didn't hit this check. I think it's actually unlikely to get here without a nested length test.

Copy link
Member

Choose a reason for hiding this comment

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

It may be good to say why we bail (both cases).

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I see there was a typo in my example. Should have said not 4

Copy link
Member Author

@alrz alrz Oct 22, 2021

Choose a reason for hiding this comment

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

Then it would be exhaustive. (no error)

case BoundDagIndexerEvaluation e:
var indexerTemp = new BoundDagTemp(e.Syntax, e.IndexerType, e);
int index = e.Index;
if (index > lengthValue - 1)
Copy link
Member

@jcouv jcouv Oct 20, 2021

Choose a reason for hiding this comment

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

Do we need this check? #Closed

Copy link
Member Author

@alrz alrz Oct 22, 2021

Choose a reason for hiding this comment

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

It would hit if we didn't check slice/lengthValues above. But I think it's safer to just check all this upfront to avoid a crash. The explainer is generally defensive on out-of-bound errors and such. See tryHandleTuplePattern for instance.
Should check the other end as well. (added)

continue;
// A list pattern can only support a single slice within.
// We won't try to generate a list pattern if there's more.
case BoundDagSliceEvaluation e when slice == null:
Copy link
Member

@jcouv jcouv Oct 20, 2021

Choose a reason for hiding this comment

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

nit: We can remove the when clause and use an assertion instead (Debug.Assert(slice is null);) #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.

See comment above.

Copy link
Member Author

@alrz alrz Oct 22, 2021

Choose a reason for hiding this comment

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

Would it help to add a Assert.Fail in code paths that we don't have a test scenario for?

Copy link
Member

@jcouv jcouv Oct 22, 2021

Choose a reason for hiding this comment

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

I'd even throw UnreachableException, since we won't get here with multiple slices.
Never mind

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.

Done with review pass (iteration 8)

@alrz alrz requested a review from jcouv October 22, 2021 16:17
if (slice.StartIndex - slice.EndIndex > lengthValue)
{
// Bail if the sample value is less than the required minimum length by the slice.
return null;
Copy link
Member

@jcouv jcouv Oct 22, 2021

Choose a reason for hiding this comment

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

Is this reachable? If not, consider throwing UnreachableException instead.
Ah, saw your comment about the explainer being defensive. #Closed

var src = @"
_ = new C() switch
{
null or { Length: 4 } => 0,
Copy link
Member

Choose a reason for hiding this comment

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

Should this be not 4?

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 (iteration 12) modulo one test nit

@jcouv
Copy link
Member

jcouv commented Oct 22, 2021

@333fred @dotnet/roslyn-compiler for second review. Thanks

@jcouv jcouv added this to the 17.1.P1 milestone Oct 22, 2021
return null;

if (!constraints.All(c => c switch
{
Copy link
Member

Choose a reason for hiding this comment

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

Can we indent this? The current indentation level hurts my head.

};

_ = this switch // didn't test for not null first element
_ = this switch // we didn't test for [.. null] but we're looking for an example with Length=1. incorrect explanation [.. null, null] // 1
Copy link
Member

Choose a reason for hiding this comment

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

incorrect explanation [.. null, null]

Let's remove this bit.

// _ = this switch // we didn't test for [.. null] but we're looking for an example with Length=1. incorrect explanation [.. null, null] // 1
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("_").WithLocation(20, 18),
// (27,18): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered.
// _ = this switch // didn't test for [.. [not null]] // // 2
Copy link
Member

Choose a reason for hiding this comment

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

It general, it would be good to add an explanatory comment in this file somewhere that these are the ideal explanations, but we're not able to handle these cases.

@333fred
Copy link
Member

333fred commented Oct 22, 2021

Done review pass (commit 13). Just a bit of formatting cleanup.

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

LGTM (commit 14)

@jcouv jcouv mentioned this pull request Oct 22, 2021
91 tasks
@jcouv jcouv modified the milestones: 17.1.P1, 17.0, 17.1 Oct 27, 2021
@jcouv jcouv merged commit 94ea14c into dotnet:features/list-patterns Oct 27, 2021
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.

3 participants