Changes in / [ce02877:38e20a80]
- Files:
-
- 5 deleted
- 13 edited
-
doc/theses/mike_brooks_MMath/array.tex (modified) (25 diffs)
-
doc/theses/mike_brooks_MMath/programs/hello-accordion.cfa (modified) (5 diffs)
-
doc/theses/mike_brooks_MMath/programs/hello-array.cfa (modified) (2 diffs)
-
doc/theses/mike_brooks_MMath/programs/hello-md.cfa (modified) (1 diff)
-
doc/theses/mike_brooks_MMath/uw-ethesis-frontpgs.tex (modified) (1 diff)
-
doc/theses/mike_brooks_MMath/uw-ethesis.tex (modified) (1 diff)
-
doc/user/user.tex (modified) (7 diffs)
-
libcfa/src/collections/array.hfa (modified) (9 diffs)
-
libcfa/src/enum.cfa (modified) (1 diff)
-
src/GenPoly/Box.cpp (modified) (12 diffs)
-
src/Parser/parser.yy (modified) (4 diffs)
-
tests/Makefile.am (modified) (3 diffs)
-
tests/array-collections/.expect/boxed.txt (deleted)
-
tests/array-collections/array-sbscr-types.cfa (modified) (1 diff)
-
tests/array-collections/boxed.bookend.cfa (deleted)
-
tests/array-collections/boxed.cases.hfa (deleted)
-
tests/array-collections/boxed.hfa (deleted)
-
tests/array-collections/boxed.main.cfa (deleted)
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/mike_brooks_MMath/array.tex
rce02877 r38e20a80 6 6 7 7 Arrays in C are possible the single most misunderstood and incorrectly used features in the language, resulting in the largest proportion of runtime errors and security violations. 8 This chapter describes the new \CFA language and library features that introduce a length-checked array-type to the \CFA standard library~\cite{Cforall}. 9 10 Specifically, a new \CFA array is declared: 11 \begin{cfa} 12 @array( float, 99 )@ x; $\C[2.75in]{// x contains 99 floats}$ 13 \end{cfa} 14 using generic type @array@ with arguments @float@ and @99@. 15 A function @f@ is declared with an @array@ parameter of length @42@. 16 \begin{cfa} 8 This chapter describes the new \CFA language and library features that introduce a length-checked array-type to the \CFA standard library~\cite{Cforall}, \eg: 9 \begin{cfa} 10 @array( float, 99 )@ x; $\C{// x contains 99 floats}$ 17 11 void f( @array( float, 42 )@ & p ) {} $\C{// p accepts 42 floats}$ 18 12 f( x ); $\C{// statically rejected: types are different, 99 != 42}$ 19 13 20 test2.cfa:3:1 error: Invalid application of existing declaration(s) in expression. 21 Applying untyped: Name: f ... to: Name: x 22 \end{cfa} 14 forall( T, [N] ) 15 void g( @array( T, N )@ & p, int i ) { 16 T elem = p[i]; $\C{// dynamically checked: requires 0 <= i < N}$ 17 } 18 g( x, 0 ); $\C{// T is float, N is 99, dynamic subscript check succeeds}$ 19 g( x, 1000 ); $\C{// T is float, N is 99, dynamic subscript check fails}$ 20 \end{cfa} 21 This example declares variable @x@, with generic type @array@ using arguments @float@ and @99@. 22 Function @f@ is declared with an @array@ parameter of length @42@. 23 23 The call @f( x )@ is invalid because the @array@ lengths @99@ and @42@ do not match. 24 25 24 Next, function @g@ introduces a @forall@ prefix on type parameter @T@ and arbitrary \emph{dimension parameter} @N@, the new feature that represents a count of elements managed by the type system. 26 \begin{cfa}27 forall( T, @[N]@ )28 void g( array( T, @N@ ) & p, int i ) {29 T elem = p[i]; $\C{// dynamically checked: requires 0 <= i < N}$30 }31 g( x, 0 ); $\C{// T is float, N is 99, dynamic subscript check succeeds}$32 g( x, 1000 ); $\C{// T is float, N is 99, dynamic subscript check fails}\CRT$33 34 Cforall Runtime error: subscript 1000 exceeds dimension range [0,99) $for$ array 0x555555558020.35 \end{cfa}36 25 The call @g( x, 0 )@ is valid because @g@ accepts any length of array, where the type system infers @float@ for @T@ and length @99@ for @N@. 37 26 Inferring values for @T@ and @N@ is implicit without programmer involvement. … … 46 35 forall( [N] ) 47 36 void declDemo() { 48 float x1[N]; $\C{// built-in type ("C array")}$49 array(float, N) x2; $\C{// type from library}$37 float x1[N]; $\C{// built-in type ("C array")}$ 38 array(float, N) x2; $\C{// type from library}$ 50 39 } 51 40 \end{cfa} 52 41 Both of the locally-declared array variables, @x1@ and @x2@, have 42 elements, each element being a @float@. 53 42 The two variables have identical size and layout; they both encapsulate 42-float, stack \vs heap allocations with no additional ``bookkeeping'' allocations or headers. 54 Providing this explicit generic approach require sa significant extension to the \CFA type system to support a full-feature, safe, efficient (space and time) array-type, which forms the foundation for more complex array forms in \CFA.43 Providing this explicit generic approach required a significant extension to the \CFA type system to support a full-feature, safe, efficient (space and time) array-type, which forms the foundation for more complex array forms in \CFA. 55 44 56 45 Admittedly, the @array@ library type (type for @x2@) is syntactically different from its C counterpart. 57 46 A future goal (TODO xref) is to provide a built-in array type with syntax approaching C's (type for @x1@); 58 47 then, the library @array@ type can be removed giving \CFA a largely uniform array type. 59 At present, the C syntax @array@is only partially supported, so the generic @array@ is used exclusively in the discussion;48 At present, the built-in array is only partially supported, so the generic @array@ is used exclusively in the discussion; 60 49 feature support and C compatibility are revisited in Section ? TODO. 61 50 62 Offering the @array@ type, as a distinct alternative to the C array, is consistent with \CFA's goal of backwards compatibility, \ie virtually all existing C (@gcc@) programs can be compiled by \CFA with only a small number of changes, similar to \CC (@g++@).51 Offering an @array@ type, as a distinct alternative to the C array, is consistent with \CFA's goal of backwards compatibility, \ie virtually all existing C (gcc) programs can be compiled by \CFA with only a small number of changes, similar to \CC (g++). 63 52 However, a few compatibility-breaking changes to the behaviour of the C array are necessary, both as an implementation convenience and to fix C's lax treatment of arrays. 64 53 Hence, the @array@ type is an opportunity to start from a clean slate and show a cohesive selection of features, making it unnecessary to deal with every inherited complexity introduced by the C array TODO xref. 65 54 66 My contributions in this chapterare:55 My contributions are: 67 56 \begin{enumerate} 68 57 \item A type system enhancement that lets polymorphic functions and generic types be parameterized by a numeric value: @forall( [N] )@. … … 111 100 \end{figure} 112 101 113 \VRef[Figure]{f:fHarness} shows a harness that uses function @f@ to illustratehow dynamic values are fed into the @array@ type.114 Here, the dimension of arrays @x@, @y@, and @result@ is specified from a command-line value, @dim@,and these arrays are allocated on the stack.102 \VRef[Figure]{f:fHarness} shows a harness that uses the @f@ function illustrating how dynamic values are fed into the @array@ type. 103 Here, the dimension of the @x@, @y@, and @result@ arrays is specified from a command-line value and these arrays are allocated on the stack. 115 104 Then the @x@ array is initialized with decreasing values, and the @y@ array with amounts offset by constant @0.005@, giving relative differences within tolerance initially and diverging for later values. 116 105 The program main is run (see figure bottom) with inputs @5@ and @7@ for sequence lengths. 117 The loops follow the familiar pattern of using the variable @ dim@ to iterate through the arrays.118 Most importantly, the type system implicitly captures @ dim@ at the call of @f@ and makes it available throughout @f@ as @N@.119 The example shows @ dim@ adapting into a type-system managed length at the declarations of @x@, @y@, and @result@, @N@ adapting in the same way at @f@'s loop bound, and a pass-thru use of @dim@ at @f@'s declaration of @ret@.106 The loops follow the familiar pattern of using the variable @n@ to iterate through the arrays. 107 Most importantly, the type system implicitly captures @n@ at the call of @f@ and makes it available throughout @f@ as @N@. 108 The example shows @n@ adapting into a type-system managed length at the declarations of @x@, @y@, and @result@, @N@ adapting in the same way at @f@'s loop bound, and a pass-thru use of @n@ at @f@'s declaration of @ret@. 120 109 Except for the lifetime-management issue of @result@, \ie explicit @free@, this program has eliminated both the syntactic and semantic problems associated with C arrays and their usage. 121 110 These benefits cannot be underestimated. … … 152 141 \CC has a (mistaken) belief that references are not objects, but pointers are objects. 153 142 In the \CC example, the arrays fall back on C arrays, which have a duality with references with respect to automatic dereferencing. 154 The \CFA array is a contiguous object with an address, which can bestored as a reference or pointer.143 The \CFA array is a contiguous object with an address, which can stored as a reference or pointer. 155 144 \item 156 145 C/\CC arrays cannot be copied, while \CFA arrays can be copied, making them a first-class object (although array copy is often avoided for efficiency). … … 162 151 163 152 @template< typename T, size_t N >@ 164 void copy( T ret[ @N@], T x[@N@] ) {153 void copy( T ret[N], T x[N] ) { 165 154 for ( int i = 0; i < N; i += 1 ) ret[i] = x[i]; 166 155 } … … 178 167 int main() { 179 168 @forall( T, [N] )@ // nested function 180 void copy( array( T, @N@ ) & ret, array( T, @N@) & x ) {169 void copy( array( T, N ) & ret, array( T, N ) & x ) { 181 170 for ( i; 10 ) ret[i] = x[i]; 182 171 } … … 196 185 197 186 Continuing the discussion of \VRef[Figure]{f:fHarness}, the example has @f@ expecting two arrays of the same length. 198 A s stated previous, acompile-time error occurs when attempting to call @f@ with arrays of differing lengths.187 A compile-time error occurs when attempting to call @f@ with arrays of differing lengths. 199 188 % removing leading whitespace 200 189 \lstinput[tabsize=1]{52-53}{hello-array.cfa} 201 190 \lstinput[tabsize=1,aboveskip=0pt]{62-64}{hello-array.cfa} 202 C allows castingto assert knowledge not shared with the type system.191 As is common practice in C, the programmer is free to cast, \ie to assert knowledge not shared with the type system. 203 192 \lstinput{70-74}{hello-array.cfa} 204 193 … … 208 197 \lstinput{10-15}{hello-accordion.cfa} 209 198 This structure's layout has the starting offset of @municipalities@ varying in @NprovTerty@, and the offset of @total_pt@ and @total_mun@ varying in both generic parameters. 210 For a function that operates on a @Can Pop@ structure, the type system handles this variation transparently.199 For a function that operates on a @CanadaPop@ structure, the type system handles this variation transparently. 211 200 \lstinput{40-45}{hello-accordion.cfa} 212 \VRef[Figure]{f:checkHarness} shows the @CanPop@ harness and results with different array sizes, if the municipalities changed after a census. 201 \VRef[Figure]{f:checkHarness} shows program results where different offset values being used. 202 The output values show that @summarize@ and its caller agree on both the offsets (where the callee starts reading @cost_contribs@ and where the callee writes @total_cost@). 203 Yet the call site just says, ``pass the request.'' 213 204 214 205 \begin{figure} 215 206 \lstinput{60-68}{hello-accordion.cfa} 216 \lstinput{70-7 5}{hello-accordion.cfa}207 \lstinput{70-72}{hello-accordion.cfa} 217 208 \caption{\lstinline{check} Harness} 218 209 \label{f:checkHarness} … … 241 232 In general, storage layout is hidden by subscripting, and only appears when passing arrays among different programming languages or accessing specific hardware. 242 233 243 \VRef[Figure]{f:FixedVariable} shows two C90 approaches for manipulating a contiguous matrix.234 \VRef[Figure]{f:FixedVariable} shows two C90 approaches for manipulating contiguous arrays. 244 235 Note, C90 does not support VLAs. 245 The fixed-dimension approach (left)uses the type system;236 The fixed-dimension approach uses the type system; 246 237 however, it requires all dimensions except the first to be specified at compile time, \eg @m[][6]@, allowing all subscripting stride calculations to be generated with constants. 247 238 Hence, every matrix passed to @fp1@ must have exactly 6 columns but the row size can vary. 248 The variable-dimension approach (right) ignores (violates) the type system, \ie argument and parameters types do not match, and subscripting is performed manually using pointer arithmeticin the macro @sub@.239 The variable-dimension approach ignores (violates) the type system, \ie argument and parameters types do not match, and manually performs pointer arithmetic for subscripting in the macro @sub@. 249 240 250 241 \begin{figure} … … 267 258 ... printf( "%d ", @sub( m, r, c )@ ); ... 268 259 } 269 int vm1[ @4@][@4@], vm2[@6@][@8@]; // no VLA260 int vm1[4][4], vm2[6][8]; // no VLA 270 261 // initialize matrixes 271 262 fp2( 4, 4, vm1 ); … … 299 290 The language decides if the matrix and array-of-array are laid out the same or differently. 300 291 For example, an array-of-array may be an array of row pointers to arrays of columns, so the rows may not be contiguous in memory nor even the same length (triangular matrix). 301 Regardless, there is usually a uniform subscripting syntax masking the memory layout, even though a language could differentiated between the two forms using subscript syntax, \eg @m[1,2]@ \vs @aa[1][2]@.292 Regardless, there is usually a uniform subscripting syntax masking the memory layout, even though the two array forms could be differentiated at the subscript level, \eg @m[1,2]@ \vs @aa[1][2]@. 302 293 Nevertheless, controlling memory layout can make a difference in what operations are allowed and in performance (caching/NUMA effects). 303 294 … … 310 301 The focus of this work is on the contiguous multidimensional arrays in C. 311 302 The reason is that programmers are often forced to use the more complex array-of-array form when a contiguous array would be simpler, faster, and safer. 312 Nevertheless, the C array-of-array form is still importantfor special circumstances.303 Nevertheless, the C array-of-array form continues to be useful for special circumstances. 313 304 314 305 \VRef[Figure]{f:ContiguousNon-contiguous} shows the extensions made in C99 for manipulating contiguous \vs non-contiguous arrays.\footnote{C90 also supported non-contiguous arrays.} … … 322 313 While this contiguous-array capability is a step forward, it is still the programmer's responsibility to manually manage the number of dimensions and their sizes, both at the function definition and call sites. 323 314 That is, the array does not automatically carry its structure and sizes for use in computing subscripts. 324 While the non-contiguous style in @faa@ looks very similar to @fc@, the compiler only understands the unknown-sized array of row pointers, and it relies on the programmer to traverse the columns in a row correctly with a correctly bounded loop index.315 While the non-contiguous style in @faa@ looks very similar to @fc@, the compiler only understands the unknown-sized array of row pointers, and it relies on the programmer to traverse the columns in a row correctly. 325 316 Specifically, there is no requirement that the rows are the same length, like a poem with different length lines. 326 317 … … 374 365 this model has no awareness of dimensions just the ability to access memory at a distance from a reference point (base-displacement addressing), \eg @x + 23@ or @x[23}@ $\Rightarrow$ 23rd element from the start of @x@. 375 366 A programmer must manually build any notion of dimensions using other tools; 376 hence, this style is not offering multidimensional arrays \see{\VRef[Figure]{f:FixedVariable} right example}.367 hence, this style is not offering multidimensional arrays \see{\VRef[Figure]{f:FixedVariable}}. 377 368 \end{enumerate} 378 369 … … 390 381 A C/\CFA array interface includes the resulting memory layout. 391 382 The defining requirement of a type-2 system is the ability to slice a column from a column-finest matrix. 392 The required memory shape of such a slice is fixed, before any discussion of implementation.383 The required memory shape of such a slice is set, before any discussion of implementation. 393 384 The implementation presented here is how the \CFA array library wrangles the C type system, to make it do memory steps that are consistent with this layout. 394 385 TODO: do I have/need a presentation of just this layout, just the semantics of -[all]? … … 398 389 Beyond what C's array type offers, the new array brings direct support for working with a noncontiguous array slice, allowing a program to work with dimension subscripts given in a non-physical order. 399 390 400 The following examples use the matrix declaration @array( float, 5, 7) m@, loaded with values incremented by $0.1$, when stepping across the length-7 finely-strided column dimension, and stepping across the length-5 coarsely-strided row dimension.401 \par 391 The following examples use an @array( float, 5, 7) m@, loaded with values incremented by $0.1$, when stepping across the length-7 finely-strided column dimension, and stepping across the length-5 coarsely-strided row dimension. 392 \par\noindent 402 393 \mbox{\lstinput{121-126}{hello-md.cfa}} 403 394 \par\noindent … … 414 405 Specifically, declaring the parameter @r@ with type @array@ means that @r@ is contiguous, which is unnecessarily restrictive. 415 406 That is, @r@ need only be of a container type that offers a subscript operator (of type @ptrdiff_t@ $\rightarrow$ @float@) with managed length @N@. 416 The new-array library provides the trait @ ar@, so-defined.407 The new-array library provides the trait @ix@, so-defined. 417 408 With it, the original declaration can be generalized with the same body. 418 409 \lstinput{43-44}{hello-md.cfa} 419 410 \lstinput[aboveskip=0pt]{145-145}{hello-md.cfa} 420 411 The nontrivial slicing in this example now allows passing a \emph{noncontiguous} slice to @print1d@, where the new-array library provides a ``subscript by all'' operation for this purpose. 421 In a multi-dimensional subscript operation, any dimension given as @all@ is a placeholder, \ie ``not yet subscripted by a value'', waiting for such a value, implementing the @ ar@ trait.412 In a multi-dimensional subscript operation, any dimension given as @all@ is a placeholder, \ie ``not yet subscripted by a value'', waiting for such a value, implementing the @ix@ trait. 422 413 \lstinput{150-151}{hello-md.cfa} 423 414 … … 480 471 In both cases, value 2 selects from the coarser dimension (rows of @x@), 481 472 while the value 3 selects from the finer dimension (columns of @x@). 482 The figure illustrates the value of each subexpression, comparing how numeric subscripting proceeds from @x@, \vs from @x[all]@.473 The figure illustrates the value of each subexpression, comparing how numeric subscripting proceeds from @x@, vs from @x[all]@. 483 474 Proceeding from @x@ gives the numeric indices as coarse then fine, while proceeding from @x[all]@ gives them fine then coarse. 484 475 These two starting expressions, which are the example's only multidimensional subexpressions 485 476 (those that received zero numeric indices so far), are illustrated with vertical steps where a \emph{first} numeric index would select. 486 477 487 The figure's presentation offers an intuition answering to:What is an atomic element of @x[all]@?488 From there, @x[all]@ itself is simply a two-dimensional array, in the strict C sense, of these building blocks.478 The figure's presentation offers an intuition answering, What is an atomic element of @x[all]@? 479 From there, @x[all]@ itself is simply a two-dimensional array, in the strict C sense, of these strange building blocks. 489 480 An atom (like the bottommost value, @x[all][3][2]@), is the contained value (in the square box) 490 481 and a lie about its size (the wedge above it, growing upward). 491 An array of these atoms (like the intermediate @x[all][3]@) is just a contiguous arrangement of them, done according to their size; 492 call such an array a column. 493 A column is almost ready to be arranged into a matrix; 494 it is the \emph{contained value} of the next-level building block, but another lie about size is required. 495 At first, an atom needs to be arranged as if it were bigger, but now a column needs to be arranged as if it is smaller (the wedge above it, shrinking upward). 482 An array of these atoms (like the intermediate @x[all][3]@) is just a contiguous arrangement of them, 483 done according to their size, as announced. Call such an array a column. 484 A column is almost ready to be arranged into a matrix; it is the \emph{contained value} of the next-level building block, 485 but another lie about size is required. 486 At first, an atom needed to be arranged as if it were bigger, 487 but now a column needs to be arranged as if it is smaller (the wedge above it, shrinking upward). 496 488 These lying columns, arranged contiguously according to their size (as announced) form the matrix @x[all]@. 497 Because @x[all]@ takes indices, first for the fine stride, then for the coarse stride, it achieves the requirement of representing the transpose of @x@. 498 Yet every time the programmer presents an index, a C-array subscript is achieving the offset calculation. 489 Because @x[all]@ takes indices, first for the fine stride, then for the coarse stride, 490 it achieves the requirement of representing the transpose of @x@. 491 Yet every time the programmer presents an index, a mere C-array subscript is achieving the offset calculation. 499 492 500 493 In the @x[all]@ case, after the finely strided subscript is done (column 3 is selected), … … 502 495 compared with where analogous rows appear when the row-level option is presented for @x@. 503 496 504 \PAB{I don't understand this paragraph:These size lies create an appearance of overlap.505 For example, in \lstinline{x[all]}, the shaded band touches atoms 2.0, 2.1, 2.2, 2.3, 1.4, 1.5 and 1.6.497 These size lies create an appearance of overlap. 498 For example, in @x[all]@, the shaded band touches atoms 2.0, 2.1, 2.2, 2.3, 1.4, 1.5 and 1.6. 506 499 But only the atom 2.3 is storing its value there. 507 The rest are lying about (conflicting) claims on this location, but never exercising these alleged claims. }500 The rest are lying about (conflicting) claims on this location, but never exercising these alleged claims. 508 501 509 502 Lying is implemented as casting. … … 511 504 This structure uses one type in its internal field declaration and offers a different type as the return of its subscript operator. 512 505 The field within is a plain-C array of the fictional type, which is 7 floats long for @x[all][3][2]@ and 1 float long for @x[all][3]@. 513 The subscript operator presents what is really inside, by casting to the type below the wedge of thelie.506 The subscript operator presents what's really inside, by casting to the type below the wedge of lie. 514 507 515 508 % Does x[all] have to lie too? The picture currently glosses over how it it advertises a size of 7 floats. I'm leaving that as an edge case benignly misrepresented in the picture. Edge cases only have to be handled right in the code. 516 509 517 Casting, overlapping, and lying are unsafe. 518 The mission is to implement a style-1 feature in the type system for safe use by a programmer. 519 The offered style-1 system is allowed to be internally unsafe, 520 just as C's implementation of a style-2 system (upon a style-3 system) is unsafe within, even when the programmer is using it without casts or pointer arithmetic. 521 Having a style-1 system relieves the programmer from resorting to unsafe pointer arithmetic when working with noncontiguous slices. 522 523 % PAB: repeat from previous paragraph. 524 % The choice to implement this style-1 system upon C's style-2 arrays, rather than its style-3 pointer arithmetic, reduces the attack surface of unsafe code. 525 % My casting is unsafe, but I do not do any pointer arithmetic. 526 % When a programmer works in the common-case style-2 subset (in the no-@[all]@ top of Figure~\ref{fig:subscr-all}), my casts are identities, and the C compiler is doing its usual displacement calculations. 527 % If I had implemented my system upon style-3 pointer arithmetic, then this common case would be circumventing C's battle-hardened displacement calculations in favour of my own. 528 529 % \noindent END: Paste looking for a home 510 Casting, overlapping and lying are unsafe. 511 The mission here is to implement a style-2 feature that the type system helps the programmer use safely. 512 The offered style-2 system is allowed to be internally unsafe, 513 just as C's implementation of a style-3 system (upon a style-4 system) is unsafe within, 514 even when the programmer is using it without casts or pointer arithmetic. 515 Having a style-2 system relieves the programmer from resorting to unsafe pointer arithmetic when working with noncontiguous slices. 516 517 The choice to implement this style-2 system upon C's style-3 arrays, rather than its style-4 pointer arithmetic, 518 reduces the attack surface of unsafe code. 519 My casting is unsafe, but I do not do any pointer arithmetic. 520 When a programmer works in the common-case style-3 subset (in the no-@[all]@ top of Figure~\ref{fig:subscr-all}), 521 my casts are identities, and the C compiler is doing its usual displacement calculations. 522 If I had implemented my system upon style-4 pointer arithmetic, 523 then this common case would be circumventing C's battle-hardened displacement calculations in favour of my own. 524 525 \noindent END: Paste looking for a home 530 526 531 527 The new-array library defines types and operations that ensure proper elements are accessed soundly in spite of the overlapping. 532 The @arpk@ structure and its @-[i]@ operator are defined as: 533 \begin{cfa} 534 forall( 535 [N], $\C{// length of current dimension}$ 536 S & | sized(S), $\C{// masquerading-as}$ 537 Timmed &, $\C{// immediate element, often another array}$ 538 Tbase & $\C{// base element, e.g. float, never array}$ 528 The private @arpk@ structure (array with explicit packing) is generic over these two types (and more): the contained element, what it is masquerading as. 529 This structure's public interface is the @array(...)@ construction macro and the two subscript operators. 530 Construction by @array@ initializes the masquerading-as type information to be equal to the contained-element information. 531 Subscripting by @all@ rearranges the order of masquerading-as types to achieve, in general, nontrivial striding. 532 Subscripting by a number consumes the masquerading-as size of the contained element type, does normal array stepping according to that size, and returns there element found there, in unmasked form. 533 534 The @arpk@ structure and its @-[i]@ operator are thus defined as: 535 \begin{cfa} 536 forall( ztype(N), $\C{// length of current dimension}$ 537 dtype(S) | sized(S), $\C{// masquerading-as}$ 538 dtype E_im, $\C{// immediate element, often another array}$ 539 dtype E_base $\C{// base element, e.g. float, never array}$ 539 540 ) { // distribute forall to each element 540 541 struct arpk { 541 542 S strides[N]; $\C{// so that sizeof(this) is N of S}$ 542 543 }; 543 // expose Timmed, stride by S 544 static inline Timmed & ?[?]( arpk( N, S, Timmed, Tbase ) & a, long int i ) { 545 subcheck( a, i, 0, N ); 546 return (Timmed &)a.strides[i]; 544 // expose E_im, stride by S 545 E_im & ?[?]( arpk(N, S, E_im, E_base) & a, ptrdiff_t i ) { 546 return (E_im &) a.strides[i]; 547 547 } 548 548 } 549 549 \end{cfa} 550 The private @arpk@ structure (array with explicit packing) is generic over four types: dimension length, masquerading-as, ...551 This structure's public interface is hidden behind the @array(...)@ macro and the subscript operator.552 Construction by @array@ initializes the masquerading-as type information to be equal to the contained-element information.553 Subscripting by @all@ rearranges the order of masquerading-as types to achieve, in general, nontrivial striding.554 Subscripting by a number consumes the masquerading-as size of the contained element type, does normal array stepping according to that size, and returns there element found there, in unmasked form.555 550 556 551 An instantiation of the @arpk@ generic is given by the @array(E_base, N0, N1, ...)@ expansion, which is @arpk( N0, Rec, Rec, E_base )@, where @Rec@ is @array(E_base, N1, ...)@. … … 573 568 This section provides a demonstration of the effect. 574 569 575 The experiment compares the \CFA array system with the padded-room system [TODO:xref] most typically exemplified by Java arrays, but also reflected in the \CCpattern where restricted vector usage models a checked array.570 The experiment compares the \CFA array system with the padded-room system [TODO:xref] most typically exemplified by Java arrays, but also reflected in the C++ pattern where restricted vector usage models a checked array. 576 571 The essential feature of this padded-room system is the one-to-one correspondence between array instances and the symbolic bounds on which dynamic checks are based. 577 The experiment compares with the \CCversion to keep access to generated assembly code simple.578 579 As a control case, a simple loop (with no reused dimension sizes) is seen to get the same optimization treatment in both the \CFA and \CCversions.572 The experiment compares with the C++ version to keep access to generated assembly code simple. 573 574 As a control case, a simple loop (with no reused dimension sizes) is seen to get the same optimization treatment in both the \CFA and C++ versions. 580 575 When the programmer treats the array's bound correctly (making the subscript ``obviously fine''), no dynamic bound check is observed in the program's optimized assembly code. 581 576 But when the bounds are adjusted, such that the subscript is possibly invalid, the bound check appears in the optimized assembly, ready to catch an occurrence the mistake. … … 594 589 \section{Comparison with other arrays} 595 590 596 597 \subsection{Rust}598 599 591 \CFA's array is the first lightweight application of dependently-typed bound tracking to an extension of C. 600 Other extensions of C that apply dependently-typed bound tracking are heavyweight, in that the bound tracking is part of a linearly -typed ownership-system, whichfurther helps guarantee statically the validity of every pointer deference.592 Other extensions of C that apply dependently-typed bound tracking are heavyweight, in that the bound tracking is part of a linearly typed ownership system that further helps guarantee statically the validity of every pointer deference. 601 593 These systems, therefore, ask the programmer to convince the type checker that every pointer dereference is valid. 602 594 \CFA imposes the lighter-weight obligation, with the more limited guarantee, that initially-declared bounds are respected thereafter. … … 606 598 The \CFA array, applied to accordion structures [TOD: cross-reference] \emph{implies} the necessary pointer arithmetic, generated automatically, and not appearing at all in a user's program. 607 599 608 609 \subsection{Java} 610 611 Java arrays are arrays-of-arrays because all objects are references \see{\VRef{toc:mdimpl}}. 612 For each array, Java implicitly storages the array dimension in a descriptor, supporting array length, subscript checking, and allowing dynamically-sized array-parameter declarations. 613 \begin{cquote} 600 \subsection{Safety in a padded room} 601 602 Java's array [TODO:cite] is a straightforward example of assuring safety against undefined behaviour, at a cost of expressiveness for more applied properties. 603 Consider the array parameter declarations in: 604 614 605 \begin{tabular}{rl} 615 606 C & @void f( size_t n, size_t m, float x[n][m] );@ \\ 616 Java & @void f( float x[][]);@607 Java & @void f( float[][] a );@ 617 608 \end{tabular} 618 \end{cquote} 619 However, in the C prototype, the parameters @n@ and @m@ are documentation only as the intended size of the first and second dimension of @x@. 620 \VRef[Figure]{f:JavaVsCTriangularMatrix} compares a triangular matrix (array-of-arrays) in dynamically safe Java to unsafe C. 621 Each dynamically sized row in Java stores its dimension, while C requires the programmer to manage these sizes explicitly (@rlnth@). 622 All subscripting is Java has bounds checking, while C has none. 623 Both Java and C require explicit null checking, otherwise there is a runtime failure. 624 625 \begin{figure} 626 \setlength{\tabcolsep}{15pt} 627 \begin{tabular}{ll@{}} 628 \begin{java} 629 int m[][] = { // triangular matrix 630 new int [4], 631 new int [3], 632 new int [2], 633 new int [1], 634 null 635 }; 636 637 for ( int r = 0; r < m.length; r += 1 ) { 638 if ( m[r] == null ) continue; 639 for ( int c = 0; c < m[r].length; c += 1 ) { 640 m[r][c] = c + r; // subscript checking 641 } 642 643 } 644 645 for ( int r = 0; r < m.length; r += 1 ) { 646 if ( m[r] == null ) { 647 System.out.println( "null row" ); 648 continue; 649 } 650 for ( int c = 0; c < m[r].length; c += 1 ) { 651 System.out.print( m[r][c] + " " ); 652 } 653 System.out.println(); 654 655 } 656 \end{java} 657 & 658 \begin{cfa} 659 int * m[5] = { // triangular matrix 660 calloc( 4, sizeof(int) ), 661 calloc( 3, sizeof(int) ), 662 calloc( 2, sizeof(int) ), 663 calloc( 1, sizeof(int) ), 664 NULL 665 }; 666 int rlnth = 4; 667 for ( int r = 0; r < 5; r += 1 ) { 668 if ( m[r] == NULL ) continue; 669 for ( int c = 0; c < rlnth; c += 1 ) { 670 m[r][c] = c + r; // no subscript checking 671 } 672 rlnth -= 1; 673 } 674 rlnth = 4; 675 for ( int r = 0; r < 5; r += 1 ) { 676 if ( m[r] == NULL ) { 677 printf( "null row\n" ); 678 continue; 679 } 680 for ( int c = 0; c < rlnth; c += 1 ) { 681 printf( "%d ", m[r][c] ); 682 } 683 printf( "\n" ); 684 rlnth -= 1; 685 } 686 \end{cfa} 687 \end{tabular} 688 \caption{Java (left) \vs C (right) Triangular Matrix} 689 \label{f:JavaVsCTriangularMatrix} 690 \end{figure} 691 692 The downside of the arrays-of-arrays approach is performance due to pointer chasing versus pointer arithmetic for a contiguous arrays. 693 Furthermore, there is the cost of managing the implicit array descriptor. 694 It is unlikely that a JIT can dynamically rewrite an arrays-of-arrays form into a contiguous form. 695 696 697 \subsection{\CC} 698 699 Because C arrays are difficult and dangerous, the mantra for \CC programmers is to use @std::vector@ in place of the C array. 700 While the vector size can grow and shrink dynamically, \vs a fixed-size dynamic size with VLAs, the cost of this extra feature is mitigated by preallocating the maximum size (like the VLA) at the declaration (one dynamic call) to avoid using @push_back@. 701 \begin{c++} 702 vector< vector< int > > m( 5, vector<int>(8) ); // initialize size of 5 x 8 with 6 dynamic allocations 703 \end{c++} 704 Multidimensional arrays are arrays-of-arrays with associated costs. 705 Each @vector@ array has an array descriptor contain the dimension, which allows bound checked using @x.at(i)@ in place of @x[i]@. 706 Used with these restrictions, out-of-bound accesses are caught, and in-bound accesses never exercise the vector's ability to grow, preventing costly reallocate and copy, and never invalidate references to contained values. 707 This scheme matches Java's safety and expressiveness exactly, but with the inherent costs. 708 609 610 Java's safety against undefined behaviour assures the callee that, if @x@ is non-null, then @a.length@ is a valid access (say, evaluating to the number $\ell$) and if @i@ is in $[0, \ell)$ then @x[i]@ is a valid access. 611 If a value of @i@ outside this range is used, a runtime error is guaranteed. 612 In these respects, C offers no guarantees at all. 613 Notably, the suggestion that @n@ is the intended size of the first dimension of @x@ is documentation only. 614 Indeed, many might prefer the technically equivalent declarations @float x[][m]@ or @float (*a)[m]@ as emphasizing the ``no guarantees'' nature of an infrequently used language feature, over using the opportunity to explain a programmer intention. 615 Moreover, even if @x[0][0]@ is valid for the purpose intended, C's basic infamous feature is the possibility of an @i@, such that @x[i][0]@ is not valid for the same purpose, and yet, its evaluation does not produce an error. 616 617 Java's lack of expressiveness for more applied properties means these outcomes are possible: 618 \begin{itemize} 619 \item @x[0][17]@ and @x[2][17]@ are valid accesses, yet @x[1][17]@ is a runtime error, because @x[1]@ is a null pointer 620 \item the same observation, now because @x[1]@ refers to an array of length 5 621 \item execution times vary, because the @float@ values within @x@ are sometimes stored nearly contiguously, and other times, not at all 622 \end{itemize} 623 C's array has none of these limitations, nor do any of the ``array language'' comparators discussed in this section. 624 625 This Java level of safety and expressiveness is also exemplified in the C family, with the commonly given advice [TODO:cite example], for C++ programmers to use @std::vector@ in place of the C++ language's array, which is essentially the C array. 626 The advice is that, while a vector is also more powerful (and quirky) than an array, its capabilities include options to preallocate with an upfront size, to use an available bound-checked accessor (@a.at(i)@ in place of @x[i]@), to avoid using @push_back@, and to use a vector of vectors. 627 Used with these restrictions, out-of-bound accesses are stopped, and in-bound accesses never exercise the vector's ability to grow, which is to say, they never make the program slow to reallocate and copy, and they never invalidate the program's other references to the contained values. 628 Allowing this scheme the same referential integrity assumption that \CFA enjoys [TODO:xref], this scheme matches Java's safety and expressiveness exactly. 629 [TODO: decide about going deeper; some of the Java expressiveness concerns have mitigations, up to even more tradeoffs.] 709 630 710 631 \subsection{Levels of dependently typed arrays} … … 734 655 it can also do these other cool checks, but watch how I can mess with its conservativeness and termination 735 656 736 Two current, state-of-the-art array languages, Dex\cite{arr:dex:long} and Futhark\cite{arr:futhark:tytheory}, offer novel contributions concerning similar, restricted dependent types for tracking array length.657 Two current, state-of-the-art array languages, Dex\cite{arr:dex:long} and Futhark\cite{arr:futhark:tytheory}, offer offer novel contributions concerning similar, restricted dependent types for tracking array length. 737 658 Unlike \CFA, both are garbage-collected functional languages. 738 659 Because they are garbage-collected, referential integrity is built-in, meaning that the heavyweight analysis, that \CFA aims to avoid, is unnecessary. … … 806 727 [TODO: introduce Ada in the comparators] 807 728 808 In Ada and Dex, an array is conceived as a function whose domain must satisfy only certain structural assumptions, while in C, \CC, Java, Futhark and \CFA today, the domain is a prefix of the natural numbers.729 In Ada and Dex, an array is conceived as a function whose domain must satisfy only certain structural assumptions, while in C, C++, Java, Futhark and \CFA today, the domain is a prefix of the natural numbers. 809 730 The generality has obvious aesthetic benefits for programmers working on scheduling resources to weekdays, and for programmers who prefer to count from an initial number of their own choosing. 810 731 -
doc/theses/mike_brooks_MMath/programs/hello-accordion.cfa
rce02877 r38e20a80 9 9 10 10 forall( T, @[NprovTerty]@, @[Nmunicipalities]@ ) 11 struct Can Pop {11 struct CanadaPop { 12 12 array( T, @NprovTerty@ ) provTerty; $\C{// nested VLA}$ 13 13 array( T, @Nmunicipalities@ ) municipalities; $\C{// nested VLA}$ … … 19 19 20 20 forall( T, [NprovTerty], [Nmunicipalities] ) 21 void ?{}( T &, Can Pop( T, NprovTerty, Nmunicipalities ) & this ) {}21 void ?{}( T &, CanadaPop( T, NprovTerty, Nmunicipalities ) & this ) {} 22 22 23 23 forall( T &, [NprovTerty], [Nmunicipalities] ) 24 void ^?{}( Can Pop( T, NprovTerty, Nmunicipalities ) & this ) {}24 void ^?{}( CanadaPop( T, NprovTerty, Nmunicipalities ) & this ) {} 25 25 26 26 … … 39 39 40 40 forall( T, [NprovTerty], [Nmunicipalities] ) 41 void check( Can Pop( T, NprovTerty, Nmunicipalities ) & pop ) with( pop ) {41 void check( CanadaPop( T, NprovTerty, Nmunicipalities ) & pop ) with( pop ) { 42 42 total_pt = total_mun = 0; 43 43 for ( i; NprovTerty ) total_pt += provTerty[i]; … … 60 60 int main( int argc, char * argv[] ) { 61 61 const int npt = ato( argv[1] ), nmun = ato( argv[2] ); 62 @Can Pop( int, npt, nmun ) pop;@62 @CanadaPop( int, npt, nmun ) pop;@ 63 63 // read in population numbers 64 64 @check( pop );@ … … 71 71 Total province/territory: 36,991,981 72 72 Total municipalities: 36,991,981 73 $\$$ ./a.out 13 365474 Total province/territory: 36,991,98175 Total municipalities: 36,991,98176 73 */ 77 74 -
doc/theses/mike_brooks_MMath/programs/hello-array.cfa
rce02877 r38e20a80 9 9 10 10 forall( [@N@] ) $\C{// array dimension}$ 11 array( bool, @N@ ) & f( array( float, @N@ ) & x, array( float, @N@ ) & y ) {11 array( bool, @N@) & f( array( float, @N@ ) & x, array( float, @N@ ) & y ) { 12 12 array( bool, @N@ ) & ret = *@alloc@(); $\C{// sizeof ret used by alloc}$ 13 13 for ( i; @N@ ) { … … 29 29 30 30 int main( int argc, char * argv[] ) { 31 const int @ dim@ = ato( argv[1] );$\C{// deduce conversion type}$32 array( float, @ dim@ ) x, y; $\C{// VLAs}$33 for ( i; dim ) {$\C{// initialize arrays}$31 const int @n@ = ato( argv[1] ); $\C{// deduce conversion type}$ 32 array( float, @n@ ) x, y; $\C{// VLAs}$ 33 for ( i; n ) { $\C{// initialize arrays}$ 34 34 x[i] = 3.14 / (i + 1); 35 35 y[i] = x[i] + 0.005 ; 36 36 } 37 array( bool, @ dim@ ) & result = @f( x, y )@; $\C{// call}$37 array( bool, @n@ ) & result = @f( x, y )@; $\C{// call}$ 38 38 sout | "result: " | nonl; $\C{// print result}$ 39 for ( i; dim)39 for ( i; n ) 40 40 sout | result[i] | nonl; 41 41 sout | nl; -
doc/theses/mike_brooks_MMath/programs/hello-md.cfa
rce02877 r38e20a80 138 138 139 139 140 print1d_cstyle( @m[ 2 ]@); $\C{// row 2: 2.0 2.1 2.2 2.3 2.4 2.5 2.6}$140 print1d_cstyle( m[ 2 ] ); $\C{// row 2: 2.0 2.1 2.2 2.3 2.4 2.5 2.6}$ 141 141 142 142 -
doc/theses/mike_brooks_MMath/uw-ethesis-frontpgs.tex
rce02877 r38e20a80 140 140 141 141 I would like to thank all the little people who made this thesis possible. 142 143 Finally, a special thank you to Huawei Canada for funding this work.144 142 \cleardoublepage 145 143 \phantomsection % allows hyperref to link to the correct page -
doc/theses/mike_brooks_MMath/uw-ethesis.tex
rce02877 r38e20a80 105 105 \lstnewenvironment{c++}[1][]{\lstset{language=[GNU]C++,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 106 106 \lstnewenvironment{pascal}[1][]{\lstset{language=pascal,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 107 \lstnewenvironment{java}[1][]{\lstset{language=java,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}108 107 \lstset{inputpath={programs}} 109 108 -
doc/user/user.tex
rce02877 r38e20a80 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Fri Jul 26 06:56:11202414 %% Update Count : 6 95513 %% Last Modified On : Tue Jul 9 10:43:40 2024 14 %% Update Count : 6887 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 1598 1598 and implicitly opened \emph{after} a function-body open, to give them higher priority: 1599 1599 \begin{cfa} 1600 void f( S & s, char ®c® ) with ( s ) ®with( §\emph{\R{params}}§ )® { // syntax disallowed, illustration only1600 void f( S & s, char ®c® ) with ( s ) ®with( §\emph{\R{params}}§ )® { // syntax not allowed, illustration only 1601 1601 s.c = ®c;® i = 3; d = 5.5; 1602 1602 } … … 3313 3313 for example, the following is incorrect: 3314 3314 \begin{cfa} 3315 * [ int x ] f () fp; §\C{// routine name "f" is disallowed}§3316 \end{cfa} 3317 3318 3319 \section{ Default and Named Parameter}3320 3321 Default\index{default parameter}\index{parameter!default} and named\index{named parameter}\index{parameter!named} parameters~\cite{Hardgrave76}\footnote{3315 * [ int x ] f () fp; §\C{// routine name "f" is not allowed}§ 3316 \end{cfa} 3317 3318 3319 \section{Named and Default Arguments} 3320 3321 Named\index{named arguments}\index{arguments!named} and default\index{default arguments}\index{arguments!default} arguments~\cite{Hardgrave76}\footnote{ 3322 3322 Francez~\cite{Francez77} proposed a further extension to the named-parameter passing style, which specifies what type of communication (by value, by reference, by name) the argument is passed to the routine.} 3323 3323 are two mechanisms to simplify routine call. 3324 3325 3326 \subsection{Default} 3327 3328 A default parameter associates a default value with a parameter so it can be optionally specified in the argument list. 3329 For example, given the routine prototype: 3330 \begin{cfa} 3331 void f( int x ®= 1®, int y ®= 2®, int z ®= 3® ); 3332 \end{cfa} 3333 allowable calls are: 3334 \begin{cquote} 3335 \setlength{\tabcolsep}{0.75in} 3336 \begin{tabular}{@{}ll@{}} 3337 \textbf{positional arguments} & \textbf{empty arguments} \\ 3338 \begin{cfa} 3339 f(); §\C[0.75in]{// rewrite \(\Rightarrow\) f( 1, 2, 3 )}§ 3340 f( 4 ); §\C{// rewrite \(\Rightarrow\) f( 4, 2, 3 )}§ 3341 f( 4, 4 ); §\C{// rewrite \(\Rightarrow\) f( 4, 4, 3 )}§ 3342 f( 4, 4, 4 ); §\C{// rewrite \(\Rightarrow\) f( 4, 4, 4 )}\CRT§ 3343 3344 3345 3346 \end{cfa} 3347 & 3348 \begin{cfa} 3349 f( ®?®, 4, 4 ); §\C[1.0in]{// rewrite \(\Rightarrow\) f( 1, 4, 4 )}§ 3350 f( 4, ®?®, 4 ); §\C{// rewrite \(\Rightarrow\) f( 4, 2, 4 )}§ 3351 f( 4, 4, ®?® ); §\C{// rewrite \(\Rightarrow\) f( 4, 4, 3 )}§ 3352 f( 4, ®?®, ®?® ); §\C{// rewrite \(\Rightarrow\) f( 4, 2, 3 )}§ 3353 f( ®?®, 4, ®?® ); §\C{// rewrite \(\Rightarrow\) f( 1, 4, 3 )}§ 3354 f( ®?®, ®?®, 4 ); §\C{// rewrite \(\Rightarrow\) f( 1, 2, 4 )}§ 3355 f( ®?®, ®?®, ®?® ); §\C{// rewrite \(\Rightarrow\) f( 1, 2, 3 )}\CRT§ 3356 \end{cfa} 3357 \end{tabular} 3358 \end{cquote} 3359 where the ©?© selects the default value as the argument. 3324 Both mechanisms are discussed with respect to \CFA. 3325 \begin{description} 3326 \item[Named (or Keyword) Arguments:] 3327 provide the ability to specify an argument to a routine call using the parameter name rather than the position of the parameter. 3328 For example, given the routine: 3329 \begin{cfa} 3330 void p( int x, int y, int z ) {...} 3331 \end{cfa} 3332 a positional call is: 3333 \begin{cfa} 3334 p( 4, 7, 3 ); 3335 \end{cfa} 3336 whereas a named (keyword) call may be: 3337 \begin{cfa} 3338 p( z : 3, x : 4, y : 7 ); §\C{// rewrite \(\Rightarrow\) p( 4, 7, 3 )}§ 3339 \end{cfa} 3340 Here the order of the arguments is unimportant, and the names of the parameters are used to associate argument values with the corresponding parameters. 3341 The compiler rewrites a named call into a positional call. 3342 The advantages of named parameters are: 3343 \begin{itemize} 3344 \item 3345 Remembering the names of the parameters may be easier than the order in the routine definition. 3346 \item 3347 Parameter names provide documentation at the call site (assuming the names are descriptive). 3348 \item 3349 Changes can be made to the order or number of parameters without affecting the call (although the call must still be recompiled). 3350 \end{itemize} 3351 3352 Unfortunately, named arguments do not work in C-style programming-languages because a routine prototype is not required to specify parameter names, nor do the names in the prototype have to match with the actual definition. 3353 For example, the following routine prototypes and definition are all valid. 3354 \begin{cfa} 3355 void p( int, int, int ); §\C{// equivalent prototypes}§ 3356 void p( int x, int y, int z ); 3357 void p( int y, int x, int z ); 3358 void p( int z, int y, int x ); 3359 void p( int q, int r, int s ) {} §\C{// match with this definition}§ 3360 \end{cfa} 3361 Forcing matching parameter names in routine prototypes with corresponding routine definitions is possible, but goes against a strong tradition in C programming. 3362 Alternatively, prototype definitions can be eliminated by using a two-pass compilation, and implicitly creating header files for exports. 3363 The former is easy to do, while the latter is more complex. 3364 3365 Furthermore, named arguments do not work well in a \CFA-style programming-languages because they potentially introduces a new criteria for type matching. 3366 For example, it is technically possible to disambiguate between these two overloaded definitions of ©f© based on named arguments at the call site: 3367 \begin{cfa} 3368 int f( int i, int j ); 3369 int f( int x, double y ); 3370 3371 f( j : 3, i : 4 ); §\C{// 1st f}§ 3372 f( x : 7, y : 8.1 ); §\C{// 2nd f}§ 3373 f( 4, 5 ); §\C{// ambiguous call}§ 3374 \end{cfa} 3375 However, named arguments compound routine resolution in conjunction with conversions: 3376 \begin{cfa} 3377 f( i : 3, 5.7 ); §\C{// ambiguous call ?}§ 3378 \end{cfa} 3379 Depending on the cost associated with named arguments, this call could be resolvable or ambiguous. 3380 Adding named argument into the routine resolution algorithm does not seem worth the complexity. 3381 Therefore, \CFA does \emph{not} attempt to support named arguments. 3382 3383 \item[Default Arguments] 3384 provide the ability to associate a default value with a parameter so it can be optionally specified in the argument list. 3385 For example, given the routine: 3386 \begin{cfa} 3387 void p( int x = 1, int y = 2, int z = 3 ) {...} 3388 \end{cfa} 3389 the allowable positional calls are: 3390 \begin{cfa} 3391 p(); §\C{// rewrite \(\Rightarrow\) p( 1, 2, 3 )}§ 3392 p( 4 ); §\C{// rewrite \(\Rightarrow\) p( 4, 2, 3 )}§ 3393 p( 4, 4 ); §\C{// rewrite \(\Rightarrow\) p( 4, 4, 3 )}§ 3394 p( 4, 4, 4 ); §\C{// rewrite \(\Rightarrow\) p( 4, 4, 4 )}§ 3395 // empty arguments 3396 p( , 4, 4 ); §\C{// rewrite \(\Rightarrow\) p( 1, 4, 4 )}§ 3397 p( 4, , 4 ); §\C{// rewrite \(\Rightarrow\) p( 4, 2, 4 )}§ 3398 p( 4, 4, ); §\C{// rewrite \(\Rightarrow\) p( 4, 4, 3 )}§ 3399 p( 4, , ); §\C{// rewrite \(\Rightarrow\) p( 4, 2, 3 )}§ 3400 p( , 4, ); §\C{// rewrite \(\Rightarrow\) p( 1, 4, 3 )}§ 3401 p( , , 4 ); §\C{// rewrite \(\Rightarrow\) p( 1, 2, 4 )}§ 3402 p( , , ); §\C{// rewrite \(\Rightarrow\) p( 1, 2, 3 )}§ 3403 \end{cfa} 3360 3404 Here the missing arguments are inserted from the default values in the parameter list. 3361 3405 The compiler rewrites missing default values into explicit positional arguments. … … 3364 3408 \item 3365 3409 Routines with a large number of parameters are often very generalized, giving a programmer a number of different options on how a computation is performed. 3366 For many of these routines, there are standard or default settings that work for the majority of computations.3410 For many of these kinds of routines, there are standard or default settings that work for the majority of computations. 3367 3411 Without default values for parameters, a programmer is forced to specify these common values all the time, resulting in long argument lists that are error prone. 3368 3412 \item … … 3378 3422 Instead, a default value is used, which may not be the programmer's intent. 3379 3423 3380 Default parameters may only appear in a prototype versus definition context:3381 \begin{cfa} 3382 void f( int x, int y = 2, int z = 3 );§\C{// prototype: allowed}§3383 void f( int, int = 2, int = 3 );§\C{// prototype: allowed}§3384 void f( int x, int y = 2, int z = 3 ) ®{}® §\C{// definition: disallowed}§3424 Default values may only appear in a prototype versus definition context: 3425 \begin{cfa} 3426 void p( int x, int y = 2, int z = 3 ); §\C{// prototype: allowed}§ 3427 void p( int, int = 2, int = 3 ); §\C{// prototype: allowed}§ 3428 void p( int x, int y = 2, int z = 3 ) {} §\C{// definition: not allowed}§ 3385 3429 \end{cfa} 3386 3430 The reason for this restriction is to allow separate compilation. 3387 Multiple prototypes with different default values is undefined. 3431 Multiple prototypes with different default values is an error. 3432 \end{description} 3433 3434 Ellipse (``...'') arguments present problems when used with default arguments. 3435 The conflict occurs because both named and ellipse arguments must appear after positional arguments, giving two possibilities: 3436 \begin{cfa} 3437 p( /* positional */, ... , /* named */ ); 3438 p( /* positional */, /* named */, ... ); 3439 \end{cfa} 3440 While it is possible to implement both approaches, the first possibly is more complex than the second, \eg: 3441 \begin{cfa} 3442 p( int x, int y, int z, ... ); 3443 p( 1, 4, 5, 6, z : 3, y : 2 ); §\C{// assume p( /* positional */, ... , /* named */ );}§ 3444 p( 1, z : 3, y : 2, 4, 5, 6 ); §\C{// assume p( /* positional */, /* named */, ... );}§ 3445 \end{cfa} 3446 In the first call, it is necessary for the programmer to conceptually rewrite the call, changing named arguments into positional, before knowing where the ellipse arguments begin. 3447 Hence, this approach seems significantly more difficult, and hence, confusing and error prone. 3448 In the second call, the named arguments separate the positional and ellipse arguments, making it trivial to read the call. 3449 3450 The problem is exacerbated with default arguments, \eg: 3451 \begin{cfa} 3452 void p( int x, int y = 2, int z = 3... ); 3453 p( 1, 4, 5, 6, z : 3 ); §\C{// assume p( /* positional */, ... , /* named */ );}§ 3454 p( 1, z : 3, 4, 5, 6 ); §\C{// assume p( /* positional */, /* named */, ... );}§ 3455 \end{cfa} 3456 The first call is an error because arguments 4 and 5 are actually positional not ellipse arguments; 3457 therefore, argument 5 subsequently conflicts with the named argument z : 3. 3458 In the second call, the default value for y is implicitly inserted after argument 1 and the named arguments separate the positional and ellipse arguments, making it trivial to read the call. 3459 For these reasons, \CFA requires named arguments before ellipse arguments. 3460 Finally, while ellipse arguments are needed for a small set of existing C routines, like ©printf©, the extended \CFA type system largely eliminates the need for ellipse arguments \see{\VRef{s:Overloading}}, making much of this discussion moot. 3388 3461 3389 3462 Default arguments and overloading \see{\VRef{s:Overloading}} are complementary. … … 3393 3466 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{default arguments}} & \multicolumn{1}{c}{\textbf{overloading}} \\ 3394 3467 \begin{cfa} 3395 void f( int x, int y = 2, int z = 3 ) {...}3468 void p( int x, int y = 2, int z = 3 ) {...} 3396 3469 3397 3470 … … 3399 3472 & 3400 3473 \begin{cfa} 3401 void f( int x, int y, int z ) {...}3402 void f( int x ) { f( x, 2, 3 ); }3403 void f( int x, int y ) { f( x, y, 3 ); }3474 void p( int x, int y, int z ) {...} 3475 void p( int x ) { p( x, 2, 3 ); } 3476 void p( int x, int y ) { p( x, y, 3 ); } 3404 3477 \end{cfa} 3405 3478 \end{tabular} 3406 3479 \end{cquote} 3407 3480 the number of required overloaded routines is linear in the number of default values, which is unacceptable growth. 3408 In general, overloading is used over default parameters, if the body of the routine is significantly different. 3409 Furthermore, overloading cannot handle accessing default arguments in the middle of a positional list. 3410 \begin{cfa} 3411 f( 1, ®?®, 5 ); §\C{// rewrite \(\Rightarrow\) f( 1, 2, 5 )}§ 3412 \end{cfa} 3413 3414 3415 \subsection{Named (or Keyword)} 3416 3417 A named (keyword) parameter provides the ability to specify an argument to a routine call using the parameter name rather than the position of the parameter. 3418 For example, given the routine prototype: 3419 \begin{cfa} 3420 void f( int ®?®x, int ®?®y, int ®?®z ); 3421 \end{cfa} 3422 allowable calls are: 3423 \begin{cfa} 3424 f( ?x = 3, ?y = 4, ?z = 5 ); §\C{// rewrite \(\Rightarrow\) f( 3, 4, 5 )}§ 3425 f( ?y = 4, ?z = 5, ?x = 3 ); §\C{// rewrite \(\Rightarrow\) f( 3, 4, 5 )}§ 3426 f( ?z = 5, ?x = 3, ?y = 4 ); §\C{// rewrite \(\Rightarrow\) f( 3, 4, 5 )}§ 3427 f( ?x = 3, ?z = 5, ?y = 4 ); §\C{// rewrite \(\Rightarrow\) f( 3, 4, 5 )}§ 3428 \end{cfa} 3429 Here the ordering of the the parameters and arguments is unimportant, and the names of the parameters are used to associate argument values with the corresponding parameters. 3430 The compiler rewrites a named call into a positional call. 3431 Note, the syntax ©?x = 3© is necessary for the argument, because ©x = 3© has an existing meaning, \ie assign ©3© to ©x© and pass the value of ©x©. 3432 The advantages of named parameters are: 3433 \begin{itemize} 3434 \item 3435 Remembering the names of the parameters may be easier than the order in the routine definition. 3436 \item 3437 Parameter names provide documentation at the call site (assuming the names are descriptive). 3438 \item 3439 Changes can be made to the order or number of parameters without affecting the call (although the call must still be recompiled). 3440 \end{itemize} 3441 3442 Named parameters may only appear in a prototype versus definition context: 3443 \begin{cfa} 3444 void f( int x, int ?y, int ?z ); §\C{// prototype: allowed}§ 3445 void f( int ?x, int , int ?z ); §\C{// prototype: allowed}§ 3446 void f( int x, int ?y, int ?z ) ®{}® §\C{// definition: disallowed}§ 3447 \end{cfa} 3448 The reason for this restriction is to allow separate compilation. 3449 Multiple prototypes with different positional parameter names is an error. 3450 3451 The named parameter is not part of type resolution; 3452 only the type of the expression assigned to the named parameter affects type resolution. 3453 \begin{cfa} 3454 int f( int ?i, int ?j ); 3455 int f( int ?i, double ?j ); 3456 f( ?j = 3, ?i = 4 ); §\C{// 1st f}§ 3457 f( ?i = 7, ?j = 8.1 ); §\C{// 2nd f}§ 3458 \end{cfa} 3459 3460 3461 \subsection{Mixed Default/Named} 3462 3463 Default and named parameters can be intermixed and named parameters can have a default value. 3464 For example, given the routine prototype: 3465 \begin{cfa} 3466 void f( int x, int y ®= 1®, int ®?®z ®= 2® ); 3467 \end{cfa} 3468 allowable calls are: 3469 \begin{cfa} 3470 f( 3 ); §\C{// rewrite \(\Rightarrow\) f( 3, 1, 2 )}§ 3471 f( 3, 4 ); §\C{// rewrite \(\Rightarrow\) f( 3, 4, 2 )}§ 3472 f( 3, ?z = 5 ); §\C{// rewrite \(\Rightarrow\) f( 3, 1, 5 )}§ 3473 f( 3, 4, ?z = 5 ); §\C{// rewrite \(\Rightarrow\) f( 3, 4, 5 )}§ 3474 f( ?z = 5, 3 ); §\C{// rewrite \(\Rightarrow\) f( 3, 1, 5 )}§ 3475 f( 3, ?z = 5, 4 ); §\C{// rewrite \(\Rightarrow\) f( 3, 4, 5 )}§ 3476 \end{cfa} 3477 Finally, the ellipse (``...'') parameter must appear after positional and named parameters in a routine prototype. 3478 \begin{cfa} 3479 void f( int i = 1, int ?j = 2, ®...® ); 3480 \end{cfa} 3481 3482 \CFA named and default arguments are backwards compatible with C. 3483 \Index*[C++]{\CC{}} only supports default parameters; 3484 \Index*{Ada} supports both named and default parameters. 3481 In general, overloading should only be used over default arguments if the body of the routine is significantly different. 3482 Furthermore, overloading cannot handle accessing default arguments in the middle of a positional list, via a missing argument, such as: 3483 \begin{cfa} 3484 p( 1, /* default */, 5 ); §\C{// rewrite \(\Rightarrow\) p( 1, 2, 5 )}§ 3485 \end{cfa} 3486 3487 Given the \CFA restrictions above, both named and default arguments are backwards compatible. 3488 \Index*[C++]{\CC{}} only supports default arguments; 3489 \Index*{Ada} supports both named and default arguments. 3485 3490 3486 3491 -
libcfa/src/collections/array.hfa
rce02877 r38e20a80 1 1 #pragma once 2 2 3 //#include <assert.h>3 #include <assert.h> 4 4 5 5 … … 8 8 #define ztag(n) ttag(n) 9 9 10 #ifdef __CFA_DEBUG__11 #define subcheck( arr, sub, lb, ub ) \12 if ( (sub) < (lb) || (sub) >= (ub) ) \13 abort( "subscript %ld exceeds dimension range [%d,%zd) for array %p.\n", \14 (sub), (lb), (ub), (arr) )15 #else16 #define subcheck( arr, sub, lb, ub ) do {} while (0)17 #endif18 10 19 11 // … … 44 36 // 45 37 forall( [N], S & | sized(S), Timmed &, Tbase & ) { 46 // 47 // Single-dim array struct (with explicit packing and atom) 48 // 49 struct arpk { 50 S strides[N]; 51 }; 52 53 // About the choice of integral types offered as subscript overloads: 54 // Intent is to cover these use cases: 55 // a[0] // i : zero_t 56 // a[1] // i : one_t 57 // a[2] // i : int 58 // float foo( ptrdiff_t i ) { return a[i]; } // i : ptrdiff_t 59 // float foo( size_t i ) { return a[i]; } // i : size_t 60 // forall( [N] ) ... for( i; N ) { total += a[i]; } // i : typeof( sizeof(42) ) 61 // for( i; 5 ) { total += a[i]; } // i : int 62 // 63 // It gets complicated by: 64 // - CFA does overloading on concrete types, like int and unsigned int, not on typedefed 65 // types like size_t. So trying to overload on ptrdiff_t vs int works in 64-bit mode 66 // but not in 32-bit mode. 67 // - Given bug of Trac #247, CFA gives sizeof expressions type unsigned long int, when it 68 // should give them type size_t. 69 // 70 // gcc -m32 cfa -m32 given bug gcc -m64 (and cfa) 71 // ptrdiff_t int int long int 72 // size_t unsigned int unsigned int unsigned long int 73 // typeof( sizeof(42) ) unsigned int unsigned long int unsigned long int 74 // int int int int 75 // 76 // So the solution must support types {zero_t, one_t, int, unsigned int, long int, unsigned long int} 77 // 78 // The solution cannot rely on implicit conversions (e.g. just have one overload for ptrdiff_t) 79 // because assertion satisfaction requires types to match exacly. Both higher-dimensional 80 // subscripting and operations on slices use asserted subscript operators. The test case 81 // array-container/array-sbscr-cases covers the combinations. Mike beleives that commenting out 82 // any of the current overloads leads to one of those cases failing, either on 64- or 32-bit. 83 // Mike is open to being shown a smaller set of overloads that still passes the test. 84 85 static inline Timmed & ?[?]( arpk( N, S, Timmed, Tbase ) & a, zero_t ) { 86 //assert( 0 < N ); 87 subcheck( a, 0L, 0, N ); 88 return (Timmed &)a.strides[0]; 89 } 90 91 static inline Timmed & ?[?]( arpk( N, S, Timmed, Tbase ) & a, one_t ) { 92 //assert( 1 < N ); 93 subcheck( a, 1L, 0, N ); 94 return (Timmed &)a.strides[1]; 95 } 96 97 static inline Timmed & ?[?]( arpk( N, S, Timmed, Tbase ) & a, int i ) { 98 //assert( i < N ); 99 subcheck( a, (long int)i, 0, N ); 100 return (Timmed &)a.strides[i]; 101 } 102 103 static inline const Timmed & ?[?]( const arpk( N, S, Timmed, Tbase ) & a, int i ) { 104 //assert( i < N ); 105 subcheck( a, (long int)i, 0, N ); 106 return (Timmed &)a.strides[i]; 107 } 108 109 static inline Timmed & ?[?]( arpk( N, S, Timmed, Tbase ) & a, unsigned int i ) { 110 //assert( i < N ); 111 subcheck( a, (long int)i, 0, N ); 112 return (Timmed &)a.strides[i]; 113 } 114 115 static inline const Timmed & ?[?]( const arpk( N, S, Timmed, Tbase ) & a, unsigned int i ) { 116 //assert( i < N ); 117 subcheck( a, (unsigned long int)i, 0, N ); 118 return (Timmed &)a.strides[i]; 119 } 120 121 static inline Timmed & ?[?]( arpk( N, S, Timmed, Tbase ) & a, long int i ) { 122 //assert( i < N ); 123 subcheck( a, i, 0, N ); 124 return (Timmed &)a.strides[i]; 125 } 126 127 static inline const Timmed & ?[?]( const arpk( N, S, Timmed, Tbase ) & a, long int i ) { 128 //assert( i < N ); 129 subcheck( a, i, 0, N ); 130 return (Timmed &)a.strides[i]; 131 } 132 133 static inline Timmed & ?[?]( arpk( N, S, Timmed, Tbase ) & a, unsigned long int i ) { 134 //assert( i < N ); 135 subcheck( a, i, 0, N ); 136 return (Timmed &)a.strides[i]; 137 } 138 139 static inline const Timmed & ?[?]( const arpk( N, S, Timmed, Tbase ) & a, unsigned long int i ) { 140 //assert( i < N ); 141 subcheck( a, i, 0, N ); 142 return (Timmed &)a.strides[i]; 143 } 144 145 static inline size_t ?`len( arpk( N, S, Timmed, Tbase ) & a ) { 146 return N; 147 } 148 149 static inline void __taglen( tag(arpk( N, S, Timmed, Tbase )), tag(N) ) {} 38 39 // 40 // Single-dim array sruct (with explicit packing and atom) 41 // 42 struct arpk { 43 S strides[N]; 44 }; 45 46 // About the choice of integral types offered as subscript overloads: 47 // Intent is to cover these use cases: 48 // a[0] // i : zero_t 49 // a[1] // i : one_t 50 // a[2] // i : int 51 // float foo( ptrdiff_t i ) { return a[i]; } // i : ptrdiff_t 52 // float foo( size_t i ) { return a[i]; } // i : size_t 53 // forall( [N] ) ... for( i; N ) { total += a[i]; } // i : typeof( sizeof(42) ) 54 // for( i; 5 ) { total += a[i]; } // i : int 55 // 56 // It gets complicated by: 57 // - CFA does overloading on concrete types, like int and unsigned int, not on typedefed 58 // types like size_t. So trying to overload on ptrdiff_t vs int works in 64-bit mode 59 // but not in 32-bit mode. 60 // - Given bug of Trac #247, CFA gives sizeof expressions type unsigned long int, when it 61 // should give them type size_t. 62 // 63 // gcc -m32 cfa -m32 given bug gcc -m64 (and cfa) 64 // ptrdiff_t int int long int 65 // size_t unsigned int unsigned int unsigned long int 66 // typeof( sizeof(42) ) unsigned int unsigned long int unsigned long int 67 // int int int int 68 // 69 // So the solution must support types {zero_t, one_t, int, unsigned int, long int, unsigned long int} 70 // 71 // The solution cannot rely on implicit conversions (e.g. just have one overload for ptrdiff_t) 72 // because assertion satisfaction requires types to match exacly. Both higher-dimensional 73 // subscripting and operations on slices use asserted subscript operators. The test case 74 // array-container/array-sbscr-cases covers the combinations. Mike beleives that commenting out 75 // any of the current overloads leads to one of those cases failing, either on 64- or 32-bit. 76 // Mike is open to being shown a smaller set of overloads that still passes the test. 77 78 static inline Timmed & ?[?]( arpk(N, S, Timmed, Tbase) & a, zero_t ) { 79 assert( 0 < N ); 80 return (Timmed &) a.strides[0]; 81 } 82 83 static inline Timmed & ?[?]( arpk(N, S, Timmed, Tbase) & a, one_t ) { 84 assert( 1 < N ); 85 return (Timmed &) a.strides[1]; 86 } 87 88 static inline Timmed & ?[?]( arpk(N, S, Timmed, Tbase) & a, int i ) { 89 assert( i < N ); 90 return (Timmed &) a.strides[i]; 91 } 92 93 static inline const Timmed & ?[?]( const arpk(N, S, Timmed, Tbase) & a, int i ) { 94 assert( i < N ); 95 return (Timmed &) a.strides[i]; 96 } 97 98 static inline Timmed & ?[?]( arpk(N, S, Timmed, Tbase) & a, unsigned int i ) { 99 assert( i < N ); 100 return (Timmed &) a.strides[i]; 101 } 102 103 static inline const Timmed & ?[?]( const arpk(N, S, Timmed, Tbase) & a, unsigned int i ) { 104 assert( i < N ); 105 return (Timmed &) a.strides[i]; 106 } 107 108 static inline Timmed & ?[?]( arpk(N, S, Timmed, Tbase) & a, long int i ) { 109 assert( i < N ); 110 return (Timmed &) a.strides[i]; 111 } 112 113 static inline const Timmed & ?[?]( const arpk(N, S, Timmed, Tbase) & a, long int i ) { 114 assert( i < N ); 115 return (Timmed &) a.strides[i]; 116 } 117 118 static inline Timmed & ?[?]( arpk(N, S, Timmed, Tbase) & a, unsigned long int i ) { 119 assert( i < N ); 120 return (Timmed &) a.strides[i]; 121 } 122 123 static inline const Timmed & ?[?]( const arpk(N, S, Timmed, Tbase) & a, unsigned long int i ) { 124 assert( i < N ); 125 return (Timmed &) a.strides[i]; 126 } 127 128 static inline size_t ?`len( arpk(N, S, Timmed, Tbase) & a ) { 129 return N; 130 } 131 132 static inline void __taglen( tag(arpk(N, S, Timmed, Tbase)), tag(N) ) {} 150 133 } 151 134 152 135 // RAII pattern has workarounds for 153 136 // - Trac 226: Simplest handling would be, require immediate element to be otype, let autogen 154 // raii happen. Performance on even a couple dimensions is unacceptable because of exponential155 // thunk creation: ?{}() needs all four otype funcs from next level, so does ^?{}(), so do the156 // other two. This solution offers ?{}() that needs only ?{}(), and similar for ^?{}.137 // raii happen. Performance on even a couple dimensions is unacceptable because of exponential 138 // thunk creation: ?{}() needs all four otype funcs from next level, so does ^?{}(), so do the 139 // other two. This solution offers ?{}() that needs only ?{}(), and similar for ^?{}. 157 140 158 141 forall( [N], S & | sized(S), Timmed &, Tbase & | { void ?{}( Timmed & ); } ) 159 static inline void ?{}( arpk( N, S, Timmed, Tbase ) & this ) {160 void ?{}( S (&)[N] ) {}161 ?{}(this.strides);162 163 for (i; N) ?{}( (Timmed &)this.strides[i] );142 static inline void ?{}( arpk(N, S, Timmed, Tbase) & this ) { 143 void ?{}( S (&)[N] ) {} 144 ?{}(this.strides); 145 146 for (i; N) ?{}( (Timmed &) this.strides[i] ); 164 147 } 165 148 166 149 forall( [N], S & | sized(S), Timmed &, Tbase & | { void ^?{}( Timmed & ); } ) 167 static inline void ^?{}( arpk( N, S, Timmed, Tbase) & this ) {168 void ^?{}( S (&)[N] ) {}169 ^?{}(this.strides);170 171 for (i; N ) {172 ^?{}( (Timmed &)this.strides[N-i-1] );173 }150 static inline void ^?{}( arpk(N, S, Timmed, Tbase) & this ) { 151 void ^?{}( S (&)[N] ) {} 152 ^?{}(this.strides); 153 154 for (i; N ) { 155 ^?{}( (Timmed &) this.strides[N-i-1] ); 156 } 174 157 } 175 158 … … 182 165 183 166 forall( [N], ZTags ... , Trslt &, Tatom & | { Trslt mkar_( tag(Tatom), ZTags ); } ) 184 static inline arpk( N, Trslt, Trslt, Tatom) mkar_( tag(Tatom), tag(N), ZTags ) {}167 static inline arpk(N, Trslt, Trslt, Tatom) mkar_( tag(Tatom), tag(N), ZTags ) {} 185 168 186 169 // based on https://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in-variadic-macros 187 170 188 // Make a FOREACH macro189 #define FE_0(WHAT)190 #define FE_1(WHAT, X) WHAT(X)191 #define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__)192 #define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__)193 #define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__)194 #define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__)195 //... repeat as needed196 197 #define GET_MACRO(_0,_1,_2,_3,_4,_5,NAME,...) NAME198 #define FOR_EACH(action,...) \199 GET_MACRO(_0,__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action,__VA_ARGS__)171 // Make a FOREACH macro 172 #define FE_0(WHAT) 173 #define FE_1(WHAT, X) WHAT(X) 174 #define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__) 175 #define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__) 176 #define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__) 177 #define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__) 178 //... repeat as needed 179 180 #define GET_MACRO(_0,_1,_2,_3,_4,_5,NAME,...) NAME 181 #define FOR_EACH(action,...) \ 182 GET_MACRO(_0,__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1,FE_0)(action,__VA_ARGS__) 200 183 201 184 #define COMMA_ttag(X) , ttag(X) … … 217 200 forall( TA &, TB &, TC &, IxAB, IxBC ... | { TB & ?[?]( TA &, IxAB ); TC & ?[?]( TB &, IxBC ); } ) 218 201 static inline TC & ?[?]( TA & this, IxAB ab, IxBC bc ) { 219 return this[ab][bc];202 return this[ab][bc]; 220 203 } 221 204 … … 226 209 forall( TA &, TB &, TC &, IxAB_0, IxBC | { TB & ?[?]( TA &, IxAB_0 ); TC & ?[?]( TB &, IxBC ); } ) 227 210 static inline TC & ?[?]( TA & this, IxAB_0 ab, IxBC bc ) { 228 return this[ab][bc];211 return this[ab][bc]; 229 212 } 230 213 231 214 forall( TA &, TB &, TC &, IxAB_0, IxAB_1, IxBC | { TB & ?[?]( TA &, IxAB_0, IxAB_1 ); TC & ?[?]( TB &, IxBC ); } ) 232 215 static inline TC & ?[?]( TA & this, IxAB_0 ab0, IxAB_1 ab1, IxBC bc ) { 233 return this[[ab0,ab1]][bc];216 return this[[ab0,ab1]][bc]; 234 217 } 235 218 236 219 forall( TA &, TB &, TC &, IxAB_0, IxAB_1, IxAB_2, IxBC | { TB & ?[?]( TA &, IxAB_0, IxAB_1, IxAB_2 ); TC & ?[?]( TB &, IxBC ); } ) 237 220 static inline TC & ?[?]( TA & this, IxAB_0 ab0, IxAB_1 ab1, IxAB_2 ab2, IxBC bc ) { 238 return this[[ab0,ab1,ab2]][bc];221 return this[[ab0,ab1,ab2]][bc]; 239 222 } 240 223 … … 254 237 // Base 255 238 forall( [Nq], Sq & | sized(Sq), Tbase & ) 256 static inline tag(arpk( Nq, Sq, Tbase, Tbase )) enq_( tag(Tbase ), tag(Nq), tag(Sq), tag(Tbase) ) {257 tag(arpk( Nq, Sq, Tbase, Tbase)) ret;258 return ret;239 static inline tag(arpk(Nq, Sq, Tbase, Tbase)) enq_( tag(Tbase), tag(Nq), tag(Sq), tag(Tbase) ) { 240 tag(arpk(Nq, Sq, Tbase, Tbase)) ret; 241 return ret; 259 242 } 260 243 261 244 // Rec 262 245 forall( [Nq], Sq & | sized(Sq), [N], S & | sized(S), recq &, recr &, Tbase & | { tag(recr) enq_( tag(Tbase), tag(Nq), tag(Sq), tag(recq) ); } ) 263 static inline tag(arpk( N, S, recr, Tbase )) enq_( tag(Tbase ), tag(Nq), tag(Sq), tag(arpk( N, S, recq, Tbase)) ) {264 tag(arpk( N, S, recr, Tbase)) ret;265 return ret;246 static inline tag(arpk(N, S, recr, Tbase)) enq_( tag(Tbase), tag(Nq), tag(Sq), tag(arpk(N, S, recq, Tbase)) ) { 247 tag(arpk(N, S, recr, Tbase)) ret; 248 return ret; 266 249 } 267 250 … … 269 252 extern struct all_t {} all; 270 253 forall( [N], S & | sized(S), Te &, result &, Tbase & | { tag(result) enq_( tag(Tbase), tag(N), tag(S), tag(Te) ); } ) 271 static inline result & ?[?]( arpk( N, S, Te, Tbase) & this, all_t ) {272 return (result&) this;254 static inline result & ?[?]( arpk(N, S, Te, Tbase) & this, all_t ) { 255 return (result&) this; 273 256 } 274 257 … … 280 263 // forall(A &, Tv &, [N]) 281 264 // trait ar { 282 // Tv& ?[?]( A&, zero_t );283 // Tv& ?[?]( A&, one_t );284 // Tv& ?[?]( A&, int);285 // ...286 // size_t ?`len( A& );287 // void __taglen( tag(C), tag(N) );265 // Tv& ?[?]( A&, zero_t ); 266 // Tv& ?[?]( A&, one_t ); 267 // Tv& ?[?]( A&, int ); 268 // ... 269 // size_t ?`len( A& ); 270 // void __taglen( tag(C), tag(N) ); 288 271 // }; 289 272 290 273 // working around N's not being accepted as arguments to traits 291 274 292 #define ar( A, Tv, N ) {\293 Tv& ?[?]( A&, zero_t );\294 Tv& ?[?]( A&, one_t );\295 Tv& ?[?]( A&, int );\296 Tv& ?[?]( A&, unsigned int );\297 Tv& ?[?]( A&, long int );\298 Tv& ?[?]( A&, unsigned long int );\299 size_t ?`len( A& );\300 void __taglen( tag(A), tag(N) );\301 } 275 #define ar(A, Tv, N) { \ 276 Tv& ?[?]( A&, zero_t ); \ 277 Tv& ?[?]( A&, one_t ); \ 278 Tv& ?[?]( A&, int ); \ 279 Tv& ?[?]( A&, unsigned int ); \ 280 Tv& ?[?]( A&, long int ); \ 281 Tv& ?[?]( A&, unsigned long int ); \ 282 size_t ?`len( A& ); \ 283 void __taglen( tag(A), tag(N) ); \ 284 } -
libcfa/src/enum.cfa
rce02877 r38e20a80 25 25 E pred( E e ) { 26 26 E lower = lowerBound(); 27 if ( fromInstance( e) <= fromInstance(lower ) )27 if ( fromInstance(e) <= fromInstance(lower ) ) 28 28 abort( "call to pred() exceeds enumeration lower bound of %d", fromInstance( lower ) ); 29 return pred_unsafe( e);29 return pred_unsafe(e); 30 30 } 31 31 -
src/GenPoly/Box.cpp
rce02877 r38e20a80 673 673 TypeVarMap const & typeVars, 674 674 ast::TypeSubstitution const * typeSubs ) { 675 if ( auto name = expr->func.as<ast::NameExpr>() ) { 676 if ( "*?" == name->name ) { 677 // It's a deref. 678 // Must look under the * (and strip its ptr-ty) because expr's 679 // result could be ar/ptr-decayed. If expr.inner:T(*)[n], then 680 // expr is a poly deref, even though expr:T*, which is not poly. 681 auto ptrExpr = expr->args.front(); 682 auto ptrTy = ptrExpr->result.as<ast::PointerType>(); 683 assert(ptrTy); // thing being deref'd must be pointer 684 auto referentTy = ptrTy->base; 685 assert(referentTy); 686 return isPolyType( referentTy, typeVars, typeSubs ); 675 if ( expr->result && isPolyType( expr->result, typeVars, typeSubs ) ) { 676 if ( auto name = expr->func.as<ast::NameExpr>() ) { 677 if ( "*?" == name->name ) { 678 return true; 679 } 687 680 } 688 681 } … … 1199 1192 assert( 2 == expr->args.size() ); 1200 1193 1201 ast::Type const * arg1Ty = expr->args.front()->result; 1202 ast::Type const * arg2Ty = expr->args.back()->result; 1203 1204 // two cases: a[i] with first arg poly ptr, i[a] with second arg poly ptr 1205 bool isPoly1 = isPolyPtr( arg1Ty, scopeTypeVars, typeSubs ) != nullptr; 1206 bool isPoly2 = isPolyPtr( arg2Ty, scopeTypeVars, typeSubs ) != nullptr; 1207 1194 ast::Type const * baseType1 = 1195 isPolyPtr( expr->args.front()->result, scopeTypeVars, typeSubs ); 1196 ast::Type const * baseType2 = 1197 isPolyPtr( expr->args.back()->result, scopeTypeVars, typeSubs ); 1208 1198 // If neither argument is a polymorphic pointer, do nothing. 1209 if ( ! isPoly1 && !isPoly2 ) {1199 if ( !baseType1 && !baseType2 ) { 1210 1200 return expr; 1211 1201 } 1212 1202 // The arguments cannot both be polymorphic pointers. 1213 assert( ! isPoly1 || !isPoly2 );1203 assert( !baseType1 || !baseType2 ); 1214 1204 // (So exactly one of the arguments is a polymorphic pointer.) 1215 1205 … … 1220 1210 ast::UntypedExpr * ret = new ast::UntypedExpr( location, 1221 1211 new ast::NameExpr( location, "?+?" ) ); 1222 if ( isPoly1 ) { 1223 assert( arg1Ty ); 1224 auto arg1TyPtr = dynamic_cast<ast::PointerType const * >( arg1Ty ); 1225 assert( arg1TyPtr ); 1212 if ( baseType1 ) { 1226 1213 auto multiply = ast::UntypedExpr::createCall( location2, "?*?", { 1227 1214 expr->args.back(), 1228 new ast::SizeofExpr( location1, deepCopy( arg1TyPtr->base) ),1215 new ast::SizeofExpr( location1, deepCopy( baseType1 ) ), 1229 1216 } ); 1230 1217 ret->args.push_back( expr->args.front() ); 1231 1218 ret->args.push_back( multiply ); 1232 1219 } else { 1233 assert( isPoly2 ); 1234 assert( arg2Ty ); 1235 auto arg2TyPtr = dynamic_cast<ast::PointerType const * >( arg2Ty ); 1236 assert( arg2TyPtr ); 1220 assert( baseType2 ); 1237 1221 auto multiply = ast::UntypedExpr::createCall( location1, "?*?", { 1238 1222 expr->args.front(), 1239 new ast::SizeofExpr( location2, deepCopy( arg2TyPtr->base) ),1223 new ast::SizeofExpr( location2, deepCopy( baseType2 ) ), 1240 1224 } ); 1241 1225 ret->args.push_back( multiply ); … … 1250 1234 assert( 1 == expr->args.size() ); 1251 1235 1252 auto ptrExpr = expr->args.front();1253 auto ptrTy = ptrExpr->result.as<ast::PointerType>();1254 assert(ptrTy); // thing being deref'd must be pointer1255 auto referentTy = ptrTy->base;1256 assert(referentTy);1257 1258 1236 // If this isn't for a poly type, then do nothing. 1259 if ( !isPolyType( referentTy, scopeTypeVars, typeSubs ) ) {1237 if ( !isPolyType( expr->result, scopeTypeVars, typeSubs ) ) { 1260 1238 return expr; 1261 1239 } … … 1265 1243 // Fix expression type to remove pointer. 1266 1244 ret->result = expr->result; 1267 // apply pointer decay1268 if (auto retArTy = ret->result.as<ast::ArrayType>()) {1269 ret->result = new ast::PointerType( retArTy->base );1270 }1271 1245 ret->env = expr->env ? expr->env : ret->env; 1272 1246 return ret; … … 1317 1291 return makeIncrDecrExpr( 1318 1292 expr->location, expr, baseType, "++?" == varName ); 1319 // Addition and Subtra ction Intrinsics:1293 // Addition and Subtration Intrinsics: 1320 1294 } else if ( "?+?" == varName || "?-?" == varName ) { 1321 1295 assert( expr->result ); 1322 1296 assert( 2 == expr->args.size() ); 1323 1297 1324 ast::Type const * arg1Ty = expr->args.front()->result; 1325 ast::Type const * arg2Ty = expr->args.back()->result; 1326 1327 bool isPoly1 = isPolyPtr( arg1Ty, scopeTypeVars, typeSubs ) != nullptr; 1328 bool isPoly2 = isPolyPtr( arg2Ty, scopeTypeVars, typeSubs ) != nullptr; 1298 auto baseType1 = 1299 isPolyPtr( expr->args.front()->result, scopeTypeVars, typeSubs ); 1300 auto baseType2 = 1301 isPolyPtr( expr->args.back()->result, scopeTypeVars, typeSubs ); 1329 1302 1330 1303 CodeLocation const & location = expr->location; 1331 1304 CodeLocation const & location1 = expr->args.front()->location; 1332 1305 CodeLocation const & location2 = expr->args.back()->location; 1333 // LHS minus RHS -> (LHS minus RHS) / sizeof(LHS) 1334 if ( isPoly1 && isPoly2 ) { 1335 assert( "?-?" == varName ); 1336 assert( arg1Ty ); 1337 auto arg1TyPtr = dynamic_cast<ast::PointerType const * >( arg1Ty ); 1338 assert( arg1TyPtr ); 1306 // LHS op RHS -> (LHS op RHS) / sizeof(LHS) 1307 if ( baseType1 && baseType2 ) { 1339 1308 auto divide = ast::UntypedExpr::createCall( location, "?/?", { 1340 1309 expr, 1341 new ast::SizeofExpr( location, deepCopy( arg1TyPtr->base) ),1310 new ast::SizeofExpr( location, deepCopy( baseType1 ) ), 1342 1311 } ); 1343 1312 if ( expr->env ) divide->env = expr->env; 1344 1313 return divide; 1345 1314 // LHS op RHS -> LHS op (RHS * sizeof(LHS)) 1346 } else if ( isPoly1 ) { 1347 assert( arg1Ty ); 1348 auto arg1TyPtr = dynamic_cast<ast::PointerType const * >( arg1Ty ); 1349 assert( arg1TyPtr ); 1315 } else if ( baseType1 ) { 1350 1316 auto multiply = ast::UntypedExpr::createCall( location2, "?*?", { 1351 1317 expr->args.back(), 1352 new ast::SizeofExpr( location1, deepCopy( arg1TyPtr->base) ),1318 new ast::SizeofExpr( location1, deepCopy( baseType1 ) ), 1353 1319 } ); 1354 1320 return ast::mutate_field_index( 1355 1321 expr, &ast::ApplicationExpr::args, 1, multiply ); 1356 1322 // LHS op RHS -> (LHS * sizeof(RHS)) op RHS 1357 } else if ( isPoly2 ) { 1358 assert( arg2Ty ); 1359 auto arg2TyPtr = dynamic_cast<ast::PointerType const * >( arg2Ty ); 1360 assert( arg2TyPtr ); 1323 } else if ( baseType2 ) { 1361 1324 auto multiply = ast::UntypedExpr::createCall( location1, "?*?", { 1362 1325 expr->args.front(), 1363 new ast::SizeofExpr( location2, deepCopy( arg2TyPtr->base) ),1326 new ast::SizeofExpr( location2, deepCopy( baseType2 ) ), 1364 1327 } ); 1365 1328 return ast::mutate_field_index( … … 1625 1588 /// Change the type of generic aggregate members to char[]. 1626 1589 void mutateMembers( ast::AggregateDecl * aggr ); 1627 /// Returns the calculated sizeof /alignof expressions for type, or1628 /// nullptr for use C size/alignof().1590 /// Returns the calculated sizeof expression for type, or nullptr for use 1591 /// C sizeof(). 1629 1592 ast::Expr const * genSizeof( CodeLocation const &, ast::Type const * ); 1630 ast::Expr const * genAlignof( CodeLocation const &, ast::Type const * );1631 1593 /// Enters a new scope for type-variables, 1632 1594 /// adding the type variables from the provided type. … … 1651 1613 {} 1652 1614 1653 static ast::Type * polyToMonoTypeRec( CodeLocation const & loc,1654 ast::Type const * ty ) {1655 ast::Type * ret;1656 if ( auto aTy = dynamic_cast<ast::ArrayType const *>( ty ) ) {1657 // recursive case1658 auto monoBase = polyToMonoTypeRec( loc, aTy->base );1659 ret = new ast::ArrayType( monoBase, aTy->dimension,1660 aTy->isVarLen, aTy->isStatic, aTy->qualifiers );1661 } else {1662 // base case1663 auto charType = new ast::BasicType( ast::BasicKind::Char );1664 auto size = new ast::NameExpr( loc,1665 sizeofName( Mangle::mangleType( ty ) ) );1666 ret = new ast::ArrayType( charType, size,1667 ast::VariableLen, ast::DynamicDim, ast::CV::Qualifiers() );1668 }1669 return ret;1670 }1671 1672 1615 /// Converts polymorphic type into a suitable monomorphic representation. 1673 /// Simple cases: T -> __attribute__(( aligned(8) )) char[sizeof_T]; 1674 /// Array cases: T[eOut][eIn] -> __attribute__(( aligned(8) )) char[eOut][eIn][sizeof_T]; 1675 ast::Type * polyToMonoType( CodeLocation const & loc, ast::Type const * ty ) { 1676 auto ret = polyToMonoTypeRec( loc, ty ); 1616 /// Currently: __attribute__(( aligned(8) )) char[size_T]; 1617 ast::Type * polyToMonoType( CodeLocation const & location, 1618 ast::Type const * declType ) { 1619 auto charType = new ast::BasicType( ast::BasicKind::Char ); 1620 auto size = new ast::NameExpr( location, 1621 sizeofName( Mangle::mangleType( declType ) ) ); 1622 auto ret = new ast::ArrayType( charType, size, 1623 ast::VariableLen, ast::DynamicDim, ast::CV::Qualifiers() ); 1677 1624 ret->attributes.emplace_back( new ast::Attribute( "aligned", 1678 { ast::ConstantExpr::from_int( loc , 8 ) } ) );1625 { ast::ConstantExpr::from_int( location, 8 ) } ) ); 1679 1626 return ret; 1680 1627 } … … 1769 1716 // Forally, side effects are not safe in this function. But it works. 1770 1717 erase_if( mutDecl->attributes, matchAndMove ); 1771 1772 // Change the decl's type.1773 // Upon finishing the box pass, it shall be void*.1774 // At this middle-of-box-pass point, that type is T.1775 1776 // example 11777 // before box: T t ;1778 // before here: char _bufxx [_sizeof_Y1T]; T t = _bufxx;1779 // after here: char _bufxx [_sizeof_Y1T]; T t = _bufxx; (no change here - non array case)1780 // after box: char _bufxx [_sizeof_Y1T]; void *t = _bufxx;1781 1782 // example 21783 // before box: T t[42] ;1784 // before here: char _bufxx[42][_sizeof_Y1T]; T t[42] = _bufxx;1785 // after here: char _bufxx[42][_sizeof_Y1T]; T t = _bufxx;1786 // after box: char _bufxx[42][_sizeof_Y1T]; void *t = _bufxx;1787 1788 // Strip all "array of" wrappers1789 while ( auto arrayType = dynamic_cast<ast::ArrayType const *>( mutDecl->type.get() ) ) {1790 mutDecl->type = arrayType->base;1791 }1792 1718 1793 1719 mutDecl->init = new ast::SingleInit( decl->location, … … 1943 1869 ast::AlignofExpr const * expr ) { 1944 1870 ast::Type const * type = expr->type ? expr->type : expr->expr->result; 1945 ast::Expr const * gen = genAlignof( expr->location, type ); 1946 return ( gen ) ? gen : expr; 1871 if ( findGeneric( expr->location, type ) ) { 1872 return new ast::NameExpr( expr->location, 1873 alignofName( Mangle::mangleType( type ) ) ); 1874 } else { 1875 return expr; 1876 } 1947 1877 } 1948 1878 … … 2165 2095 2166 2096 return true; 2167 2168 } else if ( auto inst = dynamic_cast<ast::ArrayType const *>( type ) ) {2169 return findGeneric( location, inst->base );2170 2097 } 2171 2098 return false; … … 2228 2155 return makeOp( location, "?*?", sizeofBase, dim ); 2229 2156 } else if ( findGeneric( location, type ) ) { 2230 // Generate reference to _sizeof parameter2157 // Generate calculated size for generic type. 2231 2158 return new ast::NameExpr( location, sizeofName( 2232 Mangle::mangleType( type ) ) );2233 } else {2234 return nullptr;2235 }2236 }2237 2238 ast::Expr const * PolyGenericCalculator::genAlignof(2239 CodeLocation const & location, ast::Type const * type ) {2240 if ( auto * array = dynamic_cast<ast::ArrayType const *>( type ) ) {2241 // alignof array is alignof element2242 return genAlignof( location, array->base );2243 } else if ( findGeneric( location, type ) ) {2244 // Generate reference to _alignof parameter2245 return new ast::NameExpr( location, alignofName(2246 2159 Mangle::mangleType( type ) ) ); 2247 2160 } else { -
src/Parser/parser.yy
rce02877 r38e20a80 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Jul 26 14:09:30202413 // Update Count : 67 3312 // Last Modified On : Tue Jul 9 10:29:01 2024 13 // Update Count : 6713 14 14 // 15 15 … … 852 852 ; 853 853 854 argument_expression_list_opt: 855 // empty 856 { $$ = nullptr; } 857 | argument_expression_list 858 ; 859 860 argument_expression_list: 861 argument_expression 862 | argument_expression_list_opt ',' argument_expression 863 { $$ = $1->set_last( $3 ); } 864 ; 865 866 argument_expression: 867 '@' // CFA, default parameter 868 { SemanticError( yylloc, "Default parameter for argument is currently unimplemented." ); $$ = nullptr; } 869 // { $$ = new ExpressionNode( build_constantInteger( *new string( "2" ) ) ); } 870 | assignment_expression 871 ; 872 854 873 field_name_list: // CFA, tuple field selector 855 874 field … … 1097 1116 constant_expression: 1098 1117 conditional_expression 1099 ;1100 1101 argument_expression_list_opt:1102 // empty1103 { $$ = nullptr; }1104 | argument_expression_list1105 ;1106 1107 argument_expression_list:1108 argument_expression1109 // | argument_expression_list_opt ',' argument_expression // CFA, allow empty argument1110 | argument_expression_list ',' argument_expression // no empty argument1111 { $$ = $1->set_last( $3 ); }1112 ;1113 1114 argument_expression:1115 '?' // CFA, default parameter1116 // { SemanticError( yylloc, "Argument to default parameter is currently unimplemented." ); $$ = nullptr; }1117 { $$ = new ExpressionNode( build_constantInteger( yylloc, *new string( "2" ) ) ); }1118 | '?' identifier '=' assignment_expression // CFA, keyword argument1119 // { SemanticError( yylloc, "keyword argument is currently unimplemented." ); $$ = nullptr; }1120 { $$ = $4; }1121 | assignment_expression1122 1118 ; 1123 1119 … … 3547 3543 identifier_at 3548 3544 { $$ = DeclarationNode::newName( $1 ); } 3549 | '?' identifier3550 // { SemanticError( yylloc, "keyword parameter is currently unimplemented." ); $$ = nullptr; }3551 { $$ = DeclarationNode::newName( $2 ); }3552 3545 | '(' paren_identifier ')' // redundant parenthesis 3553 3546 { $$ = $2; } -
tests/Makefile.am
rce02877 r38e20a80 69 69 .PHONY : concurrency list .validate .test_makeflags 70 70 .INTERMEDIATE : .validate .validate.cfa .test_makeflags 71 EXTRA_PROGRAMS = a rray-collections/boxed avl_test linkonce linking/mangling/anon .dummy_hack # build but do not install71 EXTRA_PROGRAMS = avl_test linkonce linking/mangling/anon .dummy_hack # build but do not install 72 72 EXTRA_DIST = test.py \ 73 73 pybin/__init__.py \ … … 77 77 pybin/tools.py \ 78 78 long_tests.hfa \ 79 array-collections/boxed.hfa \80 array-collections/boxed.cases.hfa \81 79 avltree/avl-private.h \ 82 80 avltree/avl.h \ … … 106 104 done 107 105 108 array_collections_boxed_SOURCES = array-collections/boxed.main.cfa array-collections/boxed.bookend.cfa109 106 avl_test_SOURCES = avltree/avl_test.cfa avltree/avl0.cfa avltree/avl1.cfa avltree/avl2.cfa avltree/avl3.cfa avltree/avl4.cfa avltree/avl-private.cfa 110 107 linkonce_SOURCES = link-once/main.cfa link-once/partner.cfa -
tests/array-collections/array-sbscr-types.cfa
rce02877 r38e20a80 28 28 // generally using ptrdiff_t-typed variables to convey numeric values. 29 29 30 #include <assert.h>31 30 32 31 #define show( expr ) printf( "%.1f\n", expr )
Note:
See TracChangeset
for help on using the changeset viewer.