未验证 提交 53c5ac5a 编写于 作者: C Charles Stoner 提交者: GitHub

Add IncludeNullability option to Conversions (#24935)

上级 29905b3d
......@@ -33,12 +33,13 @@ internal Binder(CSharpCompilation compilation)
this.Compilation = compilation;
}
internal Binder(Binder next)
internal Binder(Binder next, Conversions conversions = null)
{
Debug.Assert(next != null);
_next = next;
this.Flags = next.Flags;
this.Compilation = next.Compilation;
_lazyConversions = conversions;
}
protected Binder(Binder next, BinderFlags flags)
......
......@@ -235,10 +235,10 @@ protected BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundEx
userDefinedConversion = CreateConversion(
syntax: syntax,
source: userDefinedConversion,
conversion: Conversions.ClassifyStandardConversion(null, conversionReturnType, conversion.BestUserDefinedConversionAnalysis.ToType, ref useSiteDiagnostics),
conversion: Conversions.ClassifyStandardConversion(null, conversionReturnType, conversionToType, ref useSiteDiagnostics),
isCast: false,
wasCompilerGenerated: true,
destination: conversion.BestUserDefinedConversionAnalysis.ToType,
destination: conversionToType,
diagnostics: diagnostics);
}
}
......@@ -253,7 +253,7 @@ protected BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundEx
@checked: false,
explicitCastInCode: isCast,
constantValueOpt: ConstantValue.NotAvailable,
type: conversion.BestUserDefinedConversionAnalysis.ToType)
type: conversionToType)
{ WasCompilerGenerated = true };
}
......
......@@ -2100,7 +2100,7 @@ private static NameSyntax GetNameSyntax(SyntaxNode syntax)
/// </summary>
/// <param name="syntax">Syntax node</param>
/// <param name="nameString">Plain text name</param>
private static NameSyntax GetNameSyntax(SyntaxNode syntax, out string nameString)
internal static NameSyntax GetNameSyntax(SyntaxNode syntax, out string nameString)
{
nameString = string.Empty;
while (true)
......@@ -2706,7 +2706,6 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio
var bestType = BestTypeInferrer.InferBestType(
boundInitializerExpressions,
this.Conversions,
includeNullability: false,
hadMultipleCandidates: out hadMultipleCandidates,
useSiteDiagnostics: ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);
......
......@@ -3586,7 +3586,6 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
trueExpr,
falseExpr,
this.Conversions,
includeNullability: false,
hadMultipleCandidates: out hadMultipleCandidates,
useSiteDiagnostics: ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);
......
......@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -10,25 +9,49 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed class BestTypeInferrer
{
private readonly Conversions _conversions;
private BestTypeInferrer(Conversions conversions)
public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<TypeSymbolWithAnnotations> types, Conversions conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
_conversions = conversions;
return GetBestType(types, conversions, ref useSiteDiagnostics);
}
// PROTOTYPE(NullableReferenceTypes): Remove includeNullability parameter. Nullability should be calculated in flow analysis.
public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<TypeSymbolWithAnnotations> types, Conversions conversions, bool includeNullability, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
private static bool? GetIsNullable(ImmutableArray<TypeSymbolWithAnnotations> types)
{
var inferrer = new BestTypeInferrer(conversions);
return inferrer.GetBestType(types, includeNullability, ref useSiteDiagnostics);
bool? isNullable = false;
foreach (var type in types)
{
if (type is null)
{
// PROTOTYPE(NullableReferenceTypes): Should ignore untyped
// expressions such as unbound lambdas and typeless tuples.
// See StaticNullChecking.LocalVar_Array_02 test.
isNullable = true;
continue;
}
if (!type.IsReferenceType)
{
return null;
}
switch (type.IsNullable)
{
case null:
if (isNullable == false)
{
isNullable = null;
}
break;
case true:
isNullable = true;
break;
}
}
return isNullable;
}
/// <remarks>
/// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification.
/// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
/// </remarks>
public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<BoundExpression> exprs, Conversions conversions, bool includeNullability, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<BoundExpression> exprs, Conversions conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// SPEC: 7.5.2.14 Finding the best common type of a set of expressions
// SPEC: In some cases, a common type needs to be inferred for a set of expressions. In particular, the element types of implicitly typed arrays and
......@@ -41,6 +64,7 @@ public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<BoundExpres
// SPEC: If no such S exists, the expressions have no best common type.
// All non-null types are candidates for best type inference.
bool includeNullability = conversions.IncludeNullability;
var candidateTypes = new HashSet<TypeSymbolWithAnnotations>(TypeSymbolWithAnnotations.EqualsComparer.Instance);
foreach (BoundExpression expr in exprs)
{
......@@ -61,14 +85,14 @@ public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<BoundExpres
hadMultipleCandidates = candidateTypes.Count > 1;
// Perform best type inference on candidate types.
return InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, includeNullability, ref useSiteDiagnostics);
return InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, ref useSiteDiagnostics);
}
/// <remarks>
/// This method implements best type inference for the conditional operator ?:.
/// NOTE: If either expression is an error type, we return error type as the inference result.
/// </remarks>
public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(BoundExpression expr1, BoundExpression expr2, Conversions conversions, bool includeNullability, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(BoundExpression expr1, BoundExpression expr2, Conversions conversions, out bool hadMultipleCandidates, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// SPEC: The second and third operands, x and y, of the ?: operator control the type of the conditional expression.
// SPEC: • If x has type X and y has type Y then
......@@ -79,6 +103,7 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
// SPEC: • Otherwise, no expression type can be determined, and a compile-time error occurs.
// A type is a candidate if all expressions are convertible to that type.
bool includeNullability = conversions.IncludeNullability;
var candidateTypes = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance();
var type1 = expr1.GetTypeAndNullability(includeNullability);
......@@ -92,7 +117,6 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
return type1;
}
// PROTOTYPE(NullableReferenceTypes): Consider nullability in conversion.
if (conversions.ClassifyImplicitConversionFromExpression(expr2, type1.TypeSymbol, ref useSiteDiagnostics).Exists)
{
candidateTypes.Add(type1);
......@@ -101,7 +125,7 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
var type2 = expr2.GetTypeAndNullability(includeNullability);
if ((object)type2 != null && !type2.Equals(type1, TypeCompareKind.ConsiderEverything))
if ((object)type2 != null)
{
if (type2.IsErrorType())
{
......@@ -110,7 +134,6 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
return type2;
}
// PROTOTYPE(NullableReferenceTypes): Consider nullability in conversion.
if (conversions.ClassifyImplicitConversionFromExpression(expr1, type2.TypeSymbol, ref useSiteDiagnostics).Exists)
{
candidateTypes.Add(type2);
......@@ -119,13 +142,10 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
hadMultipleCandidates = candidateTypes.Count > 1;
return InferBestType(candidateTypes.ToImmutableAndFree(), conversions, includeNullability, ref useSiteDiagnostics);
return InferBestType(candidateTypes.ToImmutableAndFree(), conversions, ref useSiteDiagnostics);
}
// PROTOTYPE(NullableReferenceTypes): Should be a two-pass approach
// similar to MethodTypeInferrer which compares with nullability and
// without and prefers the result with nullability.
private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnotations> types, bool includeNullability, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
private static TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnotations> types, Conversions conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// This code assumes that the types in the list are unique.
......@@ -155,7 +175,7 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot
}
else
{
var better = Better(best, type, ref useSiteDiagnostics);
var better = Better(best, type, conversions, ref useSiteDiagnostics);
if ((object)better == null)
{
......@@ -179,7 +199,7 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot
for (int i = 0; i < bestIndex; i++)
{
var type = types[i];
var better = Better(best, type, ref useSiteDiagnostics);
var better = Better(best, type, conversions, ref useSiteDiagnostics);
if (!best.Equals(better, TypeCompareKind.ConsiderEverything))
{
......@@ -187,25 +207,21 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot
}
}
// If any of the types are null, the result should be nullable.
// PROTOTYPE(NullableReferenceTypes): Should ignore untyped
// expressions such as unbound lambdas and typeless tuples.
// See StaticNullChecking.LocalVar_Array_02 test.
if (includeNullability &&
types.Any(t => (object)t == null) &&
best.IsReferenceType &&
best.IsNullable == false)
{
best = best.AsNullableReferenceType();
}
// If any of the types are nullable, the result should be nullable.
return conversions.IncludeNullability ? UpdateNullability(best, types) : best;
}
return best;
private static TypeSymbolWithAnnotations UpdateNullability(TypeSymbolWithAnnotations bestType, ImmutableArray<TypeSymbolWithAnnotations> types)
{
return bestType.IsReferenceType && bestType.IsNullable == false ?
TypeSymbolWithAnnotations.Create(bestType.TypeSymbol, GetIsNullable(types)) :
bestType;
}
/// <summary>
/// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names).
/// </summary>
private TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSymbolWithAnnotations type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
private static TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSymbolWithAnnotations type2, Conversions conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// Anything is better than an error sym.
if (type1.IsErrorType())
......@@ -218,8 +234,8 @@ private TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSy
return type1;
}
var t1tot2 = _conversions.ClassifyImplicitConversionFromType(type1.TypeSymbol, type2.TypeSymbol, ref useSiteDiagnostics).Exists;
var t2tot1 = _conversions.ClassifyImplicitConversionFromType(type2.TypeSymbol, type1.TypeSymbol, ref useSiteDiagnostics).Exists;
var t1tot2 = conversions.ClassifyImplicitConversionFromType(type1.TypeSymbol, type2.TypeSymbol, ref useSiteDiagnostics).Exists;
var t2tot1 = conversions.ClassifyImplicitConversionFromType(type2.TypeSymbol, type1.TypeSymbol, ref useSiteDiagnostics).Exists;
if (t1tot2 && t2tot1)
{
......@@ -235,7 +251,7 @@ private TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSy
if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames))
{
return MethodTypeInferrer.Merge(type1, type2, _conversions.CorLibrary);
return MethodTypeInferrer.Merge(type1, type2, conversions.CorLibrary);
}
return null;
......
......@@ -13,23 +13,28 @@ internal sealed class Conversions : ConversionsBase
private readonly Binder _binder;
public Conversions(Binder binder)
: this(binder, currentRecursionDepth: 0)
: this(binder, currentRecursionDepth: 0, includeNullability: false)
{
}
private Conversions(Binder binder, int currentRecursionDepth)
: base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth)
private Conversions(Binder binder, int currentRecursionDepth, bool includeNullability)
: base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth, includeNullability)
{
_binder = binder;
}
protected override ConversionsBase CreateInstance(int currentRecursionDepth)
{
return new Conversions(_binder, currentRecursionDepth);
return new Conversions(_binder, currentRecursionDepth, IncludeNullability);
}
private CSharpCompilation Compilation { get { return _binder.Compilation; } }
internal Conversions WithNullability(bool includeNullability)
{
return (IncludeNullability == includeNullability) ? this : new Conversions(_binder, currentRecursionDepth, includeNullability);
}
public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// Must be a bona fide delegate type, not an expression tree type.
......
......@@ -16,13 +16,17 @@ internal abstract partial class ConversionsBase
private const int MaximumRecursionDepth = 50;
protected readonly AssemblySymbol corLibrary;
private readonly int _currentRecursionDepth;
protected readonly int currentRecursionDepth;
protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth)
internal readonly bool IncludeNullability;
protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability)
{
Debug.Assert((object)corLibrary != null);
this.corLibrary = corLibrary;
_currentRecursionDepth = currentRecursionDepth;
this.currentRecursionDepth = currentRecursionDepth;
IncludeNullability = includeNullability;
}
public abstract Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics);
......@@ -47,7 +51,7 @@ public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourc
var sourceType = sourceExpression.Type;
//PERF: identity conversion is by far the most common implicit conversion, check for that first
if ((object)sourceType != null && HasIdentityConversion(sourceType, destination))
if ((object)sourceType != null && HasIdentityConversionInternal(sourceType, destination))
{
return Conversion.Identity;
}
......@@ -89,7 +93,7 @@ public Conversion ClassifyImplicitConversionFromType(TypeSymbol source, TypeSymb
Debug.Assert((object)destination != null);
//PERF: identity conversions are very common, check for that first.
if (HasIdentityConversion(source, destination))
if (HasIdentityConversionInternal(source, destination))
{
return Conversion.Identity;
}
......@@ -470,7 +474,7 @@ private Conversion ClassifyStandardImplicitConversion(TypeSymbol source, TypeSym
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
if (HasIdentityConversion(source, destination))
if (HasIdentityConversionInternal(source, destination))
{
return Conversion.Identity;
}
......@@ -677,10 +681,11 @@ private Conversion DeriveStandardExplicitFromOppositeStandardImplicitConversion(
/// * this does not check for variance conversions; if a type inherits from
/// IEnumerable&lt;string> then IEnumerable&lt;object> is not a base interface.
/// </summary>
public static bool IsBaseInterface(TypeSymbol baseType, TypeSymbol derivedType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
public bool IsBaseInterface(TypeSymbol baseType, TypeSymbol derivedType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)baseType != null);
Debug.Assert((object)derivedType != null);
if (!baseType.IsInterfaceType())
{
return false;
......@@ -694,7 +699,7 @@ public static bool IsBaseInterface(TypeSymbol baseType, TypeSymbol derivedType,
foreach (var iface in d.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
{
if (HasIdentityConversion(iface, baseType))
if (HasIdentityConversionInternal(iface, baseType))
{
return true;
}
......@@ -703,15 +708,6 @@ public static bool IsBaseInterface(TypeSymbol baseType, TypeSymbol derivedType,
return false;
}
// IsBaseClassOfClass determines whether the purported derived type is a class, and if so,
// if the purported base type is one of its base classes.
public static bool IsBaseClassOfClass(TypeSymbol baseType, TypeSymbol derivedType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)baseType != null);
Debug.Assert((object)derivedType != null);
return derivedType.IsClassType() && IsBaseClass(derivedType, baseType, ref useSiteDiagnostics);
}
// IsBaseClass returns true if and only if baseType is a base class of derivedType, period.
//
// * interfaces do not have base classes. (Structs, enums and classes other than object do.)
......@@ -720,7 +716,7 @@ public static bool IsBaseClassOfClass(TypeSymbol baseType, TypeSymbol derivedTyp
// * all base classes must be classes
// * dynamics are removed; if we have class D : B<dynamic> then B<object> is a
// base class of D. However, dynamic is never a base class of anything.
public static bool IsBaseClass(TypeSymbol derivedType, TypeSymbol baseType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
public bool IsBaseClass(TypeSymbol derivedType, TypeSymbol baseType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)derivedType != null);
Debug.Assert((object)baseType != null);
......@@ -733,7 +729,7 @@ public static bool IsBaseClass(TypeSymbol derivedType, TypeSymbol baseType, ref
for (TypeSymbol b = derivedType.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics); (object)b != null; b = b.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
{
if (HasIdentityConversion(b, baseType))
if (HasIdentityConversionInternal(b, baseType))
{
return true;
}
......@@ -1322,6 +1318,11 @@ internal bool HasCallerInfoStringConversion(TypeSymbol destination, ref HashSet<
}
public static bool HasIdentityConversion(TypeSymbol type1, TypeSymbol type2)
{
return HasIdentityConversionInternal(type1, type2, includeNullability: false);
}
private static bool HasIdentityConversionInternal(TypeSymbol type1, TypeSymbol type2, bool includeNullability)
{
// Spec (6.1.1):
// An identity conversion converts from any type to the same type. This conversion exists
......@@ -1335,15 +1336,46 @@ public static bool HasIdentityConversion(TypeSymbol type1, TypeSymbol type2)
Debug.Assert((object)type1 != null);
Debug.Assert((object)type2 != null);
return type1.Equals(type2, TypeCompareKind.AllIgnoreOptions);
// PROTOTYPE(NullableReferenceTypes): If two types differ only by nullability and
// one has IsNullable unset and the other doesn't, which do we choose in inference?
// See failing test IdentityConversion_ArrayInitializer_IsNullableNull.
var compareKind = includeNullability ?
TypeCompareKind.AllIgnoreOptions | TypeCompareKind.CompareNullableModifiersForReferenceTypes | TypeCompareKind.UnknownNullableModifierMatchesAny :
TypeCompareKind.AllIgnoreOptions;
return type1.Equals(type2, compareKind);
}
private bool HasIdentityConversionInternal(TypeSymbol type1, TypeSymbol type2)
{
return HasIdentityConversionInternal(type1, type2, IncludeNullability);
}
private static bool HasTopLevelNullabilityIdentityConversion(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations destination)
{
// PROTOTYPE(NullableReferenceTypes): If two types differ only by nullability and
// one has IsNullable unset and the other doesn't, which do we choose in inference?
// See failing test IdentityConversion_ArrayInitializer_IsNullableNull.
bool? sourceIsNullable = source.IsNullable;
bool? destinationIsNullable = destination.IsNullable;
return sourceIsNullable == null || destinationIsNullable == null || sourceIsNullable == destinationIsNullable;
}
internal static bool HasTopLevelNullabilityImplicitConversion(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations destination)
{
return HasTopLevelNullabilityImplicitConversion(source.IsNullable, destination.IsNullable);
}
internal static bool HasTopLevelNullabilityImplicitConversion(bool? sourceIsNullable, bool? destinationIsNullable)
{
return sourceIsNullable != true || destinationIsNullable != false;
}
public static bool HasIdentityConversionToAny<T>(T type, ArrayBuilder<T> targetTypes)
where T : TypeSymbol
{
for (int i = 0, n = targetTypes.Count; i < n; i++)
foreach (var targetType in targetTypes)
{
if (HasIdentityConversion(type, targetTypes[i]))
if (HasIdentityConversionInternal(type, targetType, includeNullability: false)) // PROTOTYPE(NullableReferenceTypes): Test this!
{
return true;
}
......@@ -1368,7 +1400,7 @@ public Conversion ClassifyImplicitExtensionMethodThisArgConversion(BoundExpressi
if ((object)sourceType != null)
{
if (HasIdentityConversion(sourceType, destination))
if (HasIdentityConversionInternal(sourceType, destination))
{
return Conversion.Identity;
}
......@@ -1391,7 +1423,8 @@ public Conversion ClassifyImplicitExtensionMethodThisArgConversion(BoundExpressi
destination,
ref useSiteDiagnostics,
ConversionKind.ImplicitTupleLiteral,
(ConversionsBase conversions, BoundExpression s, TypeSymbol d, ref HashSet<DiagnosticInfo> u, bool a) => conversions.ClassifyImplicitExtensionMethodThisArgConversion(s, s.Type, d, ref u),
(ConversionsBase conversions, BoundExpression s, TypeSymbol d, ref HashSet<DiagnosticInfo> u, bool a) =>
conversions.ClassifyImplicitExtensionMethodThisArgConversion(s, s.Type, d, ref u),
arg: false);
if (tupleConversion.Exists)
{
......@@ -1401,12 +1434,14 @@ public Conversion ClassifyImplicitExtensionMethodThisArgConversion(BoundExpressi
if ((object)sourceType != null)
{
Debug.Assert(!IncludeNullability); // PROTOTYPE(NullableReferenceTypes): Should fail in StaticNullChecking_FlowAnalysis.Conversions_TupleLiteralExtensionThis.
var tupleConversion = ClassifyTupleConversion(
sourceType,
destination,
ref useSiteDiagnostics,
ConversionKind.ImplicitTuple,
(ConversionsBase conversions, TypeSymbol s, TypeSymbol d, ref HashSet<DiagnosticInfo> u, bool a) => conversions.ClassifyImplicitExtensionMethodThisArgConversion(null, s, d, ref u),
(ConversionsBase conversions, TypeSymbolWithAnnotations s, TypeSymbolWithAnnotations d, ref HashSet<DiagnosticInfo> u, bool a) =>
conversions.ClassifyImplicitExtensionMethodThisArgConversion(null, s.TypeSymbol, d.TypeSymbol, ref u),
arg: false);
if (tupleConversion.Exists)
{
......@@ -1760,7 +1795,7 @@ private Conversion ClassifyImplicitNullableConversion(TypeSymbol source, TypeSym
return Conversion.NoConversion;
}
if (HasIdentityConversion(unwrappedSource, unwrappedDestination))
if (HasIdentityConversionInternal(unwrappedSource, unwrappedDestination))
{
return new Conversion(ConversionKind.ImplicitNullable, Conversion.IdentityUnderlying);
}
......@@ -1780,7 +1815,7 @@ private Conversion ClassifyImplicitNullableConversion(TypeSymbol source, TypeSym
}
private delegate Conversion ClassifyConversionFromExpressionDelegate(ConversionsBase conversions, BoundExpression sourceExpression, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool arg);
private delegate Conversion ClassifyConversionFromTypeDelegate(ConversionsBase conversions, TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool arg);
private delegate Conversion ClassifyConversionFromTypeDelegate(ConversionsBase conversions, TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool arg);
private Conversion GetImplicitTupleLiteralConversion(BoundTupleLiteral source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
......@@ -1828,6 +1863,7 @@ private Conversion GetExplicitTupleLiteralConversion(BoundTupleLiteral source, T
for (int i = 0; i < arguments.Length; i++)
{
var argument = arguments[i];
Debug.Assert(!IncludeNullability); // PROTOTYPE(NullableReferenceTypes): Should fail in StaticNullChecking_FlowAnalysis.Conversions_TupleLiteral.
var result = classifyConversion(this, argument, targetElementTypes[i].TypeSymbol, ref useSiteDiagnostics, arg);
if (!result.Exists)
{
......@@ -1848,18 +1884,27 @@ private Conversion ClassifyImplicitTupleConversion(TypeSymbol source, TypeSymbol
destination,
ref useSiteDiagnostics,
ConversionKind.ImplicitTuple,
(ConversionsBase conversions, TypeSymbol s, TypeSymbol d, ref HashSet<DiagnosticInfo> u, bool a) => conversions.ClassifyImplicitConversionFromType(s, d, ref u),
(ConversionsBase conversions, TypeSymbolWithAnnotations s, TypeSymbolWithAnnotations d, ref HashSet<DiagnosticInfo> u, bool a) =>
{
if (conversions.IncludeNullability && !HasTopLevelNullabilityImplicitConversion(s, d))
{
return Conversion.NoConversion;
}
return conversions.ClassifyImplicitConversionFromType(s.TypeSymbol, d.TypeSymbol, ref u);
},
arg: false);
}
private Conversion ClassifyExplicitTupleConversion(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics, bool forCast)
{
Debug.Assert(!IncludeNullability); // PROTOTYPE(NullableReferenceTypes): Should NullableWalker call ClassifyExplicitTupleConversion?
return ClassifyTupleConversion(
source,
destination,
ref useSiteDiagnostics,
ConversionKind.ExplicitTuple,
(ConversionsBase conversions, TypeSymbol s, TypeSymbol d, ref HashSet<DiagnosticInfo> u, bool a) => conversions.ClassifyConversionFromType(s, d, ref u, a),
(ConversionsBase conversions, TypeSymbolWithAnnotations s, TypeSymbolWithAnnotations d, ref HashSet<DiagnosticInfo> u, bool a) =>
conversions.ClassifyConversionFromType(s.TypeSymbol, d.TypeSymbol, ref u, a),
forCast);
}
......@@ -1884,7 +1929,7 @@ private Conversion ClassifyExplicitTupleConversion(TypeSymbol source, TypeSymbol
var nestedConversions = ArrayBuilder<Conversion>.GetInstance(sourceTypes.Length);
for (int i = 0; i < sourceTypes.Length; i++)
{
var conversion = classifyConversion(this, sourceTypes[i].TypeSymbol, destTypes[i].TypeSymbol, ref useSiteDiagnostics, arg);
var conversion = classifyConversion(this, sourceTypes[i], destTypes[i], ref useSiteDiagnostics, arg);
if (!conversion.Exists)
{
nestedConversions.Free();
......@@ -1918,7 +1963,7 @@ private Conversion ClassifyExplicitNullableConversion(TypeSymbol source, TypeSym
TypeSymbol unwrappedSource = source.StrippedType();
TypeSymbol unwrappedDestination = destination.StrippedType();
if (HasIdentityConversion(unwrappedSource, unwrappedDestination))
if (HasIdentityConversionInternal(unwrappedSource, unwrappedDestination))
{
return new Conversion(ConversionKind.ExplicitNullable, Conversion.IdentityUnderlying);
}
......@@ -1964,18 +2009,22 @@ private bool HasCovariantArrayConversion(TypeSymbol source, TypeSymbol destinati
}
// * S and T differ only in element type. In other words, S and T have the same number of dimensions.
if (!s.HasSameShapeAs(d))
{
return false;
}
// * Both SE and TE are reference types.
// * An implicit reference conversion exists from SE to TE.
return
s.HasSameShapeAs(d) &&
HasImplicitReferenceConversion(s.ElementType.TypeSymbol, d.ElementType.TypeSymbol, ref useSiteDiagnostics);
return HasImplicitReferenceConversion(s.ElementType, d.ElementType, ref useSiteDiagnostics);
}
public bool HasIdentityOrImplicitReferenceConversion(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
if (HasIdentityConversion(source, destination))
if (HasIdentityConversionInternal(source, destination))
{
return true;
}
......@@ -2057,6 +2106,15 @@ private bool HasArrayConversionToInterface(ArrayTypeSymbol source, TypeSymbol de
return HasIdentityOrImplicitReferenceConversion(source.ElementType.TypeSymbol, argument0, ref useSiteDiagnostics);
}
private bool HasImplicitReferenceConversion(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
if (IncludeNullability && !HasTopLevelNullabilityImplicitConversion(source, destination))
{
return false;
}
return HasImplicitReferenceConversion(source.TypeSymbol, destination.TypeSymbol, ref useSiteDiagnostics);
}
private bool HasImplicitReferenceConversion(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)source != null);
......@@ -2093,46 +2151,26 @@ private bool HasImplicitReferenceConversion(TypeSymbol source, TypeSymbol destin
return true;
}
if (HasImplicitConversionToInterface(source, destination, ref useSiteDiagnostics))
{
return true;
}
break;
return HasImplicitConversionToInterface(source, destination, ref useSiteDiagnostics);
case TypeKind.Interface:
// SPEC: From any interface-type S to any interface-type T, provided S is derived from T.
// NOTE: This handles variance conversions
if (HasImplicitConversionToInterface(source, destination, ref useSiteDiagnostics))
{
return true;
}
break;
return HasImplicitConversionToInterface(source, destination, ref useSiteDiagnostics);
case TypeKind.Delegate:
// SPEC: From any delegate-type to System.Delegate and the interfaces it implements.
// NOTE: This handles variance conversions.
if (HasImplicitConversionFromDelegate(source, destination, ref useSiteDiagnostics))
{
return true;
}
break;
return HasImplicitConversionFromDelegate(source, destination, ref useSiteDiagnostics);
case TypeKind.TypeParameter:
if (HasImplicitReferenceTypeParameterConversion((TypeParameterSymbol)source, destination, ref useSiteDiagnostics))
{
return true;
}
break;
return HasImplicitReferenceTypeParameterConversion((TypeParameterSymbol)source, destination, ref useSiteDiagnostics);
case TypeKind.Array:
// SPEC: From an array-type S ... to an array-type T, provided ...
// SPEC: From any array-type to System.Array and the interfaces it implements.
// SPEC: From a single-dimensional array type S[] to IList<T>, provided ...
if (HasImplicitConversionFromArray(source, destination, ref useSiteDiagnostics))
{
return true;
}
break;
return HasImplicitConversionFromArray(source, destination, ref useSiteDiagnostics);
}
// UNDONE: Implicit conversions involving type parameters that are known to be reference types.
......@@ -2149,24 +2187,26 @@ private bool HasImplicitConversionToInterface(TypeSymbol source, TypeSymbol dest
// * From any class type S to any interface type T provided S implements an interface
// convertible to T.
if (source.IsClassType())
{
return HasAnyBaseInterfaceConversion(source, destination, ref useSiteDiagnostics);
}
// * From any interface type S to any interface type T provided S implements an interface
// convertible to T.
// * From any interface type S to any interface type T provided S is not T and S is
// an interface convertible to T.
if (source.IsClassType() && HasAnyBaseInterfaceConversion(source, destination, ref useSiteDiagnostics))
if (source.IsInterfaceType())
{
return true;
}
if (source.IsInterfaceType() && HasAnyBaseInterfaceConversion(source, destination, ref useSiteDiagnostics))
{
return true;
}
if (HasAnyBaseInterfaceConversion(source, destination, ref useSiteDiagnostics))
{
return true;
}
if (source.IsInterfaceType() && source != destination && HasInterfaceVarianceConversion(source, destination, ref useSiteDiagnostics))
{
return true;
if (!HasIdentityConversionInternal(source, destination) && HasInterfaceVarianceConversion(source, destination, ref useSiteDiagnostics))
{
return true;
}
}
return false;
......@@ -2312,7 +2352,7 @@ private bool HasImplicitEffectiveBaseConversion(TypeParameterSymbol source, Type
{
// * From T to its effective base class C.
var effectiveBaseClass = source.EffectiveBaseClass(ref useSiteDiagnostics);
if (HasIdentityConversion(effectiveBaseClass, destination))
if (HasIdentityConversionInternal(effectiveBaseClass, destination))
{
return true;
}
......@@ -2440,7 +2480,7 @@ private bool HasVariantConversion(NamedTypeSymbol source, NamedTypeSymbol destin
// CONSIDER: A more rigorous solution would mimic the CLI approach, which uses
// a combination of requiring finite instantiation closures (see section 9.2 of
// the CLI spec) and records previous conversion steps to check for cycles.
if (_currentRecursionDepth >= MaximumRecursionDepth)
if (currentRecursionDepth >= MaximumRecursionDepth)
{
// NOTE: The spec doesn't really address what happens if there's an overflow
// in our conversion check. It's sort of implied that the conversion "proof"
......@@ -2456,16 +2496,16 @@ private bool HasVariantConversion(NamedTypeSymbol source, NamedTypeSymbol destin
return quickResult.Value();
}
return this.CreateInstance(_currentRecursionDepth + 1).
return this.CreateInstance(currentRecursionDepth + 1).
HasVariantConversionNoCycleCheck(source, destination, ref useSiteDiagnostics);
}
private static ThreeState HasVariantConversionQuick(NamedTypeSymbol source, NamedTypeSymbol destination)
private ThreeState HasVariantConversionQuick(NamedTypeSymbol source, NamedTypeSymbol destination)
{
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
if (HasIdentityConversion(source, destination))
if (HasIdentityConversionInternal(source, destination))
{
return ThreeState.True;
}
......@@ -2484,9 +2524,9 @@ private bool HasVariantConversionNoCycleCheck(NamedTypeSymbol source, NamedTypeS
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
var typeParameters = ArrayBuilder<TypeSymbol>.GetInstance();
var sourceTypeArguments = ArrayBuilder<TypeSymbol>.GetInstance();
var destinationTypeArguments = ArrayBuilder<TypeSymbol>.GetInstance();
var typeParameters = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance();
var sourceTypeArguments = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance();
var destinationTypeArguments = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance();
try
{
......@@ -2499,29 +2539,39 @@ private bool HasVariantConversionNoCycleCheck(NamedTypeSymbol source, NamedTypeS
for (int paramIndex = 0; paramIndex < typeParameters.Count; ++paramIndex)
{
TypeSymbol sourceTypeArgument = sourceTypeArguments[paramIndex];
TypeSymbol destinationTypeArgument = destinationTypeArguments[paramIndex];
var sourceTypeArgument = sourceTypeArguments[paramIndex];
var destinationTypeArgument = destinationTypeArguments[paramIndex];
// If they're identical then this one is automatically good, so skip it.
if (HasIdentityConversion(sourceTypeArgument, destinationTypeArgument))
if (HasIdentityConversionInternal(sourceTypeArgument.TypeSymbol, destinationTypeArgument.TypeSymbol) &&
(!IncludeNullability || HasTopLevelNullabilityIdentityConversion(sourceTypeArgument, destinationTypeArgument)))
{
continue;
}
TypeParameterSymbol typeParameterSymbol = (TypeParameterSymbol)typeParameters[paramIndex];
if (typeParameterSymbol.Variance == VarianceKind.None)
{
return false;
}
var typeParameterSymbol = (TypeParameterSymbol)typeParameters[paramIndex].TypeSymbol;
if (typeParameterSymbol.Variance == VarianceKind.Out && !HasImplicitReferenceConversion(sourceTypeArgument, destinationTypeArgument, ref useSiteDiagnostics))
switch (typeParameterSymbol.Variance)
{
return false;
}
case VarianceKind.None:
return false;
if (typeParameterSymbol.Variance == VarianceKind.In && !HasImplicitReferenceConversion(destinationTypeArgument, sourceTypeArgument, ref useSiteDiagnostics))
{
return false;
case VarianceKind.Out:
if (!HasImplicitReferenceConversion(sourceTypeArgument, destinationTypeArgument, ref useSiteDiagnostics))
{
return false;
}
break;
case VarianceKind.In:
if (!HasImplicitReferenceConversion(destinationTypeArgument, sourceTypeArgument, ref useSiteDiagnostics))
{
return false;
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(typeParameterSymbol.Variance);
}
}
}
......@@ -2664,7 +2714,7 @@ private bool HasIdentityOrReferenceConversion(TypeSymbol source, TypeSymbol dest
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
if (HasIdentityConversion(source, destination))
if (HasIdentityConversionInternal(source, destination))
{
return true;
}
......@@ -2703,7 +2753,7 @@ private bool HasExplicitReferenceConversion(TypeSymbol source, TypeSymbol destin
}
// SPEC: From any class-type S to any class-type T, provided S is a base class of T.
if (IsBaseClassOfClass(source, destination, ref useSiteDiagnostics))
if (destination.IsClassType() && IsBaseClass(destination, source, ref useSiteDiagnostics))
{
return true;
}
......@@ -2774,7 +2824,7 @@ private bool HasExplicitReferenceTypeParameterConversion(TypeSymbol source, Type
{
for (var type = t.EffectiveBaseClass(ref useSiteDiagnostics); (object)type != null; type = type.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
{
if (HasIdentityConversion(type, source))
if (HasIdentityConversionInternal(type, source))
{
return true;
}
......@@ -2889,7 +2939,7 @@ private bool HasExplicitDelegateConversion(TypeSymbol source, TypeSymbol destina
var destinationType = (NamedTypeSymbol)destination;
var original = sourceType.OriginalDefinition;
if (HasIdentityConversion(source, destination))
if (HasIdentityConversionInternal(source, destination))
{
return false;
}
......@@ -2910,7 +2960,7 @@ private bool HasExplicitDelegateConversion(TypeSymbol source, TypeSymbol destina
switch (original.TypeParameters[i].Variance)
{
case VarianceKind.None:
if (!HasIdentityConversion(sourceArg, destinationArg))
if (!HasIdentityConversionInternal(sourceArg, destinationArg))
{
return false;
}
......@@ -2924,7 +2974,7 @@ private bool HasExplicitDelegateConversion(TypeSymbol source, TypeSymbol destina
break;
case VarianceKind.In:
bool hasIdentityConversion = HasIdentityConversion(sourceArg, destinationArg);
bool hasIdentityConversion = HasIdentityConversionInternal(sourceArg, destinationArg);
bool bothAreReferenceTypes = sourceArg.IsReferenceType && destinationArg.IsReferenceType;
if (!(hasIdentityConversion || bothAreReferenceTypes))
{
......@@ -2972,7 +3022,7 @@ private bool HasExplicitArrayConversion(TypeSymbol source, TypeSymbol destinatio
foreach (var iface in this.corLibrary.GetDeclaredSpecialType(SpecialType.System_Array).AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteDiagnostics))
{
if (HasIdentityConversion(iface, source))
if (HasIdentityConversionInternal(iface, source))
{
return true;
}
......@@ -3010,7 +3060,7 @@ private bool HasExplicitArrayConversion(TypeSymbol source, TypeSymbol destinatio
var sourceElement = ((NamedTypeSymbol)source).TypeArgumentWithDefinitionUseSiteDiagnostics(0, ref useSiteDiagnostics).TypeSymbol;
var destinationElement = destinationArray.ElementType.TypeSymbol;
if (HasIdentityConversion(sourceElement, destinationElement))
if (HasIdentityConversionInternal(sourceElement, destinationElement))
{
return true;
}
......
......@@ -10,18 +10,18 @@ namespace Microsoft.CodeAnalysis.CSharp
internal sealed class TypeConversions : ConversionsBase
{
public TypeConversions(AssemblySymbol corLibrary)
: this(corLibrary, currentRecursionDepth: 0)
: this(corLibrary, currentRecursionDepth: 0, includeNullability: false)
{
}
private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth)
: base(corLibrary, currentRecursionDepth)
private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability)
: base(corLibrary, currentRecursionDepth, includeNullability)
{
}
protected override ConversionsBase CreateInstance(int currentRecursionDepth)
{
return new TypeConversions(this.corLibrary, currentRecursionDepth);
return new TypeConversions(this.corLibrary, currentRecursionDepth, IncludeNullability);
}
public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
......
......@@ -90,8 +90,6 @@ private enum Dependency
private readonly ImmutableArray<TypeSymbolWithAnnotations> _formalParameterTypes;
private readonly ImmutableArray<RefKind> _formalParameterRefKinds;
private readonly ImmutableArray<BoundExpression> _arguments;
// PROTOTYPE(NullableReferenceTypes): Remove conditional and infer nullability always.
private readonly bool _includeNullability;
private readonly TypeSymbolWithAnnotations[] _fixedResults;
private readonly HashSet<TypeSymbolWithAnnotations>[] _exactBounds;
......@@ -214,8 +212,7 @@ private enum Dependency
ImmutableArray<RefKind> formalParameterRefKinds, // Optional; assume all value if missing.
ImmutableArray<BoundExpression> arguments,// Required; in scenarios like method group conversions where there are
// no arguments per se we cons up some fake arguments.
ref HashSet<DiagnosticInfo> useSiteDiagnostics,
bool includeNullability = false)
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert(!methodTypeParameters.IsDefault);
Debug.Assert(methodTypeParameters.Length > 0);
......@@ -239,8 +236,7 @@ private enum Dependency
constructedContainingTypeOfMethod,
formalParameterTypes,
formalParameterRefKinds,
arguments,
includeNullability);
arguments);
return inferrer.InferTypeArgs(binder, ref useSiteDiagnostics);
}
......@@ -259,8 +255,7 @@ private enum Dependency
NamedTypeSymbol constructedContainingTypeOfMethod,
ImmutableArray<TypeSymbolWithAnnotations> formalParameterTypes,
ImmutableArray<RefKind> formalParameterRefKinds,
ImmutableArray<BoundExpression> arguments,
bool includeNullability)
ImmutableArray<BoundExpression> arguments)
{
_conversions = conversions;
_methodTypeParameters = methodTypeParameters;
......@@ -268,7 +263,6 @@ private enum Dependency
_formalParameterTypes = formalParameterTypes;
_formalParameterRefKinds = formalParameterRefKinds;
_arguments = arguments;
_includeNullability = includeNullability;
_fixedResults = new TypeSymbolWithAnnotations[methodTypeParameters.Length];
_exactBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
_upperBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
......@@ -2343,6 +2337,9 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
// Fix considering nullability, if possible, otherwise fix ignoring nullability.
// PROTOTYPE(NullableReferenceTypes): Avoid calling Fix ignoring nullability if the nullability call succeeds.
// PROTOTYPE(NullableReferenceTypes): Avoid comparing with and without nullability here.
// Initial binding should use includeNullability: false, and NullableWalker should use
// includeNullability: true, and NullableWalker should compare the two.
HashSet<DiagnosticInfo> ignoredDiagnostics = null;
var withNullability = Fix(exact, lower, upper, ref ignoredDiagnostics, _conversions, includeNullability: true);
var withoutNullability = Fix(exact, lower, upper, ref useSiteDiagnostics, _conversions, includeNullability: false);
......@@ -2534,13 +2531,21 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
private bool? IsNullable(BoundExpression expr)
{
if (!_includeNullability)
if (!_conversions.IncludeNullability)
{
return false;
}
return expr.IsNullable();
}
internal static TypeSymbol Merge(TypeSymbol first, TypeSymbol second, AssemblySymbol corLibrary)
{
var firstWithAnnotations = TypeSymbolWithAnnotations.Create(first);
var secondWithAnnotations = TypeSymbolWithAnnotations.Create(second);
return MergeNullability(MergeTupleNames(MergeDynamic(firstWithAnnotations, secondWithAnnotations, corLibrary), secondWithAnnotations), secondWithAnnotations).TypeSymbol;
}
// PROTOTYPE(NullableReferenceTypes): Remove this overload.
internal static TypeSymbolWithAnnotations Merge(TypeSymbolWithAnnotations first, TypeSymbolWithAnnotations second, AssemblySymbol corLibrary)
{
return MergeNullability(MergeTupleNames(MergeDynamic(first, second, corLibrary), second), second);
......@@ -2786,7 +2791,6 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray<NamedTy
ConversionsBase conversions,
MethodSymbol method,
ImmutableArray<BoundExpression> arguments,
bool includeNullability,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert((object)method != null);
......@@ -2809,8 +2813,7 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray<NamedTy
constructedFromMethod.ContainingType,
constructedFromMethod.GetParameterTypes(),
constructedFromMethod.ParameterRefKinds,
arguments,
includeNullability);
arguments);
if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics))
{
......
......@@ -3080,7 +3080,6 @@ internal EffectiveParameters(ImmutableArray<TypeSymbolWithAnnotations> types, Im
_binder.Conversions,
method,
args,
includeNullability: Compilation.IsFeatureEnabled(MessageID.IDS_FeatureStaticNullChecking),
useSiteDiagnostics: ref useSiteDiagnostics);
if (inferredFromFirstArgument.IsDefault)
{
......
......@@ -222,7 +222,7 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres
switch (expr.Kind)
{
case BoundKind.SuppressNullableWarningExpression:
return false;
return null;
case BoundKind.Local:
{
var local = (BoundLocal)expr;
......@@ -245,9 +245,15 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres
var right = op.RightOperand.IsNullable();
return (left == true) ? right : left;
}
case BoundKind.ThisReference:
case BoundKind.BaseReference:
case BoundKind.NewT:
case BoundKind.ObjectCreationExpression:
case BoundKind.DelegateCreationExpression:
return false;
case BoundKind.NoPiaObjectCreationExpression:
case BoundKind.InterpolatedString:
case BoundKind.TypeOfOperator:
case BoundKind.NameOfOperator:
case BoundKind.TupleLiteral:
return false;
case BoundKind.DefaultExpression:
......@@ -263,9 +269,16 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres
}
var constant = expr.ConstantValue;
if (constant != null && constant.IsNull)
if (constant != null)
{
return true;
if (constant.IsNull)
{
return true;
}
if (expr.Type?.IsReferenceType == true)
{
return false;
}
}
return null;
......
......@@ -140,8 +140,8 @@ public TypeSymbolWithAnnotations InferredReturnType(ref HashSet<DiagnosticInfo>
}
else
{
// PROTOTYPE(NullableReferenceTypes): Should pass includeNullability: false.
bestResultType = BestTypeInferrer.InferBestType(resultTypes, binder.Conversions, includeNullability, ref useSiteDiagnostics);
var conversions = binder.Conversions.WithNullability(includeNullability);
bestResultType = BestTypeInferrer.InferBestType(resultTypes, conversions, ref useSiteDiagnostics);
}
if (!isAsync)
......
......@@ -319,7 +319,7 @@ protected static bool HasInitializer(FieldSymbol field)
/// <summary>
/// Perform data flow analysis, reporting all necessary diagnostics.
/// </summary>
public static void Analyze(CSharpCompilation compilation, Symbol member, BoundNode node, DiagnosticBag diagnostics, bool requireOutParamsAssigned = true)
public static void Analyze(CSharpCompilation compilation, MethodSymbol member, BoundNode node, DiagnosticBag diagnostics, bool requireOutParamsAssigned = true)
{
Debug.Assert(diagnostics != null);
......
......@@ -11,6 +11,7 @@
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
......@@ -36,6 +37,8 @@ internal sealed partial class NullableWalker : DataFlowPassBase<NullableWalker.L
// PROTOTYPE(NullableReferenceTypes): Remove the Binder if possible.
private readonly Binder _binder;
private readonly Conversions _conversions;
/// <summary>
/// Invalid type, used only to catch Visit methods that do not set
/// _result.Type. See VisitExpressionWithoutStackGuard.
......@@ -65,15 +68,19 @@ protected override void Free()
private NullableWalker(
CSharpCompilation compilation,
Symbol member,
MethodSymbol member,
BoundNode node,
bool includeNonNullableWarnings)
: base(compilation, member, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: false)
{
_sourceAssembly = ((object)member == null) ? null : (SourceAssemblySymbol)member.ContainingAssembly;
this._currentMethodOrLambda = member as MethodSymbol;
this._currentMethodOrLambda = member;
_includeNonNullableWarnings = includeNonNullableWarnings;
// PROTOTYPE(NullableReferenceTypes): Do we really need a Binder?
// If so, are we interested in an InMethodBinder specifically?
_binder = compilation.GetBinderFactory(node.SyntaxTree).GetBinder(node.Syntax);
Debug.Assert(!_binder.Conversions.IncludeNullability);
_conversions = _binder.Conversions.WithNullability(true);
}
protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()
......@@ -98,10 +105,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
return pendingReturns;
}
/// <summary>
/// Perform data flow analysis, reporting all necessary diagnostics.
/// </summary>
public static void Analyze(CSharpCompilation compilation, Symbol member, BoundNode node, DiagnosticBag diagnostics)
public static void Analyze(CSharpCompilation compilation, MethodSymbol member, BoundNode node, DiagnosticBag diagnostics)
{
Debug.Assert(diagnostics != null);
......@@ -321,6 +325,18 @@ private new void VisitLvalue(BoundExpression node)
}
}
private Result VisitRvalueWithResult(BoundExpression node)
{
base.VisitRvalue(node);
return _result;
}
private static object GetTypeAsDiagnosticArgument(TypeSymbol typeOpt)
{
// PROTOTYPE(NullableReferenceTypes): Avoid hardcoded string.
return typeOpt ?? (object)"<null>";
}
/// <summary>
/// Report nullable mismatch warnings and optionally update tracked value on assignment.
/// </summary>
......@@ -569,7 +585,7 @@ private void EnterParameter(ParameterSymbol parameter)
var paramType = parameter.Type.TypeSymbol;
if (EmptyStructTypeCache.IsTrackableStructType(paramType))
{
InheritNullableStateOfTrackableStruct(paramType, slot, -1, parameter.RefKind != RefKind.None, slotWatermark: GetSlotWatermark());
InheritNullableStateOfTrackableStruct(paramType, slot, valueSlot: -1, isByRefTarget: parameter.RefKind != RefKind.None, slotWatermark: GetSlotWatermark());
}
}
}
......@@ -673,16 +689,7 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n
public override BoundNode VisitLocal(BoundLocal node)
{
// Note: the caller should avoid allowing this to be called for the left-hand-side of
// an assignment (if a simple variable or this-qualified or deconstruction variables) or an out parameter.
// That's because this code assumes the variable is being read, not written.
Debug.Assert(!IsConditionalState);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
_result = GetAdjustedResult(GetDeclaredLocalResult(node.LocalSymbol));
}
_result = GetAdjustedResult(GetDeclaredLocalResult(node.LocalSymbol));
return null;
}
......@@ -717,10 +724,10 @@ protected override BoundExpression VisitExpressionWithoutStackGuard(BoundExpress
_result = _invalidType; // PROTOTYPE(NullableReferenceTypes): Move to `Visit` method?
var result = base.VisitExpressionWithoutStackGuard(node);
#if DEBUG
// Verify Visit method set ResultType.
// Verify Visit method set _result.
if (!IsConditionalState)
{
var resultType = _result.Type;
TypeSymbolWithAnnotations resultType = _result.Type;
Debug.Assert((object)resultType != _invalidType);
Debug.Assert((object)resultType == null || AreCloseEnough(resultType.TypeSymbol, node.Type));
}
......@@ -770,7 +777,7 @@ private void VisitObjectOrDynamicObjectCreation(BoundExpression node, BoundExpre
slot = GetOrCreateSlot(receiver);
if (slot > 0 && isTrackableStructType)
{
InheritNullableStateOfTrackableStruct(type, slot, -1, false, slotWatermark: GetSlotWatermark());
InheritNullableStateOfTrackableStruct(type, slot, valueSlot: -1, isByRefTarget: false, slotWatermark: GetSlotWatermark());
}
}
}
......@@ -816,10 +823,9 @@ private void VisitObjectCreationInitializer(Symbol containingSymbol, int contain
}
break;
default:
VisitRvalue(node);
Result result = VisitRvalueWithResult(node);
if ((object)containingSymbol != null)
{
var result = _result;
var type = GetTypeOrReturnTypeWithAdjustedNullableAnnotations(containingSymbol);
TrackNullableStateForAssignment(node, containingSlot, type, node, result.Type, result.Slot);
}
......@@ -896,16 +902,14 @@ private ObjectCreationPlaceholderLocal GetOrCreateObjectCreationPlaceholder(Boun
public override BoundNode VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node)
{
Debug.Assert(!IsConditionalState);
int receiverSlot = -1;
int receiverSlot = -1;
var arguments = node.Arguments;
var constructor = node.Constructor;
for (int i = 0; i < arguments.Length; i++)
{
var argument = arguments[i];
VisitRvalue(argument);
Result argumentResult = _result;
Result argumentResult = VisitRvalueWithResult(argument);
var parameter = constructor.Parameters[i];
WarnOnNullReferenceArgument(argument, argumentResult.Type, parameter, expanded: false);
......@@ -950,38 +954,54 @@ private ArrayTypeSymbol VisitArrayInitializer(BoundArrayCreation node)
var arrayType = (ArrayTypeSymbol)node.Type;
var elementType = arrayType.ElementType;
var elementBuilder = ArrayBuilder<BoundExpression>.GetInstance();
GetArrayElements(node.InitializerOpt, elementBuilder);
BoundArrayInitialization initialization = node.InitializerOpt;
var elementBuilder = ArrayBuilder<BoundExpression>.GetInstance(initialization.Initializers.Length);
GetArrayElements(initialization, elementBuilder);
var typeBuilder = (node.Syntax.Kind() == SyntaxKind.ImplicitArrayCreationExpression) ? ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance(elementBuilder.Count) : null;
foreach (var element in elementBuilder)
// PROTOTYPE(NullableReferenceType): Removing and recalculating conversions should not
// be necessary for explicitly typed arrays. In those cases, VisitConversion should warn
// on nullability mismatch (although we'll need to ensure we handle the case where
// initial binding calculated an Identity conversion, even though nullability was distinct.
int n = elementBuilder.Count;
var resultBuilder = ArrayBuilder<Result>.GetInstance(n);
for (int i = 0; i < n; i++)
{
VisitRvalue(element);
Result elementResult = _result;
if (typeBuilder != null)
{
typeBuilder.Add(elementResult.Type);
}
else if (elementType?.IsReferenceType == true)
BoundExpression element = RemoveImplicitConversions(elementBuilder[i]);
elementBuilder[i] = element;
resultBuilder.Add(VisitRvalueWithResult(element));
}
// PROTOTYPE(NullableReferenceType): Record in the BoundArrayCreation
// whether the array was implicitly typed, rather than relying on syntax.
if (node.Syntax.Kind() == SyntaxKind.ImplicitArrayCreationExpression)
{
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
var resultTypes = resultBuilder.SelectAsArray(r => r.Type);
// PROTOTYPE(NullableReferenceType): Initial binding calls InferBestType(ImmutableArray<BoundExpression>, ...)
// overload. Why are we calling InferBestType(ImmutableArray<TypeSymbolWithAnnotations>, ...) here?
// PROTOTYPE(NullableReferenceType): InferBestType(ImmutableArray<BoundExpression>, ...)
// uses a HashSet<TypeSymbol> to reduce the candidates to the unique types before comparing.
// Should do the same here.
var bestType = BestTypeInferrer.InferBestType(resultTypes, _conversions, useSiteDiagnostics: ref useSiteDiagnostics);
if ((object)bestType != null)
{
TrackNullableStateForAssignment(element, -1, elementType, element, elementResult.Type, elementResult.Slot);
elementType = bestType;
}
arrayType = arrayType.WithElementType(elementType);
}
if (typeBuilder != null)
TypeSymbol elementTypeSymbol = elementType?.TypeSymbol;
bool elementTypeIsReferenceType = elementType?.IsReferenceType == true;
for (int i = 0; i < n; i++)
{
var elementTypes = typeBuilder.ToImmutableAndFree();
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
elementType = BestTypeInferrer.InferBestType(elementTypes, compilation.Conversions, includeNullability: true, useSiteDiagnostics: ref useSiteDiagnostics);
if ((object)elementType != null)
var element = elementBuilder[i];
var result = GenerateConversionForAssignment(element, elementTypeSymbol, resultBuilder[i]);
if (elementTypeIsReferenceType)
{
for (int i = 0; i < elementTypes.Length; i++)
{
ReportNullabilityMismatchIfAny(elementBuilder[i], elementType, elementTypes[i]);
}
arrayType = arrayType.WithElementType(elementType);
TrackNullableStateForAssignment(element, -1, elementType, element, result.Type, result.Slot);
}
}
resultBuilder.Free();
elementBuilder.Free();
_result = _invalidType;
......@@ -1021,11 +1041,7 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node)
VisitRvalue(i);
}
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
_result = type?.ElementType;
}
_result = type?.ElementType;
return null;
}
......@@ -1036,58 +1052,51 @@ private TypeSymbolWithAnnotations InferResultNullability(BoundBinaryOperator nod
private TypeSymbolWithAnnotations InferResultNullability(BinaryOperatorKind operatorKind, MethodSymbol methodOpt, TypeSymbol resultType, TypeSymbolWithAnnotations leftType, TypeSymbolWithAnnotations rightType)
{
bool? isNullable = InferResultNullability(operatorKind, methodOpt, resultType, leftType?.IsNullable, rightType?.IsNullable);
return TypeSymbolWithAnnotations.Create(resultType, isNullable);
}
private bool? InferResultNullability(BinaryOperatorKind operatorKind, MethodSymbol methodOpt, TypeSymbol resultType, bool? leftIsNullable, bool? rightIsNullable)
{
bool? isNullable = null;
if (operatorKind.IsUserDefined())
{
if ((object)methodOpt != null && methodOpt.ParameterCount == 2)
if (operatorKind.IsLifted())
{
// PROTOTYPE(NullableReferenceTypes): Should return methodOpt.ReturnType
// since that type might include nested nullability inferred from this flow analysis.
return IsResultNullable(methodOpt);
// PROTOTYPE(NullableReferenceTypes): Conversions: Lifted operator
return TypeSymbolWithAnnotations.Create(resultType, isNullableIfReferenceType: null);
}
else
// PROTOTYPE(NullableReferenceTypes): Update method based on operand types.
if ((object)methodOpt != null && methodOpt.ParameterCount == 2)
{
return null;
return methodOpt.ReturnType;
}
}
else if (operatorKind.IsDynamic())
{
return null;
}
else if (resultType.IsReferenceType == true)
else if (!operatorKind.IsDynamic() && resultType.IsReferenceType == true)
{
switch (operatorKind.Operator() | operatorKind.OperandTypes())
{
case BinaryOperatorKind.DelegateCombination:
if (leftIsNullable == false || rightIsNullable == false)
{
return false;
}
else if (leftIsNullable == true && rightIsNullable == true)
{
return true;
}
else
{
Debug.Assert(leftIsNullable == null || rightIsNullable == null);
return null;
bool? leftIsNullable = leftType?.IsNullable;
bool? rightIsNullable = rightType?.IsNullable;
if (leftIsNullable == false || rightIsNullable == false)
{
isNullable = false;
}
else if (leftIsNullable == true && rightIsNullable == true)
{
isNullable = true;
}
else
{
Debug.Assert(leftIsNullable == null || rightIsNullable == null);
}
}
break;
case BinaryOperatorKind.DelegateRemoval:
return true; // Delegate removal can produce null.
isNullable = true; // Delegate removal can produce null.
break;
default:
isNullable = false;
break;
}
return false;
}
else
{
return null;
}
return TypeSymbolWithAnnotations.Create(resultType, isNullable);
}
protected override void AfterLeftChildHasBeenVisited(BoundBinaryOperator binary)
......@@ -1217,7 +1226,7 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato
SetUnreachable();
}
leftType = InferResultNullability(node.LeftConversion, node.Type, leftType);
leftType = InferResultNullability(node.LeftOperand, node.LeftConversion, node.Type, leftType);
VisitRvalue(node.RightOperand);
var rightType = _result.Type;
......@@ -1268,9 +1277,8 @@ public override BoundNode VisitConditionalAccess(BoundConditionalAccess node)
Debug.Assert(!IsConditionalState);
var receiver = node.Receiver;
VisitRvalue(receiver);
var receiverType = VisitRvalueWithResult(receiver).Type;
var receiverType = _result.Type;
var receiverState = this.State.Clone();
if (receiver.Type?.IsReferenceType == true)
......@@ -1393,7 +1401,7 @@ public override BoundNode VisitCall(BoundCall node)
}
ImmutableArray<Result> results = VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt, method, node.ArgsToParamsOpt, node.Expanded);
if (method.IsGenericMethod && !HasExplicitTypeArguments(node))
if (method.IsGenericMethod && HasImplicitTypeArguments(node))
{
method = InferMethod(node, method, results.SelectAsArray(r => r.Type));
}
......@@ -1416,19 +1424,25 @@ public override BoundNode VisitCall(BoundCall node)
return null;
}
private static bool HasExplicitTypeArguments(BoundCall node)
// PROTOTYPE(NullableReferenceTypes): Record in the node whether type
// arguments were implicit, to allow for cases where the syntax is not an
// invocation (such as a synthesized call from a query interpretation).
private static bool HasImplicitTypeArguments(BoundCall node)
{
var syntax = node.Syntax;
if (syntax.Kind() != SyntaxKind.InvocationExpression)
{
// Unexpected syntax kind.
return false;
}
syntax = ((Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax)syntax).Expression;
if (syntax.Kind() == SyntaxKind.QualifiedName)
var nameSyntax = Binder.GetNameSyntax(((InvocationExpressionSyntax)syntax).Expression, out var _);
if (nameSyntax == null)
{
syntax = ((Microsoft.CodeAnalysis.CSharp.Syntax.QualifiedNameSyntax)syntax).Right;
// Unexpected syntax kind.
return false;
}
return syntax.Kind() == SyntaxKind.GenericName;
nameSyntax = nameSyntax.GetUnqualifiedName();
return nameSyntax.Kind() != SyntaxKind.GenericName;
}
protected override void VisitArguments(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, MethodSymbol method)
......@@ -1583,15 +1597,14 @@ private MethodSymbol InferMethod(BoundCall node, MethodSymbol method, ImmutableA
refKinds.Free();
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
var result = MethodTypeInferrer.Infer(
_binder,
compilation.Conversions,
_binder,
_conversions,
definition.TypeParameters,
definition.ContainingType,
parameterTypes,
parameterRefKinds,
arguments,
ref useSiteDiagnostics,
includeNullability: true);
ref useSiteDiagnostics);
if (result.Success)
{
// PROTOTYPE(NullableReferenceTypes): Report conversion warnings.
......@@ -1607,18 +1620,46 @@ private MethodSymbol InferMethod(BoundCall node, MethodSymbol method, ImmutableA
// PROTOTYPE(NullableReferenceTypes): Support field initializers in local functions.
}
private bool? IsResultNullable(Symbol resultSymbol)
private static BoundExpression RemoveImplicitConversions(BoundExpression expr)
{
TypeSymbolWithAnnotations resultType = GetTypeOrReturnTypeWithAdjustedNullableAnnotations(resultSymbol);
// PROTOTYPE(NullableReferenceTypes): The loop is necessary to handle
// implicit conversions that have multiple parts (for instance, user-defined
// conversions). Should check for those cases explicitly.
while (true)
{
if (expr.Kind != BoundKind.Conversion)
{
break;
}
var conversion = (BoundConversion)expr;
if (conversion.ExplicitCastInCode || !conversion.Conversion.Exists)
{
break;
}
expr = conversion.Operand;
}
return expr;
}
if (!resultType.IsVoid && resultType.IsReferenceType)
// See Binder.GenerateConversionForAssignment for corresponding approach in initial binding.
private Result GenerateConversionForAssignment(BoundExpression value, TypeSymbol targetType, Result valueResult)
{
if (targetType is null)
{
return resultType.IsNullable;
return Result.Unset;
}
else
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
TypeSymbol sourceType = valueResult.Type?.TypeSymbol;
var conversion = (sourceType is null) ?
_conversions.ClassifyImplicitConversionFromExpression(value, targetType, ref useSiteDiagnostics) :
_conversions.ClassifyImplicitConversionFromType(sourceType, targetType, ref useSiteDiagnostics);
if (!conversion.Exists)
{
return null;
ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInAssignment, value.Syntax, GetTypeAsDiagnosticArgument(valueResult.Type?.TypeSymbol), targetType);
}
return InferResultNullability(value, conversion, targetType, valueResult.Type);
}
/// <summary>
......@@ -1739,7 +1780,7 @@ public override BoundNode VisitConversion(BoundConversion node)
}
else
{
resultType = InferResultNullability(node.Conversion, node.Type, operandType, fromConversionNode: true);
resultType = InferResultNullability(operand, node.Conversion, node.Type, operandType, fromConversionNode: true);
}
_result = resultType;
}
......@@ -1762,12 +1803,11 @@ public override BoundNode VisitConvertedTupleLiteral(BoundConvertedTupleLiteral
private void VisitTupleExpression(BoundTupleExpression node)
{
var arguments = node.Arguments;
foreach (var arg in arguments)
{
VisitRvalue(arg);
}
// PROTOTYPE(NullableReferenceTypes): Result should include nullability of arguments.
_result = TypeSymbolWithAnnotations.Create(node.Type);
ImmutableArray<TypeSymbolWithAnnotations> elementTypes = arguments.SelectAsArray((a, w) => w.VisitRvalueWithResult(a).Type, this);
var tupleOpt = (TupleTypeSymbol)node.Type;
_result = (tupleOpt is null) ?
null :
TypeSymbolWithAnnotations.Create(tupleOpt.WithElementTypes(elementTypes));
}
private void ReportNullabilityMismatchWithTargetDelegate(SyntaxNode syntax, NamedTypeSymbol delegateType, MethodSymbol method)
......@@ -1787,7 +1827,7 @@ private void ReportNullabilityMismatchWithTargetDelegate(SyntaxNode syntax, Name
if (IsNullabilityMismatch(invoke.ReturnType, method.ReturnType))
{
ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, syntax,
new FormattedSymbol(method, SymbolDisplayFormat.MinimallyQualifiedFormat),
new FormattedSymbol(method, SymbolDisplayFormat.MinimallyQualifiedFormat),
delegateType);
}
......@@ -1799,22 +1839,22 @@ private void ReportNullabilityMismatchWithTargetDelegate(SyntaxNode syntax, Name
{
ReportStaticNullCheckingDiagnostics(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, syntax,
new FormattedSymbol(method.Parameters[i], SymbolDisplayFormat.ShortFormat),
new FormattedSymbol(method, SymbolDisplayFormat.MinimallyQualifiedFormat),
new FormattedSymbol(method, SymbolDisplayFormat.MinimallyQualifiedFormat),
delegateType);
}
}
}
private static TypeSymbolWithAnnotations InferResultNullability(Conversion conversion, TypeSymbol targetType, TypeSymbolWithAnnotations operandType, bool fromConversionNode = false)
private static TypeSymbolWithAnnotations InferResultNullability(BoundExpression operand, Conversion conversion, TypeSymbol targetType, TypeSymbolWithAnnotations operandType, bool fromConversionNode = false)
{
bool? isNullable = null;
bool? isNullableIfReferenceType = null;
switch (conversion.Kind)
{
case ConversionKind.MethodGroup:
case ConversionKind.AnonymousFunction:
case ConversionKind.InterpolatedString:
isNullable = false;
isNullableIfReferenceType = false;
break;
case ConversionKind.ExplicitUserDefined:
......@@ -1858,7 +1898,7 @@ private static TypeSymbolWithAnnotations InferResultNullability(Conversion conve
{
// PROTOTYPE(NullableReferenceTypes): Should we worry about a pathological case of boxing nullable value known to be not null?
// For example, new int?(0)
isNullable = operandType.IsNullableType();
isNullableIfReferenceType = operandType.IsNullableType();
}
else
{
......@@ -1877,7 +1917,7 @@ private static TypeSymbolWithAnnotations InferResultNullability(Conversion conve
// PROTOTYPE(NullableReferenceTypes): Should an explicit cast cast away
// outermost nullability? For instance, is `s` a `string!` or `string?`?
// object? obj = ...; var s = (string)obj;
isNullable = operandType?.IsNullable;
isNullableIfReferenceType = (operandType is null) ? (operand.IsLiteralNull() || operand.IsLiteralDefault()) : operandType.IsNullable;
break;
case ConversionKind.Deconstruction:
......@@ -1895,7 +1935,7 @@ private static TypeSymbolWithAnnotations InferResultNullability(Conversion conve
}
// PROTOTYPE(NullableReferenceTypes): Include nested nullability?
return TypeSymbolWithAnnotations.Create(targetType, isNullable);
return TypeSymbolWithAnnotations.Create(targetType, isNullableIfReferenceType);
}
public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
......@@ -1991,12 +2031,7 @@ private void VisitThisOrBaseReference(BoundExpression node)
public override BoundNode VisitParameter(BoundParameter node)
{
Debug.Assert(!IsConditionalState);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
_result = GetAdjustedResult(GetDeclaredParameterResult(node.ParameterSymbol));
}
_result = GetAdjustedResult(GetDeclaredParameterResult(node.ParameterSymbol));
return null;
}
......@@ -2076,9 +2111,7 @@ public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
if ((object)targetTypeOfOperandConversion != null)
{
// PROTOTYPE(NullableReferenceTypes): Should something special be done for targetTypeOfOperandConversion for lifted case?
resultOfOperandConversionType = InferResultNullability(node.OperandConversion,
targetTypeOfOperandConversion,
operandResult.Type);
resultOfOperandConversionType = InferResultNullability(node.Operand, node.OperandConversion, targetTypeOfOperandConversion, operandResult.Type);
}
else
{
......@@ -2090,7 +2123,7 @@ public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
{
resultOfIncrementType = resultOfOperandConversionType;
}
else
else
{
WarnOnNullReferenceArgument(node.Operand, resultOfOperandConversionType, incrementOperator.Parameters[0], expanded: false);
......@@ -2102,9 +2135,7 @@ public override BoundNode VisitIncrementOperator(BoundIncrementOperator node)
WarnOnNullReferenceArgument(node, resultOfIncrementType, node.ResultConversion.Method.Parameters[0], expanded: false);
}
resultOfIncrementType = InferResultNullability(node.ResultConversion,
node.Type,
resultOfIncrementType);
resultOfIncrementType = InferResultNullability(node, node.ResultConversion, node.Type, resultOfIncrementType);
// PROTOTYPE(NullableReferenceTypes): Check node.Type.IsErrorType() instead?
if (!node.HasErrors)
......@@ -2148,9 +2179,7 @@ public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmen
// PROTOTYPE(NullableReferenceTypes): Update operator based on inferred argument types.
if ((object)node.Operator.LeftType != null)
{
leftOnRightType = InferResultNullability(node.LeftConversion,
node.Operator.LeftType,
leftOnRight.Type);
leftOnRightType = InferResultNullability(node.Left, node.LeftConversion, node.Operator.LeftType, leftOnRight.Type);
}
else
{
......@@ -2163,7 +2192,7 @@ public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmen
if ((object)node.Operator.ReturnType != null)
{
if (node.Operator.Kind.IsUserDefined() && (object)node.Operator.Method != null && node.Operator.Method.ParameterCount == 2)
{
{
WarnOnNullReferenceArgument(node.Left, leftOnRightType, node.Operator.Method.Parameters[0], expanded: false);
WarnOnNullReferenceArgument(node.Right, rightType, node.Operator.Method.Parameters[1], expanded: false);
}
......@@ -2176,9 +2205,7 @@ public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmen
WarnOnNullReferenceArgument(node, resultType, node.FinalConversion.Method.Parameters[0], expanded: false);
}
resultType = InferResultNullability(node.FinalConversion,
node.Type,
resultType);
resultType = InferResultNullability(node, node.FinalConversion, node.Type, resultType);
}
else
{
......@@ -2301,12 +2328,7 @@ public override BoundNode VisitIndexerAccess(BoundIndexerAccess node)
var method = node.Indexer.GetOwnOrInheritedGetMethod();
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, method, node.ArgsToParamsOpt, node.Expanded);
Debug.Assert(!IsConditionalState);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
_result = GetTypeOrReturnTypeWithAdjustedNullableAnnotations(node.Indexer);
}
_result = GetTypeOrReturnTypeWithAdjustedNullableAnnotations(node.Indexer);
return null;
}
......@@ -2320,11 +2342,9 @@ private void VisitMemberAccess(BoundExpression receiverOpt, Symbol member, bool
{
Debug.Assert(!IsConditionalState);
VisitRvalue(receiverOpt);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
Result receiverResult = _result;
Result receiverResult = VisitRvalueWithResult(receiverOpt);
if (!member.IsStatic)
{
......@@ -2424,7 +2444,7 @@ private TypeSymbolWithAnnotations InferResultNullability(BoundUnaryOperator node
if (node.OperatorKind.IsLifted())
{
// PROTOTYPE(NullableReferenceTypes): Conversions: Lifted operator
return TypeSymbolWithAnnotations.Create(node.Type);
return TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: null);
}
// PROTOTYPE(NullableReferenceTypes): Update method based on inferred operand type.
if ((object)node.MethodOpt != null && node.MethodOpt.ParameterCount == 1)
......@@ -2486,7 +2506,7 @@ private TypeSymbolWithAnnotations InferResultNullability(BoundUserDefinedConditi
if (node.OperatorKind.IsLifted())
{
// PROTOTYPE(NullableReferenceTypes): Conversions: Lifted operator
return TypeSymbolWithAnnotations.Create(node.Type);
return TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: null);
}
// PROTOTYPE(NullableReferenceTypes): Update method based on inferred operand types.
if ((object)node.LogicalOperator != null && node.LogicalOperator.ParameterCount == 2)
......@@ -2575,21 +2595,15 @@ private TypeSymbolWithAnnotations InferResultNullabilityOfBinaryLogicalOperator(
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
{
var result = base.VisitAwaitExpression(node);
Debug.Assert(!IsConditionalState);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
if (!node.Type.IsReferenceType || node.HasErrors || (object)node.GetResult == null)
{
if (!node.Type.IsReferenceType || node.HasErrors || (object)node.GetResult == null)
{
SetResult(node);
}
else
{
// PROTOTYPE(NullableReferenceTypes): Update method based on inferred receiver type.
_result = GetTypeOrReturnTypeWithAdjustedNullableAnnotations(node.GetResult);
}
SetResult(node);
}
else
{
// PROTOTYPE(NullableReferenceTypes): Update method based on inferred receiver type.
_result = GetTypeOrReturnTypeWithAdjustedNullableAnnotations(node.GetResult);
}
return result;
}
......@@ -2635,7 +2649,6 @@ public override BoundNode VisitAsOperator(BoundAsOperator node)
{
var result = base.VisitAsOperator(node);
Debug.Assert(!IsConditionalState);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
bool? isNullable = null;
......@@ -2788,14 +2801,10 @@ public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node)
Debug.Assert(node.Type.IsDynamic());
Debug.Assert(node.Type.IsReferenceType);
Debug.Assert(!IsConditionalState);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
// PROTOTYPE(NullableReferenceTypes): Update applicable members based on inferred argument types.
bool? isNullable = InferResultNullabilityFromApplicableCandidates(StaticCast<Symbol>.From(node.ApplicableMethods));
_result = TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: isNullable);
}
// PROTOTYPE(NullableReferenceTypes): Update applicable members based on inferred argument types.
bool? isNullable = InferResultNullabilityFromApplicableCandidates(StaticCast<Symbol>.From(node.ApplicableMethods));
_result = TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: isNullable);
return null;
}
......@@ -2898,17 +2907,12 @@ public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess no
VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default(ImmutableArray<int>), expanded: false);
Debug.Assert(node.Type.IsDynamic());
Debug.Assert(!IsConditionalState);
//if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability?
{
// PROTOTYPE(NullableReferenceTypes): Update applicable members based on inferred argument types.
bool? isNullable = (node.Type?.IsReferenceType == true) ?
InferResultNullabilityFromApplicableCandidates(StaticCast<Symbol>.From(node.ApplicableIndexers)) :
null;
_result = TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: isNullable);
}
// PROTOTYPE(NullableReferenceTypes): Update applicable members based on inferred argument types.
bool? isNullable = (node.Type?.IsReferenceType == true) ?
InferResultNullabilityFromApplicableCandidates(StaticCast<Symbol>.From(node.ApplicableIndexers)) :
null;
_result = TypeSymbolWithAnnotations.Create(node.Type, isNullableIfReferenceType: isNullable);
return null;
}
......@@ -2963,7 +2967,7 @@ private static bool IsNullabilityMismatch(TypeSymbol type1, TypeSymbol type2)
if (type.IsReferenceType)
{
bool? memberResultIsNullable = IsResultNullable(member);
bool? memberResultIsNullable = type.IsNullable;
if (memberResultIsNullable == true)
{
// At least one candidate can produce null, assume dynamic access can produce null as well
......@@ -3041,7 +3045,7 @@ public override BoundNode VisitThrowExpression(BoundThrowExpression node)
return result;
}
#endregion Visitors
#endregion Visitors
protected override string Dump(LocalState state)
{
......@@ -3104,6 +3108,7 @@ protected override bool IntersectWith(ref LocalState self, ref LocalState other)
}
}
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal struct Result
{
internal readonly TypeSymbolWithAnnotations Type;
......@@ -3128,6 +3133,12 @@ private Result(TypeSymbolWithAnnotations type, int slot)
Type = type;
Slot = slot;
}
private string GetDebuggerDisplay()
{
var type = (object)Type == null ? "<null>" : Type.GetDebuggerDisplay();
return $"Type={type}, Slot={Slot}";
}
}
#if REFERENCE_STATE
......
......@@ -68,7 +68,6 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m
conversions,
method,
arguments.AsImmutable(),
includeNullability: false, // PROTOTYPE(NullableReferenceTypes): Support nullability if feature enabled.
useSiteDiagnostics: ref useSiteDiagnostics);
if (typeArgs.IsDefault)
......
......@@ -328,7 +328,7 @@ public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor)
return visitor.VisitProperty(this);
}
internal virtual PropertySymbol AsMember(NamedTypeSymbol newOwner)
internal PropertySymbol AsMember(NamedTypeSymbol newOwner)
{
Debug.Assert(this.IsDefinition);
Debug.Assert(ReferenceEquals(newOwner.OriginalDefinition, this.ContainingSymbol.OriginalDefinition));
......
......@@ -140,7 +140,7 @@ protected override void CheckInterfaces(DiagnosticBag diagnostics)
if (interfaces.Count > 1)
{
var seenInterfaces = new Dictionary<NamedTypeSymbol, NamedTypeSymbol>(EqualsIgnoringComparer.InstanceIgnoringTupleNames);
var seenInterfaces = new Dictionary<NamedTypeSymbol, NamedTypeSymbol>(EqualsIgnoringTupleNames);
foreach (var @interface in interfaces)
{
NamedTypeSymbol other;
......
......@@ -213,6 +213,36 @@ internal TupleTypeSymbol WithUnderlyingType(NamedTypeSymbol newUnderlyingType)
return Create(_locations, newUnderlyingType, _elementLocations, _elementNames, _errorPositions);
}
/// <summary>
/// Copy this tuple, but modify it to use the new element types.
/// </summary>
internal TupleTypeSymbol WithElementTypes(ImmutableArray<TypeSymbolWithAnnotations> newElementTypes)
{
Debug.Assert(_elementTypes.Length == newElementTypes.Length);
Debug.Assert(newElementTypes.All(t => (object)t != null));
NamedTypeSymbol firstTupleType;
NamedTypeSymbol chainedTupleType;
if (_underlyingType.Arity < TupleTypeSymbol.RestPosition)
{
firstTupleType = _underlyingType.OriginalDefinition;
chainedTupleType = null;
}
else
{
chainedTupleType = _underlyingType.OriginalDefinition;
var underlyingType = _underlyingType;
do
{
underlyingType = ((TupleTypeSymbol)underlyingType.TypeArgumentsNoUseSiteDiagnostics[TupleTypeSymbol.RestIndex].TypeSymbol).UnderlyingNamedType;
} while (underlyingType.Arity >= TupleTypeSymbol.RestPosition);
firstTupleType = underlyingType.OriginalDefinition;
}
return Create(
ConstructTupleUnderlyingType(firstTupleType, chainedTupleType, newElementTypes),
elementNames: _elementNames);
}
/// <summary>
/// Copy this tuple, but modify it to use the new element names.
/// Also applies new location of the whole tuple as well as each element.
......@@ -319,34 +349,40 @@ private static NamedTypeSymbol GetTupleUnderlyingType(ImmutableArray<TypeSymbolW
int remainder;
int chainLength = NumberOfValueTuples(numElements, out remainder);
NamedTypeSymbol currentSymbol = default(NamedTypeSymbol);
NamedTypeSymbol firstTupleType = compilation.GetWellKnownType(GetTupleType(remainder));
if ((object)diagnostics != null && (object)syntax != null)
{
ReportUseSiteAndObsoleteDiagnostics(syntax, diagnostics, firstTupleType);
}
currentSymbol = firstTupleType.Construct(ImmutableArray.Create(elementTypes, (chainLength - 1) * (RestPosition - 1), remainder));
int loop = chainLength - 1;
if (loop > 0)
NamedTypeSymbol chainedTupleType = null;
if (chainLength > 1)
{
NamedTypeSymbol chainedTupleType = compilation.GetWellKnownType(GetTupleType(RestPosition));
chainedTupleType = compilation.GetWellKnownType(GetTupleType(RestPosition));
if ((object)diagnostics != null && (object)syntax != null)
{
ReportUseSiteAndObsoleteDiagnostics(syntax, diagnostics, chainedTupleType);
}
}
do
{
var chainedTypes = ImmutableArray.Create(elementTypes, (loop - 1) * (RestPosition - 1), RestPosition - 1).Add(TypeSymbolWithAnnotations.Create(currentSymbol));
return ConstructTupleUnderlyingType(firstTupleType, chainedTupleType, elementTypes);
}
currentSymbol = chainedTupleType.Construct(chainedTypes);
loop--;
}
while (loop > 0);
internal static NamedTypeSymbol ConstructTupleUnderlyingType(NamedTypeSymbol firstTupleType, NamedTypeSymbol chainedTupleTypeOpt, ImmutableArray<TypeSymbolWithAnnotations> elementTypes)
{
Debug.Assert(chainedTupleTypeOpt is null == elementTypes.Length < RestPosition);
int numElements = elementTypes.Length;
int remainder;
int chainLength = NumberOfValueTuples(numElements, out remainder);
NamedTypeSymbol currentSymbol = firstTupleType.Construct(ImmutableArray.Create(elementTypes, (chainLength - 1) * (RestPosition - 1), remainder));
int loop = chainLength - 1;
while (loop > 0)
{
var chainedTypes = ImmutableArray.Create(elementTypes, (loop - 1) * (RestPosition - 1), RestPosition - 1).Add(TypeSymbolWithAnnotations.Create(currentSymbol));
currentSymbol = chainedTupleTypeOpt.Construct(chainedTypes);
loop--;
}
return currentSymbol;
......
......@@ -104,16 +104,19 @@ private InterfaceInfo GetInterfaceInfo()
return info;
}
internal static readonly EqualityComparer<TypeSymbol> EqualsConsiderEverything = new TypeSymbolComparer(TypeCompareKind.ConsiderEverything);
internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringTupleNames = new TypeSymbolComparer(TypeCompareKind.IgnoreTupleNames);
/// <summary>
/// A comparer that treats dynamic and object as "the same" types, and also ignores tuple element names differences.
/// </summary>
internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringDynamicAndTupleNamesComparer = new EqualsIgnoringComparer(TypeCompareKind.IgnoreDynamicAndTupleNames);
internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringDynamicAndTupleNamesComparer = new TypeSymbolComparer(TypeCompareKind.IgnoreDynamicAndTupleNames);
/// <summary>
/// A comparator that pays attention to nullable modifiers in addition to default behavior.
/// </summary>
internal static readonly EqualityComparer<TypeSymbol> EqualsIncludingNullableComparer = new EqualsIgnoringComparer(TypeCompareKind.CompareNullableModifiersForReferenceTypes);
internal static readonly EqualityComparer<TypeSymbol> EqualsIncludingNullableComparer = new TypeSymbolComparer(TypeCompareKind.CompareNullableModifiersForReferenceTypes);
/// <summary>
/// The original definition of this symbol. If this symbol is constructed from another
......@@ -307,13 +310,11 @@ public override int GetHashCode()
return RuntimeHelpers.GetHashCode(this);
}
internal sealed class EqualsIgnoringComparer : EqualityComparer<TypeSymbol>
internal sealed class TypeSymbolComparer : EqualityComparer<TypeSymbol>
{
public static EqualsIgnoringComparer InstanceIgnoringTupleNames { get; } = new EqualsIgnoringComparer(TypeCompareKind.IgnoreTupleNames);
private readonly TypeCompareKind _comparison;
public EqualsIgnoringComparer(TypeCompareKind comparison)
public TypeSymbolComparer(TypeCompareKind comparison)
{
_comparison = comparison;
}
......@@ -354,7 +355,7 @@ protected virtual ImmutableArray<NamedTypeSymbol> GetAllInterfaces()
protected virtual ImmutableArray<NamedTypeSymbol> MakeAllInterfaces()
{
var result = ArrayBuilder<NamedTypeSymbol>.GetInstance();
var visited = new HashSet<NamedTypeSymbol>(EqualsIgnoringComparer.InstanceIgnoringTupleNames);
var visited = new HashSet<NamedTypeSymbol>(EqualsIgnoringTupleNames);
for (var baseType = this; !ReferenceEquals(baseType, null); baseType = baseType.BaseTypeNoUseSiteDiagnostics)
{
......
......@@ -3846,7 +3846,7 @@ struct S2
}
[Fact]
public void PassingParameters_1()
public void PassingParameters_01()
{
CSharpCompilation c = CreateStandardCompilation(@"
class C
......@@ -3992,7 +3992,7 @@ class CL1
}
[Fact]
public void PassingParameters_2()
public void PassingParameters_02()
{
CSharpCompilation c = CreateStandardCompilation(@"
class C
......@@ -4025,7 +4025,7 @@ class CL0
}
[Fact]
public void PassingParameters_3()
public void PassingParameters_03()
{
CSharpCompilation c = CreateStandardCompilation(@"
class C
......@@ -4295,12 +4295,12 @@ class CL0<T>
", parseOptions: TestOptions.Regular8);
c.VerifyDiagnostics(
// (12,16): warning CS8620: Nullability of reference types in argument of type 'CL0<string?>' doesn't match target type 'CL0<string>' for parameter 'x' in 'void C.M1(ref CL0<string> x)'.
// M1(ref x1);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "x1").WithArguments("CL0<string?>", "CL0<string>", "x", "void C.M1(ref CL0<string> x)").WithLocation(12, 16),
// (12,16): warning CS8619: Nullability of reference types in value of type 'CL0<string>' doesn't match target type 'CL0<string?>'.
// M1(ref x1);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x1").WithArguments("CL0<string>", "CL0<string?>").WithLocation(12, 16),
// (12,16): warning CS8620: Nullability of reference types in argument of type 'CL0<string?>' doesn't match target type 'CL0<string>' for parameter 'x' in 'void C.M1(ref CL0<string> x)'.
// M1(ref x1);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "x1").WithArguments("CL0<string?>", "CL0<string>", "x", "void C.M1(ref CL0<string> x)").WithLocation(12, 16),
// (19,16): warning CS8619: Nullability of reference types in value of type 'CL0<string?>' doesn't match target type 'CL0<string>'.
// M2(out x2);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2").WithArguments("CL0<string?>", "CL0<string>").WithLocation(19, 16),
......@@ -4310,6 +4310,30 @@ class CL0<T>
);
}
[Fact]
public void RefOutParameters_05()
{
var source =
@"class C
{
static void F()
{
object? x = null;
object? y = null;
G(out x, ref y);
}
static void G(out object x, ref object y)
{
x = new object();
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (7,22): warning CS8604: Possible null reference argument for parameter 'y' in 'void C.G(out object x, ref object y)'.
// G(out x, ref y);
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "y").WithArguments("y", "void C.G(out object x, ref object y)").WithLocation(7, 22));
}
[Fact]
public void TargetingUnannotatedAPIs_01()
{
......@@ -5823,7 +5847,7 @@ void Test1(object x1, object? y1)
}
[Fact]
public void Loop_1()
public void Loop_01()
{
CSharpCompilation c = CreateStandardCompilation(@"
class C
......@@ -5893,7 +5917,7 @@ class CL1
}
[Fact]
public void Loop_2()
public void Loop_02()
{
CSharpCompilation c = CreateStandardCompilation(@"
class C
......@@ -8099,7 +8123,8 @@ class CL1
);
}
[Fact]
// PROTOTYPE(NullableReferenceTypes): Calculate lamba conversion.
[Fact(Skip = "TODO")]
public void Lambda_12()
{
CSharpCompilation c = CreateStandardCompilation(@"
......@@ -8166,7 +8191,8 @@ class CL1
);
}
[Fact]
// PROTOTYPE(NullableReferenceTypes): Calculate lamba conversion.
[Fact(Skip = "TODO")]
public void Lambda_13()
{
CSharpCompilation c = CreateStandardCompilation(@"
......@@ -8324,7 +8350,8 @@ static void Test2()
);
}
[Fact]
// PROTOTYPE(NullableReferenceTypes): Calculate lamba conversion.
[Fact(Skip = "TODO")]
public void Lambda_16()
{
CSharpCompilation c = CreateStandardCompilation(@"
......@@ -8359,7 +8386,8 @@ class CL1<T>
);
}
[Fact]
// PROTOTYPE(NullableReferenceTypes): Calculate lamba conversion.
[Fact(Skip = "TODO")]
public void Lambda_17()
{
CSharpCompilation c = CreateStandardCompilation(@"
......@@ -8396,6 +8424,49 @@ class CL1<T>
);
}
[Fact]
public void UnboundLambda_01()
{
var source =
@"class C
{
static void F()
{
var y = x => x;
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (5,13): error CS0815: Cannot assign lambda expression to an implicitly-typed variable
// var y = x => x;
Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "y = x => x").WithArguments("lambda expression").WithLocation(5, 13));
}
[Fact]
public void UnboundLambda_02()
{
var source =
@"class C
{
static void F(object? x)
{
var z = y => y ?? x.ToString();
}
}";
// PROTOTYPE(NullableReferenceTypes): Should not report HDN_ExpressionIsProbablyNeverNull for `y`.
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (5,13): error CS0815: Cannot assign lambda expression to an implicitly-typed variable
// var z = y => y ?? x.ToString();
Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "z = y => y ?? x.ToString()").WithArguments("lambda expression").WithLocation(5, 13),
// (5,22): hidden CS8607: Expression is probably never null.
// var z = y => y ?? x.ToString();
Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "y").WithLocation(5, 22),
// (5,27): warning CS8602: Possible dereference of a null reference.
// var z = y => y ?? x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(5, 27));
}
[Fact]
public void NewT_01()
{
......@@ -9531,6 +9602,30 @@ void Test1(C x1)
);
}
// PROTOTYPE(NullableReferenceTypes): Should report WRN_NullReferenceReceiver.
[Fact(Skip = "TODO")]
public void Discard_01()
{
var source =
@"class C
{
static void F((object, object?) t)
{
object? x;
((_, x) = t).Item1.ToString();
((x, _) = t).Item2.ToString();
}
}";
var comp = CreateStandardCompilation(
source,
references: new[] { ValueTupleRef, SystemRuntimeFacadeRef },
parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (7,9): warning CS8602: Possible dereference of a null reference.
// ((x, _) = t).Item2.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "((x, _) = t).Item2").WithLocation(7, 9));
}
[Fact]
public void BinaryOperator_01()
{
......@@ -10157,6 +10252,89 @@ class CL0
);
}
[Fact]
public void BinaryOperator_14()
{
var source =
@"struct S
{
public static S operator&(S a, S b) => a;
public static S operator|(S a, S b) => b;
public static bool operator true(S? s) => true;
public static bool operator false(S? s) => false;
static void And(S x, S? y)
{
if (x && x) { }
if (x && y) { }
if (y && x) { }
if (y && y) { }
}
static void Or(S x, S? y)
{
if (x || x) { }
if (x || y) { }
if (y || x) { }
if (y || y) { }
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void BinaryOperator_15()
{
var source =
@"struct S
{
public static S operator+(S a, S b) => a;
static void F(S x, S? y)
{
S? s;
s = x + x;
s = x + y;
s = y + x;
s = y + y;
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void BinaryOperator_16()
{
var source =
@"struct S
{
public static bool operator<(S a, S b) => true;
public static bool operator<=(S a, S b) => true;
public static bool operator>(S a, S b) => true;
public static bool operator>=(S a, S b) => true;
public static bool operator==(S a, S b) => true;
public static bool operator!=(S a, S b) => true;
public override bool Equals(object other) => true;
public override int GetHashCode() => 0;
static void F(S x, S? y)
{
if (x < y) { }
if (x <= y) { }
if (x > y) { }
if (x >= y) { }
if (x == y) { }
if (x != y) { }
if (y < x) { }
if (y <= x) { }
if (y > x) { }
if (y >= x) { }
if (y == x) { }
if (y != x) { }
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void MethodGroupConversion_01()
{
......@@ -10326,9 +10504,9 @@ class CL0<T>
", parseOptions: TestOptions.Regular8);
c.VerifyDiagnostics(
// (12,35): warning CS8621: Nullability of reference types in return type of 'string C.M1<string>()' doesn't match the target delegate 'Func<string?>'.
// System.Func<string?> u1 = M1<string>;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "M1<string>").WithArguments("string C.M1<string>()", "System.Func<string?>").WithLocation(12, 35),
// (12,35): warning CS8621: Nullability of reference types in return type of 'string C.M1<string>()' doesn't match the target delegate 'Func<string?>'.
// System.Func<string?> u1 = M1<string>;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "M1<string>").WithArguments("string C.M1<string>()", "System.Func<string?>").WithLocation(12, 35),
// (17,34): warning CS8621: Nullability of reference types in return type of 'string? C.M1<string?>()' doesn't match the target delegate 'Func<string>'.
// System.Func<string> u2 = M1<string?>;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "M1<string?>").WithArguments("string? C.M1<string?>()", "System.Func<string>").WithLocation(17, 34),
......@@ -10456,6 +10634,22 @@ class CL2
);
}
[Fact]
public void UnaryOperator_02()
{
var source =
@"struct S
{
public static S operator~(S s) => s;
static void F(S? s)
{
s = ~s;
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void Conversion_01()
{
......@@ -10994,12 +11188,12 @@ void Test5(dynamic x5)
", new[] { CSharpRef, SystemCoreRef }, parseOptions: TestOptions.Regular8);
c.VerifyDiagnostics(
// (16,22): warning CS8601: Possible null reference assignment.
// dynamic u2 = x2++;
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x2++").WithLocation(16, 22),
// (27,22): hidden CS8607: Expression is probably never null.
// dynamic v4 = u4 ?? new object();
Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "u4").WithLocation(27, 22)
// (16,22): warning CS8601: Possible null reference assignment.
// dynamic u2 = x2++;
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x2++").WithLocation(16, 22),
// (27,22): hidden CS8607: Expression is probably never null.
// dynamic v4 = u4 ?? new object();
Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "u4").WithLocation(27, 22)
);
}
......@@ -15164,7 +15358,8 @@ void Test3()
);
}
[Fact(Skip = "Variance")]
// PROTOTYPE(NullableReferenceTypes): Conversions: Other
[Fact(Skip = "TODO")]
public void Covariance_Interface()
{
var source =
......@@ -15183,12 +15378,13 @@ class C
// (6,42): warning CS8619: Nullability of reference types in value of type 'I<string?>' doesn't match target type 'I<string>'.
// static I<string> F3(I<string?> i) => i;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "i").WithArguments("I<string?>", "I<string>").WithLocation(6, 42),
// (7,43): warning CS8619: Nullability of reference types in value of type 'I<string?>' doesn't match target type 'I<object>'.
// (7,42): warning CS8619: Nullability of reference types in value of type 'I<string?>' doesn't match target type 'I<object>'.
// static I<object> F4(I<string?> i) => i;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "i").WithArguments("I<string?>", "I<object>").WithLocation(7, 43));
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "i").WithArguments("I<string?>", "I<object>").WithLocation(7, 42));
}
[Fact(Skip = "Variance")]
// PROTOTYPE(NullableReferenceTypes): Conversions: Other
[Fact(Skip = "TODO")]
public void Contravariance_Interface()
{
var source =
......@@ -15204,15 +15400,16 @@ class C
source,
parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (6,42): warning CS8619: Nullability of reference types in value of type 'I<string?>' doesn't match target type 'I<string>'.
// static I<string> F3(I<string?> i) => i;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "i").WithArguments("I<string?>", "I<string>").WithLocation(6, 42),
// (7,42): warning CS8619: Nullability of reference types in value of type 'I<object?>' doesn't match target type 'I<string>'.
// static I<string> F4(I<object?> i) => i;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "i").WithArguments("I<object?>", "I<string>").WithLocation(7, 42));
// (4,42): warning CS8619: Nullability of reference types in value of type 'I<string>' doesn't match target type 'I<string?>'.
// static I<string?> F1(I<string> i) => i;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "i").WithArguments("I<string>", "I<string?>").WithLocation(4, 42),
// (5,42): warning CS8619: Nullability of reference types in value of type 'I<object>' doesn't match target type 'I<string?>'.
// static I<string?> F2(I<object> i) => i;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "i").WithArguments("I<object>", "I<string?>").WithLocation(5, 42));
}
[Fact(Skip = "Variance")]
// PROTOTYPE(NullableReferenceTypes): Conversions: Other
[Fact(Skip = "TODO")]
public void Covariance_Delegate()
{
var source =
......@@ -15248,7 +15445,8 @@ static void Main()
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "F3").WithArguments("o", "void C.F3(object o)", "D<string?>").WithLocation(17, 20));
}
[Fact(Skip = "Variance")]
// PROTOTYPE(NullableReferenceTypes): Conversions: Other
[Fact(Skip = "TODO")]
public void Contravariance_Delegate()
{
var source =
......@@ -15276,12 +15474,12 @@ static void Main()
source,
parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (12,19): warning CS8621: Nullability of reference types in return type of 'string? C.F2()' doesn't match the target delegate 'D<object>'.
// F<object>(F2); // warning
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "F2").WithArguments("string? C.F2()", "D<object>").WithLocation(12, 19),
// (14,19): warning CS8621: Nullability of reference types in return type of 'object? C.F4()' doesn't match the target delegate 'D<object>'.
// F<object>(F4); // warning
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "F4").WithArguments("object? C.F4()", "D<object>").WithLocation(14, 19),
// (17,20): warning CS8621: Nullability of reference types in return type of 'object C.F3()' doesn't match the target delegate 'D<object?>'.
// F<object?>(F3);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "F3").WithArguments("object C.F3()", "D<object?>").WithLocation(17, 20));
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "F4").WithArguments("object? C.F4()", "D<object>").WithLocation(14, 19));
}
[Fact]
......@@ -15947,7 +16145,7 @@ static void M(T? t)
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "t").WithArguments("t", "T? C<T>.G(T t)").WithLocation(8, 13));
}
// PROTOTYPE(NullableReferenceTypes): Best type for conditional.
// PROTOTYPE(NullableReferenceTypes): Conversions: ConditionalOperator
[Fact(Skip = "TODO")]
public void SuppressNullableWarning_Conditional()
{
......@@ -16095,12 +16293,12 @@ static class E
source,
parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (7,9): warning CS8619: Nullability of reference types in value of type 'I<object?>' doesn't match target type 'I<object>'.
// (7,9): warning CS8620: Nullability of reference types in argument of type 'C<object?>' doesn't match target type 'I<object>' for parameter 'o' in 'void E.F1(I<object> o)'.
// x.F1();
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x.F1").WithArguments("I<object?>", "I<object>").WithLocation(7, 9),
// (9,9): warning CS8619: Nullability of reference types in value of type 'I<object>' doesn't match target type 'I<object?>'.
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "x").WithArguments("C<object?>", "I<object>", "o", "void E.F1(I<object> o)").WithLocation(7, 9),
// (9,9): warning CS8620: Nullability of reference types in argument of type 'C<object>' doesn't match target type 'I<object?>' for parameter 'o' in 'void E.F2(I<object?> o)'.
// y.F2();
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y.F2").WithArguments("I<object>", "I<object?>").WithLocation(9, 9));
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "y").WithArguments("C<object>", "I<object?>", "o", "void E.F2(I<object?> o)").WithLocation(9, 9));
}
[Fact]
......@@ -16166,8 +16364,7 @@ static void F(C<object?> x, C<object> y)
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(I<object>)y").WithArguments("I<object>", "I<object?>").WithLocation(11, 13));
}
// PROTOTYPE(NullableReferenceTypes): Should report WRN_NullabilityMismatch*.
[Fact(Skip = "TODO")]
[Fact]
public void SuppressNullableWarning_Ref()
{
var source =
......@@ -16189,12 +16386,6 @@ static void Main()
parseOptions: TestOptions.Regular8,
options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (10,15): warning CS8620: Nullability of reference types in argument of type 'string' doesn't match target type 'string' for parameter 's' in 'void C.F(ref string s, ref string? t)'.
// F(ref s, ref t);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "s").WithArguments("string", "string", "s", "void C.F(ref string s, ref string? t)").WithLocation(10, 15),
// (10,22): warning CS8620: Nullability of reference types in argument of type 'string' doesn't match target type 'string' for parameter 't' in 'void C.F(ref string s, ref string? t)'.
// F(ref s, ref t);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "t").WithArguments("string", "string", "t", "void C.F(ref string s, ref string? t)").WithLocation(10, 22),
// (10,15): warning CS8604: Possible null reference argument for parameter 's' in 'void C.F(ref string s, ref string? t)'.
// F(ref s, ref t);
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("s", "void C.F(ref string s, ref string? t)").WithLocation(10, 15),
......@@ -16203,8 +16394,7 @@ static void Main()
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(10, 22));
}
// PROTOTYPE(NullableReferenceTypes): Should report WRN_NullabilityMismatch*.
[Fact(Skip = "TODO")]
[Fact]
public void SuppressNullableWarning_Out()
{
var source =
......@@ -16228,12 +16418,6 @@ static void Main()
parseOptions: TestOptions.Regular8,
options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics(
// (12,15): warning CS8620: Nullability of reference types in argument of type 'string' doesn't match target type 'string' for parameter 's' in 'void C.F(out string s, out string? t)'.
// F(out s, out t);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "s").WithArguments("string", "string", "s", "void C.F(out string s, out string? t)").WithLocation(12, 15),
// (12,22): warning CS8620: Nullability of reference types in argument of type 'string' doesn't match target type 'string' for parameter 't' in 'void C.F(out string s, out string? t)'.
// F(out s, out t);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "t").WithArguments("string", "string", "t", "void C.F(out string s, out string? t)").WithLocation(12, 22),
// (12,22): warning CS8601: Possible null reference assignment.
// F(out s, out t);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(12, 22));
......@@ -16284,7 +16468,6 @@ static void G(B b)
{
}
}";
var comp = CreateStandardCompilation(
source,
parseOptions: TestOptions.Regular8);
......@@ -16464,6 +16647,25 @@ static void G(object o)
Diagnostic(ErrorCode.ERR_PropertyLacksGet, "c.P").WithArguments("C.P").WithLocation(6, 11));
}
// PROTOTYPE(NullableReferenceType): Assert failure in Binder.GenerateImplicitConversionError.
[Fact(Skip = "TODO")]
public void SuppressNullableWarning_InvalidArrayInitializer()
{
var source =
@"class C
{
static void F()
{
var a = new object[] { new object(), F! };
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (5,46): error CS0428: Cannot convert method group 'F' to non-delegate type 'object'. Did you intend to invoke the method?
// var a = new object[] { new object(), F };
Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "object").WithLocation(5, 46));
}
[Fact]
public void SuppressNullableWarning_IndexedProperty()
{
......@@ -16676,9 +16878,25 @@ static void G(object? o)
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "o").WithArguments("o", "void C.F(object o)").WithLocation(8, 15));
}
[Fact]
public void IsPattern_01()
{
var source =
@"class C
{
static void F(object x) { }
static void G(string s)
{
F(s is var o);
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
// PROTOTYPE(NullableReferenceTypes): Should not warn on either call to F(string).
[Fact(Skip = "TODO")]
public void IsPattern()
public void IsPattern_02()
{
var source =
@"class C
......@@ -17178,6 +17396,7 @@ class Program
Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "default(T)").WithArguments("T", "System.Collections.Generic.IEnumerator<T>").WithLocation(4, 37));
}
// PROTOTYPE(NullableReferenceTypes): Should not report WRN_NullabilityMismatchInAssignment.
[Fact]
public void DeconstructionConversion_NoDeconstructMethod()
{
......@@ -17719,6 +17938,45 @@ static void F(A<object>? x, A<object?>? y)
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, ".F").WithLocation(17, 11));
}
[Fact]
public void GroupBy()
{
var source =
@"using System.Linq;
class Program
{
static void Main()
{
var items = from i in Enumerable.Range(0, 3) group (long)i by i;
}
}";
var comp = CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
// Tests for NullableWalker.HasImplicitTypeArguments.
[Fact]
public void ExplicitTypeArguments()
{
var source =
@"interface I<T> { }
class C
{
C P => throw new System.Exception();
I<T> F<T>(T t)
{
throw new System.Exception();
}
static void M(C c)
{
c.P.F<object>(string.Empty);
(new[]{ c })[0].F<object>(string.Empty);
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void ConstrainedToTypeParameter_01()
{
......
......@@ -59,9 +59,9 @@ static void F(object x, object? y)
// (8,9): warning CS8602: Possible dereference of a null reference.
// b[0].ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b[0]").WithLocation(8, 9),
// (9,28): warning CS8619: Nullability of reference types in value of type 'object?[]' doesn't match target type 'object[]'.
// var c = new[] { a, b };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b").WithArguments("object?[]", "object[]").WithLocation(9, 28));
// (10,9): warning CS8602: Possible dereference of a null reference.
// c[0][0].ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c[0][0]").WithLocation(10, 9));
}
[Fact]
......@@ -165,7 +165,6 @@ static void F(string s)
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "d[0]").WithLocation(12, 9));
}
// PROTOTYPE(NullableReferenceTypes): Should report nullability mismatch warnings.
[Fact]
public void ImplicitlyTypedArrayCreation_07()
{
......@@ -193,14 +192,29 @@ static void F()
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (12,42): warning CS8619: Nullability of reference types in value of type 'B1' doesn't match target type 'A<object>'.
// var a = new[] { new A<object>(), new B1() };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new B1()").WithArguments("B1", "A<object>").WithLocation(12, 42),
// (14,43): warning CS8619: Nullability of reference types in value of type 'B2' doesn't match target type 'A<object?>'.
// var b = new[] { new A<object?>(), new B2() };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new B2()").WithArguments("B2", "A<object?>").WithLocation(14, 43),
// (15,9): warning CS8602: Possible dereference of a null reference.
// b[0].F.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b[0].F").WithLocation(15, 9),
// (16,25): warning CS8619: Nullability of reference types in value of type 'B1' doesn't match target type 'A<object>'.
// var c = new[] { new B1(), new A<object>() };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new B1()").WithArguments("B1", "A<object>").WithLocation(16, 25),
// (18,25): warning CS8619: Nullability of reference types in value of type 'B2' doesn't match target type 'A<object?>'.
// var d = new[] { new B2(), new A<object?>() };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new B2()").WithArguments("B2", "A<object?>").WithLocation(18, 25),
// (19,9): warning CS8602: Possible dereference of a null reference.
// d[0].F.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "d[0].F").WithLocation(19, 9));
}
// PROTOTYPE(NullableReferenceType): The array element type should be nullable,
// even though there is no best type when considering nullability for C<object>? and
// C<object?>. In short, should report WRN_NullReferenceReceiver for `c[0].ToString()`
[Fact]
public void ImplicitlyTypedArrayCreation_08()
{
......@@ -231,15 +245,9 @@ static void F(C<object>? a, C<object?> b)
// (8,32): warning CS8619: Nullability of reference types in value of type 'C<object?>' doesn't match target type 'C<object>'.
// var c = new[] { a, b };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b").WithArguments("C<object?>", "C<object>").WithLocation(8, 32),
// (9,13): warning CS8602: Possible dereference of a null reference.
// c[0].ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c[0]").WithLocation(9, 13),
// (10,32): warning CS8619: Nullability of reference types in value of type 'C<object>' doesn't match target type 'C<object?>'.
// var d = new[] { b, a };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "a").WithArguments("C<object>", "C<object?>").WithLocation(10, 32),
// (11,13): warning CS8602: Possible dereference of a null reference.
// d[0].ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "d[0]").WithLocation(11, 13),
// (15,32): warning CS8619: Nullability of reference types in value of type 'C<object?>' doesn't match target type 'C<object>'.
// var c = new[] { a, b };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b").WithArguments("C<object?>", "C<object>").WithLocation(15, 32),
......@@ -643,6 +651,48 @@ static void F3(bool b, B x, C? y)
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b ? y : x").WithLocation(18, 10));
}
// PROTOTYPE(NullableReferenceTypes): Conversions: ConditionalOperator
[Fact(Skip = "TODO")]
public void ConditionalOperator_10()
{
var source =
@"using System;
class C
{
static void F(bool b, object? x, object y)
{
(b ? x : throw new Exception()).ToString();
(b ? y : throw new Exception()).ToString();
(b ? throw new Exception() : x).ToString();
(b ? throw new Exception() : y).ToString();
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (6,10): warning CS8602: Possible dereference of a null reference.
// (b ? x : throw new Exception()).ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b ? x : throw new Exception()").WithLocation(6, 10),
// (8,10): warning CS8602: Possible dereference of a null reference.
// (b ? throw new Exception() : x).ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b ? throw new Exception() : x").WithLocation(8, 10));
}
[Fact]
public void ConditionalOperator_11()
{
var source =
@"class C
{
static void F(bool b, object x)
{
(b ? x : throw null).ToString();
(b ? throw null : x).ToString();
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
// PROTOTYPE(NullableReferenceTypes): Conversions: NullCoalescingOperator
[Fact(Skip = "TODO")]
public void NullCoalescingOperator_01()
......@@ -815,9 +865,33 @@ static void F(object? o, object[]? a, object?[]? b)
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(b ?? c)[0]").WithLocation(15, 13));
}
// PROTOTYPE(NullableReferenceType): NullableWalker.VisitAnonymousObjectCreationExpression
// should support initializers with inferred nullability.
[Fact(Skip = "TODO")]
[Fact]
public void NullCoalescingOperator_07()
{
var source =
@"interface I<T> { }
class C
{
static object? F((I<object>, I<object?>)? x, (I<object?>, I<object>)? y)
{
return x ?? y;
}
static object F((I<object>, I<object?>)? x, (I<object?>, I<object>) y)
{
return x ?? y;
}
}";
var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (6,21): warning CS8619: Nullability of reference types in value of type '(I<object?>, I<object>)?' doesn't match target type '(I<object>, I<object?>)?'.
// return x ?? y;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("(I<object?>, I<object>)?", "(I<object>, I<object?>)?").WithLocation(6, 21),
// (10,21): warning CS8619: Nullability of reference types in value of type '(I<object?>, I<object>)' doesn't match target type '(I<object>, I<object?>)'.
// return x ?? y;
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("(I<object?>, I<object>)", "(I<object>, I<object?>)").WithLocation(10, 21));
}
[Fact]
public void AnonymousObjectCreation_01()
{
var source =
......@@ -903,6 +977,45 @@ static void Main()
comp.VerifyDiagnostics();
}
// PROTOTYPE(NullableReferenceTypes): Track nullability through deconstruction assignment.
[Fact(Skip = "TODO")]
public void DeconstructionTypeInference()
{
var source =
@"class C
{
static void F((object? a, object? b) t)
{
if (t.b == null) return;
object? x;
object? y;
(x, y) = t;
x.ToString();
y.ToString();
}
static void F(object? a, object? b)
{
if (b == null) return;
object? x;
object? y;
(x, y) = (a, b);
x.ToString();
y.ToString();
}
}";
var comp = CreateStandardCompilation(
source,
references: new[] { ValueTupleRef, SystemRuntimeFacadeRef },
parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (9,9): warning CS8602: Possible dereference of a null reference.
// x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(9, 9),
// (18,9): warning CS8602: Possible dereference of a null reference.
// x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(18, 9));
}
[Fact]
public void DynamicInvocation()
{
......@@ -1092,7 +1205,8 @@ static void H(bool b, object? x, string y)
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "x").WithLocation(15, 43));
}
[Fact]
// PROTOTYPE(NullableReferenceTypes): Infer lambda return type nullability.
[Fact(Skip = "TODO")]
public void LambdaReturnValue_03()
{
var source =
......@@ -1114,15 +1228,16 @@ static void H(bool b, object? x, string y)
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (10,43): warning CS8603: Possible null reference return.
// (10,9): warning CS8602: Possible dereference of a null reference.
// F(() => { if (b) return x; return y; }).ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "y").WithLocation(10, 43),
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(() => { if (b) return x; return y; })").WithLocation(10, 9),
// (14,9): warning CS8602: Possible dereference of a null reference.
// F(() => { if (b) return x; return y; }).ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(() => { if (b) return x; return y; })").WithLocation(14, 9));
}
[Fact]
// PROTOTYPE(NullableReferenceTypes): Infer lambda return type nullability.
[Fact(Skip = "TODO")]
public void LambdaReturnValue_04()
{
var source =
......@@ -1143,10 +1258,7 @@ static void G(object? o)
comp.VerifyDiagnostics(
// (10,9): warning CS8602: Possible dereference of a null reference.
// F(() => o).ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(() => o)").WithLocation(10, 9),
// (11,24): warning CS8602: Possible dereference of a null reference.
// if (o != null) F(() => o).ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(() => o)").WithLocation(11, 24));
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(() => o)").WithLocation(10, 9));
}
// PROTOTYPE(NullableReferenceTypes): Infer lambda return type nullability.
......@@ -1169,5 +1281,362 @@ static void G()
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
[Fact]
public void IdentityConversion_ArrayInitializer()
{
var source =
@"interface I<T> { }
interface IIn<in T> { }
interface IOut<out T> { }
class C
{
static void F(I<object> x, I<object?> y, I<object>? z, I<object?>? w)
{
(new[] { x, y })[0].ToString(); // A1
(new[] { x, z })[0].ToString(); // A2
(new[] { x, w })[0].ToString(); // A3
(new[] { y, z })[0].ToString(); // A4
(new[] { y, w })[0].ToString(); // A5
(new[] { w, z })[0].ToString(); // A6
}
static void F(IIn<object> x, IIn<object?> y, IIn<object>? z, IIn<object?>? w)
{
(new[] { x, y })[0].ToString(); // B1
(new[] { x, z })[0].ToString(); // B2
(new[] { x, w })[0].ToString(); // B3
(new[] { y, z })[0].ToString(); // B4
(new[] { y, w })[0].ToString(); // B5
(new[] { w, z })[0].ToString(); // B6
}
static void F(IOut<object> x, IOut<object?> y, IOut<object>? z, IOut<object?>? w)
{
(new[] { x, y })[0].ToString(); // C1
(new[] { x, z })[0].ToString(); // C2
(new[] { x, w })[0].ToString(); // C3
(new[] { y, z })[0].ToString(); // C4
(new[] { y, w })[0].ToString(); // C5
(new[] { w, z })[0].ToString(); // C6
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (8,21): warning CS8619: Nullability of reference types in value of type 'I<object?>' doesn't match target type 'I<object>'.
// (new[] { x, y })[0].ToString(); // A1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("I<object?>", "I<object>").WithLocation(8, 21),
// (9,9): warning CS8602: Possible dereference of a null reference.
// (new[] { x, z })[0].ToString(); // A2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, z })[0]").WithLocation(9, 9),
// (10,21): warning CS8619: Nullability of reference types in value of type 'I<object?>' doesn't match target type 'I<object>'.
// (new[] { x, w })[0].ToString(); // A3
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "w").WithArguments("I<object?>", "I<object>").WithLocation(10, 21),
// (11,21): warning CS8619: Nullability of reference types in value of type 'I<object>' doesn't match target type 'I<object?>'.
// (new[] { y, z })[0].ToString(); // A4
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "z").WithArguments("I<object>", "I<object?>").WithLocation(11, 21),
// (12,9): warning CS8602: Possible dereference of a null reference.
// (new[] { y, w })[0].ToString(); // A5
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, w })[0]").WithLocation(12, 9),
// (13,21): warning CS8619: Nullability of reference types in value of type 'I<object>' doesn't match target type 'I<object?>'.
// (new[] { w, z })[0].ToString(); // A6
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "z").WithArguments("I<object>", "I<object?>").WithLocation(13, 21),
// (18,9): warning CS8602: Possible dereference of a null reference.
// (new[] { x, z })[0].ToString(); // B2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, z })[0]").WithLocation(18, 9),
// (19,9): warning CS8602: Possible dereference of a null reference.
// (new[] { x, w })[0].ToString(); // B3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, w })[0]").WithLocation(19, 9),
// (20,9): warning CS8602: Possible dereference of a null reference.
// (new[] { y, z })[0].ToString(); // B4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, z })[0]").WithLocation(20, 9),
// (21,9): warning CS8602: Possible dereference of a null reference.
// (new[] { y, w })[0].ToString(); // B5
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, w })[0]").WithLocation(21, 9),
// (22,9): warning CS8602: Possible dereference of a null reference.
// (new[] { w, z })[0].ToString(); // B6
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { w, z })[0]").WithLocation(22, 9),
// (27,9): warning CS8602: Possible dereference of a null reference.
// (new[] { x, z })[0].ToString(); // C2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, z })[0]").WithLocation(27, 9),
// (28,9): warning CS8602: Possible dereference of a null reference.
// (new[] { x, w })[0].ToString(); // C3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, w })[0]").WithLocation(28, 9),
// (29,9): warning CS8602: Possible dereference of a null reference.
// (new[] { y, z })[0].ToString(); // C4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, z })[0]").WithLocation(29, 9),
// (30,9): warning CS8602: Possible dereference of a null reference.
// (new[] { y, w })[0].ToString(); // C5
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, w })[0]").WithLocation(30, 9),
// (31,9): warning CS8602: Possible dereference of a null reference.
// (new[] { w, z })[0].ToString(); // C6
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { w, z })[0]").WithLocation(31, 9));
}
// PROTOTYPE(NullableReferenceTypes): Update this method to use types from unannotated assemblies
// rather than `x!`, particularly because `x!` should result in IsNullable=false rather than IsNullable=null.
// PROTOTYPE(NullableReferenceTypes): Should report the same warnings (or no warnings) for { x, x! } and { x!, x }.
[Fact(Skip = "TODO")]
public void IdentityConversion_ArrayInitializer_IsNullableNull()
{
var source =
@"#pragma warning disable 0649
class A<T>
{
internal T F;
}
class B
{
static void F(object? x, object y)
{
(new[] { x, x! })[0].ToString();
(new[] { x!, x })[0].ToString();
(new[] { y, y! })[0].ToString();
(new[] { y!, y })[0].ToString();
}
static void F(A<object?> z, A<object> w)
{
(new[] { z, z! })[0].F.ToString();
(new[] { z!, z })[0].F.ToString();
(new[] { w, w! })[0].F.ToString();
(new[] { w!, w })[0].F.ToString();
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
// PROTOTYPE(NullableReferenceTypes): Update this method to use types from unannotated assemblies
// rather than `x!`, particularly because `x!` should result in IsNullable=false rather than IsNullable=null.
// PROTOTYPE(NullableReferenceTypes): Should report the same warnings (or no warnings) for (x, x!) and (x!, x).
[Fact(Skip = "TODO")]
public void IdentityConversion_TypeInference_IsNullableNull()
{
var source =
@"class A<T>
{
}
class B
{
static T F1<T>(T x, T y)
{
return x;
}
static void G1(object? x, object y)
{
F1(x, x!).ToString();
F1(x!, x).ToString();
F1(y, y!).ToString();
F1(y!, y).ToString();
}
static T F2<T>(A<T> x, A<T> y)
{
throw new System.Exception();
}
static void G(A<object?> z, A<object> w)
{
F2(z, z!).ToString();
F2(z!, z).ToString();
F2(w, w!).ToString();
F2(w!, w).ToString();
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
}
// PROTOTYPE(NullableReferenceTypes): Conversions: Other
[Fact(Skip = "TODO")]
public void IdentityConversion_ArrayInitializer_ExplicitType()
{
var source =
@"interface I<T> { }
interface IIn<in T> { }
interface IOut<out T> { }
class C
{
static void F(I<object>? x, I<object?>? y)
{
I<object?>?[] a = new[] { x };
I<object?>[] b = new[] { y };
I<object>?[] c = new[] { y };
I<object>[] d = new[] { x };
}
static void F(IIn<object>? x, IIn<object?>? y)
{
IIn<object?>?[] a = new[] { x };
IIn<object?>[] b = new[] { y };
IIn<object>?[] c = new[] { y };
IIn<object>[] d = new[] { x };
}
static void F(IOut<object>? x, IOut<object?>? y)
{
IOut<object?>?[] a = new[] { x };
IOut<object?>[] b = new[] { y };
IOut<object>?[] c = new[] { y };
IOut<object>[] d = new[] { x };
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (8,27): warning CS8619: Nullability of reference types in value of type 'I<object>?[]' doesn't match target type 'I<object?>?[]'.
// I<object?>?[] a = new[] { x };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { x }").WithArguments("I<object>?[]", "I<object?>?[]").WithLocation(8, 27),
// (9,26): warning CS8619: Nullability of reference types in value of type 'I<object?>?[]' doesn't match target type 'I<object?>[]'.
// I<object?>[] b = new[] { y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { y }").WithArguments("I<object?>?[]", "I<object?>[]").WithLocation(9, 26),
// (10,26): warning CS8619: Nullability of reference types in value of type 'I<object?>?[]' doesn't match target type 'I<object>?[]'.
// I<object>?[] c = new[] { y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { y }").WithArguments("I<object?>?[]", "I<object>?[]").WithLocation(10, 26),
// (11,25): warning CS8619: Nullability of reference types in value of type 'I<object>?[]' doesn't match target type 'I<object>[]'.
// I<object>[] d = new[] { x };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { x }").WithArguments("I<object>?[]", "I<object>[]").WithLocation(11, 25),
// (15,29): warning CS8619: Nullability of reference types in value of type 'IIn<object>?[]' doesn't match target type 'IIn<object?>?[]'.
// IIn<object?>?[] a = new[] { x };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { x }").WithArguments("IIn<object>?[]", "IIn<object?>?[]").WithLocation(15, 29),
// (16,28): warning CS8619: Nullability of reference types in value of type 'IIn<object?>?[]' doesn't match target type 'IIn<object?>[]'.
// IIn<object?>[] b = new[] { y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { y }").WithArguments("IIn<object?>?[]", "IIn<object?>[]").WithLocation(16, 28),
// (18,27): warning CS8619: Nullability of reference types in value of type 'IIn<object>?[]' doesn't match target type 'IIn<object>[]'.
// IIn<object>[] d = new[] { x };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { x }").WithArguments("IIn<object>?[]", "IIn<object>[]").WithLocation(18, 27),
// (23,29): warning CS8619: Nullability of reference types in value of type 'IOut<object?>?[]' doesn't match target type 'IOut<object?>[]'.
// IOut<object?>[] b = new[] { y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { y }").WithArguments("IOut<object?>?[]", "IOut<object?>[]").WithLocation(23, 29),
// (24,29): warning CS8619: Nullability of reference types in value of type 'IOut<object?>?[]' doesn't match target type 'IOut<object>?[]'.
// IOut<object>?[] c = new[] { y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { y }").WithArguments("IOut<object?>?[]", "IOut<object>?[]").WithLocation(24, 29),
// (25,28): warning CS8619: Nullability of reference types in value of type 'IOut<object>?[]' doesn't match target type 'IOut<object>[]'.
// IOut<object>[] d = new[] { x };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new[] { x }").WithArguments("IOut<object>?[]", "IOut<object>[]").WithLocation(25, 28));
}
[Fact]
public void ImplicitConversion_ArrayInitializer_ExplicitType_01()
{
var source =
@"class A<T> { }
class B<T> : A<T> { }
class C
{
static void F(A<object> x, B<object?> y)
{
var z = new A<object>[] { x, y };
var w = new A<object?>[] { x, y };
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (7,38): warning CS8619: Nullability of reference types in value of type 'B<object?>' doesn't match target type 'A<object>'.
// var z = new A<object>[] { x, y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("B<object?>", "A<object>").WithLocation(7, 38),
// (8,36): warning CS8619: Nullability of reference types in value of type 'A<object>' doesn't match target type 'A<object?>'.
// var w = new A<object?>[] { x, y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("A<object>", "A<object?>").WithLocation(8, 36));
}
[Fact]
public void ImplicitConversion_ArrayInitializer_ExplicitType_02()
{
var source =
@"interface IIn<in T> { }
interface IOut<out T> { }
class C
{
static void F(IIn<object> x, IIn<object?> y)
{
var a = new IIn<string?>[] { x };
var b = new IIn<string>[] { y };
}
static void F(IOut<string> x, IOut<string?> y)
{
var a = new IOut<object?>[] { x };
var b = new IOut<object>[] { y };
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (7,38): warning CS8619: Nullability of reference types in value of type 'IIn<object>' doesn't match target type 'IIn<string?>'.
// var a = new IIn<string?>[] { x };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("IIn<object>", "IIn<string?>").WithLocation(7, 38),
// (13,38): warning CS8619: Nullability of reference types in value of type 'IOut<string?>' doesn't match target type 'IOut<object>'.
// var b = new IOut<object>[] { y };
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("IOut<string?>", "IOut<object>").WithLocation(13, 38));
}
[Fact]
public void MultipleConversions_ArrayInitializer()
{
var source =
@"class A
{
public static implicit operator C(A a) => new C();
}
class B : A
{
}
class C
{
static void F(B x, C? y)
{
(new[] { x, y })[0].ToString();
(new[] { y, x })[0].ToString();
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(
// (12,9): warning CS8602: Possible dereference of a null reference.
// (new[] { x, y })[0].ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, y })[0]").WithLocation(12, 9),
// (13,9): warning CS8602: Possible dereference of a null reference.
// (new[] { y, x })[0].ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { y, x })[0]").WithLocation(13, 9));
}
// PROTOTYPE(NullableReferenceTypes): Conversions: ConditionalOperator
[Fact(Skip = "TODO")]
public void IdentityConversion_ConditionalOperator()
{
var source =
@"interface I<T> { }
interface IIn<in T> { }
interface IOut<out T> { }
class C
{
static void F(bool c, I<object> x, I<object?> y)
{
I<object> a;
a = c ? x : y;
a = false ? x : y;
a = true ? x : y;
I<object?> b;
b = c ? x : y;
b = false ? x : y;
b = true ? x : y;
}
static void F(bool c, IIn<object> x, IIn<object?> y)
{
IIn<object> a;
a = c ? x : y;
a = false ? x : y;
a = true ? x : y;
IIn<object?> b;
b = c ? x : y;
b = false ? x : y;
b = true ? x : y;
}
static void F(bool c, IOut<object> x, IOut<object?> y)
{
IOut<object> a;
a = c ? x : y;
a = false ? x : y;
a = true ? x : y;
IOut<object?> b;
b = c ? x : y;
b = false ? x : y;
b = true ? x : y;
}
}";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics(/*TODO*/);
}
}
}
......@@ -598,8 +598,7 @@ static void F(string? s)
var declarator = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().First();
var symbol = (LocalSymbol)model.GetDeclaredSymbol(declarator);
Assert.Equal("System.String", symbol.Type.ToTestDisplayString());
// PROTOTYPE(NullableReferenceTypes): IsNullable should be inferred nullable state: null.
Assert.Equal(false, symbol.Type.IsNullable);
Assert.Equal(null, symbol.Type.IsNullable);
}
[Fact]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册