Please confirm this is a MSFT bug

  • Thread starter Thread starter Jake Forson
  • Start date Start date
J

Jake Forson

Hi there,

Before I notify MSFT, can someone confirm this is in fact a compiler bug (it
seems pretty obvious but I'm fairly new to C#). The following code
(erroneously) generates compiler error CS0177 (The out parameter 'Whatever'
must be assigned to before control leaves the current method).

public void SomeFunc(out string Whatever)
{
bool Continue = ShouldWeContinue();
if (Continue)
{
Whatever = "Continue";
}

if (!Continue)
{
Whatever = "Didn't continue";
}
}
 
Jake,

The compiler error is correct - you must assign a value to 'Whatever'
before leaving the method. Your code could easily be updated to do this
as so:

bool Continue = ShouldWeContinue();
if (Continue)
Whatever = "Continue";
else
Whatever = "Didn't continue";

Hope this helps.


Dan Manges
 
Jake,
The compiler error is correct - you must assign a value to 'Whatever'
before leaving the method. Your code could easily be updated to do this
as so:

bool Continue = ShouldWeContinue();
if (Continue)
Whatever = "Continue";
else
Whatever = "Didn't continue";

Hope this helps.

Thanks for the feedback. It does assign it in all cases however which is why
I consider it a bug.
 
As written, yes - we can observe that it will always have a value by the
time it exits.

However: if the variable was a field, or if the variable was used in a (2.0)
anonymous delegate (as a /captured/ variable), then it would be possible for
(without it being obvious in the code) another thread to alter the value of
Continue between the two calls, which incidentally may or may-not be spotted
depending on volatility / memory-barriers. Given this, it makes a lot of
sense for me for the compiler to follow the "keep it simple" principle, and
consider all three cases equally - otherwise you would have to remember daft
rules about when each applies, and making a change (like using it in an
anonymous delegate) would suddenly cause unrelated code to not compile.

Does that make sense?

Marc
 
Jake Forson said:
Thanks for the feedback. It does assign it in all cases however which is
why I consider it a bug.

Not so much a bug as the compiler not being as smart as it could. From the
compilers point of view there are 2 seperate IF statements. There is only so
far the compiler can go when determining code paths.
It probably doesn't consider the actual contents of the if statements, so
worries about the consequences if neither one is true.

:-)

ChrisM.
 
Jake Forson said:
Thanks for the feedback. It does assign it in all cases however which is
why I consider it a bug.
Yes, it does assign it in all cases, but are they supposed to check every if
statement to see if it is an exact opposite of every other if statement.
Seems pretty silly and I don't see it as a bug.

This code:
private bool TestThis()
{
bool Continue = ShouldWeContinue();
if (Continue)
{
return true;
}

if (!Continue)
{
return false;
}
}

Produces "Not all code paths return a value" which I don't see as a bug
either. Yes, these simple examples are easy to check. But, it could
actually get way too complex to check.
 
As written, yes - we can observe that it will always have a value by the
time it exits.

However: if the variable was a field, or if the variable was used in a
(2.0) anonymous delegate (as a /captured/ variable), then it would be
possible for (without it being obvious in the code) another thread to
alter the value of Continue between the two calls, which incidentally may
or may-not be spotted depending on volatility / memory-barriers. Given
this, it makes a lot of sense for me for the compiler to follow the "keep
it simple" principle, and consider all three cases equally - otherwise you
would have to remember daft rules about when each applies, and making a
change (like using it in an anonymous delegate) would suddenly cause
unrelated code to not compile.

Does that make sense?

Thanks for the info. While the KISS principle may apply here (on that we
agree), I'm at odds with your other points. It's certainly easier to parse
code without worrying about trapping this type of scenario and even
beneficial if it imrpoves compiler performance and robustness, but it's
still legal IMO (or should be). I doubt the C# standard says otherwise (I'd
be surprised to find out). As for your threading point, I don't follow this
at all :) I'm not familiar with threading in .NET yet, but in the usual
case, it's normally up to the programmer to properly synchronize access to
shared resources accordingly. I don't see how threading plays into this
scenario at all (assuming the programmer is doing his job but again, I'm not
familiar with the .NET threading model). In any case, while this is hardly a
blocking issue, it does force the programmer to re-work what appears to be
perfectly legal code (and this contrived example is just a dumbed down
version of what I really wanted to do but can't). Anyway, thanks again for
the feedback.
 
Hi,

Personally I find the compiler answer correct.

It would not happen if you do

if (Continue)
{
Whatever = "Continue";
}
else
Whatever = "Didn't continue";


And in any case it's not a bug, it would be more like an optimization
 
Not so much a bug as the compiler not being as smart as it could. From the
compilers point of view there are 2 seperate IF statements. There is only
so far the compiler can go when determining code paths.
It probably doesn't consider the actual contents of the if statements, so
worries about the consequences if neither one is true.

Unless the standard says otherwise (or defines it as an implementation
detail which prevents you from writing portable code so it seems very
unlikely), it is therefore a bug IMO. The compiler is supposed to parse the
code according to the rules so as long as I've assigned something to my
"out" parameter, I've met my own burden so the compiler should meet its :)
 
Hi,

Thanks for the feedback. It does assign it in all cases however which is
why I consider it a bug.

Well, you selected the most obvious option, bool which only have two
possible values. what if instead you select an enum (or in an even more
extreme case an int) ?
Would you expect the compiler to keep track if you used all the possible
values?

Too much work IMO.
 
Hi,
Personally I find the compiler answer correct.

It would not happen if you do

if (Continue)
{
Whatever = "Continue";
}
else
Whatever = "Didn't continue";


And in any case it's not a bug, it would be more like an optimization

I don't see how it qualifies as an optimization :) A language is defined by
a standard which sets down all rules that you must follow. Unless the
standard stipulates certain rules or conventions that render this example
illegal (I don't have a copy in front of me to check), then I'm not
violationg any rules so the compiler is in error :)
 
Thanks for the feedback. It does assign it in all cases however which is
Well, you selected the most obvious option, bool which only have two
possible values. what if instead you select an enum (or in an even more
extreme case an int) ?
Would you expect the compiler to keep track if you used all the possible
values?

Too much work IMO.

Again, the compiler doesn't define the language. The standard does and the
compiler must follow it even if it doesn't like it. Otherwise it's not a
compliant compiler and this should be stipulated somewhere. In this case
especially, since MSFT created C#, they're not following their own rules
(assuming this example is in fact legal - I wouldn't mind hearing from a
language lawyer on the subject)
 
Jake Forson said:
Unless the standard says otherwise (or defines it as an implementation
detail which prevents you from writing portable code so it seems very
unlikely), it is therefore a bug IMO. The compiler is supposed to parse
the code according to the rules so as long as I've assigned something to
my "out" parameter, I've met my own burden so the compiler should meet its
:)

Yeeeaaa...

OK, so granted it is clear (to you and me) that in your case, either one or
the other of the if statements will definatly be true, and the compiler
could (I suppose) be smart enough to work that out.
However, how complex to you expect the compiler to get?

How about:

int testValue;
testValue = GetMyTestValue();
if (testValue <=5)
Whatever = "Continue";
if (testValue >=6)
Whatever = "Dont Continue";

should that compile?

what then if we change the first line to

double testValue;

I think it is also true that your original way of writing the method is
probably not the 'correct' way to write it. If it is written in the
'correct' way, that is as an IF..THEN..ELSE then the compiler is perfectly
happy with it.

;-P

ChrisM.
 
I don't see how threading plays into this scenario at all (assuming the
programmer is doing his job but again <snip>
The compiler does not (nor should not) enforce thread-synchronisation around
fields; this means that from the compiler's point-of-view the scenario I
posted is entirely plausible, so (if a field of captured variable) it *must*
barf at this.
your threading point, I don't follow this at all <snip>

The following code demonstates the /captured/ variable issue:

bool Continue = ShouldWeContinue();
something.SomeEvent += delegate {
DoSomething(ref Continue);
};
if (Continue) // ... etc as before

Now, SomeEvent could fire between the two "if" tests, changing the value of
Continue, and hence our return-variable might never be assigned. So: even in
a very simple example it is impossible to define a single behaviour that
works consistently for variables, fields and captured variables, *let alone*
properties and methods. As Jeff observes, with only a few more lines of code
it could get *must* more complex, too much so for either the compiler or
developer to validate that each route leaves a valid state.

So: it would only ever work with the most noddy of examples : why complicate
the compiler for such a trivial case? Why not just be consistent and
*either* assign a default value at initialisation, *or* use if/else so that
each route can be easily traced.

Marc
 
OK, so granted it is clear (to you and me) that in your case, either one
or the other of the if statements will definatly be true, and the compiler
could (I suppose) be smart enough to work that out.
However, how complex to you expect the compiler to get?

How about:

int testValue;
testValue = GetMyTestValue();
if (testValue <=5)
Whatever = "Continue";
if (testValue >=6)
Whatever = "Dont Continue";

should that compile?

