Skip to content

Commit 6c4313b

Browse files
committed
Pattern matching: Ensure indexer types match when indexer evaluations are checked for equivalence
Fixes #82398.
1 parent 76ad580 commit 6c4313b

File tree

2 files changed

+112
-2
lines changed

2 files changed

+112
-2
lines changed

src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,7 @@ private static bool IsSameEntity(BoundDagTemp input1, BoundDagTemp input2)
16591659
// Even though the two tests appear unrelated (with different inputs),
16601660
// it is possible that they are in fact related under certain conditions.
16611661
// For instance, the inputs [0] and [^1] point to the same element when length is 1.
1662-
case (BoundDagIndexerEvaluation s1, BoundDagIndexerEvaluation s2):
1662+
case (BoundDagIndexerEvaluation s1, BoundDagIndexerEvaluation s2) when s1.IndexerType.Equals(s2.IndexerType, TypeCompareKind.AllIgnoreOptions):
16631663
// Take the top-level input and normalize indices to account for indexer accesses inside a slice.
16641664
// For instance [0] in nested list pattern [ 0, ..[$$], 2 ] refers to [1] in the containing list.
16651665
(s1Input, BoundDagTemp s1LengthTemp, int s1Index) = GetCanonicalInput(s1);
@@ -1716,7 +1716,7 @@ internal static bool IsEqualEvaluation(BoundDagEvaluation? s1Source, BoundDagEva
17161716
// Even though the two tests appear unrelated (with different inputs),
17171717
// it is possible that they are in fact related under certain conditions.
17181718
// For instance, the inputs [0] and [^1] point to the same element when length is 1.
1719-
case (BoundDagIndexerEvaluation s1, BoundDagIndexerEvaluation s2):
1719+
case (BoundDagIndexerEvaluation s1, BoundDagIndexerEvaluation s2) when s1.IndexerType.Equals(s2.IndexerType, TypeCompareKind.AllIgnoreOptions):
17201720
// Take the top-level input and normalize indices to account for indexer accesses inside a slice.
17211721
// For instance [0] in nested list pattern [ 0, ..[$$], 2 ] refers to [1] in the containing list.
17221722
(BoundDagTemp s1Input, BoundDagTemp s1LengthTemp, int s1Index) = GetCanonicalInput(s1);

src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_ListPatterns.cs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9633,4 +9633,114 @@ void M()
96339633
Patterns (0)
96349634
""");
96359635
}
9636+
9637+
[Fact]
9638+
[WorkItem("https://github.com/dotnet/roslyn/issues/82398")]
9639+
public void IndexerEvaluation_DifferentResultTypes()
9640+
{
9641+
var source = """
9642+
using System;
9643+
9644+
class CollectionA
9645+
{
9646+
public int Length => 3;
9647+
public int this[int i] => 101;
9648+
public CollectionB Slice(int start, int length) => new CollectionB();
9649+
}
9650+
9651+
class CollectionB
9652+
{
9653+
public int Length => 1;
9654+
public string this[int i] => "102";
9655+
}
9656+
9657+
class Program
9658+
{
9659+
static bool Test(CollectionA a1)
9660+
{
9661+
return a1 is [100, ..] or [.. ["102", ..]];
9662+
}
9663+
9664+
static void Main()
9665+
{
9666+
var a1 = new CollectionA();
9667+
Console.WriteLine(Test(a1));
9668+
}
9669+
}
9670+
""";
9671+
9672+
var compilation = CreateCompilationWithIndexAndRange(source, options: TestOptions.DebugExe);
9673+
9674+
VerifyDecisionDagDump<IsPatternExpressionSyntax>(compilation,
9675+
@"[0]: t0 != null ? [1] : [10]
9676+
[1]: t1 = t0.Length; [2]
9677+
[2]: t1 >= 1 ? [3] : [10]
9678+
[3]: t2 = t0[0]; [4]
9679+
[4]: t2 == 100 ? [9] : [5]
9680+
[5]: t3 = DagSliceEvaluation(t0); [6]
9681+
[6]: t4 = t3.Length; [7]
9682+
[7]: t5 = t3[0]; [8]
9683+
[8]: t5 == ""102"" ? [9] : [10]
9684+
[9]: leaf <isPatternSuccess> `[100, ..] or [.. [""102"", ..]]`
9685+
[10]: leaf <isPatternFailure> `[100, ..] or [.. [""102"", ..]]`
9686+
");
9687+
9688+
var verifier = CompileAndVerify(compilation, expectedOutput: "True").VerifyDiagnostics();
9689+
9690+
verifier.VerifyIL("Program.Test", @"
9691+
{
9692+
// Code size 84 (0x54)
9693+
.maxstack 3
9694+
.locals init (int V_0,
9695+
int V_1,
9696+
CollectionB V_2,
9697+
string V_3,
9698+
bool V_4,
9699+
bool V_5)
9700+
IL_0000: nop
9701+
IL_0001: ldarg.0
9702+
IL_0002: brfalse.s IL_0048
9703+
IL_0004: ldarg.0
9704+
IL_0005: callvirt ""int CollectionA.Length.get""
9705+
IL_000a: stloc.0
9706+
IL_000b: ldloc.0
9707+
IL_000c: ldc.i4.1
9708+
IL_000d: blt.s IL_0048
9709+
IL_000f: ldarg.0
9710+
IL_0010: ldc.i4.0
9711+
IL_0011: callvirt ""int CollectionA.this[int].get""
9712+
IL_0016: stloc.1
9713+
IL_0017: ldloc.1
9714+
IL_0018: ldc.i4.s 100
9715+
IL_001a: beq.s IL_0043
9716+
IL_001c: ldarg.0
9717+
IL_001d: ldc.i4.0
9718+
IL_001e: ldloc.0
9719+
IL_001f: callvirt ""CollectionB CollectionA.Slice(int, int)""
9720+
IL_0024: stloc.2
9721+
IL_0025: ldloc.2
9722+
IL_0026: callvirt ""int CollectionB.Length.get""
9723+
IL_002b: pop
9724+
IL_002c: ldloc.2
9725+
IL_002d: ldc.i4.0
9726+
IL_002e: callvirt ""string CollectionB.this[int].get""
9727+
IL_0033: stloc.3
9728+
IL_0034: ldloc.3
9729+
IL_0035: ldstr ""102""
9730+
IL_003a: call ""bool string.op_Equality(string, string)""
9731+
IL_003f: brtrue.s IL_0043
9732+
IL_0041: br.s IL_0048
9733+
IL_0043: ldc.i4.1
9734+
IL_0044: stloc.s V_4
9735+
IL_0046: br.s IL_004b
9736+
IL_0048: ldc.i4.0
9737+
IL_0049: stloc.s V_4
9738+
IL_004b: ldloc.s V_4
9739+
IL_004d: stloc.s V_5
9740+
IL_004f: br.s IL_0051
9741+
IL_0051: ldloc.s V_5
9742+
IL_0053: ret
9743+
}
9744+
");
9745+
}
96369746
}

0 commit comments

Comments
 (0)