diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
index cbc56794b..81ac1c17a 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/PatternMatching.cs
@@ -310,6 +310,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
+#if CS80
public void RecursivePattern_Type(object x)
{
if (x is X { C: string text })
@@ -322,6 +323,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
+ public void RecursivePattern_Constant(object obj)
+ {
+ if (obj is X { C: null } x)
+ {
+ Console.WriteLine("Test " + x);
+ }
+ else
+ {
+ Console.WriteLine("not Test");
+ }
+ }
+
+#endif
private bool F()
{
return true;
diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index c73084370..feafd7199 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -4546,14 +4546,14 @@ namespace ICSharpCode.Decompiler.CSharp
{
left = left.UnwrapChild(castExpr.Expression);
}
- var right = TranslatePattern(inst);
+ var right = TranslatePattern(inst, left.Type);
return new BinaryOperatorExpression(left, BinaryOperatorType.IsPattern, right)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.Boolean)))
.WithILInstruction(inst);
}
- ExpressionWithILInstruction TranslatePattern(ILInstruction pattern)
+ ExpressionWithILInstruction TranslatePattern(ILInstruction pattern, IType leftHandType)
{
switch (pattern)
{
@@ -4590,7 +4590,7 @@ namespace ICSharpCode.Decompiler.CSharp
continue;
}
recursivePatternExpression.SubPatterns.Add(
- new NamedArgumentExpression { Name = member.Name, Expression = TranslatePattern(subPattern) }
+ new NamedArgumentExpression { Name = member.Name, Expression = TranslatePattern(subPattern, member.ReturnType) }
.WithRR(new MemberResolveResult(null, member))
);
}
@@ -4616,6 +4616,31 @@ namespace ICSharpCode.Decompiler.CSharp
return new TypeReferenceExpression(ConvertType(matchInstruction.Variable.Type))
.WithILInstruction(matchInstruction);
}
+ case Comp comp:
+ var constantValue = Translate(comp.Right, leftHandType);
+ switch (comp.Kind)
+ {
+ case ComparisonKind.Equality:
+ return constantValue
+ .WithILInstruction(comp);
+ case ComparisonKind.Inequality:
+ return new UnaryOperatorExpression(UnaryOperatorType.PatternNot, constantValue)
+ .WithILInstruction(comp);
+ case ComparisonKind.LessThan:
+ return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalLessThan, constantValue)
+ .WithILInstruction(comp);
+ case ComparisonKind.LessThanOrEqual:
+ return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalLessThanOrEqual, constantValue)
+ .WithILInstruction(comp);
+ case ComparisonKind.GreaterThan:
+ return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalGreaterThan, constantValue)
+ .WithILInstruction(comp);
+ case ComparisonKind.GreaterThanOrEqual:
+ return new UnaryOperatorExpression(UnaryOperatorType.PatternRelationalGreaterThanOrEqual, constantValue)
+ .WithILInstruction(comp);
+ default:
+ throw new InvalidOperationException("Unexpected comparison kind: " + comp.Kind);
+ }
default:
throw new NotImplementedException();
}
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
index af925fc14..460f9a421 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
@@ -46,6 +46,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole NullConditionalRole = new TokenRole("?");
public readonly static TokenRole SuppressNullableWarningRole = new TokenRole("!");
public readonly static TokenRole IndexFromEndRole = new TokenRole("^");
+ public readonly static TokenRole PatternNotRole = new TokenRole("not");
public UnaryOperatorExpression()
{
@@ -126,6 +127,16 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return SuppressNullableWarningRole;
case UnaryOperatorType.IndexFromEnd:
return IndexFromEndRole;
+ case UnaryOperatorType.PatternNot:
+ return PatternNotRole;
+ case UnaryOperatorType.PatternRelationalLessThan:
+ return BinaryOperatorExpression.LessThanRole;
+ case UnaryOperatorType.PatternRelationalLessThanOrEqual:
+ return BinaryOperatorExpression.LessThanOrEqualRole;
+ case UnaryOperatorType.PatternRelationalGreaterThan:
+ return BinaryOperatorExpression.GreaterThanRole;
+ case UnaryOperatorType.PatternRelationalGreaterThanOrEqual:
+ return BinaryOperatorExpression.GreaterThanOrEqualRole;
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
}
@@ -156,6 +167,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case UnaryOperatorType.Await:
case UnaryOperatorType.SuppressNullableWarning:
case UnaryOperatorType.IndexFromEnd:
+ case UnaryOperatorType.PatternNot:
+ case UnaryOperatorType.PatternRelationalLessThan:
+ case UnaryOperatorType.PatternRelationalLessThanOrEqual:
+ case UnaryOperatorType.PatternRelationalGreaterThan:
+ case UnaryOperatorType.PatternRelationalGreaterThanOrEqual:
return ExpressionType.Extension;
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
@@ -216,5 +232,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// C# 8 prefix ^ operator
///
IndexFromEnd,
+ ///
+ /// C# 9 not pattern
+ ///
+ PatternNot,
+ ///
+ /// C# 9 relational pattern
+ ///
+ PatternRelationalLessThan,
+ ///
+ /// C# 9 relational pattern
+ ///
+ PatternRelationalLessThanOrEqual,
+ ///
+ /// C# 9 relational pattern
+ ///
+ PatternRelationalGreaterThan,
+ ///
+ /// C# 9 relational pattern
+ ///
+ PatternRelationalGreaterThanOrEqual,
}
}
diff --git a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs
index 7cdc090db..c99a429a8 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs
@@ -183,17 +183,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (trueInst.MatchBranch(out var trueBlock) && trueBlock.IncomingEdgeCount == 1 && trueBlock.Parent == container)
{
- DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, trueBlock, falseInst);
+ DetectPropertySubPatterns((MatchInstruction)ifInst.Condition, trueBlock, falseInst, context);
}
return true;
}
- private bool DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst)
+ private bool DetectPropertySubPatterns(MatchInstruction parentPattern, Block block, ILInstruction parentFalseInst, ILTransformContext context)
{
// if (match.notnull.type[System.String] (V_0 = callvirt get_C(ldloc V_2))) br IL_0022
// br IL_0037
- if (block.Instructions.Count == 2 && block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst))
+ if (MatchBlockContainingOneCondition(block, out var condition, out var trueInst, out var falseInst))
{
if (MatchInstruction.IsPatternMatch(condition, out var operand))
{
@@ -213,17 +213,44 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, falseInst))
{
- return false;
+ if (!DetectExitPoints.CompatibleExitInstruction(parentFalseInst, trueInst))
+ {
+ return false;
+ }
+ ExtensionMethods.Swap(ref trueInst, ref falseInst);
+ condition = Comp.LogicNot(condition);
}
+ context.Step("Move property sub pattern", condition);
parentPattern.SubPatterns.Add(condition);
- block.Instructions.RemoveAt(0);
- block.Instructions[0] = trueInst;
+ block.Instructions.Clear();
+ block.Instructions.Add(trueInst);
return true;
}
}
return false;
}
+ private static bool MatchBlockContainingOneCondition(Block block, [NotNullWhen(true)] out ILInstruction? condition, [NotNullWhen(true)] out ILInstruction? trueInst, [NotNullWhen(true)] out ILInstruction? falseInst)
+ {
+ switch (block.Instructions.Count)
+ {
+ case 2:
+ return block.MatchIfAtEndOfBlock(out condition, out trueInst, out falseInst);
+ case 3:
+ condition = null;
+ if (!block.MatchIfAtEndOfBlock(out var loadTemp, out trueInst, out falseInst))
+ return false;
+ if (!(loadTemp.MatchLdLoc(out var tempVar) && tempVar.IsSingleDefinition && tempVar.LoadCount == 1))
+ return false;
+ return block.Instructions[0].MatchStLoc(tempVar, out condition);
+ default:
+ condition = null;
+ trueInst = null;
+ falseInst = null;
+ return false;
+ }
+ }
+
private bool CheckAllUsesDominatedBy(ILVariable v, BlockContainer container, ILInstruction trueInst,
ILInstruction storeToV, ILInstruction? loadInNullCheck, ILTransformContext context, ref ControlFlowGraph? cfg)
{