| [3d1e617] | 1 | # Thoughts on Resolver Design #
 | 
|---|
 | 2 | 
 | 
|---|
 | 3 | ## Conversions ##
 | 
|---|
 | 4 | C's implicit "usual arithmetic conversions" define a structure among the 
 | 
|---|
 | 5 | built-in types consisting of _unsafe_ narrowing conversions and a hierarchy of 
 | 
|---|
 | 6 | _safe_ widening conversions. 
 | 
|---|
 | 7 | There is also a set of _explicit_ conversions that are only allowed through a 
 | 
|---|
 | 8 | cast expression.
 | 
|---|
 | 9 | Based on Glen's notes on conversions [1], I propose that safe and unsafe 
 | 
|---|
 | 10 | conversions be expressed as constructor variants, though I make explicit 
 | 
|---|
 | 11 | (cast) conversions a constructor variant as well rather than a dedicated 
 | 
|---|
 | 12 | operator. 
 | 
|---|
 | 13 | Throughout this article, I will use the following operator names for 
 | 
|---|
 | 14 | constructors and conversion functions from `From` to `To`:
 | 
|---|
 | 15 | 
 | 
|---|
 | 16 |         void ?{} ( To*, To );            // copy constructor
 | 
|---|
 | 17 |         void ?{} ( To*, From );          // explicit constructor
 | 
|---|
 | 18 |         void ?{explicit} ( To*, From );  // explicit cast conversion
 | 
|---|
 | 19 |         void ?{safe} ( To*, From );      // implicit safe conversion
 | 
|---|
 | 20 |         void ?{unsafe} ( To*, From );    // implicit unsafe conversion
 | 
|---|
 | 21 | 
 | 
|---|
 | 22 | [1] http://plg.uwaterloo.ca/~cforall/Conversions/index.html
 | 
|---|
 | 23 | 
 | 
|---|
 | 24 | Glen's design made no distinction between constructors and unsafe implicit 
 | 
|---|
 | 25 | conversions; this is elegant, but interacts poorly with tuples. 
 | 
|---|
 | 26 | Essentially, without making this distinction, a constructor like the following 
 | 
|---|
 | 27 | would add an interpretation of any two `int`s as a `Coord`, needlessly 
 | 
|---|
 | 28 | multiplying the space of possible interpretations of all functions:
 | 
|---|
 | 29 | 
 | 
|---|
 | 30 |         void ?{}( Coord *this, int x, int y );
 | 
|---|
 | 31 | 
 | 
|---|
 | 32 | That said, it would certainly be possible to make a multiple-argument implicit 
 | 
|---|
 | 33 | conversion, as below, though the argument above suggests this option should be 
 | 
|---|
 | 34 | used infrequently:
 | 
|---|
 | 35 | 
 | 
|---|
 | 36 |         void ?{unsafe}( Coord *this, int x, int y );
 | 
|---|
 | 37 | 
 | 
|---|
 | 38 | An alternate possibility would be to only count two-arg constructors 
 | 
|---|
 | 39 | `void ?{} ( To*, From )` as unsafe conversions; under this semantics, safe and  
 | 
|---|
 | 40 | explicit conversions should also have a compiler-enforced restriction to 
 | 
|---|
 | 41 | ensure that they are two-arg functions (this restriction may be valuable 
 | 
|---|
 | 42 | regardless).
 | 
|---|
 | 43 | 
 | 
|---|
 | 44 | ### Constructor Idiom ###
 | 
|---|
 | 45 | Basing our notion of conversions off otherwise normal Cforall functions means 
 | 
|---|
 | 46 | that we can use the full range of Cforall features for conversions, including 
 | 
|---|
 | 47 | polymorphism.
 | 
|---|
 | 48 | Glen [1] defines a _constructor idiom_ that can be used to create chains of 
 | 
|---|
 | 49 | safe conversions without duplicating code; given a type `Safe` which members 
 | 
|---|
 | 50 | of another type `From` can be directly converted to, the constructor idiom 
 | 
|---|
 | 51 | allows us to write a conversion for any type `To` which `Safe` converts to: 
 | 
|---|
 | 52 | 
 | 
|---|
 | 53 |         forall(otype To | { void ?{safe}( To*, Safe ) })
 | 
|---|
 | 54 |         void ?{safe}( To *this, From that ) {
 | 
|---|
 | 55 |                 Safe tmp = /* some expression involving that */;
 | 
|---|
 | 56 |                 *this = tmp; // uses assertion parameter
 | 
|---|
 | 57 |         }
 | 
|---|
 | 58 | 
 | 
|---|
 | 59 | This idiom can also be used with only minor variations for a parallel set of 
 | 
|---|
 | 60 | unsafe conversions.
 | 
|---|
 | 61 | 
 | 
|---|
 | 62 | What selective non-use of the constructor idiom gives us is the ability to 
 | 
|---|
 | 63 | define a conversion that may only be the *last* conversion in a chain of such. 
 | 
|---|
 | 64 | Constructing a conversion graph able to unambiguously represent the full 
 | 
|---|
 | 65 | hierarchy of implicit conversions in C is provably impossible using only 
 | 
|---|
 | 66 | single-step conversions with no additional information (see Appendix B), but 
 | 
|---|
 | 67 | this mechanism is sufficiently powerful (see [1], though the design there has 
 | 
|---|
 | 68 | some minor bugs; the general idea is to use the constructor idiom to define 
 | 
|---|
 | 69 | two chains of conversions, one among the signed integral types, another among 
 | 
|---|
 | 70 | the unsigned, and to use monomorphic conversions to allow conversions between 
 | 
|---|
 | 71 | signed and unsigned integer types).   
 | 
|---|
 | 72 | 
 | 
|---|
 | 73 | ### Implementation Details ###
 | 
|---|
 | 74 | It is desirable to have a system which can be efficiently implemented, yet 
 | 
|---|
 | 75 | also to have one which has sufficient power to distinguish between functions 
 | 
|---|
 | 76 | on all possible axes of polymorphism. 
 | 
|---|
 | 77 | This ordering may be a partial order, which may complicate implementation 
 | 
|---|
 | 78 | somewhat; in this case it may be desirable to store the set of implementations 
 | 
|---|
 | 79 | for a given function as the directed acyclic graph (DAG) representing the 
 | 
|---|
 | 80 | order.
 | 
|---|
 | 81 | 
 | 
|---|
 | 82 | ## Conversion Costs ##
 | 
|---|
| [d14d96a] | 83 | Each possible resolution of an expression has a _cost_ tuple consisting of 
 | 
|---|
 | 84 | the following components: _unsafe_ conversion cost, _polymorphic_ 
 | 
|---|
 | 85 | specialization cost, _safe_ conversion cost, a count of _explicit_ 
 | 
|---|
 | 86 | conversions, and _qualifier_ conversion cost. 
 | 
|---|
 | 87 | These components are lexically-ordered and can be summed element-wise; 
 | 
|---|
 | 88 | summation starts at `(0, 0, 0, 0, 0)`.
 | 
|---|
| [3d1e617] | 89 | 
 | 
|---|
| [d5f1cfc] | 90 | ### Lvalue and Qualifier Conversions ###
 | 
|---|
 | 91 | C defines the notion of a _lvalue_, essentially an addressable object, as well 
 | 
|---|
 | 92 | as a number of type _qualifiers_, `const`, `volatile`, and `restrict`. 
 | 
|---|
 | 93 | As these type qualifiers are generally only meaningful to the type system as 
 | 
|---|
 | 94 | applied to lvalues, the two concepts are closely related. 
 | 
|---|
 | 95 | A const lvalue cannot be modified, the compiler cannot assume that a volatile 
 | 
|---|
 | 96 | lvalue will not be concurrently modified by some other part of the system, and 
 | 
|---|
 | 97 | a restrict lvalue must have pointer type, and the compiler may assume that no 
 | 
|---|
 | 98 | other pointer in scope aliases that pointer (this is solely a performance 
 | 
|---|
 | 99 | optimization, and may be ignored by implementers).
 | 
|---|
 | 100 | _Lvalue-to-rvalue conversion_, which takes an lvalue of type `T` and converts 
 | 
|---|
 | 101 | it to an expression result of type `T` (commonly called an _rvalue_ of type 
 | 
|---|
 | 102 | `T`) also strips all the qualifiers from the lvalue, as an expression result 
 | 
|---|
 | 103 | is a value, not an addressable object that can have properties like 
 | 
|---|
 | 104 | immutability. 
 | 
|---|
 | 105 | Though lvalue-to-rvalue conversion strips the qualifiers from lvalues, 
 | 
|---|
 | 106 | derived rvalue types such as pointer types may include qualifiers; 
 | 
|---|
 | 107 | `const int *` is a distinct type from `int *`, though the latter is safely 
 | 
|---|
 | 108 | convertable to the former. 
 | 
|---|
 | 109 | In general, any number of qualifiers can be safely added to the 
 | 
|---|
 | 110 | pointed-to-type of a pointer type, e.g. `int *` converts safely to 
 | 
|---|
 | 111 | `const int *` and `volatile int *`, both of which convert safely to 
 | 
|---|
 | 112 | `const volatile int *`.
 | 
|---|
 | 113 | 
 | 
|---|
 | 114 | Since lvalues are precicely "addressable objects", in C, only lvalues can be 
 | 
|---|
 | 115 | used as the operand of the `&` address-of operator. 
 | 
|---|
 | 116 | Similarly, only modifiable lvalues may be used as the assigned-to 
 | 
|---|
 | 117 | operand of the mutating operators: assignment, compound assignment 
 | 
|---|
 | 118 | (e.g. `+=`), and increment and decrement; roughly speaking, lvalues without 
 | 
|---|
 | 119 | the `const` qualifier are modifiable, but lvalues of incomplete types, array 
 | 
|---|
 | 120 | types, and struct or union types with const members are also not modifiable. 
 | 
|---|
 | 121 | Lvalues are produced by the following expressions: object identifiers 
 | 
|---|
 | 122 | (function identifiers are not considered to be lvalues), the result of the `*` 
 | 
|---|
 | 123 | dereference operator applied to an object pointer, the result of a member 
 | 
|---|
 | 124 | expression `s.f` if the left argument `s` is an lvalue (note that the 
 | 
|---|
 | 125 | preceding two rules imply that the result of indirect member expressions 
 | 
|---|
 | 126 | `s->f` are always lvalues, by desugaring to `(*s).f`), and the result of the 
 | 
|---|
 | 127 | indexing operator `a[i]` (similarly by its desugaring to `*((a)+(i))`). 
 | 
|---|
 | 128 | Somewhat less obviously, parenthesized lvalue expressions, string literals, 
 | 
|---|
 | 129 | and compound literals (e.g. `(struct foo){ 'x', 3.14, 42 }`) are also lvalues.
 | 
|---|
 | 130 | 
 | 
|---|
 | 131 | All of the conversions described above are defined in standard C, but Cforall 
 | 
|---|
 | 132 | requires further features from its type system. 
 | 
|---|
 | 133 | In particular, to allow overloading of the `*?` and `?[?]` dereferencing and 
 | 
|---|
 | 134 | indexing operators, Cforall requires a way to declare that the functions 
 | 
|---|
 | 135 | defining these operators return lvalues, and since C functions never return 
 | 
|---|
 | 136 | lvalues and for syntactic reasons we wish to distinguish functions which 
 | 
|---|
 | 137 | return lvalues from functions which return pointers, this is of necessity an 
 | 
|---|
 | 138 | extension to standard C. 
 | 
|---|
 | 139 | In the current design, an `lvalue` qualifier can be added to function return 
 | 
|---|
 | 140 | types (and only to function return types), the effect of which is to return a 
 | 
|---|
 | 141 | pointer which is implicitly dereferenced by the caller.
 | 
|---|
 | 142 | C++ includes the more general concept of _references_, which are typically 
 | 
|---|
 | 143 | implemented as implicitly dereferenced pointers as well. 
 | 
|---|
 | 144 | Another use case which C++ references support is providing a way to pass 
 | 
|---|
 | 145 | function parameters by reference (rather than by value) with a natural 
 | 
|---|
 | 146 | syntax; Cforall in its current state has no such mechanism. 
 | 
|---|
 | 147 | As an example, consider the following (currently typical) copy-constructor 
 | 
|---|
 | 148 | signature and call:
 | 
|---|
 | 149 | 
 | 
|---|
 | 150 |         void ?{}(T *lhs, T rhs);
 | 
|---|
 | 151 |         
 | 
|---|
 | 152 |         T x;
 | 
|---|
 | 153 |         T y = { x };
 | 
|---|
 | 154 | 
 | 
|---|
 | 155 | Note that the right-hand argument is passed by value, and would in fact be 
 | 
|---|
 | 156 | copied twice in the course of the constructor call `T y = { x };` (once into 
 | 
|---|
 | 157 | the parameter by C's standard `memcpy` semantics, once again in the body of 
 | 
|---|
 | 158 | the copy constructor, though it is possible that return value optimization 
 | 
|---|
 | 159 | will elide the `memcpy`-style copy).
 | 
|---|
 | 160 | However, to pass by reference using the existing pointer syntax, the example 
 | 
|---|
 | 161 | above would look like this:
 | 
|---|
 | 162 | 
 | 
|---|
 | 163 |         void ?{}(T *lhs, const T *rhs);
 | 
|---|
 | 164 |         
 | 
|---|
 | 165 |         T x;
 | 
|---|
 | 166 |         T y = { &x };
 | 
|---|
| [3d1e617] | 167 | 
 | 
|---|
| [d5f1cfc] | 168 | This example is not even as bad as it could be; assuming pass-by-reference is 
 | 
|---|
 | 169 | the desired semantics for the `?+?` operator, that implies the following 
 | 
|---|
 | 170 | design today:
 | 
|---|
| [3d1e617] | 171 | 
 | 
|---|
| [d5f1cfc] | 172 |         T ?+?(const T *lhs, const T *rhs);
 | 
|---|
 | 173 |         
 | 
|---|
 | 174 |         T a, b;
 | 
|---|
 | 175 |         T c = &a + &b,
 | 
|---|
 | 176 | 
 | 
|---|
 | 177 | In addition to `&a + &b` being unsightly and confusing syntax to add `a` and 
 | 
|---|
 | 178 | `b`, it also introduces a possible ambiguity with pointer arithmetic on `T*` 
 | 
|---|
 | 179 | which can only be resolved by return-type inference.
 | 
|---|
 | 180 | 
 | 
|---|
 | 181 | Pass-by-reference and marking functions as returning lvalues instead of the 
 | 
|---|
 | 182 | usual rvalues are actually closely related concepts, as obtaining a reference 
 | 
|---|
 | 183 | to pass depends on the referenced object being addressable, i.e. an lvalue, 
 | 
|---|
 | 184 | and lvalue return types are effectively return-by-reference. 
 | 
|---|
 | 185 | Cforall should also unify the concepts, with a parameterized type for 
 | 
|---|
 | 186 | "reference to `T`", which I will write `ref T`. 
 | 
|---|
 | 187 | Syntax bikeshedding can be done later (there are some examples at the bottom 
 | 
|---|
 | 188 | of this section), but `ref T` is sufficiently distinct from both the existing 
 | 
|---|
 | 189 | `lvalue T` (which it subsumes) and the closely related C++ `T&` to allow 
 | 
|---|
 | 190 | independent discussion of its semantics.
 | 
|---|
 | 191 | 
 | 
|---|
 | 192 | Firstly, assignment to a function parameter as part of a function call and 
 | 
|---|
 | 193 | local variable initialization have almost identical semantics, so should be 
 | 
|---|
 | 194 | treated similarly for the reference type too; this implies we should be able 
 | 
|---|
 | 195 | to declare local variables of reference type, as in the following:
 | 
|---|
 | 196 | 
 | 
|---|
 | 197 |         int x = 42;
 | 
|---|
 | 198 |         ref int r = x; // r is now an alias for x
 | 
|---|
 | 199 | 
 | 
|---|
 | 200 | Unlike in C++, we would like to have the capability to re-bind references 
 | 
|---|
 | 201 | after initialization, as this allows the attractive syntax of references to 
 | 
|---|
 | 202 | support some further useful code patterns, such as first initializing a 
 | 
|---|
 | 203 | reference after its declaration. 
 | 
|---|
 | 204 | Constant references to `T` (`const ref T`) should not be re-bindable. 
 | 
|---|
 | 205 | 
 | 
|---|
 | 206 | One option for re-binding references is to use a dedicated operator, as in the 
 | 
|---|
 | 207 | code example below:
 | 
|---|
 | 208 | 
 | 
|---|
 | 209 |         int i = 42, j = 7;
 | 
|---|
 | 210 |         ref int r = i; // bind r to i
 | 
|---|
 | 211 |         r = j;         // set i (== r) to 7
 | 
|---|
 | 212 |         r := j;        // rebind r to j using the new := rebind operator
 | 
|---|
 | 213 |         i = 42;        // reset i (!= r) to 42
 | 
|---|
 | 214 |         assert( r == 7 );
 | 
|---|
 | 215 | 
 | 
|---|
 | 216 | The other syntactic option for reference re-bind would be to overload 
 | 
|---|
 | 217 | assignment and use type inference on the left and right-hand sides to 
 | 
|---|
 | 218 | determine whether the referred-to variable on the left should be reassigned to 
 | 
|---|
 | 219 | the value on the right, or if the reference on the left should be aliased to 
 | 
|---|
 | 220 | the reference on the right. 
 | 
|---|
 | 221 | This could be disambiguated with casts, as in the following code example:
 | 
|---|
 | 222 | 
 | 
|---|
 | 223 |         int i
 | 
|---|
 | 224 |         int j;
 | 
|---|
 | 225 |         ref int r = i;   // (0a)
 | 
|---|
 | 226 |         ref int s = i;   // (0b)
 | 
|---|
 | 227 |         
 | 
|---|
 | 228 |         i = j;           // (1)
 | 
|---|
 | 229 |         i = (int)s;      // (2)
 | 
|---|
 | 230 |         i = s;           // (3)
 | 
|---|
 | 231 |         // ---------------------
 | 
|---|
 | 232 |         r = s;           // (4)
 | 
|---|
 | 233 |         r = (ref int)j;  // (5)
 | 
|---|
 | 234 |         // ---------------------
 | 
|---|
 | 235 |         r = j;           // (6)
 | 
|---|
 | 236 |         r = (int)s;      // (7)
 | 
|---|
 | 237 | 
 | 
|---|
 | 238 | By the expected aliasing syntax, (0a) and (0b) are initializing `r` and `s` as 
 | 
|---|
 | 239 | aliases for `i`.
 | 
|---|
 | 240 | For C compatibility, (1) has to be assignment; in general, any assignment to a 
 | 
|---|
 | 241 | non-reference type should be assignment, so (2) and (3) are as well. 
 | 
|---|
 | 242 | By types, (4) and (5) should have the same semantics, and the semantics of (6) 
 | 
|---|
 | 243 | and (7) should match as well. 
 | 
|---|
 | 244 | This suggests that (4) and (5) are reference re-bind, and (6) and (7) are an 
 | 
|---|
 | 245 | assignment to the referred variable; this makes the syntax to explicitly alias 
 | 
|---|
 | 246 | a local variable rather ugly (and inconsistent with the initialization 
 | 
|---|
 | 247 | syntax), as well as making it rather awkward to copy the value stored in one 
 | 
|---|
 | 248 | reference-type variable into another reference type variable (which is likely 
 | 
|---|
 | 249 | more painful in functions with by-reference parameters than with local 
 | 
|---|
 | 250 | variables of reference type).
 | 
|---|
 | 251 | 
 | 
|---|
 | 252 | Because of the aforementioned issues with overloading assignment as reference 
 | 
|---|
 | 253 | rebind, in addition to the fact that reference rebind should not be a 
 | 
|---|
 | 254 | user-overloadable operator (unlike assignment), I propose refererence rebind 
 | 
|---|
 | 255 | should have its own dedicated operator. 
 | 
|---|
 | 256 | 
 | 
|---|
 | 257 | The semantics and restrictions of `ref T` are effectively the semantics of an 
 | 
|---|
 | 258 | lvalue of type `T`, and by this analogy there should be a safe, qualifier 
 | 
|---|
 | 259 | dropping conversion from `ref const volatile restrict T` (and every other 
 | 
|---|
 | 260 | qualifier combination on the `T` in `ref T`) to `T`. 
 | 
|---|
 | 261 | With this conversion, the resolver may type most expressions that C would 
 | 
|---|
 | 262 | call "lvalue of type `T`" as `ref T`. 
 | 
|---|
 | 263 | There's also an obvious argument that lvalues of a (possibly-qualified) type 
 | 
|---|
 | 264 | `T` should be convertable to references of type `T`, where `T` is also 
 | 
|---|
 | 265 | so-qualified (e.g. lvalue `int` to `ref int`, lvalue `const char` to 
 | 
|---|
 | 266 | `ref const char`). 
 | 
|---|
 | 267 | By similar arguments to pointer types, qualifiers should be addable to the 
 | 
|---|
 | 268 | referred-to type of a reference (e.g. `ref int` to `ref const int`). 
 | 
|---|
 | 269 | As a note, since pointer arithmetic is explictly not defined on `ref T`, 
 | 
|---|
 | 270 | `restrict ref T` should be allowable and would have alias-analysis rules that 
 | 
|---|
 | 271 | are actually comprehensible to mere mortals.
 | 
|---|
 | 272 | 
 | 
|---|
 | 273 | Using pass-by-reference semantics for function calls should not put syntactic 
 | 
|---|
 | 274 | constraints on how the function is called; particularly, temporary values 
 | 
|---|
 | 275 | should be able to be passed by reference. 
 | 
|---|
 | 276 | The mechanism for this pass-by-reference would be to store the value of the 
 | 
|---|
 | 277 | temporary expression into a new unnamed temporary, and pass the reference of 
 | 
|---|
 | 278 | that temporary to the function.
 | 
|---|
 | 279 | As an example, the following code should all compile and run:
 | 
|---|
 | 280 | 
 | 
|---|
 | 281 |         void f(ref int x) { printf("%d\n", x++); }
 | 
|---|
 | 282 |         
 | 
|---|
 | 283 |         int i = 7, j = 11;
 | 
|---|
 | 284 |         const int answer = 42;
 | 
|---|
 | 285 |         
 | 
|---|
 | 286 |         f(i);      // (1)
 | 
|---|
 | 287 |         f(42);     // (2)
 | 
|---|
 | 288 |         f(i + j);  // (3)
 | 
|---|
 | 289 |         f(answer); // (4)
 | 
|---|
 | 290 | 
 | 
|---|
 | 291 | The semantics of (1) are just like C++'s, "7" is printed, and `i` has the 
 | 
|---|
 | 292 | value 8 afterward. 
 | 
|---|
 | 293 | For (2), "42" is printed, and the increment of the unnamed temporary to 43 is 
 | 
|---|
 | 294 | not visible to the caller; (3) behaves similarly, printing "19", but not 
 | 
|---|
 | 295 | changing `i` or `j`. 
 | 
|---|
 | 296 | (4) is a bit of an interesting case; we want to be able to support named 
 | 
|---|
 | 297 | constants like `answer` that can be used anywhere the constant expression 
 | 
|---|
 | 298 | they're replacing (like `42`) could go; in this sense, (4) and (2) should have 
 | 
|---|
 | 299 | the same semantics. 
 | 
|---|
 | 300 | However, we don't want the mutation to the `x` parameter to be visible in 
 | 
|---|
 | 301 | `answer` afterward, because `answer` is a constant, and thus shouldn't change. 
 | 
|---|
 | 302 | The solution to this is to allow chaining of the two `ref` conversions; 
 | 
|---|
 | 303 | `answer` has the type `ref const int`, which can be converted to `int` by the 
 | 
|---|
 | 304 | lvalue-to-rvalue conversion (which drops the qualifiers), then up to `ref int` 
 | 
|---|
 | 305 | by the temporary-producing rvalue-to-lvalue conversion. 
 | 
|---|
 | 306 | Thus, an unnamed temporary is inserted, initialized to `answer` (i.e. 42), 
 | 
|---|
 | 307 | mutated by `f`, then discarded; "42" is printed, just as in case (2), and 
 | 
|---|
 | 308 | `answer` still equals 42 after the call, because it was the temporary that was 
 | 
|---|
 | 309 | mutated, not `answer`.
 | 
|---|
 | 310 | It may be somewhat surprising to C++ programmers that `f(i)` mutates `i` while 
 | 
|---|
 | 311 | `f(answer)` does not mutate `answer` (though `f(answer)` would be illegal in 
 | 
|---|
 | 312 | C++, leading to the dreaded "const hell"), but the behaviour of this rule can 
 | 
|---|
 | 313 | be determined by examining local scope with the simple rule "non-`const` 
 | 
|---|
 | 314 | references to `const` variables produce temporaries", which aligns with 
 | 
|---|
 | 315 | programmer intuition that `const` variables cannot be mutated.
 | 
|---|
 | 316 | 
 | 
|---|
 | 317 | To bikeshed syntax for `ref T`, there are three basic options: language 
 | 
|---|
 | 318 | keywords (`lvalue T` is already in Cforall), compiler-supported "special" 
 | 
|---|
 | 319 | generic types (e.g. `ref(T)`), or sigils (`T&` is familiar to C++ 
 | 
|---|
 | 320 | programmers). 
 | 
|---|
 | 321 | Keyword or generic based approaches run the risk of name conflicts with 
 | 
|---|
 | 322 | existing code, while any sigil used would have to be carefully chosen to not 
 | 
|---|
 | 323 | create parsing conflicts.
 | 
|---|
 | 324 | 
 | 
|---|
 | 325 | **TODO** Consider arguments for move semantics and see if there is a 
 | 
|---|
 | 326 | compelling case for rvalue references.
 | 
|---|
| [3d1e617] | 327 | 
 | 
|---|
 | 328 | ### Conversion Operator Costs ###
 | 
|---|
 | 329 | Copy constructors, safe conversions, and unsafe conversions all have an 
 | 
|---|
 | 330 | associated conversion cost, calculated according to the algorithm below:
 | 
|---|
 | 331 | 
 | 
|---|
 | 332 | 1. Monomorphic copy constructors have a conversion cost of `(0, 0, 0, 0)`
 | 
|---|
 | 333 | 2. Monomorphic safe conversions have a conversion cost of `(0, 0, 1, 1)`
 | 
|---|
 | 334 | 3. Monomoprhic unsafe conversions have a conversion cost of `(1, 0, 0, 1)`
 | 
|---|
 | 335 | 4. Polymorphic conversion operators (or copy constructors) have a conversion 
 | 
|---|
 | 336 |    cost of `(0, 1, 0, 1)` plus the conversion cost of their monomorphic 
 | 
|---|
 | 337 |    equivalent and the sum of the conversion costs of all conversion operators 
 | 
|---|
 | 338 |    passed as assertion parameters, but where the fourth "count" element of the 
 | 
|---|
 | 339 |    cost tuple is fixed to `1`.
 | 
|---|
 | 340 | 
 | 
|---|
 | 341 | **TODO** Polymorphism cost may need to be reconsidered in the light of the 
 | 
|---|
 | 342 | thoughts on polymorphism below.
 | 
|---|
 | 343 | **TODO** You basically just want path-length in the conversion graph implied 
 | 
|---|
 | 344 | by the set of conversions; the only tricky question is whether or not you can 
 | 
|---|
 | 345 | account for "mixed" safe and unsafe conversions used to satisfy polymorphic 
 | 
|---|
 | 346 | constraints, whether a polymorphic conversion should cost more than a 
 | 
|---|
 | 347 | monomorphic one, and whether to account for non-conversion constraints in the 
 | 
|---|
 | 348 | polymorphism cost
 | 
|---|
 | 349 | 
 | 
|---|
 | 350 | ### Argument-Parameter Matching ###
 | 
|---|
 | 351 | Given a function `f` with an parameter list (after tuple flattening) 
 | 
|---|
 | 352 | `(T1 t1, T2 t2, ... Tn tn)`, and a function application 
 | 
|---|
 | 353 | `f(<e1>, <e2>, ... <em>)`, the cost of matching each argument to the 
 | 
|---|
 | 354 | appropriate parameter is calculated according to the algorithm below:
 | 
|---|
 | 355 | 
 | 
|---|
 | 356 | Given a parameter `t` of type `T` and an expression `<e>` from these lists, 
 | 
|---|
 | 357 | `<e>` will have a set of interpretations of types `E1, E2, ... Ek` with 
 | 
|---|
 | 358 | associated costs `(u1, p1, s1, c1), (u2, p2, s2, c2), ... (uk, pk, sk, ck)`. 
 | 
|---|
 | 359 | (If any `Ei` is a tuple type, replace it with its first flattened element for 
 | 
|---|
 | 360 | the purposes of this section.) 
 | 
|---|
 | 361 | 
 | 
|---|
 | 362 | The cost of matching the interpretation of `<e>` with type `Ei` to `t1` with 
 | 
|---|
 | 363 | type `T` is the sum of the interpretation cost `(ui, pi, si, ci)` and the 
 | 
|---|
 | 364 | conversion operator cost from `Ei` to `T`.
 | 
|---|
 | 365 | 
 | 
|---|
 | 366 | ### Object Initialization ###
 | 
|---|
 | 367 | The cost to initialize an object is calculated very similarly to 
 | 
|---|
 | 368 | argument-parameter matching, with a few modifications. 
 | 
|---|
 | 369 | Firstly, explicit constructors are included in the set of available 
 | 
|---|
 | 370 | conversions, with conversion cost `(0, 0, 0, 1)` plus associated polymorphic 
 | 
|---|
 | 371 | conversion costs (if applicable) and the _interpretation cost_ of the 
 | 
|---|
 | 372 | constructor, the sum of the argument-parameter matching costs for its 
 | 
|---|
 | 373 | parameters. 
 | 
|---|
 | 374 | Also, ties in overall cost (interpretation cost plus conversion cost) are 
 | 
|---|
 | 375 | broken by lowest conversion cost (i.e. of alternatives with the same overall 
 | 
|---|
 | 376 | cost, copy constructors are preferred to other explicit constructors, 
 | 
|---|
 | 377 | explicit constructors are preferred to safe conversions, which are preferred 
 | 
|---|
 | 378 | to unsafe conversions). 
 | 
|---|
 | 379 | An object initialization is properly typed if it has exactly one min-cost 
 | 
|---|
 | 380 | interpretation.
 | 
|---|
 | 381 | 
 | 
|---|
 | 382 | ### Explicit Casts ###
 | 
|---|
 | 383 | Explicit casts are handled similarly to object initialization. 
 | 
|---|
 | 384 | Copy constructors and other explicit constructors are not included in the set 
 | 
|---|
 | 385 | of possible conversions, though interpreting a cast as type ascription 
 | 
|---|
 | 386 | (`(T)e`, meaning the interpretation of `e` as type `T`) has conversion cost 
 | 
|---|
 | 387 | `(0, 0, 0, 0)`. 
 | 
|---|
 | 388 | Explicit conversion operators are also included in the set of possible 
 | 
|---|
 | 389 | conversions, with cost `(0, 0, 0, 1)` plus whatever polymorphic conversion 
 | 
|---|
 | 390 | costs are invoked. 
 | 
|---|
 | 391 | Unlike for explicit constructors and other functions, implicit conversions are 
 | 
|---|
 | 392 | never applied to the argument or return type of an explicit cast operator, so 
 | 
|---|
 | 393 | that the cast may be used more effectively as a method for the user programmer 
 | 
|---|
 | 394 | to guide type resolution.
 | 
|---|
 | 395 | 
 | 
|---|
 | 396 | ## Trait Satisfaction ##
 | 
|---|
 | 397 | A _trait_ consists of a list of _type variables_ along with a (possibly empty) 
 | 
|---|
 | 398 | set of _assertions_ on those variables. 
 | 
|---|
 | 399 | Assertions can take two forms, _variable assertions_ and the more common 
 | 
|---|
 | 400 | _function assertions_, as in the following example:
 | 
|---|
 | 401 | 
 | 
|---|
 | 402 |         trait a_trait(otype T, otype S) {
 | 
|---|
 | 403 |                 T a_variable_assertion;
 | 
|---|
 | 404 |                 S* another_variable_assertion;
 | 
|---|
 | 405 |                 S a_function_assertion( T* );
 | 
|---|
 | 406 |         };
 | 
|---|
 | 407 | 
 | 
|---|
 | 408 | Variable assertions enforce that a variable with the given name and type 
 | 
|---|
 | 409 | exists (the type is generally one of the type variables, or derived from one), 
 | 
|---|
 | 410 | while a function assertion enforces that a function with a 
 | 
|---|
 | 411 | _compatible signature_ to the provided function exists. 
 | 
|---|
 | 412 | 
 | 
|---|
 | 413 | To test if some list of types _satisfy_ the trait, the types are first _bound_ 
 | 
|---|
 | 414 | to the type variables, and then declarations to satisfy each assertion are 
 | 
|---|
 | 415 | sought out. 
 | 
