What's wrong with just a runtime-checked const?
Reiner Pope
reiner.pope at gmail.com
Tue Jul 18 01:02:12 PDT 2006
xs0 wrote:
>
>>> - 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.
>
> Well, it's not really important where it's stored - if you cast to
> int/long, you lose the constness information in any case...
If you stored it separately, it wouldn't be accessible easily to the
user. This is obviously an implementation detail, and not the
interesting part of the discussion, though. :)
>
>
>>> - 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.
>
> Agreed :) It's definitely more flexible, but the question is whether the
> cost is low enough for the feature to still be usable, and whether it's
> realistically implementable at all.. Note that even if the GC is
> modified, and the constness information is stored somewhere externally,
> it can take a lot of updating when passing pointers/references around...
Yes, and I don't know enough about function calling to know what exactly
is possible, etc.
>
>
>>> - 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;
>
> Well, you assume the optimizer, but it's not usually used with debug
> builds, even if it supported this optimization.
>
> And there definitely are cases where each access would require a check;
> in a multi-threaded program you can hardly assume anything about
> non-local variables, especially that they don't change :)
>
I don't see the problem: you pass isConst by value, so there is no way
for anything external to alter the const-ness level that that particular
function has. How can that cause any problems?
>
>> 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.
>
> That is true only for correct programs ;) Considering how the purpose is
> to catch bugs, though, you can't make that assumption...
Since the programmer shouldn't be able to access the isConst variable,
there's no way to change it, so it shouldn't be a problem.
>
>
>> 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();
>
> if (name_m is name)
>
Yes, but try getting that to work with a statically-checked const. It
simply won't work unless you have a very smart checker. Of course, you
could leave const out altogether, but then we would be making no
progress. Let me demonstrate the static const-checking problem:
char[] tolower(const char[] input) // the input must be const, because
we agree with CoW, so we won't change it
{
// do some stuff
if ( a write is necessary )
{ // copy it into another variable, since we can't change input (it's
const)
}
return something; // This something could possibly be input, so it
also needs to be declared const. So we go back and make the return value
of the function also a const.
}
// Now, since the return value is const, we *must* dup it.
Can you suggest another solution other than avoiding const-checking
entirely? By the way, a dedicated string class is actually an
implementation of runtime const checking.
> but, it's certainly not a pretty solution... and there's a related
> question - how can tolower know whether to .dup or not.. there are
> certainly cases where it isn't necessary.
This is just another issue that static checking can't solve but runtime
checking can (providing we have a modification of the libraries to
support a CoW and a in-place version).
>
>> 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.
>
> Well, if you wanted, you could make tolower() return that already, it
> doesn't have much to do with constness..
I've said above how it has to do with constness. Constness is not
required, but if you decide to use it statically, then you get all these
problems which you can't solve.
>> 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.
>
> Sure, I'm interested :) But the way I see it, it's a practically
> impossible problem to solve in a way that is time/space efficient,
> useful, and easy to use, even more so if the solution is not static..
>
How important is time/space in a debug/unittest build? Walter said
somewhere that unit tests often cause slow-downs to half speeds, so is
speed really such an issue then? Similarly, unit tests builds are 3x the
size of release builds (on my current test, anyway). Should we really be
worried about efficiency concerns in non-release builds given the losses
in efficiency we already accept?
> xs0
More information about the Digitalmars-d-learn
mailing list