Of course it should if the standard says so. The standard wags the
(compiler's) tail, not the other way around.
I think it is also true that your original way of writing the method is
probably not the 'correct' way to write it. If it is written in the
'correct' way, that is as an IF..THEN..ELSE then the compiler is perfectly
happy with it.

There's nothing incorrect about it at all. It may be verbose, less efficient
(potentially) , etc., but it's not "incorrect" as far as the language is
concerned (again, subject to what the standard says). Note that this isn't a
real world example anyway but is effectively the same as what I was trying
to do (which was much cleaner).
 
Jake said:
Again, the compiler doesn't define the language. The standard does
and the compiler must follow it even if it doesn't like it. Otherwise
it's not a compliant compiler and this should be stipulated
somewhere. In this case especially, since MSFT created C#, they're
not following their own rules (assuming this example is in fact legal
- I wouldn't mind hearing from a language lawyer on the subject)

See section 12.3.5 of ECMA-334.

http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf

In your case, the compiler wins - it's not required to do the level of
analysis to prove that the two if statements in your example are mutually
exclusive, so your two if statements don't constitute definite assignment to
the variable according to the rules.

-cd
 
The following code demonstates the /captured/ variable issue:

bool Continue = ShouldWeContinue();
something.SomeEvent += delegate {
DoSomething(ref Continue);
};
if (Continue) // ... etc as before
Now, SomeEvent could fire between the two "if" tests, changing the value
of Continue, and hence our return-variable might never be assigned. So:
even in a very simple example it is impossible to define a single
behaviour that works consistently for variables, fields and captured
variables, *let alone* properties and methods. As Jeff observes, with only
a few more lines of code it could get *must* more complex, too much so for
either the compiler or developer to validate that each route leaves a
valid state.

I'm not certain whether my point is defensible or even valid since I'm not
all that familiar with .NET threading. However, your example wouldn't
normally apply at all in this case (assuming I'm not out in left field as
far as .NET is conerned). If an event fires it will do so on another thread
and if that thread is going to modify "Continue" in anyway, then someone
(the programmer) needs to synchronize access to "Continue" using whatever
synchronization primitives the language (.NET) offers. IMHO this has nothing
to do with the original issue :)
 
Jake,

Two things:

a) If you think it is a bug, go and register it on the Product Feedback
center. If MS confirms it is a bug, they will put it in their system, if
not, they will tell you why. If you think it ^should^ be addressed, then
you can get votes for your suggestion, and assuming you get enough support,
it will help influence the product.

b) You keep talking about whether or not the compiler conforms to the spec.
The spec is at:

http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf

Furthermore, section 12.1.6 of the spec states:

Every output parameter of a function member shall be definitely assigned
(§12.3) before the function member returns normally.

If you then read section 12.3 (as well as re-read what some of the other
posters say), you will eventually see why your code is not legal. The
compiler can not guarantee that Continue will evaulate at ^runtime^ in such
a way that the code path will be hit, and therefore, can not guarantee that
the output variable will be set.

You are asking the compiler to do something which it is not defined (or
required) to do, which is to evaluate ^how^ the code paths will execute,
given all possible values.

There is no need to spam the group, btw, now that you have a) the spec
which you say the compiler should conform to, and b) a way to point out what
you consider to be a bug, and make a suggestion to change the compiler if it
is not (which it is not a bug).
 
OK. I never said your example does this. What I am saying is that there
*are* separate, more complex, real-world examples (such as the one I
presented). My key point is that it is highly desirable that the language
and compiler can handle these situations according to a *single*
*consistent* set of rules.
If an event fires it will do so on another thread and if that thread is
going to modify "Continue" in anyway, then someone (the programmer) needs
to synchronize access to "Continue" using whatever

Again, from the compiler's perspective they don't *need* to; it is just a
damned good idea. The compiler enforces rules, not ideals. As such the
compiler *cannot* (ever) guard against threading foobars. It's hard enough
for the original developer to identify this!

But... at the end of the day, this whole discussion is a non-issue. It gets
raised on this list (and others) every week or so, and each time the weight
of opinion is with the compiler and the language spec. I will happily
conceed that spec conformance makes a far better reason "why", but IMO the
non-trivial examples like the above help provide counter-cases against what
you are proposing - *particularly* with a view to consistency between
use-cases. It *is* a realistic example; if I could be bothered I would knock
some code together to illustrate. But I can't be ;-p

Marc
 
There is no need to spam the group, btw, now that you have a) the spec
which you say the compiler should conform to, and b) a way to point out
what you consider to be a bug, and make a suggestion to change the
compiler if it is not (which it is not a bug).

With all due respect, I'm not spamming this group. Nor do I wish to pursue
this issue ad nauseam. I'm trying to get to the bottom of it. I'm not a
programming novice (20+ years working in C/C++) and I know things aren't
always so cut-and-dry. People make their cases without knowing what the real
rules are and often quote from the standard without understanding it (not
necessarily in your case but in general - there's a lot of technical
legalese to wade through and it's easy to get things wrong). In any case,
now that I have the standard I can check for myself. Don't confuse someone
who's legimately defending a point however with someone who's being hostile
or argumentative (just because it seems that way to you).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top