|---|
 | 416 | Variable assertions require an exact match, because they are passed as object 
 | 
|---|
 | 417 | pointers, and there is no mechanism to employ conversion functions, while 
 | 
|---|
 | 418 | function assertions only require a function that can be wrapped to a 
 | 
|---|
 | 419 | compatible type; for example, the declarations below satisfy 
 | 
|---|
 | 420 | `a_trait(int, short)`:
 | 
|---|
 | 421 | 
 | 
|---|
 | 422 |         int a_variable_assertion;
 | 
|---|
 | 423 |         short* another_variable_assertion;
 | 
|---|
 | 424 |         char a_function_assertion( void* );
 | 
|---|
 | 425 |         // int* may be implicitly converted to void*, and char to short, so the 
 | 
|---|
 | 426 |         // above works 
 | 
|---|
 | 427 | 
 | 
|---|
 | 428 | Cforall Polymorphic functions have a _constraining trait_, denoted as follows: 
 | 
|---|
 | 429 | 
 | 
|---|
 | 430 |         forall(otype A, otype B | some_trait(A, B))
 | 
|---|
 | 431 | 
 | 
|---|
 | 432 | The trait may be anonymous, with the same syntax as a trait declaration, and 
 | 
|---|
 | 433 | may be unioned together using `|` or `,`.
 | 
|---|
 | 434 | 
 | 
|---|
 | 435 | **TODO** Consider including field assertions in the list of constraint types, 
 | 
|---|
 | 436 | also associated types and the appropriate matching type assertion.
 | 
|---|
 | 437 | 
 | 
|---|
 | 438 | ## Polymorphism Costs ##
 | 
|---|
 | 439 | The type resolver should prefer functions that are "less polymorphic" to 
 | 
|---|
 | 440 | functions that are "more polymorphic". 
 | 
|---|
 | 441 | Determining how to order functions by degree of polymorphism is somewhat less 
 | 
|---|
 | 442 | straightforward, though, as there are multiple axes of polymorphism and it is 
 | 
|---|
 | 443 | not always clear how they compose. 
 | 
|---|
 | 444 | The natural order for degree of polymorphism is a partial order, and this 
 | 
|---|
 | 445 | section includes some open questions on whether it is desirable or feasible to 
 | 
|---|
 | 446 | develop a tie-breaking strategy to impose a total order on the degree of 
 | 
|---|
 | 447 | polymorphism of functions. 
 | 
|---|
 | 448 | Helpfully, though, the degree of polymorphism is a property of functions 
 | 
|---|
 | 449 | rather than function calls, so any complicated graph structure or calculation 
 | 
|---|
 | 450 | representing a (partial) order over function degree of polymorphism can be 
 | 
|---|
 | 451 | calculated once and cached.
 | 
|---|
 | 452 | 
 | 
|---|
 | 453 | ### Function Parameters ###
 | 
|---|
 | 454 | All other things being equal, if a parameter of one function has a concrete 
 | 
|---|
 | 455 | type and the equivalent parameter of another function has a dynamic type, the 
 | 
|---|
 | 456 | first function is less polymorphic:
 | 
|---|
 | 457 | 
 | 
|---|
 | 458 |                         void f( int, int );  // (0) least polymorphic 
 | 
|---|
 | 459 |         forall(otype T) void f( T, int );    // (1a) more polymorphic than (0)
 | 
|---|
 | 460 |         forall(otype T) void f( int, T );    // (1b) more polymorphic than (0)
 | 
|---|
 | 461 |                                              //      incomparable with (1a)
 | 
|---|
 | 462 |         forall(otype T) void f( T, T );      // (2) more polymorphic than (1a/b)
 | 
|---|
 | 463 | 
 | 
|---|
 | 464 | This should extend to parameterized types (pointers and generic types) also: 
 | 
|---|
 | 465 | 
 | 
|---|
 | 466 |         forall(otype S) struct box { S val; };
 | 
|---|
 | 467 |         
 | 
|---|
 | 468 |         forall(otype T) void f( T, T* );       // (3) less polymorphic than (2)
 | 
|---|
 | 469 |         forall(otype T) void f( T, T** );      // (4) less polymorphic than (3)
 | 
|---|
 | 470 |         forall(otype T) void f( T, box(T) );   // (5) less polymorphic than (2)
 | 
|---|
 | 471 |                                                //     incomparable with (3)
 | 
|---|
 | 472 |         forall(otype T) void f( T, box(T*) );  // (6) less polymorphic than (5)
 | 
|---|
 | 473 | 
 | 
|---|
 | 474 | Every function in the group above is incomparable with (1a/b), but that's fine 
 | 
|---|
 | 475 | because an `int` isn't a pointer or a `box`, so the ambiguity shouldn't occur 
 | 
|---|
 | 476 | much in practice (unless there are safe or unsafe conversions defined between 
 | 
|---|
 | 477 | the possible argument types).
 | 
|---|
 | 478 | 
 | 
|---|
 | 479 | For degree of polymorphism from arguments, I think we should not distinguish 
 | 
|---|
 | 480 | between different type parameters, e.g. the following should be considered 
 | 
|---|
 | 481 | equally polymorphic:
 | 
|---|
 | 482 | 
 | 
|---|
 | 483 |         forall(otype T, otype S) void f( T, T, S );  // (7)
 | 
|---|
 | 484 |         forall(otype T, otype S) void f( S, T, T );  // (8)
 | 
|---|
 | 485 | 
 | 
|---|
 | 486 | However parameter lists are compared, parameters of multi-parameter generic 
 | 
|---|
 | 487 | types should ideally be treated as a recursive case, e.g. in the example 
 | 
|---|
 | 488 | below, (9) is less polymorphic than (10), which is less polymorphic than (11): 
 | 
|---|
 | 489 | 
 | 
|---|
 | 490 |         forall(otype T, otype S) struct pair { T x; S y; };
 | 
|---|
 | 491 |         
 | 
|---|
 | 492 |                         void f( pair(int, int) );  // (9)
 | 
|---|
 | 493 |         forall(otype T) void f( pair(T, int) );    // (10)
 | 
|---|
 | 494 |         forall(otype T) void f( pair(T, T) );      // (11)
 | 
|---|
 | 495 | 
 | 
|---|
 | 496 | Parameter comparison could possibly be made somewhat cheaper at loss of some 
 | 
|---|
 | 497 | precision by representing each parameter as a value from the natural numbers 
 | 
|---|
 | 498 | plus infinity, where infinity represents a monomorphic parameter and a finite 
 | 
|---|
 | 499 | number counts how many levels deep the shallowest type variable is, e.g. where 
 | 
|---|
 | 500 | `T` is a type variable, `int` would have value infinity, `T` would have value 
 | 
|---|
 | 501 | 0, `T*` would have value 1, `box(T)*` would have value 2, etc. 
 | 
|---|
 | 502 | Under this scheme, higher values represent less polymorphism. 
 | 
|---|
 | 503 | This makes the partial order on parameters a total order, so that many of the 
 | 
|---|
 | 504 | incomparable functions above compare equal, though that is perhaps a virtue. 
 | 
|---|
 | 505 | It also loses the ability to differentiate between some multi-parameter 
 | 
|---|
 | 506 | generic types, such as the parameters in (10) and (11), which would both be 
 | 
|---|
 | 507 | valued 1, losing the polymorphism distinction between them.
 | 
|---|
 | 508 | 
 | 
|---|
 | 509 | A variant of the above scheme would be to fix a maximum depth of polymorphic 
 | 
|---|
 | 510 | type variables (16 seems like a reasonable choice) at which a parameter would 
 | 
|---|
 | 511 | be considered to be effectively monomorphic, and to subtract the value  
 | 
|---|
 | 512 | described above from that maximum, clamping the result to a minimum of 0. 
 | 
|---|
 | 513 | Under this scheme, assuming a maximum value of 4, `int` has value 0, `T` has 
 | 
|---|
 | 514 | value 4, `T*` has value 3, `box(T)*` has value 2, and `box(T*)**` has value 0, 
 | 
|---|
 | 515 | the same as `int`. 
 | 
|---|
 | 516 | This can be quite succinctly represented, and summed without the presence of a 
 | 
|---|
 | 517 | single monomorphic parameter pushing the result to infinity, but does lose the 
 | 
|---|
 | 518 | ability to distinguish between very deeply structured polymorphic types. 
 | 
|---|
 | 519 | 
 | 
|---|
 | 520 | ### Parameter Lists ###
 | 
|---|
 | 521 | A partial order on function parameter lists can be produced by the 
 | 
|---|
 | 522 | product order of the partial orders on parameters described above. 
 | 
|---|
 | 523 | In more detail, this means that for two parameter lists with the same arity, 
 | 
|---|
 | 524 | if any pair of corresponding parameters are incomparable with respect to each 
 | 
|---|
 | 525 | other, the two parameter lists are incomparable; if in all pairs of 
 | 
|---|
 | 526 | corresponding parameters one list's parameter is always (less than or) equal 
 | 
|---|
 | 527 | to the other list's parameter than the first parameter list is (less than or) 
 | 
|---|
 | 528 | equal to the second parameter list; otherwise the lists are incomparable with 
 | 
|---|
 | 529 | respect to each other.
 | 
|---|
 | 530 | 
 | 
|---|
 | 531 | How to compare parameter lists of different arity is a somewhat open question. 
 | 
|---|
 | 532 | A simple, but perhaps somewhat unsatisfying, solution would be just to say 
 | 
|---|
 | 533 | that such lists are incomparable. 
 | 
|---|
 | 534 | The simplist approach to make them comparable is to say that, given two lists 
 | 
|---|
 | 535 | `(T1, T2, ... Tn)` and `(S1, S2, ... Sm)`, where `n <= m`, the parameter lists 
 | 
|---|
 | 536 | can be compared based on their shared prefix of `n` types. 
 | 
|---|
 | 537 | This approach breaks the transitivity property of the equivalence relation on 
 | 
|---|
 | 538 | the partial order, though, as seen below:
 | 
|---|
 | 539 | 
 | 
|---|
 | 540 |         forall(otype T) void f( T, int );       // (1a)
 | 
|---|
 | 541 |         forall(otype T) void f( T, int, int );  // (12)
 | 
|---|
 | 542 |         forall(otype T) void f( T, int, T );    // (13)
 | 
|---|
 | 543 | 
 | 
|---|
 | 544 | By this rule, (1a) is equally polymorphic to both (12) and (13), so by 
 | 
|---|
 | 545 | transitivity (12) and (13) should also be equally polymorphic, but that is not 
 | 
|---|
 | 546 | actually the case.
 | 
|---|
 | 547 | 
 | 
|---|
 | 548 | We can fix the rule by saying that `(T1 ... Tn)` can be compared to 
 | 
|---|
 | 549 | `(S1 ... Sm)` by _extending_ the list of `T`s to `m` types by inserting 
 | 
|---|
 | 550 | notional monomorphic parameters. 
 | 
|---|
 | 551 | In this case, (1a) and (12) are equally polymorphic, because (1a) gets 
 | 
|---|
 | 552 | extended with a monomorphic type that compares equal to (12)'s third `int` 
 | 
|---|
 | 553 | parameter, but (1a) is less polymorphic than (13), because its notional 
 | 
|---|
 | 554 | monomorphic third parameter is less polymorphic than (13)'s `T`. 
 | 
|---|
 | 555 | Essentially what this rule says is that any parameter list with more 
 | 
|---|
 | 556 | parameters is no less polymorphic than one with fewer. 
 | 
|---|
 | 557 | 
 | 
|---|
 | 558 | We could collapse this parameter list ordering to a succinct total order by 
 | 
|---|
 | 559 | simply taking the sum of the clamped parameter polymorphism counts, but this 
 | 
|---|
 | 560 | would again make most incomparable parameter lists compare equal, as well as 
 | 
|---|
 | 561 | having the potential for some unexpected results based on the (completely 
 | 
|---|
 | 562 | arbitrary) value chosen for "completely polymorphic". 
 | 
|---|
 | 563 | For instance, if we set 4 to be the maximum depth of polymorphism (as above), 
 | 
|---|
 | 564 | the following functions would be equally polymorphic, which is a somewhat 
 | 
|---|
 | 565 | unexpected result:
 | 
|---|
 | 566 | 
 | 
|---|
 | 567 |         forall(otype T) void g( T, T, T, int );    // 4 + 4 + 4 + 0 = 12
 | 
|---|
 | 568 |         forall(otype T) void g( T*, T*, T*, T* );  // 3 + 3 + 3 + 3 = 12
 | 
|---|
 | 569 | 
 | 
|---|
 | 570 | These functions would also be considered equally polymorphic:
 | 
|---|
 | 571 | 
 | 
|---|
 | 572 |         forall(otype T) void g( T, int );    // 4 + 0 = 4;
 | 
|---|
 | 573 |         forall(otype T) void g( T**, T** );  // 2 + 2 = 4;
 | 
|---|
 | 574 | 
 | 
|---|
 | 575 | This issue can be mitigated by choosing a larger maximum depth of 
 | 
|---|
 | 576 | polymorphism, but this scheme does have the distinct disadvantage of either 
 | 
|---|
 | 577 | specifying the (completely arbitrary) maximum depth as part of the language or 
 | 
|---|
 | 578 | allowing the compiler to refuse to accept otherwise well-typed deeply-nested 
 | 
|---|
 | 579 | polymorphic types.  
 | 
|---|
 | 580 | 
 | 
|---|
 | 581 | For purposes of determining polymorphism, the list of return types of a 
 | 
|---|
 | 582 | function should be treated like another parameter list, and combined with the 
 | 
|---|
 | 583 | degree of polymorphism from the parameter list in the same way that the 
 | 
|---|
 | 584 | parameters in the parameter list are combined. 
 | 
|---|
 | 585 | For instance, in the following, (14) is less polymorphic than (15) which is 
 | 
|---|
 | 586 | less polymorphic than (16):
 | 
|---|
 | 587 | 
 | 
|---|
 | 588 |         forall(otype T) int f( T );  // (14)
 | 
|---|
 | 589 |         forall(otype T) T*  f( T );  // (15)
 | 
|---|
 | 590 |         forall(otype T) T   f( T );  // (16)
 | 
|---|
 | 591 | 
 | 
|---|
 | 592 | ### Type Variables and Bounds ###
 | 
|---|
 | 593 | Degree of polymorphism doesn't solely depend on the parameter lists, though. 
 | 
|---|
 | 594 | Glen's thesis (4.4.4, p.89) gives an example that shows that it also depends 
 | 
|---|
 | 595 | on the number of type variables as well:
 | 
|---|
 | 596 | 
 | 
|---|
 | 597 |         forall(otype T)          void f( T, int );  // (1a) polymorphic
 | 
|---|
 | 598 |         forall(otype T)          void f( T, T );    // (2)  more polymorphic
 | 
|---|
 | 599 |         forall(otype T, otype S) void f( T, S );    // (17) most polymorphic
 | 
|---|
 | 600 | 
 | 
|---|
 | 601 | Clearly the `forall` type parameter list needs to factor into calculation of 
 | 
|---|
 | 602 | degree of polymorphism as well, as it's the only real differentiation between 
 | 
|---|
 | 603 | (2) and (17). 
 | 
|---|
 | 604 | The simplest way to include the type parameter list would be to simply count 
 | 
|---|
 | 605 | the type variables and say that functions with more type variables are more 
 | 
|---|
 | 606 | polymorphic.
 | 
|---|
 | 607 | 
 | 
|---|
 | 608 | However, it also seems natural that more-constrained type variables should be 
 | 
|---|
 | 609 | counted as "less polymorphic" than less-constrained type variables. 
 | 
|---|
 | 610 | This would allow our resolver to pick more specialized (and presumably more 
 | 
|---|
 | 611 | efficient) implementations of functions where one exists. 
 | 
|---|
 | 612 | For example:
 | 
|---|
 | 613 | 
 | 
|---|
 | 614 |         forall(otype T | { void g(T); }) T f( T );  // (18) less polymorphic
 | 
|---|
 | 615 |         forall(otype T)                  T f( T );  // (16) more polymorphic
 | 
|---|
 | 616 | 
 | 
|---|
 | 617 | We could account for this by counting the number of unique constraints and 
 | 
|---|
 | 618 | saying that functions with more constraints are less polymorphic.
 | 
|---|
 | 619 | 
 | 
|---|
 | 620 | That said, we do model the `forall` constraint list as a (possibly anonymous) 
 | 
|---|
 | 621 | _trait_, and say that each trait is a set of constraints, so we could 
 | 
|---|
 | 622 | presumably define a partial order over traits based on subset inclusion, and 
 | 
|---|
 | 623 | use this partial order instead of the weaker count of constraints to order the 
 | 
