提交 868ca6b4 编写于 作者: N Neal Gafter 提交者: GitHub

Fix issues with `dynamic` in pattern-matching (#17202)

* Add breaking changes doc for post-VS2017
Fixes #17089 
上级 ef4646ac
Changes since VS2017 (C# 7)
===========================
- https://github.com/dotnet/roslyn/issues/17089
In C# 7, the compiler accepted a pattern of the form `dynamic identifier`, e.g. `if (e is dynamic x)`. This was accepted only if the expression `e` was statically of type `dynamic`. The compiler now rejects the use of the type `dynamic` for a pattern variable declaration, as no object's runtime type is `dynamic`.
......@@ -128,6 +128,9 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
return convertedExpression;
}
/// <summary>
/// Check that the pattern type is valid for the operand. Return true if an error was reported.
/// </summary>
private bool CheckValidPatternType(
CSharpSyntaxNode typeSyntax,
BoundExpression operand,
......@@ -139,6 +142,11 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
{
Debug.Assert((object)operandType != null);
Debug.Assert((object)patternType != null);
// Because we do not support recursive patterns, we always have an operand
Debug.Assert((object)operand != null);
// Once we support recursive patterns that will be relaxed.
if (operandType.IsErrorType() || patternType.IsErrorType())
{
return false;
......@@ -156,6 +164,12 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
}
else if (!isVar)
{
if (patternType.IsDynamic())
{
Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax);
return true;
}
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
Conversion conversion =
operand != null
......@@ -164,6 +178,10 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
diagnostics.Add(typeSyntax, useSiteDiagnostics);
switch (conversion.Kind)
{
case ConversionKind.ExplicitDynamic:
case ConversionKind.ImplicitDynamic:
// Since the input was `dynamic`, which is equivalent to `object`, there must also
// exist some unboxing, identity, or reference conversion as well, making the conversion legal.
case ConversionKind.Boxing:
case ConversionKind.ExplicitNullable:
case ConversionKind.ExplicitReference:
......
......@@ -528,11 +528,6 @@ private Conversion ClassifyImplicitBuiltInConversionSlow(TypeSymbol source, Type
return conversion;
}
//if (HasImplicitDynamicConversion(source, destination))
//{
// return Conversion.ImplicitDynamic;
//}
return Conversion.NoConversion;
}
......@@ -1861,14 +1856,13 @@ private static bool HasImplicitDynamicConversionFromExpression(TypeSymbol expres
// An implicit dynamic conversion exists from an expression of type dynamic to any type T.
Debug.Assert((object)destination != null);
return (object)expressionType != null && expressionType.Kind == SymbolKind.DynamicType && !destination.IsPointerType();
return expressionType?.Kind == SymbolKind.DynamicType && !destination.IsPointerType();
}
private static bool HasExplicitDynamicConversion(TypeSymbol source, TypeSymbol destination)
{
// SPEC: An explicit dynamic conversion exists from an expression of type dynamic to any type T.
// ISSUE: Note that this is exactly the same as an implicit dynamic conversion. Conversion of
// ISSUE: dynamic to int is both an explicit dynamic conversion and an implicit dynamic conversion.
// SPEC: An explicit dynamic conversion exists from an expression of [sic] type dynamic to any type T.
// ISSUE: The "an expression of" part of the spec is probably an error; see https://github.com/dotnet/csharplang/issues/132
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
......
......@@ -7495,6 +7495,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to It is not legal to use the type &apos;dynamic&apos; in a pattern..
/// </summary>
internal static string ERR_PatternDynamicType {
get {
return ResourceManager.GetString("ERR_PatternDynamicType", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The switch case has already been handled by a previous case..
/// </summary>
......
......@@ -5017,4 +5017,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_TypeForwardedToMultipleAssemblies" xml:space="preserve">
<value>Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'.</value>
</data>
<data name="ERR_PatternDynamicType" xml:space="preserve">
<value>It is not legal to use the type 'dynamic' in a pattern.</value>
</data>
</root>
\ No newline at end of file
......@@ -1457,6 +1457,7 @@ internal enum ErrorCode
ERR_AttributesInLocalFuncDecl = 8205,
ERR_TypeForwardedToMultipleAssemblies = 8206,
ERR_ExpressionTreeContainsDiscard = 8207,
ERR_PatternDynamicType = 8208,
#endregion more stragglers for C# 7
ERR_Merge_conflict_marker_encountered = 8300,
......
......@@ -5619,5 +5619,60 @@ public static void Main()
var expectedOutput = @"eval";
var comp = CompileAndVerify(compilation, expectedOutput: expectedOutput);
}
[Fact]
[WorkItem(17089, "https://github.com/dotnet/roslyn/issues/17089")]
public void Dynamic_01()
{
var source =
@"using System;
public class X
{
static void M1(dynamic d)
{
if (d is 1)
{
Console.Write('r');
}
else if (d is int i)
{
Console.Write('o');
}
else if (d is var z)
{
long l = z;
Console.Write('s');
}
}
static void M2(dynamic d)
{
switch (d)
{
case 1:
Console.Write('l');
break;
case int i:
Console.Write('y');
break;
case var z:
long l = z;
Console.Write('n');
break;
}
}
public static void Main(string[] args)
{
M1(1);
M1(2);
M1(3L);
M2(1);
M2(2);
M2(3L);
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }, options: TestOptions.ReleaseExe);
var comp = CompileAndVerify(compilation, expectedOutput: "roslyn");
}
}
}
......@@ -2756,7 +2756,7 @@ static void Main()
var comp = CompileAndVerify(compilation, expectedOutput: @"pass");
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/17089")]
[Fact]
[WorkItem(17089, "https://github.com/dotnet/roslyn/issues/17089")]
public void Dynamic_01()
{
......@@ -2769,12 +2769,79 @@ static void Main()
dynamic d = 1;
switch (d)
{
case dynamic x: // should be an error
case dynamic x: // error 1
break;
}
if (d is dynamic y) {} // error 2
if (d is var z) // ok
{
long l = z;
string s = z;
}
}
}
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics(
// (9,18): error CS8207: It is not legal to use the type 'dynamic' in a pattern.
// case dynamic x: // error 1
Diagnostic(ErrorCode.ERR_PatternDynamicType, "dynamic"),
// (12,18): error CS8207: It is not legal to use the type 'dynamic' in a pattern.
// if (d is dynamic y) {} // error 2
Diagnostic(ErrorCode.ERR_PatternDynamicType, "dynamic").WithLocation(12, 18)
);
}
[Fact]
[WorkItem(17089, "https://github.com/dotnet/roslyn/issues/17089")]
public void Dynamic_02()
{
var source =
@"
class Program
{
static void Main()
{
dynamic d = 1;
switch (d)
{
case int i: // should not be an error
case object x:
case var y: // OK, catches null
case var z: // error: subsumed
break;
}
switch (d)
{
case object x:
case null:
case var y: // error: subsumed
break;
}
switch (d)
{
case object x:
case 1: // error: subsumed
break;
}
switch (d)
{
case object x:
case int y: // error: subsumed
break;
}
switch (d)
{
case object x:
case (dynamic)null:
case (string)null: // error: subsumed
break;
}
switch (d)
{
case int i:
case long l:
case object o:
case var v:
break;
}
}
......@@ -2782,7 +2849,21 @@ static void Main()
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.ReleaseExe);
compilation.VerifyDiagnostics(
// Per https://github.com/dotnet/roslyn/issues/17089, the compiler does not produce correct diagnostics for this.
// (11,18): error CS8120: The switch case has already been handled by a previous case.
// case var z: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "var z").WithLocation(11, 18),
// (18,18): error CS8120: The switch case has already been handled by a previous case.
// case var y: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "var y").WithLocation(18, 18),
// (24,13): error CS8120: The switch case has already been handled by a previous case.
// case 1: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "case 1:"),
// (30,18): error CS8120: The switch case has already been handled by a previous case.
// case int y: // error: subsumed
Diagnostic(ErrorCode.ERR_PatternIsSubsumed, "int y").WithLocation(30, 18),
// (37,13): error CS0152: The switch statement contains multiple cases with the label value 'null'
// case (string)null: // error: subsumed
Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "case (string)null:").WithArguments("null").WithLocation(37, 13)
);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册