Const correctness revisited (proposal)

Oliver Dathe o.dathe at gmx.de
Sun Mar 23 19:53:35 PDT 2008


Hello D folks,

I'd like to introduce some suggestions for D2:
   1.) Enforceable immutability of function calls regarding parameters
   2.) Parameter related tail constness
   3.) Enforceable tail constness on calls

In thread [1] the problem was stated, what a clean D2 version of a 
function may look like, that returns a mutable slice from an input 
string which (the input string) may not be mutable by the function 
itself. There were no really clean solutions avoiding casts and 
templates. Therefore I'd like to suggest to think about how to force 
constness/immutability of functions and calls regarding parameters.

Proposals:

1.) Enforceable immutability of function calls regarding parameters

I'd like to suggest that, at the time we call a function, we may be able 
to force some overlay constness regarding the parameters. e.g.

   char[] foo(char[] source, char[] pattern) {} // example of Steven [1]
   char[] x = "some input";
   char[] y = foo(const x, "somepattern");      // (1) note the const

(1) The const means we want foo() to not mutate this parameter (with the 
respective transitivity to underlying calls which take x as parameter). 
If there is no version of foo() that could meet this requirement (at 
least implicitly), it is an error. Note that the typeof(source) remains 
plain char[] here.

Ok, usually if the creator knew he does not want to mutate a parameter 
he would declared it as const. But, there may be several reasons not to 
do so: a.) D1 b.) if he wants to return a mutable char[] slice of source 
he has to cast away the const which is not desirable in general.

Having parameter source be of type char[] gives us the opportunity to 
return a mutable slice without casting away constness. Calling foo(const 
x, ...); gives us the opportinity to ensure that x is not mutable by 
foo() while already having typeof(source)==char[].

You may also want to take some void d1function(char[] x); and call it 
e.g. with some const(char)[] argument, which may then internally be 
translated to casting away the constness but forcing immutability of x 
in d1function().

Now let us extend this to a more general scheme:


2.) Parameter related tail const correctness

If we think of how to bring this kind of overlay constness to the 
function declaration, we may come up with adding a tail constness 
regarding parameters:

   char[] bar(char[] a const, int b) {...} // (2)

(2) bar() is forced to be compilable if parameter a virtually was const 
char[] but the actual typeof(a) remains char[]. If bar() tries to mutate 
parameter a, that would be an error.

This also implies that if bar() calls foo(x,...) the call internally 
would force this overlay constness in foo() regarding x (transitive), so 
the call silently becomes foo(const x,...); behind the scenes.

Advantages of parameter related tail constness:
a.) It is some sort of injected, transparent and transient overlay 
constness only regarding the functions scope.
b.) Usual advantages with const correctness like detecting accidental 
mutation but you may already stick with the basic types.
c.) As expressive to the reader as if the parameter type was constified 
  but it just stays basic.
d.) You may stick with the easygoing D1 typing style, but you get the 
opportunity to let a 'silent const guard' work for you behind the scenes.
e.) It just naturally extends the tail constness concept from 'this' to 
parameters.
f.) May encourage SafeD [3][4]
g.) Compiler/Optimizer may already deduce this attribute?
h.) No different bytecode version

Recall Stevens problem [1] is solved with either

   char[] foo(char[] input const; char[] pattern){...}// at declaration
   // or
   char[] mutableslice = foo(const x; "mypattern");   // at function call
   // or both


3.) Enforceable tail constness on calls

Try to force the call to be immutable regarding their respective object. 
The call must either be explicitly tail const [2] or implicitly by not 
mutating the object.

   z = obj.foo(x,y) const;     // (3)
   z = obj.foo(x,y) invariant; // (4)
   func("input") invariant;    // (5)

(3) Forces foo() to be explicitly or implicitly tail const. If that is 
not possible it is an error.
(4) and (5) world-immutability. See [2]


[1] 
http://www.digitalmars.com/d/archives/digitalmars/D/const_debacle_68011.html
[2] http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf p.29
[3] 
http://www.digitalmars.com/d/archives/digitalmars/D/Memory_Safety_68031.html
[4] http://www.digitalmars.com/d/2.0/safed.html

-Oliver



More information about the Digitalmars-d mailing list