|---|
 | 624 | list of type parameters of a function, as below:
 | 
|---|
 | 625 | 
 | 
|---|
 | 626 |         trait has_g(otype T) { void g(T); };
 | 
|---|
 | 627 |         trait has_h(otype S) { void h(T); };
 | 
|---|
 | 628 |         trait has_gh(otype R | has_g(R) | has_h(R)) {};
 | 
|---|
 | 629 |         // has_gh is equivlent to { void g(R); void h(R); }
 | 
|---|
 | 630 |         
 | 
|---|
 | 631 |         forall(otype T | has_gh(T)) T f( T );  // (19) least polymorphic
 | 
|---|
 | 632 |         forall(otype T | has_g(T))  T f( T );  // (18) more polymorphic than (19)
 | 
|---|
 | 633 |         forall(otype T | has_h(T))  T f( T );  // (18b) more polymorphic than (19) 
 | 
|---|
 | 634 |                                                //       incomparable with (18)
 | 
|---|
 | 635 |         forall(otype T)             T f( T );  // (16) most polymorphic
 | 
|---|
 | 636 | 
 | 
|---|
 | 637 | The tricky bit with this is figuring out how to compare the constraint 
 | 
|---|
 | 638 | functions for equality up to type variable renaming; I suspect there's a known 
 | 
|---|
 | 639 | solution, but don't know what it is (perhaps some sort of unification 
 | 
|---|
 | 640 | calculation, though I hope there's a more lightweight option). 
 | 
|---|
 | 641 | We also should be able to take advantage of the programmer-provided trait 
 | 
|---|
 | 642 | subset information (like the constraint on `has_gh` in the example) to more 
 | 
|---|
 | 643 | efficiently generate the partial-order graph for traits, which should be able 
 | 
|---|
 | 644 | to be cached for efficiency.
 | 
|---|
 | 645 | 
 | 
|---|
 | 646 | Combining count of type variables with the (partial) order on the trait 
 | 
|---|
 | 647 | constraining those variables seems like it should be a fairly straightforward 
 | 
|---|
 | 648 | product ordering to me - one `forall` qualifier is (less than or) equal to 
 | 
|---|
 | 649 | another if it has both a (less than or) equal number of type variables and a 
 | 
|---|
 | 650 | (less than or) equal degree of polymorphism from its constraining trait; the 
 | 
|---|
 | 651 | two qualifiers are incomparable otherwise. 
 | 
|---|
 | 652 | If an easier-to-calculate total ordering is desired, it might be acceptable to 
 | 
|---|
 | 653 | use the number of type variables, with ties broken by number of constraints.
 | 
|---|
 | 654 | 
 | 
|---|
 | 655 | Similarly, to combine the (partial) orders on parameter and return lists with 
 | 
|---|
 | 656 | the (partial) order on `forall` qualifiers, a product ordering seems like the 
 | 
|---|
 | 657 | reasonable choice, though if we wanted a total order a reasonable choice would 
 | 
|---|
 | 658 | be to use whatever method we use to combine parameter costs into parameter 
 | 
|---|
 | 659 | lists to combine the costs for the parameter and return lists, then break ties 
 | 
|---|
 | 660 | by the order on the `forall` qualifiers.
 | 
|---|
 | 661 | 
 | 
|---|
 | 662 | ## Expression Costs ##
 | 
|---|
 | 663 | 
 | 
|---|
 | 664 | ### Variable Expressions ###
 | 
|---|
 | 665 | Variables may be overloaded; that is, there may be multiple distinct variables 
 | 
|---|
 | 666 | with the same name so long as each variable has a distinct type. 
 | 
|---|
 | 667 | The variable expression `x` has one zero-cost interpretation as type `T` for 
 | 
|---|
 | 668 | each variable `T x` in scope.
 | 
|---|
 | 669 | 
 | 
|---|
 | 670 | ### Member Selection Expressions ###
 | 
|---|
 | 671 | For every interpretation `I` of `e` which has a struct or union type `S`, 
 | 
|---|
 | 672 | `e.y` has an interpretation of type `T` for each member `T y` of `S`, with the 
 | 
|---|
 | 673 | same cost as `I`. 
 | 
|---|
 | 674 | Note that there may be more than one member of `S` with the same name, as per 
 | 
|---|
 | 675 | Cforall's usual overloading rules.
 | 
|---|
 | 676 | The indirect member expression `e->y` is desugared to `(*e).y` and interpreted 
 | 
|---|
 | 677 | analogously.
 | 
|---|
 | 678 | 
 | 
|---|
 | 679 | **TODO** Consider allowing `e.y` to be interpreted as `e->y` if no 
 | 
|---|
 | 680 | interpretations as `e.y` exist.
 | 
|---|
 | 681 | 
 | 
|---|
 | 682 | ### Address Expressions ###
 | 
|---|
 | 683 | Address expressions `&e` have an interpretation for each interpretation `I` of 
 | 
|---|
 | 684 | `e` that is an lvalue of type `T`, with the same cost as `I` and type `T*`. 
 | 
|---|
 | 685 | Lvalues result from variable expressions, member selection expressions, or 
 | 
|---|
 | 686 | application of functions returning an lvalue-qualified type. 
 | 
|---|
 | 687 | Note that the dereference operator is overloadable, so the rules for its 
 | 
|---|
 | 688 | resolution follow those for function application below.
 | 
|---|
 | 689 | 
 | 
|---|
 | 690 | **TODO** Consider allowing conversion-to-lvalue so that, e.g., `&42` spawns a 
 | 
|---|
 | 691 | new temporary holding `42` and takes its address.
 | 
|---|
 | 692 | 
 | 
|---|
 | 693 | ### Boolean-context Expressions ###
 | 
|---|
 | 694 | C has a number of "boolean contexts", where expressions are assigned a truth 
 | 
|---|
 | 695 | value; these include both arguments to the short-circuiting `&&` and `||` 
 | 
|---|
 | 696 | operators, as well as the conditional expressions in `if` and `while` 
 | 
|---|
 | 697 | statements, the middle expression in `for` statements, and the first argument 
 | 
|---|
 | 698 | to the `?:` ternary conditional operator. 
 | 
|---|
 | 699 | In all these contexts, C interprets `0` (which is both an integer and a null 
 | 
|---|
 | 700 | pointer literal) as false, and all other integer or pointer values as true. 
 | 
|---|
 | 701 | In this spirit, Cforall allows other types to be considered "truthy" if they 
 | 
|---|
 | 702 | support the following de-sugaring in a conditional context (see notes on 
 | 
|---|
 | 703 | interpretation of literal `0` below):
 | 
|---|
 | 704 | 
 | 
|---|
 | 705 |         x => ((int)( x != 0 ))
 | 
|---|
 | 706 | 
 | 
|---|
 | 707 | ### Literal Expressions ###
 | 
|---|
 | 708 | Literal expressions (e.g. 42, 'c', 3.14, "Hello, world!") have one 
 | 
|---|
 | 709 | zero-cost interpretation with the same type the expression would have in C, 
 | 
|---|
 | 710 | with three exceptions:
 | 
|---|
 | 711 | 
 | 
|---|
 | 712 | Character literals like 'x' are typed as `char` in Cforall, not `int` as in C. 
 | 
|---|
 | 713 | This change breaks very little C code (primarily `sizeof 'x'`; the implicit 
 | 
|---|
 | 714 | conversion from `int` to `char` and lack of overloading handle most other 
 | 
|---|
 | 715 | expressions), matches the behaviour of C++, and is more compatible with 
 | 
|---|
 | 716 | programmer intuition.
 | 
|---|
 | 717 | 
 | 
|---|
 | 718 | The literals `0` and `1` are also treated specially by Cforall, due to their 
 | 
|---|
 | 719 | potential uses in operator overloading. 
 | 
|---|
 | 720 | Earlier versions of Cforall allowed `0` and `1` to be variable names, allowing 
 | 
|---|
 | 721 | multiple interpretations of them according to the existing variable 
 | 
|---|
 | 722 | overloading rules, with the following declarations in the prelude:
 | 
|---|
 | 723 | 
 | 
|---|
 | 724 |         const int 0, 1;
 | 
|---|
 | 725 |         forall ( dtype DT ) const DT * const    0;
 | 
|---|
 | 726 |         forall ( ftype FT ) FT * const          0;
 | 
|---|
 | 727 | 
 | 
|---|
 | 728 | This did, however, create some backward-compatibility problems and potential 
 | 
|---|
 | 729 | performance issues, and works poorly for generic types. To start with, this 
 | 
|---|
 | 730 | (entirely legal C) code snippet doesn't compile in Cforall:
 | 
|---|
 | 731 | 
 | 
|---|
 | 732 |         if ( 0 ) {}
 | 
|---|
 | 733 | 
 | 
|---|
 | 734 | It desugars to `if ( (int)(0 != 0) ) {}`, and since both `int` and 
 | 
|---|
 | 735 | `forall(dtype DT) DT*` have a != operator which returns `int` the resolver can 
 | 
|---|
 | 736 | not choose which `0` variable to take, because they're both exact matches.
 | 
|---|
 | 737 | 
 | 
|---|
 | 738 | The general != computation may also be less efficient than a check for a zero 
 | 
|---|
 | 739 | value; take the following example of a rational type:
 | 
|---|
 | 740 | 
 | 
|---|
 | 741 |         struct rational { int32_t num, int32_t den };
 | 
|---|
 | 742 |         rational 0 = { 0, 1 };
 | 
|---|
 | 743 |         
 | 
|---|
 | 744 |         int ?!=? (rational a, rational b) {
 | 
|---|
 | 745 |                 return ((int64_t)a.num)*b.den != ((int64_t)b.num)*a.den;
 | 
|---|
 | 746 |         }
 | 
|---|
 | 747 |         
 | 
|---|
 | 748 |         int not_zero (rational a) { return a.num != 0; }
 | 
|---|
 | 749 | 
 | 
|---|
 | 750 | To check if two rationals are equal we need to do a pair of multiplications to 
 | 
|---|
 | 751 | normalize them (the casts in the example are to prevent overflow), but to 
 | 
|---|
 | 752 | check if a rational is non-zero we just need to check its numerator, a more 
 | 
|---|
 | 753 | efficient operation.
 | 
|---|
 | 754 | 
 | 
|---|
 | 755 | Finally, though polymorphic null-pointer variables can be meaningfully 
 | 
|---|
 | 756 | defined, most other polymorphic variables cannot be, which makes it difficult 
 | 
|---|
 | 757 | to make generic types "truthy" using the existing system:
 | 
|---|
 | 758 | 
 | 
|---|
 | 759 |         forall(otype T) struct pair { T x; T y; };
 | 
|---|
 | 760 |         forall(otype T | { T 0; }) pair(T) 0 = { 0, 0 };
 | 
|---|
 | 761 | 
 | 
|---|
 | 762 | Now, it seems natural enough to want to define the zero for this pair type as 
 | 
