[.net] C# -- implementing operator++

Started by
7 comments, last by Conner McCloud 17 years, 6 months ago
I am experiencing an InvalidProgramException, and can't quite seem to figure out the cause. The documentation says this is probably a bug either in the C# compiler or the JIT compiler. Running PEVerify yields a StackUnderflow error at the line I can trace the problem to, so I assume the problem is not in the JIT portion. But before I bring this to MSs attention, I want to make sure I'm not doing something retarded. The following file reproduces the error consistantly:

namespace ErrorTest
{
    class Program
    {
        public Program(int _) { i = _; }

        public Program Begin() {
            return new Program(0);
        }

        public static Program operator ++(Program b) {
            Program tmp = new Program(b.i);
            tmp.i++;
            return tmp;
        }

        public int i;

        static void Main(string[] args) {
            Program x = new Program(0);
            x.Begin()++;
            //++x.Begin(); //This also fails
        }
    }
}
Realistically, x.begin()++; could be replaced with (new Program(0))++; and the results would be the same. I'm guessing the problem is that the temporary Program object is getting lost somewhere. The operator++ implementation is what I'm mostly concerned with. Is that the typical methodology [construct, modify, and return a temporary]? It produces the sort of results one would expect, and I've found a few snippets scattered around the internet that would support it, but I want to make sure. Unfortunately, I'm finding it rather hard to dissect the MSIL myself, but if it helps here's what I get out of the dissassembler:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       14 (0xe)
  .maxstack  2
  .locals init ([0] class ErrorTest.Program x)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  newobj     instance void ErrorTest.Program::.ctor(int32)
  IL_0007:  stloc.0
  IL_0008:  call       class ErrorTest.Program ErrorTest.Program::op_Increment(class ErrorTest.Program)
  IL_000d:  ret
} // end of method Program::Main
PEVerify says the stack underflow occurs at offet 0x8, which I assume refers to IL_0008 in that code. If anybody could verify that I'm not doing anything obviously wrong, I'd appreciate it. CM
Advertisement
Hello

I belive that you must use it like this:

((Program)x++).Begin();
Quote:Original post by Hole
Hello

I belive that you must use it like this:

((Program)x++).Begin();

I do not want to change x, I want to change x.Begin(). The example code is a little odd, but in my original code "Program" is a container, and "Program.Begin()" is an iterator. So there's a type change that prevents that reordering from being valid.

CM
I would suggest running this by the people at the MSDN forums. There's actual softies on the C# team who lurk there and can take a better look at it.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
To my knowledge, the ++ operator is automatically generated from the + operator...
Rob Loach [Website] [Projects] [Contact]
Technically what it's doing is right, it's only doing it incorrectly.

According to the ECMA C# standard:

§14.5.9
The operand of a postfix increment or decrement operation shall be an expression classified as a variable, a property access, or an indexer access. The result of the operation is a value of the same type as the operand.

So, since in the case you've presented, what you're applying the post-increment operator to is not any of variable, property or indexer, your program is mostly what is incorrect.

Note that I say mostly, not completely, as the compiler also seems to be screwing up (which is obvious, as you do not get a compiler error nor does it throw an InvalidOperationException, which I believe is what the correct exception would be in this case). After looking at the IL, it seems that the program is not loading an instance of Program onto the evaluation stack before calling Program.op_increment, and when (as far as I know) the JIT attempts to verify the code, it detects this and throws an InvalidProgramException.

I would recommend, as Promit did, for you to post about this either on Usenet or MSDN or somewhere that someone from Microsoft would see it who would be able to do something about it. Like I said, I believe that this is the correct outcome for the program as the construct is invalid, just that the compiler is going about expressing it incorrectly (most likely due to incorrect or non-thorough checks when working with user-defined increment and decrement operators, for example, try applying ++ to a constant such as 10).
Oh, also, for reference I patched the IL of the Main method to actually execute correctly. Of course my code won't actually match up to that of the code generator, it still would be a good reference for what it should be. (Note that this is minus your call to Begin(), this could be what you get after inlining the code to Begin()):


.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 24 (0x18)
.maxstack 1
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldc.i4.s 10
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: call void [mscorlib]System.Console::WriteLine(int32)
IL_000a: nop
//////// New Code
IL_000b: ldc.i4.0
IL_000c: newobj instance void ErrorTest.Program::.ctor(int32)
//////// End New Code
IL_0011: call class ErrorTest.Program ErrorTest.Program::op_Increment(class ErrorTest.Program)
IL_0016: pop
IL_0017: ret
} // end of method Program::Main
I think the AP is definately correct. I just tried (new int())++;, and it results in a [somewhat confusing] error message: "The left-hand side of an assignment must be a variable, property or indexer."

I'll definately take this over to MSDN, thanks.

CM
Just a little more fun with ++. Given the specific warning I got from (new int())++, I decided to replace the Begin() in my original code with a read only property with the same name. I assumed it would fail, because it would try and set the new value to the property, and I didn't provide a set method. But the change was easy enough to make.

The result was the compiler crashing and me sending an error report to MS. I assume this is a less elegant version of the Internal Compiler Error you get in C++.
Quote:Original post by Rob Loach
To my knowledge, the ++ operator is automatically generated from the + operator...

++ has to be implemented individually, because you can overload + several times and none of them have to be integral. However, you get both the preincrement and the postincrement versions from a single overload. You were probably thinking of +=, which is generated for you if you overload +.

CM

This topic is closed to new replies.

Advertisement