未验证 提交 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.
......
......@@ -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);
......
......@@ -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)
{
......
......@@ -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.
先完成此消息的编辑!
想要评论请 注册