Dilemma: how compatible do you want to be?

We are reaching the end of the process to implement the BYOR (Bring Your Own Runtime) support and we are working on some of the more obscure things inside the compiler: implicit and explicit conversions and the related compiler options, such as /vo4 and /vo7.

Numeric comparisons

If you would ask any normal person which number is bigger: -1 or any arbitrary positive number, then most people will say that -1 is smaller. Now ask the same thing to a computer and you may receive diferent and unexpected answers. Consider the following example program:

FUNCTION Start AS VOID
    LOCAL dwValue AS DWORD
    LOCAL liValue AS LONG
    liValue := -1
    dwValue := 1
    ? liValue < dwValue
    RETURN

Compile this code in Vulcan and it will compile without warnings, but the result will be that liValue (-1) is NOT smaller than dwValue (1).

Compile the same code in X# and you will get a compiler warning that you are comparing signed and unsigned integers. But the result is correct liValue is < dwValue.

Compile the same code (you wil have to remove the AS VOID from the start function) in VO and you will get a compiler warning 51422 (overflow or loss of data), which many people have disabled.If you ignore the error then the result will be that liValue(1) is NOT smaller than dwValue(1).

If you enable overflow errors in Vulcan (/ovf+) you will see what happened: the comparison will throw an overflow exception. The LONG Value gets converted from -1 to a DWORD with the value 0xFFFFFFFF and this produces an overflow. But without overflow checking this will then lead to  a comparison where 0xFFFFFFFF is compared with 1. VO does the same.

After our implementation of the /vo4 compiler option in X# the compier warning will disappear, but the result of the comparison will be different from VO and Vulcan, because Roslyn will detect the possible overflow problem and will convert the comparison to a 64 bit comparison.

Now the 1 million dollar question of course is what we should do:

  • create a compatible but incorrect result or
  • create a correct but incompatible result.

Please let us know what you think of this...

{rscomments on}

A similar problem is

  • Assigning a LONG to a PTR compiles in VO and Vulcan without an error
  • Assigning a PTR to a LONG compiles in VO and Vulcan with an error

We would expect the complete opposite: assigning to an integer should be considered safe, but assigning a value to a PTR and thus potentially creating an invalid PTR seems pretty unsafe to us. So what do we do ?

  • Compatible and unsafe?
  • Incompatible and safe?

PS. For the next build we have already implemented:

  • Vulcan compatible codeblocks
  • Vulcan compatible string comparisons (/vo13)
  • Vulcan compatible implicit casts and conversions (/vo7)
  • Vulcan compatible signed/unsigned operations (/vo4)

10 comments

  • I would choose "correct but incompatible" as long the compiler gives a warning. If one wants the "incorrect" result, he can, by casting the long to a dword (correct?).
  • The same as Otto said :"I would choose "correct but incompatible" as long the compiler gives a warning. If one wants the "incorrect" result, he can, by casting the long to a dword (correct?). "

    This is a perfect example where Unit tests come into the game. With them the devolper gets a failing test that leads to the code that has to be changed.

    Regards
    Frank
  • I agree with Wolfgang, correct but incompatible, and with a compiler warning - for both cases.
    Danilo
  • Food for thought: Wrote a C# console app. Both versions return: True.

    static void Main(string[] args)
    {
    UInt32 dwValue; // unsigned 32bit integer (DWORD ??)
    Int64 liValue; // signed 64bit integer
    liValue = -1;
    dwValue = 1;
    Console.WriteLine( liValue < dwValue);
    Console.Read();
    }

    static void Main(string[] args)
    {
    uint dwValue; // unsigned 32bit integer
    int liValue; // signed 32bit integer
    liValue = -1;
    dwValue = 1;
    Console.WriteLine( liValue < dwValue);
    Console.Read();
    }

    The following gives: CS0034 C# Operator '<' is ambiguous on operands of type 'long' and 'ulong'
    {
    UInt64 dwValue;
    Int64 liValue;
    liValue = -1;
    dwValue = 1;
    Console.WriteLine( liValue < dwValue);
    Console.Read();
    }
  • Well, the verdict by our judges is very clear: "Correct but incompatible, with a warning."
    That is what we will implement in the next build of X#.
    We will also include an example in the documentation explaning what VO and Vulcan did, how X# does it and how you can emulate the (incorrect) VO and Vulcan behavior