From 14001cca583103c8a5f3edda0f4e4067f32ecb0f Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 14 Jun 2018 22:18:49 -0700 Subject: [PATCH 1/6] Include nullability in CheckConstraints --- .../CSharp/Portable/Binder/Binder_Symbols.cs | 3 +- .../Semantics/Conversions/Conversions.cs | 2 +- .../Semantics/Conversions/ConversionsBase.cs | 2 + .../Semantics/Conversions/TypeConversions.cs | 5 + .../OverloadResolution/OverloadResolution.cs | 1 + .../Portable/BoundTree/UnboundLambda.cs | 2 +- .../Portable/CSharpResources.Designer.cs | 18 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 3 + .../CSharp/Portable/Errors/ErrorFacts.cs | 1 + .../Portable/FlowAnalysis/NullableWalker.cs | 35 +- .../Generated/ErrorFacts.Generated.cs | 1 + .../Portable/Symbols/ConstraintsHelper.cs | 30 +- .../Symbols/MethodSymbolExtensions.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Attributes/AttributeTests_Nullable.cs | 78 ++-- .../Semantics/NullableReferenceTypesTests.cs | 363 +++++++++++++++++- .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 1 + 30 files changed, 623 insertions(+), 60 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index bb01d8b95e27a..173d67f1e58a1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1166,7 +1166,8 @@ private NamedTypeSymbol ConstructNamedType( if (ShouldCheckConstraints) { - type.CheckConstraintsForNonTuple(this.Conversions, typeSyntax, typeArgumentsSyntax, this.Compilation, basesBeingResolved, diagnostics); + bool includeNullability = Compilation.IsFeatureEnabled(MessageID.IDS_FeatureStaticNullChecking); + type.CheckConstraintsForNonTuple(this.Conversions.WithNullability(includeNullability), typeSyntax, typeArgumentsSyntax, this.Compilation, basesBeingResolved, diagnostics); } type = (NamedTypeSymbol)TupleTypeSymbol.TransformToTupleIfCompatible(type); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 2ed12a6c27ec9..f417b86d58b5b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -30,7 +30,7 @@ protected override ConversionsBase CreateInstance(int currentRecursionDepth) private CSharpCompilation Compilation { get { return _binder.Compilation; } } - internal Conversions WithNullability(bool includeNullability) + internal override ConversionsBase WithNullability(bool includeNullability) { return (IncludeNullability == includeNullability) ? this : new Conversions(_binder, currentRecursionDepth, includeNullability); } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 32017d08dcdfc..0d605b550afa6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -29,6 +29,8 @@ protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, IncludeNullability = includeNullability; } + internal abstract ConversionsBase WithNullability(bool includeNullability); + public abstract Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics); public abstract Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref HashSet useSiteDiagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs index 284e00923be97..4d18fe1e52b19 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs @@ -24,6 +24,11 @@ protected override ConversionsBase CreateInstance(int currentRecursionDepth) return new TypeConversions(this.corLibrary, currentRecursionDepth, IncludeNullability); } + internal override ConversionsBase WithNullability(bool includeNullability) + { + return (IncludeNullability == includeNullability) ? this : new TypeConversions(corLibrary, currentRecursionDepth, includeNullability); + } + public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics) { // Conversions involving method groups require a Binder. diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 33a49a3bbd399..ec4b49f1d6e7c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -386,6 +386,7 @@ private bool FailsConstraintChecks(MethodSymbol method, out ArrayBuilder + /// Looks up a localized string similar to The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'.. + /// + internal static string WRN_NullabilityMismatchInTypeParameterConstraint { + get { + return ResourceManager.GetString("WRN_NullabilityMismatchInTypeParameterConstraint", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type.. + /// + internal static string WRN_NullabilityMismatchInTypeParameterConstraint_Title { + get { + return ResourceManager.GetString("WRN_NullabilityMismatchInTypeParameterConstraint_Title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot convert null literal to non-nullable reference or unconstrained type parameter.. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index bd3e6fe97d253..3210759de43ad 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5363,6 +5363,12 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot use a nullable reference type in object creation. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + Explicit application of 'System.Runtime.CompilerServices.NullableAttribute' is not allowed. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 7dfb8bff81842..da2b6239e124e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1613,6 +1613,9 @@ internal enum ErrorCode WRN_NoBestNullabilityConditionalExpression = 8626, ERR_NullableUnconstrainedTypeParameter = 8627, ERR_AnnotationDisallowedInObjectCreation = 8628, + WRN_MissingNonNullTypesContext = 8629, + ERR_NonNullTypesNotAvailable = 8630, + WRN_NullabilityMismatchInTypeParameterConstraint = 8631, } // Note: you will need to re-generate compiler code after adding warnings (build\scripts\generate-compiler-code.cmd) } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 25924631d1deb..dcf1d570abd3c 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -342,6 +342,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate: case ErrorCode.WRN_NullAsNonNullable: case ErrorCode.WRN_NoBestNullabilityConditionalExpression: + case ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint: case ErrorCode.WRN_Experimental: case ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable: case ErrorCode.WRN_TupleBinopLiteralNameMismatch: diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 97077b9ec886d..9b8704c413a83 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -141,7 +141,7 @@ private NullableWalker( // If so, are we interested in an InMethodBinder specifically? _binder = compilation.GetBinderFactory(node.SyntaxTree).GetBinder(node.Syntax); Debug.Assert(!_binder.Conversions.IncludeNullability); - _conversions = _binder.Conversions.WithNullability(true); + _conversions = (Conversions)_binder.Conversions.WithNullability(true); _useMethodSignatureReturnType = (object)methodSignatureOpt != null && useMethodSignatureReturnType; _useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes; _methodSignatureOpt = methodSignatureOpt; @@ -2044,10 +2044,15 @@ private MethodSymbol VisitArguments( // We do a first pass to work through the arguments without making any assumptions ImmutableArray results = VisitArgumentsEvaluate(arguments, refKindsOpt); - if ((object)method != null && method.IsGenericMethod && HasImplicitTypeArguments(node)) + if ((object)method != null && method.IsGenericMethod) { - method = InferMethodTypeArguments((BoundCall)node, method, GetArgumentsForMethodTypeInference(arguments, results)); - parameters = method.Parameters; + if (HasImplicitTypeArguments(node)) + { + method = InferMethodTypeArguments((BoundCall)node, method, GetArgumentsForMethodTypeInference(arguments, results)); + parameters = method.Parameters; + } + var syntax = node.Syntax; + CheckMethodConstraints((syntax as InvocationExpressionSyntax)?.Expression ?? syntax, method); } // PROTOTYPE(NullableReferenceTypes): Can we handle some error cases? @@ -2486,7 +2491,6 @@ private MethodSymbol InferMethodTypeArguments(BoundCall node, MethodSymbol metho ref useSiteDiagnostics); if (result.Success) { - // PROTOTYPE(NullableReferenceTypes): Check constraints return definition.Construct(result.InferredTypeArguments); } return method; @@ -2529,6 +2533,27 @@ BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, Type } } + private void CheckMethodConstraints(SyntaxNode syntax, MethodSymbol method) + { + var diagnosticsBuilder = ArrayBuilder.GetInstance(); + var warningsBuilder = ArrayBuilder.GetInstance(); + ArrayBuilder useSiteDiagnosticsBuilder = null; + ConstraintsHelper.CheckMethodConstraints( + method, + _conversions, + compilation, + diagnosticsBuilder, + warningsBuilder, + ref useSiteDiagnosticsBuilder); + foreach (var pair in warningsBuilder) + { + Diagnostics.Add(pair.DiagnosticInfo, syntax.Location); + } + useSiteDiagnosticsBuilder?.Free(); + warningsBuilder.Free(); + diagnosticsBuilder.Free(); + } + private void ReplayReadsAndWrites(LocalFunctionSymbol localFunc, SyntaxNode syntax, bool writes) diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index c1e439496aa18..025525992a297 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -202,6 +202,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate: case ErrorCode.WRN_NullAsNonNullable: case ErrorCode.WRN_NoBestNullabilityConditionalExpression: + case ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 2bc415c511030..ed39359c759c0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -702,7 +702,7 @@ public static bool CheckConstraints( var diagnosticsBuilder = ArrayBuilder.GetInstance(); ArrayBuilder useSiteDiagnosticsBuilder = null; - var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); + var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, warningsBuilderOpt: null, ref useSiteDiagnosticsBuilder); if (useSiteDiagnosticsBuilder != null) { @@ -733,7 +733,7 @@ public static bool CheckConstraints( var diagnosticsBuilder = ArrayBuilder.GetInstance(); ArrayBuilder useSiteDiagnosticsBuilder = null; - var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); + var result = CheckMethodConstraints(method, conversions, currentCompilation, diagnosticsBuilder, warningsBuilderOpt: null, ref useSiteDiagnosticsBuilder); if (useSiteDiagnosticsBuilder != null) { @@ -764,6 +764,7 @@ private static bool CheckTypeConstraints( type.TypeArgumentsNoUseSiteDiagnostics, currentCompilation, diagnosticsBuilder, + warningsBuilderOpt: conversions.IncludeNullability ? diagnosticsBuilder : null, ref useSiteDiagnosticsBuilder); } @@ -772,6 +773,7 @@ public static bool CheckMethodConstraints( ConversionsBase conversions, Compilation currentCompilation, ArrayBuilder diagnosticsBuilder, + ArrayBuilder warningsBuilderOpt, ref ArrayBuilder useSiteDiagnosticsBuilder, BitVector skipParameters = default(BitVector)) { @@ -783,6 +785,7 @@ public static bool CheckMethodConstraints( method.TypeArguments, currentCompilation, diagnosticsBuilder, + warningsBuilderOpt, ref useSiteDiagnosticsBuilder, skipParameters); } @@ -797,6 +800,7 @@ public static bool CheckMethodConstraints( /// Containing symbol type arguments. /// Improves error message detail. /// Diagnostics. + /// Nullability warnings. /// Parameters to skip. /// /// If an original form of a type constraint @@ -810,12 +814,14 @@ public static bool CheckConstraints( ImmutableArray typeArguments, Compilation currentCompilation, ArrayBuilder diagnosticsBuilder, + ArrayBuilder warningsBuilderOpt, ref ArrayBuilder useSiteDiagnosticsBuilder, BitVector skipParameters = default(BitVector), HashSet ignoreTypeConstraintsDependentOnTypeParametersOpt = null) { Debug.Assert(typeParameters.Length == typeArguments.Length); Debug.Assert(typeParameters.Length > 0); + Debug.Assert(warningsBuilderOpt == null || conversions.IncludeNullability); int n = typeParameters.Length; bool succeeded = true; @@ -830,7 +836,7 @@ public static bool CheckConstraints( var typeArgument = typeArguments[i]; var typeParameter = typeParameters[i]; - if (!CheckConstraints(containingSymbol, conversions, substitution, typeParameter, typeArgument, currentCompilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder, + if (!CheckConstraints(containingSymbol, conversions, substitution, typeParameter, typeArgument, currentCompilation, diagnosticsBuilder, warningsBuilderOpt, ref useSiteDiagnosticsBuilder, ignoreTypeConstraintsDependentOnTypeParametersOpt)) { succeeded = false; @@ -849,6 +855,7 @@ private static bool CheckConstraints( TypeSymbolWithAnnotations typeArgument, Compilation currentCompilation, ArrayBuilder diagnosticsBuilder, + ArrayBuilder warningsBuilderOpt, ref ArrayBuilder useSiteDiagnosticsBuilder, HashSet ignoreTypeConstraintsDependentOnTypeParametersOpt) { @@ -911,8 +918,17 @@ private static bool CheckConstraints( foreach (var constraintType in constraintTypes) { - if (SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics)) + // PROTOTYPE(NullableReferenceTypes): Conversions should cache version without IncludeNullability. + if (SatisfiesConstraintType(conversions.WithNullability(false), typeArgument, constraintType, ref useSiteDiagnostics)) { + if (warningsBuilderOpt != null) + { + Debug.Assert(conversions.IncludeNullability); + if (!SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics)) + { + warningsBuilderOpt.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, containingSymbol.ConstructedFrom(), constraintType, typeParameter, typeArgument))); + } + } continue; } @@ -1003,7 +1019,8 @@ private static bool SatisfiesConstraintType( // "An identity conversion (6.1.1). // An implicit reference conversion (6.1.6). ..." - if (conversions.HasIdentityOrImplicitReferenceConversion(typeArgument.TypeSymbol, constraintType.TypeSymbol, ref useSiteDiagnostics)) + if ((!conversions.IncludeNullability || ConversionsBase.HasTopLevelNullabilityImplicitConversion(typeArgument, constraintType)) && + conversions.HasIdentityOrImplicitReferenceConversion(typeArgument.TypeSymbol, constraintType.TypeSymbol, ref useSiteDiagnostics)) { return true; } @@ -1126,6 +1143,9 @@ private static bool IsEncompassedBy(ConversionsBase conversions, TypeSymbol a, T Debug.Assert(IsValidEncompassedByArgument(a)); Debug.Assert(IsValidEncompassedByArgument(b)); + // IncludeNullability should not be used when calculating EffectiveBaseType or EffectiveInterfaceSet. + Debug.Assert(!conversions.IncludeNullability); + return conversions.HasIdentityOrImplicitReferenceConversion(a, b, ref useSiteDiagnostics) || conversions.HasBoxingConversion(a, b, ref useSiteDiagnostics); } diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs index 3f36d3a337269..21d3346a8a3e0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbolExtensions.cs @@ -113,7 +113,7 @@ public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol m var diagnosticsBuilder = ArrayBuilder.GetInstance(); var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck); ArrayBuilder useSiteDiagnosticsBuilder = null; - var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder, + var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, warningsBuilderOpt: null, ref useSiteDiagnosticsBuilder, ignoreTypeConstraintsDependentOnTypeParametersOpt: notInferredTypeParameters.Count > 0 ? notInferredTypeParameters : null); diagnosticsBuilder.Free(); notInferredTypeParameters.Free(); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 3ac0747bdbe6f..a229e382812d8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -9035,6 +9035,16 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 1a17b7e5cbcd1..5d84ed2f97ebc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -9035,6 +9035,16 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b8acb0296162c..37601a28a2011 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -9035,6 +9035,16 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 23ec34bf1ab29..0a1abd05b1cf1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -9035,6 +9035,16 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 3b14f22f9e727..9a5d3b79d7426 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -9035,6 +9035,16 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index fa87f1c881592..0f7cf07144832 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -9035,6 +9035,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 50efb64fdca06..4290e0d4df0da 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -9035,6 +9035,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 18c006a0cf224..bc01c86d8275c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -9035,6 +9035,16 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 22d151aebe543..2e3fbd9ab2efd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -9035,6 +9035,16 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index a7b5ae1032ba1..35f549c16f502 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -9035,6 +9035,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 0e821517f39f1..ccd37baf66ffd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -9035,6 +9035,16 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 76f5d42c66c48..99ce071131d31 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -9035,6 +9035,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index f66bb84eae8bf..66e7a5a6dcdca 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -9035,6 +9035,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Converting null literal or possible null value to non-nullable type. + + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + The type '{3}' cannot be used as type parameter '{2}' in the generic type or method '{0}'. Nullability of type argument '{3}' doesn't match constraint type '{1}'. + + + + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match constraint type. + + \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs index 9ae2cbd80da8c..f8424aece4c07 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Nullable.cs @@ -14,6 +14,18 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class AttributeTests_Nullable : CSharpTestBase { + private const string NonNullTypesAttributesDefinition = @" +namespace System.Runtime.CompilerServices +{ + [System.AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public sealed class NonNullTypesAttribute : Attribute + { + public NonNullTypesAttribute(bool flag = true) { } + } +} +"; + private const string NonNullTypesTrue = "[module: System.Runtime.CompilerServices.NonNullTypes(true)]"; + // An empty project should not require System.Attribute. [Fact] public void EmptyProject_MissingAttribute() @@ -312,18 +324,6 @@ public void EmitAttribute_NetModuleNoDeclarations() }); } - private const string NonNullTypesAttributesDefinition = @" -namespace System.Runtime.CompilerServices -{ - [System.AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public sealed class NonNullTypesAttribute : Attribute - { - public NonNullTypesAttribute(bool flag = true) { } - } -} -"; - private const string NonNullTypesTrue = "[module: System.Runtime.CompilerServices.NonNullTypes(true)]"; - [Fact] public void EmitAttribute_BaseClass() { @@ -487,7 +487,7 @@ public class C where T : A? public class D where T : A { }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); CompileAndVerify(comp, validator: assembly => { var reader = assembly.GetMetadataReader(); @@ -511,19 +511,29 @@ static void Main() new C(); new C(); new C(); - new D(); + new D(); // warning new D(); - new D(); + new D(); // warning new D(); } }"; - var comp2 = CreateCompilation(new[] { source, source2 }, parseOptions: TestOptions.Regular8); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new D()` and `new D()`: - comp2.VerifyEmitDiagnostics(); - - comp2 = CreateCompilation(source2, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new D()` and `new D()`: - comp2.VerifyEmitDiagnostics(); + var comp2 = CreateCompilation(new[] { source, source2, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); + comp2.VerifyEmitDiagnostics( + // (10,15): warning CS8627: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("D", "A", "T", "A?").WithLocation(10, 15), + // (12,15): warning CS8627: The type 'B?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'B?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B?").WithArguments("D", "A", "T", "B?").WithLocation(12, 15)); + + comp2 = CreateCompilation(new[] { source2, NonNullTypesTrue }, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); + comp2.VerifyEmitDiagnostics( + // (10,15): warning CS8627: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("D", "A", "T", "A?").WithLocation(10, 15), + // (12,15): warning CS8627: The type 'B?' cannot be used as type parameter 'T' in the generic type or method 'D'. Nullability of type argument 'B?' doesn't match constraint type 'A'. + // new D(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B?").WithArguments("D", "A", "T", "B?").WithLocation(12, 15)); var type = comp2.GetMember("C"); Assert.Equal("A?", type.TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); @@ -590,7 +600,7 @@ public class B where T : A public class C where T : A { }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); CompileAndVerify(comp, validator: assembly => { var reader = assembly.GetMetadataReader(); @@ -615,13 +625,23 @@ static void Main() new C>(); } }"; - var comp2 = CreateCompilation(new[] { source, source2 }, parseOptions: TestOptions.Regular8); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new B>()` and `new C>()`. - comp2.VerifyEmitDiagnostics(); + var comp2 = CreateCompilation(new[] { source, source2, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); + comp2.VerifyEmitDiagnostics( + // (6,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new B>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B", "A", "T", "A").WithLocation(6, 15), + // (7,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'C'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new C>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("C", "A", "T", "A").WithLocation(7, 15)); - comp2 = CreateCompilation(source2, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); - // PROTOTYPE(NullableReferenceTypes): Report warning for `new B>()` and `new C>()`. - comp2.VerifyDiagnostics(); + comp2 = CreateCompilation(new[] { source2, NonNullTypesTrue }, parseOptions: TestOptions.Regular8, references: new[] { comp.EmitToImageReference() }); + comp2.VerifyDiagnostics( + // (6,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new B>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B", "A", "T", "A").WithLocation(6, 15), + // (7,15): warning CS8627: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'C'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // new C>(); // warning + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("C", "A", "T", "A").WithLocation(7, 15)); var type = comp2.GetMember("B"); Assert.Equal("A!", type.TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 296a3484a7e63..10533fa8a7724 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -33770,7 +33770,10 @@ static void G(C? x, C y) Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, x)").WithLocation(6, 9), // (7,9): warning CS8602: Possible dereference of a null reference. // F(x, y).ToString(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, y)").WithLocation(7, 9)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, y)").WithLocation(7, 9), + // (8,9): warning CS8631: The type 'C?' cannot be used as type parameter 'U' in the generic type or method 'C.F(T, U)'. Nullability of type argument 'C?' doesn't match constraint type 'C'. + // F(y, x).ToString(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F").WithArguments("C.F(T, U)", "C", "U", "C?").WithLocation(8, 9)); } [Fact] @@ -36734,9 +36737,11 @@ class B4 : A, I { }"; var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); - // PROTOTYPE(NullableReferenceTypes): Should report warnings that `T?` - // does not satisfy `where T : class` constraint or `where U : T` constraint. - comp.VerifyDiagnostics(); + // PROTOTYPE(NullableReferenceTypes): Should report `T?` does not satisfy `where T : class`. + comp.VerifyDiagnostics( + // (15,7): warning CS8627: The type 'T?' cannot be used as type parameter 'U' in the generic type or method 'I'. Nullability of type argument 'T?' doesn't match constraint type 'T'. + // class B2 : A, I + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B2").WithArguments("I", "T", "U", "T?").WithLocation(15, 7)); } // `class C where T : class, T?` from metadata. @@ -37331,7 +37336,10 @@ static void Main() } }"; var comp = CreateCompilation(new[] { source, NonNullTypesTrue }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (8,29): warning CS8631: The type 'A2' cannot be used as type parameter 'U' in the generic type or method 'B2'. Nullability of type argument 'A2' doesn't match constraint type 'A2'. + // new B2, A2>(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A2").WithArguments("B2", "A2", "U", "A2").WithLocation(8, 29)); var typeParameters = comp.GetMember("B1").TypeParameters; Assert.Equal("A1", typeParameters[0].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); Assert.Equal("A1?", typeParameters[1].ConstraintTypesNoUseSiteDiagnostics[0].ToTestDisplayString(true)); @@ -37435,32 +37443,33 @@ public void Constraint_Oblivious_01() } public class A where T : I { - public static void F(T t) { } }"; var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); var ref0 = comp0.EmitToImageReference(); var source = -@"class B1 : I { } +@"using System; +class B1 : I { } class B2 : I { } class C { static void Main() { - A.F(null); // warning - A.F(null); // warning - A.F(null); - A.F(null); + Type t; + t = typeof(A); + t = typeof(A); // 1 + t = typeof(A); // 2 + t = typeof(A); } }"; var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8, references: new[] { ref0 }); comp.VerifyDiagnostics( - // (7,17): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. - // A.F(null); // warning - Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 17), - // (8,17): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. - // A.F(null); // warning - Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 17)); + // (10,22): warning CS8631: The type 'B2' cannot be used as type parameter 'T' in the generic type or method 'A'. Nullability of type argument 'B2' doesn't match constraint type 'I'. + // t = typeof(A); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B2").WithArguments("A", "I", "T", "B2").WithLocation(10, 22), + // (11,22): warning CS8631: The type 'B1?' cannot be used as type parameter 'T' in the generic type or method 'A'. Nullability of type argument 'B1?' doesn't match constraint type 'I'. + // t = typeof(A); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B1?").WithArguments("A", "I", "T", "B1?").WithLocation(11, 22)); var constraintTypes = comp.GetMember("A").TypeParameters[0].ConstraintTypesNoUseSiteDiagnostics; Assert.Equal("I", constraintTypes[0].ToTestDisplayString(true)); @@ -37490,6 +37499,326 @@ static void F(I i) comp.VerifyDiagnostics(); } + [Fact] + public void Constraint_Oblivious_03() + { + var source0 = +@"public class A { } +public class B0 where T : A { }"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"#pragma warning disable 0169 +#pragma warning disable 8618 +using System.Runtime.CompilerServices; +class B1 where T : A? { } +class B2 where T : A { } +[NonNullTypes] class B3 where T : A? { } +[NonNullTypes] class B4 where T : A { } +class C +{ + B0 F1; + B0 F2; + B1 F3; + B1 F4; + B2 F5; + B2 F6; + B3 F7; + B3 F8; + B4 F9; // 1 + B4 F10; +} +[NonNullTypes] +class D +{ + B0 G1; + B0 G2; + B1 G3; + B1 G4; + B2 G5; + B2 G6; + B3 G7; + B3 G8; + B4 G9; // 2 + B4 G10; +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (18,8): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // B4 F9; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("B4", "A", "T", "A?").WithLocation(18, 8), + // (32,8): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // B4 G9; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A?").WithArguments("B4", "A", "T", "A?").WithLocation(32, 8)); + } + + [Fact] + public void Constraint_Oblivious_04() + { + var source0 = +@"public class A { } +public class B0 where T : A { }"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"#pragma warning disable 0169 +#pragma warning disable 8618 +using System.Runtime.CompilerServices; +class B1 where T : A { } +class B2 where T : A { } +[NonNullTypes] class B3 where T : A { } +[NonNullTypes] class B4 where T : A { } +class C +{ + B0> F1; + B0> F2; + B1> F3; + B1> F4; + B2> F5; + B2> F6; + B3> F7; + B3> F8; + B4> F9; // 1 + B4> F10; +} +[NonNullTypes] +class D +{ + B0> G1; + B0> G2; + B1> G3; + B1> G4; // 2 + B2> G5; + B2> G6; + B3> G7; + B3> G8; // 3 + B4> G9; // 4 + B4> G10; +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (18,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B4> F9; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B4", "A", "T", "A").WithLocation(18, 8), + // (27,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B1'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B1> G4; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B1", "A", "T", "A").WithLocation(27, 8), + // (31,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B3'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B3> G8; // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B3", "A", "T", "A").WithLocation(31, 8), + // (32,8): warning CS8631: The type 'A' cannot be used as type parameter 'T' in the generic type or method 'B4'. Nullability of type argument 'A' doesn't match constraint type 'A'. + // B4> G9; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B4", "A", "T", "A").WithLocation(32, 8)); + } + + // Boxing conversion. + [Fact] + public void Constraint_BoxingConversion() + { + var source0 = +@"public interface I { } +public interface IIn { } +public interface IOut { } +public struct S0 : I { } +public struct SIn0 : IIn { } +public struct SOut0 : IOut { } +public class A +{ + public static void F0() where T : I { } + public static void FIn0() where T : IIn { } + public static void FOut0() where T : IOut { } +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"struct S1 : I { } +struct S2 : I { } +struct SIn1 : IIn { } +struct SIn2 : IIn { } +struct SOut1 : IOut { } +struct SOut2 : IOut { } +class B : A +{ + static void F1() where T : I { } + static void F2() where T : I { } + static void FIn1() where T : IIn { } + static void FIn2() where T : IIn { } + static void FOut1() where T : IOut { } + static void FOut2() where T : IOut { } + static void F() + { + F0(); + F0(); + F0(); + F1(); + F1(); + F1(); // 1 + F2(); + F2(); // 2 + F2(); + } + static void FIn() + { + FIn0(); + FIn0(); + FIn0(); + FIn1(); + FIn1(); + FIn1(); // 3 + FIn2(); + FIn2(); + FIn2(); + } + static void FOut() + { + FOut0(); + FOut0(); + FOut0(); + FOut1(); + FOut1(); + FOut1(); + FOut2(); + FOut2(); // 4 + FOut2(); + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (22,9): warning CS8627: The type 'S2' cannot be used as type parameter 'T' in the generic type or method 'B.F1()'. Nullability of type argument 'S2' doesn't match constraint type 'I'. + // F1(); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("B.F1()", "I", "T", "S2").WithLocation(22, 9), + // (24,9): warning CS8627: The type 'S1' cannot be used as type parameter 'T' in the generic type or method 'B.F2()'. Nullability of type argument 'S1' doesn't match constraint type 'I'. + // F2(); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F2").WithArguments("B.F2()", "I", "T", "S1").WithLocation(24, 9), + // (34,9): warning CS8627: The type 'SIn2' cannot be used as type parameter 'T' in the generic type or method 'B.FIn1()'. Nullability of type argument 'SIn2' doesn't match constraint type 'IIn'. + // FIn1(); // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FIn1").WithArguments("B.FIn1()", "IIn", "T", "SIn2").WithLocation(34, 9), + // (48,9): warning CS8627: The type 'SOut1' cannot be used as type parameter 'T' in the generic type or method 'B.FOut2()'. Nullability of type argument 'SOut1' doesn't match constraint type 'IOut'. + // FOut2(); // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FOut2").WithArguments("B.FOut2()", "IOut", "T", "SOut1").WithLocation(48, 9)); + } + + [Fact] + public void Constraint_ImplicitTypeParameterConversion() + { + var source0 = +@"public interface I { } +public interface IIn { } +public interface IOut { } +public class A +{ + public static void F0() where T : I { } + public static void FIn0() where T : IIn { } + public static void FOut0() where T : IOut { } +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"class B : A +{ + static void F1() where T : I { } + static void F2() where T : I { } + static void FIn1() where T : IIn { } + static void FIn2() where T : IIn { } + static void FOut1() where T : IOut { } + static void FOut2() where T : IOut { } + static void F() where T : I where U : I + { + F0(); + F0(); + F1(); + F1(); // 1 + F2(); // 2 + F2(); + } + static void FIn() where T : IIn where U : IIn + { + FIn0(); + FIn0(); + FIn1(); + FIn1(); // 3 + FIn2(); + FIn2(); + } + static void FOut() where T : IOut where U : IOut + { + FOut0(); + FOut0(); + FOut1(); + FOut1(); + FOut2(); // 4 + FOut2(); + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (14,9): warning CS8627: The type 'U' cannot be used as type parameter 'T' in the generic type or method 'B.F1()'. Nullability of type argument 'U' doesn't match constraint type 'I'. + // F1(); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("B.F1()", "I", "T", "U").WithLocation(14, 9), + // (15,9): warning CS8627: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'B.F2()'. Nullability of type argument 'T' doesn't match constraint type 'I'. + // F2(); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F2").WithArguments("B.F2()", "I", "T", "T").WithLocation(15, 9), + // (23,9): warning CS8627: The type 'U' cannot be used as type parameter 'T' in the generic type or method 'B.FIn1()'. Nullability of type argument 'U' doesn't match constraint type 'IIn'. + // FIn1(); // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FIn1").WithArguments("B.FIn1()", "IIn", "T", "U").WithLocation(23, 9), + // (33,9): warning CS8627: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'B.FOut2()'. Nullability of type argument 'T' doesn't match constraint type 'IOut'. + // FOut2(); // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "FOut2").WithArguments("B.FOut2()", "IOut", "T", "T").WithLocation(33, 9)); + } + + [Fact] + public void Constraint_MethodTypeInference() + { + var source0 = +@"public class A { } +public class B +{ + public static void F0(T t) where T : A + { + } +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular7); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"class C : B +{ + static void F1(T t) where T : A + { + } + static void G(A x, A? y) + { + F0(x); + F1(x); + F0(y); + F1(y); // 1 + x = y; + F0(x); + F1(x); // 2 + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (11,9): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'C.F1(T)'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // F1(y); // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("C.F1(T)", "A", "T", "A?").WithLocation(11, 9), + // (12,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // x = y; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "y").WithLocation(12, 13), + // (14,9): warning CS8631: The type 'A?' cannot be used as type parameter 'T' in the generic type or method 'C.F1(T)'. Nullability of type argument 'A?' doesn't match constraint type 'A'. + // F1(x); // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F1").WithArguments("C.F1(T)", "A", "T", "A?").WithLocation(14, 9)); + } + [Fact] public void ThisAndBaseMemberInLambda() { diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 10afffb2588a5..b51c0d743d994 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -279,6 +279,7 @@ public void WarningLevel_2() case ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate: case ErrorCode.WRN_NullAsNonNullable: case ErrorCode.WRN_NoBestNullabilityConditionalExpression: + case ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_InvalidVersionFormat: From f63475ee14677505a0885802c0f25711ba2ebf88 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 31 Jul 2018 09:12:49 -0700 Subject: [PATCH 2/6] Cache Conversions.WithNullability() --- .../Semantics/Conversions/Conversions.cs | 13 +++++----- .../Semantics/Conversions/ConversionsBase.cs | 24 +++++++++++++++++-- .../Semantics/Conversions/TypeConversions.cs | 14 ++++++----- .../Portable/Symbols/ConstraintsHelper.cs | 1 - 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index f417b86d58b5b..69f6ad273779b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -13,26 +13,27 @@ internal sealed class Conversions : ConversionsBase private readonly Binder _binder; public Conversions(Binder binder) - : this(binder, currentRecursionDepth: 0, includeNullability: false) + : this(binder, currentRecursionDepth: 0, includeNullability: false, otherNullabilityOpt: null) { } - private Conversions(Binder binder, int currentRecursionDepth, bool includeNullability) - : base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth, includeNullability) + private Conversions(Binder binder, int currentRecursionDepth, bool includeNullability, Conversions otherNullabilityOpt) + : base(binder.Compilation.Assembly.CorLibrary, currentRecursionDepth, includeNullability, otherNullabilityOpt) { _binder = binder; } protected override ConversionsBase CreateInstance(int currentRecursionDepth) { - return new Conversions(_binder, currentRecursionDepth, IncludeNullability); + return new Conversions(_binder, currentRecursionDepth, IncludeNullability, otherNullabilityOpt: null); } private CSharpCompilation Compilation { get { return _binder.Compilation; } } - internal override ConversionsBase WithNullability(bool includeNullability) + protected override ConversionsBase WithNullabilityCore(bool includeNullability) { - return (IncludeNullability == includeNullability) ? this : new Conversions(_binder, currentRecursionDepth, includeNullability); + Debug.Assert(IncludeNullability != includeNullability); + return new Conversions(_binder, currentRecursionDepth, includeNullability, this); } public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 0d605b550afa6..5d1a831fad7bc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -20,16 +20,36 @@ internal abstract partial class ConversionsBase internal readonly bool IncludeNullability; - protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability) + private ConversionsBase _lazyOtherNullability; + + protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability, ConversionsBase otherNullabilityOpt) { Debug.Assert((object)corLibrary != null); + Debug.Assert(otherNullabilityOpt == null || includeNullability == !otherNullabilityOpt.IncludeNullability); + Debug.Assert(otherNullabilityOpt == null || currentRecursionDepth == otherNullabilityOpt.currentRecursionDepth); this.corLibrary = corLibrary; this.currentRecursionDepth = currentRecursionDepth; IncludeNullability = includeNullability; + _lazyOtherNullability = otherNullabilityOpt; + } + + internal ConversionsBase WithNullability(bool includeNullability) + { + if (IncludeNullability == includeNullability) + { + return this; + } + if (_lazyOtherNullability == null) + { + _lazyOtherNullability = WithNullabilityCore(includeNullability); + } + Debug.Assert(_lazyOtherNullability.IncludeNullability == includeNullability); + Debug.Assert(_lazyOtherNullability._lazyOtherNullability == this); + return _lazyOtherNullability; } - internal abstract ConversionsBase WithNullability(bool includeNullability); + protected abstract ConversionsBase WithNullabilityCore(bool includeNullability); public abstract Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs index 4d18fe1e52b19..c9490059d762c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs @@ -4,29 +4,31 @@ using Roslyn.Utilities; using System; using System.Collections.Generic; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp { internal sealed class TypeConversions : ConversionsBase { public TypeConversions(AssemblySymbol corLibrary) - : this(corLibrary, currentRecursionDepth: 0, includeNullability: false) + : this(corLibrary, currentRecursionDepth: 0, includeNullability: false, otherNullabilityOpt: null) { } - private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability) - : base(corLibrary, currentRecursionDepth, includeNullability) + private TypeConversions(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability, TypeConversions otherNullabilityOpt) + : base(corLibrary, currentRecursionDepth, includeNullability, otherNullabilityOpt) { } protected override ConversionsBase CreateInstance(int currentRecursionDepth) { - return new TypeConversions(this.corLibrary, currentRecursionDepth, IncludeNullability); + return new TypeConversions(this.corLibrary, currentRecursionDepth, IncludeNullability, otherNullabilityOpt: null); } - internal override ConversionsBase WithNullability(bool includeNullability) + protected override ConversionsBase WithNullabilityCore(bool includeNullability) { - return (IncludeNullability == includeNullability) ? this : new TypeConversions(corLibrary, currentRecursionDepth, includeNullability); + Debug.Assert(IncludeNullability != includeNullability); + return new TypeConversions(corLibrary, currentRecursionDepth, includeNullability, this); } public override Conversion GetMethodGroupConversion(BoundMethodGroup source, TypeSymbol destination, ref HashSet useSiteDiagnostics) diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index ed39359c759c0..99fd2edc33067 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -918,7 +918,6 @@ private static bool CheckConstraints( foreach (var constraintType in constraintTypes) { - // PROTOTYPE(NullableReferenceTypes): Conversions should cache version without IncludeNullability. if (SatisfiesConstraintType(conversions.WithNullability(false), typeArgument, constraintType, ref useSiteDiagnostics)) { if (warningsBuilderOpt != null) From 1aed4a89e1ed273a791c3fc9bf647479457d5eef Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 31 Jul 2018 21:44:26 -0700 Subject: [PATCH 3/6] Misc. --- .../CSharp/Portable/FlowAnalysis/NullableWalker.cs | 7 +++++-- .../Semantic/Semantics/NullableReferenceTypesTests.cs | 8 +++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 55615dd714829..4674131087118 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -2051,8 +2051,11 @@ private MethodSymbol VisitArguments( method = InferMethodTypeArguments((BoundCall)node, method, GetArgumentsForMethodTypeInference(arguments, results)); parameters = method.Parameters; } - var syntax = node.Syntax; - CheckMethodConstraints((syntax as InvocationExpressionSyntax)?.Expression ?? syntax, method); + if (!method.IsDefinition) + { + var syntax = node.Syntax; + CheckMethodConstraints((syntax as InvocationExpressionSyntax)?.Expression ?? syntax, method); + } } // PROTOTYPE(NullableReferenceTypes): Can we handle some error cases? diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 9c67dbcd63105..a0c5eaf8cbd52 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -36920,11 +36920,9 @@ class B4 : A, I { }"; var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); - // PROTOTYPE(NullableReferenceTypes): Should report `T?` does not satisfy `where T : class`. - comp.VerifyDiagnostics( - // (15,7): warning CS8627: The type 'T?' cannot be used as type parameter 'U' in the generic type or method 'I'. Nullability of type argument 'T?' doesn't match constraint type 'T'. - // class B2 : A, I - Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "B2").WithArguments("I", "T", "U", "T?").WithLocation(15, 7)); + // PROTOTYPE(NullableReferenceTypes): Should report warnings that `T?` + // does not satisfy `where T : class` constraint or `where U : T` constraint. + comp.VerifyDiagnostics(); } // `class C where T : class, T?` from metadata. From 27ffe07b7c7d2533bc9854d368acbaccc2e9bc8f Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 1 Aug 2018 16:39:57 -0700 Subject: [PATCH 4/6] PR feedback --- .../Semantics/Conversions/ConversionsBase.cs | 2 +- .../Semantics/NullableReferenceTypesTests.cs | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 5d1a831fad7bc..f9cb2f175ecd5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -25,7 +25,7 @@ internal abstract partial class ConversionsBase protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability, ConversionsBase otherNullabilityOpt) { Debug.Assert((object)corLibrary != null); - Debug.Assert(otherNullabilityOpt == null || includeNullability == !otherNullabilityOpt.IncludeNullability); + Debug.Assert(otherNullabilityOpt == null || includeNullability != otherNullabilityOpt.IncludeNullability); Debug.Assert(otherNullabilityOpt == null || currentRecursionDepth == otherNullabilityOpt.currentRecursionDepth); this.corLibrary = corLibrary; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index a0c5eaf8cbd52..ca20c819ccbd1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -16,6 +16,8 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class NullableReferenceTypesTests : CSharpTestBase { + // PROTOTYPE(NullableReferenceTypes): Move attribute definitions to base class. + private const string NullableAttributeDefinition = @" namespace System.Runtime.CompilerServices { @@ -33938,9 +33940,9 @@ public void TypeInference_06() static T F(T t, U u) where U : T => t; static void G(C? x, C y) { - F(x, x).ToString(); - F(x, y).ToString(); - F(y, x).ToString(); + F(x, x).ToString(); // warning: may be null + F(x, y).ToString(); // warning may be null + F(y, x).ToString(); // warning: x does not satisfy U constraint F(y, y).ToString(); } }"; @@ -33949,13 +33951,13 @@ static void G(C? x, C y) parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( // (6,9): warning CS8602: Possible dereference of a null reference. - // F(x, x).ToString(); + // F(x, x).ToString(); // warning: may be null Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, x)").WithLocation(6, 9), // (7,9): warning CS8602: Possible dereference of a null reference. - // F(x, y).ToString(); + // F(x, y).ToString(); // warning may be null Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F(x, y)").WithLocation(7, 9), // (8,9): warning CS8631: The type 'C?' cannot be used as type parameter 'U' in the generic type or method 'C.F(T, U)'. Nullability of type argument 'C?' doesn't match constraint type 'C'. - // F(y, x).ToString(); + // F(y, x).ToString(); // warning: x does not satisfy U constraint Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "F").WithArguments("C.F(T, U)", "C", "U", "C?").WithLocation(8, 9)); } From 990a1a17a12a4b8f6f1a8636749f3540d5bb2baa Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 1 Aug 2018 22:24:25 -0700 Subject: [PATCH 5/6] Enable tests --- .../Semantics/NullableReferenceTypesTests.cs | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index ca20c819ccbd1..ffb6946945090 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -4041,12 +4041,11 @@ void Dummy() } } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): syntax-based detection of NonNullTypes is temporary")] + [Fact] public void Overriding_Methods() { var source = @" using System.Runtime.CompilerServices; -[module: NonNullTypes(true)] public abstract class A { [NonNullTypes(false)] @@ -4083,11 +4082,17 @@ public class B2 : A var compilation = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); compilation.VerifyDiagnostics( + // (7,14): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [return: NonNullTypes(false)] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(7, 14), + // (8,55): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public abstract System.Action Oblivious2([NonNullTypes(false)] System.Action x); + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(8, 55), // (17,44): warning CS8609: Nullability of reference types in return type doesn't match overridden member. - // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 + // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 // PROTOTYPE(NullableReferenceTypes): Should not warn Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "Oblivious2").WithLocation(17, 44), // (17,44): warning CS8610: Nullability of reference types in type of parameter 'x' doesn't match overridden member. - // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 + // public override System.Action Oblivious2(System.Action x) => throw null; // warn 3 and 4 // PROTOTYPE(NullableReferenceTypes): Should not warn Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "Oblivious2").WithArguments("x").WithLocation(17, 44), // (18,44): warning CS8609: Nullability of reference types in return type doesn't match overridden member. // public override System.Action M3(System.Action x) => throw null; // warn 5 and 6 @@ -4106,7 +4111,19 @@ public class B2 : A Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOnOverride, "M5").WithLocation(20, 44), // (20,44): warning CS8610: Nullability of reference types in type of parameter 'x' doesn't match overridden member. // public override System.Action M5(System.Action x) => throw null; // warn 9 and 10 - Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("x").WithLocation(20, 44) + Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOnOverride, "M5").WithArguments("x").WithLocation(20, 44), + // (30,14): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [return: NonNullTypes(false)] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(30, 14), + // (31,47): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override System.Action M4([NonNullTypes(false)] System.Action x) => throw null; + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(31, 47), + // (25,14): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [return: NonNullTypes(false)] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(25, 14), + // (26,55): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override System.Action Oblivious1([NonNullTypes(false)] System.Action x) => throw null; + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(26, 55) ); var b1 = compilation.GetTypeByMetadataName("B1"); @@ -4179,12 +4196,11 @@ public class Class : Base where T : class comp.VerifyDiagnostics(); } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): hits an assertion in AsObliviousReferenceType")] + [Fact] public void Overriding_Properties_WithNullableTypeArgument_WithStructConstraint() { var source = @" using System.Runtime.CompilerServices; -[module: NonNullTypes(true)] public class List { } public class Base where T : struct { @@ -4200,12 +4216,11 @@ public class Class : Base where T : struct comp.VerifyDiagnostics(); } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): hits an assertion in CopyTypeCustomModifiers")] + [Fact] public void Overriding_Indexer() { var source = @" using System.Runtime.CompilerServices; -[module: NonNullTypes(true)] public class List { } public class Base { @@ -4222,7 +4237,13 @@ public class Class2 : Base } "; var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (15,92): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override List this[[NonNullTypes(false)] List x] { [return: NonNullTypes(false)] get => throw null; set => throw null; } + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(15, 92), + // (15,42): error CS0592: Attribute 'NonNullTypes' is not valid on this declaration type. It is only valid on 'module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // public override List this[[NonNullTypes(false)] List x] { [return: NonNullTypes(false)] get => throw null; set => throw null; } + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "NonNullTypes").WithArguments("NonNullTypes", "module, class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(15, 42)); } [Fact] @@ -4585,7 +4606,7 @@ public override string?[]? this[long x] // 3 } } - [Fact(Skip = "PROTOTYPE(NullableReferenceTypes): hits assertion in AsObliviousReferenceType")] + [Fact] public void Overriding_22() { var source = @@ -12047,7 +12068,7 @@ class CL1 ); } - [Fact(Skip = "Unexpected warning")] + [Fact] public void ConditionalBranching_08() { CSharpCompilation c = CreateCompilation(new[] { @" @@ -12074,8 +12095,12 @@ class CL1 public bool P2 { get { return true;} } } ", NonNullTypesTrue, NonNullTypesAttributesDefinition }, parseOptions: TestOptions.Regular8); - + // PROTOTYPE(NullableReferenceTypes): Not tracking state of x?.P == expr + // unless expr is `null`. See https://github.com/dotnet/roslyn/issues/26624. c.VerifyDiagnostics( + // (12,20): warning CS8602: Possible dereference of a null reference. + // return x1.P2; + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(12, 20) ); } From cec0d9f20b4431ffe56aa0dc3778281ebb9592c6 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 2 Aug 2018 22:50:40 -0700 Subject: [PATCH 6/6] PR feedback --- .../Semantics/Conversions/ConversionsBase.cs | 8 +++++ .../Portable/Symbols/ConstraintsHelper.cs | 5 +++- .../Semantics/NullableReferenceTypesTests.cs | 29 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index f9cb2f175ecd5..1b0d270614b8b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -20,6 +20,10 @@ internal abstract partial class ConversionsBase internal readonly bool IncludeNullability; + /// + /// An optional clone of this instance with distinct IncludeNullability. + /// Used to avoid unnecessary allocations when calling WithNullability() repeatedly. + /// private ConversionsBase _lazyOtherNullability; protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, bool includeNullability, ConversionsBase otherNullabilityOpt) @@ -34,6 +38,10 @@ protected ConversionsBase(AssemblySymbol corLibrary, int currentRecursionDepth, _lazyOtherNullability = otherNullabilityOpt; } + /// + /// Returns this instance if includeNullability is correct, and returns a + /// cached clone of this instance with distinct includeNullability otherwise. + /// internal ConversionsBase WithNullability(bool includeNullability) { if (IncludeNullability == includeNullability) diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 99fd2edc33067..501ba01cc92d1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -925,7 +925,8 @@ private static bool CheckConstraints( Debug.Assert(conversions.IncludeNullability); if (!SatisfiesConstraintType(conversions, typeArgument, constraintType, ref useSiteDiagnostics)) { - warningsBuilderOpt.Add(new TypeParameterDiagnosticInfo(typeParameter, new CSDiagnosticInfo(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, containingSymbol.ConstructedFrom(), constraintType, typeParameter, typeArgument))); + var diagnostic = new CSDiagnosticInfo(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, containingSymbol.ConstructedFrom(), constraintType, typeParameter, typeArgument); + warningsBuilderOpt.Add(new TypeParameterDiagnosticInfo(typeParameter, diagnostic)); } } continue; @@ -1018,6 +1019,8 @@ private static bool SatisfiesConstraintType( // "An identity conversion (6.1.1). // An implicit reference conversion (6.1.6). ..." + + // When nullability is considered, top-level nullability must be implicitly convertible. if ((!conversions.IncludeNullability || ConversionsBase.HasTopLevelNullabilityImplicitConversion(typeArgument, constraintType)) && conversions.HasIdentityOrImplicitReferenceConversion(typeArgument.TypeSymbol, constraintType.TypeSymbol, ref useSiteDiagnostics)) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index ffb6946945090..40b535c95bd11 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -37823,6 +37823,35 @@ class D Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterConstraint, "A").WithArguments("B4", "A", "T", "A").WithLocation(32, 8)); } + [Fact] + public void Constraint_TypeParameterConstraint() + { + var source0 = +@"public class A1 + where T : class + where U : class, T +{ +} +public class A2 + where T : class + where U : class, T? +{ +}"; + var comp0 = CreateCompilation(source0, parseOptions: TestOptions.Regular8); + comp0.VerifyDiagnostics(); + var ref0 = comp0.EmitToImageReference(); + + var source = +@"using System.Runtime.CompilerServices; +class B1 where T : A1 { } +class B2 where T : A2 { } +[NonNullTypes] class B3 where T : A1 { } +[NonNullTypes] class B4 where T : A2 { }"; + var comp = CreateCompilation(new[] { source, NonNullTypesAttributesDefinition }, references: new[] { ref0 }, parseOptions: TestOptions.Regular8); + // PROTOTYPE(NullableReferenceTypes): Should report warnings for each. + comp.VerifyDiagnostics(); + } + // Boxing conversion. [Fact] public void Constraint_BoxingConversion()