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

Emit forwarded types in a deterministic order #16303

Merged
merged 3 commits into from
Jan 13, 2017
Merged

Emit forwarded types in a deterministic order #16303

merged 3 commits into from
Jan 13, 2017

Conversation

OmarTawfik
Copy link
Contributor

@OmarTawfik OmarTawfik commented Jan 6, 2017

Fixes #11990

Customer and Scenario Info
Who is impacted by this bug: customers trying to use the deterministic output feature of compilers
What is the customer scenario and impact of the bug: two identical builds might not produce the same bits.
What is the workaround: none.
How was the bug found: Manual code inspection.
If this fix is for a regression - what had regressed, when was the regression introduced, and why was the regression originally missed: No.
​What is a Customer Rea​dy description of change should we need to document this: builds are now one step closer to be 100% deterministic (see #372).

Bug Fix Details
Overview of the fix: Enumerating a hashset in an ordered way when emitting forwarded types in an assembly.
Which VS products, SKUs, or components need the fix: all SKUs shipping Roslyn compilers.
What branch does the fix go into: lab/ml.
What feature areas are impacted by the fix: Roslyn compilers.
What is the risk of the code change: none.
Does this change have impact on anything else (e.g. setup, perf, stress, ux, visual freeze, go live, breaking change, samples, etc): no.
How is this fix rolling forward in the next major release / sprintly train (e.g. Dev15): RTM.

​Validation
What testing/validation was done on the fix, and what were the results: manual testing an unit tests were included in the PR.
Which engineering partner teams have signed off on the fix: @dotnet/roslyn-compiler
Who code reviewed the fix: @AlekseyTs @cston

Additional information during Divisional Escrow Mode
Which Engineering DIRECTOR is accountable for the code change and quality: @MattGertz
What are you doing to ensure that this bug does not happen again: not a regression.
Which part of the D15 Escrow Bar does this fall under?: RTM ASK MODE

@OmarTawfik
Copy link
Contributor Author

@dotnet/roslyn-compiler

@cston
Copy link
Member

cston commented Jan 6, 2017

Please add a test if possible.

@@ -557,7 +557,10 @@ private void ReportExportedTypeNameCollisions(ImmutableArray<Cci.ExportedType> e
// (type, index of the parent exported type in builder, or -1 if the type is a top-level type)
var stack = ArrayBuilder<(NamedTypeSymbol type, int parentIndex)>.GetInstance();

foreach (NamedTypeSymbol forwardedType in wellKnownAttributeData.ForwardedTypes)
// Hashset enumeration is not guarenteed to be deterministic. Emitting in the order of fully qualified names.
Copy link
Member

Choose a reason for hiding this comment

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

Typo: guaranteed

@@ -557,7 +557,10 @@ private void ReportExportedTypeNameCollisions(ImmutableArray<Cci.ExportedType> e
// (type, index of the parent exported type in builder, or -1 if the type is a top-level type)
var stack = ArrayBuilder<(NamedTypeSymbol type, int parentIndex)>.GetInstance();

foreach (NamedTypeSymbol forwardedType in wellKnownAttributeData.ForwardedTypes)
// Hashset enumeration is not guarenteed to be deterministic. Emitting in the order of fully qualified names.
var orderedForwardedTypes = wellKnownAttributeData.ForwardedTypes.OrderBy(t => t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 6, 2017

Choose a reason for hiding this comment

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

t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) [](start = 95, length = 59)

t.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) [](start = 95, length = 59)

We probably should get display string from OriginalDefinition and use metadata format, for example "...Dictionary`2" vs. "...Dictionary<K, V>". #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@AlekseyTs you mean SymbolDisplayFormat.QualifiedNameArityFormat?

Copy link
Contributor

@AlekseyTs AlekseyTs Jan 6, 2017

Choose a reason for hiding this comment

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

you mean SymbolDisplayFormat.QualifiedNameArityFormat?

Yes. #Closed

Copy link
Member

@tmat tmat Jan 7, 2017

Choose a reason for hiding this comment

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

Isn't this unnecessarily building display strings? Don't we have any other symbol comparer?

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jan 6, 2017

Similar issue should be addressed for VB. There should be test that verifies the order. #Closed

Omar Tawfik added 2 commits January 6, 2017 15:55
Emit forwarded types in a deterministic order
@OmarTawfik
Copy link
Contributor Author

@cston @AlekseyTs comments addressed.

@@ -184,6 +185,54 @@ public void TestWriteOnlyStream()
compilation.Emit(output);
}

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

Choose a reason for hiding this comment

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

Typo: Emitted

for (var i = 1; i <= generatedTypes; i++)
{
forwardingCode.AppendLine($"[assembly: TypeForwardedTo(typeof(ForwardedToNamespace.T{i}))]");
}
Copy link
Member

Choose a reason for hiding this comment

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

forwardingCode can be created once, outside of local function.

Assert.Equal(generatedTypes, baseline.Length);

var reference = compileAndGetExportedTypes().ToArray();
Assert.Equal(baseline, reference);
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 10, 2017

Choose a reason for hiding this comment

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

Assert.Equal(baseline, reference); [](start = 12, length = 34)

It feels like the test would be better if TypeForwardedTo attributes would not be in sorted order in source and we simply verify that ExportedTypes are sorted in the way we expect. #Closed

forwardedToCode.AppendLine("namespace ForwardedToNamespace {");
for (var i = 1; i <= generatedTypes; i++)
{
forwardedToCode.AppendLine($"public class T{i} {{}}");
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 10, 2017

Choose a reason for hiding this comment

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

T{i} [](start = 58, length = 4)

Consider incorporating generic types into the test. #Closed

const int generatedTypes = 100;

var forwardedToCode = new StringBuilder();
forwardedToCode.AppendLine("namespace ForwardedToNamespace {");
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 10, 2017

Choose a reason for hiding this comment

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

ForwardedToNamespace [](start = 50, length = 20)

It would be good to add variations in namespace name into the mix. #Closed

assemblyName:="ForwardingAssembly",
source:=String.Empty,
options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
references:={forwardingNetModule.EmitToImageReference(), forwardedToCompilation.EmitToImageReference()})
Copy link
Contributor

Choose a reason for hiding this comment

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

forwardedToCompilation.EmitToImageReference() [](start = 77, length = 45)

Doing this twice, consider caching result and reusing.

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this issue is not addressed.


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

Assert.Equal(generatedTypes, baseline.Length)

Dim reference = compileAndGetExportedTypes().ToArray()
Assert.Equal(baseline, reference)
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 10, 2017

Choose a reason for hiding this comment

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

Assert.Equal(baseline, reference) [](start = 12, length = 33)

Same comments as for C# test. #Closed

@@ -517,7 +517,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
' (type, index of the parent exported type in builder, or -1 if the type is a top-level type)
Dim stack = ArrayBuilder(Of (type As NamedTypeSymbol, parentIndex As Integer)).GetInstance()

For Each forwardedType As NamedTypeSymbol In wellKnownAttributeData.ForwardedTypes
' Hashset enumeration Is Not guaranteed to be deterministic. Emitting in the order of fully qualified names.
Dim orderedForwardedTypes = wellKnownAttributeData.ForwardedTypes.OrderBy(Function(t) t.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat))
Copy link
Member

