Open cston opened 5 years ago
As I understand it, there are two separate things:
These should be separate things, and I’m working on a PR that separates them into two separate enums. An annotation (1) can affect flow analysis (2) when we for example read a field or property or get a value from an invocation. Flow analysis (2) can affect the inferred nullability of a type (1) due to type inference, but in that case we would always want to infer a speakable annotation. At least, that is how I understand the latest discussion in the LDM.
@gafter
The nullability of a type where the type is stated. This has three states: annotated, unannotated (in a context where nullable is enabled), and oblivious (unannotated where nullable is not enabled). The nullability of an expression/value computed during flow analysis. This has two states: can be null, or cannot be null.
I'm not sure I understand why something can only be oblivious in a context where nullability is disabled, and why an expression is either null or not null. What is the type of y here, and the what is the nullability of its initialiser expression:
#nullable disable
void M(string x)
{
#nullable enable
var y = x;
}
The state of the initializing expression is “not null” (because it is the rvalue read from an oblivious symbol). From that we infer the type string
(unannotated) for y
.
Updated OP to record the decision on throw null
:
throw null
should warn.
Throw statements and throw expressions yield a warning when their operand may be null.
No special treatment of constant or literal null here, as everywhere else.
Added
A variable of a type parameter that cannot be annotated, when read, may yield a null value. But a (possibly) null value cannot be stored in it, because the type may be substituted with a type that is non-nullable. If we apply the usual rules, that would mean this causes a diagnostic:
void M<T>(T t)
{
t = t; // warning: might be assigning null to T
}
To solve this, we propose the following rules:
default
and default(T)
null
e as T
e.GetFirstOrDefault()
- which would be annotated with [MaybeNull]We should discuss this and make sure we agree.
!
change top level nullability when there was no warning?Added
ref
arguments always be assumed to be assigned to? (open)class C
{
public void M(string? s)
{
M2(ref s);
s.ToString(); // warn?
}
void M2(ref string s2) => throw null;
}
Two options:
Relates to https://github.com/dotnet/roslyn/pull/33447#discussion_r258184852
Added
From to dotnet/roslyn#33639 The current LDM position is that we use the annotation context to compute the annotations (oblivious versus non-nullable). We don't currently do this.
Added:
In LDM 2/20, we decided on a list of "pure" null-tests, which affect both branches. This gist documents a proposal whereby: A switch with a pure test sets the state of the tested variable to maybe-null at the entrance of the switch.
We need to review this proposal.
We need to confirm whether the same patterns which are considered "pure" null tests in an is
(if (x is null) ...
or if (x is {}) ...
for instance) are also considered "pure" null tests in a switch
.
Added:
default
loophole (open)var y = default(SomeStructWithNonNullableFields); // we currently produce no warning
I understand that there are issues with ref assemblies, but maybe that means the guidelines for ref assemblies should be updated (if you have any private non-nullable field, then the reference assembly should preserve that).
Alternatively, would it be useful to be able to mark some struct
types as not being instantiatable via default
?
Alternatively, would it be useful to be able to mark some
struct
types as not being instantiatable viadefault
?
Considering that this has been proposed more than once (#146, #2019), I'm certain that such an annotation will be well-received for many developers.
Thanks for the references @Joe4evr. I'll read up on those issues and make one of them a championed issue.
Added
Should the result of a dynamic invocation be treated as non-null, as it is generally outside the scope of static analysis, or should we attempt to infer a nullability from the set of known candidates when the receiver is not dynamic
?
Open LDM Issues for Nullable Reference Types
Open issues are on top. Resolved issues are at the bottom.
Relating to championed issue https://github.com/dotnet/csharplang/issues/36
There are also some issues marked as "Language Design" in roslyn repo.
Confirm interaction between MaybeNull and NotNull (open)
Which takes precedence? (currently it's
MaybeNull
) See https://github.com/dotnet/roslyn/pull/35955#discussion_r288212991Should AllowNull and NotNull be allowed on value types? (open)
Tracked by https://github.com/dotnet/roslyn/issues/36009 See https://github.com/dotnet/roslyn/pull/35955
Should
!
change top level nullability when there was no warning? (open)See https://github.com/dotnet/roslyn/pull/33447#discussion_r258177260
Applying a null-suppression operator currently changes the top-level nullability of the expression it's applied to, regardless of whether that expression produced a warning in the first place. After https://github.com/dotnet/roslyn/pull/33447 is merged, this will mean that applying the
!
to aref
parameter that accepts a nullable object will mark the expression as non-null, regardless of the type of the ref. See the example below:Should we get a warning on the
s.ToString()
call?Should
ref
arguments always be assumed to be assigned to? (open)Two options:
Relates to https://github.com/dotnet/roslyn/pull/33447#discussion_r258184852
Tracking state across conversions (open)
Which conversions should preserve the nullable state of members?
[MaybeNull]
and other attributes (open)See https://github.com/dotnet/roslyn/issues/30953
Resolve unknowns with
[MaybeNull]
and other similar attributes that affect nullability, and the interaction with unspeakable types.Using an attribute doesn't work well with locals or nested types (
IEnumerable<T /*maybe-null*/>
).var?
(open)See https://github.com/dotnet/roslyn/issues/31874
Should we support
var?
, and if so, what are the open issues? Along those lines, would we allowvar? (x, y) = ...
deconstructions? Some alternatives are: (1) the user can spell out the type or cast, (2) we could always takevar
to infer the nullable version of the type. Note:var?
suffers from similar issues asT?
when applied toint?
.typeof(string?)
(open)Should we permit (or forbid)
typeof(string?)
, given that we can never honor the nullable annotation?When we compute a nullable annotation in flow analysis, should we use the context? (open)
From dotnet/roslyn#33639 The current LDM position is that we use the annotation context to compute the annotations (oblivious versus non-nullable). We don't currently do this. For example, when doing type inference, would we infer oblivious in a context where the nullability annotations are disabled, but non-null where nullability annotations are enabled?
Result of a dynamic invocation (open)
Should the result of a dynamic invocation be treated as non-null, as it is generally outside the scope of static analysis, or should we attempt to infer a nullability from the set of known candidates when the receiver is not
dynamic
?See also https://github.com/dotnet/roslyn/issues/29893
Resolved issues
Track assignments through
ref
with conditional expressions (closed)cc @gafter
What is the nullability of
ref
variables when assigned through conditional expressions?Resolved 2/13/2019:
Yes, those two should warn Tracked by https://github.com/dotnet/roslyn/issues/33365
Nullability of conditional access with unconstrained type parameters (closed)
cc @AlekseyTs
What is the nullability of
x?.F()
?Resolved 2/13/2019:
The nullability of a conditional invocation is always nullable. Tracked by https://github.com/dotnet/roslyn/issues/33430
!
operator on L-values (closed)See https://github.com/dotnet/roslyn/issues/27522
!
is currently allowed for certainout
scenarios.Should
!
be allowed for the following?Resolved 2/13/2019:
We'll disallow those additional scenarios for now.
Expression is always (or never)
null
(closed)See https://github.com/dotnet/roslyn/issues/22743 See https://github.com/dotnet/roslyn/issues/29868 cc @AlekseyTs, @gafter
Confirm we should (or not) report hidden diagnostics when an expression is always (or never)
null
. This might require nullable analysis that assumes expressions are nullable (rather than non-nullable) when in doubt.Resolved 2019-02-20:
These were never part of the language specification. OK to drop them.
throw null
(closed)Should
throw null
produce a warning that the exception expression may be null?The current checked-in behavior (as of this PR) is to warn on
throw null
.Resolved by email (2/12/2019):
throw null
should warn. Throw statements and throw expressions yield a warning when their operand may be null. No special treatment of constant or literal null here, as everywhere else. Relates to https://github.com/dotnet/roslyn/issues/32877is
nullability infalse
case (closed)See https://github.com/dotnet/roslyn/issues/30297
Should
is
update the nullability in both branches or should the one branch be treated as unreachable?Note: this was discussed on 2/13/2019, but not resolved yet.
"Pure" null tests affect both branches:
x == null
x != null
(Type)x == null
(Type)x != null
x is null
s is string
(when the type ofs
isstring
)s is string s2
(when the type ofs
isstring
)s is string _
(when the type ofs
isstring
)Other null tests are not "pure" and so affect only one branch:
x is string
x is string s
x is C { Property = 3 }
TryGetValue
([NotNullWhenTrue]
)string.IsNullOrEmpty(s)
([NotNullWhenFalse]
)x?.ToString() != null
Resolved 2019-02-20:
We approved this proposal (some null tests are considered "pure" or "deliberate") with some adjustments. See the notes for details. Follow-up bug is https://github.com/dotnet/roslyn/issues/33526
Discuss the
default
loophole (closed)var y = default(SomeStructWithNonNullableFields); // we currently produce no warning
I understand that there are issues with ref assemblies, but maybe that means the guidelines for ref assemblies should be updated (if you have any private non-nullable field, then the reference assembly should preserve that). Alternatively, would it be useful to be able to mark some
struct
types as not being instantiatable viadefault
?Resolved 2019-03-04:
Keep this loophole for now.
Nullable analysis of switch expressions and statements (closed)
In LDM 2/20, we decided on a list of "pure" null-tests, which affect both branches. This gist documents a proposal whereby: A switch with a pure test sets the state of the tested variable to maybe-null at the entrance of the switch.
We need to review this proposal. We need to confirm whether the same patterns which are considered "pure" null tests in an
is
(if (x is null) ...
orif (x is {}) ...
for instance) are also considered "pure" null tests in aswitch
.Resolved 3/6/2019:
We're not considering
is {}
or{} => ...
to be "pure tests". Neithero is object
ors is string
.Nullable analysis of unreachable code (closed)
See https://github.com/dotnet/roslyn/issues/28798 See https://github.com/dotnet/roslyn/issues/32047 cc @gafter
How should nullability analysis treat unreachable code?
Resolved 3/6/2019:
No nullability warnings in unreachable code.
handling of type parameters that cannot be annotated (closed)
See also https://github.com/dotnet/roslyn/issues/33436
A variable of a type parameter that cannot be annotated, when read, may yield a null value. But a (possibly) null value cannot be stored in it, because the type may be substituted with a type that is non-nullable. If we apply the usual rules, that would mean this causes a diagnostic:
To solve this, we propose the following rules:
default
anddefault(T)
(the default conversion, though implicit, does introduce a warning)null
(the null conversion, though implicit, does introduce a warning)e?.M()
where the return type is a type parameter known to be a reference typeT
when the dynamic value might benull
T
e as T
when there is not an implicit conversion from the type ofe
toT
e.GetFirstOrDefault()
which is annotated with[MaybeNull]
.How can these diagnostics be suppressed?
Resolved 3/6/2019:
Proposal was accepted.
Element-wise analysis of tuple conversions (closed)
See https://github.com/dotnet/roslyn/issues/33035
Reasons to use element-wise analysis of tuple conversions:
Resolved 3/8/2019 with team:
In short, we're ok with current behavior (similar to
Pair<T1, T2>
and anonymous types).For the record, the alternative was clarified as (roughly): whenever you read a local of tuple type, we would give the result a type based on the state of the tuple. But this involves complexity:
ref
arguments. And the benefits aren’t clear:(string?, string)
in the scenario below doesn’t seem a major advantage.Should anonymous type fields have top-level nullability? (closed)
Should the top-level nullability of anonymous type fields be part of the inferred signature in addition to being tracked?
Resolved 3/8/2019:
Yes, we should warn on
a = new { x = y };
. But we should not warn onIdentity(a).x.ToString();
(we inferredIdentity<string!>
).