Skip to content

Avoid calculating inferred delegate type unnecessarily in conversions and type inference#58115

Merged
cston merged 5 commits intodotnet:mainfrom
cston:58106
Dec 9, 2021
Merged

Avoid calculating inferred delegate type unnecessarily in conversions and type inference#58115
cston merged 5 commits intodotnet:mainfrom
cston:58106

Conversation

@cston
Copy link
Copy Markdown
Contributor

@cston cston commented Dec 4, 2021

Fixes #58106.

@ghost ghost added the Area-Compilers label Dec 4, 2021
@cston cston marked this pull request as ready for review December 6, 2021 07:10
@cston cston requested a review from a team as a code owner December 6, 2021 07:11

namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
internal sealed class InferredDelegateTypeData
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 6, 2021

Choose a reason for hiding this comment

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

InferredDelegateTypeData

Consider adding a doc comment for the type and its member. #Closed

Data = new ConcurrentDictionary<object, NullableWalker.Data>();
}
}
internal object? CompilationData;
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 6, 2021

Choose a reason for hiding this comment

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

CompilationData

Consider reflecting test-only nature of this field in the name. For example, "TestOnlyCompilationData". #Closed

/// Nullable analysis data for methods, parameter default values, and attributes.
/// The key is a symbol for methods or parameters, and syntax for attributes.
/// The data is collected during testing only.
/// Optional data collected during testing only.
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 6, 2021

Choose a reason for hiding this comment

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

Optional data collected during testing only.

Consider giving examples of type that can be used as values for this field. #Closed

var thisDelegateType = GetInternalDelegateType();
var otherDelegateType = otherType.GetInternalDelegateType();

Debug.Assert(thisDelegateType is { });
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 6, 2021

Choose a reason for hiding this comment

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

Debug.Assert(thisDelegateType is { });

It is not obvious why this is a safe assumption. #Closed

@AlekseyTs
Copy link
Copy Markdown
Contributor

AlekseyTs commented Dec 7, 2021

        return HasDelegateVarianceConversion(sourceDelegate, destinationDelegate, ref useSiteInfo);

It doesn't look like this function is going to be happy about null arguments.


In reply to: 987389957


Refers to: src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs:2706 in 1acfcdc. [](commit_id = 1acfcdc, deletion_comment = False)

{
if (HasImplicitFunctionTypeConversion(sourceFunctionType, destination, ref useSiteInfo))
if (HasImplicitFunctionTypeConversion(sourceFunctionType, destination, ref useSiteInfo) &&
sourceFunctionType.GetInternalDelegateType() is { })
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 7, 2021

Choose a reason for hiding this comment

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

sourceFunctionType.GetInternalDelegateType() is { }

I think it would be better if HasImplicitFunctionTypeConversion checked for that when appropriate. #Closed

}

/// <summary>
/// A <see cref="TypeSymbol"/> implementation that represents the inferred signature of a
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 7, 2021

Choose a reason for hiding this comment

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

that represents the inferred signature

Is this still accurate? #Closed

@AlekseyTs
Copy link
Copy Markdown
Contributor

Done with review pass (commit 2)


[Fact]
[WorkItem(58106, "https://github.com/dotnet/roslyn/issues/58106")]
public void InferDelegateType_01()
Copy link
Copy Markdown
Member

@333fred 333fred Dec 7, 2021

Choose a reason for hiding this comment

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

Are these enough of a stress test that it will demonstrate the perf problem? Or can we create a pathological test that won't compile in any reasonable amount of time without this change? #Resolved

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, these are not stress tests. I decided to test these cases more directly by tracking the InferredDelegateCount instead. We could test perf although that might require a test that is slow even after the fix.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like tracking the count here as that is directly tracking the problem we are trying to fix here (avoiding unnecessary work).

My biggest fear is making sure that future developers understand why we are tracking the count though. The only documentation here is the issue and the issue doesn't specifically call out that this is a customer scenario. It only mentions that it's causing us to do unnecessary work. I think we should make the issue more specific here, say it caused customer reported problems and link to the VS feedback (or link to the feedback here).

Want to make it clear as possible to future developers why this count is meaningful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've added comments to these tests and links to the related issue.