Choose a reason for hiding this comment

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

Lowercase for Is Not.

@OmarTawfik
Copy link
Contributor Author

@AlekseyTs @cston tests updated to include multiple namespaces and generics. Typos fixed.

@@ -12,6 +12,7 @@
using Roslyn.Test.Utilities;
using Microsoft.CodeAnalysis.Emit;
using System.Diagnostics;
using System.Text;
Copy link
Member

Choose a reason for hiding this comment

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

Is this namespace used? Same question for VB.

var namespaceName = block.MetadataReader.GetString(type.Namespace);

metadataFullNames.Add($"{namespaceName}.{typeName}");
}
Copy link
Member

Choose a reason for hiding this comment

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

Minor point: Consider extracting a helper method shared with VB. See MetadataValidation.GetFullTypeNames() for example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@cston done.

@cston
Copy link
Member

cston commented Jan 12, 2017

LGTM

public class Type1 {}
}
namespace Namespace3 {
public class GenericType2<T, U> {}
Copy link
Contributor

@AlekseyTs AlekseyTs Jan 12, 2017

Choose a reason for hiding this comment

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

public class GenericType2<T, U> {} [](start = 4, length = 34)

public class GenericType2<T, U> {} [](start = 4, length = 34)

It would be good to have types that differ only by generic arity. #Closed

@OmarTawfik
Copy link
Contributor Author

@AlekseyTs do I've a sign off?

@AlekseyTs
Copy link
Contributor

LGTM

@OmarTawfik
Copy link
Contributor Author

@MattGertz @natidea can I get approval for RTM?

@MattGertz
Copy link
Contributor

@OmarTawfik Template is missing.

@OmarTawfik
Copy link
Contributor Author

@MattGertz done.

@MattGertz MattGertz added this to the 2.0 (RTM) milestone Jan 13, 2017
@MattGertz
Copy link
Contributor

RTW.

@OmarTawfik
Copy link
Contributor Author

@jaredpar I see the milestone for this bug changed. Should I rebase to post-dev15?

@jaredpar
Copy link
Member

@OmarTawfik no this needs to target master / RTW

@OmarTawfik OmarTawfik merged commit d77c3e2 into dotnet:master Jan 13, 2017
@OmarTawfik OmarTawfik deleted the fix-11990-deterministic-forwarded-types-emit branch January 13, 2017 20:41
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.

The order in which type forwarders are emitted to ExportedType table is non-deterministic
7 participants