提交 02a09423 编写于 作者: N Neal Gafter 提交者: GitHub

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.
上级 8058ba4e
......@@ -197,8 +197,26 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
//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;
}
}
}
......
......@@ -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)
......
......@@ -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);
}
}
......
......@@ -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<DiagnosticInfo> useSiteDiagnostics)
{
if (expressionType == patternType)
if ((object)expressionType == (object)patternType)
{
return true;
}
......
......@@ -7604,7 +7604,16 @@ internal class CSharpResources {
}
/// <summary>
/// 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 &apos;{0}&apos; cannot be handled by a pattern of type &apos;{1}&apos; in C# {2}. Please use language version {3} or greater..
/// </summary>
internal static string ERR_PatternWrongGenericTypeInVersion {
get {
return ResourceManager.GetString("ERR_PatternWrongGenericTypeInVersion", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An expression of type &apos;{0}&apos; cannot be handled by a pattern of type &apos;{1}&apos;..
/// </summary>
internal static string ERR_PatternWrongType {
get {
......
......@@ -4891,7 +4891,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<value>The switch case has already been handled by a previous case.</value>
</data>
<data name="ERR_PatternWrongType" xml:space="preserve">
<value>An expression of type {0} cannot be handled by a pattern of type {1}.</value>
<value>An expression of type '{0}' cannot be handled by a pattern of type '{1}'.</value>
</data>
<data name="WRN_AttributeIgnoredWhenPublicSigning" xml:space="preserve">
<value>Attribute '{0}' is ignored when public signing is specified.</value>
......@@ -5070,4 +5070,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_VoidInTuple" xml:space="preserve">
<value>A tuple may not contain a value of type 'void'.</value>
</data>
<data name="ERR_PatternWrongGenericTypeInVersion" xml:space="preserve">
<value>An expression of type '{0}' cannot be handled by a pattern of type '{1}' in C# {2}. Please use language version {3} or greater.</value>
</data>
</root>
......@@ -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
}
}
......@@ -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.
......
......@@ -177,7 +177,7 @@ private void LowerPatternSwitch(BoundExpression loweredExpression, BoundPatternS
}
}
if (defaultLabel != null)
if (defaultLabel != null && !loweredDecisionTree.MatchIsComplete)
{
Add(loweredDecisionTree, (e, t) => new DecisionTree.Guarded(loweredExpression, loweredExpression.Type, default(ImmutableArray<KeyValuePair<BoundExpression, BoundExpression>>), 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))
......
......@@ -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<BoundExpression>.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());
}
/// <summary>
......@@ -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<DiagnosticInfo> 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<T> 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<T> 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<T>(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<BoundExpression>(
_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)
);
}
}
}
......
......@@ -1102,6 +1102,11 @@ private MethodSymbol GetFieldFromHandleMethod(NamedTypeSymbol fieldContainer)
public BoundExpression Convert(TypeSymbol type, BoundExpression arg)
{
if (type == arg.Type)
{
return arg;
}
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
Conversion c = Compilation.Conversions.ClassifyConversionFromExpression(arg, type, ref useSiteDiagnostics);
Debug.Assert(c.Exists);
......
......@@ -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 @@ .maxstack 2
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 @@ .maxstack 2
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(
@"<symbols>
<methods>
......@@ -6600,7 +6594,6 @@ .maxstack 2
<slot kind=""temp"" />
<slot kind=""1"" offset=""11"" />
<slot kind=""temp"" />
<slot kind=""temp"" />
<slot kind=""35"" offset=""378"" />
<slot kind=""35"" offset=""413"" />
<slot kind=""1"" offset=""378"" />
......@@ -6612,22 +6605,22 @@ .maxstack 2
<entry offset=""0x0"" startLine=""4"" startColumn=""5"" endLine=""4"" endColumn=""6"" />
<entry offset=""0x1"" startLine=""5"" startColumn=""9"" endLine=""5"" endColumn=""19"" />
<entry offset=""0x5"" hidden=""true"" />
<entry offset=""0x60"" startLine=""7"" startColumn=""20"" endLine=""7"" endColumn=""34"" />
<entry offset=""0x65"" startLine=""9"" startColumn=""20"" endLine=""9"" endColumn=""34"" />
<entry offset=""0x6a"" startLine=""10"" startColumn=""17"" endLine=""10"" endColumn=""23"" />
<entry offset=""0x6c"" startLine=""11"" startColumn=""20"" endLine=""11"" endColumn=""34"" />
<entry offset=""0x71"" startLine=""13"" startColumn=""20"" endLine=""13"" endColumn=""34"" />
<entry offset=""0x76"" startLine=""14"" startColumn=""17"" endLine=""14"" endColumn=""23"" />
<entry offset=""0x78"" startLine=""16"" startColumn=""17"" endLine=""16"" endColumn=""23"" />
<entry offset=""0x7a"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" />
<entry offset=""0x7c"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" />
<entry offset=""0x81"" hidden=""true"" />
<entry offset=""0xba"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" />
<entry offset=""0xbc"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" />
<entry offset=""0xbe"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" />
<entry offset=""0xc3"" hidden=""true"" />
<entry offset=""0xcf"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" />
<entry offset=""0xd1"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" />
<entry offset=""0x5c"" startLine=""7"" startColumn=""20"" endLine=""7"" endColumn=""34"" />
<entry offset=""0x61"" startLine=""9"" startColumn=""20"" endLine=""9"" endColumn=""34"" />
<entry offset=""0x66"" startLine=""10"" startColumn=""17"" endLine=""10"" endColumn=""23"" />
<entry offset=""0x68"" startLine=""11"" startColumn=""20"" endLine=""11"" endColumn=""34"" />
<entry offset=""0x6d"" startLine=""13"" startColumn=""20"" endLine=""13"" endColumn=""34"" />
<entry offset=""0x72"" startLine=""14"" startColumn=""17"" endLine=""14"" endColumn=""23"" />
<entry offset=""0x74"" startLine=""16"" startColumn=""17"" endLine=""16"" endColumn=""23"" />
<entry offset=""0x76"" startLine=""18"" startColumn=""17"" endLine=""18"" endColumn=""23"" />
<entry offset=""0x78"" startLine=""20"" startColumn=""9"" endLine=""20"" endColumn=""19"" />
<entry offset=""0x7d"" hidden=""true"" />
<entry offset=""0xb2"" startLine=""23"" startColumn=""17"" endLine=""23"" endColumn=""23"" />
<entry offset=""0xb4"" startLine=""25"" startColumn=""17"" endLine=""25"" endColumn=""23"" />
<entry offset=""0xb6"" startLine=""27"" startColumn=""9"" endLine=""27"" endColumn=""19"" />
<entry offset=""0xbb"" hidden=""true"" />
<entry offset=""0xc7"" startLine=""30"" startColumn=""17"" endLine=""30"" endColumn=""23"" />
<entry offset=""0xc9"" startLine=""32"" startColumn=""5"" endLine=""32"" endColumn=""6"" />
</sequencePoints>
</method>
</methods>
......
......@@ -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>(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<Derived>(new Derived());
M<Derived>(new Base());
}
public static void M<T>(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<T> : Base { }
public class Program
{
public static void Main(string[] args)
{
M<Base>(new Derived<Base>());
M<Base>(new Base());
}
public static void M<T>(T x) where T: Base
{
Console.Write(x is Derived<T> b0);
switch (x)
{
case Derived<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 'T' cannot be handled by a pattern of type 'Derived<T>' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is Derived<T> b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived<T>").WithArguments("T", "Derived<T>", "7", "7.1").WithLocation(13, 28),
// (16,18): error CS9003: An expression of type 'T' cannot be handled by a pattern of type 'Derived<T>' in C# 7. Please use language version 7.1 or greater.
// case Derived<T> b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Derived<T>").WithArguments("T", "Derived<T>", "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<T>
{
public class Derived : Base { }
}
public class Program
{
public static void Main(string[] args)
{
M<Base>(new Container<Base>.Derived());
M<Base>(new Base());
}
public static void M<T>(T x) where T: Base
{
Console.Write(x is Container<T>.Derived b0);
switch (x)
{
case Container<T>.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<T>.Derived' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is Container<T>.Derived b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived").WithArguments("T", "Container<T>.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<T>.Derived' in C# 7. Please use language version 7.1 or greater.
// case Container<T>.Derived b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived").WithArguments("T", "Container<T>.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<T>
{
public class Derived : Base { }
}
public class Program
{
public static void Main(string[] args)
{
M<Base>(new Container<Base>.Derived[1]);
M<Base>(new Base[1]);
}
public static void M<T>(T[] x) where T: Base
{
Console.Write(x is Container<T>.Derived[] b0);
switch (x)
{
case Container<T>.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<T>.Derived[]' in C# 7. Please use language version 7.1 or greater.
// Console.Write(x is Container<T>.Derived[] b0);
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived[]").WithArguments("T[]", "Container<T>.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<T>.Derived[]' in C# 7. Please use language version 7.1 or greater.
// case Container<T>.Derived[] b1:
Diagnostic(ErrorCode.ERR_PatternWrongGenericTypeInVersion, "Container<T>.Derived[]").WithArguments("T[]", "Container<T>.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");
}
}
}
......@@ -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<int, int> 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<int, int> x: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "System.ValueTuple<int, int> 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");
}
}
}
......@@ -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);
......
......@@ -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>(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()
{
......
......@@ -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<string> 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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册