From 02a09423c9ea63bf377caf7acdb9871b12112d4f Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Sun, 7 May 2017 20:57:24 -0700 Subject: [PATCH] Relax conversion requirements for pattern-matching involving type parameters. (#18784) Fixes #16195 This is a language change for 7.1. See https://github.com/dotnet/csharplang/issues/154. --- .../CSharp/Portable/Binder/Binder_Patterns.cs | 22 +- .../Binder/SubsumptionDiagnosticBuilder.cs | 3 +- .../CSharp/Portable/BoundTree/DecisionTree.cs | 4 +- .../Portable/BoundTree/DecisionTreeBuilder.cs | 31 +- .../Portable/CSharpResources.Designer.cs | 11 +- .../CSharp/Portable/CSharpResources.resx | 5 +- .../CSharp/Portable/Errors/ErrorCode.cs | 5 + .../CSharp/Portable/Errors/MessageID.cs | 2 + .../LocalRewriter_PatternSwitchStatement.cs | 5 +- .../LocalRewriter/LocalRewriter_Patterns.cs | 94 +- .../Lowering/SyntheticBoundNodeFactory.cs | 5 + .../CSharp/Test/Emit/CodeGen/SwitchTests.cs | 1223 ++++++++++++++--- .../CSharp/Test/Emit/PDB/PDBTests.cs | 203 ++- .../Semantics/PatternMatchingTests.cs | 221 +++ .../Semantic/Semantics/PatternSwitchTests.cs | 115 ++ .../Test/Utilities/CSharp/TestOptions.cs | 1 + .../UpgradeProject/UpgradeProjectTests.cs | 19 + .../CSharpUpgradeProjectCodeFixProvider.cs | 3 +- 18 files changed, 1597 insertions(+), 375 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index ea035f8778d3c..7a0a35e4dd9b9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -197,8 +197,26 @@ private bool CheckValidPatternType( //case ConversionKind.ImplicitConstant: //case ConversionKind.ImplicitNumeric: default: - Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); - return true; + if (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter()) + { + LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion(); + if (requiredVersion > Compilation.LanguageVersion) + { + Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax, + operandType, patternType, + Compilation.LanguageVersion.ToDisplayString(), + new CSharpRequiredLanguageVersion(requiredVersion)); + return true; + } + + // permit pattern-matching when one of the types is an open type in C# 7.1. + break; + } + else + { + Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); + return true; + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs b/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs index 96d57e2e17119..f1695a0bda40c 100644 --- a/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SubsumptionDiagnosticBuilder.cs @@ -52,7 +52,8 @@ internal bool AddLabel(BoundPatternSwitchLabel label, DiagnosticBag diagnostics, // For purposes of subsumption, we do not take into consideration the value // of the input expression. Therefore we consider null possible if the type permits. Syntax = label.Syntax; - var subsumedErrorCode = CheckSubsumed(label.Pattern, _subsumptionTree, inputCouldBeNull: true); + var inputCouldBeNull = _subsumptionTree.Type.CanContainNull(); + var subsumedErrorCode = CheckSubsumed(label.Pattern, _subsumptionTree, inputCouldBeNull: inputCouldBeNull); if (subsumedErrorCode != 0 && subsumedErrorCode != ErrorCode.ERR_NoImplicitConvCast) { if (!label.HasErrors) diff --git a/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs b/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs index f1c1244c2ba44..e030bc16cb558 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/DecisionTree.cs @@ -93,7 +93,7 @@ public static DecisionTree Create(BoundExpression expression, TypeSymbol type, S expression = new BoundLocal(expression.Syntax, temp, null, type); } - if (expression.Type.CanContainNull()) + if (type.CanContainNull() || type.SpecialType == SpecialType.None) { // We need the ByType decision tree to separate null from non-null values. // Note that, for the purpose of the decision tree (and subsumption), we @@ -104,8 +104,6 @@ public static DecisionTree Create(BoundExpression expression, TypeSymbol type, S else { // If it is a (e.g. builtin) value type, we can switch on its (constant) values. - // If it isn't a builtin, in practice we will only use the Default part of the - // ByValue. return new ByValue(expression, type, temp); } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs b/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs index c2db5dbcacff4..6e72bb9566008 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/DecisionTreeBuilder.cs @@ -130,7 +130,7 @@ private DecisionTree AddByValue(DecisionTree.Guarded guarded, BoundConstantPatte private DecisionTree AddByValue(DecisionTree.ByValue byValue, BoundConstantPattern value, DecisionMaker makeDecision) { - Debug.Assert(value.Value.Type == byValue.Type); + Debug.Assert(value.Value.Type.Equals(byValue.Type, TypeCompareKind.IgnoreDynamicAndTupleNames)); if (byValue.Default != null) { return AddByValue(byValue.Default, value, makeDecision); @@ -221,7 +221,7 @@ private DecisionTree AddByValue(DecisionTree.ByType byType, BoundConstantPattern var kvp = byType.TypeAndDecision[i]; var matchedType = kvp.Key; var decision = kvp.Value; - if (matchedType.TupleUnderlyingTypeOrSelf() == value.Value.Type.TupleUnderlyingTypeOrSelf()) + if (matchedType.Equals(value.Value.Type, TypeCompareKind.IgnoreDynamicAndTupleNames)) { forType = decision; break; @@ -260,21 +260,30 @@ private DecisionTree AddByType(DecisionTree decision, TypeSymbol type, DecisionM case DecisionTree.DecisionKind.ByValue: { var byValue = (DecisionTree.ByValue)decision; + DecisionTree result; if (byValue.Default == null) { - byValue.Default = makeDecision(byValue.Expression, byValue.Type); - if (byValue.Default.MatchIsComplete) + if (byValue.Type.Equals(type, TypeCompareKind.IgnoreDynamicAndTupleNames)) { - byValue.MatchIsComplete = true; + result = byValue.Default = makeDecision(byValue.Expression, byValue.Type); + } + else + { + byValue.Default = new DecisionTree.ByType(byValue.Expression, byValue.Type, null); + result = AddByType(byValue.Default, type, makeDecision); } - - return byValue.Default; } else { - Debug.Assert(byValue.Default.Type == type); - return Add(byValue.Default, makeDecision); + result = AddByType(byValue.Default, type, makeDecision); } + + if (byValue.Default.MatchIsComplete) + { + byValue.MatchIsComplete = true; + } + + return result; } case DecisionTree.DecisionKind.Guarded: return AddByType((DecisionTree.Guarded)decision, type, makeDecision); @@ -321,7 +330,7 @@ private DecisionTree AddByType(DecisionTree.ByType byType, TypeSymbol type, Deci if (byType.TypeAndDecision.Count != 0) { var lastTypeAndDecision = byType.TypeAndDecision.Last(); - if (lastTypeAndDecision.Key.TupleUnderlyingTypeOrSelf() == type.TupleUnderlyingTypeOrSelf()) + if (lastTypeAndDecision.Key.Equals(type, TypeCompareKind.IgnoreDynamicAndTupleNames)) { result = Add(lastTypeAndDecision.Value, makeDecision); } @@ -533,7 +542,7 @@ private DecisionTree Add(DecisionTree.ByType byType, DecisionMaker makeDecision) TypeSymbol patternType, ref HashSet useSiteDiagnostics) { - if (expressionType == patternType) + if ((object)expressionType == (object)patternType) { return true; } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 0431b81ebdfb2..7a1975a98f5f2 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -7604,7 +7604,16 @@ internal static string ERR_PatternNullableType { } /// - /// Looks up a localized string similar to An expression of type {0} cannot be handled by a pattern of type {1}.. + /// Looks up a localized string similar to An expression of type '{0}' cannot be handled by a pattern of type '{1}' in C# {2}. Please use language version {3} or greater.. + /// + internal static string ERR_PatternWrongGenericTypeInVersion { + get { + return ResourceManager.GetString("ERR_PatternWrongGenericTypeInVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An expression of type '{0}' cannot be handled by a pattern of type '{1}'.. /// internal static string ERR_PatternWrongType { get { diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 2803be5762032..9227afe9d86e8 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4891,7 +4891,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The switch case has already been handled by a previous case. - An expression of type {0} cannot be handled by a pattern of type {1}. + An expression of type '{0}' cannot be handled by a pattern of type '{1}'. Attribute '{0}' is ignored when public signing is specified. @@ -5070,4 +5070,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A tuple may not contain a value of type 'void'. + + An expression of type '{0}' cannot be handled by a pattern of type '{1}' in C# {2}. Please use language version {3} or greater. + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 52951edaf9ae1..40cbe2de7be99 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1476,8 +1476,13 @@ internal enum ErrorCode WRN_Experimental = 8305, ERR_TupleInferredNamesNotAvailable = 8306, + #region diagnostics for C# 7.1 + ERR_BadDynamicMethodArgDefaultLiteral = 9000, ERR_DefaultLiteralNotValid = 9001, WRN_DefaultInSwitch = 9002, + ERR_PatternWrongGenericTypeInVersion = 9003, + + #endregion diagnostics for C# 7.1 } } diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index c80e0e7c7a27c..78d7b2bd52e0a 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -129,6 +129,7 @@ internal enum MessageID IDS_ThrowExpression = MessageBase + 12717, IDS_FeatureDefaultLiteral = MessageBase + 12718, IDS_FeatureInferredTupleNames = MessageBase + 12719, + IDS_FeatureGenericPatternMatching = MessageBase + 12720, } // Message IDs may refer to strings that need to be localized. @@ -188,6 +189,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // C# 7.1 features. case MessageID.IDS_FeatureDefaultLiteral: case MessageID.IDS_FeatureInferredTupleNames: + case MessageID.IDS_FeatureGenericPatternMatching: return LanguageVersion.CSharp7_1; // C# 7 features. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs index 4905069c78f0b..f54cbd709d3bb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PatternSwitchStatement.cs @@ -177,7 +177,7 @@ private DecisionTree LowerToDecisionTree( } } - if (defaultLabel != null) + if (defaultLabel != null && !loweredDecisionTree.MatchIsComplete) { Add(loweredDecisionTree, (e, t) => new DecisionTree.Guarded(loweredExpression, loweredExpression.Type, default(ImmutableArray>), defaultSection, null, defaultLabel)); } @@ -219,7 +219,8 @@ private void LowerDecisionTree(BoundExpression expression, DecisionTree decision // Store the input expression into a temp if (decisionTree.Expression != expression) { - _loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, expression)); + var convertedExpression = _factory.Convert(decisionTree.Expression.Type, expression); + _loweredDecisionTree.Add(_factory.Assignment(decisionTree.Expression, convertedExpression)); } if (_declaredTempSet.Add(decisionTree.Temp)) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs index 5bb54c41658cc..9f4b5eeaf3b58 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Patterns.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Roslyn.Utilities; using System.Collections.Immutable; +using System.Collections.Generic; namespace Microsoft.CodeAnalysis.CSharp { @@ -80,7 +81,7 @@ private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern lowered return result; } - Debug.Assert((object)loweredPattern.Variable != null && loweredInput.Type == loweredPattern.Variable.GetTypeOrReturnType()); + Debug.Assert((object)loweredPattern.Variable != null && loweredInput.Type.Equals(loweredPattern.Variable.GetTypeOrReturnType(), TypeCompareKind.IgnoreDynamicAndTupleNames)); var assignment = _factory.AssignmentExpression(loweredPattern.VariableAccess, loweredInput); return _factory.MakeSequence(assignment, result); @@ -93,10 +94,10 @@ private BoundExpression MakeIsDeclarationPattern(BoundDeclarationPattern lowered return _factory.Sequence(ImmutableArray.Create(temp), sideEffects: ImmutableArray.Empty, - result: MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, discard, requiresNullTest: true)); + result: MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, discard, requiresNullTest: loweredInput.Type.CanContainNull())); } - return MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, loweredPattern.VariableAccess, requiresNullTest: true); + return MakeIsDeclarationPattern(loweredPattern.Syntax, loweredInput, loweredPattern.VariableAccess, requiresNullTest: loweredInput.Type.CanContainNull()); } /// @@ -141,13 +142,36 @@ private BoundExpression CompareWithConstant(BoundExpression input, BoundExpressi ); } + private bool MatchIsIrrefutable(TypeSymbol sourceType, TypeSymbol targetType, bool requiredNullTest) + { + // use site diagnostics will already have been reported during binding. + HashSet ignoredDiagnostics = null; + switch (_compilation.Conversions.ClassifyBuiltInConversion(sourceType, targetType, ref ignoredDiagnostics).Kind) + { + case ConversionKind.Boxing: + case ConversionKind.ImplicitReference: + case ConversionKind.Identity: + return true; + default: + return false; + } + } + BoundExpression MakeIsDeclarationPattern(SyntaxNode syntax, BoundExpression loweredInput, BoundExpression loweredTarget, bool requiresNullTest) { var type = loweredTarget.Type; + requiresNullTest = requiresNullTest && loweredInput.Type.CanContainNull(); - // The type here is not a Nullable instance type, as that would have led to the semantic error: - // ERR_PatternNullableType: It is not legal to use nullable type '{0}' in a pattern; use the underlying type '{1}' instead. - Debug.Assert(!type.IsNullableType()); + // It is possible that the input value is already of the correct type, in which case the pattern + // is irrefutable, and we can just do the assignment and return true (or perform the null test). + if (MatchIsIrrefutable(loweredInput.Type, loweredTarget.Type, requiresNullTest)) + { + var convertedInput = _factory.Convert(loweredTarget.Type, loweredInput); + var assignment = _factory.AssignmentExpression(loweredTarget, convertedInput); + return requiresNullTest + ? _factory.ObjectNotEqual(assignment, _factory.Null(type)) + : _factory.MakeSequence(assignment, _factory.Literal(true)); + } // a pattern match of the form "expression is Type identifier" is equivalent to // an invocation of one of these helpers: @@ -158,31 +182,15 @@ BoundExpression MakeIsDeclarationPattern(SyntaxNode syntax, BoundExpression lowe // t = e as T; // return t != null; // } - if (loweredInput.Type == type) - { - // CONSIDER: this can be done whenever input.Type is a subtype of type for improved code - var assignment = _factory.AssignmentExpression(loweredTarget, loweredInput); - return requiresNullTest - ? _factory.ObjectNotEqual(assignment, _factory.Null(type)) - : _factory.MakeSequence(assignment, _factory.Literal(true)); - } - else - { - return _factory.ObjectNotEqual( - _factory.AssignmentExpression(loweredTarget, _factory.As(loweredInput, type)), - _factory.Null(type)); - } + return _factory.ObjectNotEqual( + _factory.AssignmentExpression(loweredTarget, _factory.As(loweredInput, type)), + _factory.Null(type)); } else if (type.IsValueType) { - // It is possible that the input value is already of the correct type, in which case the pattern - // is irrefutable, and we can just do the assignment and return true. - if (loweredInput.Type == type) - { - return _factory.MakeSequence( - _factory.AssignmentExpression(loweredTarget, loweredInput), - _factory.Literal(true)); - } + // The type here is not a Nullable instance type, as that would have led to the semantic error: + // ERR_PatternNullableType: It is not legal to use nullable type '{0}' in a pattern; use the underlying type '{1}' instead. + Debug.Assert(!type.IsNullableType()); // It may be possible to improve this code by only assigning t when returning // true (avoid returning a new default value) @@ -207,18 +215,28 @@ BoundExpression MakeIsDeclarationPattern(SyntaxNode syntax, BoundExpression lowe Debug.Assert(type.IsTypeParameter()); // bool Is(this object i, out T o) // { - // // inefficient because it performs the type test twice. - // bool s = i is T; - // if (s) o = (T)i; + // // inefficient because it performs the type test twice, and also because it boxes the input. + // bool s; + // o = (s = i is T) ? (T)i : default(T); // return s; // } - return _factory.Conditional(_factory.Is(loweredInput, type), - _factory.MakeSequence(_factory.AssignmentExpression( - loweredTarget, - _factory.Convert(type, loweredInput)), - _factory.Literal(true)), - _factory.Literal(false), - _factory.SpecialType(SpecialType.System_Boolean)); + + // Because a cast involving a type parameter is not necessarily a valid conversion (or, if it is, it might not + // be of a kind appropriate for pattern-matching), we use `object` as an intermediate type for the input expression. + var tmpType = _factory.SpecialType(SpecialType.System_Object); + var s = _factory.SynthesizedLocal(_factory.SpecialType(SpecialType.System_Boolean), syntax); + var i = _factory.SynthesizedLocal(tmpType, syntax); // we copy the input to avoid double evaluation + return _factory.Sequence( + ImmutableArray.Create(s, i), + ImmutableArray.Create( + _factory.AssignmentExpression(_factory.Local(i), _factory.Convert(tmpType, loweredInput)), + _factory.AssignmentExpression(loweredTarget, _factory.Conditional( + _factory.AssignmentExpression(_factory.Local(s), _factory.Is(_factory.Local(i), type)), + _factory.Convert(type, _factory.Local(i)), + _factory.Default(type), type)) + ), + _factory.Local(s) + ); } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 7910d872db4dd..3f4fe16167ae4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -1102,6 +1102,11 @@ private MethodSymbol GetFieldFromHandleMethod(NamedTypeSymbol fieldContainer) public BoundExpression Convert(TypeSymbol type, BoundExpression arg) { + if (type == arg.Type) + { + return arg; + } + HashSet useSiteDiagnostics = null; Conversion c = Compilation.Conversions.ClassifyConversionFromExpression(arg, type, ref useSiteDiagnostics); Debug.Assert(c.Exists); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs index cb76663d2d41c..39c1c7f56c87b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/SwitchTests.cs @@ -7637,12 +7637,15 @@ public static void Main() expectedOutput: "RemoveEmptyEntries"); compVerifier.VerifyIL("Program.Main", @"{ - // Code size 12 (0xc) + // Code size 14 (0xe) .maxstack 1 + .locals init (object V_0) IL_0000: ldc.i4.1 IL_0001: box ""System.StringSplitOptions"" - IL_0006: call ""void System.Console.WriteLine(object)"" - IL_000b: ret + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""void System.Console.WriteLine(object)"" + IL_000d: ret }" ); compVerifier = CompileAndVerify(source, @@ -7650,20 +7653,23 @@ .maxstack 1 expectedOutput: "RemoveEmptyEntries"); compVerifier.VerifyIL("Program.Main", @"{ - // Code size 22 (0x16) + // Code size 24 (0x18) .maxstack 1 - .locals init (object V_0) //o + .locals init (object V_0, + object V_1) //o IL_0000: nop - IL_0001: br.s IL_0003 - IL_0003: ldc.i4.1 - IL_0004: box ""System.StringSplitOptions"" - IL_0009: stloc.0 - IL_000a: br.s IL_000c - IL_000c: ldloc.0 - IL_000d: call ""void System.Console.WriteLine(object)"" - IL_0012: nop - IL_0013: br.s IL_0015 - IL_0015: ret + IL_0001: ldc.i4.1 + IL_0002: box ""System.StringSplitOptions"" + IL_0007: stloc.0 + IL_0008: br.s IL_000a + IL_000a: ldloc.0 + IL_000b: stloc.1 + IL_000c: br.s IL_000e + IL_000e: ldloc.1 + IL_000f: call ""void System.Console.WriteLine(object)"" + IL_0014: nop + IL_0015: br.s IL_0017 + IL_0017: ret }" ); } @@ -7799,12 +7805,15 @@ public static void M(int x) expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 12 (0xc) + // Code size 14 (0xe) .maxstack 1 + .locals init (System.IComparable V_0) IL_0000: ldarg.0 IL_0001: box ""int"" - IL_0006: call ""void System.Console.Write(object)"" - IL_000b: ret + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""void System.Console.Write(object)"" + IL_000d: ret }" ); compVerifier = CompileAndVerify(source, @@ -7812,29 +7821,32 @@ .maxstack 1 expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 28 (0x1c) + // Code size 32 (0x20) .maxstack 1 .locals init (int V_0, - int V_1, - System.IComparable V_2, //i - int V_3) + System.IComparable V_1, + int V_2, + System.IComparable V_3, //i + int V_4) IL_0000: nop IL_0001: ldarg.0 - IL_0002: stloc.1 - IL_0003: ldloc.1 - IL_0004: stloc.3 - IL_0005: ldloc.3 - IL_0006: stloc.0 - IL_0007: br.s IL_0009 + IL_0002: stloc.2 + IL_0003: ldloc.2 + IL_0004: stloc.s V_4 + IL_0006: ldloc.s V_4 + IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: box ""int"" - IL_000f: stloc.2 + IL_000f: stloc.1 IL_0010: br.s IL_0012 - IL_0012: ldloc.2 - IL_0013: call ""void System.Console.Write(object)"" - IL_0018: nop - IL_0019: br.s IL_001b - IL_001b: ret + IL_0012: ldloc.1 + IL_0013: stloc.3 + IL_0014: br.s IL_0016 + IL_0016: ldloc.3 + IL_0017: call ""void System.Console.Write(object)"" + IL_001c: nop + IL_001d: br.s IL_001f + IL_001f: ret }" ); } @@ -7866,24 +7878,21 @@ public static void M(int? x) expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 33 (0x21) - .maxstack 2 + // Code size 25 (0x19) + .maxstack 1 .locals init (int? V_0, System.IComparable V_1) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call ""bool int?.HasValue.get"" - IL_0009: brfalse.s IL_0020 + IL_0009: brfalse.s IL_0018 IL_000b: ldloc.0 IL_000c: box ""int?"" - IL_0011: isinst ""System.IComparable"" - IL_0016: dup - IL_0017: stloc.1 - IL_0018: brfalse.s IL_0020 - IL_001a: ldloc.1 - IL_001b: call ""void System.Console.Write(object)"" - IL_0020: ret + IL_0011: stloc.1 + IL_0012: ldloc.1 + IL_0013: call ""void System.Console.Write(object)"" + IL_0018: ret }" ); compVerifier = CompileAndVerify(source, @@ -7891,8 +7900,8 @@ .locals init (int? V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 53 (0x35) - .maxstack 2 + // Code size 45 (0x2d) + .maxstack 1 .locals init (int? V_0, System.IComparable V_1, int? V_2, @@ -7908,23 +7917,20 @@ .locals init (int? V_0, IL_0009: ldloca.s V_0 IL_000b: call ""bool int?.HasValue.get"" IL_0010: brtrue.s IL_0014 - IL_0012: br.s IL_0025 + IL_0012: br.s IL_001d IL_0014: ldloc.0 IL_0015: box ""int?"" - IL_001a: isinst ""System.IComparable"" - IL_001f: dup - IL_0020: stloc.1 - IL_0021: brfalse.s IL_0025 - IL_0023: br.s IL_0027 - IL_0025: br.s IL_0034 - IL_0027: ldloc.1 - IL_0028: stloc.3 - IL_0029: br.s IL_002b - IL_002b: ldloc.3 - IL_002c: call ""void System.Console.Write(object)"" - IL_0031: nop - IL_0032: br.s IL_0034 - IL_0034: ret + IL_001a: stloc.1 + IL_001b: br.s IL_001f + IL_001d: br.s IL_002c + IL_001f: ldloc.1 + IL_0020: stloc.3 + IL_0021: br.s IL_0023 + IL_0023: ldloc.3 + IL_0024: call ""void System.Console.Write(object)"" + IL_0029: nop + IL_002a: br.s IL_002c + IL_002c: ret }" ); } @@ -8058,28 +8064,36 @@ public static void M(object x) expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 38 (0x26) - .maxstack 1 + // Code size 51 (0x33) + .maxstack 2 .locals init (object V_0, - T V_1) + T V_1, + object V_2, + T V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 - IL_0003: brfalse.s IL_0025 + IL_0003: brfalse.s IL_0032 IL_0005: ldloc.0 - IL_0006: isinst ""T"" - IL_000b: brtrue.s IL_0010 - IL_000d: ldc.i4.0 - IL_000e: br.s IL_0018 - IL_0010: ldloc.0 - IL_0011: unbox.any ""T"" - IL_0016: stloc.1 - IL_0017: ldc.i4.1 - IL_0018: brfalse.s IL_0025 - IL_001a: ldloc.1 - IL_001b: box ""T"" - IL_0020: call ""void System.Console.Write(object)"" - IL_0025: ret + IL_0006: stloc.2 + IL_0007: ldloc.2 + IL_0008: isinst ""T"" + IL_000d: ldnull + IL_000e: cgt.un + IL_0010: dup + IL_0011: brtrue.s IL_001e + IL_0013: ldloca.s V_3 + IL_0015: initobj ""T"" + IL_001b: ldloc.3 + IL_001c: br.s IL_0024 + IL_001e: ldloc.2 + IL_001f: unbox.any ""T"" + IL_0024: stloc.1 + IL_0025: brfalse.s IL_0032 + IL_0027: ldloc.1 + IL_0028: box ""T"" + IL_002d: call ""void System.Console.Write(object)"" + IL_0032: ret }" ); compVerifier = CompileAndVerify(source, @@ -8087,13 +8101,15 @@ .locals init (object V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 58 (0x3a) - .maxstack 1 + // Code size 75 (0x4b) + .maxstack 2 .locals init (object V_0, T V_1, object V_2, T V_3, //i - object V_4) + object V_4, + object V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 @@ -8103,28 +8119,34 @@ .locals init (object V_0, IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_0025 + IL_000c: br.s IL_0036 IL_000e: ldloc.0 - IL_000f: isinst ""T"" - IL_0014: brtrue.s IL_0019 - IL_0016: ldc.i4.0 - IL_0017: br.s IL_0021 - IL_0019: ldloc.0 - IL_001a: unbox.any ""T"" - IL_001f: stloc.1 - IL_0020: ldc.i4.1 - IL_0021: brfalse.s IL_0025 - IL_0023: br.s IL_0027 - IL_0025: br.s IL_0039 - IL_0027: ldloc.1 - IL_0028: stloc.3 - IL_0029: br.s IL_002b - IL_002b: ldloc.3 - IL_002c: box ""T"" - IL_0031: call ""void System.Console.Write(object)"" - IL_0036: nop - IL_0037: br.s IL_0039 - IL_0039: ret + IL_000f: stloc.s V_5 + IL_0011: ldloc.s V_5 + IL_0013: isinst ""T"" + IL_0018: ldnull + IL_0019: cgt.un + IL_001b: dup + IL_001c: brtrue.s IL_002a + IL_001e: ldloca.s V_6 + IL_0020: initobj ""T"" + IL_0026: ldloc.s V_6 + IL_0028: br.s IL_0031 + IL_002a: ldloc.s V_5 + IL_002c: unbox.any ""T"" + IL_0031: stloc.1 + IL_0032: brfalse.s IL_0036 + IL_0034: br.s IL_0038 + IL_0036: br.s IL_004a + IL_0038: ldloc.1 + IL_0039: stloc.3 + IL_003a: br.s IL_003c + IL_003c: ldloc.3 + IL_003d: box ""T"" + IL_0042: call ""void System.Console.Write(object)"" + IL_0047: nop + IL_0048: br.s IL_004a + IL_004a: ret }" ); } @@ -8159,28 +8181,36 @@ public static void M(IComparable x) expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 38 (0x26) - .maxstack 1 + // Code size 51 (0x33) + .maxstack 2 .locals init (System.IComparable V_0, - T V_1) + T V_1, + object V_2, + T V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 - IL_0003: brfalse.s IL_0025 + IL_0003: brfalse.s IL_0032 IL_0005: ldloc.0 - IL_0006: isinst ""T"" - IL_000b: brtrue.s IL_0010 - IL_000d: ldc.i4.0 - IL_000e: br.s IL_0018 - IL_0010: ldloc.0 - IL_0011: unbox.any ""T"" - IL_0016: stloc.1 - IL_0017: ldc.i4.1 - IL_0018: brfalse.s IL_0025 - IL_001a: ldloc.1 - IL_001b: box ""T"" - IL_0020: call ""void System.Console.Write(object)"" - IL_0025: ret + IL_0006: stloc.2 + IL_0007: ldloc.2 + IL_0008: isinst ""T"" + IL_000d: ldnull + IL_000e: cgt.un + IL_0010: dup + IL_0011: brtrue.s IL_001e + IL_0013: ldloca.s V_3 + IL_0015: initobj ""T"" + IL_001b: ldloc.3 + IL_001c: br.s IL_0024 + IL_001e: ldloc.2 + IL_001f: unbox.any ""T"" + IL_0024: stloc.1 + IL_0025: brfalse.s IL_0032 + IL_0027: ldloc.1 + IL_0028: box ""T"" + IL_002d: call ""void System.Console.Write(object)"" + IL_0032: ret }" ); compVerifier = CompileAndVerify(source, @@ -8188,13 +8218,15 @@ .locals init (System.IComparable V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 58 (0x3a) - .maxstack 1 + // Code size 75 (0x4b) + .maxstack 2 .locals init (System.IComparable V_0, T V_1, System.IComparable V_2, T V_3, //i - System.IComparable V_4) + System.IComparable V_4, + object V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 @@ -8204,28 +8236,34 @@ .locals init (System.IComparable V_0, IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: brtrue.s IL_000e - IL_000c: br.s IL_0025 + IL_000c: br.s IL_0036 IL_000e: ldloc.0 - IL_000f: isinst ""T"" - IL_0014: brtrue.s IL_0019 - IL_0016: ldc.i4.0 - IL_0017: br.s IL_0021 - IL_0019: ldloc.0 - IL_001a: unbox.any ""T"" - IL_001f: stloc.1 - IL_0020: ldc.i4.1 - IL_0021: brfalse.s IL_0025 - IL_0023: br.s IL_0027 - IL_0025: br.s IL_0039 - IL_0027: ldloc.1 - IL_0028: stloc.3 - IL_0029: br.s IL_002b - IL_002b: ldloc.3 - IL_002c: box ""T"" - IL_0031: call ""void System.Console.Write(object)"" - IL_0036: nop - IL_0037: br.s IL_0039 - IL_0039: ret + IL_000f: stloc.s V_5 + IL_0011: ldloc.s V_5 + IL_0013: isinst ""T"" + IL_0018: ldnull + IL_0019: cgt.un + IL_001b: dup + IL_001c: brtrue.s IL_002a + IL_001e: ldloca.s V_6 + IL_0020: initobj ""T"" + IL_0026: ldloc.s V_6 + IL_0028: br.s IL_0031 + IL_002a: ldloc.s V_5 + IL_002c: unbox.any ""T"" + IL_0031: stloc.1 + IL_0032: brfalse.s IL_0036 + IL_0034: br.s IL_0038 + IL_0036: br.s IL_004a + IL_0038: ldloc.1 + IL_0039: stloc.3 + IL_003a: br.s IL_003c + IL_003c: ldloc.3 + IL_003d: box ""T"" + IL_0042: call ""void System.Console.Write(object)"" + IL_0047: nop + IL_0048: br.s IL_004a + IL_004a: ret }" ); } @@ -8260,31 +8298,38 @@ public static void M(U x) where T : U expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 53 (0x35) - .maxstack 1 + // Code size 61 (0x3d) + .maxstack 2 .locals init (U V_0, - T V_1) + T V_1, + object V_2, + T V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: box ""U"" - IL_0008: brfalse.s IL_0034 + IL_0008: brfalse.s IL_003c IL_000a: ldloc.0 IL_000b: box ""U"" - IL_0010: isinst ""T"" - IL_0015: brtrue.s IL_001a - IL_0017: ldc.i4.0 - IL_0018: br.s IL_0027 - IL_001a: ldloc.0 - IL_001b: box ""U"" - IL_0020: unbox.any ""T"" - IL_0025: stloc.1 - IL_0026: ldc.i4.1 - IL_0027: brfalse.s IL_0034 - IL_0029: ldloc.1 - IL_002a: box ""T"" - IL_002f: call ""void System.Console.Write(object)"" - IL_0034: ret + IL_0010: stloc.2 + IL_0011: ldloc.2 + IL_0012: isinst ""T"" + IL_0017: ldnull + IL_0018: cgt.un + IL_001a: dup + IL_001b: brtrue.s IL_0028 + IL_001d: ldloca.s V_3 + IL_001f: initobj ""T"" + IL_0025: ldloc.3 + IL_0026: br.s IL_002e + IL_0028: ldloc.2 + IL_0029: unbox.any ""T"" + IL_002e: stloc.1 + IL_002f: brfalse.s IL_003c + IL_0031: ldloc.1 + IL_0032: box ""T"" + IL_0037: call ""void System.Console.Write(object)"" + IL_003c: ret }" ); compVerifier = CompileAndVerify(source, @@ -8292,13 +8337,15 @@ .locals init (U V_0, expectedOutput: "1"); compVerifier.VerifyIL("Program.M", @"{ - // Code size 73 (0x49) - .maxstack 1 + // Code size 85 (0x55) + .maxstack 2 .locals init (U V_0, T V_1, U V_2, T V_3, //i - U V_4) + U V_4, + object V_5, + T V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 @@ -8309,30 +8356,35 @@ .locals init (U V_0, IL_0009: ldloc.0 IL_000a: box ""U"" IL_000f: brtrue.s IL_0013 - IL_0011: br.s IL_0034 + IL_0011: br.s IL_0040 IL_0013: ldloc.0 IL_0014: box ""U"" - IL_0019: isinst ""T"" - IL_001e: brtrue.s IL_0023 - IL_0020: ldc.i4.0 - IL_0021: br.s IL_0030 - IL_0023: ldloc.0 - IL_0024: box ""U"" - IL_0029: unbox.any ""T"" - IL_002e: stloc.1 - IL_002f: ldc.i4.1 - IL_0030: brfalse.s IL_0034 - IL_0032: br.s IL_0036 - IL_0034: br.s IL_0048 - IL_0036: ldloc.1 - IL_0037: stloc.3 - IL_0038: br.s IL_003a - IL_003a: ldloc.3 - IL_003b: box ""T"" - IL_0040: call ""void System.Console.Write(object)"" - IL_0045: nop - IL_0046: br.s IL_0048 - IL_0048: ret + IL_0019: stloc.s V_5 + IL_001b: ldloc.s V_5 + IL_001d: isinst ""T"" + IL_0022: ldnull + IL_0023: cgt.un + IL_0025: dup + IL_0026: brtrue.s IL_0034 + IL_0028: ldloca.s V_6 + IL_002a: initobj ""T"" + IL_0030: ldloc.s V_6 + IL_0032: br.s IL_003b + IL_0034: ldloc.s V_5 + IL_0036: unbox.any ""T"" + IL_003b: stloc.1 + IL_003c: brfalse.s IL_0040 + IL_003e: br.s IL_0042 + IL_0040: br.s IL_0054 + IL_0042: ldloc.1 + IL_0043: stloc.3 + IL_0044: br.s IL_0046 + IL_0046: ldloc.3 + IL_0047: box ""T"" + IL_004c: call ""void System.Console.Write(object)"" + IL_0051: nop + IL_0052: br.s IL_0054 + IL_0054: ret }" ); } @@ -8357,18 +8409,15 @@ public static void Main() expectedOutput: "RemoveEmptyEntries"); compVerifier.VerifyIL("Program.Main", @"{ - // Code size 22 (0x16) - .maxstack 2 + // Code size 14 (0xe) + .maxstack 1 .locals init (object V_0) //o IL_0000: ldc.i4.1 IL_0001: box ""System.StringSplitOptions"" - IL_0006: isinst ""object"" - IL_000b: dup - IL_000c: stloc.0 - IL_000d: brfalse.s IL_0015 - IL_000f: ldloc.0 - IL_0010: call ""void System.Console.WriteLine(object)"" - IL_0015: ret + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: call ""void System.Console.WriteLine(object)"" + IL_000d: ret }" ); compVerifier = CompileAndVerify(source, @@ -8376,31 +8425,785 @@ .locals init (object V_0) //o expectedOutput: "RemoveEmptyEntries"); compVerifier.VerifyIL("Program.Main", @"{ - // Code size 31 (0x1f) - .maxstack 2 + // Code size 23 (0x17) + .maxstack 1 .locals init (object V_0, //o bool V_1) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: box ""System.StringSplitOptions"" - IL_0007: isinst ""object"" + IL_0007: stloc.0 + IL_0008: ldc.i4.1 + IL_0009: stloc.1 + IL_000a: ldloc.1 + IL_000b: brfalse.s IL_0016 + IL_000d: nop + IL_000e: ldloc.0 + IL_000f: call ""void System.Console.WriteLine(object)"" + IL_0014: nop + IL_0015: nop + IL_0016: ret +}" + ); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestMatchWithTypeParameter_01() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + Console.Write(M1(2)); + Console.Write(M2(3)); + Console.Write(M1(1.1)); + Console.Write(M2(1.1)); + } + public static T M1(ValueType o) + { + return o is T t ? t : default(T); + } + public static T M2(ValueType o) + { + switch (o) + { + case T t: + return t; + default: + return default(T); + } + } +} +"; + CreateStandardCompilation(source, parseOptions: TestOptions.Regular7).VerifyDiagnostics( + // (13,21): error CS9003: An expression of type 'ValueType' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // return o is T t ? t : default(T); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("System.ValueType", "T", "7", "7.1").WithLocation(13, 21), + // (19,18): error CS9003: An expression of type 'ValueType' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // case T t: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("System.ValueType", "T", "7", "7.1").WithLocation(19, 18) + ); + var compVerifier = CompileAndVerify(source, + options: TestOptions.ReleaseDll.WithOutputKind(OutputKind.ConsoleApplication), + parseOptions: TestOptions.Regular7_1, + expectedOutput: "2300"); + compVerifier.VerifyIL("Program.M1", +@"{ + // Code size 46 (0x2e) + .maxstack 2 + .locals init (T V_0, //t + object V_1, + T V_2) + IL_0000: ldarg.0 + IL_0001: stloc.1 + IL_0002: ldloc.1 + IL_0003: isinst ""T"" + IL_0008: ldnull + IL_0009: cgt.un + IL_000b: dup + IL_000c: brtrue.s IL_0019 + IL_000e: ldloca.s V_2 + IL_0010: initobj ""T"" + IL_0016: ldloc.2 + IL_0017: br.s IL_001f + IL_0019: ldloc.1 + IL_001a: unbox.any ""T"" + IL_001f: stloc.0 + IL_0020: brtrue.s IL_002c + IL_0022: ldloca.s V_2 + IL_0024: initobj ""T"" + IL_002a: ldloc.2 + IL_002b: ret + IL_002c: ldloc.0 + IL_002d: ret +}" + ); + compVerifier.VerifyIL("Program.M2", +@"{ + // Code size 51 (0x33) + .maxstack 2 + .locals init (System.ValueType V_0, + T V_1, + object V_2, + T V_3) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: brfalse.s IL_0029 + IL_0005: ldloc.0 + IL_0006: stloc.2 + IL_0007: ldloc.2 + IL_0008: isinst ""T"" + IL_000d: ldnull + IL_000e: cgt.un + IL_0010: dup + IL_0011: brtrue.s IL_001e + IL_0013: ldloca.s V_3 + IL_0015: initobj ""T"" + IL_001b: ldloc.3 + IL_001c: br.s IL_0024 + IL_001e: ldloc.2 + IL_001f: unbox.any ""T"" + IL_0024: stloc.1 + IL_0025: brfalse.s IL_0029 + IL_0027: ldloc.1 + IL_0028: ret + IL_0029: ldloca.s V_3 + IL_002b: initobj ""T"" + IL_0031: ldloc.3 + IL_0032: ret +}" + ); + compVerifier = CompileAndVerify(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + parseOptions: TestOptions.Regular7_1, + expectedOutput: "2300"); + compVerifier.VerifyIL("Program.M1", +@"{ + // Code size 52 (0x34) + .maxstack 2 + .locals init (T V_0, //t + object V_1, + T V_2, + T V_3) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.1 + IL_0003: ldloc.1 + IL_0004: isinst ""T"" + IL_0009: ldnull + IL_000a: cgt.un IL_000c: dup - IL_000d: stloc.0 - IL_000e: ldnull - IL_000f: cgt.un + IL_000d: brtrue.s IL_001a + IL_000f: ldloca.s V_2 + IL_0011: initobj ""T"" + IL_0017: ldloc.2 + IL_0018: br.s IL_0020 + IL_001a: ldloc.1 + IL_001b: unbox.any ""T"" + IL_0020: stloc.0 + IL_0021: brtrue.s IL_002e + IL_0023: ldloca.s V_2 + IL_0025: initobj ""T"" + IL_002b: ldloc.2 + IL_002c: br.s IL_002f + IL_002e: ldloc.0 + IL_002f: stloc.3 + IL_0030: br.s IL_0032 + IL_0032: ldloc.3 + IL_0033: ret +}" + ); + compVerifier.VerifyIL("Program.M2", +@"{ + // Code size 82 (0x52) + .maxstack 2 + .locals init (System.ValueType V_0, + T V_1, + System.ValueType V_2, + T V_3, //t + System.ValueType V_4, + object V_5, + T V_6, + T V_7) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloc.2 + IL_0004: stloc.s V_4 + IL_0006: ldloc.s V_4 + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: brtrue.s IL_000e + IL_000c: br.s IL_0036 + IL_000e: ldloc.0 + IL_000f: stloc.s V_5 + IL_0011: ldloc.s V_5 + IL_0013: isinst ""T"" + IL_0018: ldnull + IL_0019: cgt.un + IL_001b: dup + IL_001c: brtrue.s IL_002a + IL_001e: ldloca.s V_6 + IL_0020: initobj ""T"" + IL_0026: ldloc.s V_6 + IL_0028: br.s IL_0031 + IL_002a: ldloc.s V_5 + IL_002c: unbox.any ""T"" + IL_0031: stloc.1 + IL_0032: brfalse.s IL_0036 + IL_0034: br.s IL_0038 + IL_0036: br.s IL_0041 + IL_0038: ldloc.1 + IL_0039: stloc.3 + IL_003a: br.s IL_003c + IL_003c: ldloc.3 + IL_003d: stloc.s V_7 + IL_003f: br.s IL_004f + IL_0041: ldloca.s V_6 + IL_0043: initobj ""T"" + IL_0049: ldloc.s V_6 + IL_004b: stloc.s V_7 + IL_004d: br.s IL_004f + IL_004f: ldloc.s V_7 + IL_0051: ret +}" + ); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestMatchWithTypeParameter_02() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + Console.Write(M1(2)); + Console.Write(M2(3)); + Console.Write(M1(1.1)); + Console.Write(M2(1.1)); + } + public static int M1(T o) + { + return o is int t ? t : default(int); + } + public static int M2(T o) + { + switch (o) + { + case int t: + return t; + default: + return default(int); + } + } +}"; + CreateStandardCompilation(source, parseOptions: TestOptions.Regular7).VerifyDiagnostics( + // (13,21): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'int' in C# 7. Please use language version 7.1 or greater. + // return o is int t ? t : default(int); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "int").WithArguments("T", "int", "7", "7.1").WithLocation(13, 21), + // (19,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'int' in C# 7. Please use language version 7.1 or greater. + // case int t: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "int").WithArguments("T", "int", "7", "7.1").WithLocation(19, 18) + ); + var compVerifier = CompileAndVerify(source, + options: TestOptions.ReleaseDll.WithOutputKind(OutputKind.ConsoleApplication), + parseOptions: TestOptions.Regular7_1, + expectedOutput: "2300"); + compVerifier.VerifyIL("Program.M1", +@"{ + // Code size 38 (0x26) + .maxstack 1 + .locals init (int V_0, //t + int? V_1) + IL_0000: ldarg.0 + IL_0001: box ""T"" + IL_0006: isinst ""int?"" + IL_000b: unbox.any ""int?"" + IL_0010: stloc.1 + IL_0011: ldloca.s V_1 + IL_0013: call ""int int?.GetValueOrDefault()"" + IL_0018: stloc.0 + IL_0019: ldloca.s V_1 + IL_001b: call ""bool int?.HasValue.get"" + IL_0020: brtrue.s IL_0024 + IL_0022: ldc.i4.0 + IL_0023: ret + IL_0024: ldloc.0 + IL_0025: ret +}" + ); + compVerifier.VerifyIL("Program.M2", +@"{ + // Code size 48 (0x30) + .maxstack 1 + .locals init (T V_0, + int V_1, + int? V_2) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: box ""T"" + IL_0008: brfalse.s IL_002e + IL_000a: ldloc.0 + IL_000b: box ""T"" + IL_0010: isinst ""int?"" + IL_0015: unbox.any ""int?"" + IL_001a: stloc.2 + IL_001b: ldloca.s V_2 + IL_001d: call ""int int?.GetValueOrDefault()"" + IL_0022: stloc.1 + IL_0023: ldloca.s V_2 + IL_0025: call ""bool int?.HasValue.get"" + IL_002a: brfalse.s IL_002e + IL_002c: ldloc.1 + IL_002d: ret + IL_002e: ldc.i4.0 + IL_002f: ret +}" + ); + compVerifier = CompileAndVerify(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + parseOptions: TestOptions.Regular7_1, + expectedOutput: "2300"); + compVerifier.VerifyIL("Program.M1", +@"{ + // Code size 44 (0x2c) + .maxstack 1 + .locals init (int V_0, //t + int? V_1, + int V_2) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: box ""T"" + IL_0007: isinst ""int?"" + IL_000c: unbox.any ""int?"" IL_0011: stloc.1 - IL_0012: ldloc.1 - IL_0013: brfalse.s IL_001e - IL_0015: nop - IL_0016: ldloc.0 - IL_0017: call ""void System.Console.WriteLine(object)"" - IL_001c: nop - IL_001d: nop - IL_001e: ret + IL_0012: ldloca.s V_1 + IL_0014: call ""int int?.GetValueOrDefault()"" + IL_0019: stloc.0 + IL_001a: ldloca.s V_1 + IL_001c: call ""bool int?.HasValue.get"" + IL_0021: brtrue.s IL_0026 + IL_0023: ldc.i4.0 + IL_0024: br.s IL_0027 + IL_0026: ldloc.0 + IL_0027: stloc.2 + IL_0028: br.s IL_002a + IL_002a: ldloc.2 + IL_002b: ret +}" + ); + compVerifier.VerifyIL("Program.M2", +@"{ + // Code size 75 (0x4b) + .maxstack 1 + .locals init (T V_0, + int V_1, + T V_2, + int V_3, //t + T V_4, + int? V_5, + int V_6) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.2 + IL_0003: ldloc.2 + IL_0004: stloc.s V_4 + IL_0006: ldloc.s V_4 + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: box ""T"" + IL_000f: brtrue.s IL_0013 + IL_0011: br.s IL_0038 + IL_0013: ldloc.0 + IL_0014: box ""T"" + IL_0019: isinst ""int?"" + IL_001e: unbox.any ""int?"" + IL_0023: stloc.s V_5 + IL_0025: ldloca.s V_5 + IL_0027: call ""int int?.GetValueOrDefault()"" + IL_002c: stloc.1 + IL_002d: ldloca.s V_5 + IL_002f: call ""bool int?.HasValue.get"" + IL_0034: brfalse.s IL_0038 + IL_0036: br.s IL_003a + IL_0038: br.s IL_0043 + IL_003a: ldloc.1 + IL_003b: stloc.3 + IL_003c: br.s IL_003e + IL_003e: ldloc.3 + IL_003f: stloc.s V_6 + IL_0041: br.s IL_0048 + IL_0043: ldc.i4.0 + IL_0044: stloc.s V_6 + IL_0046: br.s IL_0048 + IL_0048: ldloc.s V_6 + IL_004a: ret +}" + ); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestMatchWithTypeParameter_03() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + Console.Write(M1(2)); + Console.Write(M2(3)); + Console.Write(M1(1.1)); + Console.Write(M2(1.1)); + } + public static T M1(ValueType o) where T : struct + { + return o is T t ? t : default(T); + } + public static T M2(ValueType o) where T : struct + { + switch (o) + { + case T t: + return t; + default: + return default(T); + } + } +} +"; + var compVerifier = CompileAndVerify(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + expectedOutput: "2300"); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestMatchWithTypeParameter_04() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + var x = new X(); + Console.Write(M1(new A()) ?? x); + Console.Write(M2(new A()) ?? x); + Console.Write(M1(new B()) ?? x); + Console.Write(M2(new B()) ?? x); + } + public static T M1(A o) where T : class + { + return o is T t ? t : default(T); + } + public static T M2(A o) where T : class + { + switch (o) + { + case T t: + return t; + default: + return default(T); + } + } +} +class A +{ +} +class B : A +{ +} +class X : B { } +"; + CreateStandardCompilation(source, parseOptions: TestOptions.Regular7).VerifyDiagnostics( + // (14,21): error CS9003: An expression of type 'A' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // return o is T t ? t : default(T); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("A", "T", "7", "7.1").WithLocation(14, 21), + // (20,18): error CS9003: An expression of type 'A' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // case T t: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("A", "T", "7", "7.1").WithLocation(20, 18) + ); + var compVerifier = CompileAndVerify(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + parseOptions: TestOptions.Regular7_1, + expectedOutput: "XXBB"); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestMatchWithTypeParameter_05() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + var x = new X(); + Console.Write(M1(new A()) ?? x); + Console.Write(M2(new A()) ?? x); + Console.Write(M1(new B()) ?? x); + Console.Write(M2(new B()) ?? x); + } + public static T M1(A o) where T : I1 + { + return o is T t ? t : default(T); + } + public static T M2(A o) where T : I1 + { + switch (o) + { + case T t: + return t; + default: + return default(T); + } + } +} +interface I1 +{ +} +class A : I1 +{ +} +class B : A +{ +} +class X : B { } +"; + CreateStandardCompilation(source, parseOptions: TestOptions.Regular7).VerifyDiagnostics( + // (14,21): error CS9003: An expression of type 'A' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // return o is T t ? t : default(T); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("A", "T", "7", "7.1").WithLocation(14, 21), + // (20,18): error CS9003: An expression of type 'A' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // case T t: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("A", "T", "7", "7.1").WithLocation(20, 18) + ); + var compVerifier = CompileAndVerify(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + parseOptions: TestOptions.Regular7_1, + expectedOutput: "XXBB"); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestMatchWithTypeParameter_06() + { + var source = +@"using System; +class Program +{ + public static void Main(string[] args) + { + Console.Write(M1(new A())); + Console.Write(M2(new A())); + Console.Write(M1(new A())); + Console.Write(M2(new A())); + } + public static bool M1(A o) where T : I1 + { + return o is T t; + } + public static bool M2(A o) where T : I1 + { + switch (o) + { + case T t: + return true; + default: + return false; + } + } +} +interface I1 +{ +} +struct A : I1 +{ +} +struct B : I1 +{ +} +"; + CreateStandardCompilation(source, parseOptions: TestOptions.Regular7).VerifyDiagnostics( + // (13,21): error CS9003: An expression of type 'A' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // return o is T t; + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("A", "T", "7", "7.1").WithLocation(13, 21), + // (19,18): error CS9003: An expression of type 'A' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // case T t: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("A", "T", "7", "7.1").WithLocation(19, 18) + ); + var compilation = CreateStandardCompilation(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + parseOptions: TestOptions.Regular7_1) + .VerifyDiagnostics(); + var compVerifier = CompileAndVerify(compilation, + expectedOutput: "FalseFalseTrueTrue"); + compVerifier.VerifyDiagnostics(); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestIgnoreDynamicVsObjectAndTupleElementNames_01() + { + var source = +@"public class Generic +{ + public enum Color { Red, Blue } +} +class Program +{ + public static void Main(string[] args) + { + } + public static void M2(object o) + { + switch (o) + { + case Generic.Color c: + case Generic.Color.Red: + case Generic<(int x, int y)>.Color.Blue: + case Generic.Color.Red: + case Generic.Color.Red: // error: duplicate case + case Generic<(int z, int w)>.Color.Blue: // error: duplicate case + case Generic<(int z, long w)>.Color.Blue: + default: + break; + } + } +} +"; + CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }).VerifyDiagnostics( + // (18,13): error CS8120: The switch case has already been handled by a previous case. + // case Generic.Color.Red: // error: duplicate case + Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case Generic.Color.Red:").WithLocation(18, 13), + // (19,13): error CS8120: The switch case has already been handled by a previous case. + // case Generic<(int z, int w)>.Color.Blue: // error: duplicate case + Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case Generic<(int z, int w)>.Color.Blue:").WithLocation(19, 13) + ); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void TestIgnoreDynamicVsObjectAndTupleElementNames_02() + { + var source = +@"using System; +public class Generic +{ + public enum Color { Red, Blue } +} +class Program +{ + public static void Main(string[] args) + { + Console.WriteLine(M1.Color>(Generic.Color.Red)); // False + Console.WriteLine(M1.Color>(Generic.Color.Red)); // False + Console.WriteLine(M1.Color>(Generic<(int z, int w)>.Color.Red)); // True + Console.WriteLine(M1.Color>(Generic.Color.Blue)); // True + + Console.WriteLine(M2(Generic.Color.Red)); // Generic.Color.Red + Console.WriteLine(M2(Generic.Color.Blue)); // Generic.Color.Blue + Console.WriteLine(M2(Generic.Color.Red)); // None + Console.WriteLine(M2(Generic.Color.Red)); // Generic.Color.Red + } + public static bool M1(object o) + { + return o is T t; + } + public static string M2(object o) + { + switch (o) + { + case Generic.Color c: + return ""Generic.Color."" + c; + case Generic.Color.Red: + return ""Generic.Color.Red""; + case Generic.Color.Blue: + return ""Generic.Color.Blue""; + default: + return ""None""; + } + } +} +"; + var compilation = CreateStandardCompilation(source, + options: TestOptions.DebugDll.WithOutputKind(OutputKind.ConsoleApplication), + references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }) + .VerifyDiagnostics(); + var compVerifier = CompileAndVerify(compilation, + expectedOutput: @"False +False +True +True +Generic.Color.Red +Generic.Color.Blue +None +Generic.Color.Red"); + compVerifier.VerifyDiagnostics(); + compVerifier.VerifyIL("Program.M2", +@"{ + // Code size 154 (0x9a) + .maxstack 2 + .locals init (object V_0, + Generic.Color V_1, + Generic.Color V_2, + object V_3, + Generic.Color V_4, //c + object V_5, + Generic.Color? V_6, + Generic.Color? V_7, + int V_8, + string V_9) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: stloc.3 + IL_0003: ldloc.3 + IL_0004: stloc.s V_5 + IL_0006: ldloc.s V_5 + IL_0008: stloc.0 + IL_0009: ldloc.0 + IL_000a: brtrue.s IL_000e + IL_000c: br.s IL_0060 + IL_000e: ldloc.0 + IL_000f: isinst ""Generic.Color?"" + IL_0014: unbox.any ""Generic.Color?"" + IL_0019: stloc.s V_6 + IL_001b: ldloca.s V_6 + IL_001d: call ""Generic.Color Generic.Color?.GetValueOrDefault()"" + IL_0022: stloc.1 + IL_0023: ldloca.s V_6 + IL_0025: call ""bool Generic.Color?.HasValue.get"" + IL_002a: brfalse.s IL_002e + IL_002c: br.s IL_0062 + IL_002e: ldloc.0 + IL_002f: isinst ""Generic.Color?"" + IL_0034: unbox.any ""Generic.Color?"" + IL_0039: stloc.s V_7 + IL_003b: ldloca.s V_7 + IL_003d: call ""Generic.Color Generic.Color?.GetValueOrDefault()"" + IL_0042: stloc.2 + IL_0043: ldloca.s V_7 + IL_0045: call ""bool Generic.Color?.HasValue.get"" + IL_004a: brfalse.s IL_0060 + IL_004c: ldloc.2 + IL_004d: stloc.s V_8 + IL_004f: ldloc.s V_8 + IL_0051: brfalse.s IL_005c + IL_0053: br.s IL_0055 + IL_0055: ldloc.s V_8 + IL_0057: ldc.i4.1 + IL_0058: beq.s IL_005e + IL_005a: br.s IL_0060 + IL_005c: br.s IL_007c + IL_005e: br.s IL_0085 + IL_0060: br.s IL_008e + IL_0062: ldloc.1 + IL_0063: stloc.s V_4 + IL_0065: br.s IL_0067 + IL_0067: ldstr ""Generic.Color."" + IL_006c: ldloc.s V_4 + IL_006e: box ""Generic.Color"" + IL_0073: call ""string string.Concat(object, object)"" + IL_0078: stloc.s V_9 + IL_007a: br.s IL_0097 + IL_007c: ldstr ""Generic.Color.Red"" + IL_0081: stloc.s V_9 + IL_0083: br.s IL_0097 + IL_0085: ldstr ""Generic.Color.Blue"" + IL_008a: stloc.s V_9 + IL_008c: br.s IL_0097 + IL_008e: ldstr ""None"" + IL_0093: stloc.s V_9 + IL_0095: br.s IL_0097 + IL_0097: ldloc.s V_9 + IL_0099: ret }" ); } - #endregion regression tests + #endregion "regression tests" } } diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index a99d0900eabd4..80adf674c9e72 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -6472,19 +6472,18 @@ static void M(object o) var c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugDll); CompileAndVerify(c).VerifyIL("Program.M", @"{ - // Code size 210 (0xd2) + // Code size 202 (0xca) .maxstack 2 .locals init (object V_0, int V_1, object V_2, object V_3, int? V_4, - int V_5, - object V_6, - int V_7, + object V_5, + int V_6, + object V_7, object V_8, - object V_9, - object V_10) + object V_9) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 @@ -6494,7 +6493,7 @@ .locals init (object V_0, IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brtrue.s IL_000c - IL_000a: br.s IL_005e + IL_000a: br.s IL_005a IL_000c: ldloc.0 IL_000d: isinst ""int?"" IL_0012: unbox.any ""int?"" @@ -6504,88 +6503,83 @@ .locals init (object V_0, IL_0020: stloc.1 IL_0021: ldloca.s V_4 IL_0023: call ""bool int?.HasValue.get"" - IL_0028: brfalse.s IL_005e + IL_0028: brfalse.s IL_005a IL_002a: ldloc.1 - IL_002b: stloc.s V_5 - IL_002d: ldloc.s V_5 - IL_002f: ldc.i4.1 - IL_0030: sub - IL_0031: switch ( - IL_004c, - IL_0054, - IL_005a, - IL_0052, - IL_0058) - IL_004a: br.s IL_005e - IL_004c: br.s IL_0060 - IL_004e: br.s IL_006c - IL_0050: br.s IL_007a - IL_0052: br.s IL_006a - IL_0054: br.s IL_0065 - IL_0056: br.s IL_005e - IL_0058: br.s IL_0076 - IL_005a: br.s IL_0071 - IL_005c: br.s IL_005e - IL_005e: br.s IL_0078 - IL_0060: ldarg.0 - IL_0061: brfalse.s IL_006a - IL_0063: br.s IL_004e - IL_0065: ldarg.0 - IL_0066: brfalse.s IL_006a - IL_0068: br.s IL_0056 - IL_006a: br.s IL_007c - IL_006c: ldarg.0 - IL_006d: brtrue.s IL_0076 - IL_006f: br.s IL_0050 - IL_0071: ldarg.0 - IL_0072: brtrue.s IL_0076 - IL_0074: br.s IL_005c - IL_0076: br.s IL_007c - IL_0078: br.s IL_007c - IL_007a: br.s IL_007c - IL_007c: ldarg.0 - IL_007d: stloc.2 - IL_007e: ldloc.2 - IL_007f: stloc.s V_8 - IL_0081: ldloc.s V_8 - IL_0083: stloc.s V_6 - IL_0085: ldloc.s V_6 - IL_0087: brtrue.s IL_008b - IL_0089: br.s IL_00b8 - IL_008b: ldloc.s V_6 - IL_008d: isinst ""int?"" - IL_0092: unbox.any ""int?"" - IL_0097: stloc.s V_4 - IL_0099: ldloca.s V_4 - IL_009b: call ""int int?.GetValueOrDefault()"" - IL_00a0: stloc.s V_7 - IL_00a2: ldloca.s V_4 - IL_00a4: call ""bool int?.HasValue.get"" - IL_00a9: brfalse.s IL_00b8 - IL_00ab: ldloc.s V_7 - IL_00ad: stloc.s V_5 - IL_00af: ldloc.s V_5 - IL_00b1: ldc.i4.1 - IL_00b2: beq.s IL_00b6 - IL_00b4: br.s IL_00b8 - IL_00b6: br.s IL_00ba - IL_00b8: br.s IL_00bc - IL_00ba: br.s IL_00be - IL_00bc: br.s IL_00be - IL_00be: ldarg.0 - IL_00bf: stloc.2 - IL_00c0: ldloc.2 - IL_00c1: stloc.s V_10 - IL_00c3: ldloc.s V_10 - IL_00c5: stloc.s V_9 - IL_00c7: ldloc.s V_9 - IL_00c9: brtrue.s IL_00cd - IL_00cb: br.s IL_00cd - IL_00cd: br.s IL_00cf - IL_00cf: br.s IL_00d1 - IL_00d1: ret -} -"); + IL_002b: ldc.i4.1 + IL_002c: sub + IL_002d: switch ( + IL_0048, + IL_0050, + IL_0056, + IL_004e, + IL_0054) + IL_0046: br.s IL_005a + IL_0048: br.s IL_005c + IL_004a: br.s IL_0068 + IL_004c: br.s IL_0076 + IL_004e: br.s IL_0066 + IL_0050: br.s IL_0061 + IL_0052: br.s IL_005a + IL_0054: br.s IL_0072 + IL_0056: br.s IL_006d + IL_0058: br.s IL_005a + IL_005a: br.s IL_0074 + IL_005c: ldarg.0 + IL_005d: brfalse.s IL_0066 + IL_005f: br.s IL_004a + IL_0061: ldarg.0 + IL_0062: brfalse.s IL_0066 + IL_0064: br.s IL_0052 + IL_0066: br.s IL_0078 + IL_0068: ldarg.0 + IL_0069: brtrue.s IL_0072 + IL_006b: br.s IL_004c + IL_006d: ldarg.0 + IL_006e: brtrue.s IL_0072 + IL_0070: br.s IL_0058 + IL_0072: br.s IL_0078 + IL_0074: br.s IL_0078 + IL_0076: br.s IL_0078 + IL_0078: ldarg.0 + IL_0079: stloc.2 + IL_007a: ldloc.2 + IL_007b: stloc.s V_7 + IL_007d: ldloc.s V_7 + IL_007f: stloc.s V_5 + IL_0081: ldloc.s V_5 + IL_0083: brtrue.s IL_0087 + IL_0085: br.s IL_00b0 + IL_0087: ldloc.s V_5 + IL_0089: isinst ""int?"" + IL_008e: unbox.any ""int?"" + IL_0093: stloc.s V_4 + IL_0095: ldloca.s V_4 + IL_0097: call ""int int?.GetValueOrDefault()"" + IL_009c: stloc.s V_6 + IL_009e: ldloca.s V_4 + IL_00a0: call ""bool int?.HasValue.get"" + IL_00a5: brfalse.s IL_00b0 + IL_00a7: ldloc.s V_6 + IL_00a9: ldc.i4.1 + IL_00aa: beq.s IL_00ae + IL_00ac: br.s IL_00b0 + IL_00ae: br.s IL_00b2 + IL_00b0: br.s IL_00b4 + IL_00b2: br.s IL_00b6 + IL_00b4: br.s IL_00b6 + IL_00b6: ldarg.0 + IL_00b7: stloc.2 + IL_00b8: ldloc.2 + IL_00b9: stloc.s V_9 + IL_00bb: ldloc.s V_9 + IL_00bd: stloc.s V_8 + IL_00bf: ldloc.s V_8 + IL_00c1: brtrue.s IL_00c5 + IL_00c3: br.s IL_00c5 + IL_00c5: br.s IL_00c7 + IL_00c7: br.s IL_00c9 + IL_00c9: ret +}"); c.VerifyPdb( @" @@ -6600,7 +6594,6 @@ .locals init (object V_0, - @@ -6612,22 +6605,22 @@ .locals init (object V_0, diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs index 4debc4bd7c19d..bb02cebad09b3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs @@ -5674,5 +5674,226 @@ public static void Main(string[] args) var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe); var comp = CompileAndVerify(compilation, expectedOutput: "roslyn"); } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void OpenTypeMatch_01() + { + var source = +@"using System; +public class Base { } +public class Derived : Base { } +public class Program +{ + public static void Main(string[] args) + { + M(new Derived()); + M(new Base()); + } + public static void M(T x) where T: Base + { + Console.Write(x is Derived b0); + switch (x) + { + case Derived b1: + Console.Write(1); + break; + default: + Console.Write(0); + break; + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7); + compilation.VerifyDiagnostics( + // (13,28): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived' in C# 7. Please use language version 7.1 or greater. + // Console.Write(x is Derived b0); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived").WithArguments("T", "Derived", "7", "7.1").WithLocation(13, 28), + // (16,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived' in C# 7. Please use language version 7.1 or greater. + // case Derived b1: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived").WithArguments("T", "Derived", "7", "7.1").WithLocation(16, 18) + ); + compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1); + compilation.VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "True1False0"); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void OpenTypeMatch_02() + { + var source = +@"using System; +public class Base { } +public class Derived : Base { } +public class Program +{ + public static void Main(string[] args) + { + M(new Derived()); + M(new Base()); + } + public static void M(Base x) + { + Console.Write(x is T b0); + switch (x) + { + case T b1: + Console.Write(1); + break; + default: + Console.Write(0); + break; + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7); + compilation.VerifyDiagnostics( + // (13,28): error CS9003: An expression of type 'Base' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // Console.Write(x is T b0); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("Base", "T", "7", "7.1").WithLocation(13, 28), + // (16,18): error CS9003: An expression of type 'Base' cannot be handled by a pattern of type 'T' in C# 7. Please use language version 7.1 or greater. + // case T b1: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "T").WithArguments("Base", "T", "7", "7.1") + ); + compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1); + compilation.VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "True1False0"); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void OpenTypeMatch_03() + { + var source = +@"using System; +public class Base { } +public class Derived : Base { } +public class Program +{ + public static void Main(string[] args) + { + M(new Derived()); + M(new Base()); + } + public static void M(T x) where T: Base + { + Console.Write(x is Derived b0); + switch (x) + { + case Derived b1: + Console.Write(1); + break; + default: + Console.Write(0); + break; + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7); + compilation.VerifyDiagnostics( + // (13,28): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived' in C# 7. Please use language version 7.1 or greater. + // Console.Write(x is Derived b0); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived").WithArguments("T", "Derived", "7", "7.1").WithLocation(13, 28), + // (16,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived' in C# 7. Please use language version 7.1 or greater. + // case Derived b1: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived").WithArguments("T", "Derived", "7", "7.1").WithLocation(16, 18) + ); + compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1); + compilation.VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "True1False0"); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void OpenTypeMatch_04() + { + var source = +@"using System; +public class Base { } +class Container +{ + public class Derived : Base { } +} +public class Program +{ + public static void Main(string[] args) + { + M(new Container.Derived()); + M(new Base()); + } + public static void M(T x) where T: Base + { + Console.Write(x is Container.Derived b0); + switch (x) + { + case Container.Derived b1: + Console.Write(1); + break; + default: + Console.Write(0); + break; + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7); + compilation.VerifyDiagnostics( + // (16,28): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Container.Derived' in C# 7. Please use language version 7.1 or greater. + // Console.Write(x is Container.Derived b0); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container.Derived").WithArguments("T", "Container.Derived", "7", "7.1").WithLocation(16, 28), + // (19,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Container.Derived' in C# 7. Please use language version 7.1 or greater. + // case Container.Derived b1: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container.Derived").WithArguments("T", "Container.Derived", "7", "7.1").WithLocation(19, 18) + ); + compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1); + compilation.VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "True1False0"); + } + + [Fact, WorkItem(16195, "https://github.com/dotnet/roslyn/issues/16195")] + public void OpenTypeMatch_05() + { + var source = +@"using System; +public class Base { } +class Container +{ + public class Derived : Base { } +} +public class Program +{ + public static void Main(string[] args) + { + M(new Container.Derived[1]); + M(new Base[1]); + } + public static void M(T[] x) where T: Base + { + Console.Write(x is Container.Derived[] b0); + switch (x) + { + case Container.Derived[] b1: + Console.Write(1); + break; + default: + Console.Write(0); + break; + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7); + compilation.VerifyDiagnostics( + // (16,28): error CS9003: An expression of type 'T[]' cannot be handled by a pattern of type 'Container.Derived[]' in C# 7. Please use language version 7.1 or greater. + // Console.Write(x is Container.Derived[] b0); + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container.Derived[]").WithArguments("T[]", "Container.Derived[]", "7", "7.1").WithLocation(16, 28), + // (19,18): error CS9003: An expression of type 'T[]' cannot be handled by a pattern of type 'Container.Derived[]' in C# 7. Please use language version 7.1 or greater. + // case Container.Derived[] b1: + Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container.Derived[]").WithArguments("T[]", "Container.Derived[]", "7", "7.1").WithLocation(19, 18) + ); + compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular7_1); + compilation.VerifyDiagnostics(); + CompileAndVerify(compilation, expectedOutput: "True1False0"); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs index 02e5101e77f67..08fdf2ace7478 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternSwitchTests.cs @@ -2987,5 +2987,120 @@ static void Main() Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "case (string)null:").WithArguments("null").WithLocation(37, 13) ); } + + [Fact] + public void SubsumedCasesAreUnreachable_01() + { + var source = +@" +class Program +{ + static void Main(string[] args) + { + switch (args.Length) + { + case 1: + break; + case System.IComparable c: + break; + case 2: // error: subsumed + break; // unreachable + case int n: // error: subsumed + break; // unreachable + case var i: // error: subsumed + break; // unreachable + default: + break; // ok; default case is always considered reachable + } + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe); + compilation.VerifyDiagnostics( + // (12,13): error CS8120: The switch case has already been handled by a previous case. + // case 2: // error: subsumed + Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case 2:").WithLocation(12, 13), + // (14,18): error CS8120: The switch case has already been handled by a previous case. + // case int n: // error: subsumed + Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "int n").WithLocation(14, 18), + // (16,18): error CS8120: The switch case has already been handled by a previous case. + // case var i: // error: subsumed + Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "var i").WithLocation(16, 18), + // (13,17): warning CS0162: Unreachable code detected + // break; // unreachable + Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(13, 17), + // (15,17): warning CS0162: Unreachable code detected + // break; // unreachable + Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(15, 17), + // (17,17): warning CS0162: Unreachable code detected + // break; // unreachable + Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(17, 17) + ); + } + + [Fact] + public void SwitchTuple() + { + var source = +@" +class Program +{ + static void Main(string[] args) + { + switch ((x: 1, y: 2)) + { + case System.IComparable c: + break; + case System.ValueTuple x: // error: subsumed + break; // unreachable + default: + break; // ok; default case is always considered reachable + } + } +} +"; + var compilation = CreateStandardCompilation( + source, options: TestOptions.ReleaseExe, references: new[] { SystemRuntimeFacadeRef, ValueTupleRef }); + compilation.VerifyDiagnostics( + // (10,18): error CS8120: The switch case has already been handled by a previous case. + // case System.ValueTuple x: // error: subsumed + Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "System.ValueTuple x").WithLocation(10, 18), + // (11,17): warning CS0162: Unreachable code detected + // break; // unreachable + Diagnostic(ErrorCode.WRN_UnreachableCode, "break").WithLocation(11, 17) + ); + } + + [Fact] + public void ByValueThenByTypeTwice() + { + var source = +@" +class Program +{ + static bool b = false; + static int i = 2; + static void Main(string[] args) + { + switch (i) + { + case 1: + break; + case System.IComparable c when b: + break; + case System.IFormattable f when b: + break; + default: + System.Console.WriteLine(nameof(Main)); + break; + } + } +} +"; + var compilation = CreateStandardCompilation( + source, options: TestOptions.ReleaseExe, references: new[] { SystemRuntimeFacadeRef, ValueTupleRef }); + compilation.VerifyDiagnostics(); + var comp = CompileAndVerify(compilation, expectedOutput: "Main"); + } } } diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index 0ec664f2163fa..e95f1ea33c900 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -14,6 +14,7 @@ public static class TestOptions public static readonly CSharpParseOptions Script = new CSharpParseOptions(kind: SourceCodeKind.Script, documentationMode: DocumentationMode.None); public static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None); public static readonly CSharpParseOptions Regular6 = Regular.WithLanguageVersion(LanguageVersion.CSharp6); + public static readonly CSharpParseOptions Regular7 = Regular.WithLanguageVersion(LanguageVersion.CSharp7); public static readonly CSharpParseOptions Regular7_1 = Regular.WithLanguageVersion(LanguageVersion.CSharp7_1); public static readonly CSharpParseOptions RegularWithDocumentationComments = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.Diagnose); diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs index b4be75cb1764c..9848a492cf754 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/UpgradeProject/UpgradeProjectTests.cs @@ -179,6 +179,25 @@ class Program index: 1); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public async Task UpgradeProjectFromCSharp7ToCSharp7_1_B() + { + await TestLanguageVersionUpgradedAsync( +@"public class Base { } +public class Derived : Base { } +public class Program +{ + public static void M(T x) where T: Base + { + System.Console.Write(x is [|Derived|] b0); + } +} +", + LanguageVersion.CSharp7_1, + new CSharpParseOptions(LanguageVersion.CSharp7), + index: 1); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] public async Task UpgradeAllProjectsToDefault() { diff --git a/src/Features/CSharp/Portable/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs b/src/Features/CSharp/Portable/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs index 627811613da25..bed9a14231d66 100644 --- a/src/Features/CSharp/Portable/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs @@ -25,9 +25,10 @@ internal class CSharpUpgradeProjectCodeFixProvider : AbstractUpgradeProjectCodeF private const string CS8107 = nameof(CS8107); // error CS8059: Feature is not available in C# 7.0. Please use language version X or greater. private const string CS8302 = nameof(CS8302); // error CS8302: Feature is not available in C# 7.1. Please use language version X or greater. private const string CS8306 = nameof(CS8306); // error CS8306: ... Please use language version 7.1 or greater to access a un-named element by its inferred name. + private const string CS9003 = nameof(CS9003); // error CS9003: An expression of type '{0}' cannot be handled by a pattern of type '{1}' in C# {2}. Please use language version {3} or greater. public override ImmutableArray FixableDiagnosticIds { get; } = - ImmutableArray.Create(CS8022, CS8023, CS8024, CS8025, CS8026, CS8059, CS8107, CS8302, CS8306); + ImmutableArray.Create(CS8022, CS8023, CS8024, CS8025, CS8026, CS8059, CS8107, CS8302, CS8306, CS9003); public override string UpgradeThisProjectResource => CSharpFeaturesResources.Upgrade_this_project_to_csharp_language_version_0; public override string UpgradeAllProjectsResource => CSharpFeaturesResources.Upgrade_all_csharp_projects_to_language_version_0;