Note that there are some explanatory texts on larger screens.

plurals
  1. POSerious bugs with lifted/nullable conversions from int, allowing conversion from decimal
    primarykey
    data
    text
    <p>I think this question will bring me instant fame here on Stack Overflow.</p> <p>Suppose you have the following type:</p> <pre><code>// represents a decimal number with at most two decimal places after the period struct NumberFixedPoint2 { decimal number; // an integer has no fractional part; can convert to this type public static implicit operator NumberFixedPoint2(int integer) { return new NumberFixedPoint2 { number = integer }; } // this type is a decimal number; can convert to System.Decimal public static implicit operator decimal(NumberFixedPoint2 nfp2) { return nfp2.number; } /* will add more nice members later */ } </code></pre> <p>It has been written such that only safe conversions that don't lose precision are allowed. However, when I try this code:</p> <pre><code> static void Main() { decimal bad = 2.718281828m; NumberFixedPoint2 badNfp2 = (NumberFixedPoint2)bad; Console.WriteLine(badNfp2); } </code></pre> <p>I am surprised this compiles and, when run, <strong>writes out <code>2</code></strong>. The conversion from <code>int</code> (of value <code>2</code>) to <code>NumberFixedPoint2</code> is important here. (An overload of <code>WriteLine</code> that takes in a <code>System.Decimal</code> is preferred, in case anyone wonders.)</p> <p>Why on Earth is the conversion from <code>decimal</code> to <code>NumberFixedPoint2</code> allowed? (By the way, in the above code, if <code>NumberFixedPoint2</code> is changed from a struct to a class, nothing changes.)</p> <p>Do you know if the C# Language Specification says that an implicit conversion from <code>int</code> to a custom type "implies" the existence of a "direct" explicit conversion from <code>decimal</code> to that custom type?</p> <p>It becomes much worse. Try this code instead:</p> <pre><code> static void Main() { decimal? moreBad = 7.3890560989m; NumberFixedPoint2? moreBadNfp2 = (NumberFixedPoint2?)moreBad; Console.WriteLine(moreBadNfp2.Value); } </code></pre> <p>As you see, we have (lifted) <code>Nullable&lt;&gt;</code> conversions here. But oh yes, that does compile.</p> <p>When compiled in <strong>x86</strong> "platform", this code writes out an unpredictable numeric value. Which one varies from time to time. As an example, on one occasion I got <strong><code>2289956</code></strong>. Now, that's one serious bug!</p> <p>When compiled for the <strong>x64</strong> platform, the above code crashes the application with a <code>System.InvalidProgramException</code> with message <strong>Common Language Runtime detected an invalid program.</strong> According to the documentation of the <code>InvalidProgramException</code> class:</p> <blockquote> <p>Generally this indicates a bug in the compiler that generated the program.</p> </blockquote> <p>Does anyone (like Eric Lippert, or someone who has worked with lifted conversions in the C# compiler) know the cause of these bugs? Like, what is a sufficient condition that we don't run into them in our code? Because the type <code>NumberFixedPoint2</code> is actually something that we have in real code (managing other people's money and stuff).</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload