|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | 3 | // See the LICENSE file in the project root for more information. |
4 | 4 |
|
| 5 | +using System; |
5 | 6 | using System.Collections.Immutable; |
6 | 7 | using System.Diagnostics; |
7 | 8 | using Microsoft.CodeAnalysis.CSharp.CodeGen; |
@@ -1024,13 +1025,67 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundImplicitIndexerAcces |
1024 | 1025 | AddPlaceholderReplacement(node.ArgumentPlaceholders[1], rangeSizeExpr); |
1025 | 1026 |
|
1026 | 1027 | var sliceCall = (BoundCall)node.IndexerOrSliceAccess; |
1027 | | - var rewrittenIndexerAccess = VisitExpression(sliceCall); |
| 1028 | + |
| 1029 | + // [start..] can be simplified to .Substring(start) or .Slice(start) for some built-in types |
| 1030 | + // e.g. string and (ReadOnly)Span |
| 1031 | + |
| 1032 | + BoundExpression? moreEfficientIndexerAccess = null; |
| 1033 | + if (endMakeOffsetInput is null) |
| 1034 | + { |
| 1035 | + MethodSymbol? singleArgumentOverload = null; |
| 1036 | + |
| 1037 | + if (TryGetSpecialTypeMethod(node.Syntax, SpecialMember.System_String__SubstringIntInt, out var stringSubstring, isOptional: true) |
| 1038 | + && sliceCall.Method.Equals(stringSubstring, TypeCompareKind.ConsiderEverything)) |
| 1039 | + { |
| 1040 | + if (TryGetSpecialTypeMethod(node.Syntax, SpecialMember.System_String__SubstringInt, out var stringOverload, isOptional: true)) |
| 1041 | + { |
| 1042 | + singleArgumentOverload = stringOverload; |
| 1043 | + } |
| 1044 | + } |
| 1045 | + // with single typeWithElementType parameter ((ReadOnly)(Span|Memory)) |
| 1046 | + // Method name is always "Slice" |
| 1047 | + else if (sliceCall is { Method: SubstitutedMethodSymbol { UnderlyingMethod: var generalizedMethod, Name: WellKnownMemberNames.SliceMethodName }, Type: NamedTypeSymbol { Name: var typeName } typeWithElementType }) |
| 1048 | + { |
| 1049 | + // We use the return typeWithElementType of the current twoArgumentOverloadSymbol to simplify the logic |
| 1050 | + singleArgumentOverload = (typeName switch |
| 1051 | + { |
| 1052 | + // <int> will be ignored by nameof |
| 1053 | + nameof(Span<int>) => tryGetCorrespondingOneArgumentOverload(WellKnownMember.System_Span_T__Slice_Int_Int, WellKnownMember.System_Span_T__Slice_Int, generalizedMethod, node.Syntax), |
| 1054 | + nameof(ReadOnlySpan<int>) => tryGetCorrespondingOneArgumentOverload(WellKnownMember.System_ReadOnlySpan_T__Slice_Int_Int, WellKnownMember.System_ReadOnlySpan_T__Slice_Int, generalizedMethod, node.Syntax), |
| 1055 | + nameof(Memory<int>) => tryGetCorrespondingOneArgumentOverload(WellKnownMember.System_Memory_T__Slice_Int_Int, WellKnownMember.System_Memory_T__Slice_Int, generalizedMethod, node.Syntax), |
| 1056 | + nameof(ReadOnlyMemory<int>) => tryGetCorrespondingOneArgumentOverload(WellKnownMember.System_ReadOnlyMemory_T__Slice_Int_Int, WellKnownMember.System_ReadOnlyMemory_T__Slice_Int, generalizedMethod, node.Syntax), |
| 1057 | + _ => null, |
| 1058 | + // The returned typeWithElementType of the above twoArgumentOverloadSymbol has not had a concrete element typeWithElementType yet. |
| 1059 | + })?.AsMember(typeWithElementType); |
| 1060 | + } |
| 1061 | + |
| 1062 | + if (singleArgumentOverload is not null) |
| 1063 | + { |
| 1064 | + moreEfficientIndexerAccess = F.Call(VisitExpression(receiver), singleArgumentOverload, VisitExpression(startExpr)); |
| 1065 | + } |
| 1066 | + } |
| 1067 | + |
| 1068 | + var rewrittenIndexerAccess = moreEfficientIndexerAccess ?? VisitExpression(sliceCall); |
1028 | 1069 |
|
1029 | 1070 | RemovePlaceholderReplacement(node.ArgumentPlaceholders[0]); |
1030 | 1071 | RemovePlaceholderReplacement(node.ArgumentPlaceholders[1]); |
1031 | 1072 | RemovePlaceholderReplacement(node.ReceiverPlaceholder); |
1032 | 1073 |
|
1033 | 1074 | return rewrittenIndexerAccess; |
| 1075 | + |
| 1076 | + MethodSymbol? tryGetCorrespondingOneArgumentOverload(WellKnownMember twoArgumentOverloadMember, WellKnownMember oneArgumentOverloadMember, MethodSymbol method, SyntaxNode syntax) |
| 1077 | + { |
| 1078 | + if (!TryGetWellKnownTypeMember(syntax, twoArgumentOverloadMember, out MethodSymbol? twoArgumentOverloadSymbol, isOptional: true) |
| 1079 | + || !method.Equals(twoArgumentOverloadSymbol) |
| 1080 | + || !TryGetWellKnownTypeMember(syntax, oneArgumentOverloadMember, out MethodSymbol? oneArgumentOverloadSymbol, isOptional: true)) |
| 1081 | + { |
| 1082 | + return null; |
| 1083 | + } |
| 1084 | + Debug.Assert(twoArgumentOverloadSymbol.Name == oneArgumentOverloadSymbol.Name |
| 1085 | + && twoArgumentOverloadSymbol.ReturnType.Equals(oneArgumentOverloadSymbol.ReturnType, TypeCompareKind.ConsiderEverything)); |
| 1086 | + return oneArgumentOverloadSymbol; |
| 1087 | + |
| 1088 | + } |
1034 | 1089 | } |
1035 | 1090 |
|
1036 | 1091 | private BoundExpression MakeRangeSize(ref BoundExpression startExpr, BoundExpression endExpr, ArrayBuilder<LocalSymbol> localsBuilder, ArrayBuilder<BoundExpression> sideEffectsBuilder) |
|
0 commit comments