Skip to content

Commit

Permalink
Remove NonNullTypes context and other unnecessary information stored …
Browse files Browse the repository at this point in the history
…in TypeSymbolWithAnnotations. (#30913)

Remove NonNullTypes context and other unnecessary information stored in TypeSymbolWithAnnotations.

Fixes #30845.

Explicitly handle nullability analysis for unconstraint type parameters.

Related to #29981, #29993
  • Loading branch information
AlekseyTs authored Nov 9, 2018
1 parent 7f47498 commit e63f327
Show file tree
Hide file tree
Showing 38 changed files with 4,743 additions and 614 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpre
fields[i] = new AnonymousTypeField(
fieldName == null ? "$" + i.ToString() : fieldName,
fieldSyntaxNodes[i].Location,
TypeSymbolWithAnnotations.Create(fieldType, isNullableIfReferenceType));
TypeSymbolWithAnnotations.Create(fieldType, isNullableIfReferenceType, fromDeclaration: true));

// NOTE: ERR_InvalidAnonymousTypeMemberDeclarator (CS0746) would be generated by parser if needed
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Constraints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ private static bool IsValidConstraintType(TypeConstraintSyntax syntax, TypeSymbo
break;

case SpecialType.System_Object:
if (typeWithAnnotations.IsAnnotated)
if (typeWithAnnotations.NullableAnnotation == NullableAnnotation.Nullable)
{
// "Constraint cannot be special class '{0}'"
Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, typeWithAnnotations);
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3705,7 +3705,7 @@ protected BoundExpression BindObjectCreationExpression(ObjectCreationExpressionS
var type = typeWithAnnotations.TypeSymbol;
var originalType = type;

if (typeWithAnnotations.IsAnnotated && !type.IsNullableType())
if (typeWithAnnotations.NullableAnnotation == NullableAnnotation.Nullable && !type.IsNullableType())
{
diagnostics.Add(ErrorCode.ERR_AnnotationDisallowedInObjectCreation, node.Location, type);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS

if (a.QuestionToken.IsKind(SyntaxKind.QuestionToken))
{
type = TypeSymbolWithAnnotations.Create(array, isNullableIfReferenceType: true);
type = TypeSymbolWithAnnotations.Create(array, isNullableIfReferenceType: true, fromDeclaration: true);
reportNullableReferenceTypesIfNeeded(a.QuestionToken);
}
else
Expand Down
4 changes: 1 addition & 3 deletions src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -807,9 +807,7 @@ private EnumeratorResult GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder bui
builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, errorLocationSyntax);
builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, errorLocationSyntax);
builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax);
builder.ElementType = TypeSymbolWithAnnotations.Create(
GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax),
isNullableIfReferenceType: builder.CurrentPropertyGetter?.ReturnType.IsNullable);
builder.ElementType = builder.CurrentPropertyGetter?.ReturnType ?? TypeSymbolWithAnnotations.Create(GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax));

Debug.Assert((object)builder.GetEnumeratorMethod == null ||
builder.GetEnumeratorMethod.ReturnType.SpecialType == SpecialType.System_Collections_IEnumerator);
Expand Down
61 changes: 45 additions & 16 deletions src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,65 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal static class BestTypeInferrer
{
public static bool? GetIsNullable(ArrayBuilder<TypeSymbolWithAnnotations> types)
public static NullableAnnotation GetNullableAnnotation(TypeSymbol bestType, ArrayBuilder<TypeSymbolWithAnnotations> types)
{
bool? isNullable = false;
bool bestTypeIsPossiblyNullableReferenceTypeTypeParameter = bestType.IsPossiblyNullableReferenceTypeTypeParameter();
NullableAnnotation? result = null;
foreach (var type in types)
{
if (type.IsNull)
{
// https://github.com/dotnet/roslyn/issues/27961 Should ignore untyped
// expressions such as unbound lambdas and typeless tuples.
isNullable = true;
result = NullableAnnotation.NullableBasedOnAnalysis;
continue;
}
if (!type.IsReferenceType)

if (!type.IsReferenceType && !type.TypeSymbol.IsPossiblyNullableReferenceTypeTypeParameter())
{
return null;
return NullableAnnotation.Unknown;
}
switch (type.IsNullable)

NullableAnnotation nullableAnnotation;

if (type.IsPossiblyNullableReferenceTypeTypeParameter() && !bestTypeIsPossiblyNullableReferenceTypeTypeParameter)
{
case null:
if (isNullable == false)
{
isNullable = null;
}
break;
case true:
isNullable = true;
break;
nullableAnnotation = NullableAnnotation.NullableBasedOnAnalysis;
}
else
{
nullableAnnotation = type.NullableAnnotation;
}

if (nullableAnnotation == NullableAnnotation.Unknown)
{
if (result?.IsAnyNotNullable() != false)
{
result = NullableAnnotation.Unknown;
}
}
else if (nullableAnnotation.IsAnyNullable())
{
if (result?.IsAnyNullable() != true)
{
result = nullableAnnotation;
}
else if (result != nullableAnnotation)
{
result = NullableAnnotation.Nullable;
}
}
else if (result == null)
{
result = nullableAnnotation;
}
else if (result.GetValueOrDefault() == NullableAnnotation.NotNullableBasedOnAnalysis && nullableAnnotation == NullableAnnotation.NotNullable)
{
result = NullableAnnotation.NotNullable;
}
}
return isNullable;

return result ?? NullableAnnotation.NotNullable;
}

/// <remarks>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,8 @@ private static bool HasIdentityConversionInternal(TypeSymbol type1, TypeSymbol t
Debug.Assert((object)type1 != null);
Debug.Assert((object)type2 != null);

// Note, when we are paying attention to nullability, we ignore insignificant differences and oblivious mismatch.
// See TypeCompareKind.UnknownNullableModifierMatchesAny and TypeCompareKind.IgnoreInsignificantNullableModifiersDifference
var compareKind = includeNullability ?
TypeCompareKind.AllIgnoreOptions & ~TypeCompareKind.IgnoreNullableModifiersForReferenceTypes :
TypeCompareKind.AllIgnoreOptions;
Expand All @@ -1389,22 +1391,63 @@ private bool HasIdentityConversionInternal(TypeSymbol type1, TypeSymbol type2)
return HasIdentityConversionInternal(type1, type2, IncludeNullability);
}

