-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Optimize single spread collection expression for List<T>
#74769
Optimize single spread collection expression for List<T>
#74769
Conversation
[Theory] | ||
[InlineData(TargetFramework.Net80)] | ||
[InlineData(TargetFramework.Standard)] | ||
public void List_SingleSpread_CustomCollection_ICollectionAndStructEnumerator(TargetFramework targetFramework) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original motivation for verifying multiple target frameworks was to test AddRange
code path. Since now there is no difference between codegen on .NET Framework and modern .NET, I don't see value in having multiple TFMs to verify against
@@ -35272,20 +35316,20 @@ static void Main() | |||
{ | |||
IEnumerable<int> e = [1, 2, 3]; | |||
e.Report(); | |||
List<int> list = [..e]; | |||
List<int> list = [..e, 4]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didin't want to mix single-spread and AddRange
tests together and I wanted to be sure, that AddRange
code paths are still being verified. Therefore I modified existing AddRange
tests, but added an equivalent SingleSpread
tests on top for these cases
@@ -19501,7 +19433,7 @@ static void Main() | |||
var x = F([1, 2, 3]); | |||
x.Report(); | |||
} | |||
static List<T> F<T>(T[] items) => [..items]; | |||
static List<T> F<T>(T[] items) => [..items, ..items]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test for missing members doesn't make sense with new codegen, thus modified
@@ -10048,18 +10048,13 @@ static void Main() | |||
} | |||
"""; | |||
|
|||
var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); | |||
var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], [1, 2, 3],", verify: Verification.Skipped); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All changed tests can now use standard TFM, so IncludeExpectedOutput
is no longer needed
[Fact] | ||
public void List_SingleSpread_CustomCollection_NotICollectionAndNoStructEnumerator() | ||
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] | ||
public void List_SingleSpread_IEnumerable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The diff is a mess, I don't know why is that. I just added a chunk of SingleSpread
tests above SingleSpread_CustomCollection
cases
} | ||
|
||
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] | ||
public void List_SingleSpread_IEnumerable_ClassConstraint() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unconstrained and struct
-constrained cases are considered as boxing conversions, therefore they are verified by List_AddRange_IEnumerable_Constraint
test
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs
Outdated
Show resolved
Hide resolved
|
||
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; | ||
|
||
var conversion = _compilation.Conversions.ClassifyImplicitConversionFromType(spreadType, iEnumerableOfElementType, ref discardedUseSiteInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we maybe use the actual use site info for thisand report it if we take the optimzal code path an the end? Unline the ICollection<T>
check, the resulting conversion is kinda used in the final codegn, although implicitly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the original code for the AddRange case was reporting the use-site diagnostic, but I think it's fine to drop it. The code we generate does not actually use this type in any way, it's just a hint for us to decide if AddRange/ToList is likely to be efficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM except some minor comments
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs
Outdated
Show resolved
Hide resolved
|
||
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; | ||
|
||
var conversion = _compilation.Conversions.ClassifyImplicitConversionFromType(spreadType, iEnumerableOfElementType, ref discardedUseSiteInfo); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the original code for the AddRange case was reporting the use-site diagnostic, but I think it's fine to drop it. The code we generate does not actually use this type in any way, it's just a hint for us to decide if AddRange/ToList is likely to be efficient.
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs
Outdated
Show resolved
Hide resolved
Unfortunatelly yes, bootstrap failure seem to consistently reproduce in CI and I was able to reproduce the failure locally. Also it is a "bad" type of failure where no
There are multiple error like this, some have suspisious "Unnamed compilation" in the message, e.g.
Given that the stack trace doesn't show anything related to the changes of the PR and no |
Please test the following, where using System.Collections.Generic;
interface IMyEnumerable<T> : IEnumerable<T>
{
new MyEnumerator<T> GetEnumerator();
}
interface IMyCollection<T> : ICollection<T>
{
new MyEnumerator<T> GetEnumerator();
}
struct MyEnumerator<T>
{
public T Current => default;
public bool MoveNext() => false;
}
class Program
{
static void Main()
{
}
#nullable enable
static List<U> M1<T, U>(T c)
where T : class, IMyEnumerable<U>
where U : class
{
return [..c];
}
static List<U?> M2<T, U>(T c)
where T : class, IMyEnumerable<U>
where U : class
{
return [..c];
}
static List<U> M3<T, U>(T c)
where T : class, IMyCollection<U>
where U : class
{
return [..c];
}
static List<U?> M4<T, U>(T c)
where T : class, IMyCollection<U>
where U : class
{
return [..c];
}
} |
I ran
|
08edcd2
to
23d22b2
Compare
Please add the test from #74769 (comment). |
src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs
Outdated
Show resolved
Hide resolved
@cston Please merge |
Sorry for the delay. I wanted to give @RikkiGibson a chance to review the last two commits. |
Thanks @DoctorKrolic. |
Closes: #71217