{"id":2337,"date":"2015-06-07T15:00:26","date_gmt":"2015-06-07T03:00:26","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=2337"},"modified":"2015-06-08T09:24:39","modified_gmt":"2015-06-07T21:24:39","slug":"a-silent-danger","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/2337\/","title":{"rendered":"A Silent Danger&#8230;"},"content":{"rendered":"<span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>A brief post on a long standing omission in type checking in Pascal and the limitations of Range Checking as applied to the problem.<\/p>\n<p><!--more--><\/p>\n<p>Consider this contrived example of a simple function:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  function IsLessThan64K(aValue: Integer): Boolean;\r\n  begin\r\n    result := aValue &lt; Word($ffff);\r\n  end;\r\n<\/pre>\n<p>This very simple function accepts an explicitly <em>32-bit<\/em> <strong>Integer<\/strong> parameter and simply returns <strong>TRUE<\/strong> if the value passed is less than 64K (65536).  Worth noting at this point is that <strong>Integer<\/strong> is 32-bit regardless of whether you are compiling for 32-bit or 64-bit platforms.<\/p>\n<p>Now consider two <strong>Int64<\/strong> variables, declared and initialised as follows:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  a, b: Int64;\r\n\r\n  a := $7fff;                \/\/ 32767\r\n  b := $7fffffffffffffff;    \/\/ 9223372036854775807\r\n<\/pre>\n<p>Now consider the results of the following calls to that <em>IsLessThan64K()<\/em> function, involving <strong>a<\/strong> and <strong>b<\/strong>:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  IsLessThan64K( a );\r\n  IsLessThan64K( b );\r\n  IsLessThan64K( b - a );\r\n<\/pre>\n<p>Somewhat surprisingly, given the dramatic discrepancy between the types of the variables and the type of the function parameter, these all compile without grumbling.  But Pascal is strongly typed, so if the compiler is happy then all must be right with the world, surely ?<\/p>\n<p>Well, let&#8217;s look at the results of these calls:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  IsLessThan64K( a );       \/\/ Returns TRUE - so far so good\r\n  IsLessThan64K( b );       \/\/ Returns TRUE ... um, what ?\r\n  IsLessThan64K( b - a );   \/\/ Also returns TRUE ... OMG THE COMPILER IS BROKEN!\r\n<\/pre>\n<p>That last comment is wrong.  The compiler isn&#8217;t broken, it has just not being very helpful in this particular case.<\/p>\n<p>Using the debugger, if we step into the last of these three calls we can see what value the function thinks it has been passed, and we find that this value is <strong>-32768<\/strong> when the correct result of <strong>a &#8211; b<\/strong> should be <strong>9223372036854743040<\/strong>.  <\/p>\n<p>Hmmm, maybe the compiler is broken after all ?<\/p>\n<p>No.  So what&#8217;s going on ?<\/p>\n<p>If we examine these values in their hexadecimal notation, things become a little clearer:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  a       $0000000000007fff                     32767\r\n  b       $7fffffffffffffff       9223372036854775807\r\n\r\n  b - a   $7fffffffffff8000       9223372036854743040\r\n                  $ffff8000                    -32768\r\n<\/pre>\n<p>The 64-bit value passed to that function has been simply <em>truncated<\/em> to 32-bits, to fit into that value.  As a result, the internal representation results in that massively large positive number being interpreted as a much smaller, negative number.  Precisely the sort of problem that a strongly typed language is supposed to prevent, surely ?<\/p>\n<p>So it may come as a surprise to some that <em>all three<\/em> of those calls will compile without any indication that the wrong type has been passed.<\/p>\n<p>There is no warning or even so much as a hint about the inappropriate attempt to pass a 64-bit value in that 32-bit parameter.<\/p>\n<h2>Range Checking: An Incomplete Solution<\/h2>\n<p>There is a compiler setting that can help in these circumstances:  <strong>Range Checking<\/strong>.<\/p>\n<p>First of all, be aware that this option is not on by default.  Not even in debug builds.  You ideally do not have it enabled in release builds in order to avoid the performance overhead involved, but in debug builds it perhaps makes sense.<\/p>\n<p>This is necessary because as suggested by the fact that this compiler option is grouped under &#8220;<strong>Runtime Errors<\/strong>&#8220;, this does not affect <em>compilation<\/em> of code.  Rather it introduces additional checks at runtime to ensure that values are within the required ranges of the types involved.<\/p>\n<p>This point bears emphasis:  <strong>Range Checking<\/strong> applies to <strong>value<\/strong>, not to <em>types<\/em>.<\/p>\n<p>So, for example, even with <strong>Range Checking<\/strong> enabled, the call involving <strong>a<\/strong> both compiles <em>and runs<\/em> without any problem what-so-ever:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  IsLessThan64K( a );\r\n<\/pre>\n<p>The range checking introduced by the compiler does not care that <strong>a<\/strong> is the wrong type.  It merely checks the <em>value<\/em>, finds that it is in the range required for a 32-bit Integer and waves it through.  In this contrived example of course <strong>a<\/strong> always has a value in the required range, but in a real-world scenario it may have any number of different values at different times in the execution of the code, only some of which may trigger a range check exception.<\/p>\n<p>What you really want &#8211; and perhaps expect &#8211; from a strongly typed language, is for the compiler to tell you that the potential error exists in the first place.<\/p>\n<p>Caveat Developor:  <strong>It doesn&#8217;t<\/strong>.<\/p>\n<h2>Not Just an Int(64) Problem<\/h2>\n<p>This isn&#8217;t limited only to 32\/64-bit integer types.  Exactly the same issue also affects other ordinal types.  e.g. Passing a <strong>Word<\/strong> in a <strong>Byte<\/strong> parameter, an <strong>Integer<\/strong> in a <strong>Word<\/strong>, etc.<\/p>\n<h2>Also, Not Just a Delphi Problem<\/h2>\n<p>Having quickly investigated in other variants of Pascal this appears to be a consistent behaviour, suggesting that perhaps it is some sort of hang-over from legacy Pascal compiler days.<\/p>\n<p>Neither Oxygene (at least when compiling for .net) nor FreePascal complain about this sort of thing either.  In the case of FreePascal this is particularly odd since this compiler supports a warning which seems aimed at precisely this situation: <strong>Type size mismatch, possible loss of data \/ range check error<\/strong>.<\/p>\n<p>For Oxygene\/Delphi the question is why does no such warning exist ?<\/p>\n<p>For FreePascal the question is if this code does not trigger that warning, what would ?<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\"> 3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>A brief post on a long standing omission in type checking in Pascal and the limitations of Range Checking as applied to the problem.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4],"tags":[13,127,173,288],"class_list":["post-2337","post","type-post","status-publish","format-standard","hentry","category-delphi","tag-language","tag-object-pascal","tag-pascal","tag-type-safety"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-BH","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2337","targetHints":{"allow":["GET"]}}],"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=2337"}],"version-history":[{"count":6,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2337\/revisions"}],"predecessor-version":[{"id":2343,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/2337\/revisions\/2343"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=2337"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=2337"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=2337"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}