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 error recovery for various nested conversion scenarios #74089

Merged
merged 3 commits into from
Jun 27, 2024

Conversation

RikkiGibson
Copy link
Contributor

Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611

  • We had various places where we visited the underlying conversions of a conversion, but did not call MarkUnderlyingConversionsChecked. This caused assert failures in various nested conversion scenarios (exercised in new tests). It seems like this method is just a sanity check to ensure we actually traversed the whole conversion "tree" and should be called whenever we are applying the nested conversions to the corresponding subexpressions. See pre-existing call sites of the method for comparison.
  • We were not calling BindToNaturalType for the operand of a user-defined conversion which was not valid e.g. due to ambiguity. This results in unexpected unconverted target-typed expressions in the bound tree in later phases.
  • Increased uniformity of NullableWalker assertions for switch expressions versus conditional operators. This fixed assertion failures in some of the new tests.
  • NullableWalker would report nullability warnings for user-defined conversion arguments, when the conversion was not valid in initial binding anyway. Note that a user-defined conversion may exist but still not be valid, when it is ambiguous which user-defined conversion method should be used. Entering this code path in such cases was causing an assertion to fail and is not needed.

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jun 20, 2024
@RikkiGibson RikkiGibson marked this pull request as ready for review June 21, 2024 00:10
@RikkiGibson RikkiGibson requested a review from a team as a code owner June 21, 2024 00:10
@RikkiGibson
Copy link
Contributor Author

@dotnet/roslyn-compiler for review

@@ -683,6 +683,7 @@ private static void CheckInlineArrayTypeIsSupported(SyntaxNode syntax, TypeSymbo
}

var elementConversions = conversion.UnderlyingConversions;
conversion.MarkUnderlyingConversionsChecked();
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 24, 2024

Choose a reason for hiding this comment

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

conversion.MarkUnderlyingConversionsChecked();

Consider moving this after the work of checking the underlying conversions is done. I assume that is after the loop below. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

BTW, it is not clear to me what is this operation going to change. It look like we are mutating a local value of a struct, which is not returned or stored anywhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

it is not clear to me what is this operation going to change

I guess this operation is mutating a class instance referenced by the struct, therefore, the mutation is preserved even when the struct value is not preserved.
Is this change backed by a test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we do not make this call, then newly added test Tuple_UserDefinedConversion_CollectionExpression_NotAmbiguous will fail in AssertUnderlyingConversionsChecked.

@@ -1645,6 +1646,7 @@ internal bool TryGetCollectionIterationType(SyntaxNode syntax, TypeSymbol collec
bool targetTyped = conversionIfTargetTyped is { };
Debug.Assert(targetTyped || destination.IsErrorType() || destination.Equals(source.Type, TypeCompareKind.ConsiderEverything));
ImmutableArray<Conversion> underlyingConversions = conversionIfTargetTyped.GetValueOrDefault().UnderlyingConversions;
conversionIfTargetTyped.GetValueOrDefault().MarkUnderlyingConversionsChecked();
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 24, 2024

Choose a reason for hiding this comment

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

conversionIfTargetTyped.GetValueOrDefault()

Consider extracting a local and reusing. #Closed

@@ -1645,6 +1646,7 @@ internal bool TryGetCollectionIterationType(SyntaxNode syntax, TypeSymbol collec
bool targetTyped = conversionIfTargetTyped is { };
Debug.Assert(targetTyped || destination.IsErrorType() || destination.Equals(source.Type, TypeCompareKind.ConsiderEverything));
ImmutableArray<Conversion> underlyingConversions = conversionIfTargetTyped.GetValueOrDefault().UnderlyingConversions;
conversionIfTargetTyped.GetValueOrDefault().MarkUnderlyingConversionsChecked();
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 24, 2024

Choose a reason for hiding this comment

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

MarkUnderlyingConversionsChecked

Consider doing this after the conversions are actually checked. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change backed by a test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we do not make this call, then newly added test Tuple_UserDefinedConversion_ConditionalExpression_NotAmbiguous will fail in AssertUnderlyingConversionsChecked.

@@ -1682,6 +1684,7 @@ private BoundExpression ConvertSwitchExpression(BoundUnconvertedSwitchExpression
Conversion conversion = conversionIfTargetTyped ?? Conversion.Identity;
Debug.Assert(targetTyped || destination.IsErrorType() || destination.Equals(source.Type, TypeCompareKind.ConsiderEverything));
ImmutableArray<Conversion> underlyingConversions = conversion.UnderlyingConversions;
conversion.MarkUnderlyingConversionsChecked();
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 24, 2024

Choose a reason for hiding this comment

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

MarkUnderlyingConversionsChecked

Consider doing this after the conversions are actually checked. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change backed by a test

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we do not make this call, then newly added test Tuple_UserDefinedConversion_SwitchExpression_NotAmbiguous will fail in AssertUnderlyingConversionsChecked.

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 1)

@RikkiGibson RikkiGibson requested a review from AlekseyTs June 24, 2024 21:38
@RikkiGibson
Copy link
Contributor Author

It looks like some test runs are timing out. I would be surprised if just the changes in c39c918 were causing this. Following up with infraswat.

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.

LGTM (commit 3)

@RikkiGibson RikkiGibson requested a review from a team June 25, 2024 15:17
@RikkiGibson
Copy link
Contributor Author

@dotnet/roslyn-compiler for a second review

1 similar comment
@RikkiGibson
Copy link
Contributor Author

@dotnet/roslyn-compiler for a second review

@@ -1723,7 +1727,7 @@ private BoundExpression ConvertSwitchExpression(BoundUnconvertedSwitchExpression

return new BoundConversion(
syntax,
source,
BindToNaturalType(source, diagnostics),
Copy link
Member

Choose a reason for hiding this comment

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

Should we be calling BindToTypeForErrorRecovery instead? It seems like the main difference is around error reporting; any errors from BindToNaturalType will be put into diagnostics, while BindToTypeForErrorRecovery will discard them. Do we already have a diagnostic at this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We will definitely have some diagnostic about this expression, because of the preceding if (!hasErrors) GenerateImplicitConversionError(...).

re: the difference in practice. If we used BindToTypeForErrorRecovery here, then test Repro_VsFeedback_2088611 wouldn't report ERR_ImplicitObjectCreationNoTargetType. It would only report ERR_ConversionNotTupleCompatible. I feel like in this particular case, it's maybe better to have a squiggle on the new() because it happens to be related to the ambiguity. But, ideally, we would be able to report some kind of user-defined operator overload resolution error here, I think.

@RikkiGibson RikkiGibson merged commit 9951ceb into dotnet:main Jun 27, 2024
24 checks passed
@RikkiGibson RikkiGibson deleted the vsfeedback-2088611 branch June 27, 2024 00:41
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jun 27, 2024
@RikkiGibson RikkiGibson modified the milestones: Next, 17.12 P1 Jul 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants