Skip to content

Track assemblies declaring well-known types used by a compilation.#40010

Merged
AlekseyTs merged 2 commits intodotnet:features/UsedAssemblyReferencesfrom
AlekseyTs:UsedAssemblies_02
Nov 28, 2019
Merged

Track assemblies declaring well-known types used by a compilation.#40010
AlekseyTs merged 2 commits intodotnet:features/UsedAssemblyReferencesfrom
AlekseyTs:UsedAssemblies_02

Conversation

@AlekseyTs
Copy link
Copy Markdown
Contributor

Related to #37768.

@AlekseyTs AlekseyTs requested a review from a team November 25, 2019 21:24
@AlekseyTs
Copy link
Copy Markdown
Contributor Author

AlekseyTs commented Nov 26, 2019

@dotnet/roslyn-compiler Please review #Resolved

diagnostics.Add(declaration.Type, useSiteDiagnostics);
}
else if (TypeSymbol.Equals(previousType, Compilation.GetWellKnownType(WellKnownType.System_Exception), TypeCompareKind.ConsiderEverything2) &&
else if (TypeSymbol.Equals(previousType, Compilation.GetWellKnownType(WellKnownType.System_Exception, recordUsage: false), TypeCompareKind.ConsiderEverything2) &&
Copy link
Copy Markdown
Member

@jcouv jcouv Nov 26, 2019

Choose a reason for hiding this comment

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

ConsiderEverything2 [](start = 163, length = 19)

nit: when modifying a method, consider scrubbing ConsiderEverything2 (is ConsiderEverything the correct option?) #WontFix

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.

is ConsiderEverything the correct option?

The option used here has no relation with the purpose of this PR and I do not intend spending any time thinking about it under umbrella of this PR.


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

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 understand, but then I'm afraid we won't make progress on reviewing those incrementally, as every PR will likely raise similar pushback... Maybe worth discussing in team meeting (how do we make progress on those?), but unrelated to your PR. Thanks


In reply to: 350910134 [](ancestors = 350910134,350908701)

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.

but then I'm afraid we won't make progress on reviewing those incrementally, as every PR will likely raise similar pushback...

There is a PR that touches 90 files across wide range of features and there is a PR that focuses on specific feature where a comparison actually plays a role.


In reply to: 350914019 [](ancestors = 350914019,350910134,350908701)

if ((!enumeratorType.IsSealed && !isAsync) ||
this.Conversions.ClassifyImplicitConversionFromType(enumeratorType,
isAsync ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable) : this.Compilation.GetSpecialType(SpecialType.System_IDisposable),
isAsync ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable, recordUsage: false) : this.Compilation.GetSpecialType(SpecialType.System_IDisposable),
Copy link
Copy Markdown
Member

@jcouv jcouv Nov 26, 2019

Choose a reason for hiding this comment

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

System_IAsyncDisposable [](start = 78, length = 23)

I assume we don't need to record this because we're also using IAsyncEnumerable or IAsyncEnumerator, which depend on this type, so will record usage? #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 assume we don't need to record this because we're also using IAsyncEnumerable or IAsyncEnumerator, which depend on this type, so will record usage?

An attempt to classify conversion is not usage. Usage would be binding a conversion. In addition, we are using enumeratorType and for that type all bases (including implemented interfaces) are considered used.


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


if (TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T), TypeCompareKind.ConsiderEverything2) ||
TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerator_T), TypeCompareKind.ConsiderEverything2))
if (TypeSymbol.Equals(originalDefinition, compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IAsyncEnumerable_T, recordUsage: false), TypeCompareKind.ConsiderEverything2) ||
Copy link
Copy Markdown
Member

@jcouv jcouv Nov 26, 2019

Choose a reason for hiding this comment

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

ConsiderEverything2 [](start = 185, length = 19)

nit: ConsiderEverything #WontFix

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.

nit: ConsiderEverything

Not related to the change


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

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Nov 26, 2019

I have a general question on the scenarios/APIs/design: is it a requirement that we should be able to list used/unused assembly references without going to emit phase? My naive approach would have been to intercept references during emit phase, as it seems more centralized.
Or is the problem that we need to only record well-known types, and the emit phase doesn't distinguish those?


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

@AlekseyTs
Copy link
Copy Markdown
Contributor Author

AlekseyTs commented Nov 26, 2019

is it a requirement that we should be able to list used/unused assembly references without going to emit phase?

  • Emit is triggered by a special compilation API and it requires additional inputs not known to a Compilation. There is a requirement to make API available for analyzers, analyzers also don't have that information. Analyzers work during compilation and triggering at the same time yet another emit based on analyzer's request feels like a source of all kinds of problems.
  • Performance is another factor, full emit will take longer

For V1 of the feature we decided to track what we can track from binding and lowering.


In reply to: 558761495 [](ancestors = 558761495,558743543)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Nov 26, 2019

Makes sense. Thanks!


In reply to: 558766520 [](ancestors = 558766520,558761495,558743543)

}

TypeSymbol securityAction = compilation.GetWellKnownType(WellKnownType.System_Security_Permissions_SecurityAction);
TypeSymbol securityAction = compilation.GetWellKnownType(WellKnownType.System_Security_Permissions_SecurityAction, recordUsage: false);
Copy link
Copy Markdown
Member

@jcouv jcouv Nov 26, 2019

Choose a reason for hiding this comment

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

compilation.GetWellKnownType(WellKnownType.System_Security_Permissions_SecurityAction, recordUsage: false) [](start = 40, length = 106)

nit: I assume that some of the types don't record usage because they are referenced by other types which did record. This may be worth a comment? #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.

nit: I assume that some of the types don't record usage because they are referenced by other types which did record. This may be worth a comment?

Recorded below.


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

// Do the steps in compilation to get the method body diagnostics, but don't actually generate
// IL or emit an assembly.
private void GetDiagnosticsForAllMethodBodies(DiagnosticBag diagnostics, CancellationToken cancellationToken)
private void GetDiagnosticsForAllMethodBodies(DiagnosticBag diagnostics, bool doLowering, CancellationToken cancellationToken)
Copy link
Copy Markdown
Member

@jcouv jcouv Nov 26, 2019

Choose a reason for hiding this comment

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

doLowering [](start = 86, length = 10)

nit: This parameter probably could use a comment #Pending

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.

nit: This parameter probably could use a comment

Please provide suggested wording beyond what the name already implies.


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

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

case ConversionKind.StackAllocToSpanType:
CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefStructs, diagnostics);
stackAllocType = Compilation.GetWellKnownType(WellKnownType.System_Span_T).Construct(elementType);
stackAllocType = Compilation.GetWellKnownType(WellKnownType.System_Span_T, recordUsage: false).Construct(elementType);
Copy link
Copy Markdown
Member

@333fred 333fred Nov 27, 2019

Choose a reason for hiding this comment

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

recordUsage: false [](start = 95, length = 18)

Why not record usage here? This seems like it will definitely be used by the final assembly? #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.

Why not record usage here? This seems like it will definitely be used by the final assembly?

An attempt to classify conversion is not a usage. It can be made by any external consumer through public API.


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

