Each type reference is accompanied by a NullableAttribute with an array of bytes, where 0 is Oblivious, 1 is NotAnnotated and 2 is Annotated.
All value types are marked with flag 0 (oblivious).
To optimize trivial cases the attribute can be omitted, or instead can be replaced with an attribute that takes a single byte value rather than an array.
To optimize trivial cases the attribute can be omitted, or instead can be replaced with an attribute that takes a single byte value rather than an array.
Trivial/optimized cases:
1) All parts are NotAnnotated – a NullableAttribute with a single value 1 (rather than an array of 1s)
2) All parts are Annotated - a NullableAttribute with a single value 2 (rather than an array of 2s)
3) All parts are Oblivious – the attribute is omitted, this matches how we interpret the lack of an attribute in legacy assemblies.
For completeness, we would also recognize a NullableAttribute with a single value 0 (rather than an array of 0s),
but compiler will never emit an attribute like this.
but compiler will never emit an attribute like this.
NullableAttribute(1) should be placed on a type parameter definition that has a `notnull` constraint.
NullableAttribute(1) should be placed on a type parameter definition that has a `class!` constraint.
...
...
@@ -103,9 +103,11 @@ A number of null checks affect the flow state when tested for:
-`is` operator: `x is null`, `x is K` (where `K` is a constant), `x is string`, `x is string s`
Invocation of methods annotated with the following attributes will also affect flow analysis:
-`[NotNullWhenTrue]` (e.g. `TryGetValue`) and `[NotNullWhenFalse]` (e.g. `string.IsNullOrEmpty`)
-`[EnsuresNotNull]` (e.g. `ThrowIfNull`)
- simple pre-conditions: `[AllowNull]` and `[DisallowNull]`
- simple post-conditions: `[MaybeNull]` and `[NotNull]`
- conditional post-conditions: `[MaybeNullWhen(bool)]` and `[NotNullWhen(bool)]`
-`[AssertsTrue]` (e.g. `Debug.Assert`) and `[AssertsFalse]`
See https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-15.md
// (34,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t2, out var s7) // 12
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(34, 13),
// (35,15): warning CS8602: Dereference of a possibly null reference.
// (38,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t2, out var s8) // 15
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(38, 13),
// (39,15): warning CS8602: Dereference of a possibly null reference.
// (48,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t3, out var s10) // 20
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(48, 13),
// (49,15): warning CS8602: Dereference of a possibly null reference.
// (52,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t3, out var s11) // 23
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(52, 13),
// (53,15): warning CS8602: Dereference of a possibly null reference.
// (34,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t2, out var s7) // 5
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(34, 13),
// (35,15): warning CS8602: Dereference of a possibly null reference.
// (38,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t2, out var s8) // 7
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(38, 13),
// (39,15): warning CS8602: Dereference of a possibly null reference.
// (48,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t3, out var s10) // 10
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(48, 13),
// (49,15): warning CS8602: Dereference of a possibly null reference.
// (52,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t3, out var s11) // 12
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(52, 13),
// (53,15): warning CS8602: Dereference of a possibly null reference.
// Warn on misused nullability attributes (AllowNull on type that could be marked with `?`, MaybeNull on an `in` or by-val parameter)? https://github.com/dotnet/roslyn/issues/36073
var source =
@"using System.Runtime.CompilerServices;
class Program
{
static void F0<T>([AllowNull, MaybeNull]T t) { }
static void F1<T>([AllowNull, MaybeNull]ref T t) { }
static void F2<T>([AllowNull, MaybeNull]T t) where T : class { }
static void F3<T>([AllowNull, MaybeNull]T t) where T : struct { }
static void F4<T>([AllowNull, MaybeNull]T? t) where T : class { }
static void F5<T>([AllowNull, MaybeNull]T? t) where T : struct { }
static void F6<T>([AllowNull, MaybeNull]in T t) { }
static void M<T>(string? s1, string s2)
{
F0<string>(s1);
s1.ToString(); // 1
F0<string>(s2);
s2.ToString(); // 2
}
static void M_WithRef<T>(string? s1, string s2)
{
F1<string>(ref s1);
s1.ToString(); // 3
F1<string>(ref s2); // 4
s2.ToString(); // 5
}
}";
var comp = CreateNullableCompilation(new[] { AllowNullAttributeDefinition, MaybeNullAttributeDefinition, source });
comp.VerifyDiagnostics(
// (15,9): warning CS8602: Dereference of a possibly null reference.
// (39,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, ref T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// (43,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, ref T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// (53,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, ref T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// (57,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, ref T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// (86,9): error CS0411: The type arguments for method 'Program.F1<T>(T, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// (87,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// (91,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// (95,9): error CS0411: The type arguments for method 'Program.F1<T>(T, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// (96,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// (100,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// (15,22): warning CS8620: Argument of type 'List<string?>' cannot be used for parameter 'b' of type 'List<string>' in 'void C.F(ref List<string> a, ref List<string>? b, ref List<string?> c, ref List<string?>? d)' due to differences in the nullability of reference types.
// (15,36): warning CS8620: Argument of type 'List<string>' cannot be used for parameter 'd' of type 'List<string?>' in 'void C.F(ref List<string> a, ref List<string>? b, ref List<string?> c, ref List<string?>? d)' due to differences in the nullability of reference types.
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (12,34): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)'.
// (12,34): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object?, object>(Pair<object?, object> p, out object? t, out object u)'.
// (object? x, object? y) = p; // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "p").WithArguments("p", "void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)").WithLocation(12, 34),
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "p").WithArguments("p", "void E.Deconstruct<object?, object>(Pair<object?, object> p, out object? t, out object u)").WithLocation(12, 34),
// (13,9): warning CS8602: Dereference of a possibly null reference.
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (12,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)'.
// (12,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object?>(Pair<object, object?> p, out object t, out object? u)'.
// (object? x, (object y, object? z)) = p; // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "(object? x, (object y, object? z)) = p").WithArguments("p", "void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)").WithLocation(12, 9),
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "(object? x, (object y, object? z)) = p").WithArguments("p", "void E.Deconstruct<object, object?>(Pair<object, object?> p, out object t, out object? u)").WithLocation(12, 9),
// (13,9): warning CS8602: Dereference of a possibly null reference.
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (21,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object>(Pair2<object, object> p, out object t, out object u)'.
// (21,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object?>(Pair2<object, object?> p, out object t, out object? u)'.
// var (x, (y, z)) = p; // 1, 2, 3
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "var (x, (y, z)) = p").WithArguments("p", "void E.Deconstruct<object, object>(Pair2<object, object> p, out object t, out object u)").WithLocation(21, 9),
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "var (x, (y, z)) = p").WithArguments("p", "void E.Deconstruct<object, object?>(Pair2<object, object?> p, out object t, out object? u)").WithLocation(21, 9),
// (21,27): warning CS8634: The type 'object?' cannot be used as type parameter 'T' in the generic type or method 'E.Deconstruct<T, U>(Pair<T, U>, out T, out U)'. Nullability of type argument 'object?' doesn't match 'class' constraint.
// var (x, (y, z)) = p; // 1, 2, 3
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "p").WithArguments("E.Deconstruct<T, U>(Pair<T, U>, out T, out U)", "T", "object?").WithLocation(21, 27),