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

Implement ImmutableSegmentedDictionary<TKey, TValue> #50189

Merged
merged 14 commits into from
Jan 27, 2021

Conversation

sharwell
Copy link
Member

@sharwell sharwell commented Dec 31, 2020

Provides an implementation of IImmutableDictionary<TKey, TValue> which is backed by SegmentedDictionary<TKey, TValue>. The immutable wrapper is a value type, offering benefits similar to the cases where ImmutableArray<T> is more appropriate than ImmutableList<T>. The implementation preserves all functionality from ImmutableDictionary<TKey, TValue> (intended to be a near drop-in replacement), with the following exceptions:

This comparer is not required by IImmutableDictionary<TKey, TValue> and
is not directly supported by SegmentedDictionary<TKey, TValue>. Removing
the property and relying on EqualityComparer<TValue>.Default allows for
more efficient wrapper implementation.
@sharwell sharwell force-pushed the immutable-segmented-dictionary branch from 8fa13a5 to 8ecf29e Compare January 13, 2021 00:20
@sharwell sharwell marked this pull request as ready for review January 13, 2021 00:20
@sharwell
Copy link
Member Author

@333fred @tmat @jaredpar for reviews

// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
Copy link
Member

Choose a reason for hiding this comment

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

Need to add #nullable enable so that it gets enabled in projects that don't have it enabled by default.

Copy link
Member Author

Choose a reason for hiding this comment

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

➡️ The current code follows the patterns established in the Microsoft.CodeAnalysis.PooledObjects.Package package. If we want to change the way we annotate source packages, we can do so but it should be a separate pull request that includes validation to prevent subtle regressions.

Copy link
Member

@tmat tmat Jan 21, 2021

Choose a reason for hiding this comment

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

Sounds good. Let's follow up.

Copy link
Member Author

Choose a reason for hiding this comment

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

➡️ #50749

@333fred
Copy link
Member

333fred commented Jan 20, 2021

Done review pass (commit 4)


namespace Microsoft.CodeAnalysis.Collections
{
internal static class RoslynImmutableInterlocked
Copy link
Member

Choose a reason for hiding this comment

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

RoslynImmutableInterlocke [](start = 26, length = 25)

I'd move these helpers to ImmutableSegmentedDictionary static class. Exposing "RoslynImmutableInterlocked" type is odd.

Copy link
Member Author

Choose a reason for hiding this comment

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

➡️ This type is intentionally designed as a drop-in replacement for code currently using ImmutableInterlocked with ImmutableDictionary<TKey, TValue>.

Copy link
Member

Choose a reason for hiding this comment

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

That may be, but I don't like these type names. You can drop-in replace as well, just use different type names.

Copy link
Member Author

@sharwell sharwell Jan 21, 2021

Choose a reason for hiding this comment

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

These names follow the pattern of RoslynDebug and RoslynString. The methods it contains are exactly the same as the methods in ImmutableInterlocked (signatures and semantics), but with signatures adjusted to work with our types in ways that ImmutableInterlocked does not support. I can rename this to ImmutableInterlocked as soon as C# is updated to allow static extensions to existing static classes.

The name can't otherwise be changed without actively interfering with a-priori developer knowledge.

Copy link
Member

Choose a reason for hiding this comment

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

RoslynDebug and RoslynString are internal types limited to Roslyn repo and not exposed by a source package. What if we had another type that needs interlocked operations? Are we gonna have RoslynImmutableInterlocked2?

Copy link
Member

Choose a reason for hiding this comment

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

ImmutableInterlocked provides atomic operations for all of the immutable collections types.

That would require either static extension methods or all immutable types defined in the same assembly.

Other members are going to be added to this type when ImmutableSegmentedList is added

SegmentedCollectionsInterlocked?

Copy link
Member Author

@sharwell sharwell Jan 21, 2021

Choose a reason for hiding this comment

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

That works for ImmutableSegmentedDictionary<TKey, TValue> and ImmutableSegmentedList<T>, but not for ImmutableArrayDictionary<TKey, TValue>.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll file a follow-up issue to discuss naming for static helper types in source packages.

Copy link
Member

Choose a reason for hiding this comment

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

Is the plan to move these to System.Collections.Immutable at some point?

Copy link
Member Author

Choose a reason for hiding this comment

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

Filed #50750. Moving to System.Collections.Immutable is an option, though I'm not sure if/when that would occur.

@sharwell sharwell force-pushed the immutable-segmented-dictionary branch from 2ffe81e to 54600cd Compare January 21, 2021 00:11
@sharwell sharwell requested a review from a team as a code owner January 21, 2021 00:11

mutable.Add(1, "a");
var immutable2 = mutable.ToImmutable();
Assert.NotSame(immutable1, immutable2); // "Mutating the collection did not reset the Immutable property."
Assert.Same(immutable2, mutable.ToImmutable()); // "The Immutable property getter is creating new objects without any differences."
Assert.False(immutable1 == immutable2); // "Mutating the collection did not reset the Immutable property."
Copy link
Member

Choose a reason for hiding this comment

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

Consider using Equal/NotEqual for these, rather than True/False with operators, as that will give better errors.

Copy link
Member Author

Choose a reason for hiding this comment

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

This test is leveraging the fact that == implements reference equality for these collections (similar to ImmutableArray<T>). While Equals technically would do the same thing for this type, it feels one step further removed because it looks like value equality is intended by the test when we're really looking for reference equality.

Copy link
Member

Choose a reason for hiding this comment

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

I don't believe that outweighs the benefits from getting clear error messages.

Copy link
Member Author

@sharwell sharwell Jan 23, 2021

Choose a reason for hiding this comment

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

➡️ Switched to Assert.False(IsSame(...)) to match the other cases

Copy link
Member

Choose a reason for hiding this comment

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

I don't believe that addresses my comments about error messages.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is correct, but also By Design in accordance with our adopted test framework (see xunit/xunit#350 for additional discussion). The approach used by this pull request adheres to the test framework recommendation that the assertion call site clearly show the intent of the assertion, which IsSame will do.

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.

I'm not going to block the PR over the test comments, but I really would prefer if our tests had good error messages for failures.

@sharwell sharwell merged commit 1857667 into dotnet:master Jan 27, 2021
@ghost ghost added this to the Next milestone Jan 27, 2021
@sharwell sharwell deleted the immutable-segmented-dictionary branch January 27, 2021 08:19
@RikkiGibson RikkiGibson modified the milestones: Next, 16.10.P1 Feb 17, 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.

6 participants