Permalink
Browse files

No nullability warnings in best type and type inference (#32947)

  • Loading branch information...
jcouv committed Feb 1, 2019
1 parent e654d5b commit c8e0e7f29853eb5775da2187991e68fa5077f7af
Showing with 524 additions and 1,140 deletions.
  1. +3 −7 docs/features/nullable-reference-types.md
  2. +2 −2 src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
  3. +1 −1 src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs
  4. +5 −26 src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs
  5. +34 −50 src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
  6. +0 −1 src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs
  7. +1 −1 src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs
  8. +16 −0 src/Compilers/CSharp/Portable/BoundTree/BoundExpressionWithNullability.cs
  9. +1 −1 src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
  10. +0 −54 src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
  11. +0 −18 src/Compilers/CSharp/Portable/CSharpResources.resx
  12. +3 −3 src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
  13. +0 −6 src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
  14. +20 −36 src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
  15. +0 −3 src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs
  16. +2 −2 src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs
  17. +1 −2 src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs
  18. +4 −9 src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs
  19. +2 −2 src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs
  20. +9 −15 src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs
  21. +1 −2 src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs
  22. +1 −3 src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs
  23. +9 −22 src/Compilers/CSharp/Portable/Symbols/TypeSymbolWithAnnotations.cs
  24. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
  25. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
  26. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
  27. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
  28. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
  29. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
  30. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
  31. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
  32. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
  33. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
  34. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
  35. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
  36. +0 −30 src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
  37. +407 −479 src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
  38. +0 −3 src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs
  39. +2 −2 src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs
@@ -192,7 +192,7 @@ Merging equivalent but not identical types is done as follows:
- Merging `dynamic` and `object` results in the type `dynamic`.
- Merging tuple types that differ in element names is specified elsewhere.
- Merging equivalent types that differ in nullability is performed as follows: merging the types `Tn` and `Um` (where `n` and `m` are differing nullability annotations) results in the type `Vk` where `V` is the result of merging `T` and `U` using the invariant rule, and `k` is as follows:
- if either `n` or `m` are non-nullable, non-nullable. In this case, if the other is nullable, a warning should be produced.
- if either `n` or `m` are non-nullable, non-nullable.
- if either `n` or `m` are nullable, nullable.
- otherwise oblivious.
- Merging constructed generic types is performed as follows: Merging the types `K<A1, A2, ...>` and `K<B1, B2, ...>` results in the type `K<C1, C2, ...>` where `Ci` is the result of merging `Ai` and `Bi` by the invariant rule.
@@ -236,16 +236,12 @@ It is intended that these merging rules are associative and commutative, so that
> ***Open issue***: these rules do not describe the handling of merging pointer types.
### Array creation
The calculation of the _best type_ element nullability uses the Conversions rules above.
The top-level and nested nullability are calculated independently.
The top-level nullability is the most relaxed of the elements, where `!` is a `~` is a `?`.
The nested nullability is the merged nullability of the best common type. If there is a merge conflict,
the nested nullability is `~` and a warning is reported.
The calculation of the _best type_ element nullability uses the Conversions rules above and the covariant merging rules.
```c#
var w = new [] { notNull, oblivious }; // ~[]!
var x = new [] { notNull, maybeNull, oblivious }; // ?[]!
var y = new [] { enumerableOfNotNull, enumerableOfMaybeNull, enumerableOfOblivious }; // IEnumerable<?>!
var z = new [] { listOfNotNull, listOfMaybeNull, listOfOblivious }; // List<~>!, warning
var z = new [] { listOfNotNull, listOfMaybeNull, listOfOblivious }; // List<~>!
```

### Null-coalescing operator
@@ -2868,7 +2868,7 @@ private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreatio
ImmutableArray<BoundExpression> boundInitializerExpressions = BindArrayInitializerExpressions(initializer, diagnostics, dimension: 1, rank: rank);

HashSet<DiagnosticInfo> useSiteDiagnostics = null;
TypeSymbol bestType = BestTypeInferrer.InferBestType(boundInitializerExpressions, this.Conversions, out _, ref useSiteDiagnostics);
TypeSymbol bestType = BestTypeInferrer.InferBestType(boundInitializerExpressions, this.Conversions, ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);

if ((object)bestType == null || bestType.SpecialType == SpecialType.System_Void) // Dev10 also reports ERR_ImplicitlyTypedArrayNoBestType for void.
@@ -2895,7 +2895,7 @@ private BoundExpression BindImplicitStackAllocArrayCreationExpression(ImplicitSt
ImmutableArray<BoundExpression> boundInitializerExpressions = BindArrayInitializerExpressions(initializer, diagnostics, dimension: 1, rank: 1);

HashSet<DiagnosticInfo> useSiteDiagnostics = null;
TypeSymbol bestType = BestTypeInferrer.InferBestType(boundInitializerExpressions, this.Conversions, out _, ref useSiteDiagnostics);
TypeSymbol bestType = BestTypeInferrer.InferBestType(boundInitializerExpressions, this.Conversions, ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);

if ((object)bestType == null || bestType.SpecialType == SpecialType.System_Void)
@@ -3717,7 +3717,7 @@ private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node
{
bool hadMultipleCandidates;
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
TypeSymbol bestType = BestTypeInferrer.InferBestTypeForConditionalOperator(trueExpr, falseExpr, this.Conversions, out hadMultipleCandidates, out _, ref useSiteDiagnostics);
TypeSymbol bestType = BestTypeInferrer.InferBestTypeForConditionalOperator(trueExpr, falseExpr, this.Conversions, out hadMultipleCandidates, ref useSiteDiagnostics);
diagnostics.Add(node, useSiteDiagnostics);

if ((object)bestType == null)
@@ -31,7 +31,6 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
public static TypeSymbol InferBestType(
ImmutableArray<BoundExpression> exprs,
ConversionsBase conversions,
out bool hadNullabilityMismatch,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// SPEC: 7.5.2.14 Finding the best common type of a set of expressions
@@ -55,7 +54,6 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
{
if (type.IsErrorType())
{
hadNullabilityMismatch = false;
return type;
}

@@ -70,7 +68,7 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
// Perform best type inference on candidate types.
var builder = ArrayBuilder<TypeSymbol>.GetInstance(candidateTypes.Count);
builder.AddRange(candidateTypes);
var result = GetBestType(builder, conversions, out hadNullabilityMismatch, ref useSiteDiagnostics);
var result = GetBestType(builder, conversions, ref useSiteDiagnostics);
builder.Free();
return result;
}
@@ -84,7 +82,6 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
BoundExpression expr2,
ConversionsBase conversions,
out bool hadMultipleCandidates,
out bool hadNullabilityMismatch,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// SPEC: The second and third operands, x and y, of the ?: operator control the type of the conditional expression.
@@ -107,7 +104,6 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
if (type1.IsErrorType())
{
hadMultipleCandidates = false;
hadNullabilityMismatch = false;
return type1;
}

@@ -124,7 +120,6 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
if (type2.IsErrorType())
{
hadMultipleCandidates = false;
hadNullabilityMismatch = false;
return type2;
}

@@ -136,7 +131,7 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi

hadMultipleCandidates = candidateTypes.Count > 1;

return GetBestType(candidateTypes, conversions, out hadNullabilityMismatch, ref useSiteDiagnostics);
return GetBestType(candidateTypes, conversions, ref useSiteDiagnostics);
}
finally
{
@@ -147,7 +142,6 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
internal static TypeSymbol GetBestType(
ArrayBuilder<TypeSymbol> types,
ConversionsBase conversions,
out bool hadNullabilityMismatch,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
// This code assumes that the types in the list are unique.
@@ -157,7 +151,6 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
// might be intransitive?

// Short-circuit some common cases.
hadNullabilityMismatch = false;
switch (types.Count)
{
case 0:
@@ -178,29 +171,22 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
}
else
{
var better = Better(best, type, conversions, out bool hadMismatch, ref useSiteDiagnostics);
var better = Better(best, type, conversions, ref useSiteDiagnostics);

if ((object)better == null)
{
best = null;
hadNullabilityMismatch = false;
}
else
{
if (!better.Equals(best, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
{
hadNullabilityMismatch = false;
}
best = better;
hadNullabilityMismatch |= hadMismatch;
bestIndex = i;
}
}
}

if ((object)best == null)
{
hadNullabilityMismatch = false;
return null;
}

@@ -209,16 +195,13 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
for (int i = 0; i < bestIndex; i++)
{
TypeSymbol type = types[i];
TypeSymbol better = Better(best, type, conversions, out bool hadMismatch, ref useSiteDiagnostics);
TypeSymbol better = Better(best, type, conversions, ref useSiteDiagnostics);
if (!best.Equals(better, TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
{
hadNullabilityMismatch = false;
return null;
}
hadNullabilityMismatch |= hadMismatch;
}

Debug.Assert(!hadNullabilityMismatch || conversions.IncludeNullability);
return best;
}

@@ -229,11 +212,8 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
TypeSymbol type1,
TypeSymbol type2,
ConversionsBase conversions,
out bool hadNullabilityMismatch,
ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
hadNullabilityMismatch = false;

// Anything is better than an error sym.
if (type1.IsErrorType())
{
@@ -267,8 +247,7 @@ public static NullableAnnotation GetNullableAnnotation(ArrayBuilder<TypeSymbolWi
TypeSymbolWithAnnotations.Create(type1),
TypeSymbolWithAnnotations.Create(type2),
VarianceKind.Out,
conversions,
out hadNullabilityMismatch).TypeSymbol;
conversions).TypeSymbol;
}

return null;
Oops, something went wrong.

0 comments on commit c8e0e7f

Please sign in to comment.