TypeSymbol.Equals(
convertedArguments[0].Type,
Compilation.GetWellKnownType(WellKnownType.System_Range),
Compilation.GetWellKnownType(WellKnownType.System_Range, recordUsage: false),
Copy link
Copy Markdown
Member

@333fred 333fred Nov 27, 2019

Choose a reason for hiding this comment

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

recordUsage: false [](start = 77, length = 18)

Similar question: wouldn't we want to record usage here unless there are errors? #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.

Similar question: wouldn't we want to record usage here unless there are errors?

I don't think we should say that we use System.Range here if the convertedArguments[0].Type is not it. Do we report any error here if System.Range is missing? We use convertedArguments[0].Type and that will be tracked elsewhere by other means.


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

{
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
TypeSymbol type = GetWellKnownType(wellKnownType, ref useSiteDiagnostics);
TypeSymbol type = GetWellKnownTypeWithoutRecordingUsage(wellKnownType, ref useSiteDiagnostics);
Copy link
Copy Markdown
Member

@333fred 333fred Nov 27, 2019

Choose a reason for hiding this comment

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

GetWellKnownTypeWithoutRecordingUsage [](start = 30, length = 37)

Everywhere else we've been using an optional parameter. Consider removing this method and just using that parameter. #WontFix

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.

Everywhere else we've been using an optional parameter. Consider removing this method and just using that parameter.

This method never records usage, I see no point in adding any useless optional parameter.


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

var result = TryImplicitConversionToArrayIndex(expr, type, node, attemptDiagnostics);
if (result is object)
{
if (!IsSemanticModelBinder)
Copy link
Copy Markdown
Member

@333fred 333fred Nov 27, 2019

Choose a reason for hiding this comment

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

IsSemanticModelBinder [](start = 21, length = 21)

We're checking/passing this condition in a bunch of different places. Consider adding a RecordUsedAssemblyIfNecessary on Binder that checks this in one place. #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.

We're checking/passing this condition in a bunch of different places. Consider adding a RecordUsedAssemblyIfNecessary on Binder that checks this in one place.

I am not sure I am following. It looks like there are only two if statements like this in binder code. I don't believe it is worth adding a helper for that.


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

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.

It looks like there are only two if statements like this in binder code

The previous PR had 2 direct copies of this, plus 2 where this was checked in a nested local function (Binder.ResultSymbol). The symbol code also had a few cases where the symbol checked the property off the binder it was passed.


In reply to: 351437499 [](ancestors = 351437499,351359049)

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.

And as we add tracking more scenarios, I imagine that will continue to expand.


In reply to: 351445110 [](ancestors = 351445110,351437499,351359049)

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.

The previous PR had 2 direct copies of this, plus 2 where this was checked in a nested local function (Binder.ResultSymbol). The symbol code also had a few cases where the symbol checked the property off the binder it was passed.

I do not see this. There are only two places where we do if (!IsSemanticModelBinder) Compilation.AddUsedAssembly(...) and all of them are added in this PR. Binder.ResultSymbol doesn't even call Compilation.AddUsedAssembly. It is quite possible I still do not understand what exact refactoring you are suggesting. Are you suggesting to add another property to return !IsSemanticModelBinder from it and use it instead of checking IsSemanticModelBinder inline?

And as we add tracking more scenarios, I imagine that will continue to expand.

I do not plan to add any more similar tracking during binding.


In reply to: 351445297 [](ancestors = 351445297,351445110,351437499,351359049)

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.

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.

The point being that, in general, I don't really think that IsSpeculativeSemanticModel is the type of check that it would occur to me that I need to perform before recording whether or not I'm using a type reference. I'm worried that in the future, someone will add code here that just adds the used assembly unconditionally, so it would be better to have that check made for you as part of a RecordIfNecessary API.


In reply to: 351453835 [](ancestors = 351453835,351451006,351445297,351445110,351437499,351359049)

Copy link
Copy Markdown
Contributor Author

@AlekseyTs AlekseyTs Nov 27, 2019

Choose a reason for hiding this comment

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

so it would be better to have that check made for you as part of a RecordIfNecessary API.

Sorry, I do not see the benefit as anyone can call APIs on compilation directly. Again, it is quite possible that I am misinterpreting your suggestion. Perhaps you could provide the code for what is it you are suggesting.


In reply to: 351455345 [](ancestors = 351455345,351453835,351451006,351445297,351445110,351437499,351359049)

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.

Perhaps we can follow up on this later in-person?


In reply to: 351457798 [](ancestors = 351457798,351455345,351453835,351451006,351445297,351445110,351437499,351359049)

/// This is fixed in 4.5 thus enabling block array initialization for a very common case.
/// We look for the presence of <see cref="System.Runtime.GCLatencyMode.SustainedLowLatency"/> which was introduced in .NET Framework 4.5
/// </remarks>
private bool EnableEnumArrayBlockInitialization
Copy link
Copy Markdown
Member

@333fred 333fred Nov 27, 2019

Choose a reason for hiding this comment

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

I'm not seeing what change requires this to move from the existing location? #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.

I'm not seeing what change requires this to move from the existing location?

There is only one consumer of this API, the emit layer. This makes it clear what it should do about tracking well-known types. The method is moved to avoid accidental use elsewhere in the future.


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

}

var ctor = ((MethodSymbol)this._module.Compilation.GetWellKnownTypeMember(WellKnownMember.System_ReadOnlySpan_T__ctor));
var ctor = ((MethodSymbol)this._module.Compilation.GetWellKnownTypeMember(WellKnownMember.System_ReadOnlySpan_T__ctor, recordUsage: false));
Copy link
Copy Markdown
Member

@333fred 333fred Nov 27, 2019

Choose a reason for hiding this comment

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

Should we be recording usage if we successfully emit? #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.

Should we be recording usage if we successfully emit?

We don't track anything during emit, because the tracking is limited to binding and lowering, after that we return the result.


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

@333fred
Copy link
Copy Markdown
Member

333fred commented Nov 27, 2019

Done review pass (commit 2)

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 2), but I would like to talk more about refactoring the IsSemanticModelBinder check into a more centralized location.

@AlekseyTs AlekseyTs merged commit 6ef9ace into dotnet:features/UsedAssemblyReferences Nov 28, 2019
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