What's wrong with just a runtime-checked const?

Reiner Pope reiner.pope at gmail.com
Sun Jul 16 14:53:31 PDT 2006


xs0 wrote:
> Reiner Pope wrote:
>>
>> Wouldn't a runtime const check be much more flexible than a 
>> compile-time check? Const-safeness is fundamentally a 
>> correctness-checking feature, just like unit tests, so why not make it 
>> operate exactly like unit tests? I'm thinking of something like array 
>> bounds checking:
>> [snip]
>> The even better thing about this is that most code doesn't need to 
>> have const-correctness in mind when writing it, and it shouldn't break 
>> existing code, because the only code that will break is code that is 
>> buggy code anyway.
>>
>> Am I completely missing the point?
>> Will it cause memory/speed issues (keeping in mind that it's only for 
>> debug builds)?
> 
> Well, I don't think you completely missed the point, but doing it would 
> cause all sorts of issues:

> - where should the tag be placed? you can't put it inside the pointer, 
> as there are no free bits; you also can't put it next to a pointer, as 
> it would affect memory layout of structures (in particular, it would 
> make debug-built and release-built code non-interoperable).
Yeah, I've thought about it a fair bit more and compatibility seems to 
be the main issue. I did indeed mean that it would be stored next to the 
pointer, but it does seem like that could cause problems with 
interoperability. If all the info could be stored separately in a 
bit-array managed by, say, the GC, then perhaps that would avoid the 
issue of interoperability. But I haven't quite worked out how that would 
be implemented yet...

> - it can still be trivially subverted - just cast to int/long and back
I presume this point leads on from the above, where you assumed that the 
bit was stored *in* the pointer. So that's a null issue, since it 
clearly isn't.

> - you can't just check at the beginning of a function
I don't understand your reason for this, but when I started writing down 
the details more specifically, I realised that the advantage of runtime 
const checking is a *huge* increase in flexibility, and having to say at 
compile-time whether a function is const or not removes that flexibility 
in my mind.

> - you can get the pointer in the middle of it; you can also get the
> pointer in _another_ function (from a global or in a multi-threaded
> program); checking at every access would be too expensive, I think,
> even for a debug build

I don't know how expensive it would be. Considering that all it is is
   assert(!isConst);
that operation doesn't seem too hard. And of course, the compiler is 
free to optimize away duplicate versions of it, like this:

   _member = 4;
   _member = 3;

Obviously there's no need to check isConst before the second assignment. 
And on the issue of the pointer suddenly changing in the middle of the 
function: well, it's actually not an issue, because I worked out that 
it's best for the function to have a local copy of the isConst variable, 
which means no other resources can change it (other than a malicious 
hacker with pointers). This is also conceptually right, because it 
doesn't make sense to give a function a certain level of modification 
access and then change it while it is running.

However, I think now that checking at every access is the only flexible 
way to manage const.

In my view, we either need runtime-checked const or nothing. Consider 
this, and tell me how we can avoid excess dup calls if we use C++-style 
const-checking:

  class foo {
    private char[] _name;
    public const char[] getName()
    {
      return _name;
    }
    ...
  }
  ...
  Import std.string;
  foo f;
  /*const*/ char[] name = f.getName();
  char[] name_m = tolower(name);
  // Can we modify name_m? Better dup it, just to make sure
  name_m = name_m.dup();

With runtime const checking, we could fix that, though, by also getting 
a bool variable back from tolower() which says whether it returned the 
original string or a copy. Quite simply, compile-time const won't let 
you do that, so you will actually produce *slower* code.

If you're interested, I'm trying to come up with a draft which has the 
actual details, jut so it gives a more solid ground for discussion. My 
problem at the moment is just what we mentioned earlier: operability 
between debug and release builds. I suggested that each function should 
be passed, behind the scenes, the relevant isConst variable, but that's 
not compatible with release builds. I then thought that you could have 
two copies of a function: one for const variables and one for non-const 
variables. The correct one would be chosen at runtime, and external 
packages would always choose the one for non-const variables (which is a 
copy of the one for const variables, but with checking turned off). This 
effectively duplicates the amount of machine code generated by the 
compiler, but I am convinced that a solution like I propose is much 
better than a simple static checking scheme for the flexibility reason I 
mentioned above.

Reiner



More information about the Digitalmars-d-learn mailing list