@333fred
Copy link
Copy Markdown
Member

333fred commented Dec 7, 2021

Done review pass (commit 2)

if (destination is FunctionTypeSymbol destinationFunctionType)
{
return HasImplicitFunctionTypeToFunctionTypeConversion(source, destinationFunctionType, ref useSiteInfo);
if (!HasImplicitFunctionTypeToFunctionTypeConversion(source, destinationFunctionType, ref useSiteInfo))
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 7, 2021

Choose a reason for hiding this comment

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

if (!HasImplicitFunctionTypeToFunctionTypeConversion(source, destinationFunctionType, ref useSiteInfo))

It looks like we can keep the original logic, i.e.

return HasImplicitFunctionTypeToFunctionTypeConversion(source, destinationFunctionType, ref useSiteInfo);
``` #Closed

@@ -2699,6 +2705,11 @@ private bool HasImplicitFunctionTypeToFunctionTypeConversion(FunctionTypeSymbol
var sourceDelegate = sourceType.GetInternalDelegateType();
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 7, 2021

Choose a reason for hiding this comment

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

GetInternalDelegateType

Were all other call sites of this API reviewed and properly adjusted to handle nulls? #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I have reviewed all use of GetInternalDelegateType().

var otherType = (t2 as FunctionTypeSymbol)?._delegateType;
return _delegateType.Equals(otherType, compareKind);
return t2 is FunctionTypeSymbol otherType &&
Equals(GetInternalDelegateType(), otherType.GetInternalDelegateType(), compareKind);
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 7, 2021

Choose a reason for hiding this comment

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

GetInternalDelegateType(), otherType.GetInternalDelegateType()

It looks like two FunctionTypeSymbols failing inference will be considered equal. Is this intentional? #Closed

{
return false;
}
return source.GetInternalDelegateType() is { };
Copy link
Copy Markdown
Contributor

@AlekseyTs AlekseyTs Dec 7, 2021

Choose a reason for hiding this comment

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

return source.GetInternalDelegateType() is { };

How confident are we that all places like this one got fixed up? I mean places that do not explicitly use GetInternalDelegateType API, but assume that it is going to return a non-null value just because we are dealing with FunctionTypeSymbol and before this PR that meant that the signature was inferred successfully. #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe I have reviewed all callers that use FunctionTypeSymbol directly or indirectly.

The most interesting cases are in MethodTypeInferrer where the bounds collection was (and still is) including FunctionTypeSymbol instances in the set of lower bounds and which now explicitly drops bounds with null delegate type in MethodTypeInferrer.Fix().

@AlekseyTs
Copy link
Copy Markdown
Contributor

Done with review pass (commit 3).

Copy link
Copy Markdown
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 4), assuming CI is passing.

Copy link
Copy Markdown
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 5)

@cston cston merged commit 2ee4427 into dotnet:main Dec 9, 2021
@ghost ghost added this to the Next milestone Dec 9, 2021
@cston cston deleted the 58106 branch December 9, 2021 16:51
333fred added a commit to 333fred/roslyn that referenced this pull request Dec 10, 2021
…rations

* upstream/main: (396 commits)
  Update several ExpressionCompiler unit tests for inferred delegate types (dotnet#58203)
  Avoid calculating inferred delegate type unnecessarily in conversions and type inference (dotnet#58115)
  OmniSharp options (dotnet#58208)
  Fix generator caching in compiler server (dotnet#57177)
  Clarify use of null as an initialized value
  BoundDecisionDag.Rewrite - Avoid capturing the replacement map (dotnet#58137)
  Address feedback
  Add regex parser tests
  Add additional test for another bug report
  Add additional test for another bug report
  Add additional test for another bug report
  Add additional test for another bug report
  Add additional test for another bug report
  Add missing delegate casts (dotnet#58170)
  Add additional test for another bug report
  Add additional test for another bug report
  Add additional test for another bug report
  Add additional test for another bug report
  Add additional test for another bug report
  Add additional test for another bug report
  ...
@cston cston modified the milestones: Next, 17.1.P3 Dec 10, 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.

Avoid calculating inferred delegate type unnecessarily in conversions and type inference

4 participants