|---|
 | 763 | a pair of the zero values of its element type (if they're defined). 
 | 
|---|
 | 764 | The declaration of `pair(T) 0` above is actually illegal though, as there is 
 | 
|---|
 | 765 | no way to represent the zero values of an infinite number of types in the 
 | 
|---|
 | 766 | single memory location available for this polymorphic variable - the 
 | 
|---|
 | 767 | polymorphic null-pointer variables defined in the prelude are legal, but that 
 | 
|---|
 | 768 | is only because all pointers are the same size and the single zero value is a 
 | 
|---|
 | 769 | legal value of all pointer types simultaneously; null pointer is, however, 
 | 
|---|
 | 770 | somewhat unique in this respect.
 | 
|---|
 | 771 | 
 | 
|---|
 | 772 | The technical explanation for the problems with polymorphic zero is that `0` 
 | 
|---|
 | 773 | is really a rvalue, not a lvalue - an expression, not an object. 
 | 
|---|
 | 774 | Drawing from this, the solution we propose is to give `0` a new built-in type, 
 | 
|---|
 | 775 | `_zero_t` (name open to bikeshedding), and similarly give `1` the new built-in 
 | 
|---|
 | 776 | type `_unit_t`. 
 | 
|---|
 | 777 | If the prelude defines != over `_zero_t` this solves the `if ( 0 )` problem, 
 | 
|---|
 | 778 | because now the unambiguous best interpretation of `0 != 0` is to read them 
 | 
|---|
 | 779 | both as `_zero_t` (and say that this expression is false). 
 | 
|---|
 | 780 | Backwards compatibility with C can be served by defining conversions in the 
 | 
|---|
 | 781 | prelude from `_zero_t` and `_unit_t` to `int` and the appropriate pointer 
 | 
|---|
 | 782 | types, as below:
 | 
|---|
 | 783 | 
 | 
|---|
 | 784 |         // int 0;
 | 
|---|
 | 785 |         forall(otype T | { void ?{safe}(T*, int); }) void ?{safe} (T*, _zero_t);
 | 
|---|
 | 786 |         forall(otype T | { void ?{unsafe}(T*, int); }) void ?{unsafe} (T*, _zero_t);
 | 
|---|
 | 787 |         
 | 
|---|
 | 788 |         // int 1;
 | 
|---|
 | 789 |         forall(otype T | { void ?{safe}(T*, int); }) void ?{safe} (T*, _unit_t);
 | 
|---|
 | 790 |         forall(otype T | { void ?{unsafe}(T*, int); }) void ?{unsafe} (T*, _unit_t);
 | 
|---|
 | 791 |         
 | 
|---|
 | 792 |         // forall(dtype DT) const DT* 0;
 | 
|---|
 | 793 |         forall(dtype DT) void ?{safe}(const DT**, _zero_t);
 | 
|---|
 | 794 |         // forall(ftype FT) FT* 0;
 | 
|---|
 | 795 |         forall(ftype FT) void ?{safe}(FT**, _zero_t);
 | 
|---|
 | 796 | 
 | 
|---|
 | 797 | Further, with this change, instead of making `0` and `1` overloadable 
 | 
|---|
 | 798 | variables, we can instead allow user-defined constructors (or, more flexibly, 
 | 
|---|
 | 799 | safe conversions) from `_zero_t`, as below:
 | 
|---|
 | 800 | 
 | 
|---|
 | 801 |         // rational 0 = { 0, 1 };
 | 
|---|
 | 802 |         void ?{safe} (rational *this, _zero_t) { this->num = 0; this->den = 1; }
 | 
|---|
 | 803 | 
 | 
|---|
 | 804 | Note that we don't need to name the `_zero_t` parameter to this constructor, 
 | 
|---|
 | 805 | because its only possible value is a literal zero. 
 | 
|---|
 | 806 | This one line allows `0` to be used anywhere a `rational` is required, as well 
 | 
|---|
 | 807 | as enabling the same use of rationals in boolean contexts as above (by 
 | 
|---|
 | 808 | interpreting the `0` in the desguraring to be a rational by this conversion). 
 | 
|---|
 | 809 | Furthermore, while defining a conversion function from literal zero to 
 | 
|---|
 | 810 | `rational` makes rational a "truthy" type able to be used in a boolean 
 | 
|---|
 | 811 | context, we can optionally further optimize the truth decision on rationals as 
 | 
|---|
 | 812 | follows:
 | 
|---|
 | 813 | 
 | 
|---|
 | 814 |         int ?!=? (rational a, _zero_t) { return a.num != 0; }
 | 
|---|
 | 815 | 
 | 
|---|
 | 816 | This comparison function will be chosen in preference to the more general 
 | 
|---|
 | 817 | rational comparison function for comparisons against literal zero (like in 
 | 
|---|
 | 818 | boolean contexts) because it doesn't require a conversion on the `0` argument. 
 | 
|---|
 | 819 | Functions of the form `int ?!=? (T, _zero_t)` can acutally be used in general 
 | 
|---|
 | 820 | to make a type `T` truthy without making `0` a value which can convert to that 
 | 
|---|
 | 821 | type, a capability not available in the current design.
 | 
|---|
 | 822 | 
 | 
|---|
 | 823 | This design also solves the problem of polymorphic zero for generic types, as 
 | 
|---|
 | 824 | in the following example:
 | 
|---|
 | 825 | 
 | 
|---|
 | 826 |         // ERROR: forall(otype T | { T 0; }) pair(T) 0 = { 0, 0 };
 | 
|---|
 | 827 |         forall(otype T | { T 0; }) void ?{safe} (pair(T) *this, _zero_t) {
 | 
|---|
 | 828 |                 this->x = 0; this->y = 0;
 | 
|---|
 | 829 |         }
 | 
|---|
 | 830 | 
 | 
|---|
 | 831 | The polymorphic variable declaration didn't work, but this constructor is 
 | 
|---|
 | 832 | perfectly legal and has the desired semantics.
 | 
|---|
 | 833 | 
 | 
|---|
| [d5f1cfc] | 834 | We can assert that `T` can be used in a boolean context as follows: 
 | 
|---|
 | 835 | 
 | 
|---|
 | 836 |         `forall(otype T | { int ?!=?(T, _zero_t); })`
 | 
|---|
 | 837 |  
 | 
|---|
 | 838 | Since the C standard (6.5.16.1.1) specifically states that pointers can be 
 | 
|---|
 | 839 | assigned into `_Bool` variables (and implies that other artithmetic types can 
 | 
|---|
 | 840 | be assigned into `_Bool` variables), it seems natural to say that assignment 
 | 
|---|
 | 841 | into a `_Bool` variable effectively constitutes a boolean context. 
 | 
|---|
 | 842 | To allow this interpretation, I propose including the following function (or 
 | 
|---|
 | 843 | its effective equivalent) in the prelude:
 | 
|---|
 | 844 | 
 | 
|---|
 | 845 |         forall(otype T | { int ?!=?(T, _zero_t); })
 | 
|---|
 | 846 |         void ?{safe}( _Bool *this, T that ) { *this = that != 0; }
 | 
|---|
 | 847 | 
 | 
|---|
 | 848 | Note that this conversion is not transitive; that is, for `t` a variable of 
 | 
|---|
 | 849 | some "truthy" type `T`, `(_Bool)t;` would use this conversion (in the absence 
 | 
|---|
 | 850 | of a lower-cost one), `(int)t;` would not use this conversion (and in fact 
 | 
|---|
 | 851 | would not be legal in the absence of another valid way to convert a `T` to an 
 | 
|---|
 | 852 | `int`), but `(int)(_Bool)t;` could legally use this conversion.
 | 
|---|
 | 853 | 
 | 
|---|
| [3d1e617] | 854 | Similarly giving literal `1` the special type `_unit_t` allows for more 
 | 
|---|
 | 855 | concise and consistent specification of the increment and decrement operators, 
 | 
|---|
 | 856 | using the following de-sugaring:
 | 
|---|
 | 857 | 
 | 
|---|
 | 858 |         ++i => i += 1
 | 
|---|
 | 859 |         i++ => (tmp = i, i += 1, tmp)
 | 
|---|
 | 860 |         --i => i -= 1
 | 
|---|
 | 861 |         i-- => (tmp = i, i -= 1, tmp)
 | 
|---|
 | 862 | 
 | 
|---|
 | 863 | In the examples above, `tmp` is a fresh temporary with its type inferred from 
 | 
|---|
 | 864 | the return type of `i += 1`. 
 | 
|---|
 | 865 | Under this proposal, defining a conversion from `_unit_t` to `T` and a 
 | 
|---|
 | 866 | `lvalue T ?+=? (T*, T)` provides both the pre- and post-increment operators 
 | 
|---|
 | 867 | for free in a consistent fashion (similarly for -= and the decrement 
 | 
|---|
 | 868 | operators).
 | 
|---|
 | 869 | If a meaningful `1` cannot be defined for a type, both increment operators can 
 | 
|---|
 | 870 | still be defined with the signature `lvalue T ?+=? (T*, _unit_t)`. 
 | 
|---|
 | 871 | Similarly, if scalar addition can be performed on a type more efficiently than 
 | 
|---|
 | 872 | by repeated increment, `lvalue T ?+=? (T*, int)` will not only define the 
 | 
|---|
 | 873 | addition operator, it will simultaneously define consistent implementations of 
 | 
|---|
 | 874 | both increment operators (this can also be accomplished by defining a 
 | 
|---|
 | 875 | conversion from `int` to `T` and an addition operator `lvalue T ?+=?(T*, T)`).
 | 
|---|
 | 876 | 
 | 
|---|
 | 877 | To allow functions of the form `lvalue T ?+=? (T*, int)` to satisfy "has an 
 | 
|---|
 | 878 | increment operator" assertions of the form `lvalue T ?+=? (T*, _unit_t)`, 
 | 
|---|
 | 879 | we also define a non-transitive unsafe conversion from `_Bool` (allowable 
 | 
|---|
 | 880 | values `0` and `1`) to `_unit_t` (and `_zero_t`) as follows:
 | 
|---|
 | 881 | 
 | 
|---|
 | 882 |         void ?{unsafe} (_unit_t*, _Bool) {}
 | 
|---|
 | 883 | 
 | 
|---|
 | 884 | As a note, the desugaring of post-increment above is possibly even more 
 | 
|---|
 | 885 | efficient than that of C++ - in C++, the copy to the temporary may be hidden 
 | 
|---|
 | 886 | in a separately-compiled module where it can't be elided in cases where it is 
 | 
|---|
 | 887 | not used, whereas this approach for Cforall always gives the compiler the 
 | 
|---|
 | 888 | opportunity to optimize out the temporary when it is not needed. 
 | 
|---|
 | 889 | Furthermore, one could imagine a post-increment operator that returned some 
 | 
|---|
 | 890 | type `T2` that was implicitly convertable to `T` but less work than a full 
 | 
|---|
 | 891 | copy of `T` to create (this seems like an absurdly niche case) - since the 
 | 
|---|
 | 892 | type of `tmp` is inferred from the return type of `i += 1`, you could set up 
 | 
|---|
 | 893 | functions with the following signatures to enable an equivalent pattern in 
 | 
|---|
 | 894 | Cforall:
 | 
|---|
 | 895 | 
 | 
|---|
 | 896 |         lvalue T2 ?+=? (T*, _unit_t); // increment operator returns T2
 | 
|---|
 | 897 |         void ?{} (T2*, T);            // initialize T2 from T for use in `tmp = i`
 | 
|---|
 | 898 |         void ?{safe} (T*, T2);        // allow T2 to be used as a T when needed to 
 | 
|---|
 | 899 |                                       // preserve expected semantics of T x = y++;
 | 
|---|
 | 900 | 
 | 
|---|
 | 901 | **TODO** Look in C spec for literal type interprations.
 | 
|---|
 | 902 | **TODO** Write up proposal for wider range of literal types, put in appendix
 | 
|---|
 | 903 | 
 | 
|---|
 | 904 | ### Initialization and Cast Expressions ###
 | 
|---|
 | 905 | An initialization expression `T x = e` has one interpretation for each 
 | 
|---|
 | 906 | interpretation `I` of `e` with type `S` which is convertable to `T`. 
 | 
|---|
 | 907 | The cost of the interpretation is the cost of `I` plus the conversion cost 
 | 
|---|
 | 908 | from `S` to `T`.
 | 
|---|
 | 909 | A cast expression `(T)e` is interpreted as hoisting initialization of a 
 | 
|---|
 | 910 | temporary variable `T tmp = e` out of the current expression, then replacing 
 | 
|---|
 | 911 | `(T)e` by the new temporary `tmp`.
 | 
|---|
 | 912 | 
 | 
|---|
 | 913 | ### Assignment Expressions ###
 | 
|---|
 | 914 | An assignment expression `e = f` desugars to `(?=?(&e, f), e)`, and is then 
 | 
|---|
 | 915 | interpreted according to the usual rules for function application and comma 
 | 
|---|
 | 916 | expressions. 
 | 
|---|
 | 917 | Operator-assignment expressions like `e += f` desugar similarly as 
 | 
|---|
 | 918 | `(?+=?(&e, f), e)`.
 | 
|---|
 | 919 | 
 | 
|---|
 | 920 | ### Function Application Expressions ###
 | 
|---|
 | 921 | Every _compatible function_ and satisfying interpretation of its arguments and 
 | 
|---|
 | 922 | polymorphic variable bindings produces one intepretation for the function 
 | 
|---|
 | 923 | application expression. 
 | 
|---|
 | 924 | Broadly speaking, the resolution cost of a function application is the sum of 
 | 
|---|
 | 925 | the cost of the interpretations of all arguments, the cost of all conversions 
 | 
|---|
 | 926 | to make those argument interpretations match the parameter types, and the 
 | 
|---|
 | 927 | binding cost of any of the function's polymorphic type parameters.
 | 
|---|
 | 928 | 
 | 
|---|
 | 929 | **TODO** Work out binding cost in more detail.
 | 
|---|
 | 930 | **TODO** Address whether "incomparably polymorphic" should be treated as 
 | 
|---|
 | 931 | "equally polymorphic" and be disambiguated by count of (safe) conversions.
 | 
|---|
 | 932 | **TODO** Think about what polymorphic return types mean in terms of late 
 | 
|---|
 | 933 | binding.
 | 
|---|
 | 934 | **TODO** Consider if "f is less polymorphic than g" can mean exactly "f 
 | 
|---|
 | 935 | specializes g"; if we don't consider the assertion parameters (except perhaps 
 | 
|---|
 | 936 | by count) and make polymorphic variables bind exactly (rather than after 
 | 
|---|
 | 937 | implicit conversions) this should actually be pre-computable.
 | 
|---|
 | 938 | **TODO** Add "deletable" functions - take Thierry's suggestion that a deleted 
 | 
|---|
 | 939 | function declaration is costed out by the resolver in the same way that any 
 | 
|---|
 | 940 | other function declaration is costed; if the deleted declaration is the unique 
 | 
|---|
 | 941 | min-cost resolution refuse to type the expression, if it is tied for min-cost 
 | 
|---|
 | 942 | then take the non-deleted alternative, and of two equivalent-cost deleted 
 | 
|---|
 | 943 | interpretations with the same return type pick one arbitrarily rather than 
 | 
|---|
| [d5f1cfc] | 944 | producing an ambiguous resolution. This would also be useful for forbidding 
 | 
|---|
 | 945 | pointer-to-floating-point explicit conversions (C11, 6.5.4.4).
 | 
