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

C#7 pattern matching: if (obj is string s == false) does not work as expected. #17828

Closed
sumtec opened this issue Mar 14, 2017 · 4 comments
Closed
Labels
Area-Compilers Resolution-By Design The behavior reported in the issue matches the current design

Comments

@sumtec
Copy link

sumtec commented Mar 14, 2017

Version Used:
C#7 with VS2017

Steps to Reproduce:

  1. Create a new C#7 project and add the following function to any ".cs" file:
void Test(object obj)
{
  if (obj is string s == false) return;
  Debug.WriteLine(s);
}
  1. Observe the IntelliSense or Build output.

Expected Behavior:
No error; Or,
Showing an error saying something like "Pattern matching in the if statement is not allow to test with false" on the line of the "if" statement.

Actual Behavior:
Showing an error saying "Use of unassigned local variable 's'" on the line of the statement "Debug.WriteLine(s)".

Reason for thinking this is a bug:
The following code can be compiled without issue:

void TestOK(object obj)
{
  if (!(obj is string s)) return;
  Debug.WriteLine(s);
}

The !x is equivalent to 'x == false'. And '!' is sometimes easy to be skipped by human eyes.

And I understand that in the example of if (obj is string s == nonConstantValue) ..., we don't know if obj is a string or not if the "if-block". So, the "s" is meaningless. However, I think:

  1. It's ease to know if it is comparing to a constant false or not.
  2. We should, in this example, tell the user that the s here is meaningless and not allow them to write such an "if" statement, instead of showing "unassigned s" when the user attempt to use it.

And yes, if we are allowing user to compare with false, there would be some weird cases like if (obj is string s != true) or if (!(obj is string) == false). However, this is the users' problem.

P.S.:
The following code cannot be compiled as well:

void TestTrue(object obj)
{
   if ((obj is string s) == true) Debug.WriteLine(s);
}
@AdamSpeight2008
Copy link
Contributor

s is definitely unassigned if the pattern doesn't match, hence the error.

@gafter
Copy link
Member

gafter commented Mar 14, 2017

The unary negation operator ! inverts "definitely assigned when true" and "definitely assigned when false". The binary relational operators never produce results in these states. Therefore ! and == false are not equivalent operations. This issue demonstrates that fact.

It is by design.

@gafter gafter added the Resolution-By Design The behavior reported in the issue matches the current design label Mar 14, 2017
@gafter gafter closed this as completed Mar 14, 2017
@sumtec
Copy link
Author

sumtec commented Mar 28, 2017

Hi @gafter,

Correct me if I am wrong:

When you say ! invert the definite assignment, you mean !(obj is string s) has:
before(!(obj is string s)) = {}
true(obj is string s)=after(is string s) U {s}
true(!(obj is string s)) = false(obj is string s) = {}

So:
after(if (!(obj is string s)) return;)
= after(if(!(obj is string s)) return else {}
= true(obj is string s) = {s}

However, I am still getting confused about why this is not able to be compiled:

void TestNotOK(object obj)
{
  if (!(obj is string s == true)) return;
  Debug.WriteLine(s);
}

It seems like if we put an ==, no matter if either left or right to be a constant, it cannot assume it as true or false. Or, can we simply say, the operator == will simply put it into an unknown state. It make sense but it's anti-intuitive, like this one:

void TestNotOK(object obj)
{
  if (obj is string s == true) Debug.WriteLine(s); // error
}

Regards,
Frank

@gafter
Copy link
Member

gafter commented Mar 29, 2017

If you have an expression for which a variable is "definitely assigned when true", then using == true on that expression will destroy the "partial" definitely assigned state; the variable will not be definitely assigned afterwards. Here is an example without pattern-matching:

    static void Main()
    {
        bool b = true;
        int x;
        if (b && M(out x)) Console.WriteLine(x); // ok
        if ((b && M(out x)) == true) Console.WriteLine(x); // error
    }
    private static bool M(out int x)
    {
        x = 1;
        return true;
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Resolution-By Design The behavior reported in the issue matches the current design
Projects
None yet
Development

No branches or pull requests

4 participants