Changes in / [06d75931:3a08cb1]


Ignore:
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • doc/proposals/tuples.md

    r06d75931 r3a08cb1  
    22======
    33
    4 This is a proposal discusses update to Cforall tuples as they exist after
    5 the work done by Robert Schluntz (after they were added to Cforall by
    6 Rodolfo Esteves and to K-W C by Dave Till). The new features and ideas
    7 discussed here are designed to address problems that have appeared as tuples
    8 have been used in the past 6 years. Some or all of the changes discussed may
    9 already be implemented.
     4Tuples were introduced by Dave Till in K-W C, added to CFA by Rodolfo Esteves,
     5and extended by Robert Schluntz. This proposal discusses updates for CFA tuples
     6to address problems that have appeared in their usage over the past 6 years.
     7The proposal attempts to address problems by adding new functionality, updating
     8existing features, and removing some problematic ones.
    109
    1110The core change is breaking the current restructurable tuples into unstructured
     
    1514Current State of Tuples
    1615-----------------------
    17 An overview of the current tuple design is the starting place for the proposal.
     16An overview of the current tuples design is the starting place for the proposal.
    1817
    1918### Tuple Syntax
     
    2322(deconstructing tuples).
    2423
    25 Current syntax for tuple types:
     24Current syntax for tuple types.
    2625
    2726-   Nullary: [void] or []
    28 
    29         [void] f();
    30         [] f();
    31 
    3227-   Unary: [TYPE]
    33 
    34         [int] f();
    35         [double] x = 1.5;
    36 
    37 -   Multiary: [TYPE, TYPE, ...]
    38 
    39         [bool, char] f();
    40         [short, int, long] x;
    41 
    42 Tuple types can appear anywhere regular types may be used, except for the
    43 nullary tuple which can only appear where void can be used (usually return
    44 types), with the exception of special case when void is used to make an empty
    45 parameter list.
     28-   Nary: [TYPE, TYPE, ...]
     29
     30Tuple types can appear in a function return and parameter declaration, or a
     31tuple variable declaration. Note, the Nullary tuple is only meaningful in the
     32return context,
     33
     34        void f(...);   // equivalent
     35        [void] f(...);
     36        [] f(...);
     37
     38as C does not support a void declaration.
     39
     40        int f( void x );    // disallowed
     41        int f( [void] x );
     42        int f( [] x );
    4643
    4744Current syntax for tuple expressions:
     
    4946-   Nullary: (Same as `void`, use return without an expression.)
    5047
    51         [] f() { return; }
    52 
    53 -   Unary: EXPR (in return only) or {EXPR}
    54 
    55         [int] f() { return 3; }
    56         [int] x = {3};
    57 
    58 -   Multiary: [EXPR, EXPR, ...]
    59 
    60         [int, int] f() { return [3, 4]; }
    61         [bool, char, int] x = [true, 'a', 3];
    62 
    63 Tuple expressions should be useable whenever an expression of that type is
    64 expected. Issues with the current state will be discussed later.
    65 
     48        [] f( ) { return; }
     49        [] f( ) { return [] ; }
     50
     51-   Unary:
     52
     53        [int] f( ) { return 3; }
     54        [int] f( ) { return [3]; }
     55
     56-   Nary: [EXPR, EXPR]
     57
     58        [int,int] f( ) { return [3,4]; }
     59
     60Currently, there is a parsing problem for nullary and unary tuple expression,
     61which is being looked at. Hence, only these two forms work.
     62
     63        [] f( ) { return; } // nullary
     64        [int] f( ) { return 3; } // unary
     65   
    6666Current syntax for tuple indexing is an integer constant, where its value
    6767selects a tuple member, e.g.:
    6868
    69         [char, int] tup = ['a', 0];
    70         char ch = tup.0;
    71         int value = tup.1;
     69        [char, int] tup;
     70        tup = ['a', 0];
     71        char ch = t.0; // select first tuple member
     72        int value = t.1; // select second tuple member
    7273
    7374### Mass and Multi-Assignment
     
    7879
    7980        [int, long, float] dst;
    80         int src = 4;
    81         dst = src;
    82         // Becomes: dst.0 = src; dst.1 = src; dst.2 = src;
     81        int src = 4
     82        dst = src;  // => dst.0 = src; dst.1 = src; dst.2 = src
    8383
    8484Multi-assignment assigns every element in the destination tuple to the
    8585corresponding element in the source tuple. Both tuples must be the same size
    86 and the elements pairs must be assignment compatible conversions.
     86and the elements assignment compatible => conversions.
    8787
    8888        [long, int, float] dst;
    8989        [int, char, double] src = [1, 'a', 300.0];
    90         dst = src;
    91         // Becomes: dst.0 = src.0; dst.1 = src.1; dst.2 = src.1;
     90        dst = src;   // => dst.0 = src.0; dst.1 = src.1; dst.2 = src.1
    9291
    9392### Tuple Restructuring
     
    111110        parmFunc(fst.0, fst.1, snd.0, snd.1);
    112111
     112There are few languages supporting multiple return-values as a standalone
     113feature (SETL). Go has multiple return-values but restricts their use in
     114matching arguments to parameters.
     115
     116        func argFunc() (int, int) {
     117            return 3, 7
     118        }
     119        func parmFunc( a int, b int ) {
     120             fmt.Println(a, b )
     121        }
     122        func main() {
     123             parmFunc2( argFunc() ); // arguments must match exactly with parameters
     124        }
     125
    113126### Tuple Casts
    114127
     
    137150        [int, [bool, char], int] w;
    138151        [i, b, c, i] = w;
    139         [i, b, c] = ([int, [bool, char]])w;
    140         // But this does not work:
    141         [i, b, c, i] = ([int, bool, char, int])w;
     152        [i, b, c, i] = ([int, bool, char, int])w; // fails
     153        [i, b, c] = ([int, [bool, char]])w; // works
    142154
    143155### Polymorphic Tuple Parameters
     
    148160sequence of types.
    149161
    150         // Fixed Length Max - Base Case:
    151         forall(T | { int ?>?( T, T ); })
    152         T max(T v1, T v2) { return v1 > v2 ? v1 : v2; }
    153 
    154         // Variable Length Max - Recursive:
    155         forall(T, Ts... | { T max(T, T); T max(Ts); })
     162        forall( T | { int ?>?( T, T ); } )
     163        T max( T v1, T v2 ) { return v1 > v2 ? v1 : v2; }  // base case
     164
     165        forall(T, Ts... | { T max(T, T); T max(Ts); })  // recursive case
    156166        T max(T arg, Ts args) {
    157167                return max(arg, max(args));
    158168        }
    159169
    160 This feature introduces a type name into scope (Ts). It is used as a type and
    161 the value it declares (args) is a tuple value, and that the second assertion
    162 "T max(Ts);" matches takes a single tuple, but with restructuring it can also
    163 match functions that take a series of non-tuple arguments.
    164 
    165 A good way to explain this is to show a partial implementation the following
    166 call `max(int_a, int_b, int_c);` into actual code:
    167 
    168         void _thunk0(int v1, int v2) {
    169                 return max{?>?}(v1, v2);
    170         }
    171         void _thunk1([int, int] tup) {
    172                 return max{?>?}(tup.0, tup.1);
    173         }
    174         max{_thunk0, _thunk1}(int_a, [int_b, int_c]);
    175 
    176 The part to highlight is that "_thunk1", one of the functions created to
    177 make an inexact match that can be used as a call site into an exact match
    178 so the function can be passed through function parameters.
    179 Although, the tuple type `[int, int]` matches a parameter list `int, int`,
    180 just with a bit of low lever restructuring.
    181 
    182 In larger cases (with four or more parameters in the first case), the
    183 recursive case will work similarly, creating an intermidate function to
    184 restructure the tuple into a new call such as:
    185 
    186         void _thunk2([int, int, int] tup) {
    187                 return max{_thunk0, _thunk1}(tup.0, [tup.1, tup.2]);
    188         }
     170This feature introduces a type name into scope (Ts). It is used as a type but
     171because the tuple is flattened, the second assertion "T max(Ts);" matches types
     172with multiple parameters (the `...`), although it is used as a tuple function
     173inside the function body (max(args)).
     174
     175The first non-recursive max function is the polymorphic base-case for the
     176recursion, i.e., find the maximum of two identically typed values with a
     177greater-than (>) operator.  The second recursive max function takes two
     178parameters, a T and a Ts tuple, handling all argument lengths greater than two.
     179The recursive function computes the maximum for the first argument and the
     180maximum value of the rest of the tuple.  The call of max with one argument is
     181the recursive call, where the tuple is converted into two arguments by taking
     182the first value (lisp car) from the tuple pack as the first argument
     183(flattening) and the remaining pack becomes the second argument (lisp cdr).
     184The recursion stops when the tuple is empty.  For example, max( 2, 3, 4 )
     185matches with the recursive function, which performs return max( 2, max( [3, 4]
     186) ) and one more step yields return max( 2, max( 3, 4 ) ), so the tuple is
     187empty.
     188
    189189
    190190Issues with the Current State
     
    199199be.
    200200
    201 Tuples do not have the lifetime operators (copy construction, copy assignment
    202 and destructor) that define an object type in Cforall. This means they are
    203 not polymorphic otypes, they are objects in C's usage of the word. This means
    204 that tuples cannot be used as a single object in
    205 
    206 There are several reasons for this. The fluid nature of tuples (flattening
    207 and restructuring) means that matching against a tuple type is fluid in some
    208 inconvent ways.
    209 
    210         forall(T, U) void ?{}([T, U] & this;
    211         void ?{}([int, int, int] & this);
    212         // What constructor(s) should be used here?
    213         [int, [int, int]] pair;
    214 
    215 Should the polymorpic constructor be applied twice or should the tuple be
    216 restructured and the three element constructor be used? This is not really
    217 something a user should be wondering about (especially since tuples should
    218 always be simple containers wrapping their elements).
     201Because of the fluid nature of tuples (flattening/structuring), routines like
     202constructions, destructor, or assignment do not make sense, e.g. this
     203constructor matches multiple a tuple types:
     204
     205        void ?{} ( [int, int, int] ? this );
     206        [int, [int, int]] x; // all match constructor type by flattening
     207        [int,  int, int]  y;
     208        [[int, int], int] z;
     209
     210as could a similarly typed destructor or assignment operator.  This prevents
     211tuples from being interwoven with regular polymorphic code.
    219212
    220213### Providing TType Arguments is Inconsistent
     
    224217functions and there are very few existing use-cases for ttype structures.
    225218
    226         forall(T, Us...)
    227         void function(T head, Us tail) {}
    228 
    229         forall(T, Us...)
    230         struct Type {};
    231 
    232         int a;  bool b;  char c;
    233         function(a, b, c);
    234         function(a, [b, c]);
    235         // Doesn't work: Type(int, bool, char) x;
    236         Type(int, [bool, char]) x;
    237 
    238 The `function` case works either way because the tuple value can be
    239 restructured if it does not match exactly. The `Type` case though does not
    240 allow restructuring when the type is passed as a type value. This may be
    241 the most correct form, but seems surprising compared to the common use.
    242 
    243 ### Unary Tuple Value Syntax
    244 
    245 Unary tuples don't use the standard tuple syntax and one of the two alternate
    246 syntax options your may use instead doesn't work consistently. Solving both
    247 of these issues would help with consistency.
     219Passing arguments to a function inlines the arguments
     220while passing them to a polymorphic type requires them to be
     221enclosed in a tuple. Compare `function(x, y, z)` with `Type(A, [B, C])`.
     222
     223This did not come up previously as there was little reason to explicitly
     224provide ttype arguments. They are implicit for functions and there is very
     225little use case for a ttype on a struct or union.
    248226
    249227### Syntax Conflict
    250228
    251 The tuple syntax conflicts with designators and the C23 (C++-style) attribute
     229The tuple syntax conflicts with designators and the new C++-style attribute
    252230syntax.
    253231
    254         int a[10] = { [2] = 3 };
    255 
    256 Here [2] = 3 is an array designator, but has the same syntax as an assignment
    257 to a tuple of references, it isn't until the resolver determains that 2 is
    258 not a reference that that case can be ruled out.
    259 
    260         [[ unused ]] [[int, int], int] x;
    261 
    262 Here the opening `[[` could be a nested tuple or the beginning of an
    263 attribute, the parser can't figure out until later.
     232        struct S { int a[10]; } = { [2] = 3 }; // [2] looks like a tuple
     233        [[ unused ]] [[3, 4]]; // look ahead problem
    264234
    265235These conflicts break C compatibility goals of Cforall. Designators had to
    266236their syntax change and Cforall cannot parse the new attributes.
    267 The behaviour of these cases, if they could be parsed, should be unchanged.
     237
     238Although most of this redesign is about the semantics of tuples, but an update
     239to tuple syntax that removes these conflicts would improve the compatibility of
     240Cforall going forward (and also open up the new attribute syntax for cforall
     241features).
    268242
    269243Change in Model
    270244---------------
    271245This proposal modifies the existing tuples to better handle its main use
    272 cases. There are some use cases that are no longer handled, so in addition
    273 to changing the existing "restructured tuples" to "unstructured tuples"
    274 (or packs) a new "structured tuple" is added in the form of struct tuple.
    275 
    276 The new tuples is even more "unstructured" than before. New tuples are
     246cases. There are some use cases that are no longer handled, and a new
     247struct tuple is added to cover those cases.
     248
     249The new tuples is even more "unstructured" than before.  New tuples are
    277250considered a sequence of types or typed entities. These packs are then unpacked
    278251into the surrounding context. Put differently, tuples are now flattened as much
     
    286259unstructured tuples no longer support.
    287260
    288 Note that the underlying implementation might not actually look like this,
    289 but this is how it should seem to the user.
     261Note that the underlying implementation might not actually look like this.
    290262
    291263Changed Features
     
    293265Some of the concrete changes to the features of the language.
    294266
    295 ### Unstructured Tuple / Pack Type
    296 
    297 The evolution of our current tuples (called packs in an attempt to make the
    298 names more distinct). They work like the current tuples except they are
    299 always flattened. You might still be able to declare nested tuples, but these
    300 are not meaningful, they are flattened to a single level and, where
    301 approprate, is inlined into the sounding content.
    302 
    303         [bool, [char, int], long] x;
    304         // Becomes:
    305         [bool, char, int, long] x;
    306 
    307         void f(bool a0, [char, [int, long]] a1, float a2);
    308         // Becomes:
    309         void f(bool a0, char a1_0, int a1_1, long a1_2, float a2);
    310 
    311         [,] f(int a0, [,] a1, [bool,] a2);
    312         // Becomes:
    313         void f(int a0, bool a2);
    314 
    315 This actually means that tuples do not participate in overload resolution
    316 in the way the do currently. Restructuring is always free because they are
    317 always reduced to the same form as part of type matching.
    318 
    319 Packs are still not object types and do not have lifetime functions. They
    320 cannot be used as object types, nor as any data type, but they can be still
    321 used as ttypes.
    322 
    323 (Unless they need to have lifetime functions for other features.)
    324 
    325267### Structured Tuple Type
    326268
    327 There is a standard library or built-in type named `tuple`, it does not need
    328 a special syntax to write types or values. The type definition might need
    329 some primitive support, but if supported as a regular type would look
    330 something like this:
     269There is a standard library or built-in type named `tuple`, it does not need a
     270special syntax to write types or instances. The type definition might need some
     271primitive support, but if supported as a regular type would look something like
     272this:
    331273
    332274        forall(Ts...)
    333275        struct tuple {
    334                 inline Ts all;
     276                inline Ts all; // inline all specified fields
    335277        };
    336278
    337 This type wraps up an unstructured tuple, in this case a pack of members,
    338 and gives it "edges" and so structure. It also gives tuples an interface
    339 more like a normal structure type, for the anonymous structure use case.
    340 
    341 The struct tuple does have lifetime functions, can use list initializer
    342 syntax and otherwise be treated as a normal structure. You can use regular
    343 member access syntax to convert the struct tuple into a unstructured pack,
    344 writing `object.all`.
    345 
    346 The `inline` modifier is a new specialized feature to allow you use tuple
    347 index expressions directly on the type. With or without inline you should
    348 be able to chain a tuple index expression onto the member expression, such
    349 as `object.all.1`. The inline specifier allows you to skip the middle
    350 and use `object.1`.
    351 
    352 More complex operations can usually be preformed by taking the pack out of
    353 the struct tuple and acting on it. Polymorphic operations only have to go one
    354 level down to get to base operations, there is no need for recursive
    355 construction, nor manually coding different sizes of tuple. This should give
    356 most of the ease of working with a primitive tuple, because effectively you
    357 are except it has been given fixed edges.
    358 
    359 A tuple struct should also be an object type, and let's you pass multiple
    360 values through a single polymorphic slot.
    361 
    362 A note on the naming here, because there are more pieces that need names
    363 instead of symbols. The name `tuple` follows because that is the common name,
    364 although this means this is now the default tuple in some sense. The name of
    365 the pack was picked to work with inline and make the difference between `.0`
    366 and `.all` clear. (If inline doesn't work then `get` might be a better name.)
     279This type is constructed the same way as most types, a list initializer with
     280each tuple argument, and the lifetime functions (construction, assignment and
     281destruction) work the same. Field access works two ways, the first is accessing
     282the `all` field, effectively converting the structured tuple into an
     283unstructured tuple, the other is to use tuple indexing directly on the
     284structure as if it is an unstructured tuple.
     285
     286(If `inline` does not work, just rename all to `get`. It does make things a bit
     287longer but has no change in functionality. If the `all` access does not work,
     288that is more problematic, and tuple slicing might provide a work around.)
    367289
    368290### Type Qualifier Distribution
     
    408330up, that cannot be enforced with two different tuples.
    409331
    410 This example doesn't have to be implemented this way because we do have the
    411 special case operations for these already.
    412 
    413332### Type Packs
    414333
     
    452371
    453372For local declarations it works similarly, except the name introduced is
    454 directly usable as a tuple instead of going through a member access.
     373directly usable as a tuple.
    455374
    456375        forall(Objects...)
     
    461380        }
    462381
    463 ### Tuple Pack Restructuring
    464 
    465 Implicit restructuring still occurs, but is not considered as part of
    466 overload resolution. The structure of tuples is ignored, where individual
    467 tuples begin or end is not considered, just the sequence of types.
    468 
    469 This can be viewed as flattening both sides (the arguments/caller and the
    470 parameters/callee) before type resolution. Both any nested packs and packs
    471 into any larger context:
    472 
    473         call(false, ['a', [-7,]], 3.14)
    474         // Inline Nesting:
    475         call(false, ['a', -7], 3.14)
    476         // Inline into Context:
    477         call(false, 'a', -7, 3.14)
    478 
    479 The main context where tuples are inlined are parameter lists, in that you
    480 can consider a parameter list as a single pack. Return types could be viewed
    481 in this way, but because they are presented in a single type, they are
    482 already wrapped in a tuple.
    483 
    484         [int, [bool, char]] f();
    485         // Inline Nesting:
    486         [int, bool, char] f();
    487 
    488 This also happens "after" any polymorphic parameters are instantiated.
    489 
    490 Empty packs are also collapsed, logically being removed or replaced with
    491 void as in the following example:
    492 
    493         // For example, this function:
    494         forall(Ts...) Ts example(int first, Ts middle, int last);
    495         // With Ts = [,], should not be treated as:
    496         [,] example(int first, [,] middle, int last);
    497         // But instead:
    498         void example(int first, int last);
    499 
    500 Or closer to it, there are some slight differences between the nullary tuple
    501 and void, for instance creating some instances for consistency with the other
    502 tuple types.
    503 
    504 The struct tuple is never implicitly restructured.
    505 
    506382### Tuple Declaration/Deconstruction
    507383
     
    510386elements of the tuple can be named.
    511387
    512         [int quo, int rem] quorem = divmod(a, b);
     388        [int quo, int rem] ret = divmod(a, b);
    513389
    514390Here `ret` refers to the tuple, the entire pack, while `quo` and `rem`
     
    520396no named to access it.
    521397
    522 This does not add any new functionality, more it is a qualify of life feature
    523 making it very easy to give elements of a tuple descriptive names
    524 instead of trying to combine a name for the whole tuple with an index.
    525 (`quo` and `rem` vs. `quorem.0` and `quorem.1`)
     398PAB: I do understand the point of this.
    526399
    527400### Tuple Casts
     
    532405so it may be replaced with other features (for example: tuple slicing).
    533406
    534         // Allowed:
    535         ([bool, short])tuple_bool_char;
    536         ([int, float])tuple_float_float;
    537 
    538         // Forbidden:
    539         ([int, int, int])tuple_int_int;
    540         ([long, [float, int]])tuple_long_float_int;
    541 
    542 ### New Tuple Literal Syntax
    543 
    544 In addition to the main redesign of this proposal, the syntax is updated to
    545 be more consistant. It is now a "[]" list with "," separators and a ","/comma
    546 terminator that is optional for tuples with 2 or more elements (those that
    547 already have a comma in them) and manditory in other tuples.
    548 
    549 It should also be symmetric between types and value, differing only in that
    550 an element (ITEM below) of a tuple type is a type and the element of a tuple
    551 value is a value.
    552 
    553 -   Nullary Tuple: [,]
    554 -   Unary Tuples: [ITEM,]
    555 -   Multiary Tuples: [ITEM, ITEM] [ITEM, ITEM,] [ITEM, ITEM, ITEM] ...
    556 
    557 Currently, although syntax is provided to write them, but manually writing
    558 a nullary or unary tuple should be very rare. This is because a nullary tuple
    559 can either be erased or treated as void, while unary tuples can be treated
    560 as their element type. Similary for nested tuples, can be treated as the
    561 flattened tuple.
    562 
    563 (Update: Orignal nullary tuple proposal was `[]`, but that had some issues.)
    564 
    565 ### TType Argument Syntax
    566 
    567 It would be nice to seamlessly provide packs of types in polymorphic
    568 argument lists, however doing so requires a new restriction.
    569 
    570 As an example of the change, consider providing arguments to a polymorphic
    571 structure with a otype and a ttype parameter.
    572 
    573         forall(T, Us...) struct D {...};
    574         D(int, [,])            // D(int)
    575         D(bool, [char,])       // D(bool, char)
    576         D(long, [int, short])  // D(long, int, short)
    577 
    578 This is a nice syntaxtic improvement for the common case, it does restrict
    579 some of the more complex cases, such as cases with two ttypes. There are
    580 various restrictions on this for functions. If the rules make it unambigous
    581 then the grouping could be omitted, or a more advanced version, it can be
    582 ommited only in the indivual cases where it is unambigous.
     407### Forbidden Tuples
     408
     409The unstructured tuple cannot represent all the types that the previous
     410semi-structured tuple could. These cases still exist in various ways,
     411specifically in the internals of a polymorphic type, but in general should be
     412considered in their reduced form.
     413
     414Forbidding some of these tuples may remove ambiguity and solve some syntax
     415conflicts that currently exist.
     416
     417Nullary, or 0 element tuples, are equivalent to void, the type that carries
     418no data, because they do not either. It should either be replaced with void
     419or removed entirely when it appears in a larger sequence.
     420
     421        // For example, this function:
     422        forall(Ts...) Ts example(int first, ????)
     423        // With Ts = [], should not be treated as:
     424        [] example(int first, [] middle, int last);
     425        // But instead:
     426        void example(int first, int last);
     427
     428Unary, or 1 element tuples, should be the same as their element type. That
     429is to say a single type in an unstructured tuple is a no-op.
     430
     431Lastly, nested tuples are always flattened to a one-depth tuple.  This means
     432that `[bool, [char, int], float]` is resolved as `[bool, char, int, float]`,
     433with the internal structure of the tuple ignored.
     434
     435The flatten into a large sequence rule mentioned above is actually just an
     436application of this. Unstructured tuples can already be restructured, even at
     437the top level of an function call. This can be expressed by considering the
     438argument list as a tuple:
     439
     440        call(false, ['a', -7], 3.14)
     441        call([false, ['a', -7], 3.14])
     442        call([false, 'a', -7, 3.14])
     443        call(false, 'a', -7, 3.14)
     444
     445The ability to write nested tuples may remain so tuple deconstruction can be
     446used to name a slice of the tuple.
    583447
    584448### Tuple Slicing (Range Tuple Indexing)
     
    590454index expression to be a list-range a multiple sub-tuples can be extracted.
    591455
    592         ['a', 'b', 'c', 'd', 'e', 'f'].0~3
    593         ['a', 'b', 'c', 'd', 'e', 'f'].1~5~2
    594         // Produces:
    595         ['a', 'b', 'c']
    596         ['b', 'd']
    597 
    598 In words, the first indexes the first 3 tuple elements (0, 1, 2, stopping
    599 before index 3) and the second indexes elements starting at index 1, stoping
    600 before index 5 going in steps of 2. This does not allow for arbitary
    601 selections, but it covers many common slices.
     456        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].0~2,5~9~2
     457
     458produces the tuple
     459
     460        [0, 1, 2, 5, 7, 9]
     461
     462(Note the position and values are the same to simplify the example.) That is,
     463index the first 3 tuple elements, and then indexes elements 5 to 9 in steps of
     4642. Not all selections are possible with this mechanism (e.g., index the
     465Fibonacci elements), but it covers many cases.
    602466
    603467To get the new tuple, the range has to be constructed and traversed at compile
     
    622486The iterators proposal has a section on some new range types, this feature
    623487is intended to be built on that feature. Not simply reuse some of the syntax
    624 that is also used in the special for loop. This may add some requirements
    625 about the compile-time evaluation on range expressions to make sure these
    626 expressions can be evaluated at compile-time.
     488that is also used in the special for loop. And compile-time evaluation may
     489need to be improved.
     490
     491PAB, I don't understand this last part as the index range is compile time not
     492runtime.
    627493
    628494Implementation
     
    630496An overview of the implementation details of the new proposal.
    631497
    632 ### Structured Tuple
    633 
    634 There is actually a decision point here. If it can be implemented entirely
    635 in the standard library, that would be good. If it cannot, it can actually
    636 be implemented as the same primitive implementation as we use now and may be
    637 how unstructured tuple packs are implemented.
    638 
    639 ### Member Pack Implementation
    640 
    641 Implementing member packs can use the same pattern as current built-in tuple
    642 syntax. This is a two stage process, first is by ... then the it can be
    643 handled by the existing code for polymorphic but not variadic structures.
    644 
    645         forall(Ts...)
    646         struct tuple {
    647                 Ts get;
    648         };
    649 
    650 So when used with diffences length an hidden declaration is created which
    651 has the pack parameter replaced with the approprate number of object type
    652 parameters. Such as in the following examples:
    653 
    654         struct tuple$0 {
    655         };
    656 
    657         forall(Ts$0)
    658         struct tuple$1 {
    659                 Ts$0 get$0;
    660         };
    661 
    662         forall(Ts$0, Ts$1)
    663         struct tuple$2 {
    664                 Ts$0 get$0;
    665                 Ts$1 get$1;
    666         };
    667 
    668 Then it can be handled by the regular boxing code. Detection of these cases
    669 will have to changed because it is no longer a single case. But the pattern
    670 does work if the structure has additional fields in it, as they are just
    671 placed before and after the pack.
    672 
    673         forall(U, Ts...)
    674         struct blob {
    675                 int header;
    676                 Ts data;
    677                 U footer;
    678         };
    679 
    680         forall(U, Ts$0, Ts$1)
    681         struct blob$2 {
    682                 int header;
    683                 Ts$0 data$0;
    684                 Ts$1 data$1;
    685                 U footer;
    686         };
    687 
    688 The `inline` member pack just effects the resolver. It should be expanded out
    689 to the member access and then the tuple index expression.
    690 
    691 ### Local Declaration Pack
    692 
    693 Packs of local declarations can be handled very differently if they are
    694 concrete or not. Concrete packs can be writen out in full (even if some
    695 individual elements are polymorphic) can be inlined directly, expanding out
    696 into a series of object declarations.
    697 
    698         [A, B] local = func();
    699         call(local);
    700 
    701         A local$0;
    702         B local$1;
    703         ?{}([&local$0, &local$1], func());
    704         call([local$0, local$1]);
    705 
    706 In the polymorphic case though, that entire group needs to be reduced to a
    707 block that can put into a generic function. Here we can use something like
    708 the currently existing implementation, where the entire tuple an opaque
    709 bundle, passed to adapters and thunks for handling.
    710 
    711 This is also how tuple declarations are implemented. In the concrete case the
    712 element names are real and the pack name is replaced with a tuple expression
    713 of the elements. In the polymorphic case the pack name is real and the
    714 element names are replaced with tuple index expressions.
    715 
    716 ### Unstructured Tuple Implementation
    717 
    718 Unstructured tuples have two general implementation stratagies. Either they
    719 are erased and the packs inlined into their context, or they are wrapped
    720 up as like a structured tuple, generally using the current implementation.
    721 
    722 For example, if a concrete pack (size and all the types are known), appears
    723 in a parameter list the pack can be split and inlined into the argument list.
    724 
    725         void function(int a, [bool, char, short] b, float c);
    726         // Becomes:
    727         void function(int a, bool b0, char b1, short b2, float c);
    728 
    729 On the other hand, if a polymorphic pack appears in the same place, it will
    730 have to be wrapped up in a dynamic structure so it different instances can
    731 
    732         forall(Ts...)
    733         void function(int a, Ts b, float c);
    734         // Becomes (after the implicit packing and unpacking operations):
    735         forall(T)
    736         void function(int a, T* b, float c);
     498### Structured Tuple Implementation
     499
     500Under the hood, unstructured tuples are implemented as structured tuples, with
     501the restructuring code inserted wherever needed.  In short, the base
     502implementation should stay mostly the same.
     503
     504PAB: The current implementation does not use convert unstructured tuples to
     505structured tuples. Look at the code generated for
     506
     507        int x, y;
     508        [x, y] = 3;
     509        [x, y] = [y, x];
     510
     511
     512Actually inlining tuples can be done in some cases, it may even help with
     513some forms like a fixed tuple decomposition. However, polymorphic tuples
     514still need to be reduced to a single generic form, which would be a
     515polymorphic container, a tuple.
    737516
    738517### AST Updates
    739518
    740 The AST may already be capable of representing most of this. I have only
    741 identified one feature that definitely will now work in the current AST, and
    742 that are the object like tuple declarations with named elements.
    743 
    744 Particularly, an object declaration cannot name elements of the tuple.
    745 To this end a new node type, `TupleDecl`,
    746 should be added to handle tuple deconstruction declarations
    747 (other cases can still be handled with `ObjectDecl`).
     519The current AST cannot represent all the new features. Particularly, an
     520object declaration cannot name elements of the tuple. To this end a new
     521node type, `TupleDecl`, should be added to handle tuple deconstruction
     522declarations (other cases can still be handled with `ObjectDecl`).
    748523
    749524This would act much like a `FunctionDecl` except for tuples, narrowing the
     
    752527elements of the tuples.
    753528
     529PAB: the following parses:
     530
     531        [int x, int y] foo( int p );
     532
     533and discussed by Till.
     534
    754535(I'm not actually going to decide the implementation now, but some early
    755536examination of the problem suggests that it might be better off wrapping a
    756537series of `ObjectDecl` rather than just some strings to hold the names.)
    757538
     539### Field Packs
     540
     541Field packs in structures probably have to be written out in full by the
     542specialization pass. If not, it could have some negative effects on layout,
     543causing a structure to take up extra space. It may be able to reuse some of the
     544specialization code for the existing tuples.
     545
    758546Related Features in Other Languages
    759547-----------------------------------
    760 Other languages have similar features. Organized by the related feature,
    761 then language (this does mean we can and do hit languages multiple times).
     548Other languages have similar features. Organized by the related feature.
    762549
    763550(In hindsight, I may have gone overboard with the number of examples.)
     
    797584-   https://doc.rust-lang.org/reference/expressions/tuple-expr.html
    798585
    799 #### C++
     586#### C++ tuple
    800587
    801588Implemented as a template type defined in the standard library. No special
     
    811598used, e.g., `std::get<0>( tuple )`.
    812599
    813 C++ is an interesting case because it is one of the few cases where no part
    814 of the tuple system is built-in. It is entirely created in the standard
    815 library using templates. This also has massive error messages when something
    816 goes wrong, hopefully our version of struct tuple will reduce the problem.
    817 
    818600C++ also has structured binding, a kind of limited pattern matching. In a
    819 structured binding declaration, you can write an auto typed declaration where
    820 a list of identifiers in a `[]` replaces the single identifier.
    821 
    822         auto [first, second] = getSize2Tuple();
    823 
    824 The base type has to be `auto` because the base type for each element varies.
    825 Still, qualifiers (cv and reference) on the auto will be distributed to types
    826 on the individual members.
    827 
    828 The type bound to the binding may be an array, a "plain" structure or any
    829 type that implements the tuple-like interface (`std::tuple_size` and
    830 `std::tuple_element`).
     601structured binding declaration, you can write an auto typed declaration with
     602a list of identifiers in a `[]` list.
     603For example, `auto [first, second] = getSize2Tuple();`.
    831604
    832605-   https://en.cppreference.com/w/cpp/utility/tuple
    833606-   https://en.cppreference.com/w/cpp/language/structured_binding
    834607
    835 #### Haskell
    836 
    837 Haskell has a special syntax for tuples, but otherwise they are completely
    838 normal polymorphic types. Because of this, tuples have a maximum size.
    839 Haskell (98) supports tuples of up to 15 elements and the standard library
    840 has functions for tuples of up to 7 elements.
    841 
    842 The syntax for tuples is a comma separated list of elements. Either element
    843 types to create a tuple type, or element values to create a tuple value. The
    844 context decides among them, such as `(6, "six")` or `(Bool, Char, Int)`.
    845 
    846 Also all the elements can be removed, getting an expression like "(,)" or
    847 "(,,,)", which can be then be used as a function, for a type, or an expression.
    848 
    849 Haskell supports pattern matching as the main way to extract values from a
    850 tuple, although helper functions "fst" and "snd" are provided for field
    851 access on two element tuples.
    852 
    853 Haskell does not have 0 or 1-element tuples. The nil type, written "()" for
    854 both type and value, is effectively the 0 element tuple. There is also a type
    855 called "Solo" that is a polymorphic structure with one field and is used as
    856 the 1-element tuple, but has regular Haskell data-type syntax.
    857 
    858 -   https://www.haskell.org/onlinereport/basic.html
    859 
    860 #### OCaml
    861 
    862 OCaml only supports multi-element (2 or more) tuples.  It does have the `unit`
    863 type, which has one value, written `()`. Tuple types are written as a '*'
    864 separated list of types, tuple values are written as ',' separated list of
    865 expressions. Pattern matching tuples is supported and uses the same syntax as
    866 values. Parenthesizing these lists is only needed for grouping.
    867 
    868 -   https://ocaml.org/docs/basic-data-types#tuples
    869 
    870 #### Swift
    871 
    872 Swift has tuple types that use the basic parenthesized, comma separated list of
    873 types or values. It only supports 0 and 2 or more element tuples (the `Void`
    874 type is an alias for the empty tuple type).
    875 
    876 Swift also supports named tuples. Names can be added before the tuple element,
    877 both for the tuple type and value. The syntax is a name followed by a colon,
    878 e.g., `(first: int, second: int)`. These names are a fixed part of the type,
    879 and can be used as part of field access notation (otherwise numbers are used
    880 in-place of field names `tuple.0` vs. `tuple.first`).
    881 
    882 -   https://docs.swift.org/swift-book/documentation/the-swift-programming-language/types/#Tuple-Type
    883 
    884 #### Python
    885 
    886 In Python tuples are immutable lists. Because they are dynamically typed,
    887 there is only one tuple type `tuple`.
    888 
    889 It also has various named tuples. The first, namedtuple, allows naming the
    890 elements of the tuple. The second, NamedTuple, is actually a way of creating a
    891 typed record in a normally untyped language.
    892 
    893 -   https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
    894 -   https://docs.python.org/3/library/collections.html#collections.namedtuple
    895 -   https://docs.python.org/3/library/typing.html#typing.NamedTuple
    896 
    897 #### LISP
    898 As LISP is dynamically typed, its `cons` data type is an untyped pair and is
    899 (perhaps infamously) the main constructor of all compound data types.  The
    900 function `cons` takes two arguments and builds a pair. Functions `car` and
    901 `cdr` get the first and second elements of the pair.
    902 
    903 ### Packs
    904 Packs (or unstructured tuples) are a much less common feature. In fact, there
    905 might just be one language, C++, that supports packs. The most common use
    906 case for unstructured tuples is returning multiple values, so there is a
    907 comparison to languages that have that as a special feature.
    908 
    909 #### Go
    910 Go does not have built in tuple types, but it has multi-return syntax that
    911 looks like the tuple syntax of many other languages.
    912 
    913 ```
    914 func main() {
    915         i, j := returnIntInt()
    916         ...
    917 }
    918 
    919 func returnIntInt() (int, int) {
    920         return 12, 34
    921 }
    922 ```
    923 
    924 Go can unpack multiple return values and pass them all to a function, but
    925 the unpacked tuple must match the parameter list exactly.
    926 
    927 ```
    928 func acceptIntInt(a int, b int) {
    929         fmt.Println(a, b)
    930 }
    931 ```
    932 
    933 -   https://golangdocs.com/functions-in-golang
    934 -   https://go.dev/src/go/types/tuple.go
    935 
    936 #### Lua
    937 Lua is a scripting language that is dynamically typed and stack based. Although
    938 the stack is usually only directly visible in the C-API, it does allow any
    939 function to return any number of values, one, zero or more, from any return
    940 expression.
    941 
    942 ```
    943 local funcion f()
    944         return 12, 34
    945 end
    946 
    947 local i, j = f()
    948 ```
    949 
    950 The last argument in an argument list can be an expression - function call -
    951 with multiple results and all the results are passed to the function after
    952 other arguments.
    953 
    954 Because Lua is dynamically typed, multiple return values are allowed anywhere
    955 and the length of the value listed is adjusted, discarding any extra values
    956 and filling in new values with the value `nil`. This is also used if a
    957 function is called with the incorrect number of arguments.
    958 
    959 -   https://lua.org/
    960 -   https://lua.org/manual/5.4/manual.html#3.4.12
    961 
    962 #### C++
    963 
    964 We return to C++ to view template parameter packs. These are the symmetric
    965 with our pack feature (and both are even used to construct the structured
    966 tuples in the last section).
    967 
    968 C++ template parameter packs are a modification that can be applied to any
    969 template parameter, so the parameter now takes a series of arguments instead
    970 of just one. These are used in pack expansion,
     608PAB: I do not understand the syntax `auto [first, second]`. Where does it come
     609from?
     610
     611#### C++ template
     612
     613C++ templates can take various types of parameters, including parameter
     614packs. These contain series of values. These are used in pack expansion,
    971615which usually expand to a comma separated list, but it can also be a chain of
    972616boolean binary operator applications. For example, if the parameter
     
    999643
    1000644There are also fold expressions that use binary operators to combine a pack
    1001 into a single expression. For example, you can write a variadic sum function
    1002 that compiles down to the primitive additions.
    1003 
    1004 ```
    1005 template<typename... Args>
    1006 int sum(Args&&... args) {
    1007         return (args + ... + 0);
    1008 }
    1009 ```
     645into a single expression. For example, `args + ... + 0` which adds every
     646element of the `args` pack together.
    1010647
    1011648C++ is about the best you could ask for in this area, but it does a lot of work
     
    1015652-   https://en.cppreference.com/w/cpp/language/parameter_pack
    1016653-   https://en.cppreference.com/w/cpp/language/fold
     654
     655#### Haskell
     656
     657Haskell has a special syntax for tuples, but otherwise they are completely
     658normal polymorphic types. Because of this, tuples have a maximum size.
     659Haskell (98) supports tuples of up to 15 elements and the standard library
     660has functions for tuples of up to 7 elements.
     661
     662The syntax for tuples is a comma separated list of elements. Either element
     663types to create a tuple type, or element values to create a tuple value. The
     664context decides among them, such as `(6, "six")` or `(Bool, Char, Int)`.
     665
     666Also all the elements can be removed, getting an expression like "(,)" or
     667"(,,,)", which can be then be used as a function, for a type, or an expression.
     668
     669Haskell supports pattern matching as the main way to extract values from a
     670tuple, although helper functions "fst" and "snd" are provided for field
     671access on two element tuples.
     672
     673Haskell does not have 0 or 1-element tuples. The nil type, written "()" for
     674both type and value, is effectively the 0 element tuple. There is also a type
     675called "Solo" that is a polymorphic structure with one field and is used as
     676the 1-element tuple, but has regular Haskell data-type syntax.
     677
     678-   https://www.haskell.org/onlinereport/basic.html
     679
     680#### OCaml
     681
     682OCaml only supports multi-element (2 or more) tuples.  It does have the `unit`
     683type, which has one value, written `()`. Tuple types are written as a '*'
     684separated list of types, tuple values are written as ',' separated list of
     685expressions. Pattern matching tuples is supported and uses the same syntax as
     686values. Parenthesizing these lists is only needed for grouping.
     687
     688-   https://ocaml.org/docs/basic-data-types#tuples
     689
     690#### Swift
     691
     692Swift has tuple types that use the basic parenthesized, comma separated list of
     693types or values. It only supports 0 and 2 or more element tuples (the `Void`
     694type is an alias for the empty tuple type).
     695
     696Swift also supports named tuples. Names can be added before the tuple element,
     697both for the tuple type and value. The syntax is a name followed by a colon,
     698e.g., `(first: int, second: int)`. These names are a fixed part of the type,
     699and can be used as part of field access notation (otherwise numbers are used
     700in-place of field names `tuple.0` vs. `tuple.first`).
     701
     702-   https://docs.swift.org/swift-book/documentation/the-swift-programming-language/types/#Tuple-Type
     703
     704#### Python
     705
     706In Python tuples are immutable lists. Because they are dynamically typed,
     707there is only one tuple type `tuple`.
     708
     709It also has various named tuples. The first, namedtuple, allows naming the
     710elements of the tuple. The second, NamedTuple, is actually a way of creating a
     711typed record in a normally untyped language.
     712
     713-   https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
     714-   https://docs.python.org/3/library/collections.html#collections.namedtuple
     715-   https://docs.python.org/3/library/typing.html#typing.NamedTuple
     716
     717#### LISP
     718As LISP is dynamically typed, its `cons` data type is an untyped pair and is
     719(perhaps infamously) the main constructor of all compound data types.  The
     720function `cons` takes two arguments and builds a pair. Functions `car` and
     721`cdr` get the first and second elements of the pair.
     722
     723### Packs
     724Packs (or unstructured tuples) are a much less common feature. In fact, there
     725might just be one language, C++, that supports packs. The most common use
     726case for unstructured tuples is returning multiple values, so there is a
     727comparison to languages that have that as a special feature.
     728
     729#### Go
     730Go does not have built in tuple types, but it has multi-return syntax that
     731looks like the tuple syntax of many other languages.
     732
     733```
     734func main() {
     735        i, j := returnIntInt()
     736        ...
     737}
     738
     739func returnIntInt() (int, int) {
     740        return 12, 34
     741}
     742```
     743
     744-   https://golangdocs.com/functions-in-golang
     745-   https://go.dev/src/go/types/tuple.go
     746
     747#### Lua
     748Lua is a scripting language that is dynamically typed and stack based. Although
     749the stack is usually only directly visible in the C-API, it does allow any
     750function to return any number of values, even a single return, in the return
     751expression
     752
     753```
     754local funcion f()
     755        return 12, 34
     756end
     757
     758local i, j = f()
     759```
  • doc/theses/mike_brooks_MMath/array.tex

    r06d75931 r3a08cb1  
    502502\end{itemize}
    503503
    504 The traditional C rules are:
    505 \begin{itemize}
    506 \item
    507         Reject a Statically Evaluable pair, if the expressions have two different values.
    508 \item
    509         Otherwise, accept.
    510 \end{itemize}
    511 
    512 
    513 \newcommand{\falsealarm}{{\color{orange}\small{*}}}
    514 \newcommand{\allowmisuse}{{\color{red}\textbf{!}}}
    515 \newcommand{\cmark}{\ding{51}} % from pifont
    516 \newcommand{\xmark}{\ding{55}}
    517 \begin{figure}
    518         \begin{tabular}{@{}l@{\hspace{16pt}}c@{\hspace{8pt}}c@{\hspace{16pt}}c@{\hspace{8pt}}c@{\hspace{16pt}}c}
    519          & \multicolumn{2}{c}{\underline{Values Equal}}
    520          & \multicolumn{2}{c}{\underline{Values Unequal}}
    521          & \\
    522         \textbf{Case}                                & C      & \CFA                & C                      & \CFA    & Compat. \\
    523         Both Statically Evaluable, Same Symbol       & Accept & Accept              &                        &         & \cmark \\
    524         Both Statically Evaluable, Different Symbols & Accept & Accept              & Reject                 & Reject  & \cmark \\
    525         Both Dynamic but Stable, Same Symbol         & Accept & Accept              &                        &         & \cmark \\
    526         Both Dynamic but Stable, Different Symbols   & Accept & Reject\,\falsealarm & Accept\,\allowmisuse   & Reject  & \xmark \\
    527         Both Potentially Unstable, Same Symbol       & Accept & Reject\,\falsealarm & Accept\,\allowmisuse   & Reject  & \xmark \\
    528         Any other grouping, Different Symbol         & Accept & Reject\,\falsealarm & Accept\,\allowmisuse   & Reject  & \xmark
    529         \end{tabular}
    530 
    531         \vspace{12pt}
    532         \noindent\textbf{Legend:}
    533         \begin{itemize}
    534         \item
    535                 Each row gives the treatment of a test harness of the form
    536                 \begin{cfa}
    537                         float x[ expr1 ];
    538                         float (*xp)[ expr2 ] = &x;
    539                 \end{cfa}
    540                 where \lstinline{expr1} and \lstinline{expr2} are metavariables varying according to the row's Case.
    541                 Each row's claim applies to other harnesses too, including,
    542                 \begin{itemize}
    543                 \item
    544                         calling a function with a paramter like \lstinline{x} and an argument of the \lstinline{xp} type,
    545                 \item
    546                         assignment in place of initialization,
    547                 \item
    548                         using references in place of pointers, and
    549                 \item
    550                         for the \CFA array, calling a polymorphic function on two \lstinline{T}-typed parameters with \lstinline{&x}- and \lstinline{xp}-typed arguments.
    551                 \end{itemize}
    552         \item
    553                 Each case's claim is symmetric (swapping \lstinline{expr1} with \lstinline{expr2} has no effect),
    554                 even though most test harnesses are asymetric.
    555         \item
    556                 The table treats symbolic identity (Same/Different on rows)
    557                 apart from value eqality (Equal/Unequal on columns).
    558                 \begin{itemize}
    559                 \item
    560                         The expressions \lstinline{1}, \lstinline{0+1} and \lstinline{n}
    561                         (where \lstinline{n} is a variable with value 1),
    562                         are all different symbols with the value 1.
    563                 \item
    564                         The column distinction expresses ground truth about whether an omniscient analysis should accept or reject.
    565                 \item
    566                         The row distinction expresses the simple static factors used by today's analyses.
    567                 \end{itemize}
    568         \item
    569                 Accordingly, every Reject under Values Equal is a false alarm (\falsealarm),
    570                 while every Accept under Values Unequal is an allowed misuse (\allowmisuse).
    571         \end{itemize}
    572         \caption{Case comparison for array type compatibility, given pairs of dimension expressions.
    573                 TODO: get Peter's LaTeX help on overall appearance, probably including column spacing/centering and bullet indentation.}
    574         \label{f:DimexprRuleCompare}
    575 \end{figure}
    576 
    577 
    578 Figure~\ref{f:DimexprRuleCompare} gives a case-by-case comparison of the consequences of these rule sets.
    579 It demonstrates that the \CFA false alarms occur in the same cases as C treats unsafely.
    580 It also shows that C-incompatibilities only occur in cases that C treats unsafely.
    581 
    582 
    583 The conservatism of the new rule set can leave a programmer needing a recourse,
    584 when needing to use a dimension expression whose stability argument
    585 is more subtle than current-state analysis.
    586 This recourse is to declare an explicit constant for the dimension value.
    587 Consider these two dimension expressions,
    588 whose reuses are rejected by the blunt current-state rules:
    589 \begin{cfa}
    590         void f( int & nr, const int nv ) {
    591                 float x[nr];
    592                 float (*xp)[nr] = & x; // reject: nr varying (no references)
    593                 float y[nv + 1];
    594                 float (*yp)[nv + 1] = & y; // reject: ?+? unpredicable (no functions)
    595         }
    596 \end{cfa}
    597 Yet, both dimension expressions are reused safely.
    598 (The @nr@ reference is never written, not volatile
    599 and control does not leave the function between the uses.
    600 The name @?+?@ resolves to a function that is quite predictable.)
    601 The programmer here can add the constant declarations:
    602 \begin{cfa}
    603         void f( int & nr, const int nv ) {
    604                 @const int nx@ = nr;
    605                 float x[nx];
    606                 float (*xp)[nx] = & x; // acept
    607                 @const int ny@ = nv + 1;
    608                 float y[ny];
    609                 float (*yp)[ny] = & y; // accept
    610         }
    611 \end{cfa}
    612 The result is the originally intended semantics,
    613 achieved by adding a superfluous ``snapshot it as of now'' directive.
    614 
    615 The snapshotting trick is also used by the translation, though to achieve a different outcome.
    616 Rather obviously, every array must be subscriptable, even a bizzarre one:
    617 \begin{cfa}
    618         array( float, rand(10) ) x;
    619         x[0];  // 10% chance of bound-check failure
    620 \end{cfa}
    621 Less obvious is that the mechanism of subscripting is a function call,
    622 which must communicate length accurately.
    623 The bound-check above (callee logic) must use the actual allocated length of @x@,
    624 without mistakenly reevaluating the dimension expression, @rand(10)@.
    625 Adjusting the example to make the function's use of length more explicit:
    626 \begin{cfa}
    627         forall ( T * )
    628         void f( T * x ) { sout | sizeof(*x); }
    629         float x[ rand(10) ];
    630         f( x );
    631 \end{cfa}
    632 Considering that the partly translated function declaration is, loosely,
    633 \begin{cfa}
    634         void f( size_t __sizeof_T, void * x ) { sout | __sizeof_T; }
    635 \end{cfa}
    636 the translated call must not go like:
    637 \begin{cfa}
    638         float x[ rand(10) ];
    639         f( rand(10), &x );
    640 \end{cfa}
    641 Rather, its actual translation is like:
    642 \begin{cfa}
    643         size_t __dim_x = rand(10);
    644         float x[ __dim_x ];
    645         f( __dim_x, &x );
    646 \end{cfa}
    647 The occurrence of this dimension hoisting during translation was present in the preexisting \CFA compiler.
    648 But its cases were buggy, particularly with determining, ``Can hoisting be skipped here?''
    649 For skipping this hoisting is clearly desirable in some cases,
    650 not the least of which is when the programmer has already done so manually.
    651 My work includes getting these cases right, harmonized with the accept/reject criteria, and tested.
    652 
    653 
    654 
    655 TODO: Discuss the interaction of this dimension hoisting with the challenge of extra unification for cost calculation
     504TODO: summarize the C rules and add the case-comparison table
     505
     506TODO: Discuss Recourse
     507
     508TODO: Discuss dimension hoisting, which addresses the challenge of extra unification for cost calculation
    656509
    657510\section{Multidimensional Arrays}
  • doc/theses/mike_brooks_MMath/uw-ethesis.tex

    r06d75931 r3a08cb1  
    8787\usepackage{graphicx}
    8888\usepackage{tabularx}
    89 \usepackage{pifont}
    9089\usepackage[labelformat=simple,aboveskip=0pt,farskip=0pt,font=normalsize]{subfig}
    9190\renewcommand\thesubfigure{(\alph{subfigure})}
  • src/AST/Decl.cpp

    r06d75931 r3a08cb1  
    169169}
    170170
     171const std::string EnumDecl::getUnmangeldArrayName( const ast::EnumAttribute attr ) const {
     172        switch( attr ) {
     173                case ast::EnumAttribute::Value: return "values_" + name ;
     174                case ast::EnumAttribute::Label: return "labels_" + name;
     175                default: /* Posn does not generate array */
     176                        return "";
     177        }
     178}
     179
     180unsigned EnumDecl::calChildOffset(const std::string & target) const{
     181        unsigned offset = 0;
     182        for (auto childEnum: inlinedDecl) {
     183                auto childDecl = childEnum->base;
     184                if (childDecl->name == target) {
     185                        return offset;
     186                }
     187                offset += childDecl->members.size();
     188        }
     189    std::cerr << "Cannot find the target enum" << std::endl;
     190        return 0;
     191}
     192
     193unsigned EnumDecl::calChildOffset(const ast::EnumInstType * target) const{
     194        return calChildOffset(target->base->name);
     195}
     196
     197bool EnumDecl::isSubTypeOf(const ast::EnumDecl * other) const {
     198        if (name == other->name) return true;
     199        for (auto inlined: other->inlinedDecl) {
     200                if (isSubTypeOf(inlined->base)) return true;
     201        }
     202        return false;
     203}
     204
    171205bool EnumDecl::isTyped() const { return base; }
    172206
  • src/AST/Decl.hpp

    r06d75931 r3a08cb1  
    302302};
    303303
    304 /// Enumeration attribute kind.
    305304enum class EnumAttribute{ Value, Posn, Label };
    306 
    307305/// enum declaration `enum Foo { ... };`
    308306class EnumDecl final : public AggregateDecl {
     
    330328        const char * typeString() const override { return aggrString( Enum ); }
    331329
     330        const std::string getUnmangeldArrayName( const EnumAttribute attr ) const;
     331
     332        unsigned calChildOffset(const std::string & childEnum) const;
     333        unsigned calChildOffset(const ast::EnumInstType * childEnum) const;
     334
     335        bool isSubTypeOf(const ast::EnumDecl *) const;
    332336        bool isTyped() const;
    333337        bool isOpaque() const;
  • src/ResolvExpr/CandidateFinder.cpp

    r06d75931 r3a08cb1  
    21362136}
    21372137
    2138 /// If the target enum is a child, get the offset from the base to the target.
    2139 static unsigned findChildOffset(
    2140                 const ast::EnumDecl * decl, const ast::EnumDecl * target ) {
    2141         unsigned offset = 0;
    2142         for ( auto inlined : decl->inlinedDecl ) {
    2143                 auto childDecl = inlined->base;
    2144                 if ( childDecl == target ) {
    2145                         return offset;
    2146                 }
    2147                 offset += childDecl->members.size();
    2148         }
    2149         SemanticError( decl, "Cannot find the target enum." );
    2150 }
    2151 
    21522138const ast::Expr * CandidateFinder::makeEnumOffsetCast( const ast::EnumInstType * src,
    21532139        const ast::EnumInstType * dst, const ast::Expr * expr, Cost minCost ) {
     
    21612147                ast::CastExpr * castToDst;
    21622148                if (c<minCost) {
    2163                         unsigned offset = findChildOffset( dstDecl, dstChild.get()->base );
     2149                        unsigned offset = dstDecl->calChildOffset(dstChild.get());
    21642150                        if (offset > 0) {
    21652151                                auto untyped = ast::UntypedExpr::createCall(
    2166                                         expr->location,
    2167                                         "?+?",
     2152                                        expr->location, 
     2153                                        "?+?", 
    21682154                                        { new ast::CastExpr( expr->location,
    21692155                                                expr,
  • src/ResolvExpr/CommonType.cpp

    r06d75931 r3a08cb1  
    636636        void postvisit( const ast::UnionInstType * ) {}
    637637
    638         /// Is the target enum a child of the base enum?
    639         static bool isChildEnum(
    640                         const ast::EnumDecl * decl, const ast::EnumDecl * target ) {
    641                 if ( decl == target ) return true;
    642                 for ( auto inlined : decl->inlinedDecl ) {
    643                         if ( isChildEnum( inlined->base, target ) ) return true;
    644                 }
    645                 return false;
    646         }
    647 
    648638        void postvisit( const ast::EnumInstType * param ) {
    649639                auto argAsEnumInst = dynamic_cast<const ast::EnumInstType *>(type2);
     
    651641                        const ast::EnumDecl* paramDecl = param->base;
    652642                        const ast::EnumDecl* argDecl = argAsEnumInst->base;
    653                         if ( isChildEnum( paramDecl, argDecl ) ) {
    654                                 result = param;
    655                         }
     643                        if (argDecl->isSubTypeOf(paramDecl)) result = param;
    656644                } else if ( param->base && !param->base->isCfa ) {
    657645                        auto basicType = new ast::BasicType( ast::BasicKind::UnsignedInt );
  • src/ResolvExpr/CurrentObject.cpp

    r06d75931 r3a08cb1  
    6060        /// Retrieve the list of possible (Type,Designation) pairs for the
    6161        /// current position in the current object.
    62         virtual std::deque< InitAlternative > getOptions() const = 0;
     62        virtual std::deque< InitAlternative > operator* () const = 0;
    6363
    6464        /// True if the iterator is not currently at the end.
     
    7777        virtual const Type * getNext() = 0;
    7878
    79         /// Helper for getOptions; aggregates must add designator to each init
    80         /// alternative, but adding designators in getOptions creates duplicates.
     79        /// Helper for operator*; aggregates must add designator to each init
     80        /// alternative, but adding designators in operator* creates duplicates.
    8181        virtual std::deque< InitAlternative > first() const = 0;
    8282};
     
    103103        }
    104104
    105         std::deque< InitAlternative > getOptions() const override { return first(); }
     105        std::deque< InitAlternative > operator* () const override { return first(); }
    106106
    107107        operator bool() const override { return type; }
     
    169169        }
    170170
    171         std::deque< InitAlternative > getOptions() const override { return first(); }
     171        std::deque< InitAlternative > operator* () const override { return first(); }
    172172
    173173        operator bool() const override { return index < size; }
     
    297297        }
    298298
    299         std::deque< InitAlternative > getOptions() const final {
     299        std::deque< InitAlternative > operator* () const final {
    300300                if ( memberIter && *memberIter ) {
    301301                        std::deque< InitAlternative > ret = memberIter->first();
     
    594594        PRINT( std::cerr << "____getting current options" << std::endl; )
    595595        assertf( ! objStack.empty(), "objstack empty in getOptions" );
    596         return objStack.back()->getOptions();
     596        return **objStack.back();
    597597}
    598598
  • src/Validate/ImplementEnumFunc.cpp

    r06d75931 r3a08cb1  
    7878        std::vector<ast::ptr<ast::Init>> genValueInit() const;
    7979        ast::ObjectDecl* genAttrArrayProto(
    80                 const CodeLocation& location, const std::string& prefix,
    81                 const ast::Type * type, std::vector<ast::ptr<ast::Init>>& inits ) const;
     80                const ast::EnumAttribute attr, const CodeLocation& location,
     81                std::vector<ast::ptr<ast::Init>>& inits) const;
    8282};
    8383
     
    351351
    352352ast::ObjectDecl* EnumAttrFuncGenerator::genAttrArrayProto(
    353                 const CodeLocation& location, const std::string& prefix,
    354                 const ast::Type * type, std::vector<ast::ptr<ast::Init>>& inits ) const {
     353        const ast::EnumAttribute attr, const CodeLocation& location,
     354        std::vector<ast::ptr<ast::Init>>& inits) const {
    355355        ast::ArrayType* arrT = new ast::ArrayType(
    356                 type,
     356                attr == ast::EnumAttribute::Value
     357                        ? decl->base
     358                        : new ast::PointerType(
     359                                new ast::BasicType(ast::BasicKind::Char, ast::CV::Const)),
    357360                ast::ConstantExpr::from_int(decl->location, decl->members.size()),
    358361                ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim);
     
    360363        ast::ObjectDecl* objDecl =
    361364                new ast::ObjectDecl(
    362                         decl->location, prefix + decl->name,
     365                        decl->location, decl->getUnmangeldArrayName( attr ),
    363366                        arrT, new ast::ListInit( location, std::move( inits ) ),
    364367                        ast::Storage::Static, ast::Linkage::AutoGen );
     
    414417void EnumAttrFuncGenerator::genTypedEnumFunction(const ast::EnumAttribute attr) {
    415418        if (attr == ast::EnumAttribute::Value) {
    416                 if ( !decl->isTyped() ) return;
    417                 std::vector<ast::ptr<ast::Init>> inits = genValueInit();
    418                 ast::ObjectDecl* arrayProto =
    419                         genAttrArrayProto( getLocation(), "values_", decl->base, inits );
    420                 forwards.push_back(arrayProto);
    421 
    422                 ast::FunctionDecl* funcProto = genValueProto();
    423                 produceForwardDecl(funcProto);
    424                 genValueOrLabelBody(funcProto, arrayProto);
    425                 produceDecl(funcProto);
     419                if (decl->isTyped()) {
     420                        // TypedEnum's backing arrays
     421                        std::vector<ast::ptr<ast::Init>> inits = genValueInit();
     422                        ast::ObjectDecl* arrayProto =
     423                                genAttrArrayProto(attr, getLocation(), inits);
     424                        forwards.push_back(arrayProto);
     425
     426                        ast::FunctionDecl* funcProto = genValueProto();
     427                        produceForwardDecl(funcProto);
     428                        genValueOrLabelBody(funcProto, arrayProto);
     429                        produceDecl(funcProto);
     430                } 
     431                // else {
     432                //      ast::FunctionDecl* funcProto = genQuasiValueProto();
     433                //      produceForwardDecl(funcProto);
     434                //      // genQuasiValueBody(funcProto);
     435                //      produceDecl(funcProto);
     436                // }
    426437        } else if (attr == ast::EnumAttribute::Label) {
    427438                std::vector<ast::ptr<ast::Init>> inits = genLabelInit();
    428                 const ast::Type * type = new ast::PointerType(
    429                         new ast::BasicType( ast::BasicKind::Char, ast::CV::Const ) );
    430439                ast::ObjectDecl* arrayProto =
    431                         genAttrArrayProto( getLocation(), "labels_", type, inits );
     440                        genAttrArrayProto(attr, getLocation(), inits);
    432441                forwards.push_back(arrayProto);
    433442                ast::FunctionDecl* funcProto = genLabelProto();
     
    436445                produceDecl(funcProto);
    437446        } else {
    438                 assert( attr == ast::EnumAttribute::Posn );
    439447                ast::FunctionDecl* funcProto = genPosnProto();
    440448                produceForwardDecl(funcProto);
Note: See TracChangeset for help on using the changeset viewer.