|---|
 | 946 | **TODO** Cover default parameters, maybe named parameters (see "named 
 | 
|---|
 | 947 | arguments" thread of 11 March 2016)
 | 
|---|
 | 948 |  
 | 
|---|
| [3d1e617] | 949 | 
 | 
|---|
 | 950 | ### Sizeof, Alignof & Offsetof Expressions ###
 | 
|---|
 | 951 | `sizeof`, `alignof`, and `offsetof` expressions have at most a single 
 | 
|---|
 | 952 | interpretation, of type `size_t`. 
 | 
|---|
 | 953 | `sizeof` and `alignof` expressions take either a type or an expression as a  
 | 
|---|
 | 954 | an argument; if the argument is a type, it must be a complete type which is 
 | 
|---|
 | 955 | not a function type, if an expression, the expression must have a single 
 | 
|---|
 | 956 | interpretation, the type of which conforms to the same rules. 
 | 
|---|
 | 957 | `offsetof` takes two arguments, a type and a member name; the type must be 
 | 
|---|
 | 958 | a complete structure or union type, and the second argument must name a member 
 | 
|---|
 | 959 | of that type.
 | 
|---|
 | 960 | 
 | 
|---|
 | 961 | ### Comma Expressions ###
 | 
|---|
 | 962 | A comma expression `x, y` resolves `x` as if it had been cast to `void`, and 
 | 
|---|
 | 963 | then, if there is a unique interpretation `I` of `x`, has one interpretation 
 | 
|---|
 | 964 | for each interpretation `J` of `y` with the same type as `J` costing the sum 
 | 
|---|
 | 965 | of the costs of `I` and `J`.
 | 
|---|
 | 966 | 
 | 
|---|
| [d5f1cfc] | 967 | ### Index Expressions ###
 | 
|---|
 | 968 | **TODO** Consider adding polymorphic function in prelude for this, as per 
 | 
|---|
 | 969 | 6.5.2.1.2 in the C standard:
 | 
|---|
 | 970 | 
 | 
|---|
 | 971 |         forall(otype T, otype I, otype R, otype E | { R ?+?(T, I); lvalue E *?(R); })
 | 
|---|
 | 972 |         lvalue E ?[?](T a, I i) { return *(a + i); }
 | 
|---|
 | 973 | 
 | 
|---|
 | 974 | I think this isn't actually a good idea, because the cases for it are niche, 
 | 
|---|
 | 975 | mostly odd tricks like `0[p]` as an alternate syntax for dereferencing a 
 | 
|---|
 | 976 | pointer `p`, and adding it to the prelude would slow down resolution of 
 | 
|---|
 | 977 | every index expression just a bit. Our existing prelude includes an indexing 
 | 
|---|
 | 978 | operator `forall(otype T) lvalue T ?[?](ptrdiff_t, T*)`, plus qualified 
 | 
|---|
 | 979 | variants, which should satisfy C source-compatibility without propegating this 
 | 
|---|
 | 980 | silly desugaring further.
 | 
|---|
 | 981 | 
 | 
|---|
| [3d1e617] | 982 | #### Compatible Functions ####
 | 
|---|
 | 983 | **TODO** This subsection is very much a work in progress and has multiple open 
 | 
|---|
 | 984 | design questions.
 | 
|---|
 | 985 | 
 | 
|---|
 | 986 | A _compatible function_ for an application expression is a visible function 
 | 
|---|
 | 987 | declaration with the same name as the application expression and parameter 
 | 
|---|
 | 988 | types that can be converted to from the argument types. 
 | 
|---|
 | 989 | Function pointers and variables of types with the `?()` function call operator 
 | 
|---|
 | 990 | overloaded may also serve as function declarations for purposes of 
 | 
|---|
 | 991 | compatibility. 
 | 
|---|
 | 992 | 
 | 
|---|
 | 993 | For monomorphic parameters of a function declaration, the declaration is a 
 | 
|---|
 | 994 | compatible function if there is an argument interpretation that is either an 
 | 
|---|
 | 995 | exact match, or has a safe or unsafe implicit conversion that can be used to 
 | 
|---|
 | 996 | reach the parameter type; for example: 
 | 
|---|
 | 997 | 
 | 
|---|
 | 998 |         void f(int);
 | 
|---|
 | 999 |         
 | 
|---|
 | 1000 |         f(42);        // compatible; exact match to int type
 | 
|---|
 | 1001 |         f('x');       // compatible; safe conversion from char => int
 | 
|---|
 | 1002 |         f(3.14);      // compatible; unsafe conversion from double => int
 | 
|---|
 | 1003 |         f((void*)0);  // not compatible; no implicit conversion from void* => int
 | 
|---|
 | 1004 | 
 | 
|---|
 | 1005 | Per Richard[*], function assertion satisfaction involves recursively searching 
 | 
|---|
 | 1006 | for compatible functions, not an exact match on the function types (I don't 
 | 
|---|
 | 1007 | believe the current Cforall resolver handles this properly); to extend the 
 | 
|---|
 | 1008 | previous example:
 | 
|---|
 | 1009 | 
 | 
|---|
 | 1010 |         forall(otype T | { void f(T); }) void g(T);
 | 
|---|
 | 1011 |         
 | 
|---|
 | 1012 |         g(42);    // binds T = int, takes f(int) by exact match
 | 
|---|
 | 1013 |         g('x');   // binds T = char, takes f(int) by conversion
 | 
|---|
 | 1014 |         g(3.14);  // binds T = double, takes f(int) by conversion
 | 
|---|
 | 1015 | 
 | 
|---|
 | 1016 | [*] Bilson, s.2.1.3, p.26-27, "Assertion arguments are found by searching the 
 | 
|---|
 | 1017 | accessible scopes for definitions corresponding to assertion names, and 
 | 
|---|
 | 1018 | choosing the ones whose types correspond *most closely* to the assertion 
 | 
|---|
 | 1019 | types." (emphasis mine)
 | 
|---|
 | 1020 | 
 | 
|---|
 | 1021 | There are three approaches we could take to binding type variables: type 
 | 
|---|
 | 1022 | variables must bind to argument types exactly, each type variable must bind 
 | 
|---|
 | 1023 | exactly to at least one argument, or type variables may bind to any type which 
 | 
|---|
 | 1024 | all corresponding arguments can implicitly convert to; I'll provide some 
 | 
|---|
 | 1025 | possible motivation for each approach.
 | 
|---|
 | 1026 | 
 | 
|---|
 | 1027 | There are two main arguments for the more restrictive binding schemes; the 
 | 
|---|
 | 1028 | first is that the built-in implicit conversions in C between `void*` and `T*` 
 | 
|---|
 | 1029 | for any type `T` can lead to unexpectedly type-unsafe behaviour in a more 
 | 
|---|
 | 1030 | permissive binding scheme, for example:
 | 
|---|
 | 1031 | 
 | 
|---|
 | 1032 |         forall(dtype T) T* id(T *p) { return p; }
 | 
|---|
 | 1033 |         
 | 
|---|
 | 1034 |         int main() {
 | 
|---|
 | 1035 |                 int *p = 0;
 | 
|---|
 | 1036 |                 char *c = id(p);
 | 
|---|
 | 1037 |         }
 | 
|---|
 | 1038 | 
 | 
|---|
 | 1039 | This code compiles in CFA today, and it works because the extra function 
 | 
|---|
 | 1040 | wrapper `id` provides a level of indirection that allows the non-chaining 
 | 
|---|
 | 1041 | implicit conversions from `int*` => `void*` and `void*` => `char*` to chain. 
 | 
|---|
 | 1042 | The resolver types the last line with `T` bound to `void` as follows:
 | 
|---|
 | 1043 | 
 | 
|---|
 | 1044 |         char *c = (char*)id( (void*)p );
 | 
|---|
 | 1045 | 
 | 
|---|
 | 1046 | It has been suggested that making the implicit conversions to and from `void*` 
 | 
|---|
 | 1047 | explicit in Cforall code (as in C++) would solve this particular problem, and 
 | 
|---|
 | 1048 | provide enough other type-safety benefits to outweigh the source-compatibility 
 | 
|---|
 | 1049 | break with C; see Appendix D for further details.
 | 
|---|
 | 1050 | 
 | 
|---|
 | 1051 | The second argument for a more constrained binding scheme is performance; 
 | 
|---|
 | 1052 | trait assertions need to be checked after the type variables are bound, and 
 | 
|---|
 | 1053 | adding more possible values for the type variables should correspond to a 
 | 
|---|
 | 1054 | linear increase in runtime of the resolver per type variable. 
 | 
|---|
 | 1055 | There are 21 built-in arithmetic types in C (ignoring qualifiers), and each of 
 | 
|---|
 | 1056 | them is implicitly convertable to any other; if we allow unrestricted binding 
 | 
|---|
 | 1057 | of type variables, a common `int` variable (or literal) used in the position 
 | 
|---|
 | 1058 | of a polymorphic variable parameter would cause a 20x increase in the amount 
 | 
|---|
 | 1059 | of time needed to check trait resolution for that interpretation. 
 | 
|---|
 | 1060 | These numbers have yet to be emprically substantiated, but the theory is 
 | 
|---|
 | 1061 | reasonable, and given that much of the impetus for re-writing the resolver is 
 | 
|---|
 | 1062 | due to its poor performance, I think this is a compelling argument. 
 | 
|---|
 | 1063 | 
 | 
|---|
 | 1064 | I would also mention that a constrained binding scheme is practical; the most 
 | 
|---|
 | 1065 | common type of assertion is a function assertion, and, as mentioned above, 
 | 
|---|
 | 1066 | those assertions should be able to be implicitly converted to to match. 
 | 
|---|
 | 1067 | Thus, in the example above with `g(T)`, where the assertion is `void f(T)`, 
 | 
|---|
 | 1068 | we first bind `T = int` or `T = char` or `T = double`, then substitute the 
 | 
|---|
 | 1069 | binding into the assertion, yielding assertions of `void f(int)`, 
 | 
|---|
 | 1070 | `void f(char)`, or `void f(double)`, respectively, then attempt to satisfy 
 | 
|---|
 | 1071 | these assertions to complete the binding. 
 | 
|---|
 | 1072 | Though in all three cases, the existing function with signature `void f(int)` 
 | 
|---|
 | 1073 | satisfies this assertion, the checking work cannot easily be re-used between 
 | 
|---|
 | 1074 | variable bindings, because there may be better or worse matches depending on 
 | 
|---|
 | 1075 | the specifics of the binding.
 | 
|---|
 | 1076 | 
 | 
|---|
 | 1077 | The main argument for a more flexible binding scheme is that the binding 
 | 
|---|
 | 1078 | abstraction can actually cause a wrapped function call that would work to 
 | 
|---|
 | 1079 | cease to resolve, as below:
 | 
|---|
 | 1080 | 
 | 
|---|
 | 1081 |         forall(otype T | { T ?+? (T, T) })
 | 
|---|
 | 1082 |         T add(T x, T y) { return x + y; }
 | 
|---|
 | 1083 |         
 | 
|---|
 | 1084 |         int main() {
 | 
|---|
 | 1085 |                 int i, j = 2;
 | 
|---|
 | 1086 |                 short r, s = 3;
 | 
|---|
 | 1087 |                 i = add(j, s);
 | 
|---|
 | 1088 |                 r = add(s, j);
 | 
|---|
 | 1089 |         }
 | 
|---|
 | 1090 | 
 | 
|---|
 | 1091 | Now, C's implicit conversions mean that you can call `j + s` or `s + j`, and 
 | 
|---|
 | 1092 | in both cases the short `s` is promoted to `int` to match `j`. 
 | 
|---|
 | 1093 | If, on the other hand, we demand that variables exactly match type variables, 
 | 
|---|
 | 1094 | neither call to `add` will compile, because it is impossible to simultaneously 
 | 
|---|
 | 1095 | bind `T` to both `int` and `short` (C++ has a similar restriction on template 
 | 
|---|
 | 1096 | variable inferencing). 
 | 
|---|
 | 1097 | One alternative that enables this case, while still limiting the possible 
 | 
|---|
 | 1098 | type variable bindings is to say that at least one argument must bind to its 
 | 
|---|
 | 1099 | type parameter exactly. 
 | 
|---|
 | 1100 | In this case, both calls to `add` would have the set `{ T = int, T = short }` 
 | 
|---|
 | 1101 | for candidate bindings, and would have to check both, as well as checking that 
 | 
|---|
 | 1102 | `short` could convert to `int` or vice-versa.
 | 
|---|
 | 1103 | 
 | 
|---|
 | 1104 | It is worth noting here that parameterized types generally bind their type 
 | 
|---|
 | 1105 | parameters exactly anyway, so these "restrictive" semantics only restrict a 
 | 
|---|
 | 1106 | small minority of calls; for instance, in the example following, there isn't a 
 | 
|---|
 | 1107 | sensible way to type the call to `ptr-add`:
 | 
|---|
 | 1108 | 
 | 
|---|
 | 1109 |         forall(otype T | { T ?+?(T, T) })
 | 
|---|
 | 1110 |         void ptr-add( T* rtn, T* x, T* y ) {
 | 
|---|
 | 1111 |                 *rtn = *x + *y;
 | 
|---|
 | 1112 |         }
 | 
|---|
 | 1113 |         
 | 
|---|
 | 1114 |         int main() {
 | 
|---|
 | 1115 |                 int i, j = 2;
 | 
|---|
 | 1116 |                 short s = 3;
 | 
|---|
 | 1117 |                 ptr-add(&i, &j, &s); // ERROR &s is not an int*
 | 
|---|
 | 1118 |         }
 | 
|---|
 | 1119 | 
 | 
|---|
 | 1120 | I think there is some value in providing library authors with the 
 | 
|---|
 | 1121 | capability to express "these two parameter types must match exactly". 
 | 
|---|
 | 1122 | This can be done without restricting the language's expressivity, as the `add` 
 | 
|---|
 | 1123 | case above can be made to work under the strictest type variable binding 
 | 
|---|
 | 1124 | semantics with any addition operator in the system by changing its signature 
 | 
|---|
 | 1125 | as follows:
 | 
|---|
 | 1126 | 
 | 
|---|
 | 1127 |         forall( otype T, otype R, otype S | { R ?+?(T, S); } )
 | 
|---|
 | 1128 |         R add(T x, S y) { return x + y; }
 | 
|---|
 | 1129 | 
 | 
|---|
 | 1130 | Now, it is somewhat unfortunate that the most general version here is more 
 | 
|---|
 | 1131 | verbose (and thus that the path of least resistence would be more restrictive 
 | 
|---|
 | 1132 | library code); however, the breaking case in the example code above is a bit 
 | 
|---|
 | 1133 | odd anyway - explicitly taking two variables of distinct types and relying on 
 | 
|---|
 | 1134 | C's implicit conversions to do the right thing is somewhat bad form, 
 | 
|---|
 | 1135 | especially where signed/unsigned conversions are concerned. 
 | 
|---|
 | 1136 | I think the more common case for implicit conversions is the following, 
 | 
|---|
 | 1137 | though, where the conversion is used on a literal: 
 | 
|---|
 | 1138 | 
 | 
|---|
 | 1139 |         short x = 40;
 | 
|---|
 | 1140 |         short y = add(x, 2);
 | 
|---|
 | 1141 | 
 | 
|---|
 | 1142 | One option to handle just this case would be to make literals implicitly 
 | 
|---|
 | 1143 | convertable to match polymorphic type variables, but only literals. 
 | 
|---|
 | 1144 | The example above would actually behave slightly differently than `x + 2` in 
 | 
|---|
 | 1145 | C, though, casting the `2` down to `short` rather than the `x` up to `int`, a 
 | 
|---|
 | 1146 | possible demerit of this scheme. 
 | 
|---|
 | 1147 | 
 | 
|---|
 | 1148 | The other question to ask would be which conversions would be allowed for 
 | 
|---|
 | 1149 | literals; it seems rather odd to allow down-casting `42ull` to `char`, when 
 | 
|---|
 | 1150 | the programmer has explicitly specified by the suffix that it's an unsigned 
 | 
|---|
 | 1151 | long. 
 | 
|---|
 | 1152 | Type interpretations of literals in C are rather complex (see [1]), but one 
 | 
|---|
 | 1153 | reasonable approach would be to say that un-suffixed integer literals could be 
 | 
|---|
 | 1154 | interpreted as any type convertable from int, "u" suffixed literals could be 
 | 
|---|
 | 1155 | interpreted as any type convertable from "unsigned int" except the signed 
 | 
|---|
 | 1156 | integer types, and "l" or "ll" suffixed literals could only be interpreted as 
 | 
|---|
 | 1157 | `long` or `long long`, respectively (or possibly that the "u" suffix excludes 
 | 
|---|
 | 1158 | the signed types, while the "l" suffix excludes the types smaller than 
 | 
|---|
 | 1159 | `long int`, as in [1]). 
 | 
|---|
 | 1160 | Similarly, unsuffixed floating-point literals could be interpreted as `float`, 
 | 
|---|
 | 1161 | `double` or `long double`, but "f" or "l" suffixed floating-point literals 
 | 
|---|
 | 1162 | could only be interpreted as `float` or `long double`, respectively. 
 | 
|---|
 | 1163 | I would like to specify that character literals can only be interpreted as 
 | 
|---|
 | 1164 | `char`, but the wide-character variants and the C practice of typing character 
 | 
|---|
 | 1165 | literals as `int` means that would likely break code, so character literals 
 | 
|---|
 | 1166 | should be able to take any integer type.
 | 
|---|
 | 1167 | 
 | 
|---|
 | 1168 | [1] http://en.cppreference.com/w/c/language/integer_constant
 | 
|---|
 | 1169 | 
 | 
|---|
 | 1170 | With the possible exception of the `add` case above, implicit conversions to 
 | 
|---|
 | 1171 | the function types of assertions can handle most of the expected behaviour 
 | 
|---|
 | 1172 | from C. 
 | 
|---|
 | 1173 | However, implicit conversions cannot be applied to match variable assertions, 
 | 
|---|
 | 1174 | as in the following example:
 | 
|---|
 | 1175 | 
 | 
|---|
 | 1176 |         forall( otype T | { int ?<?(T, T); T ?+?(T, T); T min; T max; } )
 | 
|---|
 | 1177 |         T clamp_sum( T x, T y ) {
 | 
|---|
 | 1178 |                 T sum = x + y;
 | 
|---|
 | 1179 |                 if ( sum < min ) return min;
 | 
|---|
 | 1180 |                 if ( max < sum ) return max;
 | 
|---|
 | 1181 |                 return sum;
 | 
|---|
 | 1182 |         }
 | 
|---|
 | 1183 |         
 | 
|---|
 | 1184 |         char min = 'A';
 | 
|---|
 | 1185 |         double max = 100.0;
 | 
|---|
 | 1186 |         //int val = clamp_sum( 'X', 3.14 );  // ERROR (1)
 | 
|---|
 | 1187 |         
 | 
|---|
 | 1188 |         char max = 'Z'
 | 
|---|
 | 1189 |         char val = clamp_sum( 'X', 3.14 ); // MATCH (2)
 | 
|---|
 | 1190 |         double val = clamp_sum( 40.9, 19.9 ); // MAYBE (3)
 | 
|---|
 | 1191 | 
 | 
|---|
 | 1192 | In this example, the call to `clamp_sum` at (1) doesn't compile, because even 
 | 
|---|
 | 1193 | though there are compatible `min` and `max` variables of types `char` and 
 | 
|---|
 | 1194 | `double`, they need to have the same type to match the constraint, and they 
 | 
|---|
 | 1195 | don't. 
 | 
|---|
 | 1196 | The (2) example does compile, but with a behaviour that might be a bit 
 | 
|---|
 | 1197 | unexpected given the "usual arithmetic conversions", in that both values are 
 | 
|---|
 | 1198 | narrowed to `char` to match the `min` and `max` constraints, rather than 
 | 
|---|
 | 1199 | widened to `double` as is usual for mis-matched arguments to +.
 | 
|---|
 | 1200 | The (3) example is the only case discussed here that would require the most 
 | 
|---|
 | 1201 | permisive type binding semantics - here, `T` is bound to `char`, to match the 
 | 
|---|
 | 1202 | constraints, and both the parameters are narrowed from `double` to `char` 
 | 
|---|
 | 1203 | before the call, which would not be allowed under either of the more 
 | 
|---|
 | 1204 | restrictive binding semantics. 
 | 
|---|
 | 1205 | However, the behaviour here is unexpected to the programmer, because the 
 | 
|---|
 | 1206 | return value will be `(double)'A' /* == 60.0 */` due to the conversions, 
 | 
|---|
 | 1207 | rather than `60.8 /* == 40.9 + 19.9 */` as they might expect.
 | 
|---|
 | 1208 | 
 | 
|---|
 | 1209 | Personally, I think that implicit conversions are not a particularly good 
 | 
|---|
 | 1210 | language design, and that the use-cases for them can be better handled with 
 | 
|---|
 | 1211 | less powerful features (e.g. more versatile rules for typing constant 
 | 
|---|
 | 1212 | expressions). 
 | 
|---|
 | 1213 | However, though we do need implicit conversions in monomorphic code for C 
 | 
|---|
 | 1214 | compatibility, I'm in favour of restricting their usage in polymorphic code, 
 | 
|---|
 | 1215 | both to give programmers some stronger tools to express their intent and to 
 | 
|---|
 | 1216 | shrink the search space for the resolver. 
 | 
|---|
 | 1217 | Of the possible binding semantics I've discussed, I'm in favour of forcing 
 | 
|---|
 | 1218 | polymorphic type variables to bind exactly, though I could be talked into 
 | 
|---|
 | 1219 | allowing literal expressions to have more flexibility in their bindings, or 
 | 
|---|
 | 1220 | possibly loosening "type variables bind exactly" to "type variables bind 
 | 
|---|
 | 1221 | exactly at least once"; I think the unrestricted combination of implicit 
 | 
|---|
 | 1222 | conversions and polymorphic type variable binding unneccesarily multiplies the 
 | 
|---|
 | 1223 | space of possible function resolutions, and that the added resolution options 
 | 
|---|
 | 1224 | are mostly unexpected and therefore confusing and not useful to user 
 | 
|---|
 | 1225 | programmers.
 | 
|---|
 | 1226 | 
 | 
|---|
| [d14d96a] | 1227 | ## Resolver Architecture ##
 | 
|---|
 | 1228 | 
 | 
|---|
 | 1229 | ### Function Application Resolution ###
 | 
|---|
 | 1230 | Our resolution algorithm for function application expressions is based on 
 | 
|---|
 | 1231 | Baker's[3] single-pass bottom-up algorithm, with Cormack's[4] single-pass 
 | 
|---|
 | 1232 | top-down algorithm applied where appropriate as an optimization. 
 | 
|---|
 | 1233 | Broadly speaking, the cost of this resolution per expression will be 
 | 
|---|
 | 1234 | proportional to `i^d`, where `i` is the number of interpretations of each 
 | 
|---|
 | 1235 | program symbol, and `d` is the maximum depth of the expression DAG. 
 | 
|---|
 | 1236 | Since `d` is determined by the user programmer (in general, bounded by a small 
 | 
|---|
 | 1237 | constant), opportunities for resolver optimization primarily revolve around 
 | 
|---|
 | 1238 | minimizing `i`, the number of interpretations of each symbol that are 
 | 
|---|
 | 1239 | considered.
 | 
|---|
 | 1240 | 
 | 
|---|
 | 1241 | [3] Baker, Theodore P. A one-pass algorithm for overload resolution in Ada. 
 | 
|---|
 | 1242 | ACM Transactions on Programming Languages and Systems (1982) 4:4 p.601-614
 | 
|---|
 | 1243 | 
 | 
|---|
 | 1244 | [4] Cormack, Gordon V. An algorithm for the selection of overloaded functions 
 | 
|---|
 | 1245 | in Ada. SIGPLAN Notices (1981) 16:2 p.48-52
 | 
|---|
 | 1246 | 
 | 
|---|
 | 1247 | Unlike Baker, our system allows implicit type conversions for function 
 | 
|---|
 | 1248 | arguments and return types; the problem then becomes to find the valid 
 | 
|---|
 | 1249 | interpretation for an expression that has the unique minimal conversion cost, 
 | 
|---|
 | 1250 | if such exists. 
 | 
|---|
 | 1251 | Interpretations can be produced both by overloaded names and implicit 
 | 
|---|
 | 1252 | conversions applied to existing interpretations; we have proposals to reduce 
 | 
|---|
 | 1253 | the number of interpretations considered from both sources. 
 | 
|---|
 | 1254 | To simplify the problem for this discussion, we will consider application 
 | 
|---|
 | 1255 | resolution restricted to a domain of functions applied to variables, possibly 
 | 
|---|
 | 1256 | in a nested manner (e.g. `f( g( x ), y )`, where `x` and `y` are variables and 
 | 
|---|
 | 1257 | `f` and `g` are functions), and possibly in a typed context such as a variable 
 | 
|---|
 | 1258 | initialization (e.g. `int i = f( x );`); the other aspects of Cforall type 
 | 
|---|
 | 1259 | resolution should be able to be straightforwardly mapped into this model. 
 | 
|---|
 | 1260 | The types of the symbol tables used for variable and function declarations 
 | 
|---|
 | 1261 | look somewhat like the following:
 | 
|---|
 | 1262 | 
 | 
|---|
 | 1263 |         variable_table = name_map( variable_name, variable_map )
 | 
|---|
 | 1264 |         
 | 
|---|
 | 1265 |         function_table = name_map( function_name, function_map )
 | 
|---|
 | 1266 |         
 | 
|---|
 | 1267 |         variable_map = multi_index( by_type( variable_type ), 
 | 
|---|
 | 1268 |                                     variable_decl_set )
 | 
|---|
 | 1269 | 
 | 
|---|
 | 1270 |         function_map = multi_index( by_int( n_params ),
 | 
|---|
 | 1271 |                                                                 by_type( return_type ),
 | 
|---|
 | 1272 |                                                                 function_decl_set )
 | 
|---|
 | 1273 | 
 | 
|---|
 | 1274 | `variable_name` and `function_name` are likely simple strings, with `name_map` 
 | 
|---|
 | 1275 | a hash table (or perhaps trie) mapping string keys to values. 
 | 
|---|
 | 1276 | `variable_decl_set` and `function_decl_set` can be thought of for the moment 
 | 
|---|
 | 1277 | as simple bags of typed declarations, where the declaration types are linked 
 | 
|---|
 | 1278 | to the graph of available conversions for that type. 
 | 
|---|
 | 1279 | In a typed context both the `variable_decl_set` and the `function_decl_set` 
 | 
|---|
 | 1280 | should be able to be selected upon by type; this is accomplished by the 
 | 
|---|
 | 1281 | `by_type` index of both `variable_map` and `function_map`. 
 | 
|---|
 | 1282 | The `by_int` index of `function_map` also provides a way to select functions 
 | 
|---|
 | 1283 | by their number of parameters; this index may be used to swiftly discard any 
 | 
|---|
 | 1284 | function declaration which does not have the appropriate number of parameters 
 | 
|---|
 | 1285 | for the argument interpretations being passed to it; given the likely small 
 | 
|---|
 | 1286 | number of entries in this map, it is possible that a binary search of a sorted 
 | 
|---|
 | 1287 | vector or even a linear search of an unsorted vector would be more efficient 
 | 
|---|
 | 1288 | than the usual hash-based index.
 | 
|---|
 | 1289 | 
 | 
|---|
 | 1290 | Given these data structures, the general outline of our algorithm follows 
 | 
|---|
 | 1291 | Baker, with Cormack's algorithm used as a heuristic filter in typed contexts. 
 | 
|---|
 | 1292 | 
 | 
|---|
 | 1293 | In an untyped context, we use a variant of Baker's bottom-up algorithm. 
 | 
|---|
 | 1294 | The leaves of the interpretation DAG are drawn from the variable symbol table, 
 | 
|---|
 | 1295 | with entries in the table each producing zero-cost interpretations, and each 
 | 
|---|
 | 1296 | implicit conversion available to be applied to the type of an existing entry 
 | 
|---|
 | 1297 | producing a further interpretation with the same cost as the conversion. 
 | 
|---|
 | 1298 | As in Baker, if two or more interpretations have the same type, only the 
 | 
|---|
 | 1299 | minimum cost interpretation with that type is produced; if there is no unique 
 | 
|---|
 | 1300 | minimum cost interpretation than resolution with that type is ambiguous, and 
 | 
|---|
 | 1301 | not permitted. 
 | 
|---|
 | 1302 | It should be relatively simple to produce the list of interpretations sorted 
 | 
|---|
 | 1303 | by cost by producing the interpretations via a breadth-first search of the 
 | 
|---|
 | 1304 | conversion graph from the initial interpretations provided in the variable 
 | 
|---|
 | 1305 | symbol table.
 | 
|---|
 | 1306 | 
 | 
|---|
 | 1307 | To match a function at one of the internal nodes of the DAG, we first look up 
 | 
|---|
 | 1308 | the function's name in the function symbol table, the appropriate number of 
 | 
|---|
 | 1309 | parameters for the arguments that are provided through the `by_int` index of 
 | 
|---|
 | 1310 | the returned `function_map`, then go through the resulting `function_decl_set` 
 | 
|---|
 | 1311 | searching for functions where the parameter types can unify with the provided 
 | 
|---|
 | 1312 | argument lists; any such matching function produces an interpretation with a 
 | 
|---|
 | 1313 | cost that is the sum of its argument costs. 
 | 
|---|
 | 1314 | Though this is not included in our simplified model, this unification step may 
 | 
|---|
 | 1315 | include binding of polymorphic variables, which introduces a cost for the 
 | 
|---|
 | 1316 | function binding itself which must be added to the argument costs. 
 | 
|---|
 | 1317 | Also, checking of function assertions would likely be done at this step as 
 | 
|---|
 | 1318 | well, possibly eliminating some possible matching functions (if no suitable 
 | 
|---|
 | 1319 | assertions can be satisfied), or adding further conversion costs for the 
 | 
|---|
 | 1320 | assertion satisfaction. 
 | 
|---|
 | 1321 | Once the set of valid function interpretations is produced, these may also be 
 | 
|---|
 | 1322 | expanded by the graph of implicit conversions on their return types, as the 
 | 
|---|
 | 1323 | variable interpretations were. 
 | 
|---|
 | 1324 | 
 | 
|---|
 | 1325 | This implicit conversion-based expansion of interpretations should be skipped 
 | 
|---|
 | 1326 | for the top-level expression if used in an untyped (void) context, e.g. for 
 | 
|---|
 | 1327 | `f` in `f( g ( x ) );` or `x` in `x;`. 
 | 
|---|
 | 1328 | On the other hand, if the top-level expression specifies a type, e.g. in 
 | 
|---|
 | 1329 | `int i = f( x );`, only top level expressions that return that type are 
 | 
|---|
 | 1330 | relevant to the search, so the candidates for `f` can be filtered first by 
 | 
|---|
 | 1331 | those that return `int` (or a type convertable to it); this can be 
 | 
|---|
 | 1332 | accomplished by performing a top-down filter of the interpretations of `f` by 
 | 
|---|
 | 1333 | the `by_type` index of the `function_map` in a manner similar to Cormack's[4] 
 | 
|---|
 | 1334 | algorithm. 
 | 
|---|
 | 1335 | 
 | 
|---|
 | 1336 | In a typed context, such as an initialization expression 
 | 
|---|
 | 1337 | `T x = f( g( y ), z );`, only interpretations of `f( g( y ), z )` which have 
 | 
|---|
 | 1338 | type `T` are valid; since there are likely to be valid interpretations of 
 | 
|---|
 | 1339 | `f( g( y ), z )` which cannot be used to initialize a variable of type `T`, we 
 | 
|---|
 | 1340 | can use this information to reduce the number of interpretations considered. 
 | 
|---|
 | 1341 | Drawing from Cormack[4], we first search for interpretations of `f` where the 
 | 
|---|
 | 1342 | return type is `T`; by breadth-first-search of the conversion graph, it should 
 | 
|---|
 | 1343 | be straightforward to order the interpretations of `f` by the cost to convert 
 | 
|---|
 | 1344 | their return type to `T`. 
 | 
|---|
 | 1345 | We can also filter out interpretations of `f` with less than two parameters, 
 | 
|---|
 | 1346 | since both `g( y )` and `z` must produce at least one parameter; we may not, 
 | 
|---|
 | 1347 | however, rule out interpretations of `f` with more than two parameters, as 
 | 
|---|
 | 1348 | there may be a valid interpretation of `g( y )` as a function returning more 
 | 
|---|
 | 1349 | than one parameter (if the expression was `f( y, z )` instead, we could use an 
 | 
|---|
 | 1350 | exact parameter count, assuming that variables of tuple type don't exist). 
 | 
|---|
 | 1351 | For each compatible interpretation of `f`, we can add the type of the first 
 | 
|---|
 | 1352 | parameter of that interpretation of `f` to a set `S`, and recursively search 
 | 
|---|
 | 1353 | for interpretations of `g( y )` that return some type `Si` in `S`, and 
 | 
|---|
 | 1354 | similarly for interpretations of `z` that match the type of any of the second 
 | 
|---|
 | 1355 | parameters of some `f`. 
 | 
|---|
 | 1356 | Naturally, if no matching interpretation of `g( y )` can be found for the 
 | 
|---|
 | 1357 | first parameter of some `f`, the type of the second parameter of that `f` will 
 | 
|---|
 | 1358 | not be added to the set of valid types for `z`. 
 | 
|---|
 | 1359 | Each node in this interpretation DAG is given a cost the same way it would be 
 | 
|---|
 | 1360 | in the bottom-up approach, with the exception that when going top-down there 
 | 
|---|
 | 1361 | must be a final bottom-up pass to sum the interpretation costs and sort them 
 | 
|---|
 | 1362 | as appropriate. 
 | 
|---|
 | 1363 | 
 | 
|---|
 | 1364 | If a parameter type for some `f` is a polymorphic type variable that is left 
 | 
|---|
 | 1365 | unbound by the return type (e.g. `forall(otype S) int f(S x, int y)`), the 
 | 
|---|
 | 1366 | matching arguments should be found using the bottom-up algorithm above for 
 | 
|---|
 | 1367 | untyped contexts, because the polymorphic type variable does not sufficiently 
 | 
|---|
 | 1368 | constrain the available interpretations of the argument expression. 
 | 
|---|
 | 1369 | Similarly, it would likely be an advantage to use top-down resolution for 
 | 
|---|
 | 1370 | cast expressions (e.g. `(int)x`), even when those cast expressions are 
 | 
|---|
 | 1371 | subexpressions of an otherwise untyped expression. 
 | 
|---|
 | 1372 | It may also be fruitful to switch between the bottom-up and top-down 
 | 
|---|
 | 1373 | algorithms if the number of valid interpretations for a subexpression or valid 
 | 
|---|
 | 1374 | types for an argument exceeds some heuristic threshold, but finding such 
 | 
|---|
 | 1375 | a threshold (if any exists) will require experimental data. 
 | 
|---|
 | 1376 | This hybrid top-down/bottom-up search provides more opportunities for pruning 
 | 
|---|
 | 1377 | interpretations than either a bottom-up or top-down approach alone, and thus 
 | 
|---|
 | 1378 | may be more efficient than either. 
 | 
|---|
 | 1379 | A top-down-only approach, however, devolves to linear search through every 
 | 
|---|
 | 1380 | possible interpretation in the solution space in an untyped context, and is 
 | 
|---|
 | 1381 | thus likely to be inferior to a strictly bottom-up approach, though this 
 | 
|---|
 | 1382 | hypothesis needs to be empirically validated.
 | 
|---|
 | 1383 | 
 | 
|---|
 | 1384 | Both Baker and Cormack explicitly generate all possible interpretations of a 
 | 
|---|
 | 1385 | given expression; thinking of the set of interpretations of an expression as a 
 | 
|---|
 | 1386 | list sorted by cost, this is an eager evaluation of the list. 
 | 
|---|
 | 1387 | However, since we generally expect that user programmers will not often use 
 | 
|---|
 | 1388 | high-cost implicit conversions, one potentially effective way to prune the 
 | 
|---|
 | 1389 | search space would be to first find the minimal-cost interpretations of any 
 | 
|---|
 | 1390 | given subexpression, then to save the resolution progress of the 
 | 
|---|
 | 1391 | subexpressions and attempt to resolve the superexpression using only those 
 | 
|---|
 | 1392 | subexpression interpretations.
 | 
|---|
 | 1393 | If no valid interpretation of the superexpression can be found, the resolver 
 | 
|---|
 | 1394 | would then repeatedly find the next-most-minimal cost interpretations of the 
 | 
|---|
 | 1395 | subexpressions and attempt to produce the minimal cost interpretation of the 
 | 
|---|
 | 1396 | superexpression. 
 | 
|---|
 | 1397 | This process would proceed until all possible subexpression interpretations 
 | 
|---|
 | 1398 | have been found and considered. 
 | 
|---|
 | 1399 | 
 | 
|---|
 | 1400 | A middle ground between the eager and lazy approaches can be taken by 
 | 
|---|
 | 1401 | considering the lexical order on the cost tuple; essentially, every 
 | 
|---|
 | 1402 | interpretation in each of the classes below will be strictly cheaper than any 
 | 
|---|
 | 1403 | interpretation in the class after it, so if a min-cost valid interpretation 
 | 
|---|
 | 1404 | can be found while only generating interpretations in a given class, that 
 | 
|---|
 | 1405 | interpretation is guaranteed to be the best possible one:
 | 
|---|
 | 1406 | 
 | 
|---|
 | 1407 | 1. Interpretations without polymorphic functions or implicit conversions
 | 
|---|
 | 1408 | 2. Interpretations without polymorphic functions using only safe conversions
 | 
|---|
 | 1409 | 3. Interpretations using polymorphic functions without unsafe conversions
 | 
|---|
 | 1410 | 4. Interpretations using unsafe conversions
 | 
|---|
 | 1411 | 
 | 
|---|
 | 1412 | In this lazy-eager approach, all the interpretations in one class would be 
 | 
|---|
 | 1413 | eagerly generated, while the interpretations in the next class would only be 
 | 
|---|
 | 1414 | considered if no match was found in the previous class.
 | 
|---|
 | 1415 | 
 | 
|---|
| [59f9273] | 1416 | Another source of efficiency would be to cache the best given interpretation 
 | 
|---|
 | 1417 | of a subexpression within an environment; this may not be incredibly useful 
 | 
|---|
 | 1418 | for explict parameters (though it may be useful for, e.g. `f( x, x )`, where 
 | 
|---|
 | 1419 | both parameters of `f` have the same type), but should pay some dividends for 
 | 
|---|
 | 1420 | the implicit assertion parameters, especially the otype parameters for the 
 | 
|---|
 | 1421 | argument of a generic type, which will generally be resolved in duplicate for 
 | 
|---|
 | 1422 | (at least) the assignment operator, constructor, copy constructor & destructor 
 | 
|---|
 | 1423 | of the generic type. 
 | 
|---|
 | 1424 | 
 | 
|---|
| [3d1e617] | 1425 | ## Appendix A: Partial and Total Orders ##
 | 
|---|
 | 1426 | The `<=` relation on integers is a commonly known _total order_, and 
 | 
|---|
 | 1427 | intuitions based on how it works generally apply well to other total orders. 
 | 
|---|
 | 1428 | Formally, a total order is some binary relation `<=` over a set `S` such that 
 | 
|---|
 | 1429 | for any two members `a` and `b` of `S`, `a <= b` or `b <= a` (if both, `a` and 
 | 
|---|
 | 1430 | `b` must be equal, the _antisymmetry_ property); total orders also have a 
 | 
|---|
 | 1431 | _transitivity_ property, that if `a <= b` and `b <= c`, then `a <= c`. 
 | 
|---|
 | 1432 | If `a` and `b` are distinct elements and `a <= b`, we may write `a < b`.
 | 
|---|
 | 1433 |  
 | 
|---|
 | 1434 | A _partial order_ is a generalization of this concept where the `<=` relation 
 | 
|---|
 | 1435 | is not required to be defined over all pairs of elements in `S` (though there 
 | 
|---|
 | 1436 | is a _reflexivity_ requirement that for all `a` in `S`, `a <= a`); in other 
 | 
|---|
 | 1437 | words, it is possible for two elements `a` and `b` of `S` to be 
 | 
|---|
 | 1438 | _incomparable_, unable to be ordered with respect to one another (any `a` and 
 | 
|---|
 | 1439 | `b` for which either `a <= b` or `b <= a` are called _comparable_). 
 | 
|---|
 | 1440 | Antisymmetry and transitivity are also required for a partial order, so all 
 | 
|---|
 | 1441 | total orders are also partial orders by definition. 
 | 
|---|
 | 1442 | One fairly natural partial order is the "subset of" relation over sets from 
 | 
|---|
 | 1443 | the same universe; `{ }` is a subset of both `{ 1 }` and `{ 2 }`, which are 
 | 
|---|
 | 1444 | both subsets of `{ 1, 2 }`, but neither `{ 1 }` nor `{ 2 }` is a subset of the 
 | 
|---|
 | 1445 | other - they are incomparable under this relation. 
 | 
|---|
 | 1446 | 
 | 
|---|
 | 1447 | We can compose two (or more) partial orders to produce a new partial order on 
 | 
|---|
 | 1448 | tuples drawn from both (or all the) sets. 
 | 
|---|
 | 1449 | For example, given `a` and `c` from set `S` and `b` and `d` from set `R`, 
 | 
|---|
 | 1450 | where both `S` and `R` both have partial orders defined on them, we can define 
 | 
|---|
 | 1451 | a ordering relation between `(a, b)` and `(c, d)`. 
 | 
|---|
 | 1452 | One common order is the _lexicographical order_, where `(a, b) <= (c, d)` iff 
 | 
|---|
 | 1453 | `a < c` or both `a = c` and `b <= d`; this can be thought of as ordering by 
 | 
|---|
 | 1454 | the first set and "breaking ties" by the second set. 
 | 
|---|
 | 1455 | Another common order is the _product order_, which can be roughly thought of 
 | 
|---|
 | 1456 | as "all the components are ordered the same way"; formally `(a, b) <= (c, d)` 
 | 
|---|
 | 1457 | iff `a <= c` and `b <= d`. 
 | 
|---|
 | 1458 | One difference between the lexicographical order and the product order is that 
 | 
|---|
 | 1459 | in the lexicographical order if both `a` and `c` and `b` and `d` are 
 | 
|---|
 | 1460 | comparable then `(a, b)` and `(c, d)` will be comparable, while in the product 
 | 
|---|
 | 1461 | order you can have `a <= c` and `d <= b` (both comparable) which will make 
 | 
|---|
 | 1462 | `(a, b)` and `(c, d)` incomparable. 
 | 
|---|
 | 1463 | The product order, on the other hand, has the benefit of not prioritizing one 
 | 
|---|
 | 1464 | order over the other.
 | 
|---|
 | 1465 | 
 | 
|---|
 | 1466 | Any partial order has a natural representation as a directed acyclic graph 
 | 
|---|
 | 1467 | (DAG). 
 | 
|---|
 | 1468 | Each element `a` of the set becomes a node of the DAG, with an arc pointing to 
 | 
|---|
 | 1469 | its _covering_ elements, any element `b` such that `a < b` but where there is 
 | 
|---|
 | 1470 | no `c` such that `a < c` and `c < b`. 
 | 
|---|
 | 1471 | Intuitively, the covering elements are the "next ones larger", where you can't 
 | 
|---|
 | 1472 | fit another element between the two. 
 | 
|---|
 | 1473 | Under this construction, `a < b` is equivalent to "there is a path from `a` to 
 | 
|---|
 | 1474 | `b` in the DAG", and the lack of cycles in the directed graph is ensured by 
 | 
|---|
 | 1475 | the antisymmetry property of the partial order.
 | 
|---|
 | 1476 | 
 | 
|---|
 | 1477 | Partial orders can be generalized to _preorders_ by removing the antisymmetry 
 | 
|---|
 | 1478 | property. 
 | 
|---|
 | 1479 | In a preorder the relation is generally called `<~`, and it is possible for 
 | 
|---|
 | 1480 | two distict elements `a` and `b` to have `a <~ b` and `b <~ a` - in this case 
 | 
|---|
 | 1481 | we write `a ~ b`; `a <~ b` and not `a ~ b` is written `a < b`. 
 | 
|---|
 | 1482 | Preorders may also be represented as directed graphs, but in this case the 
 | 
|---|
 | 1483 | graph may contain cycles.
 | 
|---|
 | 1484 | 
 | 
|---|
 | 1485 | ## Appendix B: Building a Conversion Graph from Un-annotated Single Steps ##
 | 
|---|
 | 1486 | The short answer is that it's impossible.
 | 
|---|
 | 1487 | 
 | 
|---|
 | 1488 | The longer answer is that it has to do with what's essentially a diamond 
 | 
|---|
 | 1489 | inheritance problem. 
 | 
|---|
 | 1490 | In C, `int` converts to `unsigned int` and also `long` "safely"; both convert 
 | 
|---|
 | 1491 | to `unsigned long` safely, and it's possible to chain the conversions to 
 | 
|---|
 | 1492 | convert `int` to `unsigned long`. 
 | 
|---|
 | 1493 | There are two constraints here; one is that the `int` to `unsigned long` 
 | 
|---|
 | 1494 | conversion needs to cost more than the other two (because the types aren't as 
 | 
|---|
 | 1495 | "close" in a very intuitive fashion), and the other is that the system needs a 
 | 
|---|
 | 1496 | way to choose which path to take to get to the destination type. 
 | 
|---|
 | 1497 | Now, a fairly natural solution for this would be to just say "C knows how to 
 | 
|---|
 | 1498 | convert from `int` to `unsigned long`, so we just put in a direct conversion 
 | 
|---|
 | 1499 | and make the compiler smart enough to figure out the costs" - given that the 
 | 
|---|
 | 1500 | users can build an arbitrary graph of conversions, this needs to be handled 
 | 
|---|
 | 1501 | anyway. 
 | 
|---|
 | 1502 | We can define a preorder over the types by saying that `a <~ b` if there 
 | 
|---|
 | 1503 | exists a chain of conversions from `a` to `b`. 
 | 
|---|
 | 1504 | This preorder corresponds roughly to a more usual type-theoretic concept of 
 | 
|---|
 | 1505 | subtyping ("if I can convert `a` to `b`, `a` is a more specific type than 
 | 
|---|
 | 1506 | `b`"); however, since this graph is arbitrary, it may contain cycles, so if 
 | 
|---|
 | 1507 | there is also a path to convert `b` to `a` they are in some sense equivalently 
 | 
|---|
 | 1508 | specific. 
 | 
|---|
 | 1509 | 
 | 
|---|
 | 1510 | Now, to compare the cost of two conversion chains `(s, x1, x2, ... xn)` and 
 | 
|---|
 | 1511 | `(s, y1, y2, ... ym)`, we have both the length of the chains (`n` versus `m`) 
 | 
|---|
 | 1512 | and this conversion preorder over the destination types `xn` and `ym`. 
 | 
|---|
 | 1513 | We could define a preorder by taking chain length and breaking ties by the 
 | 
|---|
 | 1514 | conversion preorder, but this would lead to unexpected behaviour when closing 
 | 
|---|
 | 1515 | diamonds with an arm length of longer than 1. 
 | 
|---|
 | 1516 | Consider a set of types `A`, `B1`, `B2`, `C` with the arcs `A->B1`, `B1->B2`, 
 | 
|---|
 | 1517 | `B2->C`, and `A->C`. 
 | 
|---|
 | 1518 | If we are comparing conversions from `A` to both `B2` and `C`, we expect the 
 | 
|---|
 | 1519 | conversion to `B2` to be chosen because it's the more specific type under the 
 | 
|---|
 | 1520 | conversion preorder, but since its chain length is longer than the conversion 
 | 
|---|
 | 1521 | to `C`, it loses and `C` is chosen. 
 | 
|---|
 | 1522 | However, taking the conversion preorder and breaking ties or ambiguities by 
 | 
|---|
 | 1523 | chain length also doesn't work, because of cases like the following example 
 | 
|---|
 | 1524 | where the transitivity property is broken and we can't find a global maximum: 
 | 
|---|
 | 1525 | 
 | 
|---|
 | 1526 |         `X->Y1->Y2`, `X->Z1->Z2->Z3->W`, `X->W`
 | 
|---|
 | 1527 | 
 | 
|---|
 | 1528 | In this set of arcs, if we're comparing conversions from `X` to each of `Y2`, 
 | 
|---|
 | 1529 | `Z3` and `W`, converting to `Y2` is cheaper than converting to `Z3`, because 
 | 
|---|
 | 1530 | there are no conversions between `Y2` and `Z3`, and `Y2` has the shorter chain 
 | 
|---|
 | 1531 | length. 
 | 
|---|
 | 1532 | Also, comparing conversions from `X` to `Z3` and to `W`, we find that the 
 | 
|---|
 | 1533 | conversion to `Z3` is cheaper, because `Z3 < W` by the conversion preorder, 
 | 
|---|
 | 1534 | and so is considered to be the nearer type. 
 | 
|---|
 | 1535 | By transitivity, then, the conversion from `X` to `Y2` should be cheaper than 
 | 
|---|
 | 1536 | the conversion from `X` to `W`, but in this case the `X` and `W` are 
 | 
|---|
 | 1537 | incomparable by the conversion preorder, so the tie is broken by the shorter 
 | 
|---|
 | 1538 | path from `X` to `W` in favour of `W`, contradicting the transitivity property 
 | 
|---|
 | 1539 | for this proposed order.
 | 
|---|
 | 1540 | 
 | 
|---|
 | 1541 | Without transitivity, we would need to compare all pairs of conversions, which 
 | 
|---|
 | 1542 | would be expensive, and possibly not yield a minimal-cost conversion even if 
 | 
|---|
 | 1543 | all pairs were comparable. 
 | 
|---|
 | 1544 | In short, this ordering is infeasible, and by extension I believe any ordering 
 | 
|---|
 | 1545 | composed solely of single-step conversions between types with no further 
 | 
|---|
 | 1546 | user-supplied information will be insufficiently powerful to express the 
 | 
|---|
 | 1547 | built-in conversions between C's types.
 | 
|---|
 | 1548 | 
 | 
|---|
 | 1549 | ## Appendix C: Proposed Prelude Changes ##
 | 
|---|
 | 1550 | **TODO** Port Glen's "Future Work" page for builtin C conversions.
 | 
|---|
 | 1551 | **TODO** Move discussion of zero_t, unit_t here.
 | 
|---|
 | 1552 | 
 | 
|---|
 | 1553 | It may be desirable to have some polymorphic wrapper functions in the prelude 
 | 
|---|
 | 1554 | which provide consistent default implementations of various operators given a 
 | 
|---|
 | 1555 | definition of one of them. 
 | 
|---|
 | 1556 | Naturally, users could still provide a monomorphic overload if they wished to 
 | 
|---|
 | 1557 | make their own code more efficient than the polymorphic wrapper could be, but 
 | 
|---|
 | 1558 | this would minimize user effort in the common case where the user cannot write 
 | 
|---|
 | 1559 | a more efficient function, or is willing to trade some runtime efficiency for 
 | 
|---|
 | 1560 | developer time. 
 | 
|---|
 | 1561 | As an example, consider the following polymorphic defaults for `+` and `+=`:
 | 
|---|
 | 1562 | 
 | 
|---|
 | 1563 |         forall(otype T | { T ?+?(T, T); })
 | 
|---|
 | 1564 |         lvalue T ?+=? (T *a, T b) {
 | 
|---|
 | 1565 |                 return *a = *a + b;
 | 
|---|
 | 1566 |         }
 | 
|---|
 | 1567 |         
 | 
|---|
 | 1568 |         forall(otype T | { lvalue T ?+=? (T*, T) })
 | 
|---|
 | 1569 |         T ?+? (T a, T b) {
 | 
|---|
 | 1570 |                 T tmp = a;
 | 
|---|
 | 1571 |                 return tmp += b;
 | 
|---|
 | 1572 |         }
 | 
|---|
 | 1573 | 
 | 
|---|
 | 1574 | Both of these have a possibly unneccessary copy (the first in copying the 
 | 
|---|
 | 1575 | result of `*a + b` back into `*a`, the second copying `a` into `tmp`), but in 
 | 
|---|
 | 1576 | cases where these copies are unavoidable the polymorphic wrappers should be 
 | 
|---|
 | 1577 | just as performant as the monomorphic equivalents (assuming a compiler 
 | 
|---|
 | 1578 | sufficiently clever to inline the extra function call), and allow programmers 
 | 
|---|
 | 1579 | to define a set of related operators with maximal concision.
 | 
|---|
 | 1580 | 
 | 
|---|
 | 1581 | **TODO** Look at what Glen and Richard have already written for this.
 | 
|---|
 | 1582 | 
 | 
|---|
 | 1583 | ## Appendix D: Feasibility of Making void* Conversions Explicit ##
 | 
|---|
 | 1584 | C allows implicit conversions between `void*` and other pointer types, as per 
 | 
|---|
 | 1585 | section 6.3.2.3.1 of the standard. 
 | 
|---|
 | 1586 | Making these implicit conversions explicit in Cforall would provide 
 | 
|---|
 | 1587 | significant type-safety benefits, and is precedented in C++. 
 | 
|---|
 | 1588 | A weaker version of this proposal would be to allow implicit conversions to 
 | 
|---|
 | 1589 | `void*` (as a sort of "top type" for all pointer types), but to make the 
 | 
|---|
 | 1590 | unsafe conversion from `void*` back to a concrete pointer type an explicit 
 | 
|---|
 | 1591 | conversion. 
 | 
|---|
 | 1592 | However, `int *p = malloc( sizeof(int) );` and friends are hugely common 
 | 
|---|
 | 1593 | in C code, and rely on the unsafe implicit conversion from the `void*` return 
 | 
|---|
 | 1594 | type of `malloc` to the `int*` type of the variable - obviously it would be 
 | 
|---|
 | 1595 | too much of a source-compatibility break to disallow this for C code. 
 | 
|---|
 | 1596 | We do already need to wrap C code in an `extern "C"` block, though, so it is 
 | 
|---|
 | 1597 | technically feasible to make the `void*` conversions implicit in C but 
 | 
|---|
 | 1598 | explicit in Cforall. 
 | 
|---|
 | 1599 | Also, for calling C code with `void*`-based APIs, pointers-to-dtype are 
 | 
|---|
 | 1600 | calling-convention compatible with `void*`; we could read `void*` in function 
 | 
|---|
 | 1601 | signatures as essentially a fresh dtype type variable, e.g:
 | 
|---|
 | 1602 | 
 | 
|---|
 | 1603 |         void* malloc( size_t )
 | 
|---|
 | 1604 |                 => forall(dtype T0) T0* malloc( size_t )
 | 
|---|
 | 1605 |         void qsort( void*, size_t, size_t, int (*)( const void*, const void* ) )
 | 
|---|
 | 1606 |                 => forall(dtype T0, dtype T1, dtype T2)
 | 
|---|
 | 1607 |                    void qsort( T0*, size_t, size_t, int (*)( const T1*, const T2* ) )
 | 
|---|
 | 1608 | 
 | 
|---|
 | 1609 | This would even allow us to leverage some of Cforall's type safety to write 
 | 
|---|
 | 1610 | better declarations for legacy C API functions, like the following wrapper for 
 | 
|---|
 | 1611 | `qsort`:
 | 
|---|
 | 1612 | 
 | 
|---|
 | 1613 |         extern "C" { // turns off name-mangling so that this calls the C library
 | 
|---|
 | 1614 |                 // call-compatible type-safe qsort signature
 | 
|---|
 | 1615 |                 forall(dtype T)
 | 
|---|
 | 1616 |                 void qsort( T*, size_t, size_t, int (*)( const T*, const T* ) );
 | 
|---|
 | 1617 |                 
 | 
|---|
 | 1618 |                 // forbid type-unsafe C signature from resolving
 | 
|---|
 | 1619 |                 void qsort( void*, size_t, size_t, int (*)( const void*, const void* ) )
 | 
|---|
 | 1620 |                         = delete;
 | 
|---|
 | 1621 |         }
 | 
|---|