/// <summary>
/// Returns true if:
/// - Either type has no nullability information (oblivious).
/// - Both types cannot have different nullability at the same time,
/// including the case of type parameters that by themselves can represent nullable and not nullable reference types.
/// </summary>
internal bool HasTopLevelNullabilityIdentityConversion(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations destination)
{
if (!IncludeNullability)
{
return true;
}
return source.IsNullable == destination.IsNullable;

if (source.NullableAnnotation == NullableAnnotation.Unknown ||
destination.NullableAnnotation == NullableAnnotation.Unknown)
{
return true;
}

if (source.IsPossiblyNullableReferenceTypeTypeParameter() && !destination.IsPossiblyNullableReferenceTypeTypeParameter())
{
return destination.NullableAnnotation.IsAnyNullable();
}

if (destination.IsPossiblyNullableReferenceTypeTypeParameter() && !source.IsPossiblyNullableReferenceTypeTypeParameter())
{
return source.NullableAnnotation.IsAnyNullable();
}

return source.NullableAnnotation.IsAnyNullable() == destination.NullableAnnotation.IsAnyNullable();
}

/// <summary>
/// Returns false if source type can be nullable at the same time when destination type can be not nullable,
/// including the case of type parameters that by themselves can represent nullable and not nullable reference types.
/// When either type has no nullability information (oblivious), this method returns true.
/// </summary>
internal bool HasTopLevelNullabilityImplicitConversion(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations destination)
{
if (!IncludeNullability)
{
return true;
}
return source.IsNullable != true || destination.IsNullable != false;

if (source.NullableAnnotation == NullableAnnotation.Unknown ||
destination.NullableAnnotation == NullableAnnotation.Unknown ||
destination.NullableAnnotation.IsAnyNullable())
{
return true;
}

if (source.IsPossiblyNullableReferenceTypeTypeParameter() && !destination.IsPossiblyNullableReferenceTypeTypeParameter())
{
return false;
}

return !source.NullableAnnotation.IsAnyNullable();
}

public static bool HasIdentityConversionToAny<T>(T type, ArrayBuilder<T> targetTypes)
Expand Down Expand Up @@ -2171,7 +2214,7 @@ private bool HasImplicitReferenceConversion(TypeSymbolWithAnnotations source, Ty
}
// Check for identity conversion of underlying types if the top-level nullability is distinct.
// (An identity conversion where nullability matches is not considered an implicit reference conversion.)
if (source.IsNullable != destination.IsNullable &&
if (source.NullableAnnotation != destination.NullableAnnotation &&
HasIdentityConversionInternal(source.TypeSymbol, destination.TypeSymbol, includeNullability: true))
{
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ private enum Dependency
private readonly ImmutableArray<TypeSymbolWithAnnotations> _formalParameterTypes;
private readonly ImmutableArray<RefKind> _formalParameterRefKinds;
private readonly ImmutableArray<BoundExpression> _arguments;
private readonly Func<BoundExpression, bool?> _getIsNullableOpt;
private readonly Func<BoundExpression, NullableAnnotation> _getNullableAnnotationOpt;

private readonly TypeSymbolWithAnnotations[] _fixedResults;
private readonly HashSet<TypeSymbolWithAnnotations>[] _exactBounds;
Expand Down Expand Up @@ -216,7 +216,7 @@ public static MethodTypeInferenceResult Infer(
// no arguments per se we cons up some fake arguments.
out bool hadNullabilityMismatch,
ref HashSet<DiagnosticInfo> useSiteDiagnostics,
Func<BoundExpression, bool?> getIsNullableOpt = null)
Func<BoundExpression, NullableAnnotation> getNullableAnnotationOpt = null)
{
Debug.Assert(!methodTypeParameters.IsDefault);
Debug.Assert(methodTypeParameters.Length > 0);
Expand All @@ -242,7 +242,7 @@ public static MethodTypeInferenceResult Infer(
formalParameterTypes,
formalParameterRefKinds,
arguments,
getIsNullableOpt);
getNullableAnnotationOpt);
return inferrer.InferTypeArgs(binder, out hadNullabilityMismatch, ref useSiteDiagnostics);
}

