Proposal for scoped const contracts

Oliver Dathe o.dathe at gmx.de
Tue Mar 25 13:06:18 PDT 2008


The following is some kind of superset of what I have proposed in [1]

Example for syntax (*):

     char[] f(char[] p : const(char)[]) { }
                 ^               ^
    Least restrictive type Tmin  |
                      Contract type Tcontract

Basic discussion:
a.) f may only compile if it accomplishes the contract regarding p.
b.) It is an error if Tmin is not implicitly castable to Tcontract.
c.) For the syntax regarding the contract I used ":" at the moment. For 
further syntax proposals, see (*)
d.) At the moment we may call the example f with parameters of type 
char[] and const(char)[]. const(char[]) is not covered by the contract 
and therefor may have to be rejected. More on that (**)
e.) It is still nothing said what the typeof(p) is inside the function. 
I assume it has to be the type of the passed parameter.
f.) If we want to return some slice of p, we get stuck again. Walter and 
Andrei have presented a solution in [2] up to some degree (***).
   char[] f(return char[] p : const(char)[]) { return p[17..42]; }
g.) f can be virtual, because no templates are required.

If static type checking is used inside f we may come out with up to N 
possible binary versions of f, where N is the number of possible 
const-levels between Tmin and Tcontract. It is not 2^N because of 
transitive const. e.g.
   void foo(char[][][][] x : const(char[][][])[]) {...}
may generate N=5 different versions up to:
   const(char[][][])[],
   const(char[][])[][],
   const(char[])[][][],
   const(char)[][][][],
   char[][][][]
Maybe more if invariant is distinguished. However we remain at one 
single sourcecode version and up to ~N binary versions. I think this is 
an acceptable behaviour.


Contract declaration syntax:

(*) Currently some variants come to my mind:

   f(Tmin p : Tcontract)  // a.)
   f(Tmin p Tcontract)    // b.)
   f(Tcontract Tmin p)    // c.)
   f(Tmin..Tcontract p)   // d.)

I think none of these would interfere with something already present. Is 
that right?

I personally would prefer a.) or b.) because a.) naturally signals the 
implicit convertibility with ":" but you may confuse with template list. 
b.) reminds to "Tail" const methods but here at parameter level [1]. 
Both have semantic similarty to what we do with parameter const contracts.

As well we could have a short handle of contract_type for the case when 
we want Tcontract=const(Tmin), a /full const contract/.

   f(Tmin p : const)          // for a.)
   f(Tmin p const)            // for b.)
   f(const Tmin p)            // for c.)
   ?                          // for d.)

That is pretty much what I proposed in [1].


Explicit, Forced and Implict Contracts:

(**) In my very first example passing an argument of type const(char[]) 
fails, because we could not directly sign the contract when we're just 
looking at the declaration.
a.) Nonetheless the compiler may be capable of signing the contract for 
the function if it can prove the function's ability to do so.
b.) We may be able to make a /request for contract/ of the function when 
calling it. Example:
   char[] x = ...;
   foo(x : const(char)); // request for contract when calling foo()
The compiler may then reject or prove&warant the offer.
c.) With this we may be able to reuse large amounts of D1 code with D2 
stylish const types. The returntype issue remains, but that does not 
apply to all functions.
d.) The compiler could try to establish the contract even if neither the 
function offers one nor the call. Maybe this also could be of help for 
the optimizer.


Return type:

(***) The return before the parameter means, that the const level of the 
type of parameter p is applied to the return value. Right now I'm not 
sure what to do if we wanted to return e.g. an element of p.

One idea would be to return "auto", meaning it returns the type of the 
value that is actually returned by the returning return statement ;) 
This goes consistent with the meaning of auto. E.g.

   auto foo(char[][] x : const(char[][])) {
     return x[17];
   }
   unittest {
     char[][]        a = ...;
     const(char)[][] b = ...;
     const(char[])[] c = ...;
     auto ya = foo(a);
     static assert (is(typeof(ya)==char[]));
     auto yb = foo(b);
     static assert (is(typeof(yb)==const(char)[]));
     auto yc = foo(c);
     static assert (is(typeof(yc)==const(char[])));
   }

But you may run into trouble elsewhere like "return found?it:other;", 
maybe. Another attempt:

   char foo(return(char[])[] p : const(char[][])) { ... }

or

   typeof(p[0]) foo(char[] p : const(char[])) {
     return p[42];
   }
   ...
   const(char)[] x = ...;
   auto y = foo(x);
   static assert (is(typeof(y)==const(char)));

[1] 
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=68137 

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=68248
[2] http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf p.38/39



More information about the Digitalmars-d mailing list