@@ -56,8 +56,11 @@ private BoundExpression RewriteCollectionExpressionConversion(Conversion convers
56
56
case CollectionExpressionTypeKind . CollectionBuilder :
57
57
// If the collection type is ImmutableArray<T>, then construction is optimized to use
58
58
// ImmutableCollectionsMarshal.AsImmutableArray.
59
+ // The only exception is when collection expression is just `[.. readOnlySpan]` of the same element type, in such cases
60
+ // it is more efficient to emit a direct call of `ImmutableArray.Create`
59
61
if ( ConversionsBase . IsSpanOrListType ( _compilation , node . Type , WellKnownType . System_Collections_Immutable_ImmutableArray_T , out var arrayElementType ) &&
60
- _compilation . GetWellKnownTypeMember ( WellKnownMember . System_Runtime_InteropServices_ImmutableCollectionsMarshal__AsImmutableArray_T ) is MethodSymbol asImmutableArray )
62
+ _compilation . GetWellKnownTypeMember ( WellKnownMember . System_Runtime_InteropServices_ImmutableCollectionsMarshal__AsImmutableArray_T ) is MethodSymbol asImmutableArray &&
63
+ ! CanOptimizeSingleSpreadAsCollectionBuilderArgument ( node , out _ ) )
61
64
{
62
65
return VisitImmutableArrayCollectionExpression ( node , arrayElementType , asImmutableArray ) ;
63
66
}
@@ -138,6 +141,24 @@ static BoundNode unwrapListElement(BoundCollectionExpression node, BoundNode ele
138
141
}
139
142
}
140
143
144
+ private static bool CanOptimizeSingleSpreadAsCollectionBuilderArgument ( BoundCollectionExpression node , [ NotNullWhen ( true ) ] out BoundExpression ? spreadExpression )
145
+ {
146
+ spreadExpression = null ;
147
+
148
+ if ( node is
149
+ {
150
+ CollectionBuilderMethod : { } builder ,
151
+ Elements : [ BoundCollectionExpressionSpreadElement { Expression : { Type : NamedTypeSymbol spreadType } expr } ] ,
152
+ } &&
153
+ ConversionsBase . HasIdentityConversion ( builder . Parameters [ 0 ] . Type , spreadType ) &&
154
+ ( ! builder . ReturnType . IsRefLikeType || builder . Parameters [ 0 ] . EffectiveScope == ScopedKind . ScopedValue ) )
155
+ {
156
+ spreadExpression = expr ;
157
+ }
158
+
159
+ return spreadExpression is not null ;
160
+ }
161
+
141
162
private BoundExpression VisitImmutableArrayCollectionExpression ( BoundCollectionExpression node , TypeWithAnnotations elementType , MethodSymbol asImmutableArray )
142
163
{
143
164
var arrayCreation = VisitArrayOrSpanCollectionExpression (
@@ -375,7 +396,14 @@ private BoundExpression VisitCollectionBuilderCollectionExpression(BoundCollecti
375
396
Debug . Assert ( spanType . OriginalDefinition . Equals ( _compilation . GetWellKnownType ( WellKnownType . System_ReadOnlySpan_T ) , TypeCompareKind . AllIgnoreOptions ) ) ;
376
397
377
398
var elementType = spanType . TypeArgumentsWithAnnotationsNoUseSiteDiagnostics [ 0 ] ;
378
- BoundExpression span = VisitArrayOrSpanCollectionExpression ( node , CollectionExpressionTypeKind . ReadOnlySpan , spanType , elementType ) ;
399
+
400
+ // If collection expression is of form `[.. anotherReadOnlySpan]`
401
+ // with `anotherReadOnlySpan` being a ReadOnlySpan of the same type as target collection type
402
+ // and that span cannot be captured in a returned ref struct
403
+ // we can directly use `anotherReadOnlySpan` as collection builder argument and skip the copying assignment.
404
+ BoundExpression span = CanOptimizeSingleSpreadAsCollectionBuilderArgument ( node , out var spreadExpression )
405
+ ? spreadExpression
406
+ : VisitArrayOrSpanCollectionExpression ( node , CollectionExpressionTypeKind . ReadOnlySpan , spanType , elementType ) ;
379
407
380
408
var invocation = new BoundCall (
381
409
node . Syntax ,
0 commit comments