Expand All @@ -262,15 +262,15 @@ private MethodTypeInferrer(
ImmutableArray<TypeSymbolWithAnnotations> formalParameterTypes,
ImmutableArray<RefKind> formalParameterRefKinds,
ImmutableArray<BoundExpression> arguments,
Func<BoundExpression, bool?> getIsNullableOpt)
Func<BoundExpression, NullableAnnotation> getNullableAnnotationOpt)
{
_conversions = conversions;
_methodTypeParameters = methodTypeParameters;
_constructedContainingTypeOfMethod = constructedContainingTypeOfMethod;
_formalParameterTypes = formalParameterTypes;
_formalParameterRefKinds = formalParameterRefKinds;
_arguments = arguments;
_getIsNullableOpt = getIsNullableOpt;
_getNullableAnnotationOpt = getNullableAnnotationOpt;
_fixedResults = new TypeSymbolWithAnnotations[methodTypeParameters.Length];
_exactBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
_upperBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
Expand Down Expand Up @@ -553,8 +553,8 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression
// Either the argument is not a tuple literal, or we were unable to do the inference from its elements, let's try to infer from argument type
if (IsReallyAType(source))
{
var isNullable = IsNullable(argument);
ExactOrBoundsInference(kind, TypeSymbolWithAnnotations.Create(source, isNullable), target, ref useSiteDiagnostics);
var annotation = GetNullableAnnotation(argument);
ExactOrBoundsInference(kind, TypeSymbolWithAnnotations.Create(source, annotation), target, ref useSiteDiagnostics);
}
}
}
Expand Down Expand Up @@ -1197,7 +1197,7 @@ private void OutputTypeInference(Binder binder, BoundExpression expression, Type
}
// SPEC: * Otherwise, if E is an expression with type U then a lower-bound
// SPEC: inference is made from U to T.
var sourceType = TypeSymbolWithAnnotations.Create(expression.Type, IsNullable(expression));
var sourceType = TypeSymbolWithAnnotations.Create(expression.Type, GetNullableAnnotation(expression));
if (!sourceType.IsNull)
{
LowerBoundInference(sourceType, target, ref useSiteDiagnostics);
Expand Down Expand Up @@ -1286,7 +1286,7 @@ private bool MethodGroupReturnTypeInference(Binder binder, BoundExpression sourc
return false;
}

bool? returnIsNullable = null; // https://github.com/dotnet/roslyn/issues/27961 Review this
NullableAnnotation returnIsNullable = NullableAnnotation.Unknown; // https://github.com/dotnet/roslyn/issues/27961 Review this
LowerBoundInference(TypeSymbolWithAnnotations.Create(returnType, returnIsNullable), delegateReturnType, ref useSiteDiagnostics);

return true;
Expand Down Expand Up @@ -2458,13 +2458,13 @@ private static TypeSymbolWithAnnotations Fix(
return best;
}

private bool? IsNullable(BoundExpression expr)
private NullableAnnotation GetNullableAnnotation(BoundExpression expr)
{
if (!_conversions.IncludeNullability)
{
return null;
return NullableAnnotation.Unknown;
}
return _getIsNullableOpt?.Invoke(expr);
return _getNullableAnnotationOpt?.Invoke(expr) ?? NullableAnnotation.Unknown;
}

internal static TypeSymbolWithAnnotations Merge(TypeSymbolWithAnnotations first, TypeSymbolWithAnnotations second, VarianceKind variance, ConversionsBase conversions, out bool hadNullabilityMismatch)
Expand Down Expand Up @@ -2727,7 +2727,7 @@ public static ImmutableArray<TypeSymbolWithAnnotations> InferTypeArgumentsFromFi
constructedFromMethod.GetParameterTypes(),
constructedFromMethod.ParameterRefKinds,
arguments,
getIsNullableOpt: null);
getNullableAnnotationOpt: null);

if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics))
{
Expand All @@ -2753,8 +2753,8 @@ private bool InferTypeArgumentsFromFirstArgument(ref HashSet<DiagnosticInfo> use
{
return false;
}
var isNullable = IsNullable(argument);
LowerBoundInference(TypeSymbolWithAnnotations.Create(source, isNullable), dest, ref useSiteDiagnostics);
var annotation = GetNullableAnnotation(argument);
LowerBoundInference(TypeSymbolWithAnnotations.Create(source, annotation), dest, ref useSiteDiagnostics);
// Now check to see that every type parameter used by the first
// formal parameter type was successfully inferred.
for (int iParam = 0; iParam < _methodTypeParameters.Length; ++iParam)
Expand Down
Loading

0 comments on commit e63f327

Please sign in to comment.