{"id":2344,"date":"2015-06-08T20:09:20","date_gmt":"2015-06-08T08:09:20","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2344"},"modified":"2015-06-08T20:09:20","modified_gmt":"2015-06-08T08:09:20","slug":"a-deeper-dive-into-range-checking","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2344\/","title":{"rendered":"A Deeper Dive into Range Checking"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Yesterday I posted about an issue with type checking in Delphi (and other Pascal) compilers.  As mentioned in that post, range checking is fundamentally flawed as a supposed solution to the problem for reasons that are explored further in this post.<\/p>\n<p><!--more--><\/p>\n<p>To recap:  Range checking does not test the types involved in <strong>both<\/strong> sides of an operation or assignment, it is is concerned only with the <em>value<\/em> involved on the one hand and just <em>one<\/em> of the types on the other.  That is, the type of the variable or parameter that is to <em>receive<\/em> the value involved.<\/p>\n<p>We shall use a simple scratch program to explore the ramifications of this:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nprogram Project1;\r\n\r\n{$APPTYPE CONSOLE}\r\n\r\n  uses\r\n    SysUtils;\r\n\r\n  procedure Foo(bar: Integer);\r\n  begin\r\n    WriteLn('Foo &gt;&gt; ', IntToStr(bar));\r\n  end;\r\n\r\nvar\r\n  i64: Int64;\r\nbegin\r\n  WriteLn('Range checking ', {$ifopt R+}'ON'{$else}'OFF'{$endif});\r\n\r\n  i64 := -1;\r\n  Foo(i64);\r\n\r\n  WriteLn('i64 &gt;&gt; ', IntToStr(i64));\r\n  ReadLn;\r\nend.\r\n<\/pre>\n<p><strong>Note:<\/strong> The final <strong>ReadLn()<\/strong> is simply to ensure that when we run this from the IDE the console window hangs around for us to check the output.<\/p>\n<p>If you compile and run this (<em>without<\/em> range checking) and you get this result:<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-off.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-off.png?resize=300%2C72&#038;ssl=1\" alt=\"range check off\" width=\"300\" height=\"72\" class=\"aligncenter size-medium wp-image-2347\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-off.png?resize=300%2C72&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-off.png?w=453&amp;ssl=1 453w\" sizes=\"(max-width: 300px) 100vw, 300px\" data-recalc-dims=\"1\" \/><\/a><\/p>\n<p>It compiles and runs without error, but what is this ?  The output actually appears consistent, despite the fact that we passed a 64-bit representation of <strong>-1<\/strong> into a <em>32-bit<\/em> parameter.  So let&#8217;s try the same thing with range checking enabled (don&#8217;t forget to do a full build after changing the project options):<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-on.png?ssl=1\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-on.png?resize=300%2C63&#038;ssl=1\" alt=\"range check on\" width=\"300\" height=\"63\" class=\"aligncenter size-medium wp-image-2348\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-on.png?resize=300%2C63&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/range-check-on.png?w=462&amp;ssl=1 462w\" sizes=\"(max-width: 300px) 100vw, 300px\" data-recalc-dims=\"1\" \/><\/a><\/p>\n<p>Some might expect this result.  After all, -1 is within the valid range for a 32-bit Integer just as much as it is for a 64-bit one.  But if we look at the <em>internal<\/em> representation of these values things are less comfortable:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n     -1    32-bit           $ffffffff\r\n     -1    64-bit   $ffffffffffffffff\r\n<\/pre>\n<p>So yes, <strong>-1<\/strong> is in the valid range of both types, but what has actually happened is that the range checking has tested the value of the 64-bit integer and, finding it acceptable, has gone right ahead and passed the <strong>truncated<\/strong> 32-bit value through to the function.<\/p>\n<p>On the one hand you might be thinking &#8220;<em>So what ?<\/em>&#8220;.  After all, <strong>-1<\/strong> is <strong>-1<\/strong> right ?<\/p>\n<p>Who cares if it&#8217;s a 64-bit representation versus a 32-bit representation.  The <em>value<\/em> is what counts, surely ?  Especially when you realise that this thinking happens to work all the way up (down?) to the maximum (minimum?) negative value of a 32-bit integer (and positive values, obviously).<\/p>\n<p>This if course is exactly the logic behind range checking and is perfectly valid for those values that &#8220;pass&#8221; this test.  It doesn&#8217;t care that the possible values involved <em>could<\/em> be out of bounds, it is only concerned with whether the <em>specific<\/em> value involved in a particular execution of some code actually is within those bounds.<\/p>\n<p>Which does nothing to identify that this exact same code is certain to fail when values are involved that do <em>not<\/em> pass the test.<\/p>\n<p>What this means of course is that if you rely on range checking to detect type errors of this nature then you <strong>have<\/strong> to <em>leave<\/em> that range checking in your production code, incurring the not insignificant overhead that involves.<\/p>\n<p>That is, range checking on every assignment of an ordinal value, every passing of an ordinal parameter.<\/p>\n<p>Every.<\/p>\n<p>Single.<\/p>\n<p>One.<\/p>\n<p>Quite apart from the penalty being carried around by all the perfectly valid code just on the off chance that there is some bad code to be identified, this is basically the same as relying on <strong>FullDebugMode<\/strong> and <strong>FastMM<\/strong> to find places where you have left dangling pointers in your code.  Perfectly valid in a debug build, but not something you would want to include in your shipping code.<\/p>\n<p>But what&#8217;s the alternative, in the absence of help from the compiler ?<\/p>\n<p>How about good old fashioned self-discipline, diligence and effective code reviews to catch the occasional, inevitable slip-up?  Maybe ?  Just a thought.<\/p>\n<p>i.e. don&#8217;t just say &#8220;<em>turn on range checking and wait for the exceptions to find the bugs<\/em>&#8220;, but do yourself what the compiler could and should be doing.<\/p>\n<p>Just because the compiler has dropped the ball is no excuse for you to follow suit.<\/p>\n<h2>Footnote: Some Curious Inconsistencies<\/h2>\n<p><strong>Warning Says &#8220;<em>No<\/em>&#8220;.  Generated Code Says &#8220;<em>No Problem<\/em>&#8220;<\/strong><\/p>\n<p>Let&#8217;s change the initialisation of <strong>i64<\/strong> to use the internal representation of <strong>-1<\/strong> that we identified:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n   i64 := $ffffffffffffffff;\r\n<\/pre>\n<p>When you compile this you do in fact get a warning:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n  Constant expression violates subrange bounds\r\n<\/pre>\n<p>This is because the literal constant <strong>$ffffffffffffffff<\/strong> is treated as <em>unsigned<\/em>, where-as the variable is <em>signed<\/em>.  However, if you compile and run this with range checking enabled you get exactly the same result as before.  I won&#8217;t bother with another screenshot.  I know seeing is believing but if you don&#8217;t believe me, just try it yourself.  \ud83d\ude42<\/p>\n<p>This is odd.  Either that value is signed and therefore out of range of the variable type involved (hence the warning) or it isn&#8217;t and we shouldn&#8217;t have received the warning.  It seems the team responsible for the compiler warnings and the team responsible for the range checking code team weren&#8217;t singing off the same hymn sheet here, which is rather worrying.<\/p>\n<p><strong>32-bit Integers Better Behaved Than Their Bigger Cousins<\/strong><\/p>\n<p>Now try the same thing with a 32-bit <strong>Integer<\/strong>:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nvar\r\n  i32: Integer;\r\nbegin\r\n  WriteLn('Range checking ', {$ifopt R+}'ON'{$else}'OFF'{$endif});\r\n\r\n  i32 := $ffffffff;\r\n  Foo(i32);\r\n\r\n  WriteLn('i32 &gt;&gt; ', IntToStr(i32));\r\n  ReadLn;\r\nend.\r\n<\/pre>\n<p>This version of the code (the <strong>foo()<\/strong> function is exactly as before) produces the same &#8220;<em>Constant expression<\/em>&#8221; warning, and when compiled with range checking <em>OFF<\/em>, produces two lines of &#8220;-1&#8221;, as before.<\/p>\n<p>The difference in this case however is that when we compile with range checking <strong>ON<\/strong> then that warning proves to be spot on.  We get a range check error when the code attempts to assign the value to <strong>i32<\/strong>.<\/p>\n<p>So we have inconsistency between handling of 32-bit and 64-bit variables and inconsistency in the case of 64-bit values in terms of the warning output and the actual code ultimately produced.<\/p>\n<p>Whichever way you look at it, range checking is fundamentally flawed as a mechanism for identifying fundamental, static errors in code, and has some very nasty smells about it in the Delphi implementation in particular.<\/p>\n<p>Next time someone suggests you lean on this to identify errors in your code you might ask them where they have tied up their horse and what arrangements they have made for collecting the dung.  \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> Yesterday I posted about an issue with type checking in Delphi (and other Pascal) compilers. As mentioned in that post, range checking is fundamentally flawed as a supposed solution to the problem for reasons that are explored further in this post.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[71,292,13,289],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-BO","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":2337,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2337\/","url_meta":{"origin":2344,"position":0},"title":"A Silent Danger&#8230;","date":"07 Jun 2015","format":false,"excerpt":"A brief post on a long standing omission in type checking in Pascal and the limitations of Range Checking as applied to the problem. Consider this contrived example of a simple function: This very simple function accepts an explicitly 32-bit Integer parameter and simply returns TRUE if the value passed\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1240,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1240\/","url_meta":{"origin":2344,"position":1},"title":"What&#8217;s in a Word &#8230; ?","date":"21 Sep 2012","format":false,"excerpt":"In an exchange with David Heffernan both on SO and in the comments here on Te Waka, I had cause to climb in my own personal \"Wayback Machine\" and further investigate an apparent change in compiler behaviour between Delphi 2007 and 2009. This change was first identified as the result\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":586,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/586\/","url_meta":{"origin":2344,"position":2},"title":"Absolute (for) Beginners","date":"15 Oct 2009","format":false,"excerpt":"I casually suggested the use of the \"absolute\" keyword in response to a question on the NZ DUG mailing list today. \u00a0I thought nothing of it but someone mentioned that it had been years since he'd seen anyone use it, so I thought maybe it was worth bringing to wider\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2574,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2574\/","url_meta":{"origin":2344,"position":3},"title":"Anonymous Classes: Identity Exposed!","date":"18 Feb 2017","format":false,"excerpt":"In my previous post on Anonymous Classes I erroneously referred to them as \"dynamic objects\" (thanks to commentors for pulling me up on that). Dynamic objects are something else entirely (although what precisely they might mean can vary on different platforms and in different languages). I have now corrected that\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2597,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2597\/","url_meta":{"origin":2344,"position":4},"title":"(True = 1) and (True = &#8216;-1&#8217;) ?","date":"17 Mar 2017","format":false,"excerpt":"It has been observed that the Delphi documentation states that the constants True and False have the values 1 and 0 respectively, not the -1 and 0 that the default string conversions apply. This does actually make sense but also lays a trap for the unwary. True, True, Wherefore Art\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2265,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2265\/","url_meta":{"origin":2344,"position":5},"title":"Nullable Types.  Not!","date":"23 Aug 2014","format":false,"excerpt":"I recently mentioned that RemObjects have placed their OS X native IDE - a.k.a. Fire - into public beta. I haven't been using it myself (yet) but have been following developments in the RemObjects Talk forums with interest, and a new feature in the Elements 8.0 compiler (also part of\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2344"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=2344"}],"version-history":[{"count":3,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2344\/revisions"}],"predecessor-version":[{"id":2351,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2344\/revisions\/2351"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2344"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2344"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2344"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}