Skip to content

Commit

Permalink
Use speakable annotations in method type re-inference (#31813)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Dec 31, 2018
1 parent e8f03e4 commit 0a81853
Show file tree
Hide file tree
Showing 24 changed files with 1,850 additions and 467 deletions.
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 @@ -358,7 +358,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS
var conversions = this.Conversions.WithNullability(includeNullability: true);
type.CheckConstraints(this.Compilation, conversions, location, diagnostics);
}
else if (constructedType.TypeSymbol.IsUnconstrainedTypeParameter())
else if (constructedType.TypeSymbol.IsTypeParameterDisallowingAnnotation())
{
diagnostics.Add(ErrorCode.ERR_NullableUnconstrainedTypeParameter, syntax.Location);
}
Expand Down
65 changes: 11 additions & 54 deletions src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,65 +10,18 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal static class BestTypeInferrer
{
public static NullableAnnotation GetNullableAnnotation(TypeSymbol bestType, ArrayBuilder<TypeSymbolWithAnnotations> types)
public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWithAnnotations> types)
{
bool bestTypeIsPossiblyNullableReferenceTypeTypeParameter = bestType.IsPossiblyNullableReferenceTypeTypeParameter();
NullableAnnotation? result = null;
NullableAnnotation result = NullableAnnotation.NotAnnotated;
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.
result = NullableAnnotation.Nullable;
continue;
}

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

NullableAnnotation nullableAnnotation;

if (type.IsPossiblyNullableReferenceTypeTypeParameter() && !bestTypeIsPossiblyNullableReferenceTypeTypeParameter)
{
nullableAnnotation = NullableAnnotation.Nullable;
}
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.Annotated;
}
}
else if (result == null)
{
result = nullableAnnotation;
}
else if (result.GetValueOrDefault() == NullableAnnotation.NotNullable && nullableAnnotation == NullableAnnotation.NotAnnotated)
{
result = NullableAnnotation.NotAnnotated;
}
Debug.Assert(!type.IsNull);
Debug.Assert(type.Equals(types[0], TypeCompareKind.AllIgnoreOptions));
// This uses the covariant merging rules.
result = result.JoinForFixingLowerBounds(type.AsSpeakable().NullableAnnotation);
}

return result ?? NullableAnnotation.NotAnnotated;
return result;
}

/// <remarks>
Expand Down Expand Up @@ -106,6 +59,10 @@ public static TypeSymbol InferBestType(
return type;
}

