diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 68a7aebfd1ab00e31fed7d318010335933d125b3..7ba3eaaeeb9cfd697aa5f7a7876878ea449d1ff6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -225,7 +225,8 @@ private BoundPattern BindDiscardPattern(DiscardPatternSyntax node, TypeSymbol in // input value among many constant tests. convertedExpression = operand; } - else if (conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true) + else if (conversion.ConversionKind == ConversionKind.NullToPointer || + (conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true)) { convertedExpression = operand; } @@ -487,6 +488,13 @@ TypeSymbol BindRecursivePatternType(TypeSyntax typeSyntax, TypeSymbol inputType, private BoundPattern BindRecursivePattern(RecursivePatternSyntax node, TypeSymbol inputType, uint inputValEscape, bool hasErrors, DiagnosticBag diagnostics) { + if (inputType.IsPointerType()) + { + diagnostics.Add(ErrorCode.ERR_PointerTypeInPatternMatching, node.Location); + hasErrors = true; + inputType = CreateErrorType(); + } + TypeSyntax typeSyntax = node.Type; TypeSymbol declType = BindRecursivePatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType); inputValEscape = GetValEscape(declType, inputValEscape); @@ -770,6 +778,13 @@ private static FieldSymbol CheckIsTupleElement(SyntaxNode node, NamedTypeSymbol private BoundPattern BindVarPattern(VarPatternSyntax node, TypeSymbol inputType, uint inputValEscape, bool hasErrors, DiagnosticBag diagnostics) { + if (inputType.IsPointerType() && node.Designation.Kind() == SyntaxKind.ParenthesizedVariableDesignation) + { + diagnostics.Add(ErrorCode.ERR_PointerTypeInPatternMatching, node.Location); + hasErrors = true; + inputType = CreateErrorType(); + } + TypeSymbol declType = inputType; Symbol foundSymbol = BindTypeOrAliasOrKeyword(node.VarKeyword, node, diagnostics, out bool isVar); if (!isVar) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 00c4989ea99569ce52aaf8b549e852e2ff74d302..31464d565c3d2f04d91b2ce91fa1d71ef204468a 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -941,7 +941,7 @@ private void VisitPattern(BoundExpression expression, TypeSymbolWithAnnotations // https://github.com/dotnet/roslyn/issues/29873 We should only report such // diagnostics for locals that are set or checked explicitly within this method. - if (!expressionResultType.IsNull && expressionResultType.ValueCanBeNull() == false && whenTrue == NullableAnnotation.NullableBasedOnAnalysis) + if (!expressionResultType.IsPointerType() && !expressionResultType.IsNull && expressionResultType.ValueCanBeNull() == false && whenTrue == NullableAnnotation.NullableBasedOnAnalysis) { ReportDiagnostic(ErrorCode.HDN_NullCheckIsProbablyAlwaysFalse, pattern.Syntax); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs index a38706c95c5405b8724228cf948e32cf19347e6b..f272cba6c199a58825b6e0c66c8539e75a70e8b2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs @@ -1528,5 +1528,114 @@ public int M(bool b) ); CompileAndVerify(compilation, expectedOutput: "1 throw"); } + + [Fact] + public void PointerAsInput_01() + { + var source = +@"public class C +{ + public unsafe static void Main() + { + int x = 0; + M(1, null); + M(2, &x); + } + static unsafe void M(int i, int* p) + { + if (p is var x) + System.Console.Write(i); + } +} +"; + var compilation = CreatePatternCompilation(source, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + compilation.VerifyDiagnostics( + ); + var expectedOutput = @"12"; + var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: Verification.Skipped); + } + + [Fact] + public void PointerAsInput_02() + { + var source = +@"public class C +{ + public unsafe static void Main() + { + int x = 0; + M(1, null); + M(2, &x); + } + static unsafe void M(int i, int* p) + { + if (p switch { _ => true }) + System.Console.Write(i); + } +} +"; + var compilation = CreatePatternCompilation(source, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + compilation.VerifyDiagnostics( + ); + var expectedOutput = @"12"; + var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: Verification.Skipped); + } + + [Fact] + public void PointerAsInput_03() + { + var source = +@"public class C +{ + public unsafe static void Main() + { + int x = 0; + M(1, null); + M(2, &x); + } + static unsafe void M(int i, int* p) + { + if (p is null) + System.Console.Write(i); + } +} +"; + var compilation = CreatePatternCompilation(source, options: TestOptions.DebugExe.WithAllowUnsafe(true)); + compilation.VerifyDiagnostics( + ); + var expectedOutput = @"1"; + var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: Verification.Skipped); + } + + [Fact] + public void PointerAsInput_04() + { + var source = +@"public class C +{ + static unsafe void M(int* p) + { + if (p is {}) { } + if (p is 1) { } + if (p is var (x, y)) { } + } +} +"; + var compilation = CreatePatternCompilation(source, options: TestOptions.DebugDll.WithAllowUnsafe(true)); + compilation.VerifyDiagnostics( + // (5,18): error CS8521: Pattern-matching is not permitted for pointer types. + // if (p is {}) { } + Diagnostic(ErrorCode.ERR_PointerTypeInPatternMatching, "{}").WithLocation(5, 18), + // (6,18): error CS0266: Cannot implicitly convert type 'int' to 'int*'. An explicit conversion exists (are you missing a cast?) + // if (p is 1) { } + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "1").WithArguments("int", "int*").WithLocation(6, 18), + // (6,18): error CS0150: A constant value is expected + // if (p is 1) { } + Diagnostic(ErrorCode.ERR_ConstantExpected, "1").WithLocation(6, 18), + // (7,18): error CS8521: Pattern-matching is not permitted for pointer types. + // if (p is var (x, y)) { } + Diagnostic(ErrorCode.ERR_PointerTypeInPatternMatching, "var (x, y)").WithLocation(7, 18) + ); + } } }