未验证 提交 b4b406b3 编写于 作者: N Neal Gafter 提交者: GitHub

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

A pointer type can match a var pattern (e.g. `var x`), a discard pattern (e.g. `_`), and a constant pattern with the value null (e.g. `null`). No other pattern forms would permit an input of a pointer type.
Fixes #30798
上级 ea9e099e
......@@ -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)
......
......@@ -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);
}
......
......@@ -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)
);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册