Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement constraints on pointer types as input to pattern-matching. #30968

Merged
merged 5 commits into from
Nov 19, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ internal BoundExpression ConvertPatternExpression(TypeSymbol inputType, CSharpSy
// input value among many constant tests.
convertedExpression = operand;
}
else if (conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true)
else if (conversion.ConversionKind == ConversionKind.NullToPointer ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to add some parenthesis here to make order of operations clear.

conversion.ConversionKind == ConversionKind.NoConversion && convertedExpression.Type?.IsErrorType() == true)
{
convertedExpression = operand;
}
Expand Down Expand Up @@ -512,6 +513,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);
Expand Down Expand Up @@ -795,6 +803,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,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.IsNullable == false && isNull == true)
if (!expressionResultType.IsPointerType() && !expressionResultType.IsNull && expressionResultType.IsNullable == false && isNull == true)
{
ReportDiagnostic(ErrorCode.HDN_NullCheckIsProbablyAlwaysFalse, pattern.Syntax);
}
Expand Down
109 changes: 109 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -921,5 +921,114 @@ class C1
Assert.Empty(symbolInfo.CandidateSymbols);
}
}

[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)
);
}
}
}