Skip to content

Commit

Permalink
Patterns: Expand non-negative sets when performing certain operations
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv committed Feb 6, 2024
1 parent f536ae1 commit 45bd785
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ public IValueSet<T> Complement()

public IValueSet<T> Intersect(IValueSet<T> o)
{
// We use non-negative integers for Count/Length on types that list-patterns can be used on (ie.countable and indexable ones).
// But we need to upgrade them to regular integers to perform operations against full integer sets.
if (this is NumericValueSet<int, NonNegativeIntTC> nonNegativeThis && o is NumericValueSet<int, IntTC>)
{
return ((IValueSet<T>)ExpandToIntegerRange(nonNegativeThis)).Intersect(o);
}
else if (o is NumericValueSet<int, NonNegativeIntTC> nonNegativeOther && this is NumericValueSet<int, IntTC>)
{
return ((IValueSet<T>)ExpandToIntegerRange(nonNegativeOther)).Intersect(this);
}

var other = (NumericValueSet<T, TTC>)o;
TTC tc = default;
var builder = ArrayBuilder<(T first, T last)>.GetInstance();
Expand Down Expand Up @@ -203,6 +214,12 @@ public IValueSet<T> Intersect(IValueSet<T> o)
}

return new NumericValueSet<T, TTC>(builder.ToImmutableAndFree());

}

private static IValueSet<int> ExpandToIntegerRange(NumericValueSet<int, NonNegativeIntTC> nonNegativeThis)
{
return new NumericValueSet<int, IntTC>(nonNegativeThis._intervals);
}

/// <summary>
Expand Down Expand Up @@ -243,6 +260,17 @@ private static T Max(T a, T b)

public IValueSet<T> Union(IValueSet<T> o)
{
// We use non-negative integers for Count/Length on types that list-patterns can be used on (ie.countable and indexable ones).
// But we need to upgrade them to regular integers to perform operations against full integer sets.
if (this is NumericValueSet<int, NonNegativeIntTC> nonNegativeThis && o is NumericValueSet<int, IntTC>)
{
return ((IValueSet<T>)ExpandToIntegerRange(nonNegativeThis)).Union(o);
}
else if (o is NumericValueSet<int, NonNegativeIntTC> nonNegativeOther && this is NumericValueSet<int, IntTC>)
{
return ((IValueSet<T>)ExpandToIntegerRange(nonNegativeOther)).Union(this);
}

var other = (NumericValueSet<T, TTC>)o;
TTC tc = default;
var builder = ArrayBuilder<(T first, T last)>.GetInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8915,4 +8915,190 @@ public void NotExhaustive_LongList()
// _ = a switch { { Length: < 1000 } => 0 };
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 1000 }").WithLocation(2, 7));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71660")]
public void MixedCountPatterns()
{
// One of the property patterns is treated as a non-negative Count pattern,
// while the other is a regular property pattern.
var source = """
using System;
using System.Collections;
using System.Collections.Generic;
System.Console.Write(select(new List<int>()));
System.Console.Write(select(new List<int>() { 42 }));
System.Console.Write(select(new C()));
int select(ICollection c)
{
try
{
return c switch
{
{ Count: 0 } => 0,
IList { Count: > 0 } => 1,
};
}
catch
{
return 2;
}
}
class C : System.Collections.ICollection
{
public int Count => 1;
public object SyncRoot => throw new NotImplementedException();
public bool IsSynchronized => throw new NotImplementedException();
public void CopyTo(Array array, int index) => throw new NotImplementedException();
public IEnumerator GetEnumerator() => throw new NotImplementedException();
}
""";
CompileAndVerify(source, expectedOutput: "012").VerifyDiagnostics(
// (13,18): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered.
// return c switch
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(13, 18)
);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71660")]
public void MixedCountPatterns_ReverseOrder()
{
var source = """
using System;
using System.Collections;
using System.Collections.Generic;
System.Console.Write(select(new List<int>()));
System.Console.Write(select(new List<int>() { 42 }));
System.Console.Write(select(new C()));
int select(ICollection c)
{
try
{
return c switch
{
IList { Count: > 0 } => 1,
{ Count: 0 } => 0,
};
}
catch
{
return 2;
}
}
class C : System.Collections.ICollection
{
public int Count => 1;
public object SyncRoot => throw new NotImplementedException();
public bool IsSynchronized => throw new NotImplementedException();
public void CopyTo(Array array, int index) => throw new NotImplementedException();
public IEnumerator GetEnumerator() => throw new NotImplementedException();
}
""";
CompileAndVerify(source, expectedOutput: "012").VerifyDiagnostics(
// (13,18): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered.
// return c switch
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(13, 18)
);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71660")]
public void MixedCountPatterns_NegativeTest()
{
var source = """
using System.Collections;
using System.Collections.Generic;
var result = (ICollection)new List<int>() switch
{
IList { Count: > 0 } => throw null,
IList { Count: < 0 } => throw null,
{ Count: 0 } => "ran",
};
System.Console.Write(result);
""";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (4,43): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered.
// var result = (ICollection)new List<int>() switch
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(4, 43),
// (7,5): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match.
// IList { Count: < 0 } => throw null,
Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "IList { Count: < 0 }").WithLocation(7, 5)
);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71660")]
public void MixedCountPatterns_NegativeTestAfterRegularPropertyPattern()
{
var source = """
using System.Collections;
using System.Collections.Generic;
_ = (ICollection)new List<int>() switch
{
IList { Count: > 0 } => 0,
{ Count: 0 } => 0,
IList { Count: < 0 } => 0,
};
""";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (4,34): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered.
// _ = (ICollection)new List<int>() switch
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(4, 34),
// (8,5): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match.
// IList { Count: < 0 } => 0,
Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "IList { Count: < 0 }").WithLocation(8, 5)
);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71660")]
public void MixedCountPatterns_ExplicitICollectionType()
{
var source = """
using System;
using System.Collections;
using System.Collections.Generic;
System.Console.Write(select(new List<int>()));
System.Console.Write(select(new List<int>() { 42 }));
System.Console.Write(select(new C()));
int select(ICollection c)
{
try
{
return c switch
{
ICollection { Count: 0 } => 0,
IList { Count: 1 } => 1,
};
}
catch
{
return 2;
}
}
class C : System.Collections.ICollection
{
public int Count => 1;
public object SyncRoot => throw new NotImplementedException();
public bool IsSynchronized => throw new NotImplementedException();
public void CopyTo(Array array, int index) => throw new NotImplementedException();
public IEnumerator GetEnumerator() => throw new NotImplementedException();
}
""";
CompileAndVerify(source, expectedOutput: "012").VerifyDiagnostics(
// (13,18): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Count: 1 }' is not covered.
// return c switch
Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Count: 1 }").WithLocation(13, 18)
);
}
}

0 comments on commit 45bd785

Please sign in to comment.