Changes in / [06d75931:3a08cb1]
- Files:
-
- 9 edited
-
doc/proposals/tuples.md (modified) (26 diffs)
-
doc/theses/mike_brooks_MMath/array.tex (modified) (1 diff)
-
doc/theses/mike_brooks_MMath/uw-ethesis.tex (modified) (1 diff)
-
src/AST/Decl.cpp (modified) (1 diff)
-
src/AST/Decl.hpp (modified) (2 diffs)
-
src/ResolvExpr/CandidateFinder.cpp (modified) (2 diffs)
-
src/ResolvExpr/CommonType.cpp (modified) (2 diffs)
-
src/ResolvExpr/CurrentObject.cpp (modified) (6 diffs)
-
src/Validate/ImplementEnumFunc.cpp (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/proposals/tuples.md
r06d75931 r3a08cb1 2 2 ====== 3 3 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. 4 Tuples were introduced by Dave Till in K-W C, added to CFA by Rodolfo Esteves, 5 and extended by Robert Schluntz. This proposal discusses updates for CFA tuples 6 to address problems that have appeared in their usage over the past 6 years. 7 The proposal attempts to address problems by adding new functionality, updating 8 existing features, and removing some problematic ones. 10 9 11 10 The core change is breaking the current restructurable tuples into unstructured … … 15 14 Current State of Tuples 16 15 ----------------------- 17 An overview of the current tuple design is the starting place for the proposal.16 An overview of the current tuples design is the starting place for the proposal. 18 17 19 18 ### Tuple Syntax … … 23 22 (deconstructing tuples). 24 23 25 Current syntax for tuple types :24 Current syntax for tuple types. 26 25 27 26 - Nullary: [void] or [] 28 29 [void] f();30 [] f();31 32 27 - 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 30 Tuple types can appear in a function return and parameter declaration, or a 31 tuple variable declaration. Note, the Nullary tuple is only meaningful in the 32 return context, 33 34 void f(...); // equivalent 35 [void] f(...); 36 [] f(...); 37 38 as C does not support a void declaration. 39 40 int f( void x ); // disallowed 41 int f( [void] x ); 42 int f( [] x ); 46 43 47 44 Current syntax for tuple expressions: … … 49 46 - Nullary: (Same as `void`, use return without an expression.) 50 47 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 60 Currently, there is a parsing problem for nullary and unary tuple expression, 61 which is being looked at. Hence, only these two forms work. 62 63 [] f( ) { return; } // nullary 64 [int] f( ) { return 3; } // unary 65 66 66 Current syntax for tuple indexing is an integer constant, where its value 67 67 selects a tuple member, e.g.: 68 68 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 72 73 73 74 ### Mass and Multi-Assignment … … 78 79 79 80 [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 83 83 84 84 Multi-assignment assigns every element in the destination tuple to the 85 85 corresponding element in the source tuple. Both tuples must be the same size 86 and the elements pairs must be assignment compatibleconversions.86 and the elements assignment compatible => conversions. 87 87 88 88 [long, int, float] dst; 89 89 [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 92 91 93 92 ### Tuple Restructuring … … 111 110 parmFunc(fst.0, fst.1, snd.0, snd.1); 112 111 112 There are few languages supporting multiple return-values as a standalone 113 feature (SETL). Go has multiple return-values but restricts their use in 114 matching 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 113 126 ### Tuple Casts 114 127 … … 137 150 [int, [bool, char], int] w; 138 151 [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 142 154 143 155 ### Polymorphic Tuple Parameters … … 148 160 sequence of types. 149 161 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 156 166 T max(T arg, Ts args) { 157 167 return max(arg, max(args)); 158 168 } 159 169 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 } 170 This feature introduces a type name into scope (Ts). It is used as a type but 171 because the tuple is flattened, the second assertion "T max(Ts);" matches types 172 with multiple parameters (the `...`), although it is used as a tuple function 173 inside the function body (max(args)). 174 175 The first non-recursive max function is the polymorphic base-case for the 176 recursion, i.e., find the maximum of two identically typed values with a 177 greater-than (>) operator. The second recursive max function takes two 178 parameters, a T and a Ts tuple, handling all argument lengths greater than two. 179 The recursive function computes the maximum for the first argument and the 180 maximum value of the rest of the tuple. The call of max with one argument is 181 the recursive call, where the tuple is converted into two arguments by taking 182 the first value (lisp car) from the tuple pack as the first argument 183 (flattening) and the remaining pack becomes the second argument (lisp cdr). 184 The recursion stops when the tuple is empty. For example, max( 2, 3, 4 ) 185 matches 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 187 empty. 188 189 189 190 190 Issues with the Current State … … 199 199 be. 200 200 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). 201 Because of the fluid nature of tuples (flattening/structuring), routines like 202 constructions, destructor, or assignment do not make sense, e.g. this 203 constructor 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 210 as could a similarly typed destructor or assignment operator. This prevents 211 tuples from being interwoven with regular polymorphic code. 219 212 220 213 ### Providing TType Arguments is Inconsistent … … 224 217 functions and there are very few existing use-cases for ttype structures. 225 218 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. 219 Passing arguments to a function inlines the arguments 220 while passing them to a polymorphic type requires them to be 221 enclosed in a tuple. Compare `function(x, y, z)` with `Type(A, [B, C])`. 222 223 This did not come up previously as there was little reason to explicitly 224 provide ttype arguments. They are implicit for functions and there is very 225 little use case for a ttype on a struct or union. 248 226 249 227 ### Syntax Conflict 250 228 251 The tuple syntax conflicts with designators and the C23 (C++-style)attribute229 The tuple syntax conflicts with designators and the new C++-style attribute 252 230 syntax. 253 231 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 264 234 265 235 These conflicts break C compatibility goals of Cforall. Designators had to 266 236 their syntax change and Cforall cannot parse the new attributes. 267 The behaviour of these cases, if they could be parsed, should be unchanged. 237 238 Although most of this redesign is about the semantics of tuples, but an update 239 to tuple syntax that removes these conflicts would improve the compatibility of 240 Cforall going forward (and also open up the new attribute syntax for cforall 241 features). 268 242 269 243 Change in Model 270 244 --------------- 271 245 This 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 246 cases. There are some use cases that are no longer handled, and a new 247 struct tuple is added to cover those cases. 248 249 The new tuples is even more "unstructured" than before. New tuples are 277 250 considered a sequence of types or typed entities. These packs are then unpacked 278 251 into the surrounding context. Put differently, tuples are now flattened as much … … 286 259 unstructured tuples no longer support. 287 260 288 Note that the underlying implementation might not actually look like this, 289 but this is how it should seem to the user. 261 Note that the underlying implementation might not actually look like this. 290 262 291 263 Changed Features … … 293 265 Some of the concrete changes to the features of the language. 294 266 295 ### Unstructured Tuple / Pack Type296 297 The evolution of our current tuples (called packs in an attempt to make the298 names more distinct). They work like the current tuples except they are299 always flattened. You might still be able to declare nested tuples, but these300 are not meaningful, they are flattened to a single level and, where301 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 resolution316 in the way the do currently. Restructuring is always free because they are317 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. They320 cannot be used as object types, nor as any data type, but they can be still321 used as ttypes.322 323 (Unless they need to have lifetime functions for other features.)324 325 267 ### Structured Tuple Type 326 268 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 likethis:269 There is a standard library or built-in type named `tuple`, it does not need a 270 special syntax to write types or instances. The type definition might need some 271 primitive support, but if supported as a regular type would look something like 272 this: 331 273 332 274 forall(Ts...) 333 275 struct tuple { 334 inline Ts all; 276 inline Ts all; // inline all specified fields 335 277 }; 336 278 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.) 279 This type is constructed the same way as most types, a list initializer with 280 each tuple argument, and the lifetime functions (construction, assignment and 281 destruction) work the same. Field access works two ways, the first is accessing 282 the `all` field, effectively converting the structured tuple into an 283 unstructured tuple, the other is to use tuple indexing directly on the 284 structure 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 287 longer but has no change in functionality. If the `all` access does not work, 288 that is more problematic, and tuple slicing might provide a work around.) 367 289 368 290 ### Type Qualifier Distribution … … 408 330 up, that cannot be enforced with two different tuples. 409 331 410 This example doesn't have to be implemented this way because we do have the411 special case operations for these already.412 413 332 ### Type Packs 414 333 … … 452 371 453 372 For local declarations it works similarly, except the name introduced is 454 directly usable as a tuple instead of going through a member access.373 directly usable as a tuple. 455 374 456 375 forall(Objects...) … … 461 380 } 462 381 463 ### Tuple Pack Restructuring464 465 Implicit restructuring still occurs, but is not considered as part of466 overload resolution. The structure of tuples is ignored, where individual467 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 the470 parameters/callee) before type resolution. Both any nested packs and packs471 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 you480 can consider a parameter list as a single pack. Return types could be viewed481 in this way, but because they are presented in a single type, they are482 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 with491 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 tuple501 and void, for instance creating some instances for consistency with the other502 tuple types.503 504 The struct tuple is never implicitly restructured.505 506 382 ### Tuple Declaration/Deconstruction 507 383 … … 510 386 elements of the tuple can be named. 511 387 512 [int quo, int rem] quorem= divmod(a, b);388 [int quo, int rem] ret = divmod(a, b); 513 389 514 390 Here `ret` refers to the tuple, the entire pack, while `quo` and `rem` … … 520 396 no named to access it. 521 397 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`) 398 PAB: I do understand the point of this. 526 399 527 400 ### Tuple Casts … … 532 405 so it may be replaced with other features (for example: tuple slicing). 533 406 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 409 The unstructured tuple cannot represent all the types that the previous 410 semi-structured tuple could. These cases still exist in various ways, 411 specifically in the internals of a polymorphic type, but in general should be 412 considered in their reduced form. 413 414 Forbidding some of these tuples may remove ambiguity and solve some syntax 415 conflicts that currently exist. 416 417 Nullary, or 0 element tuples, are equivalent to void, the type that carries 418 no data, because they do not either. It should either be replaced with void 419 or 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 428 Unary, or 1 element tuples, should be the same as their element type. That 429 is to say a single type in an unstructured tuple is a no-op. 430 431 Lastly, nested tuples are always flattened to a one-depth tuple. This means 432 that `[bool, [char, int], float]` is resolved as `[bool, char, int, float]`, 433 with the internal structure of the tuple ignored. 434 435 The flatten into a large sequence rule mentioned above is actually just an 436 application of this. Unstructured tuples can already be restructured, even at 437 the top level of an function call. This can be expressed by considering the 438 argument 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 445 The ability to write nested tuples may remain so tuple deconstruction can be 446 used to name a slice of the tuple. 583 447 584 448 ### Tuple Slicing (Range Tuple Indexing) … … 590 454 index expression to be a list-range a multiple sub-tuples can be extracted. 591 455 592 [ 'a', 'b', 'c', 'd', 'e', 'f'].0~3593 ['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 458 produces 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, 463 index the first 3 tuple elements, and then indexes elements 5 to 9 in steps of 464 2. Not all selections are possible with this mechanism (e.g., index the 465 Fibonacci elements), but it covers many cases. 602 466 603 467 To get the new tuple, the range has to be constructed and traversed at compile … … 622 486 The iterators proposal has a section on some new range types, this feature 623 487 is 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. 488 that is also used in the special for loop. And compile-time evaluation may 489 need to be improved. 490 491 PAB, I don't understand this last part as the index range is compile time not 492 runtime. 627 493 628 494 Implementation … … 630 496 An overview of the implementation details of the new proposal. 631 497 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 500 Under the hood, unstructured tuples are implemented as structured tuples, with 501 the restructuring code inserted wherever needed. In short, the base 502 implementation should stay mostly the same. 503 504 PAB: The current implementation does not use convert unstructured tuples to 505 structured tuples. Look at the code generated for 506 507 int x, y; 508 [x, y] = 3; 509 [x, y] = [y, x]; 510 511 512 Actually inlining tuples can be done in some cases, it may even help with 513 some forms like a fixed tuple decomposition. However, polymorphic tuples 514 still need to be reduced to a single generic form, which would be a 515 polymorphic container, a tuple. 737 516 738 517 ### AST Updates 739 518 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`). 519 The current AST cannot represent all the new features. Particularly, an 520 object declaration cannot name elements of the tuple. To this end a new 521 node type, `TupleDecl`, should be added to handle tuple deconstruction 522 declarations (other cases can still be handled with `ObjectDecl`). 748 523 749 524 This would act much like a `FunctionDecl` except for tuples, narrowing the … … 752 527 elements of the tuples. 753 528 529 PAB: the following parses: 530 531 [int x, int y] foo( int p ); 532 533 and discussed by Till. 534 754 535 (I'm not actually going to decide the implementation now, but some early 755 536 examination of the problem suggests that it might be better off wrapping a 756 537 series of `ObjectDecl` rather than just some strings to hold the names.) 757 538 539 ### Field Packs 540 541 Field packs in structures probably have to be written out in full by the 542 specialization pass. If not, it could have some negative effects on layout, 543 causing a structure to take up extra space. It may be able to reuse some of the 544 specialization code for the existing tuples. 545 758 546 Related Features in Other Languages 759 547 ----------------------------------- 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). 548 Other languages have similar features. Organized by the related feature. 762 549 763 550 (In hindsight, I may have gone overboard with the number of examples.) … … 797 584 - https://doc.rust-lang.org/reference/expressions/tuple-expr.html 798 585 799 #### C++ 586 #### C++ tuple 800 587 801 588 Implemented as a template type defined in the standard library. No special … … 811 598 used, e.g., `std::get<0>( tuple )`. 812 599 813 C++ is an interesting case because it is one of the few cases where no part814 of the tuple system is built-in. It is entirely created in the standard815 library using templates. This also has massive error messages when something816 goes wrong, hopefully our version of struct tuple will reduce the problem.817 818 600 C++ 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`). 601 structured binding declaration, you can write an auto typed declaration with 602 a list of identifiers in a `[]` list. 603 For example, `auto [first, second] = getSize2Tuple();`. 831 604 832 605 - https://en.cppreference.com/w/cpp/utility/tuple 833 606 - https://en.cppreference.com/w/cpp/language/structured_binding 834 607 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, 608 PAB: I do not understand the syntax `auto [first, second]`. Where does it come 609 from? 610 611 #### C++ template 612 613 C++ templates can take various types of parameters, including parameter 614 packs. These contain series of values. These are used in pack expansion, 971 615 which usually expand to a comma separated list, but it can also be a chain of 972 616 boolean binary operator applications. For example, if the parameter … … 999 643 1000 644 There 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 ``` 645 into a single expression. For example, `args + ... + 0` which adds every 646 element of the `args` pack together. 1010 647 1011 648 C++ is about the best you could ask for in this area, but it does a lot of work … … 1015 652 - https://en.cppreference.com/w/cpp/language/parameter_pack 1016 653 - https://en.cppreference.com/w/cpp/language/fold 654 655 #### Haskell 656 657 Haskell has a special syntax for tuples, but otherwise they are completely 658 normal polymorphic types. Because of this, tuples have a maximum size. 659 Haskell (98) supports tuples of up to 15 elements and the standard library 660 has functions for tuples of up to 7 elements. 661 662 The syntax for tuples is a comma separated list of elements. Either element 663 types to create a tuple type, or element values to create a tuple value. The 664 context decides among them, such as `(6, "six")` or `(Bool, Char, Int)`. 665 666 Also 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 669 Haskell supports pattern matching as the main way to extract values from a 670 tuple, although helper functions "fst" and "snd" are provided for field 671 access on two element tuples. 672 673 Haskell does not have 0 or 1-element tuples. The nil type, written "()" for 674 both type and value, is effectively the 0 element tuple. There is also a type 675 called "Solo" that is a polymorphic structure with one field and is used as 676 the 1-element tuple, but has regular Haskell data-type syntax. 677 678 - https://www.haskell.org/onlinereport/basic.html 679 680 #### OCaml 681 682 OCaml only supports multi-element (2 or more) tuples. It does have the `unit` 683 type, which has one value, written `()`. Tuple types are written as a '*' 684 separated list of types, tuple values are written as ',' separated list of 685 expressions. Pattern matching tuples is supported and uses the same syntax as 686 values. Parenthesizing these lists is only needed for grouping. 687 688 - https://ocaml.org/docs/basic-data-types#tuples 689 690 #### Swift 691 692 Swift has tuple types that use the basic parenthesized, comma separated list of 693 types or values. It only supports 0 and 2 or more element tuples (the `Void` 694 type is an alias for the empty tuple type). 695 696 Swift also supports named tuples. Names can be added before the tuple element, 697 both for the tuple type and value. The syntax is a name followed by a colon, 698 e.g., `(first: int, second: int)`. These names are a fixed part of the type, 699 and can be used as part of field access notation (otherwise numbers are used 700 in-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 706 In Python tuples are immutable lists. Because they are dynamically typed, 707 there is only one tuple type `tuple`. 708 709 It also has various named tuples. The first, namedtuple, allows naming the 710 elements of the tuple. The second, NamedTuple, is actually a way of creating a 711 typed 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 718 As 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 720 function `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 724 Packs (or unstructured tuples) are a much less common feature. In fact, there 725 might just be one language, C++, that supports packs. The most common use 726 case for unstructured tuples is returning multiple values, so there is a 727 comparison to languages that have that as a special feature. 728 729 #### Go 730 Go does not have built in tuple types, but it has multi-return syntax that 731 looks like the tuple syntax of many other languages. 732 733 ``` 734 func main() { 735 i, j := returnIntInt() 736 ... 737 } 738 739 func 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 748 Lua is a scripting language that is dynamically typed and stack based. Although 749 the stack is usually only directly visible in the C-API, it does allow any 750 function to return any number of values, even a single return, in the return 751 expression 752 753 ``` 754 local funcion f() 755 return 12, 34 756 end 757 758 local i, j = f() 759 ``` -
doc/theses/mike_brooks_MMath/array.tex
r06d75931 r3a08cb1 502 502 \end{itemize} 503 503 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 504 TODO: summarize the C rules and add the case-comparison table 505 506 TODO: Discuss Recourse 507 508 TODO: Discuss dimension hoisting, which addresses the challenge of extra unification for cost calculation 656 509 657 510 \section{Multidimensional Arrays} -
doc/theses/mike_brooks_MMath/uw-ethesis.tex
r06d75931 r3a08cb1 87 87 \usepackage{graphicx} 88 88 \usepackage{tabularx} 89 \usepackage{pifont}90 89 \usepackage[labelformat=simple,aboveskip=0pt,farskip=0pt,font=normalsize]{subfig} 91 90 \renewcommand\thesubfigure{(\alph{subfigure})} -
src/AST/Decl.cpp
r06d75931 r3a08cb1 169 169 } 170 170 171 const 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 180 unsigned 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 193 unsigned EnumDecl::calChildOffset(const ast::EnumInstType * target) const{ 194 return calChildOffset(target->base->name); 195 } 196 197 bool 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 171 205 bool EnumDecl::isTyped() const { return base; } 172 206 -
src/AST/Decl.hpp
r06d75931 r3a08cb1 302 302 }; 303 303 304 /// Enumeration attribute kind.305 304 enum class EnumAttribute{ Value, Posn, Label }; 306 307 305 /// enum declaration `enum Foo { ... };` 308 306 class EnumDecl final : public AggregateDecl { … … 330 328 const char * typeString() const override { return aggrString( Enum ); } 331 329 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; 332 336 bool isTyped() const; 333 337 bool isOpaque() const; -
src/ResolvExpr/CandidateFinder.cpp
r06d75931 r3a08cb1 2136 2136 } 2137 2137 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 2152 2138 const ast::Expr * CandidateFinder::makeEnumOffsetCast( const ast::EnumInstType * src, 2153 2139 const ast::EnumInstType * dst, const ast::Expr * expr, Cost minCost ) { … … 2161 2147 ast::CastExpr * castToDst; 2162 2148 if (c<minCost) { 2163 unsigned offset = findChildOffset( dstDecl, dstChild.get()->base);2149 unsigned offset = dstDecl->calChildOffset(dstChild.get()); 2164 2150 if (offset > 0) { 2165 2151 auto untyped = ast::UntypedExpr::createCall( 2166 expr->location, 2167 "?+?", 2152 expr->location, 2153 "?+?", 2168 2154 { new ast::CastExpr( expr->location, 2169 2155 expr, -
src/ResolvExpr/CommonType.cpp
r06d75931 r3a08cb1 636 636 void postvisit( const ast::UnionInstType * ) {} 637 637 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 648 638 void postvisit( const ast::EnumInstType * param ) { 649 639 auto argAsEnumInst = dynamic_cast<const ast::EnumInstType *>(type2); … … 651 641 const ast::EnumDecl* paramDecl = param->base; 652 642 const ast::EnumDecl* argDecl = argAsEnumInst->base; 653 if ( isChildEnum( paramDecl, argDecl ) ) { 654 result = param; 655 } 643 if (argDecl->isSubTypeOf(paramDecl)) result = param; 656 644 } else if ( param->base && !param->base->isCfa ) { 657 645 auto basicType = new ast::BasicType( ast::BasicKind::UnsignedInt ); -
src/ResolvExpr/CurrentObject.cpp
r06d75931 r3a08cb1 60 60 /// Retrieve the list of possible (Type,Designation) pairs for the 61 61 /// current position in the current object. 62 virtual std::deque< InitAlternative > getOptions() const = 0;62 virtual std::deque< InitAlternative > operator* () const = 0; 63 63 64 64 /// True if the iterator is not currently at the end. … … 77 77 virtual const Type * getNext() = 0; 78 78 79 /// Helper for getOptions; aggregates must add designator to each init80 /// alternative, but adding designators in getOptionscreates duplicates.79 /// Helper for operator*; aggregates must add designator to each init 80 /// alternative, but adding designators in operator* creates duplicates. 81 81 virtual std::deque< InitAlternative > first() const = 0; 82 82 }; … … 103 103 } 104 104 105 std::deque< InitAlternative > getOptions() const override { return first(); }105 std::deque< InitAlternative > operator* () const override { return first(); } 106 106 107 107 operator bool() const override { return type; } … … 169 169 } 170 170 171 std::deque< InitAlternative > getOptions() const override { return first(); }171 std::deque< InitAlternative > operator* () const override { return first(); } 172 172 173 173 operator bool() const override { return index < size; } … … 297 297 } 298 298 299 std::deque< InitAlternative > getOptions() const final {299 std::deque< InitAlternative > operator* () const final { 300 300 if ( memberIter && *memberIter ) { 301 301 std::deque< InitAlternative > ret = memberIter->first(); … … 594 594 PRINT( std::cerr << "____getting current options" << std::endl; ) 595 595 assertf( ! objStack.empty(), "objstack empty in getOptions" ); 596 return objStack.back()->getOptions();596 return **objStack.back(); 597 597 } 598 598 -
src/Validate/ImplementEnumFunc.cpp
r06d75931 r3a08cb1 78 78 std::vector<ast::ptr<ast::Init>> genValueInit() const; 79 79 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; 82 82 }; 83 83 … … 351 351 352 352 ast::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 { 355 355 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)), 357 360 ast::ConstantExpr::from_int(decl->location, decl->members.size()), 358 361 ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim); … … 360 363 ast::ObjectDecl* objDecl = 361 364 new ast::ObjectDecl( 362 decl->location, prefix + decl->name,365 decl->location, decl->getUnmangeldArrayName( attr ), 363 366 arrT, new ast::ListInit( location, std::move( inits ) ), 364 367 ast::Storage::Static, ast::Linkage::AutoGen ); … … 414 417 void EnumAttrFuncGenerator::genTypedEnumFunction(const ast::EnumAttribute attr) { 415 418 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 // } 426 437 } else if (attr == ast::EnumAttribute::Label) { 427 438 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 ) );430 439 ast::ObjectDecl* arrayProto = 431 genAttrArrayProto( getLocation(), "labels_", type, inits);440 genAttrArrayProto(attr, getLocation(), inits); 432 441 forwards.push_back(arrayProto); 433 442 ast::FunctionDecl* funcProto = genLabelProto(); … … 436 445 produceDecl(funcProto); 437 446 } else { 438 assert( attr == ast::EnumAttribute::Posn );439 447 ast::FunctionDecl* funcProto = genPosnProto(); 440 448 produceForwardDecl(funcProto);
Note:
See TracChangeset
for help on using the changeset viewer.