Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is operator informs nullability analysis #28686

Merged
merged 7 commits into from
Jul 23, 2018

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Jul 19, 2018

We know that x is not null following x is string, x is string s, x is K (where K is a non-null constant).
We know that s is not null following x is string s.
We know that y has the same nullability as x following x is var y.
Produce a hidden warning if a null test could never succeed ("string" is null).

Relates to #22152

@jcouv jcouv added this to the 16.0 milestone Jul 19, 2018
@jcouv jcouv requested a review from a team as a code owner July 19, 2018 04:04
@gafter
Copy link
Member

gafter commented Jul 19, 2018

Produce a hidden warning if a null test could never succeed ("string" is null).

Does not sound like a good idea, even as a hidden diagnostic. If a public API method accepts a non-nullable parameter, you still want to encourage the presence of code that guards against null.
#Resolved

@jcouv
Copy link
Member Author

jcouv commented Jul 19, 2018

@gafter We already produce such a hidden diagnostic on x == null and x != null, and I think that was discussed in LDM.
I understand that null checks are okay. Mads even has a topic queued on generating such checks for public APIs.
I'll capture an open issue. #Resolved

@@ -75,9 +75,17 @@ Flow analysis is used to infer the nullability of variables within executable co

### Warnings
_Describe set of warnings. Differentiate W warnings._
If the analysis determines that a null check always (or never) passes, a hidden warning is produced. For example: `"string" is null`.
Copy link
Member

@cston cston Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hidden diagnostic? #Resolved

- comparisons to `null`: `x == null` and `x != null`
- `is` operator: `x is null`, `x is K` (where `K` is a constant), `x is string`, `x is string s`

Invocation to methods annotated with the following attributes will also affect flow analysis:
Copy link
Member

@cston cston Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps Invocation of. #Resolved

{
bool? nullKnowledge = null;
Copy link
Member

@cston cston Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be named isNull? #Resolved

break;
}

int slot = -1;
Copy link
Member

@cston cston Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding Debug.Assert(!IsConditionalState);. #Resolved


int slot = -1;
var operand = node.Operand;
if (operand.Type.IsReferenceType)
Copy link
Member

@cston cston Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please test unconstrained generic types.

static void F<T>(T t, object? o)
{
    if (t is string) t.ToString();
    if (o is T) o.ToString();
}

#Resolved

Copy link
Member Author

@jcouv jcouv Jul 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! That's caught two issues:

  • I've adjusted the check here (we could even remove it, but why track variables with struct type?)
  • even though we realize that t isn't null after t is string, we still report a possible null de-reference. That's an existing issue with variables of unconstrained generic type (added a PROTOTYPE). #Resolved

{
void Test(object? x)
{
if (x is C c)
Copy link
Member

@cston cston Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider testing x is T t where T is unconstrained or constrained to struct or class. #Resolved

{
void Test(object? x)
{
if (x is null)
Copy link
Member

@333fred 333fred Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

x is null [](start = 12, length = 9)

Consider tests of inverted logic (ie if (!(x is null))) #Resolved

void Test(object? x)
{
const string? nullConstant = null;
if (x is nullConstant)
Copy link
Member

@333fred 333fred Jul 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullConstant [](start = 17, length = 12)

Consider a behavior verification test of an is comparison to a nonconstant. #Pending

Copy link
Member Author

@jcouv jcouv Jul 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an existing error (constant expected). The is operator works with a type, a declaration pattern or a constant pattern. #Resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be an existing error, but it's good to ensure we don't crash in this case.


In reply to: 203968132 [](ancestors = 203968132)

@333fred
Copy link
Member

333fred commented Jul 19, 2018

Done review pass (commit 2) #Resolved

/// `switch (x) ... case Point p:` // PROTOTYPE(NullableReferenceTypes): not yet handled
///
/// If the expression is trackable, we'll return with different null-states for that expression in the two conditional states.
/// If the patterns is a `var` pattern, we'll also have re-inferred the `var` type with nullability and
Copy link
Member

@cston cston Jul 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the pattern is … (singular)? #Resolved

else
{
x.ToString(); // warn
}
Copy link
Member

@cston cston Jul 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding if (x is var _) { } in the same test. #Resolved

CSharpCompilation c = CreateCompilation(@"
class C
{
static void F<T>(T t, object? o)
Copy link
Member

@333fred 333fred Jul 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

T t [](start = 21, length = 3)

Nit: t is unused, and below. #Resolved

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (commit 6) with additional test suggestion.

@jcouv jcouv merged commit 38c858c into dotnet:features/NullableReferenceTypes Jul 23, 2018
@jcouv jcouv deleted the null-pattern branch July 23, 2018 19:13
333fred added a commit to 333fred/roslyn that referenced this pull request Jul 23, 2018
…operators

* features/NullableReferenceTypes:
  `is` operator informs nullability analysis (dotnet#28686)
  Make module-level NonNullTypes explicit in tests (dotnet#28769)
  Lazily evaluate NonNullTypes (dotnet#28736)
  Calculate TypeSymbolWithAnnotations.IsNullable lazily (dotnet#28687)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants