VB.NET and its broken data types

VB.NET is starting to get on my nerves.
I have been coding in BASIC since I was 13 years old. The language and I just clicked; I felt as I was just simply asking the computer, in plain English, what to do. It just felt right.

At about that same time I also learned C, Java and (68000/80×86) assembler; but it just wasn’t the same.
Sure, anything but BASIC had the “execution speed” advantage, but early on I realized that the time spent in the “development cycle” was far more important than any other advantages that other languages could provide.

Of course, this is a highly disputable argument but at the time, it made quite a lot of sense.

So I’ve been coding in BASIC (regardless of the compiler/IDE) for most of my life and I have also been heavily criticized for it, always resorting to the common misconceptions that BASIC is slow, RAM hungry, the code looks like a spaghetti, using WEND, the POKEs and PEEKs, etc, etc…

I love challenges and coding in BASIC can be a huge one. But, so far, I think I’ve been able to do anything I’ve wanted in pure BASIC… until now.

I have coded several emulators and compilers in BASIC and I have never encountered any problems directly caused by the language itself… until now.

For the past few years (in my spare time) I’ve been working on, what should have been, a quite simple program: an 80×86 emulator.
Unfortunately, this has turned out to be a complete nightmare!

Quite early in the development of the emulator’s most basic features I realized I was going to have a problem properly handling 8 and 16 bit operations. And I was quite right.

Although the emulation code hasn’t changed that much since its initial implementation, I’ve been implementing helper functions to allow me to properly handle such operations.

Just look at the “December 30, 2012” entry, for example.
In the article, I explain that a simple data type change solved “the” problem, but  at the time I didn’t realize that this would introduce other issues; and all because of the fucked up way VB.NET handles data types.

Consider this c# code:

What result should we expect?
Well, that is simple: if “x” is an unsigned 16bit variable, then its maximum possible value (65535) plus 1 (one) should give us 0 (zero); and that is exactly what happens if you execute that code:

 

So, let’s try a VB.NET version of the code and let’s see what happens.
Here’s the code:

And here’s the result:

vb_datatypes_01

So what the hell happened???
Well, BASIC happened…

In c#, just as in c/c++, Java and many other languages, the value of “x” is rolled over to provide the correct result. In VB.NET, we simply get a stupid exception, informing us of an overflow.

The implications of this behavior are a major fact that should be considered by anyone coding in BASIC. Including myself.

Why is the VB.NET compiler throwing an exception here?
Why cannot it behave as the c# compiler?
Am I missing something? Is there, perhaps, a way to change the compiler’s default (and quite idiotic) behavior?
Can you even imagine the leaps and bounds I’ve been through trying to compensate for such behavior in the 80×86 emulator?

Oh… and in case you were wondering, yes VB6 has the exact same behavior:

vb6_datatypes_01

Will this ever be fixed?
I honestly doubt it… but one can only hope.

In any case, why is this happening? Why are these two compilers behaving differently?
Well, let’s take a look at the MSIL code.
This is what the VB.NET compiler produced:

And this is the code produced by the c# compiler:

Notice any differences?
No? Then take a closer look at the “add” (IL_0009) and “conv” (IL_000a) opcodes.

I know absolutely nothing about MSIL, but the additional overflow checks on those opcodes is a deal breaker, making VB.NET behave like a broken language.

Is there a reason behind this behavior?
I guess so… otherwise, why is Mono producing the exact same results?

mono_datatypes

Or is the Mono runtime simply emulating .NET’s stupidity?

  • Cory Smith

    Try this code by going into the project properties to the Compile section. Under here you’ll find an Advanced Compile Options… button. Once there, check (activate) the “Remove integer overflow checks”.

    The reason why mono has the same result is that there are reasons why overflow checking is in BASIC (it’s always been there). You can think of this as the difference between “thinking like a human” versus “thinking like a computer”. BASIC defaults to “thinking more like a human”; where there is a min and a max range for a number and if you go outside that… it’s a problem. Computers, on the other hand, have no concept of this (at an internal level) and will simply just “loop” the value. C-style languages default to the “think like a computer” style.

    It’s only when you start working with “interesting” projects that this behavior bites you in the rear… for the most part, it’s a good thing… except when it isn’t. 😉 Emulation is one of the biggest circumstances where I’ve personally encountered it.

    As it turns out, enabling this option will also increase overall performance of the application since this checking is no longer done.

    • First of all, I had a blast reading your comment.

      However, the
      “Remove integer overflow checks” is automatically enabled when compiling
      in Release mode… and yet, VB6 and VB.NET still behave the same way: an overflow exception is raised because of the generated IL code:

      IL_0000: ldc.i4 65535
      IL_0005: stloc.0
      IL_0006: ldloc.0
      IL_0007: ldc.i4.1
      IL_0008: add.ovf
      IL_0009: conv.ovf.u2
      IL_000a: stloc.0
      IL_000b: ldloca.s x
      IL_000d: call instance string [mscorlib]System.UInt16::ToString()
      IL_0012: call void [mscorlib]System.Console::WriteLine(string)
      IL_0017: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
      IL_001c: pop
      IL_001d: ret

      The above code was decompiled (using Telerik’s JustDecompile) from a Console app compiled in Release mode. The overflow checks are still there:

      IL_0008: add.ovf
      IL_0009: conv.ovf.u2

      • Cory Smith

        I tested/confirmed the behavior before posting with the following code:

        Sub Main()

        Dim x As UInt16 = UInt16.MaxValue
        x += 10
        Console.WriteLine(x.ToString())
        Console.ReadKey()

        End Sub

        The result displayed is 9. No exception generated.

        Debugger hooks and “inlining” optimizations are what is different between debug mode and release mode; integer overflow checks are still active either way. This is an option that must be specifically enabled in order to disable them (I love that logic).

        • Just tried the code in a console application (in release mode) and keep getting a “System.OverflowException”.

          Could you please share your project?