if (conversions.IncludeNullability)
{
type = type.SetSpeakableNullabilityForReferenceTypes();
}
candidateTypes.Add(type);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1915,7 +1915,8 @@ private Conversion GetImplicitTupleLiteralConversion(BoundTupleLiteral source, T
destination,
ref useSiteDiagnostics,
ConversionKind.ImplicitTupleLiteral,
(ConversionsBase conversions, BoundExpression s, TypeSymbolWithAnnotations d, ref HashSet<DiagnosticInfo> u, bool a) => conversions.ClassifyImplicitConversionFromExpression(s, d.TypeSymbol, ref u),
(ConversionsBase conversions, BoundExpression s, TypeSymbolWithAnnotations d, ref HashSet<DiagnosticInfo> u, bool a)
=> conversions.ClassifyImplicitConversionFromExpression(s, d.TypeSymbol, ref u),
arg: false);
}

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, NullableAnnotation> _getNullableAnnotationOpt;
private readonly Func<BoundExpression, TypeSymbolWithAnnotations> _getTypeWithAnnotationOpt;

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, NullableAnnotation> getNullableAnnotationOpt = null)
Func<BoundExpression, TypeSymbolWithAnnotations> getTypeWithAnnotationOpt = null)
{
Debug.Assert(!methodTypeParameters.IsDefault);
Debug.Assert(methodTypeParameters.Length > 0);
Expand All @@ -242,7 +242,7 @@ public static MethodTypeInferenceResult Infer(
formalParameterTypes,
formalParameterRefKinds,
arguments,
getNullableAnnotationOpt);
getTypeWithAnnotationOpt);
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, NullableAnnotation> getNullableAnnotationOpt)
Func<BoundExpression, TypeSymbolWithAnnotations> getTypeWithAnnotationOpt)
{
_conversions = conversions;
_methodTypeParameters = methodTypeParameters;
_constructedContainingTypeOfMethod = constructedContainingTypeOfMethod;
_formalParameterTypes = formalParameterTypes;
_formalParameterRefKinds = formalParameterRefKinds;
_arguments = arguments;
_getNullableAnnotationOpt = getNullableAnnotationOpt;
_getTypeWithAnnotationOpt = getTypeWithAnnotationOpt;
_fixedResults = new TypeSymbolWithAnnotations[methodTypeParameters.Length];
_exactBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
_upperBounds = new HashSet<TypeSymbolWithAnnotations>[methodTypeParameters.Length];
Expand Down Expand Up @@ -444,6 +444,7 @@ private bool AllFixed()
private void AddBound(TypeSymbolWithAnnotations addedBound, HashSet<TypeSymbolWithAnnotations>[] collectedBounds, TypeSymbolWithAnnotations methodTypeParameterWithAnnotations)
{
Debug.Assert(IsUnfixedTypeParameter(methodTypeParameterWithAnnotations));
Debug.Assert(addedBound.NullableAnnotation.IsSpeakable());

var methodTypeParameter = (TypeParameterSymbol)methodTypeParameterWithAnnotations.TypeSymbol;
int methodTypeParameterIndex = methodTypeParameter.Ordinal;
Expand Down Expand Up @@ -541,8 +542,6 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression

// SPEC: * Otherwise, no inference is made for this argument

var source = argument.Type;

if (argument.Kind == BoundKind.UnboundLambda)
{
ExplicitParameterTypeInference(argument, target, ref useSiteDiagnostics);
Expand All @@ -551,10 +550,9 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression
!MakeExplicitParameterTypeInferences(binder, (BoundTupleLiteral)argument, target, kind, ref useSiteDiagnostics))
{
// 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))
if (IsReallyAType(argument.Type))
{
var annotation = GetNullableAnnotation(argument);
ExactOrBoundsInference(kind, TypeSymbolWithAnnotations.Create(source, annotation), target, ref useSiteDiagnostics);
ExactOrBoundsInference(kind, GetTypeWithAnnotations(argument), target, ref useSiteDiagnostics);
}
}
}
Expand Down Expand Up @@ -1197,7 +1195,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, GetNullableAnnotation(expression));
var sourceType = GetTypeWithAnnotations(expression);
if (!sourceType.IsNull)
{
LowerBoundInference(sourceType, target, ref useSiteDiagnostics);
Expand Down Expand Up @@ -1505,7 +1503,7 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi
return true;
}

if (isNullableOnly(source) && isNullableOnly(target))
if (s_isNullableOnly(source) && s_isNullableOnly(target))
{
ExactOrBoundsInference(kind, source.AsNotNullableReferenceType(), target.AsNotNullableReferenceType(), ref useSiteDiagnostics);
return true;
Expand All @@ -1514,7 +1512,8 @@ private bool ExactOrBoundsNullableInference(ExactOrBoundsKind kind, TypeSymbolWi
return false;

// True if the type is nullable but not an unconstrained type parameter.
bool isNullableOnly(TypeSymbolWithAnnotations type) => type.NullableAnnotation.IsAnyNullable() && !type.TypeSymbol.IsUnconstrainedTypeParameter();
bool s_isNullableOnly (TypeSymbolWithAnnotations type)
=> type.NullableAnnotation.IsAnyNullable() && !type.TypeSymbol.IsTypeParameterDisallowingAnnotation();
}

private bool ExactNullableInference(TypeSymbolWithAnnotations source, TypeSymbolWithAnnotations target, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
Expand Down Expand Up @@ -2443,13 +2442,13 @@ private static TypeSymbolWithAnnotations Fix(
return best;
}

private NullableAnnotation GetNullableAnnotation(BoundExpression expr)
private TypeSymbolWithAnnotations GetTypeWithAnnotations(BoundExpression expr)
{
if (!_conversions.IncludeNullability)
if (_conversions.IncludeNullability && _getTypeWithAnnotationOpt != null)
{
return NullableAnnotation.Unknown;
return _getTypeWithAnnotationOpt(expr);
}
return _getNullableAnnotationOpt?.Invoke(expr) ?? NullableAnnotation.Unknown;
return TypeSymbolWithAnnotations.Create(expr.Type);
}

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

if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics))
{
Expand All @@ -2738,8 +2737,7 @@ private bool InferTypeArgumentsFromFirstArgument(ref HashSet<DiagnosticInfo> use
{
return false;
}
var annotation = GetNullableAnnotation(argument);
LowerBoundInference(TypeSymbolWithAnnotations.Create(source, annotation), dest, ref useSiteDiagnostics);
LowerBoundInference(GetTypeWithAnnotations(argument), 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 0a81853

Please sign in to comment.