Skip to content

Commit

Permalink
Standardize list pattern lowering on Index constructor. (#58055)
Browse files Browse the repository at this point in the history
Fixes #57825.
Relevant spec adjustment dotnet/csharplang#5484.
  • Loading branch information
AlekseyTs authored Dec 4, 2021
1 parent 5d5b3a3 commit 358d4ae
Show file tree
Hide file tree
Showing 9 changed files with 437 additions and 347 deletions.
6 changes: 1 addition & 5 deletions src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,11 +374,7 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu

if (!systemIndexType.HasUseSiteError)
{
// Check required well-known member. They may not be needed
// during lowering, but it's simpler to always require them to prevent
// the user from getting surprising errors when optimizations fail
_ = GetWellKnownTypeMember(WellKnownMember.System_Index__op_Implicit_FromInt32, diagnostics, syntax: node);

// Check required well-known member.
_ = GetWellKnownTypeMember(WellKnownMember.System_Index__ctor, diagnostics, syntax: node);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,15 @@ void addArg(RefKind refKind, BoundExpression expression)
BoundExpression makeUnloweredIndexArgument(int index)
{
// LocalRewriter.MakePatternIndexOffsetExpression understands this format
var ctor = (MethodSymbol)_factory.WellKnownMember(WellKnownMember.System_Index__ctor);

if (index < 0)
{
var ctor = (MethodSymbol)_factory.WellKnownMember(WellKnownMember.System_Index__ctor);
return new BoundFromEndIndexExpression(_factory.Syntax, _factory.Literal(-index),
methodOpt: ctor, _factory.WellKnownType(WellKnownType.System_Index));
}

return _factory.Convert(_factory.WellKnownType(WellKnownType.System_Index), _factory.Literal(index));
return _factory.New(ctor, _factory.Literal(index), _factory.Literal(false));
}

BoundExpression makeUnloweredRangeArgument(BoundDagSliceEvaluation e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,24 @@ private BoundExpression DetermineMakePatternIndexOffsetExpressionStrategy(
strategy = PatternIndexOffsetLoweringStrategy.UseAsIs;
return VisitExpression(operand);
}
else if (unloweredExpr is BoundObjectCreationExpression { Constructor: MethodSymbol constructor, Arguments: { Length: 2 } arguments, ArgsToParamsOpt: { IsDefaultOrEmpty: true }, InitializerExpressionOpt: null } &&
(object)constructor == _compilation.GetWellKnownTypeMember(WellKnownMember.System_Index__ctor) &&
arguments[0] is { Type.SpecialType: SpecialType.System_Int32, ConstantValue.Value: int _ and >= 0 } index &&
arguments[1] is { Type.SpecialType: SpecialType.System_Boolean, ConstantValue.Value: bool fromEnd })
{
if (fromEnd)
{
// We can replace the `argument.GetOffset(length)` call with `length - index`
strategy = PatternIndexOffsetLoweringStrategy.SubtractFromLength;
}
else
{
// We can return the int directly
strategy = PatternIndexOffsetLoweringStrategy.UseAsIs;
}

return VisitExpression(index);
}
else
{
// `argument.GetOffset(length)`
Expand Down
185 changes: 131 additions & 54 deletions src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1632,44 +1632,117 @@ class C
public static void Main()
{
var s = ""abcdef"";
Console.WriteLine(s[new Index(1, false)]);
int i = 1;
Console.WriteLine(s[new Index(i, false)]);
Console.WriteLine(s[(Index)2]);
Console.WriteLine(s[^1]);
Console.WriteLine(s[new Index(3, false)]);
Console.WriteLine(s[new Index(2, true)]);
i = 6;
Console.WriteLine(s[new Index(i, true)]);
Console.WriteLine(s[new Index(value: 2, fromEnd: false)]);
Console.WriteLine(s[new Index(fromEnd: true, value: 3)]);
Console.WriteLine(s[new Index(3, false) {}]);
}
}", expectedOutput: @"b
c
f");
f
d
e
a
c
d
d");
verifier.VerifyIL("C.Main", @"
{
// Code size 70 (0x46)
// Code size 219 (0xdb)
.maxstack 4
.locals init (string V_0,
System.Index V_1)
.locals init (int V_0, //i
string V_1,
System.Index V_2)
IL_0000: ldstr ""abcdef""
IL_0005: dup
IL_0005: ldc.i4.1
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: ldc.i4.0
IL_000a: newobj ""System.Index..ctor(int, bool)""
IL_000f: stloc.1
IL_0010: ldloca.s V_1
IL_0012: ldloc.0
IL_0013: callvirt ""int string.Length.get""
IL_0018: call ""int System.Index.GetOffset(int)""
IL_001d: callvirt ""char string.this[int].get""
IL_0022: call ""void System.Console.WriteLine(char)""
IL_0027: dup
IL_0028: ldc.i4.2
IL_0029: callvirt ""char string.this[int].get""
IL_002e: call ""void System.Console.WriteLine(char)""
IL_0033: dup
IL_0034: callvirt ""int string.Length.get""
IL_0039: ldc.i4.1
IL_003a: sub
IL_003b: callvirt ""char string.this[int].get""
IL_0040: call ""void System.Console.WriteLine(char)""
IL_0045: ret
IL_0007: dup
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000a: ldloc.0
IL_000b: ldc.i4.0
IL_000c: newobj ""System.Index..ctor(int, bool)""
IL_0011: stloc.2
IL_0012: ldloca.s V_2
IL_0014: ldloc.1
IL_0015: callvirt ""int string.Length.get""
IL_001a: call ""int System.Index.GetOffset(int)""
IL_001f: callvirt ""char string.this[int].get""
IL_0024: call ""void System.Console.WriteLine(char)""
IL_0029: dup
IL_002a: ldc.i4.2
IL_002b: callvirt ""char string.this[int].get""
IL_0030: call ""void System.Console.WriteLine(char)""
IL_0035: dup
IL_0036: dup
IL_0037: callvirt ""int string.Length.get""
IL_003c: ldc.i4.1
IL_003d: sub
IL_003e: callvirt ""char string.this[int].get""
IL_0043: call ""void System.Console.WriteLine(char)""
IL_0048: dup
IL_0049: ldc.i4.3
IL_004a: callvirt ""char string.this[int].get""
IL_004f: call ""void System.Console.WriteLine(char)""
IL_0054: dup
IL_0055: dup
IL_0056: callvirt ""int string.Length.get""
IL_005b: ldc.i4.2
IL_005c: sub
IL_005d: callvirt ""char string.this[int].get""
IL_0062: call ""void System.Console.WriteLine(char)""
IL_0067: ldc.i4.6
IL_0068: stloc.0
IL_0069: dup
IL_006a: stloc.1
IL_006b: ldloc.1
IL_006c: ldloc.0
IL_006d: ldc.i4.1
IL_006e: newobj ""System.Index..ctor(int, bool)""
IL_0073: stloc.2
IL_0074: ldloca.s V_2
IL_0076: ldloc.1
IL_0077: callvirt ""int string.Length.get""
IL_007c: call ""int System.Index.GetOffset(int)""
IL_0081: callvirt ""char string.this[int].get""
IL_0086: call ""void System.Console.WriteLine(char)""
IL_008b: dup
IL_008c: ldc.i4.2
IL_008d: callvirt ""char string.this[int].get""
IL_0092: call ""void System.Console.WriteLine(char)""
IL_0097: dup
IL_0098: stloc.1
IL_0099: ldloc.1
IL_009a: ldc.i4.3
IL_009b: ldc.i4.1
IL_009c: newobj ""System.Index..ctor(int, bool)""
IL_00a1: stloc.2
IL_00a2: ldloca.s V_2
IL_00a4: ldloc.1
IL_00a5: callvirt ""int string.Length.get""
IL_00aa: call ""int System.Index.GetOffset(int)""
IL_00af: callvirt ""char string.this[int].get""
IL_00b4: call ""void System.Console.WriteLine(char)""
IL_00b9: stloc.1
IL_00ba: ldloc.1
IL_00bb: ldc.i4.3
IL_00bc: ldc.i4.0
IL_00bd: newobj ""System.Index..ctor(int, bool)""
IL_00c2: stloc.2
IL_00c3: ldloca.s V_2
IL_00c5: ldloc.1
IL_00c6: callvirt ""int string.Length.get""
IL_00cb: call ""int System.Index.GetOffset(int)""
IL_00d0: callvirt ""char string.this[int].get""
IL_00d5: call ""void System.Console.WriteLine(char)""
IL_00da: ret
}
");
}
Expand Down Expand Up @@ -1819,41 +1892,45 @@ public static void Main()
public static void M(int[] array)
{
Console.WriteLine(array[new Index(1, false)]);
bool fromEnd = false;
Console.WriteLine(array[new Index(1, fromEnd)]);
Console.WriteLine(array[^1]);
}
}", TestOptions.ReleaseExe);
var verifier = CompileAndVerify(comp, expectedOutput: @"2
11");
verifier.VerifyIL("C.M", @"
{
// Code size 40 (0x28)
// Code size 42 (0x2a)
.maxstack 3
.locals init (int[] V_0,
System.Index V_1)
IL_0000: ldarg.0
.locals init (bool V_0, //fromEnd
int[] V_1,
System.Index V_2)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: ldc.i4.1
IL_0004: ldc.i4.0
IL_0005: newobj ""System.Index..ctor(int, bool)""
IL_000a: stloc.1
IL_000b: ldloca.s V_1
IL_000d: ldloc.0
IL_000e: ldlen
IL_000f: conv.i4
IL_0010: call ""int System.Index.GetOffset(int)""
IL_0015: ldelem.i4
IL_0016: call ""void System.Console.WriteLine(int)""
IL_001b: ldarg.0
IL_001c: dup
IL_001d: ldlen
IL_001e: conv.i4
IL_001f: ldc.i4.1
IL_0020: sub
IL_0021: ldelem.i4
IL_0022: call ""void System.Console.WriteLine(int)""
IL_0027: ret
IL_0002: ldarg.0
IL_0003: stloc.1
IL_0004: ldloc.1
IL_0005: ldc.i4.1
IL_0006: ldloc.0
IL_0007: newobj ""System.Index..ctor(int, bool)""
IL_000c: stloc.2
IL_000d: ldloca.s V_2
IL_000f: ldloc.1
IL_0010: ldlen
IL_0011: conv.i4
IL_0012: call ""int System.Index.GetOffset(int)""
IL_0017: ldelem.i4
IL_0018: call ""void System.Console.WriteLine(int)""
IL_001d: ldarg.0
IL_001e: dup
IL_001f: ldlen
IL_0020: conv.i4
IL_0021: ldc.i4.1
IL_0022: sub
IL_0023: ldelem.i4
IL_0024: call ""void System.Console.WriteLine(int)""
IL_0029: ret
}");
}

Expand Down
Loading

0 comments on commit 358d4ae

Please sign in to comment.