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

Add IncludeNullability option to Conversions (#24935)

上级 29905b3d
...@@ -33,12 +33,13 @@ internal Binder(CSharpCompilation compilation) ...@@ -33,12 +33,13 @@ internal Binder(CSharpCompilation compilation)
this.Compilation = compilation; this.Compilation = compilation;
} }
internal Binder(Binder next) internal Binder(Binder next, Conversions conversions = null)
{ {
Debug.Assert(next != null); Debug.Assert(next != null);
_next = next; _next = next;
this.Flags = next.Flags; this.Flags = next.Flags;
this.Compilation = next.Compilation; this.Compilation = next.Compilation;
_lazyConversions = conversions;
} }
protected Binder(Binder next, BinderFlags flags) protected Binder(Binder next, BinderFlags flags)
......
...@@ -235,10 +235,10 @@ protected BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundEx ...@@ -235,10 +235,10 @@ protected BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundEx
userDefinedConversion = CreateConversion( userDefinedConversion = CreateConversion(
syntax: syntax, syntax: syntax,
source: userDefinedConversion, source: userDefinedConversion,
conversion: Conversions.ClassifyStandardConversion(null, conversionReturnType, conversion.BestUserDefinedConversionAnalysis.ToType, ref useSiteDiagnostics), conversion: Conversions.ClassifyStandardConversion(null, conversionReturnType, conversionToType, ref useSiteDiagnostics),
isCast: false, isCast: false,
wasCompilerGenerated: true, wasCompilerGenerated: true,
destination: conversion.BestUserDefinedConversionAnalysis.ToType, destination: conversionToType,
diagnostics: diagnostics); diagnostics: diagnostics);
} }
} }
...@@ -253,7 +253,7 @@ protected BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundEx ...@@ -253,7 +253,7 @@ protected BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundEx
@checked: false, @checked: false,
explicitCastInCode: isCast, explicitCastInCode: isCast,
constantValueOpt: ConstantValue.NotAvailable, constantValueOpt: ConstantValue.NotAvailable,
type: conversion.BestUserDefinedConversionAnalysis.ToType) type: conversionToType)
{ WasCompilerGenerated = true }; { WasCompilerGenerated = true };
} }
......
...@@ -2100,7 +2100,7 @@ private static NameSyntax GetNameSyntax(SyntaxNode syntax) ...@@ -2100,7 +2100,7 @@ private static NameSyntax GetNameSyntax(SyntaxNode syntax)
/// </summary> /// </summary>
/// <param name="syntax">Syntax node</param> /// <param name="syntax">Syntax node</param>
/// <param name="nameString">Plain text name</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; nameString = string.Empty;
while (true) while (true)
...@@ -2706,7 +2706,6 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio ...@@ -2706,7 +2706,6 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio
var bestType = BestTypeInferrer.InferBestType( var bestType = BestTypeInferrer.InferBestType(
boundInitializerExpressions, boundInitializerExpressions,
this.Conversions, this.Conversions,
includeNullability: false,
hadMultipleCandidates: out hadMultipleCandidates, hadMultipleCandidates: out hadMultipleCandidates,
useSiteDiagnostics: ref useSiteDiagnostics); useSiteDiagnostics: ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics);
......
...@@ -3586,7 +3586,6 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node ...@@ -3586,7 +3586,6 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
trueExpr, trueExpr,
falseExpr, falseExpr,
this.Conversions, this.Conversions,
includeNullability: false,
hadMultipleCandidates: out hadMultipleCandidates, hadMultipleCandidates: out hadMultipleCandidates,
useSiteDiagnostics: ref useSiteDiagnostics); useSiteDiagnostics: ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics); diagnostics.Add(node, useSiteDiagnostics);
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.PooledObjects;
...@@ -10,25 +9,49 @@ namespace Microsoft.CodeAnalysis.CSharp ...@@ -10,25 +9,49 @@ namespace Microsoft.CodeAnalysis.CSharp
{ {
internal sealed class BestTypeInferrer internal sealed class BestTypeInferrer
{ {
private readonly Conversions _conversions; public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<TypeSymbolWithAnnotations> types, Conversions conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
private BestTypeInferrer(Conversions conversions)
{ {
_conversions = conversions; return GetBestType(types, conversions, ref useSiteDiagnostics);
} }
// PROTOTYPE(NullableReferenceTypes): Remove includeNullability parameter. Nullability should be calculated in flow analysis. private static bool? GetIsNullable(ImmutableArray<TypeSymbolWithAnnotations> types)
public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<TypeSymbolWithAnnotations> types, Conversions conversions, bool includeNullability, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{ {
var inferrer = new BestTypeInferrer(conversions); bool? isNullable = false;
return inferrer.GetBestType(types, includeNullability, ref useSiteDiagnostics); 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> /// <remarks>
/// This method finds the best common type of a set of expressions as per section 7.5.2.14 of the specification. /// 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. /// NOTE: If some or all of the expressions have error types, we return error type as the inference result.
/// </remarks> /// </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: 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 // 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 ...@@ -41,6 +64,7 @@ public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<BoundExpres
// SPEC: If no such S exists, the expressions have no best common type. // SPEC: If no such S exists, the expressions have no best common type.
// All non-null types are candidates for best type inference. // All non-null types are candidates for best type inference.
bool includeNullability = conversions.IncludeNullability;
var candidateTypes = new HashSet<TypeSymbolWithAnnotations>(TypeSymbolWithAnnotations.EqualsComparer.Instance); var candidateTypes = new HashSet<TypeSymbolWithAnnotations>(TypeSymbolWithAnnotations.EqualsComparer.Instance);
foreach (BoundExpression expr in exprs) foreach (BoundExpression expr in exprs)
{ {
...@@ -61,14 +85,14 @@ public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<BoundExpres ...@@ -61,14 +85,14 @@ public static TypeSymbolWithAnnotations InferBestType(ImmutableArray<BoundExpres
hadMultipleCandidates = candidateTypes.Count > 1; hadMultipleCandidates = candidateTypes.Count > 1;
// Perform best type inference on candidate types. // Perform best type inference on candidate types.
return InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, includeNullability, ref useSiteDiagnostics); return InferBestType(candidateTypes.AsImmutableOrEmpty(), conversions, ref useSiteDiagnostics);
} }
/// <remarks> /// <remarks>
/// This method implements best type inference for the conditional operator ?:. /// 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. /// NOTE: If either expression is an error type, we return error type as the inference result.
/// </remarks> /// </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: 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 // SPEC: • If x has type X and y has type Y then
...@@ -79,6 +103,7 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun ...@@ -79,6 +103,7 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
// SPEC: • Otherwise, no expression type can be determined, and a compile-time error occurs. // 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. // A type is a candidate if all expressions are convertible to that type.
bool includeNullability = conversions.IncludeNullability;
var candidateTypes = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance(); var candidateTypes = ArrayBuilder<TypeSymbolWithAnnotations>.GetInstance();
var type1 = expr1.GetTypeAndNullability(includeNullability); var type1 = expr1.GetTypeAndNullability(includeNullability);
...@@ -92,7 +117,6 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun ...@@ -92,7 +117,6 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
return type1; return type1;
} }
// PROTOTYPE(NullableReferenceTypes): Consider nullability in conversion.
if (conversions.ClassifyImplicitConversionFromExpression(expr2, type1.TypeSymbol, ref useSiteDiagnostics).Exists) if (conversions.ClassifyImplicitConversionFromExpression(expr2, type1.TypeSymbol, ref useSiteDiagnostics).Exists)
{ {
candidateTypes.Add(type1); candidateTypes.Add(type1);
...@@ -101,7 +125,7 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun ...@@ -101,7 +125,7 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
var type2 = expr2.GetTypeAndNullability(includeNullability); var type2 = expr2.GetTypeAndNullability(includeNullability);
if ((object)type2 != null && !type2.Equals(type1, TypeCompareKind.ConsiderEverything)) if ((object)type2 != null)
{ {
if (type2.IsErrorType()) if (type2.IsErrorType())
{ {
...@@ -110,7 +134,6 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun ...@@ -110,7 +134,6 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
return type2; return type2;
} }
// PROTOTYPE(NullableReferenceTypes): Consider nullability in conversion.
if (conversions.ClassifyImplicitConversionFromExpression(expr1, type2.TypeSymbol, ref useSiteDiagnostics).Exists) if (conversions.ClassifyImplicitConversionFromExpression(expr1, type2.TypeSymbol, ref useSiteDiagnostics).Exists)
{ {
candidateTypes.Add(type2); candidateTypes.Add(type2);
...@@ -119,13 +142,10 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun ...@@ -119,13 +142,10 @@ public static TypeSymbolWithAnnotations InferBestTypeForConditionalOperator(Boun
hadMultipleCandidates = candidateTypes.Count > 1; 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 private static TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnotations> types, Conversions conversions, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
// 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)
{ {
// This code assumes that the types in the list are unique. // This code assumes that the types in the list are unique.
...@@ -155,7 +175,7 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot ...@@ -155,7 +175,7 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot
} }
else else
{ {
var better = Better(best, type, ref useSiteDiagnostics); var better = Better(best, type, conversions, ref useSiteDiagnostics);
if ((object)better == null) if ((object)better == null)
{ {
...@@ -179,7 +199,7 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot ...@@ -179,7 +199,7 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot
for (int i = 0; i < bestIndex; i++) for (int i = 0; i < bestIndex; i++)
{ {
var type = types[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)) if (!best.Equals(better, TypeCompareKind.ConsiderEverything))
{ {
...@@ -187,25 +207,21 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot ...@@ -187,25 +207,21 @@ private TypeSymbolWithAnnotations GetBestType(ImmutableArray<TypeSymbolWithAnnot
} }
} }
// If any of the types are null, the result should be nullable. // If any of the types are nullable, the result should be nullable.
// PROTOTYPE(NullableReferenceTypes): Should ignore untyped return conversions.IncludeNullability ? UpdateNullability(best, types) : best;
// 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();
}
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> /// <summary>
/// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names). /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names).
/// </summary> /// </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. // Anything is better than an error sym.
if (type1.IsErrorType()) if (type1.IsErrorType())
...@@ -218,8 +234,8 @@ private TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSy ...@@ -218,8 +234,8 @@ private TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSy
return type1; return type1;
} }
var t1tot2 = _conversions.ClassifyImplicitConversionFromType(type1.TypeSymbol, type2.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; var t2tot1 = conversions.ClassifyImplicitConversionFromType(type2.TypeSymbol, type1.TypeSymbol, ref useSiteDiagnostics).Exists;
if (t1tot2 && t2tot1) if (t1tot2 && t2tot1)
{ {
...@@ -235,7 +251,7 @@ private TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSy ...@@ -235,7 +251,7 @@ private TypeSymbolWithAnnotations Better(TypeSymbolWithAnnotations type1, TypeSy
if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames)) if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames))
{ {
return MethodTypeInferrer.Merge(type1, type2, _conversions.CorLibrary); return MethodTypeInferrer.Merge(type1, type2, conversions.CorLibrary);
} }
return null; return null;
......
...@@ -13,23 +13,28 @@ internal sealed class Conversions : ConversionsBase ...@@ -13,23 +13,28 @@ internal sealed class Conversions : ConversionsBase
private readonly Binder _binder; private readonly Binder _binder;
public Conversions(Binder binder) public Conversions(Binder binder)
: this(binder, currentRecursionDepth: 0) : this(binder, currentRecursionDepth: 0, includeNullability: false)
{ {
} }
private Conversions(Binder binder, int currentRecursionDepth) private Conversions(Binder binder, int currentRecursionDepth, bool includeNullability)
: base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth) : base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth, includeNullability)
{ {
_binder = binder; _binder = binder;
} }
protected override ConversionsBase CreateInstance(int currentRecursionDepth) protected override ConversionsBase CreateInstance(int currentRecursionDepth)
{ {
return new Conversions(_binder, currentRecursionDepth); return new Conversions(_binder, currentRecursionDepth, IncludeNullability);
} }
private CSharpCompilation Compilation { get { return _binder.Compilation; } } 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) public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{ {
// Must be a bona fide delegate type, not an expression tree type. // Must be a bona fide delegate type, not an expression tree type.
......
...@@ -10,18 +10,18 @@ namespace Microsoft.CodeAnalysis.CSharp ...@@ -10,18 +10,18 @@ namespace Microsoft.CodeAnalysis.CSharp
internal sealed class TypeConversions : ConversionsBase internal sealed class TypeConversions : ConversionsBase
{ {
public TypeConversions(AssemblySymbol corLibrary) public TypeConversions(AssemblySymbol corLibrary)
: this(corLibrary, currentRecursionDepth: 0) : this(corLibrary, currentRecursionDepth: 0, includeNullability: false)
{ {
} }
private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth) private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability)
: base(corLibrary, currentRecursionDepth) : base(corLibrary, currentRecursionDepth, includeNullability)
{ {
} }
protected override ConversionsBase CreateInstance(int currentRecursionDepth) 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) public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
......
...@@ -90,8 +90,6 @@ private enum Dependency ...@@ -90,8 +90,6 @@ private enum Dependency
private readonly ImmutableArray<TypeSymbolWithAnnotations> _formalParameterTypes; private readonly ImmutableArray<TypeSymbolWithAnnotations> _formalParameterTypes;
private readonly ImmutableArray<RefKind> _formalParameterRefKinds; private readonly ImmutableArray<RefKind> _formalParameterRefKinds;
private readonly ImmutableArray<BoundExpression> _arguments; private readonly ImmutableArray<BoundExpression> _arguments;
// PROTOTYPE(NullableReferenceTypes): Remove conditional and infer nullability always.
private readonly bool _includeNullability;
private readonly TypeSymbolWithAnnotations[] _fixedResults; private readonly TypeSymbolWithAnnotations[] _fixedResults;
private readonly HashSet<TypeSymbolWithAnnotations>[] _exactBounds; private readonly HashSet<TypeSymbolWithAnnotations>[] _exactBounds;
...@@ -214,8 +212,7 @@ private enum Dependency ...@@ -214,8 +212,7 @@ private enum Dependency
ImmutableArray<RefKind> formalParameterRefKinds, // Optional; assume all value if missing. ImmutableArray<RefKind> formalParameterRefKinds, // Optional; assume all value if missing.
ImmutableArray<BoundExpression> arguments,// Required; in scenarios like method group conversions where there are ImmutableArray<BoundExpression> arguments,// Required; in scenarios like method group conversions where there are
// no arguments per se we cons up some fake arguments. // no arguments per se we cons up some fake arguments.
ref HashSet<DiagnosticInfo> useSiteDiagnostics, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
bool includeNullability = false)
{ {
Debug.Assert(!methodTypeParameters.IsDefault); Debug.Assert(!methodTypeParameters.IsDefault);
Debug.Assert(methodTypeParameters.Length > 0); Debug.Assert(methodTypeParameters.Length > 0);
...@@ -239,8 +236,7 @@ private enum Dependency ...@@ -239,8 +236,7 @@ private enum Dependency
constructedContainingTypeOfMethod, constructedContainingTypeOfMethod,
formalParameterTypes, formalParameterTypes,
formalParameterRefKinds, formalParameterRefKinds,
arguments, arguments);
includeNullability);
return inferrer.InferTypeArgs(binder, ref useSiteDiagnostics); return inferrer.InferTypeArgs(binder, ref useSiteDiagnostics);
} }
...@@ -259,8 +255,7 @@ private enum Dependency ...@@ -259,8 +255,7 @@ private enum Dependency
NamedTypeSymbol constructedContainingTypeOfMethod, NamedTypeSymbol constructedContainingTypeOfMethod,
ImmutableArray<TypeSymbolWithAnnotations> formalParameterTypes, ImmutableArray<TypeSymbolWithAnnotations> formalParameterTypes,
ImmutableArray<RefKind> formalParameterRefKinds, ImmutableArray<RefKind> formalParameterRefKinds,
ImmutableArray<BoundExpression> arguments, ImmutableArray<BoundExpression> arguments)
bool includeNullability)
{ {
_conversions = conversions; _conversions = conversions;
_methodTypeParameters = methodTypeParameters; _methodTypeParameters = methodTypeParameters;
...@@ -268,7 +263,6 @@ private enum Dependency ...@@ -268,7 +263,6 @@ private enum Dependency
_formalParameterTypes = formalParameterTypes; _formalParameterTypes = formalParameterTypes;
_formalParameterRefKinds = formalParameterRefKinds; _formalParameterRefKinds = formalParameterRefKinds;
_arguments = arguments; _arguments = arguments;
_includeNullability = includeNullability;
_fixedResults = new TypeSymbolWithAnnotations[methodTypeParameters.Length]; _fixedResults = new TypeSymbolWithAnnotations[methodTypeParameters.Length];
_exactBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length]; _exactBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
_upperBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length]; _upperBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
...@@ -2343,6 +2337,9 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics) ...@@ -2343,6 +2337,9 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
// Fix considering nullability, if possible, otherwise fix ignoring nullability. // Fix considering nullability, if possible, otherwise fix ignoring nullability.
// PROTOTYPE(NullableReferenceTypes): Avoid calling Fix ignoring nullability if the nullability call succeeds. // 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; HashSet<DiagnosticInfo> ignoredDiagnostics = null;
var withNullability = Fix(exact, lower, upper, ref ignoredDiagnostics, _conversions, includeNullability: true); var withNullability = Fix(exact, lower, upper, ref ignoredDiagnostics, _conversions, includeNullability: true);
var withoutNullability = Fix(exact, lower, upper, ref useSiteDiagnostics, _conversions, includeNullability: false); var withoutNullability = Fix(exact, lower, upper, ref useSiteDiagnostics, _conversions, includeNullability: false);
...@@ -2534,13 +2531,21 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics) ...@@ -2534,13 +2531,21 @@ private bool Fix(int iParam, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
private bool? IsNullable(BoundExpression expr) private bool? IsNullable(BoundExpression expr)
{ {
if (!_includeNullability) if (!_conversions.IncludeNullability)
{ {
return false; return false;
} }
return expr.IsNullable(); 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) internal static TypeSymbolWithAnnotations Merge(TypeSymbolWithAnnotations first, TypeSymbolWithAnnotations second, AssemblySymbol corLibrary)
{ {
return MergeNullability(MergeTupleNames(MergeDynamic(first, second, corLibrary), second), second); return MergeNullability(MergeTupleNames(MergeDynamic(first, second, corLibrary), second), second);
...@@ -2786,7 +2791,6 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray<NamedTy ...@@ -2786,7 +2791,6 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray<NamedTy
ConversionsBase conversions, ConversionsBase conversions,
MethodSymbol method, MethodSymbol method,
ImmutableArray<BoundExpression> arguments, ImmutableArray<BoundExpression> arguments,
bool includeNullability,
ref HashSet<DiagnosticInfo> useSiteDiagnostics) ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{ {
Debug.Assert((object)method != null); Debug.Assert((object)method != null);
...@@ -2809,8 +2813,7 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray<NamedTy ...@@ -2809,8 +2813,7 @@ private static NamedTypeSymbol GetInterfaceInferenceBound(ImmutableArray<NamedTy
constructedFromMethod.ContainingType, constructedFromMethod.ContainingType,
constructedFromMethod.GetParameterTypes(), constructedFromMethod.GetParameterTypes(),
constructedFromMethod.ParameterRefKinds, constructedFromMethod.ParameterRefKinds,
arguments, arguments);
includeNullability);
if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics)) if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics))
{ {
......
...@@ -3080,7 +3080,6 @@ internal EffectiveParameters(ImmutableArray<TypeSymbolWithAnnotations> types, Im ...@@ -3080,7 +3080,6 @@ internal EffectiveParameters(ImmutableArray<TypeSymbolWithAnnotations> types, Im
_binder.Conversions, _binder.Conversions,
method, method,
args, args,
includeNullability: Compilation.IsFeatureEnabled(MessageID.IDS_FeatureStaticNullChecking),
useSiteDiagnostics: ref useSiteDiagnostics); useSiteDiagnostics: ref useSiteDiagnostics);
if (inferredFromFirstArgument.IsDefault) if (inferredFromFirstArgument.IsDefault)
{ {
......
...@@ -222,7 +222,7 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres ...@@ -222,7 +222,7 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres
switch (expr.Kind) switch (expr.Kind)
{ {
case BoundKind.SuppressNullableWarningExpression: case BoundKind.SuppressNullableWarningExpression:
return false; return null;
case BoundKind.Local: case BoundKind.Local:
{ {
var local = (BoundLocal)expr; var local = (BoundLocal)expr;
...@@ -245,9 +245,15 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres ...@@ -245,9 +245,15 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres
var right = op.RightOperand.IsNullable(); var right = op.RightOperand.IsNullable();
return (left == true) ? right : left; return (left == true) ? right : left;
} }
case BoundKind.ThisReference:
case BoundKind.BaseReference:
case BoundKind.NewT:
case BoundKind.ObjectCreationExpression: case BoundKind.ObjectCreationExpression:
case BoundKind.DelegateCreationExpression: case BoundKind.DelegateCreationExpression:
return false; case BoundKind.NoPiaObjectCreationExpression:
case BoundKind.InterpolatedString:
case BoundKind.TypeOfOperator:
case BoundKind.NameOfOperator:
case BoundKind.TupleLiteral: case BoundKind.TupleLiteral:
return false; return false;
case BoundKind.DefaultExpression: case BoundKind.DefaultExpression:
...@@ -263,9 +269,16 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres ...@@ -263,9 +269,16 @@ internal static TypeSymbolWithAnnotations GetTypeAndNullability(this BoundExpres
} }
var constant = expr.ConstantValue; 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; return null;
......
...@@ -140,8 +140,8 @@ public TypeSymbolWithAnnotations InferredReturnType(ref HashSet<DiagnosticInfo> ...@@ -140,8 +140,8 @@ public TypeSymbolWithAnnotations InferredReturnType(ref HashSet<DiagnosticInfo>
} }
else else
{ {
// PROTOTYPE(NullableReferenceTypes): Should pass includeNullability: false. var conversions = binder.Conversions.WithNullability(includeNullability);
bestResultType = BestTypeInferrer.InferBestType(resultTypes, binder.Conversions, includeNullability, ref useSiteDiagnostics); bestResultType = BestTypeInferrer.InferBestType(resultTypes, conversions, ref useSiteDiagnostics);
} }
if (!isAsync) if (!isAsync)
......
...@@ -319,7 +319,7 @@ protected static bool HasInitializer(FieldSymbol field) ...@@ -319,7 +319,7 @@ protected static bool HasInitializer(FieldSymbol field)
/// <summary> /// <summary>
/// Perform data flow analysis, reporting all necessary diagnostics. /// Perform data flow analysis, reporting all necessary diagnostics.
/// </summary> /// </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); Debug.Assert(diagnostics != null);
......
...@@ -68,7 +68,6 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m ...@@ -68,7 +68,6 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m
conversions, conversions,
method, method,
arguments.AsImmutable(), arguments.AsImmutable(),
includeNullability: false, // PROTOTYPE(NullableReferenceTypes): Support nullability if feature enabled.
useSiteDiagnostics: ref useSiteDiagnostics); useSiteDiagnostics: ref useSiteDiagnostics);
if (typeArgs.IsDefault) if (typeArgs.IsDefault)
......
...@@ -328,7 +328,7 @@ public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor) ...@@ -328,7 +328,7 @@ public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor)
return visitor.VisitProperty(this); return visitor.VisitProperty(this);
} }
internal virtual PropertySymbol AsMember(NamedTypeSymbol newOwner) internal PropertySymbol AsMember(NamedTypeSymbol newOwner)
{ {
Debug.Assert(this.IsDefinition); Debug.Assert(this.IsDefinition);
Debug.Assert(ReferenceEquals(newOwner.OriginalDefinition, this.ContainingSymbol.OriginalDefinition)); Debug.Assert(ReferenceEquals(newOwner.OriginalDefinition, this.ContainingSymbol.OriginalDefinition));
......
...@@ -140,7 +140,7 @@ protected override void CheckInterfaces(DiagnosticBag diagnostics) ...@@ -140,7 +140,7 @@ protected override void CheckInterfaces(DiagnosticBag diagnostics)
if (interfaces.Count > 1) if (interfaces.Count > 1)
{ {
var seenInterfaces = new Dictionary<NamedTypeSymbol, NamedTypeSymbol>(EqualsIgnoringComparer.InstanceIgnoringTupleNames); var seenInterfaces = new Dictionary<NamedTypeSymbol, NamedTypeSymbol>(EqualsIgnoringTupleNames);
foreach (var @interface in interfaces) foreach (var @interface in interfaces)
{ {
NamedTypeSymbol other; NamedTypeSymbol other;
......
...@@ -213,6 +213,36 @@ internal TupleTypeSymbol WithUnderlyingType(NamedTypeSymbol newUnderlyingType) ...@@ -213,6 +213,36 @@ internal TupleTypeSymbol WithUnderlyingType(NamedTypeSymbol newUnderlyingType)
return Create(_locations, newUnderlyingType, _elementLocations, _elementNames, _errorPositions); 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> /// <summary>
/// Copy this tuple, but modify it to use the new element names. /// 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. /// Also applies new location of the whole tuple as well as each element.
...@@ -319,34 +349,40 @@ private static NamedTypeSymbol GetTupleUnderlyingType(ImmutableArray<TypeSymbolW ...@@ -319,34 +349,40 @@ private static NamedTypeSymbol GetTupleUnderlyingType(ImmutableArray<TypeSymbolW
int remainder; int remainder;
int chainLength = NumberOfValueTuples(numElements, out remainder); int chainLength = NumberOfValueTuples(numElements, out remainder);
NamedTypeSymbol currentSymbol = default(NamedTypeSymbol);
NamedTypeSymbol firstTupleType = compilation.GetWellKnownType(GetTupleType(remainder)); NamedTypeSymbol firstTupleType = compilation.GetWellKnownType(GetTupleType(remainder));
if ((object)diagnostics != null && (object)syntax != null) if ((object)diagnostics != null && (object)syntax != null)
{ {
ReportUseSiteAndObsoleteDiagnostics(syntax, diagnostics, firstTupleType); ReportUseSiteAndObsoleteDiagnostics(syntax, diagnostics, firstTupleType);
} }
currentSymbol = firstTupleType.Construct(ImmutableArray.Create(elementTypes, (chainLength - 1) * (RestPosition - 1), remainder)); NamedTypeSymbol chainedTupleType = null;
if (chainLength > 1)
int loop = chainLength - 1;
if (loop > 0)
{ {
NamedTypeSymbol chainedTupleType = compilation.GetWellKnownType(GetTupleType(RestPosition)); chainedTupleType = compilation.GetWellKnownType(GetTupleType(RestPosition));
if ((object)diagnostics != null && (object)syntax != null) if ((object)diagnostics != null && (object)syntax != null)
{ {
ReportUseSiteAndObsoleteDiagnostics(syntax, diagnostics, chainedTupleType); ReportUseSiteAndObsoleteDiagnostics(syntax, diagnostics, chainedTupleType);
} }
}
do return ConstructTupleUnderlyingType(firstTupleType, chainedTupleType, elementTypes);
{ }
var chainedTypes = ImmutableArray.Create(elementTypes, (loop - 1) * (RestPosition - 1), RestPosition - 1).Add(TypeSymbolWithAnnotations.Create(currentSymbol));
currentSymbol = chainedTupleType.Construct(chainedTypes); internal static NamedTypeSymbol ConstructTupleUnderlyingType(NamedTypeSymbol firstTupleType, NamedTypeSymbol chainedTupleTypeOpt, ImmutableArray<TypeSymbolWithAnnotations> elementTypes)
loop--; {
} Debug.Assert(chainedTupleTypeOpt is null == elementTypes.Length < RestPosition);
while (loop > 0);
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; return currentSymbol;
......
...@@ -104,16 +104,19 @@ private InterfaceInfo GetInterfaceInfo() ...@@ -104,16 +104,19 @@ private InterfaceInfo GetInterfaceInfo()
return info; return info;
} }
internal static readonly EqualityComparer<TypeSymbol> EqualsConsiderEverything = new TypeSymbolComparer(TypeCompareKind.ConsiderEverything);
internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringTupleNames = new TypeSymbolComparer(TypeCompareKind.IgnoreTupleNames);
/// <summary> /// <summary>
/// A comparer that treats dynamic and object as "the same" types, and also ignores tuple element names differences. /// A comparer that treats dynamic and object as "the same" types, and also ignores tuple element names differences.
/// </summary> /// </summary>
internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringDynamicAndTupleNamesComparer = new EqualsIgnoringComparer(TypeCompareKind.IgnoreDynamicAndTupleNames); internal static readonly EqualityComparer<TypeSymbol> EqualsIgnoringDynamicAndTupleNamesComparer = new TypeSymbolComparer(TypeCompareKind.IgnoreDynamicAndTupleNames);
/// <summary> /// <summary>
/// A comparator that pays attention to nullable modifiers in addition to default behavior. /// A comparator that pays attention to nullable modifiers in addition to default behavior.
/// </summary> /// </summary>
internal static readonly EqualityComparer<TypeSymbol> EqualsIncludingNullableComparer = new EqualsIgnoringComparer(TypeCompareKind.CompareNullableModifiersForReferenceTypes); internal static readonly EqualityComparer<TypeSymbol> EqualsIncludingNullableComparer = new TypeSymbolComparer(TypeCompareKind.CompareNullableModifiersForReferenceTypes);
/// <summary> /// <summary>
/// The original definition of this symbol. If this symbol is constructed from another /// The original definition of this symbol. If this symbol is constructed from another
...@@ -307,13 +310,11 @@ public override int GetHashCode() ...@@ -307,13 +310,11 @@ public override int GetHashCode()
return RuntimeHelpers.GetHashCode(this); 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; private readonly TypeCompareKind _comparison;
public EqualsIgnoringComparer(TypeCompareKind comparison) public TypeSymbolComparer(TypeCompareKind comparison)
{ {
_comparison = comparison; _comparison = comparison;
} }
...@@ -354,7 +355,7 @@ protected virtual ImmutableArray<NamedTypeSymbol> GetAllInterfaces() ...@@ -354,7 +355,7 @@ protected virtual ImmutableArray<NamedTypeSymbol> GetAllInterfaces()
protected virtual ImmutableArray<NamedTypeSymbol> MakeAllInterfaces() protected virtual ImmutableArray<NamedTypeSymbol> MakeAllInterfaces()
{ {
var result = ArrayBuilder<NamedTypeSymbol>.GetInstance(); 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) for (var baseType = this; !ReferenceEquals(baseType, null); baseType = baseType.BaseTypeNoUseSiteDiagnostics)
{ {
......
...@@ -598,8 +598,7 @@ static void F(string? s) ...@@ -598,8 +598,7 @@ static void F(string? s)
var declarator = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().First(); var declarator = tree.GetRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>().First();
var symbol = (LocalSymbol)model.GetDeclaredSymbol(declarator); var symbol = (LocalSymbol)model.GetDeclaredSymbol(declarator);
Assert.Equal("System.String", symbol.Type.ToTestDisplayString()); Assert.Equal("System.String", symbol.Type.ToTestDisplayString());
// PROTOTYPE(NullableReferenceTypes): IsNullable should be inferred nullable state: null. Assert.Equal(null, symbol.Type.IsNullable);
Assert.Equal(false, symbol.Type.IsNullable);
} }
[Fact] [Fact]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册