Changeset 10a9479d


Ignore:
Timestamp:
Nov 23, 2024, 8:28:37 PM (11 months ago)
Author:
JiadaL <j82liang@…>
Branches:
master
Children:
956b389
Parents:
b006c51e (diff), de7b7a5 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

Files:
6 added
60 edited
1 moved

Legend:

Unmodified
Added
Removed
  • doc/LaTeXmacros/common.sty

    rb006c51e r10a9479d  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sun Aug 25 11:52:19 2024
    14 %% Update Count     : 661
     13%% Last Modified On : Sun Nov  3 21:10:34 2024
     14%% Update Count     : 662
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    2727\usepackage[ignoredisplayed]{enumitem}  % do not affect trivlist
    2828\setlist{labelsep=1ex}% global
    29 \setlist[itemize]{topsep=0.5ex,parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent,leftmargin=\parindent}% global
     29\setlist{topsep=0pt}% global
     30\setlist[itemize]{parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent}% global
    3031\setlist[itemize,1]{label=\textbullet}% local
    3132%\renewcommand{\labelitemi}{{\raisebox{0.25ex}{\footnotesize$\bullet$}}}
    32 \setlist[enumerate]{topsep=0.5ex,parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent}% global
     33\setlist[enumerate]{parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent}% global
    3334\setlist[enumerate,2]{leftmargin=\parindent,labelsep=*,align=parleft,label=\alph*.}% local
    34 \setlist[description]{topsep=0.5ex,itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex}
     35\setlist[description]{itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex}
    3536
    3637% Names used in the document.
  • doc/LaTeXmacros/common.tex

    rb006c51e r10a9479d  
    1111%% Created On       : Sat Apr  9 10:06:17 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Sun Aug 25 11:52:20 2024
    14 %% Update Count     : 673
     13%% Last Modified On : Sun Nov  3 09:11:30 2024
     14%% Update Count     : 684
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    2727\usepackage[ignoredisplayed]{enumitem}  % do not affect trivlist
    2828\setlist{labelsep=1ex}% global
    29 \setlist[itemize]{topsep=0.5ex,parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent,leftmargin=\parindent}% global
     29\setlist{topsep=0pt}% global
     30\setlist[itemize]{parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent}% global
    3031\setlist[itemize,1]{label=\textbullet}% local
    3132%\renewcommand{\labelitemi}{{\raisebox{0.25ex}{\footnotesize$\bullet$}}}
    32 \setlist[enumerate]{topsep=0.5ex,parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent}% global
     33\setlist[enumerate]{parsep=0.25ex,itemsep=0.25ex,listparindent=\parindent}% global
    3334\setlist[enumerate,2]{leftmargin=\parindent,labelsep=*,align=parleft,label=\alph*.}% local
    34 \setlist[description]{topsep=0.5ex,itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex}
     35\setlist[description]{itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex}
    3536
    3637% Names used in the document.
  • doc/bibliography/pl.bib

    rb006c51e r10a9479d  
    36853685    address     = {Waterloo, Ontario, Canada, N2L 3G1},
    36863686    note        = {\url{http://uwspace.uwaterloo.ca/bitstream/10012/3501/1/Thesis.pdf}},
     3687}
     3688
     3689@article{Hesselink24,
     3690    author      = {Wim A. Hesselink and Peter A. Buhr and Colby A. Parsons},
     3691    title       = {First-Come-First-Served as a Separate Principle},
     3692    journal     = {ACM Trans. Parallel Comput.},
     3693    publisher   = {ACM},
     3694    address     = {New York, NY, USA},
     3695    volume      = 11,
     3696    number      = 4,
     3697    month       = nov,
     3698    year        = 2024,
    36873699}
    36883700
  • doc/theses/fangren_yu_MMath/content1.tex

    rb006c51e r10a9479d  
    1 \chapter{Recent Features Introduced to \CFA}
     1\chapter{\CFA Features and Type System Interactions}
    22\label{c:content1}
    33
    4 This chapter discusses some recent additions to the \CFA language and their interactions with the type system.
     4This chapter discusses \CFA feature introduced over time by multiple people and their interactions with the type system.
    55
    66
     
    1717Succinctly, if the address changes often, use a pointer;
    1818if the value changes often, use a reference.
    19 Note, \CC made its reference address immutable starting a \emph{belief} that immutability is a fundamental aspect of a reference's pointer.
    20 The results is asymmetry semantics between the pointer and reference.
     19Java has mutable references but no pointers.
     20\CC has mutable pointers but immutable references;
     21hence, references match with functional programming.
     22However, the consequence is asymmetry semantics between the pointer and reference.
    2123\CFA adopts a uniform policy between pointers and references where mutability is a separate property made at the declaration.
    2224
     
    3638Like pointers, reference can be cascaded, \ie a reference to a reference, \eg @&& r2@.\footnote{
    3739\CC uses \lstinline{&&} for rvalue reference, a feature for move semantics and handling the \lstinline{const} Hell problem.}
    38 Usage of a reference variable automatically performs the same number of dereferences as the number of references in its declaration, \eg @r3@ becomes @***r3@.
     40Usage of a reference variable automatically performs the same number of dereferences as the number of references in its declaration, \eg @r2@ becomes @**r2@.
    3941Finally, to reassign a reference's address needs a mechanism to stop the auto-referencing, which is accomplished by using a single reference to cancel all the auto-dereferencing, \eg @&r3 = &y@ resets @r3@'s address to point to @y@.
    4042\CFA's reference type (including multi-de/references) is powerful enough to describe the lvalue rules in C by types only.
     
    6668int x = 3; $\C{// mutable}$
    6769const int cx = 5; $\C{// immutable}$
    68 int * const cp = &x, $\C{// immutable pointer}$
     70int * const cp = &x, $\C{// immutable pointer pointer/reference}$
    6971        & const cr = cx;
    70 const int * const ccp = &cx, $\C{// immutable value and pointer}$
     72const int * const ccp = &cx, $\C{// immutable value and pointer/reference}$
    7173                        & const ccr = cx;
    72 // pointer
     74\end{cfa}
     75\begin{cquote}
     76\setlength{\tabcolsep}{26pt}
     77\begin{tabular}{@{}lll@{}}
     78pointer & reference & \\
     79\begin{cfa}
    7380*cp = 7;
    74 cp = &x; $\C{// error, assignment of read-only variable}$
    75 *ccp = 7; $\C{// error, assignment of read-only location}$
    76 ccp = &cx; $\C{// error, assignment of read-only variable}$
    77 // reference
     81cp = &x;
     82*ccp = 7;
     83ccp = &cx;
     84\end{cfa}
     85&
     86\begin{cfa}
    7887cr = 7;
    79 cr = &x; $\C{// error, assignment of read-only variable}$
    80 *ccr = 7; $\C{// error, assignment of read-only location}$
    81 ccr = &cx; $\C{// error, assignment of read-only variable}$
    82 \end{cfa}
     88cr = &x;
     89*ccr = 7;
     90ccr = &cx;
     91\end{cfa}
     92&
     93\begin{cfa}
     94// allowed
     95// error, assignment of read-only variable
     96// error, assignment of read-only location
     97// error, assignment of read-only variable
     98\end{cfa}
     99\end{tabular}
     100\end{cquote}
    83101Interestingly, C does not give a warning/error if a @const@ pointer is not initialized, while \CC does.
    84 Hence, type @& const@ is similar to \CC reference, but \CFA does not preclude initialization with a non-variable address.
     102Hence, type @& const@ is similar to a \CC reference, but \CFA does not preclude initialization with a non-variable address.
    85103For example, in system's programming, there are cases where an immutable address is initialized to a specific memory location.
    86104\begin{cfa}
     
    96114However, there is an inherent ambiguity for auto-dereferencing: every argument expression involving a reference variable can potentially mean passing the reference's value or address.
    97115Without any restrictions, this ambiguity limits the behaviour of reference types in \CFA polymorphic functions, where a type @T@ can bind to a reference or non-reference type.
    98 This ambiguity prevents the type system treating reference types the same way as other types in many cases even if type variables could be bound to reference types.
     116This ambiguity prevents the type system treating reference types the same way as other types, even if type variables could be bound to reference types.
    99117The reason is that \CFA uses a common \emph{object trait}\label{p:objecttrait} (constructor, destructor and assignment operators) to handle passing dynamic concrete type arguments into polymorphic functions, and the reference types are handled differently in these contexts so they do not satisfy this common interface.
    100118
    101119Moreover, there is also some discrepancy in how the reference types are treated in initialization and assignment expressions.
    102 For example, in line 3 of the previous example code \see{\VPageref{p:refexamples}}:
     120For example, in line 3 of the example code on \VPageref{p:refexamples}:
    103121\begin{cfa}
    104122int @&@ r1 = x,  @&&@ r2 = r1,   @&&&@ r3 = r2; $\C{// references to x}$
     
    129147vector( int @&@ ) vec; $\C{// vector of references to ints}$
    130148\end{cfa}
    131 While it is possible to write a reference type as the argument to a generic type, it is disallowed in assertion checking, if the generic type requires the object trait \see{\VPageref{p:objecttrait}} for the type argument (a fairly common use case).
     149While it is possible to write a reference type as the argument to a generic type, it is disallowed in assertion checking, if the generic type requires the object trait \see{\VPageref{p:objecttrait}} for the type argument, a fairly common use case.
    132150Even if the object trait can be made optional, the current type system often misbehaves by adding undesirable auto-dereference on the referenced-to value rather than the reference variable itself, as intended.
    133151Some tweaks are necessary to accommodate reference types in polymorphic contexts and it is unclear what can or cannot be achieved.
    134 Currently, there are contexts where \CFA programmer must use pointer types, giving up the benefits of auto-dereference operations and better syntax with reference types.
     152Currently, there are contexts where \CFA programmer is forced to use a pointer type, giving up the benefits of auto-dereference operations and better syntax with reference types.
    135153
    136154
     
    165183Along with making returning multiple values a first-class feature, tuples were extended to simplify a number of other common context that normally require multiple statements and/or additional declarations, all of which reduces coding time and errors.
    166184\begin{cfa}
    167 [x, y, z] = 3; $\C[2in]{// x = 3; y = 3; z = 3, where types are different}$
     185[x, y, z] = 3; $\C[2in]{// x = 3; y = 3; z = 3, where types may be different}$
    168186[x, y] = [y, x]; $\C{// int tmp = x; x = y; y = tmp;}$
    169187void bar( int, int, int );
     
    212230bar( t2 );                      $\C{// bar defined above}$
    213231\end{cfa}
    214 \VRef[Figure]{f:Nesting} shows The difference is nesting of structures and tuples.
     232\VRef[Figure]{f:Nesting} shows the difference is nesting of structures and tuples.
    215233The left \CC nested-structure is named so it is not flattened.
    216234The middle C/\CC nested-structure is unnamed and flattened, causing an error because @i@ and @j@ are duplication names.
     
    220238
    221239\begin{figure}
    222 \setlength{\tabcolsep}{15pt}
     240\setlength{\tabcolsep}{20pt}
    223241\begin{tabular}{@{}ll@{\hspace{90pt}}l@{}}
    224242\multicolumn{1}{c}{\CC} & \multicolumn{1}{c}{C/\CC} & \multicolumn{1}{c}{tuple} \\
     
    273291As noted, tradition languages manipulate multiple values by in/out parameters and/or structures.
    274292K-W C adopted the structure for tuple values or variables, and as needed, the fields are extracted by field access operations.
    275 As well, For the tuple-assignment implementation, the left-hand tuple expression is expanded into assignments of each component, creating temporary variables to avoid unexpected side effects.
    276 For example, the tuple value returned from @foo@ is a structure, and its fields are individually assigned to a left-hand tuple, @x@, @y@, @z@, or copied directly into a corresponding tuple variable.
     293As well, for the tuple-assignment implementation, the left-hand tuple expression is expanded into assignments of each component, creating temporary variables to avoid unexpected side effects.
     294For example, the tuple value returned from @foo@ is a structure, and its fields are individually assigned to a left-hand tuple, @x@, @y@, @z@, \emph{or} copied directly into a corresponding tuple variable.
    277295
    278296In the second implementation of \CFA tuples by Rodolfo Gabriel Esteves~\cite{Esteves04}, a different strategy is taken to handle MVR functions.
     
    286304[x, y] = gives_two();
    287305\end{cfa}
    288 The Till K-W C implementation translates the program to:
     306\VRef[Figure]{f:AlternateTupleImplementation} shows the two implementation approaches.
     307In the left approach, the return statement is rewritten to pack the return values into a structure, which is returned by value, and the structure fields are indiviually assigned to the left-hand side of the assignment.
     308In the right approach, the return statement is rewritten as direct assignments into the passed-in argument addresses.
     309The right imlementation looks more concise and saves unnecessary copying.
     310The downside is indirection within @gives_two@ to access values, unless values get hoisted into registers for some period of time, which is common.
     311
     312\begin{figure}
     313\begin{cquote}
     314\setlength{\tabcolsep}{20pt}
     315\begin{tabular}{@{}ll@{}}
     316Till K-W C implementation & Rodolfo \CFA implementation \\
    289317\begin{cfa}
    290318struct _tuple2 { int _0; int _1; }
    291 struct _tuple2 gives_two() { ... struct _tuple2 ret = { r1, r2 }, return ret; }
     319struct _tuple2 gives_two() {
     320        ... struct _tuple2 ret = { r1, r2 };
     321        return ret;
     322}
    292323int x, y;
    293324struct _tuple2 _tmp = gives_two();
    294325x = _tmp._0; y = _tmp._1;
    295326\end{cfa}
    296 while the Rodolfo implementation translates it to:
    297 \begin{cfa}
    298 void gives_two( int * r1, int * r2 ) { ... *r1 = ...; *r2 = ...; return; }
     327&
     328\begin{cfa}
     329
     330void gives_two( int * r1, int * r2 ) {
     331        ... *r1 = ...; *r2 = ...;
     332        return;
     333}
    299334int x, y;
     335
    300336gives_two( &x, &y );
    301337\end{cfa}
    302 and inside the body of the function @gives_two@, the return statement is rewritten as assignments into the passed-in argument addresses.
    303 This implementation looks more concise, and in the case of returning values having nontrivial types, \eg aggregates, this implementation saves unnecessary copying.
    304 For example,
    305 \begin{cfa}
    306 [ x, y ] gives_two();
    307 int x, y;
    308 [ x, y ] = gives_two();
    309 \end{cfa}
    310 becomes
    311 \begin{cfa}
    312 void gives_two( int &, int & );
    313 int x, y;
    314 gives_two( x, y );
    315 \end{cfa}
    316 eliminiating any copying in or out of the call.
    317 The downside is indirection within @gives_two@ to access values, unless values get hoisted into registers for some period of time, which is common.
     338\end{tabular}
     339\end{cquote}
     340\caption{Alternate Tuple Implementation}
     341\label{f:AlternateTupleImplementation}
     342\end{figure}
    318343
    319344Interestingly, in the third implementation of \CFA tuples by Robert Schluntz~\cite[\S~3]{Schluntz17}, the MVR functions revert back to structure based, where it remains in the current version of \CFA.
    320345The reason for the reversion was to have a uniform approach for tuple values/variables making tuples first-class types in \CFA, \ie allow tuples with corresponding tuple variables.
    321 This extension was possible, because in parallel with Schluntz's work, generic types were being added independently by Moss~\cite{Moss19}, and the tuple variables leveraged the same implementation techniques as the generic variables.
     346This extension was possible, because in parallel with Schluntz's work, generic types were added independently by Moss~\cite{Moss19}, and the tuple variables leveraged the same implementation techniques as the generic variables.
    322347\PAB{I'm not sure about the connection here. Do you have an example of what you mean?}
    323348
     
    339364\begin{cfa}
    340365void f( int, int );
    341 void f( [int, int] );
     366void f( @[@ int, int @]@ );
    342367f( 3, 4 );  // ambiguous call
    343368\end{cfa}
     
    358383the call to @f@ can be interpreted as @T = [1]@ and @U = [2, 3, 4, 5]@, or @T = [1, 2]@ and @U = [3, 4, 5]@, and so on.
    359384The restriction ensures type checking remains tractable and does not take too long to compute.
    360 Therefore, tuple types are never present in any fixed-argument function calls.
    361 
    362 Finally, a type-safe variadic argument signature was added by Robert Schluntz~\cite[\S~4.1.2]{Schluntz17} using @forall@ and a new tuple parameter-type, denoted by the keyword @ttype @ in Schluntz's implementation, but changed to the ellipsis syntax similar to \CC's template parameter pack.
     385Therefore, tuple types are never present in any fixed-argument function calls, because of the flattening.
     386
     387Finally, a type-safe variadic argument signature was added by Robert Schluntz~\cite[\S~4.1.2]{Schluntz17} using @forall@ and a new tuple parameter-type, denoted by the keyword @ttype@ in Schluntz's implementation, but changed to the ellipsis syntax similar to \CC's template parameter pack.
    363388For C variadics, \eg @va_list@, the number and types of the arguments must be conveyed in some way, \eg @printf@ uses a format string indicating the number and types of the arguments.
    364389\VRef[Figure]{f:CVariadicMaxFunction} shows an $N$ argument @maxd@ function using the C untyped @va_list@ interface.
     
    370395\begin{figure}
    371396\begin{cfa}
    372 double maxd( int @count@, ... ) {
     397double maxd( int @count@, @...@ ) { // ellipse parameter
    373398    double max = 0;
    374399    va_list args;
     
    566591struct U u;  u.k;  u.l;
    567592\end{cfa}
    568 and the hoisted type names can clash with global types names.
     593and the hoisted type names can clash with global type names.
    569594For good reasons, \CC chose to change this semantics:
    570595\begin{cquote}
     
    584609\end{cfa}
    585610\CFA chose to adopt the \CC non-compatible change for nested types, since \CC's change has already forced certain coding changes in C libraries that must be parsed by \CC.
     611\CFA also added the ability to access from a variable through a type to a field.
     612\begin{cfa}
     613struct S s;  @s.T@.i;  @s.U@.k;
     614\end{cfa}
    586615
    587616% https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html
     
    604633\end{cfa}
    605634Note, the position of the substructure is normally unimportant, unless there is some form of memory or @union@ overlay.
    606 Like the anonymous nested types, the aggregate field names are hoisted into @struct S@, so there is direct access, \eg @s.x@ and @s.i@.
    607 However, like the implicit C hoisting of nested structures, the field names must be unique and the type names are now at a different scope level, unlike type nesting in \CC.
    608 In addition, a pointer to a structure is automatically converted to a pointer to an anonymous field for assignments and function calls, providing containment inheritance with implicit subtyping, \ie @U@ $\subset$ @S@ and @W@ $\subset$ @S@.
    609 For example:
     635Like an anonymous nested type, a named nested Plan-9 type has its field names hoisted into @struct S@, so there is direct access, \eg @s.x@ and @s.i@.
     636Hence, the field names must be unique, unlike \CC nested types, but the type names are at a nested scope level, unlike type nesting in C.
     637In addition, a pointer to a structure is automatically converted to a pointer to an anonymous field for assignments and function calls, providing containment inheritance with implicit subtyping, \ie @U@ $\subset$ @S@ and @W@ $\subset$ @S@, \eg:
    610638\begin{cfa}
    611639void f( union U * u );
    612640void g( struct W * );
    613 union U * up;
    614 struct W * wp;
    615 struct S * sp;
    616 up = sp; $\C{// assign pointer to U in S}$
    617 wp = sp; $\C{// assign pointer to W in S}$
     641union U * up;   struct W * wp;   struct S * sp;
     642up = &s; $\C{// assign pointer to U in S}$
     643wp = &s; $\C{// assign pointer to W in S}$
    618644f( &s ); $\C{// pass pointer to U in S}$
    619645g( &s ); $\C{// pass pointer to W in S}$
    620646\end{cfa}
    621 
    622 \CFA extends the Plan-9 substructure by allowing polymorphism for values and pointers.
    623 The extended substructure is denoted using @inline@, allowing backwards compatibility to existing Plan-9 features.
     647Note, there is no value assignment, such as, @w = s@, to copy the @W@ field from @S@.
     648
     649Unfortunately, the Plan-9 designers did not lookahead to other useful features, specifically nested types.
     650This nested type compiles in \CC and \CFA.
     651\begin{cfa}
     652struct R {
     653        @struct T;@             $\C[2in]{// forward declaration, conflicts with Plan-9 syntax}$
     654        struct S {              $\C{// nested types, mutually recursive reference}\CRT$
     655                S * sp;   T * tp;  ...
     656        };
     657        struct T {
     658                S * sp;   T * tp;  ...
     659        };
     660};
     661\end{cfa}
     662Note, the syntax for the forward declaration conflicts with the Plan-9 declaration syntax.
     663
     664\CFA extends the Plan-9 substructure by allowing polymorphism for values and pointers, where the extended substructure is denoted using @inline@.
    624665\begin{cfa}
    625666struct S {
    626         @inline@ W;  $\C{// extended Plan-9 substructure}$
     667        @inline@ struct W;  $\C{// extended Plan-9 substructure}$
    627668        unsigned int tag;
    628669        @inline@ U;  $\C{// extended Plan-9 substructure}$
    629670} s;
    630671\end{cfa}
    631 Note, like \CC, \CFA allows optional prefixing of type names with their kind, \eg @struct@, @union@, and @enum@, unless there is ambiguity with variable names in the same scope.
    632 The following shows both value and pointer polymorphism.
     672Note, the declaration of @U@ is not prefixed with @union@.
     673Like \CC, \CFA allows optional prefixing of type names with their kind, \eg @struct@, @union@, and @enum@, unless there is ambiguity with variable names in the same scope.
     674In addition, a semi-non-compatible change is made so that Plan-9 syntax means a forward declaration in a nested type.
     675Since the Plan-9 extension is not part of C and rarely used, this change has minimal impact.
     676Hence, all Plan-9 semantics are denoted by the @inline@ qualifier, which good ``eye-candy'' when reading a structure definition to spot Plan-9 definitions.
     677Finally, the following code shows the value and pointer polymorphism.
    633678\begin{cfa}
    634679void f( U, U * ); $\C{// value, pointer}$
    635680void g( W, W * ); $\C{// value, pointer}$
    636 U u, * up;
    637 S s, * sp;
    638 W w, * wp;
    639 u = s;  up = sp; $\C{// value, pointer}$
    640 w = s;  wp = sp; $\C{// value, pointer}$
     681U u, * up;   S s, * sp;   W w, * wp;
     682u = s;   up = sp; $\C{// value, pointer}$
     683w = s;   wp = sp; $\C{// value, pointer}$
    641684f( s, &s ); $\C{// value, pointer}$
    642685g( s, &s ); $\C{// value, pointer}$
     
    645688In general, non-standard C features (@gcc@) do not need any special treatment, as they are directly passed through to the C compiler.
    646689However, the Plan-9 semantics allow implicit conversions from the outer type to the inner type, which means the \CFA type resolver must take this information into account.
    647 Therefore, the \CFA translator must implement the Plan-9 features and insert necessary type conversions into the translated code output.
     690Therefore, the \CFA resolver must implement the Plan-9 features and insert necessary type conversions into the translated code output.
    648691In the current version of \CFA, this is the only kind of implicit type conversion other than the standard C conversions.
    649692
    650 Since variable overloading is possible in \CFA, \CFA's implementation of Plan-9 polymorphism allows duplicate field names.
    651 When an outer field and an embedded field have the same name and type, the inner field is shadowed and cannot be accessed directly by name.
    652 While such definitions are allowed, duplicate field names is not good practice in general and should be avoided if possible.
    653 Plan-9 fields can be nested, and a struct definition can contain multiple Plan-9 embedded fields.
    654 In particular, the \newterm{diamond pattern}~\cite[\S~6.1]{Stroustrup89}\cite[\S~4]{Cargill91}  can occur and result in a nested field to be embedded twice.
     693Plan-9 polymorphism can result in duplicate field names.
     694For example, the \newterm{diamond pattern}~\cite[\S~6.1]{Stroustrup89}\cite[\S~4]{Cargill91} can result in nested fields being embedded twice.
    655695\begin{cfa}
    656696struct A { int x; };
     
    658698struct C { inline A; };
    659699struct D {
    660         inline B;
    661         inline C;
    662 };
    663 D d;
    664 \end{cfa}
    665 In the above example, the expression @d.x@ becomes ambiguous, since it can refer to the indirectly embedded field either from @B@ or from @C@.
    666 It is still possible to disambiguate the expression by first casting the outer struct to one of the directly embedded type, such as @((B)d).x@.
     700        inline B;  // B.x
     701        inline C;  // C.x
     702} d;
     703\end{cfa}
     704Because the @inline@ structures are flattened, the expression @d.x@ is ambiguous, as it can refer to the embedded field either from @B@ or @C@.
     705@gcc@ generates a syntax error about the duplicate member @x@.
     706The equivalent \CC definition compiles:
     707\begin{c++}
     708struct A { int x; };
     709struct B : public A {};
     710struct C : public A {};
     711struct D : @public B, C@ {  // multiple inheritance
     712} d;
     713\end{c++}
     714and again the expression @d.x@ is ambiguous.
     715While \CC has no direct syntax to disambiguate @x@, \ie @d.B.x@ or @d.C.x@, it is possible with casts, @((B)d).x@ or @((C)d).x@.
     716Like \CC, \CFA compiles the Plan-9 version and provides direct syntax and casts to disambiguate @x@.
     717While ambiguous definitions are allowed, duplicate field names is poor practice and should be avoided if possible.
     718However, when a programmer does not control all code, this problem can occur and a naming workaround should exist.
  • doc/theses/mike_brooks_MMath/Makefile

    rb006c51e r10a9479d  
    1010TeXSRC = ${wildcard *.tex}
    1111PicSRC = ${notdir ${wildcard ${Pictures}/*.png}}
    12 DemoSRC = ${notdir ${wildcard ${Programs}/*-demo.cfa}}
     12DemoPgmSRC = ${notdir ${wildcard ${Programs}/*-demo.cfa}}
    1313PgmSRC = ${notdir ${wildcard ${Programs}/*}}
    1414RunPgmSRC = ${notdir ${wildcard ${Programs}/*.run.*}}
     
    2424BASE = ${basename ${DOCUMENT}}                  # remove suffix
    2525
    26 DemoTex = ${DemoSRC:%.cfa=${Build}/%.tex}
    2726RunPgmExe = ${addprefix ${Build}/,${basename ${basename ${RunPgmSRC}}}}
    2827RunPgmOut = ${RunPgmExe:%=%.out}
     28DemoPgmExe = ${addprefix ${Build}/,${basename ${basename ${DemoPgmSRC}}}}
     29DemoPgmOut = ${DemoPgmExe:%=%.out}
    2930
    3031# Commands
     
    3839# Rules and Recipes
    3940
    40 .PHONY : all fragments_ran clean                        # not file names
    41 .PRECIOUS : ${Build}/% ${Build}/%-demo      # don't delete intermediates
     41.PHONY : all clean                              # not file names
     42.SECONDARY:
     43#.PRECIOUS : ${Build}/%                         # don't delete intermediates
    4244.ONESHELL :
    4345
    44 all : fragments_ran ${DOCUMENT}
    45 
    46 fragments_ran : $(RunPgmOut)
     46all : ${DOCUMENT}
    4747
    4848clean :
     
    5151# File Dependencies
    5252
    53 %.pdf : ${TeXSRC} ${DemoTex} ${PicSRC} ${PgmSRC} ${BibSRC} ${BibRep}/pl.bib ${LaTMac}/common.tex Makefile | ${Build}
     53%.pdf : ${TeXSRC} $(RunPgmOut) ${DemoPgmOut} ${PicSRC} ${BibSRC} ${BibRep}/pl.bib ${LaTMac}/common.tex Makefile | ${Build}
    5454        ${LaTeX} ${BASE}
    5555        ${BibTeX} ${Build}/${BASE}
     
    6464        mkdir -p $@
    6565
    66 %-demo.tex: %-demo | ${Build}
    67         $< > $@
     66${Build}/%-demo: ${Programs}/%-demo.cfa | ${Build}
     67        ${CFA} $< -o $@
    6868
    69 ${Build}/%-demo: ${Programs}/%-demo.cfa | ${Build}
     69${Build}/%: ${Programs}/%-demo.cfa | ${Build}
    7070        ${CFA} $< -o $@
    7171
  • doc/theses/mike_brooks_MMath/array.tex

    rb006c51e r10a9479d  
    22\label{c:Array}
    33
     4Arrays in C are possibly the single most misunderstood and incorrectly used feature in the language, resulting in the largest proportion of runtime errors and security violations.
     5This chapter describes the new \CFA language and library features that introduce a length-checked array type, @array@, to the \CFA standard library~\cite{Cforall}.
     6
     7Offering 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++@).
     8However, 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.
     9Hence, 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 of the C array.
     10
    411
    512\section{Introduction}
    613\label{s:ArrayIntro}
    714
    8 Arrays in C are possibly the single most misunderstood and incorrectly used feature in the language, resulting in the largest proportion of runtime errors and security violations.
    9 This chapter describes the new \CFA language and library features that introduce a length-checked array type to the \CFA standard library~\cite{Cforall}.
    10 
    11 Specifically, a new \CFA array is declared by instantiating the generic @array@ type,
    12 much like instantiating any other standard-library generic type (such as @dlist@),
     15The new \CFA array is declared by instantiating the generic @array@ type,
     16much like instantiating any other standard-library generic type (such as \CC @vector@),
    1317though using a new style of generic parameter.
    1418\begin{cfa}
     
    1620\end{cfa}
    1721Here, the arguments to the @array@ type are @float@ (element type) and @99@ (length).
    18 When this type is used as a function parameter, the type-system requires that a call's argument matches, down to the length.
     22When this type is used as a function parameter, the type-system requires that a call's argument is a perfect match.
    1923\begin{cfa}
    2024void f( @array( float, 42 )@ & p ) {}   $\C{// p accepts 42 floats}$
    21 f( x );                                                                 $\C{// statically rejected: types are different, 99 != 42}$
     25f( x );                                                                 $\C{// statically rejected: type lengths are different, 99 != 42}$
    2226
    2327test2.cfa:3:1 error: Invalid application of existing declaration(s) in expression.
     
    2529\end{cfa}
    2630Here, the function @f@'s parameter @p@ is declared with length 42.
    27 The call @f( x )@, with the argument being the previously-declared object, is invalid, because the @array@ lengths @99@ and @42@ do not match.
    28 
    29 A function declaration can be polymorphic over these @array@ arguments by using the @forall@ declaration prefix.
    30 This function @g@'s takes arbitrary type parameter @T@ (familiar) and \emph{dimension parameter} @N@ (new).
    31 A dimension paramter represents a to-be-determined count of elements, managed by the type system.
     31However, the call @f( x )@ is invalid, because @x@'s length is @99@, which does not match @42@.
     32
     33A function declaration can be polymorphic over these @array@ arguments by using the \CFA @forall@ declaration prefix.
    3234\begin{cfa}
    3335forall( T, @[N]@ )
     
    4042Cforall Runtime error: subscript 1000 exceeds dimension range [0,99) $for$ array 0x555555558020.
    4143\end{cfa}
     44Function @g@ takes an arbitrary type parameter @T@ and a \emph{dimension parameter} @N@.
     45A dimension parameter represents a to-be-determined count of elements, managed by the type system.
    4246The 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@.
    43 Inferring values for @T@ and @N@ is implicit, without programmer involvement.
     47Inferring values for @T@ and @N@ is implicit.
    4448Furthermore, in this case, the runtime subscript @x[0]@ (parameter @i@ being @0@) in @g@ is valid because 0 is in the dimension range $[0,99)$ of argument @x@.
    45 The call @g( x, 1000 )@ is also accepted through compile time;
     49However, the call @g( x, 1000 )@ is also accepted through compile time;
    4650however, this case's subscript, @x[1000]@, generates an error, because @1000@ is outside the dimension range $[0,99)$ of argument @x@.
     51In general, the @forall( ..., [N] )@ participates in the user-relevant declaration of the name @N@, which becomes usable in parameter/return declarations and within a function.
     52The syntactic form is chosen to parallel other @forall@ forms:
     53\begin{cfa}
     54forall( @[N]@ ) ...     $\C[1.5in]{// dimension}$
     55forall( T ) ...         $\C{// value datatype (formerly, "otype")}$
     56forall( T & ) ...       $\C{// opaque datatype (formerly, "dtype")}\CRT$
     57\end{cfa}
     58% The notation @array(thing, N)@ is a single-dimensional case, giving a generic type instance.
    4759
    4860The generic @array@ type is comparable to the C array type, which \CFA inherits from C.
    4961Their runtime characteristics are often identical, and some features are available in both.
    50 For example, assume a caller instantiates @N@ with 42 (discussion about how to follow) in:
     62For example, assume a caller has an argument that instantiates @N@ with 42.
    5163\begin{cfa}
    5264forall( [N] )
    53 void declDemo() {
     65void declDemo( ... ) {
    5466        float x1[N];                                            $\C{// built-in type ("C array")}$
    5567        array(float, N) x2;                                     $\C{// type from library}$
     
    5971The two variables have identical size and layout; they both encapsulate 42-float stack allocations, with no additional ``bookkeeping'' allocations or headers.
    6072Providing this explicit generic approach requires 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.
    61 
    62 Admittedly, the @array@ library type (type for @x2@) is syntactically different from its C counterpart.
    63 A future goal (TODO xref) is to provide the new features upon a built-in type whose syntax approaches C's (declaration style of @x1@).
     73In all following discussion, ``C array'' means the types like that of @x@ and ``\CFA array'' means the standard-library @array@ type (instantiations), like the type of @x2@.
     74
     75Admittedly, the @array@ library type for @x2@ is syntactically different from its C counterpart.
     76A future goal (TODO xref) is to provide the new @array@ features with syntax approaching C's (declaration style of @x1@).
    6477Then, the library @array@ type could be removed, giving \CFA a largely uniform array type.
    65 At present, the C-syntax array gets partial support for the new features, so the generic @array@ is used exclusively when introducing features;
     78At present, the C-syntax @array@ is only partially supported, so the generic @array@ is used exclusively in the thesis;
    6679feature support and C compatibility are revisited in Section ? TODO.
    67 
    68 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++@).
    69 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.
    70 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 of the C array.
    71 
    72 In all discussion following, ``C array'' means the types like that of @x@ and ``\CFA array'' means the standard-library @array@ type (instantiations), like the type of @x2@.
    7380
    7481My contributions in this chapter are:
     
    8390
    8491
    85 \section{Definitions and design considerations}
    86 
    87 
    88 \subsection{Dependent typing}
    89 
    90 
    91 
    92 General dependent typing allows the type system to encode arbitrary predicates (e.g. behavioural specifications for functions),
     92\section{Dependent typing}
     93
     94General dependent typing allows the type system to encode arbitrary predicates (\eg behavioural specifications for functions),
    9395which is an anti-goal for my work.
    9496Firstly, this application is strongly associated with pure functional languages,
     
    101103
    102104
    103 
    104105\section{Features added}
    105106
     
    109110By declaring type variables at the front of object declarations, an array dimension is lexically referenceable where it is needed.
    110111For example, a declaration can share one length, @N@, among a pair of parameters and the return,
    111 meaning that it requires both input arrays to be of the same length, and guarantees that the result with be of that length as well.
     112meaning that it requires both input arrays to be of the same length, and guarantees that the result is of that length as well.
    112113\lstinput{10-17}{hello-array.cfa}
    113 This function @f@ does a pointwise comparison of its two input arrays, checking if each pair of numbers is within half a percent of each other, returning the answers in a newly allocated @bool@ array.
    114 The dynamic allocation of the @ret@ array by preexisting @alloc@ uses the parameterized dimension information implicitly within its @sizeof@ determination, and casts the return type.
    115 Note that alloc only sees one whole type for its @T@ (which is @f@'s @array(bool, N)@); this type's size is a computation based on @N@.
    116 \begin{cfa}
    117 // simplification
    118 static inline forall( T & | sized(T) )
     114Function @f@ does a pointwise comparison of its two input arrays, checking if each pair of numbers is within half a percent of each other, returning the answers in a newly allocated @bool@ array.
     115The dynamic allocation of the @ret@ array, by the library @alloc@ function,
     116\begin{cfa}
     117forall( T & | sized(T) )
    119118T * alloc() {
    120         return (T *)malloc( sizeof(T) );
    121 }
    122 \end{cfa}
    123 This example illustrates how the new @array@ type plugs into existing \CFA behaviour by implementing necessary @sized@ assertions needed by other types.
    124 (@sized@ implies a concrete \vs abstract type with a runtime-available size, exposed as @sizeof@.)
     119        return @(T *)@malloc( @sizeof(T)@ );
     120}
     121\end{cfa}
     122uses the parameterized dimension information implicitly within its @sizeof@ determination, and casts the return type.
     123Note that @alloc@ only sees one whole type for its @T@ (which is @f@'s @array(bool, N)@); this type's size is a computation based on @N@.
     124This example illustrates how the new @array@ type plugs into existing \CFA behaviour by implementing necessary \emph{sized} assertions needed by other types.
     125(\emph{sized} implies a concrete \vs abstract type with a runtime-available size, exposed as @sizeof@.)
    125126As a result, there is significant programming safety by making the size accessible and implicit, compared with C's @calloc@ and non-array supporting @memalign@, which take an explicit length parameter not managed by the type system.
    126127
     
    142143The result is a significant improvement in safety and usability.
    143144
    144 In general, the @forall( ..., [N] )@ participates in the user-relevant declaration of the name @N@, which becomes usable in parameter/return declarations and within a function.
    145 The syntactic form is chosen to parallel other @forall@ forms:
    146 \begin{cfa}
    147 forall( @[N]@ ) ...     $\C[1.5in]{// dimension}$
    148 forall( T & ) ...       $\C{// opaque datatype (formerly, "dtype")}$
    149 forall( T ) ...         $\C{// value datatype (formerly, "otype")}\CRT$
    150 \end{cfa}
    151 % The notation @array(thing, N)@ is a single-dimensional case, giving a generic type instance.
    152145In summary:
    153146\begin{itemize}
     
    168161% agreed, though already said
    169162\item
    170 \CC does not allow a template function to be nested, while \CFA lests its polymorphic functions to be nested.
     163\CC does not allow a template function to be nested, while \CFA lets its polymorphic functions to be nested.
    171164% why is this important?
    172165\item
     
    227220\end{cfa}
    228221\end{tabular}
    229 \caption{\lstinline{N}-style paramters, for \CC template \vs \CFA generic type }
     222\caption{\lstinline{N}-style parameters, for \CC template \vs \CFA generic type }
    230223\label{f:TemplateVsGenericType}
    231224\end{figure}
    232225
    233226Just as the first example in \VRef[Section]{s:ArrayIntro} shows a compile-time rejection of a length mismatch,
    234 so are length mismatches stopped when they invlove dimension parameters.
     227so are length mismatches stopped when they involve dimension parameters.
    235228While \VRef[Figure]{f:fHarness} shows successfully calling a function @f@ expecting two arrays of the same length,
    236229\begin{cfa}
    237230array( bool, N ) & f( array( float, N ) &, array( float, N ) & );
    238231\end{cfa}
    239 a static rejection occurs when attempting to call @f@ with arrays of potentially differing lengths.
     232a static rejection occurs when attempting to call @f@ with arrays of differing lengths.
    240233\lstinput[tabsize=1]{70-74}{hello-array.cfa}
    241234When the argument lengths themselves are statically unknown,
     
    252245Orthogonally, the \CFA array type works within generic \emph{types}, \ie @forall@-on-@struct@.
    253246The same argument safety and the associated implicit communication of array length occurs.
    254 Preexisting \CFA allowed aggregate types to be generalized with type parameters, enabling parameterizing for element types.
     247Preexisting \CFA allowed aggregate types to be generalized with type parameters, enabling parameterizing of element types.
    255248Now, \CFA also allows parameterizing them by length.
    256249Doing so gives a refinement of C's ``flexible array member'' pattern[TODO: cite ARM 6.7.2.1 pp18]\cite{arr:gnu-flex-mbr}.
     
    259252This flexibility, in turn, allows for multiple array members.
    260253\lstinput{10-15}{hello-accordion.cfa}
    261 This structure's layout has the starting offset of @studentIds@ varying according to the generic parameter @C@, and the offset of @preferences@ varying according to both generic parameters.
    262 
    263 The school example has the data structure capturing many students' course-preference forms.
    264 It has course- and student-level metadata (their respective display names) and a position-based preferecens' matrix.
    265 The input files in \VRef[Figure]{f:checkHarness} give example data.
    266 
    267 When a function operates on a @School@ structure, the type system handles its memory layout transparently.
    268 \lstinput{30-37}{hello-accordion.cfa}
    269 In the running example, this @getPref@ function answers,
    270 for the student at position @sIx@, what is the position of its @pref@\textsuperscript{th}-favoured class?
    271 
    272 \VRef[Figure]{f:checkHarness} shows the @School@ harness and results with different array sizes.
    273 This example program prints the courses in each student's preferred order, all using the looked-up display names.
    274 Note the declaration of the @school@ variable.
     254The structure has course- and student-level metatdata (their respective field names) and a position-based preferences' matrix.
     255Its layout has the starting offset of @studentIds@ varying according to the generic parameter @C@, and the offset of @preferences@ varying according to both generic parameters.
     256
     257\VRef[Figure]{f:checkHarness} shows a program main using @School@ and results with different array sizes.
     258The @school@ variable holds many students' course-preference forms.
    275259It is on the stack and its initialization does not use any casting or size arithmetic.
    276260Both of these points are impossible with a C flexible array member.
     
    280264\end{cfa}
    281265This ability to avoid casting and size arithmetic improves safety and usability over C flexible array members.
    282 
     266Finally, inputs and outputs are given at the bottom for different sized schools.
     267The example program prints the courses in each student's preferred order, all using the looked-up display names.
    283268
    284269\begin{figure}
    285 % super hack to get this to line up
    286 \begin{tabular}{@{}ll@{\hspace{25pt}}l@{}}
    287 \begin{tabular}{@{}p{3.25in}@{}}
     270\begin{cquote}
    288271\lstinput{50-55}{hello-accordion.cfa}
    289 \vspace*{-3pt}
    290272\lstinput{90-98}{hello-accordion.cfa}
    291 \end{tabular}
    292 &
    293 \raisebox{0.32\totalheight}{%
    294 }%
    295 &
    296 \end{tabular}
    297 
    298 TODO: Get Peter's layout help
    299 
    300 \$ cat school1
    301 
     273\ \\
     274@$ cat school1@
    302275\lstinput{}{school1}
    303276
    304 \$ ./a.out < school1
    305 
     277@$ ./a.out < school1@
    306278\lstinput{}{school1.out}
    307279
    308 \$ cat school2
    309 
     280@$ cat school2@
    310281\lstinput{}{school2}
    311282
    312 \$ ./a.out < school2
    313 
     283@$ ./a.out < school2@
    314284\lstinput{}{school2.out}
     285\end{cquote}
    315286
    316287\caption{\lstinline{School} harness, input and output}
    317288\label{f:checkHarness}
    318289\end{figure}
     290
     291When a function operates on a @School@ structure, the type system handles its memory layout transparently.
     292\lstinput{30-37}{hello-accordion.cfa}
     293In the example, this @getPref@ function answers, for the student at position @is@, what is the position of its @pref@\textsuperscript{th}-favoured class?
    319294
    320295
     
    333308But simplifications close enough for the present discussion are:
    334309\begin{cfa}
    335         forall( [N] )
    336         struct array_1d_float {
    337                 float items[N];
    338         };
    339         forall( T, [N] )
    340         struct array_1d {
    341                 T items[N];
    342         };
    343 \end{cfa}
    344 This structure pattern, plus a subscript operator, is all that @array@ provides.
     310forall( [N] )
     311struct array_1d_float {
     312        float items[N];
     313};
     314forall( T, [N] )
     315struct array_1d_T {
     316        T items[N];
     317};
     318\end{cfa}
     319These two structure patterns, plus a subscript operator, is all that @array@ provides.
    345320
    346321My main work is letting a programmer define
    347 such a structre (one whose type is parameterized by @[N]@)
     322such a structure (one whose type is parameterized by @[N]@)
    348323and functions that operate on it (these being similarly parameterized).
    349324
     
    351326\begin{itemize}
    352327\item
    353         The resolver, providing values for a used declaration's type-system variables,
     328        Resolver provided values for a used declaration's type-system variables,
    354329        gathered from type information in scope at the usage site.
    355 
    356330\item
    357331        The box pass, encoding information about type parameters
    358332        into ``extra'' regular parameters/arguments on declarations and calls.
    359333        Notably, it conveys the size of a type @foo@ as a @__sizeof_foo@ parameter,
    360         and rewrites the @sizeof(foo)@ expression as @__sizeof_foo@, i.e. a use of the parameter.
     334        and rewrites the @sizeof(foo)@ expression as @__sizeof_foo@, \ie a use of the parameter.
    361335\end{itemize}
    362336
     
    364338This work is detailed in \VRef[Section]{s:ArrayTypingC}.
    365339However, the resolution--boxing scheme, in its preexisting state, was already equipped to work on (desugared) dimension parameters.
    366 The discussion following explains the desugaring and how correct lowered code results.
    367 
    368 An even simpler structure, and a toy function on it, demonstrate what's needed for the encoding.
    369 \begin{cfa}
    370         forall( [@N@] ) { // [1]
    371                 struct thing {};
    372                 void f( thing(@N@) ) { sout | @N@; } // [2], [3]
    373         }
    374         int main() {
    375                 thing( @10@ ) x;  f(x);  // prints 10, [4]
    376                 thing( 100 ) y;  f(y);  // prints 100
    377                 return 0;
    378         }
     340The following discussion explains the desugaring and how correctly lowered code results.
     341
     342A simpler structure, and a toy function on it, demonstrate what is needed for the encoding.
     343\begin{cfa}
     344forall( [@N@] ) { $\C{// [1]}$
     345        struct thing {};
     346        void f( thing(@N@) ) { sout | @N@; } $\C{// [2], [3]}$
     347}
     348int main() {
     349        thing( @10@ ) x;  f( x );  $\C{// prints 10, [4]}$
     350        thing( 100 ) y;  f( y );  $\C{// prints 100}$
     351        return 0;
     352}
    379353\end{cfa}
    380354This example has:
     
    389363        A value like 10 being used as an argument to the parameter @N@.
    390364\end{enumerate}
    391 The chosen solution being to encode the value @N@ \emph{as a type}, items 1 and 2 are immediately available for free.
     365The chosen solution is to encode the value @N@ \emph{as a type}, so items 1 and 2 are immediately available for free.
    392366Item 3 needs a way to recover the encoded value from a (valid) type (and to reject invalid types occurring here).
    393367Item 4 needs a way to produce a type that encodes the given value.
     
    400374\item
    401375        Given a dimension expression $e$, produce type @char[@$e$@]@ to represent it.
    402         If $e$ evaluates to $n$ then the endoded type has size $n$.
     376        If $e$ evaluates to $n$ then the encoded type has size $n$.
    403377\item
    404378        Given a type $T$ (produced by these rules), recover the value that it represents with the expression @sizeof(@$T$@)@.
     
    410384The running example is lowered to:
    411385\begin{cfa}
    412         forall( @N*@ ) { // [1]
    413                 struct thing {};
    414                 void f( thing(@N@) ) { sout | @sizeof(N)@; } // [2], [3]
    415         }
    416         int main() {
    417                 thing( char[@10@] ) x;  f(x);  // prints 10, [4]
    418                 thing( char[100] ) y;  f(y);  // prints 100
    419                 return 0;
    420         }
     386forall( @N *@ ) { $\C{// [1]}$
     387        struct thing {};
     388        void f( thing(@N@) ) { sout | @sizeof(N)@; } $\C{// [2], [3]}$
     389}
     390int main() {
     391        thing( char[@10@] ) x;  f( x );  $\C{// prints 10, [4]}$
     392        thing( char[100] ) y;  f( y );  $\C{// prints 100}$
     393        return 0;
     394}
    421395\end{cfa}
    422396Observe:
     
    430404        The @sout...@ expression (being an application of the @?|?@ operator) has a second argument that is an ordinary expression.
    431405\item
    432         The type of variable @x@ is another @thing(-)@ type;  the argument to the generic @thing@ is a type (array type).
     406        The type of variable @x@ is another @thing(-)@ type; the argument to the generic @thing@ is a type (array type of bytes, @char@).
    433407\end{enumerate}
    434408
    435409From this point, preexisting \CFA compilation takes over lowering it the rest of the way to C.
    436 Inspecting the result shows what the above translation achieves.
    437 A form that shows only the relevant changes of the box pass (as informed by the resolver), leaving the rest unadulterated, is:
    438 \begin{cfa}
    439         // [1]
    440         void f( size_t __sizeof_N, @void *@ ) { sout | @__sizeof_N@; } // [2], [3]
    441         int main() {
    442                 struct __conc_thing_10 {} x;  f(@10@, &x);  // prints 10, [4]
    443                 struct __conc_thing_100 {} y;  f(@100@, &y);  // prints 100
    444                 return 0;
    445         }
     410Here the result shows only the relevant changes of the box pass (as informed by the resolver), leaving the rest unadulterated:
     411\begin{cfa}
     412// [1]
     413void f( size_t __sizeof_N, @void *@ ) { sout | @__sizeof_N@; } $\C{// [2], [3]}$
     414int main() {
     415        struct __conc_thing_10 {} x;  f( @10@, &x );  $\C{// prints 10, [4]}$
     416        struct __conc_thing_100 {} y;  f( @100@, &y );  $\C{// prints 100}$
     417        return 0;
     418}
    446419\end{cfa}
    447420Observe:
     
    452425        The type @thing(N)@ is (replaced by @void *@, but thereby effectively) gone.
    453426\item
    454         The @sout...@ expression (being an application of the @?|?@ operator) has a second argument that is a regular variable (parameter) usage.
     427        The @sout...@ expression (being an application of the @?|?@ operator) has a regular variable (parameter) usage for its second argument.
    455428\item
    456429        Information about the particular @thing@ instantiation (value 10) has moved, from the type, to a regular function-call argument.
    457430\end{enumerate}
    458 At the end of the desugaring and downstream processing, the original C idiom of ``pass both a pointer and a length parameter'' has been reconstructed.
    459 In the programmer-written form, only the thing is passed.
     431At the end of the desugaring and downstream processing, the original C idiom of ``pass both a length parameter and a pointer'' has been reconstructed.
     432In the programmer-written form, only the @thing@ is passed.
    460433The compiler's action produces the more complex form, which if handwritten, would be error-prone.
    461434
    462 Back at the very front end, the parsing changes, AST schema extensions, and validation rules for enabling the sugared user input are:
     435Back at the compiler front end, the parsing changes AST schema extensions and validation rules for enabling the sugared user input.
    463436\begin{itemize}
    464437\item
     
    467440        Have the new brand of type-variable, \emph{Dimension}, in the AST form of a type-variable, to represent one parsed from @[-]@.
    468441\item
    469         Allow a type variable to occur in expression position.  Validate (after parsing) that only dimension-branded type variables are used here.
     442        Allow a type variable to occur in an expression.  Validate (after parsing) that only dimension-branded type variables are used here.
    470443\item
    471444        Allow an expression to occur in type-argument position.  Brand the resulting type argument as a dimension.
     
    473446        Validate (after parsing), on a generic-type usage, \eg the type part of the declaration
    474447        \begin{cfa}
    475                 @array_1d( foo, bar ) x;@
     448        array_1d( foo, bar ) x;
    476449        \end{cfa}
     450        \vspace*{-10pt}
    477451        that the brands on the generic arguments match the brands of the declared type variables.
    478452        Here, that @foo@ is a type and @bar@ is a dimension.
     
    488462from one party who knows it, to another who is willing to work with any given length.
    489463For scenarios where the concern is a mishandled length,
    490 the interaction is between two parties who both claim to know (something about) it.
    491 Such a scenario occurs in this pure C fragment, wich today's C compilers accept:
    492 \begin{cfa}
    493         int n = @42@;
    494         float x[n];
    495         float (*xp)[@999@] = &x;
    496         (*xp)[@500@];  // in "bound"?
    497 \end{cfa}
    498 
     464the interaction is between two parties who both claim to know something about it.
     465Such a scenario occurs in this pure C fragment, which today's C compilers accept:
     466\begin{cfa}
     467int n = @42@;
     468float x[n];
     469float (*xp)[@999@] = &x;
     470(*xp)[@500@]; $\C{// in "bound"?}$
     471\end{cfa}
    499472Here, the array @x@ has length 42, while a pointer to it (@xp@) claims length 999.
    500473So, while the subscript of @xp@ at position 500 is out of bound of its referent @x@,
     
    505478The \CFA new-array rejects the analogous case:
    506479\begin{cfa}
    507         int n = @42@;
    508         array(float, n) x;
    509         array(float, 999) * xp = x; // static rejection here
    510         (*xp)[@500@]; // runtime check vs len 999
    511 \end{cfa}
    512 
    513 % TODO: kill the vertical whitespace around these lists
    514 % nothing from https://stackoverflow.com/questions/1061112/eliminate-space-before-beginitemize is working
    515 
    516 The way the \CFA array is implemented,
    517 the type analysis of this \CFA case reduces to a case similar to the earlier C version.
     480int n = @42@;
     481array(float, n) x;
     482array(float, 999) * xp = x; $\C{// static rejection here}$
     483(*xp)[@500@]; $\C{// runtime check vs len 999}$
     484\end{cfa}
     485The way the \CFA array is implemented, the type analysis of this case reduces to a case similar to the earlier C version.
    518486The \CFA compiler's compatibility analysis proceeds as:
    519 \begin{itemize}[noitemsep,partopsep=-\parskip,parsep=0pt,leftmargin=4em]
     487\begin{itemize}[parsep=0pt]
    520488\item
    521489        Is @array(float, 999)@ type-compatible with @array(float, n)@?
    522490\item
    523         Is @arrayX(float, char[999])@ type-compatible with @arrayX(float, char[n])@?
    524         \footnote{Here, \lstinline{arrayX} represents the type that results
     491        Is @arrayX(float, char[999])@ type-compatible with @arrayX(float, char[n])@?\footnote{
     492                Here, \lstinline{arrayX} represents the type that results
    525493                from desugaring the \lstinline{array} type
    526494                into a type whose generic parameters are all types.
     
    531499        Is @char[999]@ type-compatible with @char[n]@?
    532500\end{itemize}
    533 
    534 I chose to achieve the necessary rejection of the \CFA case
    535 by adding a rejection of the corresponding C case.
    536 
    537 This decision is not backward compatible.
     501To achieve the necessary \CFA rejections meant rejecting the corresponding C case, which is not backward compatible.
    538502There are two complementary mitigations for this incompatibility.
    539503
     
    542506This situation might arise if @n@ were known to be 999,
    543507rather than 42, as in the introductory examples.
    544 The programmer can add a cast, as in:
    545 \begin{cfa}
    546         xp = ( float (*)[999] ) & x;
    547 \end{cfa}
    548 This addition causes \CFA to accept, beacause now, the programmer has accepted blame.
     508The programmer can add a cast in the \CFA code.
     509\begin{cfa}
     510xp = @(float (*)[999])@ &x;
     511\end{cfa}
     512This addition causes \CFA to accept, because now, the programmer has accepted blame.
    549513This addition is benign in plain C, because the cast is valid, just unnecessary there.
    550514Moreover, the addition can even be seen as appropriate ``eye candy,''
     
    556520Second, the incompatibility only affects types like pointer-to-array,
    557521which are are infrequently used in C.
    558 The more common C idiom for aliasing an array is to use the pointer-to-first-element type,
    559 which does not participate in the \CFA array's length checking.
    560 \footnote{Notably, the desugaring of the \lstinline{array@} type,
    561         avoids letting any \lstinline{-[-]} type decay,
    562         in order to preserve the length information that powers runtime bound checking.}
    563 Therefore, the frequency of needing to upgrade wild C code (as discussed in the first mitigation)
     522The more common C idiom for aliasing an array is to use a pointer-to-first-element type,
     523which does not participate in the \CFA array's length checking.\footnote{
     524        Notably, the desugaring of the \lstinline{array} type avoids letting any \lstinline{-[-]} type decay,
     525        in order to preserve the length information that powers runtime bound-checking.}
     526Therefore, the frequency of needing to upgrade legacy C code (as discussed in the first mitigation)
    564527is anticipated to be low.
    565528
    566529Because the incompatibility represents a low cost to a \CFA onboarding effort
    567530(with a plausible side benefit of linting the original code for a missing annotation),
    568 I elected not to add special measures to retain the compatibility.
     531no special measures were added to retain the compatibility.
    569532It would be possible to flag occurrences of @-[-]@ types that come from @array@ desugaring,
    570533treating those with stricter \CFA rules, while treating others with classic C rules.
     
    573536
    574537Having allowed that both the initial C example's check
    575 \begin{itemize}[noitemsep,partopsep=-\parskip,parsep=0pt,leftmargin=4em]
     538\begin{itemize}
    576539        \item
    577540                Is @float[999]@ type-compatible with @float[n]@?
    578541\end{itemize}
    579 and the second \CFA exmple's induced check
    580 \begin{itemize}[noitemsep,partopsep=-\parskip,parsep=0pt,leftmargin=4em]
     542and the second \CFA example's induced check
     543\begin{itemize}
    581544        \item
    582545                Is @char[999]@ type-compatible with @char[n]@?
     
    587550To implement the new \CFA rules, I took the syntactic recursion a step further, obtaining,
    588551in both cases:
    589 \begin{itemize}[noitemsep,partopsep=-\parskip,parsep=0pt,leftmargin=4em]
     552\begin{itemize}
    590553        \item
    591                 Is @999@ TBD-compatible with @n@?
     554                Is @999@ compatible with @n@?
    592555\end{itemize}
    593 This compatibility question applies to a pair of expressions, where the earlier ones were to types.
     556This compatibility question applies to a pair of expressions, where the earlier implementation were to types.
    594557Such an expression-compatibility question is a new addition to the \CFA compiler.
    595 These questions only arise in the context of dimension expressions on (C) array types.
     558Note, these questions only arise in the context of dimension expressions on (C) array types.
    596559
    597560TODO: ensure these compiler implementation matters are treated under \CFA compiler background:
     
    600563GenPoly.
    601564
    602 The relevant technical component of the \CFA compiler is,
    603 within the type resolver, the type unification procedure.
     565The relevant technical component of the \CFA compiler is the type unification procedure within the type resolver.
    604566I added rules for continuing this unification into expressions that occur within types.
    605567It is still fundamentally doing \emph{type} unification
     
    607569and not participating in binding any variables that stand in for expression fragments
    608570(for there is no such sort of variable in \CFA's analysis.)
    609 
    610571An unfortunate fact about the \CFA compiler's preexisting implementation is that
    611572type unification suffers from two forms of duplication.
     
    613574The first duplication has (many of) the unification rules stated twice.
    614575As a result, my additions for dimension expressions are stated twice.
    615 The extra statement of the rules occurs in the GenPoly module,
     576The extra statement of the rules occurs in the @GenPoly@ module,
    616577where concrete types like @array(int, 5)@\footnote{
    617578        Again, the presentation is simplified
    618         by leaving the \lstinline{array} macro unexpanded}
     579        by leaving the \lstinline{array} macro unexpanded.}
    619580are lowered into corresponding C types @struct __conc_array_1234@ (the suffix being a generated index).
    620 In this case, the struct's definition gives fields that hardcode the argument values of @float@ and @5@.
    621 The next time an @array(-,-)@ concrete instance is encountered,
    622 is the previous @struct __conc_array_1234@ suitable for it?
    623 Yes, for another occurrance of @array(int, 5)@;
     581In this case, the struct's definition contains fields that hardcode the argument values of @float@ and @5@.
     582The next time an @array(-,-)@ concrete instance is encountered, it checks if the previous @struct __conc_array_1234@ is suitable for it.
     583Yes, for another occurrence of @array(int, 5)@;
    624584no, for either @array(rational(int), 5)@ or @array(int, 42)@.
    625585By the last example, this phase must ``reject''
     
    630590In the program
    631591\begin{cfa}
    632         void f( double );
    633         forall( T & ) void f( T & );
    634         void g( int n ) {
    635                 array( float, n + 1 ) x;
    636                 f(x);
    637         }
    638 \end{cfa}
    639 when resolving the function call, the first unification stage
    640 compares the types @T@, of the parameter, with @array( float, n + 1 )@, of the argument.
     592void @f@( double );
     593forall( T & ) void @f@( T & );
     594void g( int n ) {
     595        array( float, n + 1 ) x;
     596        f(x);   // overloaded
     597}
     598\end{cfa}
     599when resolving the function call, @g@, the first unification stage
     600compares the type @T@ of the parameter with @array( float, n + 1 )@, of the argument.
    641601TODO: finish.
    642602
     
    647607        TODO: Deal with directionality, that I'm doing exact-match, no ``at least as long as,'' no subtyping.
    648608        Should it be an earlier scoping principle?  Feels like it should matter in more places than here.}
    649 So, a ``yes'' answer must represent a guarantee that both expressions will evaluate the
    650 same result, while a ``no'' can tolerate ``they might, but we're not sure,'
     609So, a ``yes'' answer must represent a guarantee that both expressions evaluate the
     610same result, while a ``no'' can tolerate ``they might, but we're not sure'',
    651611provided that practical recourses are available
    652 to let programmers express their better knowledge.
    653 The specific rule-set that I offer with the current release is, in fact, extremely conservative.
     612to let programmers express better knowledge.
     613The new rule-set in the current release is, in fact, extremely conservative.
    654614I chose to keep things simple,
    655 and allow real future needs do drive adding additional complexity,
    656 within the framework that I laid out.
     615and allow future needs to drive adding additional complexity, within the new framework.
    657616
    658617For starters, the original motivating example's rejection
     
    662621Rather, the analysis assumes a variable's value can be anything,
    663622and so there can be no guarantee that its value is 999.
    664 So, a variable use and a literal can never match.
     623So, a variable and a literal can never match.
    665624
    666625Two occurrences of the same literal value are obviously a fine match.
    667 For two occurrences of the same varialbe, more information is needed.
     626For two occurrences of the same variable, more information is needed.
    668627For example, this one is fine
    669628\begin{cfa}
    670         void f( const int n ) {
    671                 float x[n];
    672                 float (*xp)[n] = x; // accept
    673         }
     629void f( const int n ) {
     630        float x[n];
     631        float (*xp)[n] = x;  // accept
     632}
    674633\end{cfa}
    675634while this one is not:
    676635\begin{cfa}
    677         void f() {
    678                 int n = 42;
    679                 float x[n];
    680                 n = 999;
    681                 float (*xp)[n] = x; // reject
    682         }
     636void f() {
     637        int n = 42;
     638        float x[n];
     639        n = 999;
     640        float (*xp)[n] = x;  // reject
     641}
    683642\end{cfa}
    684643Furthermore, the fact that the first example sees @n@ as @const@
    685 is not actually a sufficent basis.
     644is not actually sufficient.
    686645In this example, @f@'s length expression's declaration is as @const@ as it can be,
    687646yet its value still changes between the two invocations:
    688 \begin{cfa}
    689         // compile unit 1
    690         void g();
    691         void f( const int & const nr ) {
    692                 float x[nr];
    693                 g();
    694                 float (*xp)[nr] = x; // reject
    695         }
    696         // compile unit 2
    697         static int n = 42;
    698         void g() {
    699                 n = 99;
    700         }
    701         void f( const int & );
    702         int main () {
    703                 f(n);
    704                 return 0;
    705         }
    706 \end{cfa}
    707 The issue in this last case is,
    708 just because you aren't able to change something doesn't mean someone else can't.
    709 
    710 My rule set also respects a feature of the C tradition.
    711 In spite of the several limitations of the C rules
     647\begin{cquote}
     648\setlength{\tabcolsep}{15pt}
     649\begin{tabular}{@{}ll@{}}
     650\begin{cfa}
     651// compile unit 1
     652void g();
     653void f( const int & const nr ) {
     654        float x[nr];
     655        g();    // change n
     656        @float (*xp)[nr] = x;@   // reject
     657}
     658\end{cfa}
     659&
     660\begin{cfa}
     661// compile unit 2
     662static int n = 42;
     663void g() {
     664        n = 99;
     665}
     666
     667f( n );
     668\end{cfa}
     669\end{tabular}
     670\end{cquote}
     671The issue here is that knowledge needed to make a correct decision is hidden by separate compilation.
     672Even within a translation unit, static analysis might not be able to provide all the information.
     673
     674My rule set also respects a traditional C feature: In spite of the several limitations of the C rules
    712675accepting cases that produce different values, there are a few mismatches that C stops.
    713 C is quite precise when working with two static values:
    714 \begin{cfa}
    715         enum { fortytwo = 42 };
    716         float x[fortytwo];
    717         float (*xp1)[42] = &x; // accept
    718         float (*xp2)[999] = &x; // reject
     676C is quite precise when working with two static values.
     677\begin{cfa}
     678enum { fortytwo = 42 };
     679float x[fortytwo];
     680float (*xp1)[42] = &x;    // accept
     681float (*xp2)[999] = &x;  // reject
    719682\end{cfa}
    720683My \CFA rules agree with C's on these cases.
    721684
    722 My rules classify expressions into three groups:
     685In summary, the new rules classify expressions into three groups:
    723686\begin{description}
    724687\item[Statically Evaluable]
    725688        Expressions for which a specific value can be calculated (conservatively)
    726689        at compile-time.
    727         A preexisting \CFA compiler module defines which expressions qualify,
     690        A preexisting \CFA compiler module defines which literals, enumerators, and expressions qualify,
    728691        and evaluates them.
    729         Includes literals and enumeration values.
    730692\item[Dynamic but Stable]
    731         The value of a variable declared as @const@.
    732         Includes a @const@ parameter.
     693        The value of a variable declared as @const@, including a @const@ parameter.
    733694\item[Potentially Unstable]
    734695        The catch-all category.  Notable examples include:
    735         any function-call result (@float x[foo()];@),
    736         the particular function-call result that is a pointer dereference (@void f(const int * n) { float x[*n]; }@), and
     696        any function-call result, @float x[foo()];@,
     697        the particular function-call result that is a pointer dereference, @void f(const int * n)@ @{ float x[*n]; }@, and
    737698        any use of a reference-typed variable.
    738699\end{description}
    739 
    740 My \CFA rules are:
     700Within these groups, my \CFA rules are:
    741701\begin{itemize}
    742702\item
     
    744704        Notably, this rule allows a literal to match with an enumeration value, based on the value.
    745705\item
    746         Accept a Dynamic but Stable pair, if both expressions are written out the same, e.g. refers to same variable declaration.
     706        Accept a Dynamic but Stable pair, if both expressions are written out the same, \eg refers to the same variable declaration.
    747707\item
    748708        Otherwise, reject.
    749         Notably, reject all pairs from the Potentially Unstable group.
    750         Notably, reject all pairs that cross groups.
     709        Notably, reject all pairs from the Potentially Unstable group and all pairs that cross groups.
    751710\end{itemize}
    752 
    753711The traditional C rules are:
    754712\begin{itemize}
     
    759717\end{itemize}
    760718
    761 
    762 \newcommand{\falsealarm}{{\color{orange}\small{*}}}
    763 \newcommand{\allowmisuse}{{\color{red}\textbf{!}}}
    764 \newcommand{\cmark}{\ding{51}} % from pifont
    765 \newcommand{\xmark}{\ding{55}}
    766719\begin{figure}
     720        \newcommand{\falsealarm}{{\color{blue}\small{*}}}
     721        \newcommand{\allowmisuse}{{\color{red}\textbf{!}}}
     722        \newcommand{\cmark}{\ding{51}} % from pifont
     723        \newcommand{\xmark}{\ding{55}}
     724
    767725        \begin{tabular}{@{}l@{\hspace{16pt}}c@{\hspace{8pt}}c@{\hspace{16pt}}c@{\hspace{8pt}}c@{\hspace{16pt}}c}
    768726         & \multicolumn{2}{c}{\underline{Values Equal}}
     
    778736        \end{tabular}
    779737
    780         \vspace{12pt}
    781         \noindent\textbf{Legend:}
    782         \begin{itemize}
     738        \medskip
     739        \noindent\textbf{Legend}
     740        \begin{itemize}[leftmargin=*]
    783741        \item
    784742                Each row gives the treatment of a test harness of the form
    785743                \begin{cfa}
    786                         float x[ expr1 ];
    787                         float (*xp)[ expr2 ] = &x;
     744                float x[ expr1 ];
     745                float (*xp)[ expr2 ] = &x;
    788746                \end{cfa}
    789                 where \lstinline{expr1} and \lstinline{expr2} are metavariables varying according to the row's Case.
     747                \vspace*{-10pt}
     748                where \lstinline{expr1} and \lstinline{expr2} are meta-variables varying according to the row's Case.
    790749                Each row's claim applies to other harnesses too, including,
    791750                \begin{itemize}
    792751                \item
    793                         calling a function with a paramter like \lstinline{x} and an argument of the \lstinline{xp} type,
     752                        calling a function with a parameter like \lstinline{x} and an argument of the \lstinline{xp} type,
    794753                \item
    795754                        assignment in place of initialization,
     
    801760        \item
    802761                Each case's claim is symmetric (swapping \lstinline{expr1} with \lstinline{expr2} has no effect),
    803                 even though most test harnesses are asymetric.
     762                even though most test harnesses are asymmetric.
    804763        \item
    805764                The table treats symbolic identity (Same/Different on rows)
    806                 apart from value eqality (Equal/Unequal on columns).
     765                apart from value equality (Equal/Unequal on columns).
    807766                \begin{itemize}
    808767                \item
     
    819778                while every Accept under Values Unequal is an allowed misuse (\allowmisuse).
    820779        \end{itemize}
    821         \caption{Case comparison for array type compatibility, given pairs of dimension expressions.
    822                 TODO: get Peter's LaTeX help on overall appearance, probably including column spacing/centering and bullet indentation.}
     780
     781        \caption{Case comparison for array type compatibility, given pairs of dimension expressions.}
    823782        \label{f:DimexprRuleCompare}
    824783\end{figure}
     
    826785
    827786Figure~\ref{f:DimexprRuleCompare} gives a case-by-case comparison of the consequences of these rule sets.
    828 It demonstrates that the \CFA false alarms occur in the same cases as C treats unsafely.
    829 It also shows that C-incompatibilities only occur in cases that C treats unsafely.
     787It demonstrates that the \CFA false alarms occur in the same cases as C treats unsafe.
     788It also shows that C-incompatibilities only occur in cases that C treats unsafe.
    830789
    831790
     
    837796whose reuses are rejected by the blunt current-state rules:
    838797\begin{cfa}
    839         void f( int & nr, const int nv ) {
    840                 float x[nr];
    841                 float (*xp)[nr] = & x; // reject: nr varying (no references)
    842                 float y[nv + 1];
    843                 float (*yp)[nv + 1] = & y; // reject: ?+? unpredicable (no functions)
    844         }
     798void f( int & nr, const int nv ) {
     799        float x[nr];
     800        float (*xp)[nr] = &x;  // reject: nr varying (no references)
     801        float y[nv + 1];
     802        float (*yp)[nv + 1] = &y;   // reject: ?+? unpredictable (no functions)
     803}
    845804\end{cfa}
    846805Yet, both dimension expressions are reused safely.
    847 (The @nr@ reference is never written, not volatile
     806The @nr@ reference is never written, not volatile
    848807and control does not leave the function between the uses.
    849 The name @?+?@ resolves to a function that is quite predictable.)
    850 The programmer here can add the constant declarations:
    851 \begin{cfa}
    852         void f( int & nr, const int nv ) {
    853                 @const int nx@ = nr;
    854                 float x[nx];
    855                 float (*xp)[nx] = & x; // acept
    856                 @const int ny@ = nv + 1;
    857                 float y[ny];
    858                 float (*yp)[ny] = & y; // accept
    859         }
     808The name @?+?@ resolves to a function that is quite predictable.
     809Here, the programmer can add the constant declarations (cast does not work):
     810\begin{cfa}
     811void f( int & nr, const int nv ) {
     812        @const int nx@ = nr;
     813        float x[nx];
     814        float (*xp)[nx] = & x;   // accept
     815        @const int ny@ = nv + 1;
     816        float y[ny];
     817        float (*yp)[ny] = & y;  // accept
     818}
    860819\end{cfa}
    861820The result is the originally intended semantics,
     
    863822
    864823The snapshotting trick is also used by the translation, though to achieve a different outcome.
    865 Rather obviously, every array must be subscriptable, even a bizzarre one:
    866 \begin{cfa}
    867         array( float, rand(10) ) x;
    868         x[0];  // 10% chance of bound-check failure
     824Rather obviously, every array must be subscriptable, even a bizarre one:
     825\begin{cfa}
     826array( float, rand(10) ) x;
     827x[0];  // 10% chance of bound-check failure
    869828\end{cfa}
    870829Less obvious is that the mechanism of subscripting is a function call,
     
    874833Adjusting the example to make the function's use of length more explicit:
    875834\begin{cfa}
    876         forall ( T * )
    877         void f( T * x ) { sout | sizeof(*x); }
    878         float x[ rand(10) ];
    879         f( x );
     835forall ( T * )
     836void f( T * x ) { sout | sizeof(*x); }
     837float x[ rand(10) ];
     838f( x );
    880839\end{cfa}
    881840Considering that the partly translated function declaration is, loosely,
    882841\begin{cfa}
    883         void f( size_t __sizeof_T, void * x ) { sout | __sizeof_T; }
    884 \end{cfa}
    885 the translated call must not go like:
    886 \begin{cfa}
    887         float x[ rand(10) ];
    888         f( rand(10), &x );
    889 \end{cfa}
    890 Rather, its actual translation is like:
    891 \begin{cfa}
    892         size_t __dim_x = rand(10);
    893         float x[ __dim_x ];
    894         f( __dim_x, &x );
    895 \end{cfa}
    896 The occurrence of this dimension hoisting during translation was present in the preexisting \CFA compiler.
    897 But its cases were buggy, particularly with determining, ``Can hoisting be skipped here?''
    898 For skipping this hoisting is clearly desirable in some cases,
    899 not the least of which is when the programmer has already done so manually.
    900 My work includes getting these cases right, harmonized with the accept/reject criteria, and tested.
    901 
    902 
     842void f( size_t __sizeof_T, void * x ) { sout | __sizeof_T; }
     843\end{cfa}
     844the translation must call the dimension argument twice:
     845\begin{cfa}
     846float x[ rand(10) ];
     847f( rand(10), &x );
     848\end{cfa}
     849Rather, the translation is:
     850\begin{cfa}
     851size_t __dim_x = rand(10);
     852float x[ __dim_x ];
     853f( __dim_x, &x );
     854\end{cfa}
     855The occurrence of this dimension hoisting during translation was in the preexisting \CFA compiler.
     856But its cases were buggy, particularly with determining, ``Can hoisting the expression be skipped here?'', for skipping this hoisting is clearly desirable in some cases.
     857For example, when the programmer has already done so manually. \PAB{I don't know what this means.}
     858In the new implementation, these cases are correct, harmonized with the accept/reject criteria.
    903859
    904860TODO: Discuss the interaction of this dimension hoisting with the challenge of extra unification for cost calculation
    905861
    906862
    907 \section{Multidimensional Arrays}
    908 \label{toc:mdimpl}
    909 
    910 % TODO: introduce multidimensional array feature and approaches
    911 
    912 When working with arrays, \eg linear algebra, array dimensions are referred to as ``rows'' and ``columns'' for a matrix, adding ``planes'' for a cube.
    913 (There is little terminology for higher dimensional arrays.)
    914 For example, an acrostic poem\footnote{A type of poetry where the first, last or other letters in a line spell out a particular word or phrase in a vertical column.}
    915 can be treated as a grid of characters, where the rows are the text and the columns are the embedded keyword(s).
    916 Within a poem, there is the concept of a \newterm{slice}, \eg a row is a slice for the poem text, a column is a slice for a keyword.
    917 In general, the dimensioning and subscripting for multidimensional arrays has two syntactic forms: @m[r,c]@ or @m[r][c]@.
    918 
    919 Commonly, an array, matrix, or cube, is visualized (especially in mathematics) as a contiguous row, rectangle, or block.
    920 This conceptualization is reenforced by subscript ordering, \eg $m_{r,c}$ for a matrix and $c_{p,r,c}$ for a cube.
    921 Few programming languages differ from the mathematical subscript ordering.
    922 However, computer memory is flat, and hence, array forms are structured in memory as appropriate for the runtime system.
    923 The closest representation to the conceptual visualization is for an array object to be contiguous, and the language structures this memory using pointer arithmetic to access the values using various subscripts.
    924 This approach still has degrees of layout freedom, such as row or column major order, \ie juxtaposed rows or columns in memory, even when the subscript order remains fixed.
    925 For example, programming languages like MATLAB, Fortran, Julia and R store matrices in column-major order since they are commonly used for processing column-vectors in tabular data sets but retain row-major subscripting.
    926 In general, storage layout is hidden by subscripting, and only appears when passing arrays among different programming languages or accessing specific hardware.
    927 
    928 \VRef[Figure]{f:FixedVariable} shows two C90 approaches for manipulating a contiguous matrix.
    929 Note, C90 does not support VLAs.
    930 The fixed-dimension approach (left) uses the type system;
    931 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.
    932 Hence, every matrix passed to @fp1@ must have exactly 6 columns but the row size can vary.
    933 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 arithmetic in the macro @sub@.
    934 
    935 \begin{figure}
    936 \begin{tabular}{@{}l@{\hspace{40pt}}l@{}}
    937 \multicolumn{1}{c}{\textbf{Fixed Dimension}} & \multicolumn{1}{c}{\textbf{Variable Dimension}} \\
    938 \begin{cfa}
    939 
    940 void fp1( int rows, int m[][@6@] ) {
    941         ...  printf( "%d ", @m[r][c]@ );  ...
    942 }
    943 int fm1[4][@6@], fm2[6][@6@]; // no VLA
    944 // initialize matrixes
    945 fp1( 4, fm1 ); // implicit 6 columns
    946 fp1( 6, fm2 );
    947 \end{cfa}
    948 &
    949 \begin{cfa}
    950 #define sub( m, r, c ) *(m + r * sizeof( m[0] ) + c)
    951 void fp2( int rows, int cols, int *m ) {
    952         ...  printf( "%d ", @sub( m, r, c )@ );  ...
    953 }
    954 int vm1[@4@][@4@], vm2[@6@][@8@]; // no VLA
    955 // initialize matrixes
    956 fp2( 4, 4, vm1 );
    957 fp2( 6, 8, vm2 );
    958 \end{cfa}
    959 \end{tabular}
    960 \caption{C90 Fixed \vs Variable Contiguous Matrix Styles}
    961 \label{f:FixedVariable}
    962 \end{figure}
    963 
    964 Many languages allow multidimensional arrays-of-arrays, \eg in Pascal or \CC.
    965 \begin{cquote}
    966 \begin{tabular}{@{}ll@{}}
    967 \begin{pascal}
    968 var m : array[0..4, 0..4] of Integer;  (* matrix *)
    969 type AT = array[0..4] of Integer;  (* array type *)
    970 type MT = array[0..4] of AT;  (* array of array type *)
    971 var aa : MT;  (* array of array variable *)
    972 m@[1][2]@ := 1;   aa@[1][2]@ := 1 (* same subscripting *)
    973 \end{pascal}
    974 &
    975 \begin{c++}
    976 int m[5][5];
    977 
    978 typedef vector< vector<int> > MT;
    979 MT vm( 5, vector<int>( 5 ) );
    980 m@[1][2]@ = 1;  aa@[1][2]@ = 1;
    981 \end{c++}
    982 \end{tabular}
    983 \end{cquote}
    984 The language decides if the matrix and array-of-array are laid out the same or differently.
    985 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).
    986 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]@.
    987 Nevertheless, controlling memory layout can make a difference in what operations are allowed and in performance (caching/NUMA effects).
    988 
    989 C also provides non-contiguous arrays-of-arrays.
    990 \begin{cfa}
    991 int m[5][5];                                                    $\C{// contiguous}$
    992 int * aa[5];                                                    $\C{// non-contiguous}$
    993 \end{cfa}
    994 both with different memory layout using the same subscripting, and both with different degrees of issues.
    995 The focus of this work is on the contiguous multidimensional arrays in C.
    996 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.
    997 Nevertheless, the C array-of-array form is still important for special circumstances.
    998 
    999 \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.}
    1000 First, VLAs are supported.
    1001 Second, for contiguous arrays, C99 conjoins one or more of the parameters as a downstream dimension(s), \eg @cols@, implicitly using this parameter to compute the row stride of @m@.
    1002 If the declaration of @fc@ is changed to:
    1003 \begin{cfa}
    1004 void fc( int rows, int cols, int m[@rows@][@cols@] ) ...
    1005 \end{cfa}
    1006 it is possible for C to perform bound checking across all subscripting, but it does not.
    1007 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.
    1008 That is, the array does not automatically carry its structure and sizes for use in computing subscripts.
    1009 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.
    1010 Specifically, there is no requirement that the rows are the same length, like a poem with different length lines.
    1011 
    1012 \begin{figure}
    1013 \begin{tabular}{@{}ll@{}}
    1014 \multicolumn{1}{c}{\textbf{Contiguous}} & \multicolumn{1}{c}{\textbf{ Non-contiguous}} \\
    1015 \begin{cfa}
    1016 void fc( int rows, @int cols@, int m[ /* rows */ ][@cols@] ) {
    1017         ...  printf( "%d ", @m[r][c]@ );  ...
    1018 }
    1019 int m@[5][5]@;
    1020 for ( int r = 0; r < 5; r += 1 ) {
    1021 
    1022         for ( int c = 0; c < 5; c += 1 )
    1023                 m[r][c] = r + c;
    1024 }
    1025 fc( 5, 5, m );
    1026 \end{cfa}
    1027 &
    1028 \begin{cfa}
    1029 void faa( int rows, int cols, int * m[ @/* cols */@ ] ) {
    1030         ...  printf( "%d ", @m[r][c]@ );  ...
    1031 }
    1032 int @* aa[5]@;  // row pointers
    1033 for ( int r = 0; r < 5; r += 1 ) {
    1034         @aa[r] = malloc( 5 * sizeof(int) );@ // create rows
    1035         for ( int c = 0; c < 5; c += 1 )
    1036                 aa[r][c] = r + c;
    1037 }
    1038 faa( 5, 5, aa );
    1039 \end{cfa}
    1040 \end{tabular}
    1041 \caption{C99 Contiguous \vs Non-contiguous Matrix Styles}
    1042 \label{f:ContiguousNon-contiguous}
    1043 \end{figure}
    1044 
    1045 
    1046 \subsection{Multidimensional array implementation}
     863\section{Multidimensional array implementation}
    1047864\label{s:ArrayMdImpl}
    1048865
     
    12211038        S & | sized(S),                 $\C{// masquerading-as}$
    12221039        Timmed &,                               $\C{// immediate element, often another array}$
    1223         Tbase &                                 $\C{// base element, e.g. float, never array}$
     1040        Tbase &                                 $\C{// base element, \eg float, never array}$
    12241041) { // distribute forall to each element
    12251042        struct arpk {
     
    12741091
    12751092
    1276 
    1277 
     1093\section{Array lifecycle}
     1094
     1095An array is an aggregate, like a structure;
     1096both are containers wrapping subordinate objects.
     1097Any arbitrary object type, like @string@, can be an array element or structure member.
     1098A consequence is that the lifetime of the container must match with its subordinate objects: all elements and members must be initialized/uninitialized implicitly as part of the container's allocation/deallocation.
     1099Modern programming languages implicitly perform these operations via a type's constructor and destructor.
     1100Therefore, \CFA must assure that an array's subordinate objects' lifetime operations are called.
     1101
     1102Preexisting \CFA mechanisms achieve this requirement, but with poor performance.
     1103Furthermore, advanced array users need an exception to the basic mechanism, which does not occur with other aggregates.
     1104Hence, arrays introduce subleties in supporting an element's lifecycle.
     1105
     1106The preexisting \CFA support for contained-element lifecycle is based on recursive occurrences of the object-type (@otype@) pseudo-trait.
     1107A type is an @otype@, if it provides a default (parameterless) constructor, copy constructor, assignment operator, and destructor (like \CC).
     1108When declaring a structure with @otype@ members, the compiler implicitly generates implementations of the four @otype@ functions for the outer structure.
     1109Then the generated default constructor for the outer structure calls the default constructor for each member, and the other @otype@ functions work similarly.
     1110For a member that is a C array, these calls occur in a loop for each array element, which even works for VLAs.
     1111This logic works the same, whether the member is a concrete type (that happens to be an @otype@) or if the member is a polymorphic type asserted to be an @otype@ (which is implicit in the syntax, @forall(T)@).
     1112The \CFA array has the simplified form (similar to one seen before):
     1113\begin{cfa}
     1114forall( T * )   // non-otype element, no lifecycle functions
     1115// forall( T )  // otype element, lifecycle functions asserted
     1116struct array5 {
     1117        T __items[ 5 ];
     1118};
     1119\end{cfa}
     1120Being a structure with a C-array member, using the otype-form declaration for @T@ causes @array5(float)@ to implement @otype@ too.
     1121
     1122But this @otype@-recursion pattern has a performance issue.
     1123For example, in a cube of @float@:
     1124\begin{cfa}
     1125array5( array5( array5( float ) ) )
     1126\end{cfa}
     1127the first few steps of the compiler's work to find the lifecycle functions, under the @otype@-recursion pattern, are shown in \VRef[Figure]{f:OtypeRecursionBlowup}.
     1128All the work needed for the full @float@-cube would have 256 leaves.
     1129
     1130%array5(T) offers
     1131%1 parameterless ctor, which asks for T to have
     1132%       1 parameterless ctor
     1133%       2 copy ctor
     1134%       3 asgt
     1135%       4 dtor
     1136%2 copy ctor, which asks for T to have
     1137%       1 parameterless ctor
     1138%       2 copy ctor
     1139%       3 asgt
     1140%       4 dtor
     1141%3 asgt, which asks for T to have
     1142%       1 parameterless ctor
     1143%       2 copy ctor
     1144%       3 asgt
     1145%       4 dtor
     1146%4 dtor, which asks for T to have
     1147%       1 parameterless ctor
     1148%       2 copy ctor
     1149%       3 asgt
     1150%       4 dtor
     1151
     1152\begin{figure}
     1153\centering
     1154\setlength{\tabcolsep}{15pt}
     1155\begin{tabular}{@{}lll@{}}
     1156\begin{cfa}[deletekeywords={default}]
     1157float offers
     11581 default ctor
     11592 copy ctor
     11603 asgt
     11614 dtor
     1162
     1163
     1164
     1165
     1166
     1167
     1168
     1169
     1170
     1171
     1172
     1173
     1174
     1175
     1176
     1177
     1178
     1179
     1180
     1181
     1182
     1183
     1184
     1185
     1186\end{cfa}
     1187&
     1188\begin{cfa}[deletekeywords={default}]
     1189array5(float) has
     11901 default ctor, using float's
     1191        1 default ctor
     1192        2 copy ctor
     1193        3 asgt
     1194        4 dtor
     11952 copy ctor, using float's
     1196        1 default ctor
     1197        2 copy ctor
     1198        3 asgt
     1199        4 dtor
     12003 asgt, using float's
     1201        1 default ctor
     1202        2 copy ctor
     1203        3 asgt
     1204        4 dtor
     12054 dtor, using float's
     1206        1 default ctor
     1207        2 copy ctor
     1208        3 asgt
     1209        4 dtor
     1210
     1211
     1212
     1213
     1214
     1215
     1216
     1217
     1218\end{cfa}
     1219&
     1220\begin{cfa}[deletekeywords={default}]
     1221array5(array5(float)) has
     12221 default ctor, using array5(float)'s
     1223        1 default ctor, using float's
     1224                1 default ctor
     1225                2 copy ctor
     1226                3 asgt
     1227                4 dtor
     1228        2 copy ctor, using float's
     1229                1 default ctor
     1230                2 copy ctor
     1231                3 asgt
     1232                4 dtor
     1233        3 asgt, using float's
     1234                1 default ctor
     1235                2 copy ctor
     1236                3 asgt
     1237                4 dtor
     1238        4 dtor, using float's
     1239                1 default ctor
     1240                2 copy ctor
     1241                3 asgt
     1242                4 dtor
     12432 copy ctor, using array5(float)'s
     1244        ... 4 children, 16 leaves
     12453 asgt, using array5(float)'s
     1246        ... 4 children, 16 leaves
     12474 dtor, using array5(float)'s
     1248        ... 4 children, 16 leaves
     1249(64 leaves)
     1250\end{cfa}
     1251\end{tabular}
     1252\caption{Exponential thunk generation under the otype-recursion pattern.
     1253        Each time that one type's function (\eg ctor) uses another type's function, the \CFA compiler generates a thunk, to capture the used function's dependencies, presented according to the using function's need.
     1254        So, each non-leaf line represents a generated thunk and every line represents a search request for the resolver to find a satisfying function.}
     1255\label{f:OtypeRecursionBlowup}
     1256\end{figure}
     1257
     1258So the @otype@-recursion pattern seeks a quantity of helper functions, and generates a quantity of thunks, that are exponential in the number of dimensions.
     1259Anecdotal experience with this solution found the resulting compile times annoyingly slow at three dimensions, and unusable at four.
     1260
     1261The issue is that the otype-recursion pattern uses more assertions than needed.
     1262Consider how @array5(float)@'s default constructor is getting all four lifecycle assertions about the element type, @float@.
     1263It only needs @float@'s default constructor;
     1264the full set of operations is never used.
     1265Current work by the \CFA team aims to improve this situation.
     1266Therefore, a workaround is needed for now.
     1267
     1268The workaround is to provide a default constructor, copy constructor and destructor, defined with lean, bespoke assertions:
     1269\begin{cquote}
     1270\begin{tabular}{@{}l@{\hspace{0.5in}}l@{}}
     1271\begin{cfa}
     1272// autogenerated for otype-recursion:
     1273forall( T )
     1274void ?{}( array5(T) & this ) {
     1275        for (i; 5) { ( this[i] ){}; }
     1276}
     1277forall( T )
     1278void ?{}( array5(T) & this, array5(T) & src ) {
     1279        for (i; 5) { ( this[i] ){ src[i] }; }
     1280}
     1281forall( T )
     1282void ^?{}( array5(T) & this ) {
     1283        for (i; 5) { ^( this[i] ){}; }
     1284}
     1285\end{cfa}
     1286&
     1287\begin{cfa}
     1288// lean, bespoke:
     1289forall( T* | { void @?{}( T & )@; } )
     1290void ?{}( array5(T) & this ) {
     1291        for (i; 5) { ( this[i] ){}; }
     1292}
     1293forall( T* | { void @?{}( T &, T )@; } )
     1294void ?{}( array5(T) & this, array5(T) & src ) {
     1295        for (i; 5) { ( this[i] ){ src[i] }; }
     1296}
     1297forall( T* | { void @?{}( T & )@; } )
     1298void ^?{}( array5(T) & this ) {
     1299        for (i; 5) { ^( this[i] ){}; }
     1300}
     1301\end{cfa}
     1302\end{tabular}
     1303\end{cquote}
     1304Moreover, the assignment operator is skipped, to avoid hitting a lingering growth case.
     1305Skipping assignment is tolerable because array assignment is not a common operation.
     1306With this solution, the critical lifecycle functions are available, with no growth in thunk creation.
     1307
     1308Finally, the intuition that a programmer using an array always wants the elements' default constructor called \emph{automatically} is simplistic.
     1309Arrays exist to store different values at each position.
     1310So, array initialization needs to let the programmer provide different constructor arguments to each element.
     1311\begin{cfa}
     1312thread worker { int id; };
     1313void ?{}( worker & ) = void; // remove default constructor
     1314void ?{}( worker &, int id );
     1315array( worker, 5 ) ws = @{}@; // rejected; but desire is for no initialization yet
     1316for (i; 5) (ws[i]){ @i@ }; // explicitly initialize each thread, giving id
     1317\end{cfa}
     1318Note the use of the \CFA explicit constructor call, analogous to \CC's placement-@new@.
     1319This call is where initialization is desired, and not at the declaration of @ws@.
     1320The attempt to initialize from nothing (equivalent to dropping @= {}@ altogether) is invalid because the @worker@ type removes the default constructor.
     1321The @worker@ type is designed this way to work with the threading system.
     1322A thread type forks a thread at the end of each constructor and joins with it at the start of each destructor.
     1323But a @worker@ cannot begin its forked-thead work without knowing its @id@.
     1324Therefore, there is a conflict between the implicit actions of the builtin @thread@ type and a user's desire to defer these actions.
     1325
     1326Another \CFA feature may, at first, seem viable for initializing the array @ws@, though on closer inspection, it is not.
     1327C initialization, \lstinline|array(worker, 5) ws @= {};|, ignores all \CFA lifecycle management and uses C empty initialization.
     1328This option does achieve the desired semantics on the construction side.
     1329But on destruction side, the desired semantics is for implicit destructor calls to continue, to keep the join operation tied to lexical scope.
     1330C initialization disables \emph{all} implicit lifecycle management, but the goal is to disable only the implicit construction.
     1331
     1332To fix this problem, I enhanced the \CFA standard library to provide the missing semantics, available in either form:
     1333\begin{cfa}
     1334array( @uninit@(worker), 5 ) ws1;
     1335array( worker, 5) ws2 = { @delay_init@ };
     1336\end{cfa}
     1337Both cause the @ws@-construction-time implicit call chain to stop before reaching a @worker@ constructor, while leaving the implicit destruction calls intact.
     1338Two forms are available, to parallel the development of this feature in \uCpp.
     1339Originally \uCpp offered only the @ws1@ form, using the class-template @uNoCtor@ equivalent to \CFA's @uninit@.
     1340More recently, \uCpp was extended with the declaration macro, @uArray@, with usage similar to the @ws2@ case.
     1341Based on experience piloting @uArray@ as a replacement of @uNoCtor@, it might be possible to remove the first option.
     1342
     1343% note to Mike, I have more fragments on some details available in push24\fragments\uNoCtor.txt
    12781344
    12791345\section{Comparison with other arrays}
  • doc/theses/mike_brooks_MMath/background.tex

    rb006c51e r10a9479d  
    178178\lstinput{34-34}{bkgd-carray-arrty.c}
    179179The inspection begins by using @sizeof@ to provide program semantics for the intuition of an expression's type.
    180 An architecture with 64-bit pointer size is used, to keep irrelevant details fixed.
     180An architecture with 64-bit pointer size is used, to remove irrelevant details.
    181181\lstinput{35-36}{bkgd-carray-arrty.c}
    182182Now consider the @sizeof@ expressions derived from @ar@, modified by adding pointer-to and first-element (and including unnecessary parentheses to avoid any confusion about precedence).
     
    215215
    216216My observation is recognizing:
    217 \begin{itemize}[leftmargin=*,topsep=0pt,itemsep=0pt]
     217\begin{itemize}[leftmargin=*,itemsep=0pt]
    218218        \item There is value in using a type that knows its size.
    219219        \item The type pointer to the (first) element does not.
     
    302302
    303303In summary, when a function is written with an array-typed parameter,
    304 \begin{itemize}[leftmargin=*,topsep=0pt]
     304\begin{itemize}[leftmargin=*]
    305305        \item an appearance of passing an array by value is always an incorrect understanding,
    306306        \item a dimension value, if any is present, is ignored,
     
    532532\subsection{Array Parameter Declaration}
    533533
    534 Passing an array along with a function call is obviously useful.
    535 Let us say that a parameter is an array parameter when the called function intends to subscript it.
    536 This section asserts that a more satisfactory/formal characterization does not exist in C, surveys the ways that C API authors communicate ``@p@ has zero or more @T@s,'' and calls out the minority cases where the C type system is using or verifying such claims.
    537 
    538 A C function's parameter declarations look different, from the caller's and callee's perspectives.
     534Passing an array as an argument to a function is necessary.
     535Assume a parameter is an array when the function intends to subscript it.
     536This section asserts that a more satisfactory/formal characterization does not exist in C, surveys the ways that C API authors communicate ``@p@ has zero or more dimensions'' and calls out the minority cases where the C type system is using or verifying such claims.
     537
     538A C parameter declarations look different, from the caller's and callee's perspectives.
    539539Both perspectives consist of the text read by a programmer and the semantics enforced by the type system.
    540 The caller's perspecitve is available from a mere function declaration (which allow definition-before-use and separate compilation), but can also be read from (the non-body part of) a function definition.
     540The caller's perspective is available from a function declaration, which allow definition-before-use and separate compilation, but can also be read from (the non-body part of) a function definition.
    541541The callee's perspective is what is available inside the function.
    542542\begin{cfa}
    543         int foo( int, float, char );                            $\C{// declaration, names optional}$
    544         int bar( int i, float f, char c ) {             $\C{// definition, names mandatory}$
    545                 $/* caller's perspective of foo's; callee's perspective of bar's */$
    546                 ...
    547         }
    548         $/* caller's persepectives of foo's and bar's */$
    549 \end{cfa}
    550 The caller's perspective is more limited.
    551 The example shows, so far, that parameter names (by virtue of being optional) are really comments in the caller's perspective, while they are semantically significant in the callee's perspective.
     543int foo( int, float, char );                            $\C{// declaration, names optional}$
     544int bar( int i, float f, char c ) {             $\C{// definition, names mandatory}$
     545        // caller's perspective of foo; callee's perspective of bar
     546}
     547// caller's perspectives of foo's and bar's
     548\end{cfa}
     549In caller's perspective, the parameter names (by virtue of being optional) are really comments;
     550in the callee's perspective, parameter names are semantically significant.
    552551Array parameters introduce a further, subtle, semantic difference and considerable freedom to comment.
    553552
    554 At the semantic level, there is no such thing as an array parameter, except for one case (@T[static 5]@) discussed shortly.
     553At the semantic level, there is no such thing as an array parameter, except for one case (@T [static 5]@) discussed shortly.
    555554Rather, there are only pointer parameters.
    556 This fact probably shares considerable responsibility for the common sense of ``an array is just a pointer,'' wich has been refuted in non-parameter contexts.
     555This fact probably shares considerable responsibility for the common sense of ``an array is just a pointer,'' which has been refuted in non-parameter contexts.
    557556This fact holds in both the caller's and callee's perspectives.
    558 However, a parameter's type can include ``array of.''
    559 For example, the type ``pointer to array of 5 ints'' (@T(*)[5]@) is a pointer type, a fully meaningful parameter type (in the sense that this description does not contain any information that the type system ignores), and a type that appears the same in the caller's \vs callee's perspectives.
    560 The outermost type constructor (syntactically first dimension) is really the one that determines the flavour of parameter.
     557However, a parameter's type can include ``array of.'', \eg the type ``pointer to array of 5 ints'' (@T (*)[5]@) is a pointer type.
     558This type is fully meaningful in the sense that its description does not contain any information that the type system ignores, and the type appears the same in the caller's \vs callee's perspectives.
     559In fact, the outermost type constructor (syntactically first dimension) is really the one that determines the flavour of parameter.
     560
     561Yet, C allows array syntax for the outermost type constructor, from which comes the freedom to comment.
     562An array parameter declaration can specify the outermost dimension with a dimension value, @[10]@ (which is ignored), an empty dimension list, @[ ]@, or a pointer, @*@, as seen in \VRef[Figure]{f:ArParmEquivDecl}.
     563The rationale for rejecting the first ``invalid'' row follows shortly, while the second ``invalid'' row is simple nonsense, included to complete the pattern; its syntax hints at what the final row actually achieves.
    561564
    562565\begin{figure}
     
    596599\end{tabular}
    597600\end{cquote}
    598 \caption{Multiple ways to declare an arrray parameter.  Across a valid row, every declaration is equivalent.  Each column gives a declaration style.  Really, the style can be read from the first row only.  The second row shows how the style extends to multiple dimensions, with the rows thereafter providing context for the choice of which second-row \lstinline{[]}receives the column-style variation.}
     601\caption{Multiple ways to declare an array parameter.
     602Across a valid row, every declaration is equivalent.
     603Each column gives a declaration style, where the style for that column is read from the first row.
     604The second row begins the style for multiple dimensions, with the rows thereafter providing context for the choice of which second-row \lstinline{[]} receives the column-style variation.}
    599605\label{f:ArParmEquivDecl}
    600606\end{figure}
    601607
    602 Yet, C allows array syntax for the outermost type constructor, from which comes the freedom to comment.
    603 An array parameter declaration can specify the outermost dimension with a dimension value, @[10]@ (which is ignored), an empty dimension list, @[ ]@, or a pointer, @*@, as seen in \VRef[Figure]{f:ArParmEquivDecl}.  The rationale for rejecting the first ``invalid'' row follows shortly, while the second ``invalid'' row is simple nonsense, included to complete the pattern; its syntax hints at what the final row actually achieves.
    604 
    605 In the lefmost style, the typechecker ignores the actual value in most practical cases.
    606 This value is allowed to be a dynamic expression, so it is \emph{possible} to use the leftmost style in many practical cases.
     608In the leftmost style, the typechecker ignores the actual value in most practical cases.
     609This value is allowed to be a dynamic expression, and then it has practical cases.
     610\begin{cfa}
     611void foo( int @n@ ) {
     612        float _42( float @a[n]@ ) {    // nested function
     613                a[0] = 42;
     614        }
     615        float b[n];
     616        _42( b );
     617}
     618\end{cfa}
     619
    607620
    608621% To help contextualize the matrix part of this example, the syntaxes @float [5][]@, @float [][]@ and @float (*)[]@ are all rejected, for reasons discussed shortly.
    609622% So are @float[5]*@, @float[]*@ and @float (*)*@.  These latter ones are simply nonsense, though they hint at ``1d array of pointers'', whose equivalent syntax options are, @float *[5]@, @float *[]@, and @float **@.
    610623
    611 It is a matter of taste as to whether a programmer should use a form as far left as possible (getting the most out of syntactically integrated comments), sticking to the right (avoiding false comfort from suggesting the typechecker is checking more than it is), or compromising in the middle (reducing unchecked information, yet clearly stating, ``I will subscript this one'').
    612 
    613 Note that this equivalence of pointer and array declarations is special to paramters.
     624It is a matter of taste as to whether a programmer should use a form as far left as possible (getting the most out of possible subscripting and dimension sizes), sticking to the right (avoiding false comfort from suggesting the typechecker is checking more than it is), or compromising in the middle (reducing unchecked information, yet clearly stating, ``I will subscript).
     625
     626Note that this equivalence of pointer and array declarations is special to parameters.
    614627It does not apply to local variables, where true array declarations are possible.
    615628\begin{cfa}
     
    626639float sum( float v[] );
    627640float arg = 3.14;
    628 sum( &arg );                                                            $\C{// accepted, v := \&arg}$
     641sum( &arg );                                                            $\C{// accepted, v = \&arg}$
    629642\end{cfa}
    630643
     
    672685Here, the distance between the first and second elements of each array depends on the inner dimension size.
    673686
    674 The last observation is a fact of the callee's perspective.
    675 There is little type-system checking, in the caller's perspective, that what is being passed, matches.
    676 \begin{cfa}
    677 void f( float [][10] );
    678 int n = 100;
    679 float a[100], b[n];
    680 f(&a); // reject
    681 f(&b); // accept
    682 \end{cfa}
    683 This size is therefore, a callee's assumption.
    684 
    685 Finally, to handle higher-dimensional VLAs, C repurposed the @*@ \emph{within} the dimension in a declaration to mean that the callee will have make an assumption about the size here, but no (unchecked, possibly wrong) information about this assumption is included for the caller-programmer's benefit/overconfidence.
     687This significance of an inner dimension's length is a fact of the callee's perspective.
     688In the caller's perspective, the type sytem is quite lax.
     689Here, there is (some, but) little checking that what is being passed, matches.
     690% void f( float [][10] );
     691% int n = 100;
     692% float a[100], b[n];
     693% f(&a); // reject
     694% f(&b); // accept
     695\begin{cfa}
     696void foo() {
     697        void f( float [][10] );
     698        int n = 100;
     699        float a[100], b[3][12], c[n], d[n][n];
     700        f( a );
     701        f( b );    $\C{// reject: inner dimension 12 for 10}$
     702        f( c );
     703        f( @d@ );  $\C{// accept with inner dimension n for 10}$
     704        f( &a );   $\C{// reject: inner dimension 100 for 10}$
     705        f( &b );
     706        f( @&c@ ); $\C{// accept with inner dimension n for 10}$
     707        f( &d );
     708}
     709\end{cfa}
     710The cases without comments are rejections, but simply because the array ranks do not match; in the commented cases, the ranks match and the rules being discussed apply.
     711The cases @f(b)@ and @f(&a)@ show where some length checking occurs.
     712But this checking misses the cases @f(d)@ and @f(&c)@, allowing the calls with mismatched lengths, actually 100 for 10.
     713The C checking rule avoids false alarms, at the expense of safety, by allowing any combinations that involve dynamic values.
     714Ultimately, an inner dimension's size is a callee's \emph{assumption} because the type system uses declaration details in the callee's perspective that it does not enforce in the caller's perspective.
     715
     716Finally, to handle higher-dimensional VLAs, C repurposed the @*@ \emph{within} the dimension in a declaration to mean that the callee has make an assumption about the size, but no (unchecked, possibly wrong) information about this assumption is included for the caller-programmer's benefit/over-confidence.
    686717\begin{cquote}
    687718@[@ \textit{type-qualifier-list$_{opt}$} @* ]@
     
    11621193with all the variance being due to the (inevitable) cache status of the nodes being managed.
    11631194
     1195
    11641196\section{String}
     1197\label{s:String}
    11651198
    11661199A string is a sequence of symbols, where the form of a symbol can vary significantly: 7/8-bit characters (ASCII/Latin-1), or 2/4/8-byte (UNICODE) characters/symbols or variable length (UTF-8/16/32) characters.
  • doc/theses/mike_brooks_MMath/programs/hello-accordion.cfa

    rb006c51e r10a9479d  
    3030forall( [C], [S] )
    3131int getPref( @School( C, S ) & school@, int is, int pref ) {
    32     for ( ic; C ) {
    33         int curPref = @school.preferences@[ic][is];   $\C{// offset calculation implicit}$
     32        for ( ic; C ) {
     33                int curPref = @school.preferences@[ic][is];   $\C{// offset calculation implicit}$
    3434                if ( curPref == pref ) return ic;
    3535        }
    36     assert( false );
     36        assert( false );
    3737}
    3838
     
    5959
    6060        {       string sv;
    61         int iv;
    62         // headers' row
    63         sin | "\nc\\s";
    64         for ( is ; ns ) {
    65             // column label
    66             sin | sv;
    67             school.student_ids[is] = sv;
    68         }
    69         // body rows
    70         for ( ic ; nc ) {
    71             // row label
    72             sin | sv;
    73             school.course_codes[ic] = sv;
    74             for ( is ; ns ) {
    75                 // matrix item
    76                 sin | iv;
    77                 school.preferences[ic][is] = iv;
    78             }
    79         }
    80     }
     61                int iv;
     62                // headers' row
     63                sin | "\nc\\s";
     64                for ( is ; ns ) {
     65                        // column label
     66                        sin | sv;
     67                        school.student_ids[is] = sv;
     68                }
     69                // body rows
     70                for ( ic ; nc ) {
     71                        // row label
     72                        sin | sv;
     73                        school.course_codes[ic] = sv;
     74                        for ( is ; ns ) {
     75                                // matrix item
     76                                sin | iv;
     77                                school.preferences[ic][is] = iv;
     78                        }
     79                }
     80        }
    8181
    8282
     
    9191                sout | school.student_ids[is] | ": " | nonl;
    9292                for ( pref; 1 ~= nc ) {
    93             int ic = getPref(school, is, pref);
    94             sout | school.course_codes[ ic ] | nonl;
     93                        int ic = getPref(school, is, pref);
     94                        sout | school.course_codes[ ic ] | nonl;
    9595                }
    9696                sout | nl;
  • doc/theses/mike_brooks_MMath/programs/hello-array.cfa

    rb006c51e r10a9479d  
    114114        f( y, y );              $\C{// ok}$
    115115        if ( M == N )
    116                 f( x, @(array( float, M ) &)@y ); $\C{// ok}$
     116                f( x, @(array( float, M ) &)@y ); $\C{// ok}\CRT$
    117117}
    118118
  • doc/theses/mike_brooks_MMath/programs/sharing-demo.cfa

    rb006c51e r10a9479d  
    55#define str(s) #s
    66
     7ofstream outfile;
     8
    79void demo1() {
    810        sout | sepOff;
    9         sout | "Consider two strings @s1@ and @s1a@ that are in an aliasing relationship, and a third, @s2@, made by a simple copy from @s1@.";
    10         sout | "\\par\\noindent";
    11         sout | "\\begin{tabular}{llll}";
    12         sout | "\t\t\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
     11//      sout | "Consider two strings @s1@ and @s1a@ that are in an aliasing relationship, and a third, @s2@, made by a simple copy from @s1@.";
    1312
    1413        #define S1 string s1  = "abc"
     
    2120        assert( s1a == "abc" );
    2221        assert( s2 == "abc" );
    23         sout | xstr(S1) | "\t\\\\";
    24         sout | xstr(S1A) | "\t\\\\";
    25         sout | xstr(S2) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2;
    26         sout | "\\end{tabular}";
    27         sout | "\\par\\noindent";
    28 
    29         sout | "Aliasing (@`shareEdits@) means that changes flow in both directions; with a simple copy, they do not.";
    30         sout | "\\par\\noindent";
    31         sout | "\\begin{tabular}{llll}";
    32         sout | "\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
    33         sout | "\t\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     22
     23        open( outfile, "build/sharing1.tex" );
     24        outfile | "\\begin{cquote}";
     25        outfile | "\\begin{tabular}{@{}llll@{}}";
     26        outfile | "\t\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
     27        outfile | xstr(S1) | "\t\\\\";
     28        outfile | xstr(S1A) | "\t\\\\";
     29        outfile | xstr(S2) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2;
     30        outfile | "\\end{tabular}";
     31        outfile | "\\end{cquote}";
     32        close( outfile );
     33
     34//      sout | "Aliasing (@`shareEdits@) means that changes flow in both directions; with a simple copy, they do not.";
     35        open( outfile, "build/sharing2.tex" );
     36        outfile | "\\begin{cquote}";
     37        outfile | "\\begin{tabular}{@{}llll@{}}";
     38        outfile | "\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
     39        outfile | "\\multicolumn{1}{r}{initial} & " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    3440
    3541        #define S1s1 s1 [1] = '+'
    3642        S1s1;
    3743        assert( s1 == "a+c" );
    38         sout | xstr(S1s1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     44        outfile | xstr(S1s1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    3945
    4046        #define S1As1 s1a[1] = '-'
    4147        S1As1;
    4248        assert( s1a == "a-c" );
    43         sout | xstr(S1As1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     49        outfile | xstr(S1As1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    4450
    4551        #define S2s1 s2 [1] = '|'
    4652        S2s1;
    4753        assert( s2 == "a|c" );
    48         sout | xstr(S2s1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2;
    49         sout | "\\end{tabular}";
    50         sout | "\\par\\noindent";
    51 
    52         sout | "Assignment of a value is just a modificiation."
    53                    "\nThe aliasing relationship is established at construction and is unaffected by assignment of a value.";
    54         sout | "\\par\\noindent";
    55         sout | "\\begin{tabular}{llll}";
    56         sout | "\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
    57         sout | "\t\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     54        outfile | xstr(S2s1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2;
     55        outfile | "\\end{tabular}";
     56        outfile | "\\end{cquote}";
     57        close( outfile );
     58
     59//      sout | "Assignment of a value is just a modificiation."
     60//                 "\nThe aliasing relationship is established at construction and is unaffected by assignment of a value.";
     61        open( outfile, "build/sharing3.tex" );
     62        outfile | "\\begin{cquote}";
     63        outfile | "\\begin{tabular}{llll}";
     64        outfile | "\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
     65        outfile | "\\multicolumn{1}{r}{initial} & " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    5866
    5967        #define S1qrs s1  = "qrs"
    6068        S1qrs;
    6169        assert( s1 == "qrs" );
    62         sout | xstr(S1qrs) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     70        outfile | xstr(S1qrs) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    6371
    6472        #define S1Atuv s1a = "tuv"
    6573        S1Atuv;
    6674        assert( s1a == "tuv" );
    67         sout | xstr(S1Atuv) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     75        outfile | xstr(S1Atuv) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    6876
    6977        #define S2wxy s2  = "wxy"
    7078        S2wxy;
    7179        assert( s2 == "wxy" );
    72         sout | xstr(S2wxy) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2;
    73         sout | "\\end{tabular}";
    74         sout | "\\par\\noindent";
    75 
    76         sout | "Assignment from a string is just assignment of a value."
    77                    "\nWhether of not the RHS participates in aliasing is irrelevant.  Any aliasing of the LHS is unaffected.";
    78         sout | "\\par\\noindent";
    79         sout | "\\begin{tabular}{llll}";
    80         sout | "\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
    81         sout | "\t\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     80        outfile | xstr(S2wxy) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2;
     81        outfile | "\\end{tabular}";
     82        outfile | "\\end{cquote}";
     83        close( outfile );
     84
     85//      sout | "Assignment from a string is just assignment of a value."
     86//                 "\nWhether of not the RHS participates in aliasing is irrelevant.  Any aliasing of the LHS is unaffected.";
     87        open( outfile, "build/sharing4.tex" );
     88        outfile | "\\begin{cquote}";
     89        outfile | "\\begin{tabular}{llll}";
     90        outfile | "\t\t& @s1@\t& @s1a@\t& @s2@\t\\\\";
     91        outfile | "\\multicolumn{1}{r}{initial} & " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    8292
    8393        #define S1S2 s1  = s2
     
    8696        assert( s1a == "wxy" );
    8797        assert( s2 == "wxy" );
    88         sout | xstr(S1S2) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     98        outfile | xstr(S1S2) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    8999
    90100        #define S1aaa s1  = "aaa"
     
    93103        assert( s1a == "aaa" );
    94104        assert( s2 == "wxy" );
    95         sout | xstr(S1aaa) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     105        outfile | xstr(S1aaa) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    96106
    97107        #define S2S1 s2  = s1
     
    100110        assert( s1a == "aaa" );
    101111        assert( s2 == "aaa" );
    102         sout | xstr(S2S1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     112        outfile | xstr(S2S1) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    103113
    104114        #define S2bbb s2  = "bbb"
     
    107117        assert( s1a == "aaa" );
    108118        assert( s2 == "bbb" );
    109         sout | xstr(S2bbb) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    110      
    111     #define S2S1a s2  = s1a
     119        outfile | xstr(S2bbb) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     120
     121        #define S2S1a s2  = s1a
    112122        S2S1a;
    113123        assert( s1 == "aaa" );
    114124        assert( s1a == "aaa" );
    115125        assert( s2 == "aaa" );
    116         sout | xstr(S2S1a) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     126        outfile | xstr(S2S1a) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    117127
    118128        #define S2ccc s2  = "ccc"
     
    121131        assert( s1a == "aaa" );
    122132        assert( s2 == "ccc" );
    123         sout | xstr(S2ccc) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    124      
     133        outfile | xstr(S2ccc) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     134
    125135        #define S1xxx s1  = "xxx"
    126136        S1xxx;
     
    128138        assert( s1a == "xxx" );
    129139        assert( s2 == "ccc" );
    130         sout | xstr(S1xxx) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
    131         sout | "\\end{tabular}";
    132         sout | "\\par";
     140        outfile | xstr(S1xxx) | "\t& " | s1 | "\t& " | s1a | "\t& " | s2 | "\t\\\\";
     141        outfile | "\\end{tabular}";
     142        outfile | "\\end{cquote}";
     143        close( outfile );
    133144}
    134145
    135146
    136147void demo2() {
    137         sout | "Consider new strings @s1_mid@ being an alias for a run in the middle of @s1@, along with @s2@, made by a simple copy from the middle of @s1@.";
    138         sout | "\\par\\noindent";
    139         sout | "\\begin{tabular}{llll}";
    140         sout | "\t\t\t\t& @s1@\t& @s1_mid@\t& @s2@\t\\\\";
     148//      sout | "Consider new strings @s1_mid@ being an alias for a run in the middle of @s1@, along with @s2@, made by a simple copy from the middle of @s1@.";
     149        open( outfile, "build/sharing5.tex" );
     150        outfile | "\\begin{cquote}";
     151        outfile | "\\begin{tabular}{llll}";
     152        outfile | "\t\t\t\t& @s1@\t& @s1_mid@\t& @s2@\t\\\\";
    141153
    142154        #define D2_s1_abcd string s1     = "abcd"
    143155        D2_s1_abcd;
    144         sout | xstr(D2_s1_abcd) | "\t\\\\";
     156        outfile | xstr(D2_s1_abcd) | "\t\\\\";
    145157
    146158        #define D2_s1mid_s1 string s1_mid = s1(1,2)`shareEdits
    147159        D2_s1mid_s1;
    148         sout | xstr(D2_s1mid_s1) | "\t\\\\";
     160        outfile | xstr(D2_s1mid_s1) | "\t\\\\";
    149161
    150162        #define D2_s2_s1 string s2     = s1(1,2)
    151         D2_s2_s1;     
     163        D2_s2_s1;
    152164        assert( s1 == "abcd" );
    153165        assert( s1_mid == "bc" );
    154166        assert( s2 == "bc" );
    155         sout | xstr(D2_s2_s1) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    156         sout | "\\end{tabular}";
    157         sout | "\\par\\noindent";
    158 
    159     sout | "Again, @`shareEdits@ passes changes in both directions; copy does not.  Note the difference in index values, with the \\emph{b} position being 1 in the longer string and 0 in the shorter strings.  In the case of s1 aliasing with @s1_mid@, the very same character is being accessed by different postitions.";
    160         sout | "\\par\\noindent";
    161         sout | "\\begin{tabular}{llll}";
    162         sout | "\t\t\t\t& @s1@\t& @s1_mid@\t& @s2@\t\\\\";
    163         sout | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     167        outfile | xstr(D2_s2_s1) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     168        outfile | "\\end{tabular}";
     169        outfile | "\\end{cquote}";
     170        close( outfile );
     171
     172//      sout | "Again, @`shareEdits@ passes changes in both directions; copy does not.  Note the difference in index values, with the \\emph{b} position being 1 in the longer string and 0 in the shorter strings.  In the case of s1 aliasing with @s1_mid@, the very same character is being accessed by different postitions.";
     173        open( outfile, "build/sharing6.tex" );
     174        outfile | "\\begin{cquote}";
     175        outfile | "\\begin{tabular}{llll}";
     176        outfile | "\t\t\t\t& @s1@\t& @s1_mid@\t& @s2@\t\\\\";
     177        outfile | "\\multicolumn{1}{r}{initial} & " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    164178
    165179        #define D2_s1_plus s1    [1] = '+'
     
    168182        assert( s1_mid == "+c" );
    169183        assert( s2 == "bc" );
    170         sout | xstr(D2_s1_plus) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     184        outfile | xstr(D2_s1_plus) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    171185
    172186        #define D2_s1mid_minus s1_mid[0] = '-'
     
    175189        assert( s1_mid == "-c" );
    176190        assert( s2 == "bc" );
    177         sout | xstr(D2_s1mid_minus) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    178      
    179     #define D2_s2_pipe s2    [0] = '|'
     191        outfile | xstr(D2_s1mid_minus) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     192
     193        #define D2_s2_pipe s2    [0] = '|'
    180194        D2_s2_pipe;
    181195        assert( s1 == "a-cd" );
    182196        assert( s1_mid == "-c" );
    183197        assert( s2 == "|c" );
    184         sout | xstr(D2_s2_pipe) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    185         sout | "\\end{tabular}";
    186         sout | "\\par\\noindent";
    187 
    188     sout | "Once again, assignment of a value is a modificiation that flows through the aliasing relationship, without affecting its structure.";
    189         sout | "\\par\\noindent";
    190         sout | "\\begin{tabular}{llll}";
    191         sout | "\t\t\t\t& @s1@\t& @s1_mid@\t& @s2@\t\\\\";
    192         sout | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     198        outfile | xstr(D2_s2_pipe) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     199        outfile | "\\end{tabular}";
     200        outfile | "\\end{cquote}";
     201        close( outfile );
     202
     203//      sout | "Once again, assignment of a value is a modificiation that flows through the aliasing relationship, without affecting its structure.";
     204        open( outfile, "build/sharing7.tex" );
     205        outfile | "\\begin{cquote}";
     206        outfile | "\\begin{tabular}{llll}";
     207        outfile | "\t\t\t\t& @s1@\t& @s1_mid@\t& @s2@\t\\\\";
     208        outfile | "\\multicolumn{1}{r}{initial} & " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    193209
    194210        #define D2_s1mid_ff s1_mid = "ff"
     
    197213        assert( s1_mid == "ff" );
    198214        assert( s2 == "|c" );
    199         sout | xstr(D2_s1mid_ff) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    200      
     215        outfile | xstr(D2_s1mid_ff) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     216
    201217        #define D2_s2_gg s2     = "gg"
    202218        D2_s2_gg;
     
    204220        assert( s1_mid == "ff" );
    205221        assert( s2 == "gg" );
    206         sout | xstr(D2_s2_gg) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
    207         sout | "\\end{tabular}";
    208         sout | "\\par\\noindent";
    209 
    210     sout | "In the \\emph{ff} step, which is a positive example of flow across an aliasing relationship, the result is straightforward to accept because the flow direction is from contained (small) to containing (large).  The following rules for edits through aliasing substrings will guide how to flow in the opposite direction.";
    211         sout | "\\par";
    212 
    213 
    214     sout | "Growth and shrinkage are natural extensions.  An empty substring is a real thing, at a well-defined location, whose meaning is extrapolated from the examples so far.  The intended metaphor is to operating a GUI text editor.  Having an aliasing substring is like using the mouse to select a few words.  Assigning onto an aliasign substring is like typing from having a few words selected:  depending how much you type, the file being edited can get shorter or longer.";
    215         sout | "\\par\\noindent";
    216         sout | "\\begin{tabular}{lll}";
    217         sout | "\t\t\t\t& @s1@\t& @s1_mid@\t\\\\";
    218         sout | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
     222        outfile | xstr(D2_s2_gg) | "\t& " | s1 | "\t& " | s1_mid | "\t& " | s2 | "\t\\\\";
     223        outfile | "\\end{tabular}";
     224        outfile | "\\end{cquote}";
     225        close( outfile );
     226
     227//      sout | "In the \\emph{ff} step, which is a positive example of flow across an aliasing relationship, the result is straightforward to accept because the flow direction is from contained (small) to containing (large).  The following rules for edits through aliasing substrings will guide how to flow in the opposite direction.";
     228//      sout | "\\par";
     229
     230
     231//      sout | "Growth and shrinkage are natural extensions.  An empty substring is a real thing, at a well-defined location, whose meaning is extrapolated from the examples so far.  The intended metaphor is to operating a GUI text editor.  Having an aliasing substring is like using the mouse to select a few words.  Assigning onto an aliasign substring is like typing from having a few words selected:  depending how much you type, the file being edited can get shorter or longer.";
     232        open( outfile, "build/sharing8.tex" );
     233        outfile | "\\begin{cquote}";
     234        outfile | "\\begin{tabular}{lll}";
     235        outfile | "\t\t\t\t& @s1@\t& @s1_mid@\t\\\\";
     236        outfile | "\\multicolumn{1}{r}{initial} & " | s1 | "\t& " | s1_mid | "\t\\\\";
    219237
    220238        assert( s1 == "affd" );
    221 //      assert( s1_mid == "fc" );                                                    // ????????? bug?
    222         sout | xstr(D2_s2_gg) | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
     239//      assert( s1_mid == "fc" );                                                                                                        // ????????? bug?
     240        outfile | xstr(D2_s2_gg) | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
    223241
    224242        #define D2_s1mid_hhhh s1_mid = "hhhh"
     
    226244        assert( s1 == "ahhhhd" );
    227245        assert( s1_mid == "hhhh" );
    228         sout  | xstr(D2_s1mid_hhhh)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
    229      
     246        outfile  | xstr(D2_s1mid_hhhh)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
     247
    230248        #define D2_s1mid_i s1_mid = "i"
    231249        D2_s1mid_i;
    232250        assert( s1 == "aid" );
    233251        assert( s1_mid == "i" );
    234         sout  | xstr(D2_s1mid_i)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
     252        outfile  | xstr(D2_s1mid_i)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
    235253
    236254        #define D2_s1mid_empty s1_mid = ""
     
    238256        assert( s1 == "ad" );
    239257        // assert( s1_mid == "" );    ------ Should be so, but fails
    240         sout  | xstr(D2_s1mid_empty)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
     258        outfile  | xstr(D2_s1mid_empty)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
    241259
    242260        #define D2_s1mid_jj s1_mid = "jj"
     
    244262        assert( s1 == "ajjd" );
    245263        assert( s1_mid == "jj" );
    246         sout  | xstr(D2_s1mid_jj)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
    247         sout | "\\end{tabular}";
    248         sout | "\\par\\noindent";
    249 
    250     sout | "Multiple portions can be aliased.  When there are several aliasing substrings at once, the text editor analogy becomes an online multi-user editor.  I should be able to edit a paragraph in one place (changing the document's length), without my edits affecting which letters are within a mouse-selection that you had made previously, somewhere else.";
    251         sout | "\\par\\noindent";
    252         sout | "\\begin{tabular}{lllll}";
    253         sout | "\t\t\t\t& @s1@\t& @s1_bgn@\t& @s1_mid@\t& @s1_end@\t\\\\";
     264        outfile  | xstr(D2_s1mid_jj)  | "\t& " | s1 | "\t& " | s1_mid | "\t\\\\";
     265        outfile | "\\end{tabular}";
     266        outfile | "\\end{cquote}";
     267        close( outfile );
     268
     269//      sout | "Multiple portions can be aliased.  When there are several aliasing substrings at once, the text editor analogy becomes an online multi-user editor.  I should be able to edit a paragraph in one place (changing the document's length), without my edits affecting which letters are within a mouse-selection that you had made previously, somewhere else.";
     270        open( outfile, "build/sharing9.tex" );
     271        outfile | "\\begin{cquote}";
     272        outfile | "\\begin{tabular}{lllll}";
     273        outfile | "\t\t\t\t& @s1@\t& @s1_bgn@\t& @s1_mid@\t& @s1_end@\t\\\\";
    254274
    255275        #define D2_s1bgn_s1     string s1_bgn = s1(0, 1)`shareEdits
    256276        D2_s1bgn_s1;
    257         sout  | xstr(D2_s1bgn_s1)  | "\t\\\\";
     277        outfile  | xstr(D2_s1bgn_s1)  | "\t\\\\";
    258278
    259279        #define D2_s1end_s1 string s1_end = s1(3, 1)`shareEdits
     
    263283        assert( s1_mid == "jj" );
    264284        assert( s1_end == "d" );
    265         sout  | xstr(D2_s1end_s1)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
    266      
     285        outfile  | xstr(D2_s1end_s1)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
     286
    267287        #define D1_s1bgn_zzz s1_bgn = "zzzz"
    268288        D1_s1bgn_zzz;
     
    271291        assert( s1_mid == "jj" );
    272292        assert( s1_end == "d" );
    273         sout  | xstr(D1_s1bgn_zzz)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
    274         sout | "\\end{tabular}";
    275         sout | "\\par\\noindent";
    276 
    277     sout | "When an edit happens on an aliasing substring that overlaps another, an effect is unavoidable.  Here, the passive party sees its selection shortened, to exclude the characters that were not part of the original selection.";
    278         sout | "\\par\\noindent";
    279         sout | "\\begin{tabular}{llllll}";
    280         sout | "\t\t\t\t& @s1@\t& @s1_bgn@\t& @s1_crs@\t& @s1_mid@\t& @s1_end@\t\\\\";
     293        outfile  | xstr(D1_s1bgn_zzz)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
     294        outfile | "\\end{tabular}";
     295        outfile | "\\end{cquote}";
     296        close( outfile );
     297
     298//      sout | "When an edit happens on an aliasing substring that overlaps another, an effect is unavoidable.  Here, the passive party sees its selection shortened, to exclude the characters that were not part of the original selection.";
     299        open( outfile, "build/sharing10.tex" );
     300        outfile | "\\begin{cquote}";
     301        outfile | "\\begin{tabular}{llllll}";
     302        outfile | "\t\t\t\t& @s1@\t& @s1_bgn@\t& @s1_crs@\t& @s1_mid@\t& @s1_end@\t\\\\";
    281303
    282304        #define D2_s1crs_s1 string s1_crs = s1(3, 2)`shareEdits
     
    286308        assert( s1_crs == "zj" );
    287309        assert( s1_mid == "jj" );
    288         assert( s1_end == "d" ); 
    289         sout  | xstr(D2_s1crs_s1)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_crs | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
     310        assert( s1_end == "d" );
     311        outfile  | xstr(D2_s1crs_s1)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_crs | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
    290312
    291313        #define D2_s1crs_ppp s1_crs = "+++"
     
    296318        assert( s1_mid == "j" );
    297319        assert( s1_end == "d" );
    298         sout  | xstr(D2_s1crs_ppp)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_crs | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
    299         sout | "\\end{tabular}";
    300         sout | "\\par\\noindent";
    301         sout | "TODO: finish typesetting the demo";
    302 
    303     // "This shortening behaviour means that a modification has to occur entirely inside a substring, to show up in that substring.  Sharing changes through the intersection of partially overlapping aliases is still possible, so long as the receiver's boundary is not inside the edit."
     320        outfile  | xstr(D2_s1crs_ppp)  | "\t& " | s1 | "\t& " | s1_bgn | "\t& " | s1_crs | "\t& " | s1_mid | "\t& " | s1_end | "\t\\\\";
     321        outfile | "\\end{tabular}";
     322        outfile | "\\end{cquote}";
     323        close( outfile );
     324
     325        // "This shortening behaviour means that a modification has to occur entirely inside a substring, to show up in that substring.  Sharing changes through the intersection of partially overlapping aliases is still possible, so long as the receiver's boundary is not inside the edit."
    304326
    305327        string word = "Phi";
     
    309331        assert( consonants == "Ph" );
    310332        assert( miniscules == "hi" );
    311      
     333
    312334        consonants[1] = 's';
    313335        assert( word == "Psi" );
     
    321343        string greet_bgn = all(10,1)`shareEdits;
    322344        string greet_end = all(14,1)`shareEdits;
    323      
     345
    324346        assert( all == "They said hello again" );
    325347        assert( greet == "hello" );
    326348        assert( greet_bgn == "h" );
    327349        assert( greet_end == "o" );
    328      
     350
    329351
    330352        greet = "sup";
     
    333355        // assert( greet_bgn == "" );    ------ Should be so, but fails
    334356        // assert( greet_end == "" );
    335      
    336 
    337  
    338 
    339  
    340     /* As in the earlier step where \emph{aj} becomes \emph{ajjd}, such empty substrings maintain their places in the total string, and can be used for filling it.  Because @greet_bgn@ was orginally at the start of the edit, in the outcome, the empty @greet_bgn@ sits just before the written value.  Similarly @greed_end@ goes after.  Though not shown, an overwritten substring at neither side goes arbitrarily to the before side. */
    341  
    342 
    343  
    344 
    345         greet_bgn = "what"; 
    346      
    347      
     357
     358
     359
     360
     361
     362        /* As in the earlier step where \emph{aj} becomes \emph{ajjd}, such empty substrings maintain their places in the total string, and can be used for filling it.  Because @greet_bgn@ was orginally at the start of the edit, in the outcome, the empty @greet_bgn@ sits just before the written value.  Similarly @greed_end@ goes after.  Though not shown, an overwritten substring at neither side goes arbitrarily to the before side. */
     363
     364
     365
     366
     367        greet_bgn = "what";
     368
     369
    348370        assert( all == "They said whatsup again" );
    349      
     371
    350372        assert( greet == "sup" );
    351      
     373
    352374        assert( greet_bgn == "what" );
    353375        // assert( greet_end == "" );    ------ Should be so, but fails
    354      
    355 
    356         greet_end = "..."; 
    357      
    358      
     376
     377
     378        greet_end = "...";
     379
     380
    359381        assert( all == "They said whatsup... again" );
    360      
     382
    361383        assert( greet == "sup" );
    362      
     384
    363385        assert( greet_bgn == "what" );
    364      
     386
    365387        assert( greet_end == "..." );
    366      
    367 
    368  
    369 
    370  
    371     /* Though these empty substrings hold their places in the total string, an empty string only belongs to bigger strings when it occurs completely inside them.  There is no such state as including an empty substring at an edge.  For this reason, @word@ gains the characters added by assigning to @greet_bgn@ and @greet_end@, but the string @greet@ does not. */
    372  
     388
     389
     390
     391
     392
     393        /* Though these empty substrings hold their places in the total string, an empty string only belongs to bigger strings when it occurs completely inside them.  There is no such state as including an empty substring at an edge.  For this reason, @word@ gains the characters added by assigning to @greet_bgn@ and @greet_end@, but the string @greet@ does not. */
     394
    373395
    374396}
     
    377399int main(int argc, char ** argv) {
    378400
    379     demo1();
    380     demo2();
    381     printf("%% %s done running\n", argv[0]);
     401        demo1();
     402        demo2();
     403//      printf("%% %s done running\n", argv[0]);
    382404}
  • doc/theses/mike_brooks_MMath/string.tex

    rb006c51e r10a9479d  
    55
    66
    7 \section{Logical overlap}
    8 
    9 \input{sharing-demo.tex}
     7\section{String Operations}
     8
     9To prepare for the following discussion, a simple comparison among C, \CC, and \CFA basic string operation is presented.
     10\begin{cquote}
     11\begin{tabular}{@{}l|l|l@{}}
     12C @char [ ]@                    &  \CC @string@                 & \CFA @string@ \\
     13\hline
     14@strcpy@, @strncpy@             & @=@                                   & @=@   \\
     15@strcat@, @strncat@             & @+@                                   & @+@   \\
     16@strcmp@, @strncmp@             & @==@, @!=@, @<@, @<=@, @>@, @>=@ & @==@, @!=@, @<@, @<=@, @>@, @>=@ \\
     17@strlen@                                & @length@                              & @size@        \\
     18@[ ]@                                   & @[ ]@                                 & @[ ]@ \\
     19                                                & @substr@                              & @substr@      \\
     20                                                & @replace@                             & @=@ \emph{(on a substring)}\\
     21@strstr@                                & @find@, @rfind@               & @find@, MISSING \\
     22@strcspn@                               & @find_first_of@, @find_last_of@ & @include@, MISSING \\
     23@strspn@                                & @find_first_not_of@, @find_last_not_of@ & @exclude@, MISSING \\
     24                                                & @c_str@                               & MISSING \\
     25\end{tabular}
     26\end{cquote}
     27The key commonality is that operations work on groups of characters for assigning. copying, scanning, and updating.
     28Because a C string is null terminated and requires explicit storage management \see{\VRef{s:String}}, most of its group operations are error prone and expensive.
     29Most high-level string libraries use a separate length field and specialized storage management to support group operations.
     30\CC strings retain null termination to interface with library functions requiring C strings.
     31\begin{cfa}
     32int open( const char * pathname, int flags );
     33string fname{ "test.cc" );
     34open( fname.@c_str()@ );
     35\end{cfa}
     36The function @c_str@ does not create a new null-terminated C string from the \CC string, as that requires passing ownership of the C string to the caller for eventual deletion.\footnote{
     37C functions like \lstinline{strdup} do return allocated storage that must be freed by the caller.}
     38Instead, each \CC string is null terminator just in case it might be needed for this purpose.
     39Providing this backwards compatibility with C has a ubiquitous performance and storage cost.
     40
     41
     42\section{Storage Management}
     43
     44This section discusses issues related to storage management of strings.
     45Specifically, it is common for strings to logically overlap completely or partially.
     46\begin{cfa}
     47string s1 = "abcdef";
     48string s2 = s1; $\C{// complete overlap, s2 == "abcdef"}$
     49string s3 = s1.substr( 0, 3 ); $\C{// partial overlap, s3 == "abc"}$
     50\end{cfa}
     51This raises the question of how strings behave when an overlapping component is changed,
     52\begin{cfa}
     53s3[1] = 'w'; $\C{// what happens to s1 and s2?}$
     54\end{cfa}
     55This question is the notion of mutable or immutable strings.
     56For example, Java has immutable strings that are copied when any overlapping string changes.
     57Note, the notion of underlying string mutability is not specified by @const@, \eg:
     58\begin{cfa}
     59const string s1 = "abc";
     60\end{cfa}
     61Here, @const@ applies to the @s1@ pointer to @"abc"@, and @"abc"@ is an immutable constant that is \emph{copied} into the string's storage.
     62Hence, @s1@ is not pointing at an immutable constant, meaning its underlying string is always mutable, unless some other designation is specified, such as Java's global rule.
     63
     64
     65\subsection{Logical overlap}
     66
     67\CFA provides a dynamic mechanism to indicate mutable or immutable as an assignment attribute: @`shareEdits@.
    1068
    1169Consider two strings @s1@ and @s1a@ that are in an aliasing relationship, and a third, @s2@, made by a simple copy from @s1@.
    12 \par\noindent
    13 \begin{tabular}{llll}
    14                                 & @s1@  & @s1a@ & @s2@  \\
    15 %\input{sharing-demo1.tex}
    16 \end{tabular}
    17 \par\noindent
     70Aliasing (@`shareEdits@) means that changes flow in both directions; with a simple copy, they do not.
     71\input{sharing1.tex}
     72
     73Aliasing (@`shareEdits@) means that changes flow in both directions; with a simple copy, they do not.
     74\input{sharing2.tex}
     75
     76Assignment of a value is just a modification.
     77The aliasing relationship is established at construction and is unaffected by assignment of a value.
     78\input{sharing3.tex}
     79
     80Assignment from a string is just assignment of a value.
     81Whether of not the RHS participates in aliasing is irrelevant.  Any aliasing of the LHS is unaffected.
     82\input{sharing4.tex}
     83
     84Consider new strings @s1_mid@ being an alias for a run in the middle of @s1@, along with @s2@, made by a simple copy from the middle of @s1@.
     85\input{sharing5.tex}
     86
     87Again, @`shareEdits@ passes changes in both directions; copy does not.
     88Note the difference in index values, with the \emph{b} position being 1 in the longer string and 0 in the shorter strings.
     89In the case of s1 aliasing with @s1_mid@, the very same character is being accessed by different positions.
     90\input{sharing6.tex}
     91
     92Once again, assignment of a value is a modification that flows through the aliasing relationship, without affecting its structure.
     93\input{sharing7.tex}
     94
     95In the \emph{ff} step, which is a positive example of flow across an aliasing relationship, the result is straightforward to accept because the flow direction is from contained (small) to containing (large).
     96The following rules for edits through aliasing substrings will guide how to flow in the opposite direction.
     97
     98Growth and shrinkage are natural extensions.
     99An empty substring is a real thing, at a well-defined location, whose meaning is extrapolated from the examples so far.
     100The intended metaphor is to operating a GUI text editor.
     101Having an aliasing substring is like using the mouse to select a few words.
     102Assigning onto an aliasing substring is like typing from having a few words selected: depending how much you type, the file being edited can get shorter or longer.
     103\input{sharing8.tex}
     104
     105Multiple portions can be aliased.
     106When there are several aliasing substrings at once, the text editor analogy becomes an online multi-user editor.
     107I should be able to edit a paragraph in one place (changing the document's length), without my edits affecting which letters are within a mouse-selection that you had made previously, somewhere else.
     108\input{sharing9.tex}
     109
     110When an edit happens on an aliasing substring that overlaps another, an effect is unavoidable.
     111Here, the passive party sees its selection shortened, to exclude the characters that were not part of the original selection.
     112\input{sharing10.tex}
     113
     114TODO: finish typesetting the demo
     115
     116%\input{sharing-demo.tex}
    18117
    19118
    20119\subsection{RAII limitations}
    21120
    22 Earlier work on \CFA [to cite Schluntz] implemented the feature of constructors and destructors.  A constructor is a user-defined function that runs implicitly, when control passes an object's declaration, while a destructor runs at the exit of the declaration's lexical scope.  The feature allows programmers to assume that, whenever a runtime object of a certain type is accessible, the system called one of the programmer's constructor functions on that object, and a matching destructor call will happen in the future.  The feature helps programmers know that their programs' invariants obtain.
    23 
    24 The purposes of such invariants go beyond ensuring authentic values for the bits inside the object.   These invariants can track occurrences of the managed objects in other data structures.  Reference counting is a typical application of the latter invariant type.  With a reference-counting smart pointer, the constructor and destructor \emph{of the pointer type} track the life cycles of occurrences of these pointers, by incrementing and decrementing a counter (usually) on the referent object, that is, they maintain a that is state separate from the objects to whose life cycles they are attached.  Both the \CC and \CFA RAII systems ares powerful enough to achieve such reference counting.
    25 
    26 The \CC RAII system supports a more advanced application.  A life cycle function has access to the object under management, by location; constructors and destuctors receive a @this@ parameter providing its memory address.  A lifecycle-function implementation can then add its objects to a collection upon creation, and remove them at destruction.  A modulue that provides such objects, by using and encapsulating such a collection, can traverse the collection at relevant times, to keep the objects ``good.''  Then, if you are the user of such an module, declaring an object of its type means not only receiving an authentically ``good'' value at initialization, but receiving a subscription to a service that will keep the value ``good'' until you are done with it.
    27 
    28 In many cases, the relationship between memory location and lifecycle is simple.  But with stack-allocated objects being used as parameters and returns, there is a sender version in one stack frame and a receiver version in another.  \CC is able to treat those versions as distinct objects and guarantee a copy-constructor call for communicating the value from one to the other.  This ability has implications on the language's calling convention.  Consider an ordinary function @void f( Vehicle x )@, which receives an aggregate by value.  If the type @Vehicle@ has custom lifecycle functions, then a call to a user-provided copy constructor occurs, after the caller evaluates its argument expression, after the callee's stack frame exists, with room for its variable @x@ (which is the location that the copy-constructor must target), but before the user-provided body of @f@ begins executing.  \CC achieves this ordering by changing the function signature, in the compiled form, to pass-by-reference and having the callee invoke the copy constructor in its preamble.  On the other hand, if @Vehicle@ is a simple structure then the C calling convention is applied as the code originally appeared, that is, the callsite implementation code performs a bitwise copy from the caller's expression result, into the callee's x.
    29 
    30 TODO: learn correction to fix inconcsistency: this discussion says the callee invokes the copy constructor, but only the caller knows which copy constructor to use!
    31 
    32 TODO: discuss the return-value piece of this pattern
    33 
    34 The \CFA RAII system has limited support for using lifecycle functions to provide a ``stay good'' service.  It works in restricted settings, including on dynamically allocated objects.  It does not work for communicating arguments and returns by value because the system does not produce a constructor call that tracks the implied move from a sender frame to a reciver frame.  This limitation does not prevent a typical reference-counting design from using call-with-value/return-of-value, because the constructor--destructor calls are correctly balanced.  But it impedes a ``stay-good'' service from supporting call-with-value/return-of-value, because the lifecycles presented to the constructor/destor calls do not keep stable locations.  A ``stay-good'' service is acheivable so long as call-with-value/return-of-value do not occur.  The original presentation [to cite Schluntz section] acknoweledges this limitiation; the present discussion makes its consequences more apparent.
    35 
    36 The \CFA team sees this limitation as part of a tactical interem state that should some day be improved.  The \CFA compiler is currently a source-to-source translator that targets relativly portable C.  Several details of its features are provisionally awkward or under-performant until finer control of its code generation is feasible.  In the present state, all calls that appear in \CFA source code as call-with-value/return-of-value are emitted this way to the underlying C calling convention.  SO WHAT?
    37 
    38 The present string-API contribution has both the ``stay good'' promise and call-with-value/return-of-value being essential.  The main string API uses a work-around to acheive the full feature set, at a runtime performance penalty.  An alternative API sacrifices call-with-value/return-of-value functionality to recover full runtime performance.  These APIs are layered, with the slower, friendlier High Level API (HL) wrapping the faster, more primitive Low Level API (LL).  They present the same features, up to lifecycle management, with call-with-value/return-of-value being disabled in LL and implemented with the workaround in HL.  The intention is for most future code to target HL.  In a more distant future state, where \CFA has an RAII system that can handle the problematic quadrant, the HL layer can be abolished, the LL can be renamed to match today's HL, and LL can have its call-with-value/return-of-value permission reenabled.  Then, programs written originally against HL will simply run faster.  In the meantime, two use cases of LL exist.  Performance-critical sections of applications have LL as an option.  Within [Xref perf experiments], though HL-v-LL penalties are measured, typcial comparisons of the contributed string libary vs similar systems are made using LL.  This measurement gives a fair estimate of the goal state for \CFA while it is an evloving work in progress.
    39 
     121Earlier work on \CFA~\cite[ch.~2]{Schluntz17} implemented object constructors and destructors for all types (basic and user defined).
     122A constructor is a user-defined function run implicitly \emph{after} an object's declaration-storage is created, and a destructor is a user-defined function run \emph{before} an object's declaration-storage is deleted.
     123This feature, called RAII~\cite[p.~389]{Stroustrup94}, guarantees pre invariants for users before accessing an object and post invariants for the programming environment after an object terminates.
     124
     125The purposes of these invariants goes beyond ensuring authentic values inside an object.
     126Invariants can also track occurrences of managed objects in other data structures.
     127For example, reference counting is a typical application of an invariant outside of the data values.
     128With a reference-counting smart-pointer, the constructor and destructor \emph{of a pointer type} tracks the life cycle of the object it points to.
     129Both \CC and \CFA RAII systems are powerful enough to achieve reference counting.
     130
     131In general, a lifecycle function has access to an object by location, \ie constructors and destructors receive a @this@ parameter providing an object's memory address.
     132The lifecycle implementation can then add this object to a collection at creation and remove it at destruction.
     133A module providing lifecycle semantics can traverse the collection at relevant times to keep the objects ``good.''
     134Hence, declaring such an object not only ensures ``good'' authentic values, but also an implicit subscription to a service that keeps the value ``good'' across its lifetime.
     135
     136In many cases, the relationship between memory location and lifecycle is straightforward.
     137For example, stack-allocated objects being used as parameters and returns, with a sender version in one stack frame and a receiver version in another, as opposed to assignment where sender and receiver are in the same stack frame.
     138What is crucial for lifecycle management is knowing if the receiver is initialized or uninitialized, \ie an object is or is not currently associated with management.
     139To provide this knowledge, languages differentiate between initialization and assignment to a left-hand side.
     140\begin{cfa}
     141Obj obj2 = obj1;  // initialization, obj2 is uninitialized
     142obj2 = obj1;        // assignment, obj2 must be initialized for management to work
     143\end{cfa}
     144Initialization occurs at declaration by value, parameter by argument, return temporary by function call.
     145Hence, it is necessary to have two kinds of constructors: by value or object.
     146\begin{cfa}
     147Obj obj1{ 1, 2, 3 };  // by value, management is initialized
     148Obj obj2 = obj1;     // by obj, management is updated
     149\end{cfa}
     150When no object management is required, initialization copies the right-hand value.
     151Hence, the calling convention remains uniform, where the unmanaged case uses @memcpy@ as the initialization constructor and managed uses the specified initialization constructor.
     152
     153The \CFA RAII system supports lifecycle functions, except for returning a value from a function to a temporary.
     154For example, in \CC:
     155\begin{cfa}
     156struct S {...};
     157S identity( S s ) { return s; }
     158S s;
     159s = identity( s ); // S temp = identity( s ); s = temp;
     160\end{cfa}
     161the generated code for the function call created a temporary with initialization from the function call, and then assigns the temporary to the receiver.
     162This two step approach means extra storage for the temporary and two copies to get the result into the receiver variable.
     163\CC{17} introduced return value-optimization (RVO)~\cite{RVO20} to ``avoid copying an object that a function returns as its value, including avoiding creation of a temporary object''.
     164\CFA uses C semantics for function return giving direct value-assignment, which eliminates unnecessary code, but skips an essential feature needed by lifetime management.
     165The following discusses the consequences of this semantics with respect to lifetime management of \CFA strings.
     166
     167The present string-API contribution provides lifetime management with initialization semantics on function return.
     168The workaround to achieve the full lifetime semantics does have a runtime performance penalty.
     169An alternative API sacrifices return initialization semantics to recover full runtime performance.
     170These APIs are layered, with the slower, friendlier High Level API (HL) wrapping the faster, more primitive Low Level API (LL).
     171Both API present the same features, up to lifecycle management, with return initialization being disabled in LL and implemented with the workaround in HL.
     172The intention is for most future code to target HL.
     173When \CFA becomes a full compiler, it can provide return initialization with RVO optimizations.
     174Then, programs written with the HL API will simply run faster.
     175In the meantime, performance-critical sections of applications use LL.
     176Subsequent performance experiments~\VRef{s:PerformanceAssessment} with other string libraries has \CFA strings using the LL API.
     177These measurement gives a fair estimate of the goal state for \CFA.
    40178
    41179
    42180\subsection{Memory management}
    43181
    44 A centrepriece of the string module is its memory manager.  The managment scheme defines a large shared buffer for strings' text.  Allocation in this buffer is always bump-pointer; the buffer is compacted and/or relocated with growth when it fills.  A string is a smart pointer into this buffer.
    45 
    46 This cycle of frequent cheap allocations, interspersed with infrequent expensive compactions, has obvious similarities to a general-purpose memory manager based on garbage collection (GC).  A few differences are noteworthy.  First, in a general purpose manager, the objects of allocation contain pointers to other such objects, making the transitive reachability of these objects be a critical property.  Here, the allocations are of buffers of text, never pointers, so one allocation never keeps another one alive.  Second, in a general purpose manager, where the handle that keeps an allocation alive is the same as the program's general-purpose inter-object reference, an extremely lean representation of this reference is required.  Here, a fatter representation is acceptable because [why??].
    47 
    48 
    49 Figure [memmgr-basix.vsdx] shows the representation.  A heap header, with its text buffer, defines a sharing context.  Often, one global sharing context is appropriate for an entire program; exceptions are discussed in [xref TBD].  Strings are handles into the buffer.  They are members of a linked list whose order matches the order of their buffer fragments (exactly, where there is no overlapping, and approximately, where there is).  The header maintains a next-allocation pointer (alloc, in the figure) after the last live allocation of the buffer.  No external references into the buffer are allowed and the management procedure relocates the text allocations as needed.  The string handles contain explicit length fields, null termination characters are not used and all string text is kept in contiguous storage.  When strings (the inter-linked hanldes) are allocated on the program's call stack, a sustained period with no use of the program's dynamic memory allocator can ensue, during which the program nonetheless creates strings, destroys them, and runs length-increasing modifications on existing ones. 
    50 
    51 Compaction happens when the heap fills.  It is the first of two uses of the linked list.  The list allows discovering all live string handles, and from them, the ranges of the character buffer that are in use.  With these ranges known, their total character count gives the amount of space in use.  When that amount is small, compared with the current buffer size, an in-place compaction occurs, which enatils copying the in-use ranges, to be adjacent, at the font of the buffer.  When the in-use amount is large, a larger buffer is allocated (using the program's general-purpose dynamic allcator), the in-use strings are copied to be adjacent at the front of it, and the original buffer is freed back to the program's general allocator.  Either way, navigating the links between the handles provides the pointers into the buffer, first read, to find the source fragment, then written with the location of the resultant fragment.  This linkage across the structure is unaffected during a compaction; only the pointers from the handles to the buffer are modified.  This modification, along with the grooming/provisioning of the text storage resouce that it represents, is an example, in the language of [xref RAII limitations] of the string module providing a ``stay good'' service.
    52 
    53 Object lifecycle events are the subscription-management triggers in such a service.  There are two fundamental string-creation routines:  importing from external text like a C-string, and initialization from an existing \CFA string.  When importing, a fresh allocation at the free end fo the buffer occurs, into which the text is copied.  The resultant handle is therefore inserted into the list at the position after the incumbent last handle, a position given by the heap manager's ``last handle'' pointer.  When initializing from text already on the \CFA heap, the resultant handle is a second reference onto the original run of characters.  In this case, the resultant handle's linked-list position is beside the original handle.  Both string initialization styles preserve the string module's internal invriant that the linked-list order match the buffer order.  For string destruction, the list being doubly linked provides for easy removal of the disappearing handle.
    54 
    55 While a string handle is live, it accepts modification operations, some of which make it reference a different portion of the underlying buffer, and accordingly, move the handle to a different position in the inter-handle list.   While special cases have more optimal handling, the general case requires a fresh buffer run.  In this case, the new run is allocated at the bump-pointer end and filled with the required value.  Then, handles that originally referenced the old location and need to see the new value are pointed at the new buffer location, unlinked from their original positions in the handles' list, and linked in at the end of the list.  An optimal case, when the target is not a substring of something larger, and the source is text from elsewhere in the managed buffer, allows the target to be re-pointed at the source characters, and accordingly, move list position to be beside the source.  Cases where in-place editing happens, addressed further in [xref: TBD], leave affected handles in their original list positions.  In analogy to the two cases of string initialization, the two cases of realizing assignment by moving either to a fresh buffer run, or to overlap references with the source, maintain the invariant of linked list order matching buffer order.
    56 
    57 
    58 To explain: GCing allocator doing bump-pointer with compaction
    59 
    60 
    61 
    62 At the level of the memory manager, these modifications can aways be explained as assignments; for example, an append is an assignemnt into the empty substring at the end.
    63 
    64 While favourable conditions allow for in-place editing, the general case requires a fresh buffer run.  For example, if the new value does not fit in the old place, or if other handles are still using the old value, then the new value will use a fresh buffer run.
     182A centrepiece of the string module is its memory manager.
     183The management scheme defines a shared buffer for string text.
     184Allocation in this buffer is via a bump-pointer;
     185the buffer is compacted and/or relocated with growth when it fills.
     186A string is a smart pointer into this buffer.
     187
     188This cycle of frequent cheap allocations, interspersed with infrequent expensive compactions, has obvious similarities to a general-purpose memory manager based on garbage collection (GC).
     189A few differences are noteworthy.
     190First, in a general purpose manager, the objects of allocation contain pointers to other such objects, making the transitive reachability of these objects be a critical property.
     191Here, the allocations are text, so one allocation never keeps another alive.
     192Second, in a general purpose manager, the handle that keeps an allocation alive is just a lean pointer.
     193For strings, a fatter representation is acceptable because there are fewer string head pointers versus chained pointers within nodes as for linked containers.
     194
     195\begin{figure}
     196\includegraphics{memmgr-basic}
     197\caption{String memory-management data structures}
     198\label{f:memmgr-basic}
     199\end{figure}
     200
     201\VRef[Figure]{f:memmgr-basic} shows the representation.
     202A heap header and its text buffer, defines a sharing context.
     203Normally, one global sharing context is appropriate for an entire program;
     204exceptions are discussed in [xref TBD].
     205A string is a handle into the buffer and linked into a list.
     206The list is doubly linked for $O(1)$ insertion and removal at any location.
     207Strings are orders n the list by text-buffer address, where there is no overlapping, and approximately, where there is.
     208The header maintains a next-allocation pointer, @alloc@, pointing to the last live allocation of the buffer.
     209No external references point into the buffer and the management procedure relocates the text allocations as needed.
     210A string handle contains an explicit string, while its string is contiguous and not null terminated.
     211The length sets an upper limit on the string size, but is large (4 or 8 bytes).
     212String handles can be allocated in the stack or heap, while the text buffer is large enough with good management so that only one dynamic allocation is necessary for it during program execution.
     213During this period strings can vary in size dynamically.
     214
     215When the text buffer fills, \ie the next new string allocation causes @alloc@ to point beyond the end of the buffer, the strings are compacted.
     216The linked handles define all live strings in the buffer, which indirectly defines the allocated and free space in the buffer.
     217Since the string handles are in (roughly) sorted order, the handle list can be traversed copying the first text to the start of the buffer and subsequent strings after each over.
     218After compaction, if the amount of free storage is still less than the new string allocation, a larger text buffer is heap allocated, the current buffer is copies into the new buffer, and the original buffer is freed.
     219Note, the list of string handles is unaffected during a compaction;
     220only the string pointers are modified to new buffer locations.
     221
     222Object lifecycle events are the subscription-management triggers in such a service.
     223There are two fundamental string-creation routines: importing external text like a C-string or reading a string, and initialization from an existing \CFA string.
     224When importing, storage comes from the end of the buffer, into which the text is copied.
     225The resultant handle is inserted at the end of the handle list to maintain ordering.
     226When initializing from text already in the text buffer, the new handle is a second reference into the original run of characters.
     227In this case, the new handle's linked-list position is after the original handle.
     228Both string initialization styles preserve the string module's internal invariant that the linked-list order matches the buffer order.
     229For string destruction, handles are removed from the list.
     230
     231Certain string operations can results in a subset (substring) of another string.
     232The resulting handle is then place in the correct sorted position in the list, possible with a short linear search to locate the position.
     233For string operations resulting in a new string, that string is allocated at the end of the buffer.
     234For shared-edit strings, handles that originally referenced containing locations need to see the new value at the new buffer location.
     235These strings are moved to appropriate locations at the end of the list (addressed further in [xref: TBD].
     236For nonshared-edit strings, a containing string can be moved and the nonshared strings can remain in the same position.
     237String assignment words similarly to string initialization, maintain the invariant of linked list order matching buffer order.
     238
     239At the level of the memory manager, these modifications can always be explained as assignments; for example, an append is an assignment into the empty substring at the end.
     240
     241While favourable conditions allow for in-place editing, the general case requires a fresh buffer run.
     242For example, if the new value does not fit in the old place, or if other handles are still using the old value, then the new value will use a fresh buffer run.
    65243
    66244where there is room for the resulting value in the original buffer location, and where all handles referring to the original buffer location should see the new value,
    67245
    68 
    69 always boiled down to assignment and appendment.  Assignment has special cases that happen in-place, but in the general case, it is implemented as a sequence of appends onto a fresh allocation at the end of the buffer.  (The sequence has multiple steps when the assignment target is a substring: old before, new middle, old after.)  Similarly, an append request can be serviced in-place when there is room, or as a pair of appends
    70 
     246always boiled down to assignment and appendment.
     247Assignment has special cases that happen in-place, but in the general case, it is implemented as a sequence of appends onto a fresh allocation at the end of the buffer.
     248(The sequence has multiple steps when the assignment target is a substring: old before, new middle, old after.)
     249Similarly, an append request can be serviced in-place when there is room, or as a pair of appends.
    71250
    72251
    73252\subsection{Sharing implementation}
    74253
    75 The \CFA string module has two manners in which serveral string handles can share an unerlying run of characters. 
    76 
    77 The first type of sharing is user-requested, following the [xref Logical Overlap].  Here, the user requests, explicitly, that both handles be views of the same logical, modifiable string.  This state is typically prodecd by the substring operation.  In a typical substring call, the source string-handle is referencing an entire string, and the resluting, newly made, string handle is referencing a portion of the orignal.  In this state, a subsequent modification made by either is visible in both.
    78 
    79 The second type of sharing happens when the system implicitly delays the physical execution of a logical \emph{copy} operation, as part of its copy-on-write optimization.  This state is typically produced by constructing a new string, using an original string as its intialization source.  In this state, a subsequent modification done on one handle triggers the deferred copy action, leaving the handles referencing different runs within the buffer, holding distinct values.
     254The \CFA string module has two manners in which several string handles can share an underlying run of characters. 
     255
     256The first type of sharing is user-requested, following the [xref Logical Overlap].  Here, the user requests, explicitly, that both handles be views of the same logical, modifiable string.  This state is typically produced by the substring operation.  In a typical substring call, the source string-handle is referencing an entire string, and the resulting, newly made, string handle is referencing a portion of the original.  In this state, a subsequent modification made by either is visible in both.
     257
     258The second type of sharing happens when the system implicitly delays the physical execution of a logical \emph{copy} operation, as part of its copy-on-write optimization.  This state is typically produced by constructing a new string, using an original string as its initialization source.  In this state, a subsequent modification done on one handle triggers the deferred copy action, leaving the handles referencing different runs within the buffer, holding distinct values.
    80259
    81260A further abstraction, in the string module's implementation, helps distinguish the two senses of sharing.  A share-edit set (SES) is an equivalence class over string handles, being the reflexive, symmetric and transitive closure of the relationship of one being constructed from the other, with the ``share edits'' opt-in given.  It is represented by a second linked list among the handles.  A string that shares edits with no other is in a SES by itself.  Inside a SES, a logical modification of one substring portion may change the logical value in another, depending on whether the two actually overlap.  Conversely, no logical value change can flow outside of a SES.  Even if a modification on one string handle does not reveal itself \emph{logically} to anther handle in the same SES (because they don't overlap), if the modification is length-changing, completing the modification requires visiting the second handle to adjust its location in the sliding text.
     
    84263\subsection{Avoiding implicit sharing}
    85264
    86 There are tradeoffs associated with the copy-on-write mechanism.  Several quatitative matters are detailed in the [xref: Performance Assessment] section and the qualitiative issue of multi-threaded support is introduced here.  The \CFA sting library provides a switch to disable the sharing mechanism for situtations where it is inappropriate.
     265There are tradeoffs associated with the copy-on-write mechanism.  Several qualitative matters are detailed in the [xref: Performance Assessment] section and the qualitative issue of multi-threaded support is introduced here.  The \CFA sting library provides a switch to disable the sharing mechanism for situations where it is inappropriate.
    87266
    88267Because of the inter-linked string handles, any participant managing one string is also managing, directly, the neighbouring strings, and from there, a data structure of the ``set of all strings.''  This data structure is intended for sequential access.  A negative consequence of this decision is that multiple threads using strings need to be set up so that they avoid attempting to modify (concurrently) an instance of this structure.  A positive consequence is that a single-threaded program, or a program with several independent threads, can use the sharing context without an overhead from locking.
    89268
    90 The \CFA sting library provides the @string_sharectx@ type to control an ambient sharing context for the current thread.  It allows two adjustments: to opt out of sharing entirely, or to begin sharing within a private context.  Either way, the chosen mode applies to the current thread, for the duration of the lifetime of the created  @string_sharectx@ object, up to being suspended by child liftimes of different contexts.  The indended use is with stack-managed lifetimes, in which the established context lasts until the current function returns, and affects all functions called that don't create their own contexts.
    91 \lstinputlisting[language=CFA, firstline=20, lastline=34]{sharectx-demo.cfa}
     269The \CFA sting library provides the @string_sharectx@ type to control an ambient sharing context for the current thread.  It allows two adjustments: to opt out of sharing entirely, or to begin sharing within a private context.  Either way, the chosen mode applies to the current thread, for the duration of the lifetime of the created  @string_sharectx@ object, up to being suspended by child lifetimes of different contexts.  The indented use is with stack-managed lifetimes, in which the established context lasts until the current function returns, and affects all functions called that don't create their own contexts.
     270\lstinputlisting[language=CFA, firstline=20, lastline=34]{sharectx.run.cfa}
    92271In this example, the single-letter functions are called in alphabetic order.  The functions @a@ and @d@ share string character ranges within themselves, but not with each other.  The functions @b@, @c@ and @e@ never share anything.
    93272
    94273[ TODO: true up with ``is thread local'' (implement that and expand this discussion to give a concurrent example, or adjust this wording) ]
    95274
    96 When the string library is running with sharing disabled, it runs without implicit thread-safety challenges (which same as the STL) and with performance goals similar to the STL's.  This thread-safety quality means concurrent users of one string object must still bring their own mutual exlusion, but the string libary will not add any cross thread uses that were not apparent in the user's code.
     275When the string library is running with sharing disabled, it runs without implicit thread-safety challenges (which same as the STL) and with performance goals similar to the STL's.  This thread-safety quality means concurrent users of one string object must still bring their own mutual exclusion, but the string library will not add any cross thread uses that were not apparent in the user's code.
    97276
    98277Running with sharing disabled can be thought of as STL-emulation mode.
     
    107286
    108287
    109 \subsection{Performance assessment}
    110 
    111 I assessed the CFA string library's speed and memory usage.  I present these results ineven quivalent cases, due to either micro-optimizations foregone, or fundamental costs of the added functionality.  They also show the benefits and tradeoffs, as >100\% effects, of switching to CFA, with the tradeoff points quantified.  The final test shows the overall win of the CFA text-sharing mechanism.  It exercises several operations together, showing CFA enabling clean user code to achieve performance that STL requires less-clean user code to achieve.
    112 
    113 To discuss: general goal of ... while STL makes you think about memory management, all the time, and if you do your performance can be great ... CFA sacrifices this advantage modestly in exchange for big wins when you're not thinking about memory mamangement.  [Does this position cover all of it?]
     288\section{Performance assessment}
     289\label{s:PerformanceAssessment}
     290
     291I assessed the \CFA string library's speed and memory usage.  I present these results in even equivalent cases, due to either micro-optimizations foregone, or fundamental costs of the added functionality.  They also show the benefits and tradeoffs, as >100\% effects, of switching to \CFA, with the tradeoff points quantified.  The final test shows the overall win of the \CFA text-sharing mechanism.  It exercises several operations together, showing \CFA enabling clean user code to achieve performance that STL requires less-clean user code to achieve.
     292
     293To discuss: general goal of ... while STL makes you think about memory management, all the time, and if you do your performance can be great ... \CFA sacrifices this advantage modestly in exchange for big wins when you're not thinking about memory management.  [Does this position cover all of it?]
    114294
    115295To discuss: revisit HL v LL APIs
    116296
    117 To discuss: revisit nosharing as STL emulation modes
     297To discuss: revisit no-sharing as STL emulation modes
    118298
    119299These tests use randomly generated text fragments of varying lengths.  A collection of such fragments is a \emph{corpus}.  The mean length of a fragment from corpus is a typical explanatory variable.  Such a length is used in one of three modes:
     
    121301    \item [Fixed-size] means all string fragments are of the stated size
    122302    \item [Varying from 1] means string lengths are drawn from a geometric distribution with the stated mean, and all lengths occur
    123     \item [Varying from 16] means string lengths are drawn from a geometric distribution with the stated mean, but only lengths 16 and obove occur; thus, the stated mean will be above 16.
     303    \item [Varying from 16] means string lengths are drawn from a geometric distribution with the stated mean, but only lengths 16 and above occur; thus, the stated mean will be above 16.
    124304\end{description}
    125 The geometric distribution implies that lengths much longer than the mean occur frequently.  The special treatment of length 16 deals with comparison to STL, given that STL has short-string optimization (see [todo: write and cross-ref future-work SSO]), currently not implmented in \CFA.  When success notwithstanding SSO is illustrated, a fixed-size or from-16 distribution ensures that extra-optimized cases are not part of the mix on the STL side.  In all experiments that use a corpus, its text is generated and loaded into the SUT before the timed phase begins.
     305The geometric distribution implies that lengths much longer than the mean occur frequently.  The special treatment of length 16 deals with comparison to STL, given that STL has short-string optimization (see [TODO: write and cross-ref future-work SSO]), currently not implemented in \CFA.  When success notwithstanding SSO is illustrated, a fixed-size or from-16 distribution ensures that extra-optimized cases are not part of the mix on the STL side.  In all experiments that use a corpus, its text is generated and loaded into the SUT before the timed phase begins.
    126306
    127307To discuss: vocabulary for reused case variables
     
    136316\subsubsection{Test: Append}
    137317
    138 This test measures the speed of appending fragments of text onto a growing string.  Its subcases include both CFA being similar to STL, and their designs offering a tradeoff.
    139 
    140 One experimental variable is the user's operation being @a = a + b@ vs. @a += b@.  While experienced programmers expect the latter to be ``what you obviously should do,'' controling the penatly of the former both helps the API be accessible to beginners and also helps offer confidence that when a user tries to compose operations, the forms that are most natural to the user's composition are viable.
    141 
    142 Another experimental variable is whether the user's logical allocation is fresh vs reused.  Here, \emph{reusing a logical allocation}, means that the prgram variable, into which the user is concatenating, previously held a long string:\\
     318This test measures the speed of appending fragments of text onto a growing string.  Its subcases include both \CFA being similar to STL, and their designs offering a tradeoff.
     319
     320One experimental variable is the user's operation being @a = a + b@ vs. @a += b@.  While experienced programmers expect the latter to be ``what you obviously should do,'' controlling the penalty of the former both helps the API be accessible to beginners and also helps offer confidence that when a user tries to compose operations, the forms that are most natural to the user's composition are viable.
     321
     322Another experimental variable is whether the user's logical allocation is fresh vs reused.  Here, \emph{reusing a logical allocation}, means that the program variable, into which the user is concatenating, previously held a long string:\\
    143323\begin{tabular}{ll}
    144324    Logical allocation fresh                   & Logical allocation reused                  \\
     
    150330    @ } @                                      & @ } @
    151331\end{tabular}\\
    152 These benchmark drivers have an outer loop for ``until a sample-worthy amount of execution has happened'' and an inner loop for ``build up the desired-length string.''  It is sensible to doubt that a user should have to care about this difference, yet the STL performs differently in these cases.  Concretly, both cases incur the cost of copying characters into the target string, but only the allocation-fresh case incurs a further reallocation cost, which is generally paid at points of doubling the length.  For the STL, this cost includes obtaining a fresh buffer from the memory allocator and copying older characters into the new buffer, while CFA-sharing hides such a cost entirely.  The reuse-vs-fresh distinction is only relevant in the currrent \emph{append} tests.
    153 
    154 The \emph{append} tests use the varying-from-1 corpus construction; that is they do not assume away the STL's advantage from small-string opitimization.
    155 
    156 To discuss: any other case variables intruduced in the performance intro
     332These benchmark drivers have an outer loop for ``until a sample-worthy amount of execution has happened'' and an inner loop for ``build up the desired-length string.''  It is sensible to doubt that a user should have to care about this difference, yet the STL performs differently in these cases.  Concretely, both cases incur the cost of copying characters into the target string, but only the allocation-fresh case incurs a further reallocation cost, which is generally paid at points of doubling the length.  For the STL, this cost includes obtaining a fresh buffer from the memory allocator and copying older characters into the new buffer, while \CFA-sharing hides such a cost entirely.  The reuse-vs-fresh distinction is only relevant in the current \emph{append} tests.
     333
     334The \emph{append} tests use the varying-from-1 corpus construction; that is they do not assume away the STL's advantage from small-string optimization.
     335
     336To discuss: any other case variables introduced in the performance intro
    157337
    158338\begin{figure}
     
    162342\end{figure}
    163343
    164 Figure \ref{fig:string-graph-peq-cppemu} shows this behaviour, by the STL and by \CFA in STL emulation mode.  \CFA reproduces STL's performance, up to a 15\% penalty averaged over the cases shown, diminishing with larger strings, and 50\% in the worst case.  This penatly characterizes the amount of implementation fine tuning done with STL and not done with \CFA in present state.  The larger inherent penalty, for a user mismanaging reuse, is 40\% averaged over the cases shown, is minimally 24\%, shows up consistently between the STL and \CFA implementations, and increases with larger strings.
     344Figure \ref{fig:string-graph-peq-cppemu} shows this behaviour, by the STL and by \CFA in STL emulation mode.  \CFA reproduces STL's performance, up to a 15\% penalty averaged over the cases shown, diminishing with larger strings, and 50\% in the worst case.  This penalty characterizes the amount of implementation fine tuning done with STL and not done with \CFA in present state.  The larger inherent penalty, for a user mismanaging reuse, is 40\% averaged over the cases shown, is minimally 24\%, shows up consistently between the STL and \CFA implementations, and increases with larger strings.
    165345
    166346\begin{figure}
     
    170350\end{figure}
    171351
    172 In sharing mode, \CFA makes the fresh/reuse difference disappear, as shown in Figure \ref{fig:string-graph-peq-sharing}.  At append lengths 5 and above, CFA not only splits the two baseline STL cases, but its slowdown of 16\% over (STL with user-managed reuse) is close to the \CFA-v-STL implementation difference seen with \CFA in STL-emulation mode.
     352In sharing mode, \CFA makes the fresh/reuse difference disappear, as shown in Figure \ref{fig:string-graph-peq-sharing}.  At append lengths 5 and above, \CFA not only splits the two baseline STL cases, but its slowdown of 16\% over (STL with user-managed reuse) is close to the \CFA-v-STL implementation difference seen with \CFA in STL-emulation mode.
    173353
    174354\begin{figure}
     
    178358\end{figure}
    179359
    180 When the user takes a further step beyond the STL's optimal zone, by running @x = x + y@, as in Figure \ref{fig:string-graph-pta-sharing}, the STL's penalty is above $15 \times$ while CFA's (with sharing) is under $2 \times$, averaged across the cases shown here.  Moreover, the STL's gap increases with string size, while \CFA's converges.
     360When the user takes a further step beyond the STL's optimal zone, by running @x = x + y@, as in Figure \ref{fig:string-graph-pta-sharing}, the STL's penalty is above $15 \times$ while \CFA's (with sharing) is under $2 \times$, averaged across the cases shown here.  Moreover, the STL's gap increases with string size, while \CFA's converges.
    181361
    182362\subsubsection{Test: Pass argument}
     
    184364To have introduced:  STL string library forces users to think about memory management when communicating values across a function call
    185365
    186 STL charges a prohibitive penalty for passing a string by value.  With implicit sharing active, \CFA treats this operation as normal and supported.  This test illustrates a main adjantage of the \CFA sharing algorithm.  It also has a case in which STL's small-string optimization provides a successful mitigation.
     366STL charges a prohibitive penalty for passing a string by value.  With implicit sharing active, \CFA treats this operation as normal and supported.  This test illustrates a main advantage of the \CFA sharing algorithm.  It also has a case in which STL's small-string optimization provides a successful mitigation.
    187367
    188368\begin{figure}
     
    201381This test directly compares the allocation schemes of the \CFA string with sharing, compared with the STL string.  It treats the \CFA scheme as a form of garbage collection, and the STL scheme as an application of malloc-free.  The test shows that \CFA enables faster speed at a cost in memory usage.
    202382
    203 A garbage collector, afforded the freedom of managed memory, often runs faster than malloc-free (in an ammortized analysis, even though it must occasionally stop to collect) because it is able to use its collection time to move objects.  (In the case of the mini-allocator powering the \CFA string library, objects are runs of text.)  Moving objects lets fresh allocations consume from a large contiguous store of available memory; the ``bump pointer'' book-keeping for such a scheme is very light.  A malloc-free implementation without the freedom to move objects must, in the general case, allocate in the spaces between existing objects; doing so entails the heavier book-keeping to navigate and maintain a linked structure.
     383A garbage collector, afforded the freedom of managed memory, often runs faster than malloc-free (in an amortized analysis, even though it must occasionally stop to collect) because it is able to use its collection time to move objects.  (In the case of the mini-allocator powering the \CFA string library, objects are runs of text.)  Moving objects lets fresh allocations consume from a large contiguous store of available memory; the ``bump pointer'' book-keeping for such a scheme is very light.  A malloc-free implementation without the freedom to move objects must, in the general case, allocate in the spaces between existing objects; doing so entails the heavier book-keeping to navigate and maintain a linked structure.
    204384
    205385A garbage collector keeps allocations around for longer than the using program can reach them.  By contrast, a program using malloc-free (correctly) releases allocations exactly when they are no longer reachable.  Therefore, the same harness will use more memory while running under garbage collection.  A garbage collector can minimize the memory overhead by searching for these dead allocations aggressively, that is, by collecting more often.  Tuned in this way, it spends a lot of time collecting, easily so much as to overwhelm its speed advantage from bump-pointer allocation.  If it is tuned to collect rarely, then it leaves a lot of garbage allocated (waiting to be collected) but gains the advantage of little time spent doing collection.
     
    215395\begin{figure}
    216396    \includegraphics[width=\textwidth]{string-graph-allocn.png}
    217     \caption{Space and time performance, under varying fraction-live targets, for the five string lengths shown, at (emph{Fixed-size} corpus construction.  [MISSING] The identified clusters are for the default fraction-live target, which is 30\%.  MISSING: STL results, typically just below the 0.5--0.9 CFA segment.  All runs keep an average of 836 strings live, and the median string lifetime is ?? allocations.}
     397    \caption{Space and time performance, under varying fraction-live targets, for the five string lengths shown, at (\emph{Fixed-size} corpus construction.  [MISSING] The identified clusters are for the default fraction-live target, which is 30\%.  MISSING: STL results, typically just below the 0.5--0.9 \CFA segment.  All runs keep an average of 836 strings live, and the median string lifetime is ?? allocations.}
    218398    \label{fig:string-graph-allocn}
    219399\end{figure}
    220400
    221 Figure \ref{fig:string-graph-allocn} shows the results of this experiemnt.  At all string sizes, varying the liveness threshold gives offers speed-for-space tradeoffs relative to STL.  At the default liveness threshold, all measured string sizes see a ??\%--??\% speedup for a ??\%--??\% increase in memory footprint.
     401Figure \ref{fig:string-graph-allocn} shows the results of this experiment.  At all string sizes, varying the liveness threshold gives offers speed-for-space tradeoffs relative to STL.  At the default liveness threshold, all measured string sizes see a ??\%--??\% speedup for a ??\%--??\% increase in memory footprint.
    222402
    223403
     
    225405\subsubsection{Test: Normalize}
    226406
    227 This test is more applied than the earlier ones.  It combines the effects of several operations.  It also demonstrates a case of the CFA API allowing user code to perform well, while being written without overt memory management, while achieving similar performance in STL requires adding memory-management complexity.
     407This test is more applied than the earlier ones.  It combines the effects of several operations.  It also demonstrates a case of the \CFA API allowing user code to perform well, while being written without overt memory management, while achieving similar performance in STL requires adding memory-management complexity.
    228408
    229409To motivate: edits being rare
  • doc/theses/mike_brooks_MMath/uw-ethesis.bib

    rb006c51e r10a9479d  
    124124}
    125125
    126 
    127126@misc{Mendio24,
    128127    contributer = {pabuhr@plg},
     
    132131    howpublished= {\url{https://www.mend.io/most-secure-programming-languages}},
    133132}
     133
     134@misc{RVO20,
     135    contributer = {pabuhr@plg},
     136    title       = {Return value optimization ({RVO})},
     137    author      = {Special Interest Group on {C++}},
     138    year        = 2020,
     139    month       = jun,
     140    howpublished= {\url{https://sigcpp.github.io/2020/06/08/return-value-optimization}},
     141}
  • doc/theses/mike_brooks_MMath/uw-ethesis.tex

    rb006c51e r10a9479d  
    102102\input{common}
    103103%\usepackage{common}
     104
    104105\CFAStyle                                               % CFA code-style
    105106\lstset{language=cfa,belowskip=-1pt} % set default language to CFA
     
    108109\lstnewenvironment{java}[1][]{\lstset{language=java,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}
    109110\lstset{inputpath={programs}}
     111\lstset{xleftmargin=1\parindentlnth}
    110112
    111113\newcommand{\uCpp}{$\mu$\CC}
  • doc/uC++toCFA/.gitignore

    rb006c51e r10a9479d  
    33*.pdf
    44*.ps
     5*.cc
     6*.cfa
  • doc/uC++toCFA/uC++toCFA.tex

    rb006c51e r10a9479d  
    1111%% Created On       : Wed Apr  6 14:53:29 2016
    1212%% Last Modified By : Peter A. Buhr
    13 %% Last Modified On : Tue Oct 22 17:45:48 2024
    14 %% Update Count     : 6068
     13%% Last Modified On : Fri Nov 15 09:55:34 2024
     14%% Update Count     : 6249
    1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    1616
     
    357357
    358358
     359\section{Constructor / Destructor}
     360
     361\begin{cquote}
     362\begin{tabular}{@{}l|l@{}}
     363\begin{uC++}
     364
     365struct S {
     366        int i, j;
     367
     368        @S@( int i, int j ) { S::i = i; S::j = j; }
     369        @~S@() {}
     370};
     371S s1 = { 1, 2 };
     372
     373S * s2 = new S{ 1, 2 };
     374delete s2;
     375s2 = new S{ 1, 2 };
     376delete s2;
     377S & s3 = *new S{ 1, 2 };
     378delete &s3;
     379s3 = *new S{ 1, 2 };
     380delete &s3;
     381\end{uC++}
     382&
     383\begin{cfa}
     384#include <stdlib.hfa> // new (malloc)
     385struct S {
     386        int i, j;
     387};
     388void @?{}@( S & s, int i, int j ) { s.i = i; s.j = j; }
     389void @^?{}@( S & s ) { s.i = 0; s.j = 0; }     
     390
     391S s1 = { 1, 2 };
     392// cannot use 0/1 (zero_t/one_t) with "new"
     393S * s2 = new( 1@n@, 2 ); // n => (int)
     394delete( s2 );
     395s2 = new( 1n, 2 );
     396delete( s2 );
     397S & s3 = *new( 1n, 2 );
     398delete( s3 );
     399&s3 = &*new( 1n, 2 );
     400delete( s3 );
     401\end{cfa}
     402\end{tabular}
     403\end{cquote}
     404
     405
    359406\section{\texorpdfstring{Structures (object-oriented \protect\vs routine style)}{Structures (object-oriented vs. routine style)}}
    360407
     
    381428setter( @s,@ 3 );  // normal calls
    382429int k = getter( @s@ );
    383 \end{cfa}
    384 \end{tabular}
    385 \end{cquote}
    386 
    387 
    388 \section{Constructor / Destructor}
    389 
    390 \begin{cquote}
    391 \begin{tabular}{@{}l|l@{}}
    392 \begin{uC++}
    393 
    394 struct S {
    395         int i, j;
    396         S( int i, int j ) { S::i = i; S::j = j; }
    397         ~S() {}
    398 };
    399 S s = { 1, 2 }, s2{ 1, 2 };
    400 S * s3 = new S{ 1, 2 };
    401 S & s4 = *new S{ 1, 2 };
    402 \end{uC++}
    403 &
    404 \begin{cfa}
    405 #include <stdlib.hfa> // malloc
    406 struct S {
    407         int i, j;
    408 };
    409 void ?{}( S & s, int i, int j ) { s.[i, j] = [i, j]; }
    410 void ^?{}( S & s ) {}
    411 S s = { 1, 2 }, s2{ 1, 2 };
    412 S * s3 = &(*malloc()){ 1, 2 };
    413 S & s4 = (*malloc()){ 1, 2 }; // fails
    414430\end{cfa}
    415431\end{tabular}
     
    498514
    499515
    500 \section{Coroutines}
     516\section{Coroutine}
    501517
    502518\begin{cquote}
     
    504520\begin{uC++}
    505521
    506 _Coroutine C {
     522@_Coroutine@ C {
    507523        // private coroutine fields
    508524        void main() {
    509                 ... suspend(); ...
    510                 ... _Resume E( ... ) _At partner;
    511                 ... uThisCoroutine(); ...
     525                ... @suspend();@ ...
     526                ... @_Resume E( ... ) _At partner;@
     527                ... @uThisCoroutine();@ ...
    512528
    513529        }
    514530  public:
    515531        void mem( ... ) {
    516                 ... resume() ...
     532                ... @resume();@ ...
    517533        }
    518534};
     
    521537\begin{cfa}
    522538#include <$coroutine$.hfa>
    523 coroutine C {
     539@coroutine@ C {
    524540        // private coroutine fields
    525541
    526542};
    527543void main( C & c ) {
    528         ... suspend; ... // keyword not routine
    529         ... resumeAt( partner, ExceptionInst( E, ... ) );
    530         ... active_coroutine(); ...
     544        ... @suspend;@ ... // keyword not routine
     545        ... @resumeAt( partner, ExceptionInst( E, ... ) );@
     546        ... @active_coroutine();@ ...
    531547}
    532548void mem( C & c, ... ) {
    533         ... resume( c ); ...
     549        ... @resume( c );@ ...
    534550}
    535551\end{cfa}
     
    540556
    541557
     558\section{Thread}
     559
     560\begin{cquote}
     561\begin{tabular}{@{}l|ll@{}}
     562\begin{uC++}
     563
     564@_Task@ T {
     565        // private task fields
     566        void main() {
     567                ... @_Resume E( ... ) _At partner@;
     568                ... @uThisTask();@ ...
     569        }
     570  public:
     571};
     572\end{uC++}
     573&
     574\begin{cfa}
     575#include <$thread$.hfa>
     576@thread@ T {
     577        // private task fields
     578
     579};
     580void main( @T & t@ ) {
     581        ... @resumeAt( partner, ExceptionInst( E, ... )@ );
     582        ... @active_thread();@ ...
     583}
     584\end{cfa}
     585\\
     586\multicolumn{2}{@{}l@{}}{\lstinline{T t; // start thread in main routine}}
     587\end{tabular}
     588\end{cquote}
     589
     590
    542591\section{\lstinline{COBEGIN}/\lstinline{COFOR}}
    543592
     
    548597#include <uCobegin.h>
    549598int main() {
    550         COBEGIN
     599        @COBEGIN@
    551600                BEGIN osacquire( cout ) << "A" << endl; END
    552601                BEGIN osacquire( cout ) << "B" << endl; END
     
    554603                BEGIN osacquire( cout ) << "D" << endl; END
    555604                BEGIN osacquire( cout ) << "E" << endl; END
    556         COEND
    557         COFOR( i, 1, 10,
     605        @COEND@
     606        @COFOR@( i, 1, 10,
    558607                osacquire( cout ) << i << endl;
    559608        )
     
    566615int main() {
    567616        {
    568                 corun { mutex( sout ) sout | "A"; }
     617                @corun@ { mutex( sout ) sout | "A"; }
    569618                corun { mutex( sout ) sout | "B"; }
    570619                corun { mutex( sout ) sout | "C"; }
     
    572621                corun { mutex( sout ) sout | "E"; }
    573622        }
    574         cofor( i; 10 ) {
     623        @cofor@( i; 10 ) {
    575624                mutex( sout ) sout | i;
    576625    }
     
    591640
    592641struct StrMsg : @public uActor::Message@ {
     642
    593643        const char * val; // string message
    594 
    595644
    596645        StrMsg( const char * val ) :
     
    600649_Actor Hello { ${\color{red}\LstCommentStyle{// : public uActor}}$
    601650        Allocation receive( Message & msg ) {
    602                 Case( StrMsg, msg ) { // discriminate
     651                Case( @StartMsg@, msg ) { // discriminate
     652
     653                } else Case( StrMsg, msg ) {
    603654                        osacquire( cout ) << msg_d->val << endl;
    604                 };
    605                 return Delete;  // delete after use
     655
     656                } else Case( @StopMsg@, msg )
     657                        return Delete;  // delete actor
     658                return Nodelete;  // reuse actor
    606659        }
    607660};
    608661int main() {
    609662        @uActor::start();@ // start actor system
    610         *new Hello() | *new StrMsg( "hello" );
    611         *new Hello() | *new StrMsg( "bonjour" );
    612         @uActor::stop();@  // wait for all actors to terminate
     663        *new Hello() | uActor::startMsg
     664                | *new StrMsg( "hello" ) | uActor::stopMsg;
     665        *new Hello() | uActor::startMsg
     666                | *new StrMsg( "bonjour" ) | uActor::stopMsg;
     667        @uActor::stop();@  // wait for actors to terminate
    613668}
    614669\end{uC++}
     
    623678        const char * val; // string message
    624679};
    625 void ?{}( StrMsg & msg, char * str ) {
     680void ?{}( StrMsg & msg, const char * str ) {
     681        @set_allocation( msg, Delete );@ // delete after use
    626682        msg.val = str;
    627         @set_allocation( msg, Delete );@ // delete after use
    628 }
    629 struct Hello {
    630         @inline actor;@ // derived actor
    631 };
     683}
     684struct Hello { @inline actor;@ }; // derived actor
     685allocation receive( Hello & receiver, @start_msg_t@ & ) {
     686        return Nodelete;
     687}
    632688allocation receive( Hello & receiver, StrMsg & msg ) {
    633689        mutex( sout ) sout | msg.val;
    634         return Delete;  // delete after use
     690        return Nodelete;  // reuse actor
     691}
     692allocation receive( Hello & receiver, @stop_msg_t@ & ) {
     693        return Delete;  // delete actor
    635694}
    636695
    637696int main() {
    638         @start_actor_system();@  // start actor system
    639         *(Hello *)new() | *(StrMsg *)new( "hello" );
    640         *(Hello *)new() | *(StrMsg *)new( "bonjour" );
    641         @stop_actor_system();@  // wait for all actors to terminate
    642 }
    643 \end{cfa}
    644 \end{tabular}
    645 \end{cquote}
    646 
    647 
    648 \section{Threads}
    649 
    650 \begin{cquote}
    651 \begin{tabular}{@{}l|ll@{}}
    652 \begin{uC++}
    653 
    654 @_Task@ T {
    655         // private task fields
    656         void main() {
    657                 ... _Resume E( ... ) _At partner;
    658                 ... uThisTask(); ...
    659         }
    660   public:
    661 };
    662 \end{uC++}
    663 &
    664 \begin{cfa}
    665 #include <$thread$.hfa>
    666 @thread@ T {
    667         // private task fields
    668 
    669 };
    670 void main( @T & t@ ) {
    671         ... resumeAt( partner, ExceptionInst( E, ... ) );
    672         ... active_thread(); ...
    673 }
    674 \end{cfa}
    675 \\
    676 \multicolumn{2}{@{}l@{}}{\lstinline{T t; // start thread in main routine}}
     697        @actor_start();@  // start actor system
     698        *(Hello *)new() | start_msg
     699                | *(StrMsg *)new( "hello" ) | stop_msg;
     700        *(Hello *)new() | start_msg
     701                | *(StrMsg *)new( "bonjour" ) | stop_msg;
     702        @actor_stop();@  // wait for actors to terminate
     703}
     704\end{cfa}
    677705\end{tabular}
    678706\end{cquote}
     
    710738
    711739
    712 \section{Monitors}
     740\section{Barrier}
    713741
    714742\begin{cquote}
    715743\begin{tabular}{@{}l|ll@{}}
    716744\begin{uC++}
    717 
    718 @_Monitor@ M {
    719         @uCondition@ c;
    720         bool avail = true;
     745#include <iostream>
     746using namespace std;
     747#include <uBarrier.h>
     748
     749@_Cormonitor@ Barrier
     750                : @public uBarrier@ { // inheritance
     751        int total;
     752        void @last@() { cout << total << endl; }
    721753  public:
    722 
    723         void rtn() {
    724                 if ( ! avail ) c.wait();
    725                 else avail = false;
     754        Barrier( unsigned int group ) :
     755                        @uBarrier( group )@ {
     756                total = 0;
     757        }
     758        void @block@( int subtotal ) {
     759
     760
     761                total += subtotal;
     762                @uBarrier::block();@
     763        }
     764};
     765enum { N = 3 };
     766Barrier b{ N };
     767
     768_Task T {
     769        void main() {
     770                for ( int i = 0; i < 10; i += 1 ) {
     771                        b.block( 1 );
     772                }
     773        }
     774};
     775int main() {
     776        uProcessor p[N - 1];
     777        T t[N];
     778}
     779\end{uC++}
     780&
     781\begin{cfa}
     782#include <fstream.hfa>
     783#include <$thread$.hfa>
     784#include <barrier.hfa>
     785#include <mutex_stmt.hfa>
     786struct Barrier {
     787        @barrier b;@                    // containment
     788        int total;
     789
     790};
     791void ?{}( Barrier & B, unsigned int group ) with(B) {
     792        @?{}( b, group );@              // initialize barrier
     793        total = 0;
     794}
     795unsigned int block( Barrier & B, int subtotal ) with(B) {
     796        void @last@() { sout | total; } // called by Gth arriving thread
     797        @mutex( b )@ {  // use barrier's mutual exclusion
     798                total += subtotal;
     799                return @block@( b, last ); // wait for barrier trigger
     800        }
     801}
     802enum { N = 3 };
     803Barrier b{ N };
     804
     805thread T {};
     806void main( T & ) {
     807        for ( 10 ) {
     808                block( b, 1 );
     809        }
     810}
     811
     812int main() {
     813        processor p[N - 1];
     814        T t[N];
     815}
     816\end{cfa}
     817\end{tabular}
     818\end{cquote}
     819
     820\newpage
     821
     822\section{Monitor}
     823
     824Internal Scheduling
     825\begin{cquote}
     826\begin{tabular}{@{}l|ll@{}}
     827\begin{uC++}
     828
     829@_Monitor@ BoundedBufferI {
     830        @uCondition@ full, empty;
     831        int front = 0, back = 0, count = 0;
     832        int elements[20];
     833  public:
     834
     835
     836
     837        @_Nomutex@ int query() const { return count; }
     838
     839        void insert( int elem ) {
     840                if ( count == 20 ) @empty.wait();@
     841                elements[back] = elem;
     842                back = ( back + 1 ) % 20;
     843                count += 1;
     844                @full.signal();@
     845        }
     846        int remove() {
     847                if ( count == 0 ) @full.wait();@
     848                int elem = elements[front];
     849                front = ( front + 1 ) % 20;
     850                count -= 1;
     851                @empty.signal();@
     852                return elem;
    726853        }
    727854};
     
    730857\begin{cfa}
    731858#include <$monitor$.hfa>
    732 @monitor@ M {
    733         @condition@ c;
    734         bool avail;
    735 };
    736 void ?{}( M & m ) { m.avail = true; }
    737 void rtn( M & m ) with( m ) {
    738         if ( ! avail ) wait( c );
    739         else avail = false;
    740 }
    741 
    742 \end{cfa}
    743 \\
    744 \multicolumn{2}{@{}l@{}}{\lstinline{M m;}}
     859@monitor@ BoundedBufferI {
     860        @condition@ full, empty;
     861        int front, back, count;
     862        int elements[20];
     863};
     864void ?{}( BoundedBufferI & buf ) with( buf ) {
     865        front = back = count = 0;
     866}
     867int query( BoundedBufferI & buf ) { return buf.count; }
     868int remove( BoundedBufferI & @mutex@ buf ); // forward
     869void insert( BoundedBufferI & @mutex@ buf, int elem ) with( buf ) {
     870        if ( count == 20 ) @wait( empty );@
     871        elements[back] = elem;
     872        back = ( back + 1 ) % 20;
     873        count += 1
     874        @signal( full );@
     875}
     876int remove( BoundedBufferI & @mutex@ buf ) with( buf ) {
     877        if ( count == 0 ) @wait( full );@
     878        int elem = elements[front];
     879        front = ( front + 1 ) % 20;
     880        count -= 1;
     881        @signal( empty );@
     882        return elem;
     883}
     884
     885\end{cfa}
     886\end{tabular}
     887\end{cquote}
     888
     889\enlargethispage{1000pt}
     890
     891\noindent
     892External Scheduling
     893\begin{cquote}
     894\begin{tabular}{@{}l|ll@{}}
     895\begin{uC++}
     896
     897_Monitor BoundedBuffer {
     898        int front = 0, back = 0, count = 0;
     899        int elements[20];
     900  public:
     901        _Nomutex int query() const { return count; }
     902        void insert( int elem );
     903        int remove();
     904};
     905
     906void BoundedBuffer::insert( int elem ) {
     907        if ( count == 20 ) @_Accept( remove );@
     908        elements[back] = elem;
     909        back = ( back + 1 ) % 20;
     910        count += 1;
     911}
     912int BoundedBuffer::remove() {
     913        if ( count == 0 ) @_Accept( insert );@
     914        int elem = elements[front];
     915        front = ( front + 1 ) % 20;
     916        count -= 1;
     917        return elem;
     918}
     919\end{uC++}
     920&
     921\begin{cfa}
     922#include <$monitor$.hfa>
     923monitor BoundedBuffer {
     924        int front, back, count;
     925        int elements[20];
     926};
     927void ?{}( BoundedBuffer & buf ) with( buf ) {
     928        front = back = count = 0;
     929}
     930int query( BoundedBuffer & buf ) { return buf.count; }
     931int remove( BoundedBuffer & @mutex@ buf ); // forward
     932void insert( BoundedBuffer & @mutex@ buf, int elem ) with( buf ) {
     933        if ( count == 20 ) @waitfor( remove : buf );@
     934        elements[back] = elem;
     935        back = ( back + 1 ) % 20;
     936        count += 1;
     937}
     938int remove( BoundedBuffer & @mutex@ buf ) with( buf ) {
     939        if ( count == 0 ) @waitfor( insert : buf );@
     940        int elem = elements[front];
     941        front = ( front + 1 ) % 20;
     942        count -= 1;
     943        return elem;
     944}
     945\end{cfa}
    745946\end{tabular}
    746947\end{cquote}
  • libcfa/prelude/builtins.c

    rb006c51e r10a9479d  
    1010// Created On       : Fri Jul 21 16:21:03 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Feb  2 11:33:56 2023
    13 // Update Count     : 135
     12// Last Modified On : Fri Nov  8 17:07:15 2024
     13// Update Count     : 144
    1414//
    1515
     
    140140        ) \
    141141        typeof(x) op = 1;                                                                       /* accumulate odd product */ \
     142        typeof(x) w = x; /* FIX-ME: possible bug in the box pass changing value argument through parameter */ \
    142143        for ( ; y > 1; y >>= 1 ) {                                                      /* squaring exponentiation, O(log2 y) */ \
    143                 if ( (y & 1) == 1 ) op = op * x;                                /* odd ? */ \
    144                 x = x * x; \
     144                if ( (y & 1) == 1 ) op = op * w;                                /* odd ? */ \
     145                w = w * w; \
    145146        } \
    146         return x * op
     147        return w * op
    147148#define __CFA_EXP_INT__(...) __VA_ARGS__
    148149
  • libcfa/src/concurrency/actor.hfa

    rb006c51e r10a9479d  
    398398// TODO: update globals in this file to be static fields once the static fields project is done
    399399static executor * __actor_executor_ = 0p;
    400 static bool __actor_executor_passed = false;                    // was an executor passed to start_actor_system
     400static bool __actor_executor_passed = false;                    // was an executor passed to actor_start
    401401static size_t __num_actors_ = 0;                                                // number of actor objects in system
    402402static struct thread$ * __actor_executor_thd = 0p;              // used to wake executor after actors finish
     
    410410        // Once an actor is allocated it must be sent a message or the actor system cannot stop. Hence, its receive
    411411        // member must be called to end it
    412         DEBUG_ABORT( __actor_executor_ == 0p, "Creating actor before calling start_actor_system() can cause undefined behaviour.\n" );
     412        DEBUG_ABORT( __actor_executor_ == 0p, "Creating actor before calling actor_start() can cause undefined behaviour.\n" );
    413413        alloc = Nodelete;
    414414        ticket = __get_next_ticket( *__actor_executor_ );
     
    682682}
    683683
    684 static inline void start_actor_system( size_t num_thds ) {
     684static inline void actor_start( size_t num_thds ) {
    685685        __reset_stats();
    686686        __actor_executor_thd = active_thread();
     
    689689}
    690690
    691 static inline void start_actor_system() { start_actor_system( get_proc_count( *active_cluster() ) ); }
    692 
    693 static inline void start_actor_system( executor & this ) {
     691static inline void actor_start() { actor_start( get_proc_count( *active_cluster() ) ); }
     692
     693static inline void actor_start( executor & this ) {
    694694        __reset_stats();
    695695        __actor_executor_thd = active_thread();
     
    698698}
    699699
    700 static inline void stop_actor_system() {
     700static inline void actor_stop() {
    701701        park();                                                                                         // unparked when actor system is finished
    702702
     
    715715struct finished_msg_t { inline message; } finished_msg = __base_msg_finished;
    716716
    717 allocation receive( actor & this, delete_msg_t & msg ) { return Delete; }
    718 allocation receive( actor & this, destroy_msg_t & msg ) { return Destroy; }
    719 allocation receive( actor & this, finished_msg_t & msg ) { return Finished; }
     717allocation receive( actor & this, delete_msg_t & ) { return Delete; }
     718allocation receive( actor & this, destroy_msg_t & ) { return Destroy; }
     719allocation receive( actor & this, finished_msg_t & ) { return Finished; }
    720720
    721721// Default messages used all the time.
    722 //static struct startmsg_t { inline message; } start_msg; // start actor
    723 //static struct stopmsg_t { inline message; } stop_msg; // terminate actor
     722struct start_msg_t { inline message; } start_msg = __base_msg_finished; // start actor
     723struct stop_msg_t { inline message; } stop_msg = __base_msg_finished; // terminate actor
  • libcfa/src/concurrency/barrier.hfa

    rb006c51e r10a9479d  
    1 //
     1//                               -*- Mode: C -*-
     2//
    23// Cforall Version 1.0.0 Copyright (C) 2022 University of Waterloo
    3 //
     4// 
    45// The contents of this file are covered under the licence agreement in the
    56// file "LICENCE" distributed with Cforall.
    67//
    7 // barrier.hfa -- simple barrier implemented from monitors
    8 //
    9 // Author           : Thierry Delisle
    10 // Created On       : Thu Mar 31 16:51:35 2022
    11 // Last Modified By :
    12 // Last Modified On :
    13 // Update Count     :
    14 //
     8// barrier.hfa -- simple barrier implemented using a monitor
     9// 
     10// Author           : Peter A. Buhr
     11// Created On       : Sun Nov 10 08:07:35 2024
     12// Last Modified By : Peter A. Buhr
     13// Last Modified On : Wed Nov 13 12:37:04 2024
     14// Update Count     : 9
     15// 
    1516
    1617#pragma once
     
    1819#include <monitor.hfa>
    1920
    20 // Simple barrier based on a monitor
     21// Plan 9 inheritance does not work with monitors. Two monitor locks are created.
     22
    2123monitor barrier {
    22         // Number of threads blocking needed to unblock the barrier
    23         // Unsigned should be enough, I don't expect use cases with 2^32 thread barriers.
    24         unsigned width;
    25 
    26         // Current count (counting backwards)
    27         unsigned count;
    28 
    29         // Barrier uses internal scheduling
    30         condition c;
     24        unsigned int group, arrivals;                                           // group size, arrival counter
     25        condition c;                                                                            // wait for group to form
    3126};
    3227
    33 // Constructor
    34 void ?{}( barrier & this, unsigned width ) {
    35         this.width = width;
    36         this.count = width; // Count backwards so initialize at width
     28static inline void ?{}( barrier & b, unsigned int group ) {
     29        b.group = b.arrivals = group;                                           // arrivals count backward
    3730}
    3831
    39 // block until the number of threads needed have blocked
    40 // returns an value indicating the reverse order the threads arrived in
    41 // i.e. last thread will return 0 (and not block)
    42 //      second last thread returns 1
    43 //      etc.
    44 // last is an optional hook that will be called by the last thread
    45 // before unblocking the others
    46 static inline unsigned block(barrier & mutex this, fptr_t last = (fptr_t)0 ) {
    47         this.count -= 1; // prefix decrement so we the last is 0 and not 1
    48         unsigned arrival = this.count; // Note arrival order
    49         if(arrival == 0) {
    50                 if(last) last();
    51                 // If arrived last unblock everyone and reset
    52                 signal_all(this.c);
    53                 this.count = this.width;
    54         } else {
    55                 // Otherwise block
    56                 wait(this.c);
    57         }
    58         return arrival; // return arrival order
     32// Returns a value indicating the reverse order the threads arrived, i.e. last thread returns 0 (and does not block)
     33// last is an optional hook that is called by the Gth thread before unblocking the other threads.
     34static inline unsigned int block( barrier & mutex b, fptr_t last = (fptr_t)0 ) with( b ) {
     35        arrivals -= 1;                                                                          // prefix decrement so last is 0 not 1
     36        unsigned arrived = b.arrivals;                                          // note arrival order
     37        if ( arrivals != 0 ) {                                                          // wait for group to form
     38                wait( b.c );
     39        } else {                                                                                        // group formed
     40                if ( last ) last();                                                             // safe to call
     41                signal_all( c );                                                                // unblock group
     42                arrivals = group;                                                               // reset
     43        } // if
     44        return arrived;                                                                         // return arrival order
    5945}
  • libcfa/src/concurrency/monitor.cfa

    rb006c51e r10a9479d  
    1010// Created On       : Thd Feb 23 12:27:26 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Feb 19 17:00:59 2023
    13 // Update Count     : 12
     12// Last Modified On : Thu Nov 21 08:31:55 2024
     13// Update Count     : 18
    1414//
    1515
     
    931931
    932932static inline [thread$ *, int] search_entry_queue( const __waitfor_mask_t & mask, monitor$ * monitors [], __lock_size_t count ) {
    933 
    934933        __queue_t(thread$) & entry_queue = monitors[0]->entry_queue;
    935934
     935#if 0
    936936        #if defined( __CFA_WITH_VERIFY__ )
    937937                thread$ * last = 0p;
    938938        #endif
    939939        // For each thread in the entry-queue
    940         for(    thread$ ** thrd_it = &entry_queue.head;
    941                 (*thrd_it) != 1p;
    942                 thrd_it = &get_next(**thrd_it)
    943         ) {
     940        for ( thread$ ** thrd_it = &entry_queue.head; (*thrd_it) != 1p; thrd_it = &get_next(**thrd_it) ) {
    944941                thread$ * curr = *thrd_it;
    945942
    946                 /* paranoid */ verifyf( !last || last->user_link.next == curr, "search not making progress, from %p (%p) to %p", last, last->user_link.next, curr );
     943                /* paranoid */ verifyf( !last || last->user_link.next == curr, "search not making progress, from %p (%p) to %p",
     944                                                                last, last->user_link.next, curr );
    947945                /* paranoid */ verifyf( curr != last, "search not making progress, from %p to %p", last, curr );
    948946
     
    951949                __acceptable_t * end   = end  (mask);
    952950                __acceptable_t * begin = begin(mask);
    953                 for( __acceptable_t * it = begin; it != end; it++, i++ ) {
    954                         // Check if we have a match
    955                         if( *it == curr->monitors ) {
    956 
    957                                 // If we have a match return it
    958                                 // after removeing it from the entry queue
     951                for ( __acceptable_t * it = begin; it != end; it++, i++ ) {
     952                        // Check for match
     953                        if ( *it == curr->monitors ) {
     954                                // If match, return it after removeing it from the entry queue
    959955                                return [remove( entry_queue, thrd_it ), i];
    960956                        }
     
    965961                #endif
    966962        }
    967 
     963#endif
     964        int i = 0;
     965        __acceptable_t * end   = end  (mask);
     966        __acceptable_t * begin = begin(mask);
     967        // For each acceptable (respect lexical priority in waitfor statement)
     968        for ( __acceptable_t * it = begin; it != end; it++, i++ ) {
     969                #if defined( __CFA_WITH_VERIFY__ )
     970                thread$ * last = 0p;
     971                #endif // __CFA_WITH_VERIFY__
     972
     973                for ( thread$ ** thrd_it = &entry_queue.head; (*thrd_it) != 1p; thrd_it = &get_next(**thrd_it) ) {
     974                        thread$ * curr = *thrd_it;
     975
     976                        /* paranoid */ verifyf( !last || last->user_link.next == curr, "search not making progress, from %p (%p) to %p",
     977                                                                        last, last->user_link.next, curr );
     978                        /* paranoid */ verifyf( curr != last, "search not making progress, from %p to %p", last, curr );
     979
     980                        // For each thread in the entry-queue check for a match
     981                        if ( *it == curr->monitors ) {
     982                                // If match, return it after removeing from the entry queue
     983                                return [remove( entry_queue, thrd_it ), i];
     984                        } // if
     985
     986                        #if defined( __CFA_WITH_VERIFY__ )
     987                        last = curr;
     988                        #endif
     989                } // for
     990        } // for
    968991        return [0, -1];
    969992}
  • libcfa/src/rational.cfa

    rb006c51e r10a9479d  
    1010// Created On       : Wed Apr  6 17:54:28 2016
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Aug  2 07:41:25 2024
    13 // Update Count     : 199
     12// Last Modified On : Mon Nov 11 22:37:12 2024
     13// Update Count     : 206
    1414//
    1515
     
    203203
    204204        forall( ostype & | ostream( ostype ) | { ostype & ?|?( ostype &, T ); } ) {
    205                 ostype & ?|?( ostype & os, rational(T) r ) {
     205        ostype & ?|?( ostype & os, rational(T) r ) {
    206206                        return os | r.numerator | '/' | r.denominator;
    207207                } // ?|?
  • libcfa/src/rational.hfa

    rb006c51e r10a9479d  
    1212// Created On       : Wed Apr  6 17:56:25 2016
    1313// Last Modified By : Peter A. Buhr
    14 // Last Modified On : Fri Oct  6 07:52:20 2023
    15 // Update Count     : 122
     14// Last Modified On : Fri Nov  8 17:02:09 2024
     15// Update Count     : 126
    1616//
    1717
     
    2323// implementation
    2424
    25 forall( T | arithmetic( T ) ) {
     25forall( T ) {
    2626        struct rational {
    2727                T numerator, denominator;                                               // invariant: denominator > 0
    2828        }; // rational
     29}
    2930
     31forall( T | arithmetic( T ) ) {
    3032        // constructors
    3133
  • src/AST/Expr.hpp

    rb006c51e r10a9479d  
    330330enum GeneratedFlag { ExplicitCast, GeneratedCast };
    331331
     332/// Even within the basic cast expression there are variants:
     333/// CCast - C-Style Cast: A backwards compatable cast from C.
     334/// CoerceCast - Coercion Cast: Change the type without changing the value.
     335/// ReturnCast - Ascription Cast: Requires the given expression result type.
     336enum CastKind { CCast, CoerceCast, ReturnCast };
     337
    332338/// A type cast, e.g. `(int)e`
    333339class CastExpr final : public Expr {
     
    336342        GeneratedFlag isGenerated;
    337343
    338         enum CastKind {
    339                 Default, // C
    340                 Coerce, // reinterpret cast
    341                 Return  // overload selection
    342         };
    343 
    344         CastKind kind = Default;
     344        CastKind kind = CCast;
    345345
    346346        CastExpr( const CodeLocation & loc, const Expr * a, const Type * to,
    347                 GeneratedFlag g = GeneratedCast, CastKind kind = Default ) : Expr( loc, to ), arg( a ), isGenerated( g ), kind( kind ) {}
     347                GeneratedFlag g = GeneratedCast, CastKind kind = CCast ) : Expr( loc, to ), arg( a ), isGenerated( g ), kind( kind ) {}
    348348        /// Cast-to-void
    349         CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g = GeneratedCast, CastKind kind = Default );
     349        CastExpr( const CodeLocation & loc, const Expr * a, GeneratedFlag g = GeneratedCast, CastKind kind = CCast );
    350350
    351351        /// Wrap a cast expression around an existing expression (always generated)
  • src/AST/Pass.hpp

    rb006c51e r10a9479d  
    327327/// The Pass template handles what *before* and *after* means automatically
    328328template< template<class...> class container_t = std::list >
    329 struct WithStmtsToAdd {
     329struct WithStmtsToAddX {
    330330        container_t< ptr<Stmt> > stmtsToAddBefore;
    331331        container_t< ptr<Stmt> > stmtsToAddAfter;
    332332};
     333
     334struct WithStmtsToAdd : public WithStmtsToAddX<> {};
    333335
    334336/// Used if visitor requires added declarations before or after the current node.
    335337/// The Pass template handles what *before* and *after* means automatically
    336338template< template<class...> class container_t = std::list >
    337 struct WithDeclsToAdd {
     339struct WithDeclsToAddX {
    338340        container_t< ptr<Decl> > declsToAddBefore;
    339341        container_t< ptr<Decl> > declsToAddAfter;
    340342};
     343
     344struct WithDeclsToAdd : public WithDeclsToAddX<> {};
    341345
    342346/// Use if visitation should stop at certain levels
  • src/CodeGen/CodeGenerator.cpp

    rb006c51e r10a9479d  
    680680        extension( expr );
    681681        output << "(";
    682         if ( expr->result->isVoid() ) {
    683                 output << "(void)";
    684         } else {
    685                 output << "(";
     682        switch ( expr->kind ) {
     683        case ast::CCast:
     684                if ( expr->result->isVoid() ) {
     685                        output << "(void)";
     686                } else {
     687                        output << "(";
     688                        output << genType( expr->result, "", options );
     689                        output << ")";
     690                }
     691                break;
     692        case ast::CoerceCast:
     693                assertf( ast::CoerceCast != expr->kind, "Coercion cast is not implemented." );
     694                // And likely shouldn't reach code generation when it is implemented.
     695                break;
     696        case ast::ReturnCast:
     697                // This should be invisible in the resulting C code.
     698                // Can we insert a check here?
     699                //assert( ResolvExpr::typesCompatable(???) );
     700                if ( options.genC ) break;
     701                output << "(return ";
    686702                output << genType( expr->result, "", options );
    687703                output << ")";
     704                break;
    688705        }
    689706        expr->arg->accept( *visitor );
  • src/Concurrency/Actors.cpp

    rb006c51e r10a9479d  
    194194// collects data needed for next pass that does the circular defn resolution
    195195//     for message send operators (via table above)
    196 struct GenFuncsCreateTables : public ast::WithDeclsToAdd<> {
     196struct GenFuncsCreateTables : public ast::WithDeclsToAdd {
    197197        unordered_set<const StructDecl *> & actorStructDecls;
    198198        unordered_set<const StructDecl *>  & messageStructDecls;
     
    451451// separate pass is needed since this pass resolves circular defn issues
    452452// generates the forward declarations of the send operator for actor routines
    453 struct FwdDeclOperator : public ast::WithDeclsToAdd<> {
     453struct FwdDeclOperator : public ast::WithDeclsToAdd {
    454454        unordered_set<const StructDecl *> & actorStructDecls;
    455455        unordered_set<const StructDecl *>  & messageStructDecls;
  • src/Concurrency/Corun.cpp

    rb006c51e r10a9479d  
    2525namespace Concurrency {
    2626
    27 struct CorunKeyword : public WithDeclsToAdd<>, public WithStmtsToAdd<> {
     27struct CorunKeyword : public WithDeclsToAdd, public WithStmtsToAdd {
    2828        UniqueName CorunFnNamer = "__CFA_corun_lambda_"s;
    2929        UniqueName CoforFnNamer = "__CFA_cofor_lambda_"s;
  • src/Concurrency/Keywords.cpp

    rb006c51e r10a9479d  
    117117
    118118// --------------------------------------------------------------------------
    119 struct ConcurrentSueKeyword : public ast::WithDeclsToAdd<> {
     119struct ConcurrentSueKeyword : public ast::WithDeclsToAdd {
    120120        ConcurrentSueKeyword(
    121121                std::string&& type_name, std::string&& field_name,
     
    639639// --------------------------------------------------------------------------
    640640struct SuspendKeyword final :
    641                 public ast::WithStmtsToAdd<>, public ast::WithGuards {
     641                public ast::WithStmtsToAdd, public ast::WithGuards {
    642642        SuspendKeyword() = default;
    643643        virtual ~SuspendKeyword() = default;
     
    860860
    861861// --------------------------------------------------------------------------
    862 struct MutexKeyword final : public ast::WithDeclsToAdd<> {
     862struct MutexKeyword final : public ast::WithDeclsToAdd {
    863863        const ast::FunctionDecl * postvisit( const ast::FunctionDecl * decl );
    864864        void postvisit( const ast::StructDecl * decl );
  • src/Concurrency/Waituntil.cpp

    rb006c51e r10a9479d  
    13981398// To add the predicates at global scope we need to do it in a second pass
    13991399// Predicates are added after "struct select_node { ... };"
    1400 class AddPredicateDecls final : public WithDeclsToAdd<> {
     1400class AddPredicateDecls final : public WithDeclsToAdd {
    14011401        vector<FunctionDecl *> & satFns;
    14021402        const StructDecl * selectNodeDecl = nullptr;
  • src/ControlStruct/ExceptDecl.cpp

    rb006c51e r10a9479d  
    401401}
    402402
    403 struct ExceptDeclCore : public ast::WithDeclsToAdd<> {
     403struct ExceptDeclCore : public ast::WithDeclsToAdd {
    404404        ast::StructDecl const * transformExcept( ast::StructDecl const * decl );
    405405        ast::ObjectDecl const * transformVTable(
  • src/GenPoly/Box.cpp

    rb006c51e r10a9479d  
    5555/// Adds layout-generation functions to polymorphic types.
    5656struct LayoutFunctionBuilder final :
    57                 public ast::WithDeclsToAdd<>,
     57                public ast::WithDeclsToAdd,
    5858                public ast::WithShortCircuiting,
    5959                public ast::WithVisitorRef<LayoutFunctionBuilder> {
     
    344344                public ast::WithGuards,
    345345                public ast::WithShortCircuiting,
    346                 public ast::WithStmtsToAdd<>,
     346                public ast::WithStmtsToAdd,
    347347                public ast::WithVisitorRef<CallAdapter> {
    348348        CallAdapter();
     
    15751575struct PolyGenericCalculator final :
    15761576                public ast::WithConstTypeSubstitution,
    1577                 public ast::WithDeclsToAdd<>,
     1577                public ast::WithDeclsToAdd,
    15781578                public ast::WithGuards,
    1579                 public ast::WithStmtsToAdd<>,
     1579                public ast::WithStmtsToAdd,
    15801580                public ast::WithVisitorRef<PolyGenericCalculator> {
    15811581        PolyGenericCalculator();
  • src/GenPoly/InstantiateGeneric.cpp

    rb006c51e r10a9479d  
    277277                public ast::WithVisitorRef<FixDtypeStatic>,
    278278                public ast::WithShortCircuiting,
    279                 public ast::WithStmtsToAdd<> {
     279                public ast::WithStmtsToAdd {
    280280        ast::ApplicationExpr const * previsit( ast::ApplicationExpr const * expr );
    281281        void previsit( ast::AddressExpr const * expr );
     
    421421                public ast::WithCodeLocation,
    422422                public ast::WithConstTypeSubstitution,
    423                 public ast::WithDeclsToAdd<>,
     423                public ast::WithDeclsToAdd,
    424424                public ast::WithGuards,
    425425                public ast::WithVisitorRef<GenericInstantiator>
  • src/GenPoly/Lvalue.cpp

    rb006c51e r10a9479d  
    8585struct ReferenceConversions final :
    8686                public ast::WithConstTranslationUnit,
    87                 public ast::WithGuards, public ast::WithStmtsToAdd<> {
     87                public ast::WithGuards, public ast::WithStmtsToAdd {
    8888        ast::Expr const * postvisit( ast::CastExpr const * expr );
    8989        ast::Expr const * postvisit( ast::AddressExpr const * expr );
     
    316316                        Warning::RvalueToReferenceConversion, toCString( expr->arg ) );
    317317
     318                // allowing conversion in the rvalue to const ref case
     319                // use the referenced-to type to create temp variables
     320                ast::Type const * targetType = dstType;
     321                for (int i = 0; i < diff; ++i) targetType = (strict_dynamic_cast<ast::ReferenceType const *>(targetType))->base;
     322
    318323                static UniqueName tmpNamer( "__ref_tmp_" );
    319324                ast::ObjectDecl * tmp = new ast::ObjectDecl( expr->arg->location,
    320325                        tmpNamer.newName(),
    321                         ast::deepCopy( expr->arg->result ),
     326                        // ast::deepCopy( expr->arg->result ),
     327                        ast::deepCopy (targetType),
    322328                        new ast::SingleInit( expr->arg->location, expr->arg ) );
    323329                PRINT( std::cerr << "make tmp: " << tmp << std::endl; )
     
    359365                        ret = new ast::AddressExpr( ret->location, ret );
    360366                }
    361                 if ( expr->arg->get_lvalue() &&
    362                                 !ResolvExpr::typesCompatible(
    363                                         srcType,
    364                                         strict_dynamic_cast<ast::ReferenceType const *>( dstType )->base ) ) {
    365                         // Must keep cast if cast-to type is different from the actual type.
     367                // Must keep cast if types are different.
     368                if ( !ResolvExpr::typesCompatible(
     369                                srcType,
     370                                strict_dynamic_cast<ast::ReferenceType const *>( dstType )->base ) ) {
    366371                        return ast::mutate_field( expr, &ast::CastExpr::arg, ret );
    367372                }
     
    377382                }
    378383                // Must keep cast if types are different.
    379                 if ( !ResolvExpr::typesCompatibleIgnoreQualifiers(
     384                if ( !ResolvExpr::typesCompatible(
    380385                                dstType->stripReferences(),
    381386                                srcType->stripReferences() ) ) {
     
    390395        } else {
    391396                assert( 0 == diff );
    392                 // Remove useless generated casts.
    393                 if ( expr->isGenerated == ast::GeneratedFlag::GeneratedCast &&
    394                                 ResolvExpr::typesCompatible(
     397                // Must keep cast if types are different. (Or it is explicit.)
     398                if ( ast::ExplicitCast == expr->isGenerated ||
     399                                !ResolvExpr::typesCompatible(
    395400                                        expr->result,
    396401                                        expr->arg->result ) ) {
    397                         PRINT(
    398                                 std::cerr << "types are compatible, removing cast: " << expr << '\n';
    399                                 std::cerr << "-- " << expr->result << '\n';
    400                                 std::cerr << "-- " << expr->arg->result << std::endl;
    401                         )
    402                         auto argAsEnum = expr->arg.as<ast::EnumInstType>();
    403                         auto resultAsEnum = expr->result.as<ast::EnumInstType>();
    404                         if (argAsEnum && resultAsEnum) {
    405                                 if (argAsEnum->base->name != resultAsEnum->base->name) {
    406                                         return expr;
    407                                 }
    408                         }
    409                         return ast::mutate_field( expr->arg.get(),
    410                                         &ast::Expr::env, expr->env.get() );
    411                 }
    412                 return expr;
     402                        return expr;
     403                }
     404                PRINT(
     405                        std::cerr << "types are compatible, removing cast: " << expr << '\n';
     406                        std::cerr << "-- " << expr->result << '\n';
     407                        std::cerr << "-- " << expr->arg->result << std::endl;
     408                )
     409                return ast::mutate_field( expr->arg.get(),
     410                                &ast::Expr::env, expr->env.get() );
    413411        }
    414412}
     
    505503}
    506504
     505/// Recursively move an address expression underneath casts. Casts are not
     506/// lvalue expressions in C but are sometimes considered as such in Cforall,
     507/// (passes like InstantiateGeneric can add them.) - &(int) => (int*)&
     508ast::Expr const * moveAddressUnderCast( ast::AddressExpr const * expr ) {
     509        if ( !dynamic_cast<ast::CastExpr const *>( expr->arg.get() ) ) {
     510                return expr;
     511        }
     512        auto mutExpr = ast::mutate( expr );
     513        auto mutCast = strict_dynamic_cast<ast::CastExpr *>(
     514                        ast::mutate( mutExpr->arg.release() ) );
     515        mutExpr->arg = mutCast->arg;
     516        mutCast->arg = moveAddressUnderCast( mutExpr );
     517        mutCast->result = new ast::PointerType( mutCast->result );
     518        return mutCast;
     519}
     520
    507521ast::Expr const * CollapseAddressDeref::postvisit(
    508522                ast::AddressExpr const * expr ) {
     
    516530                        return ret;
    517531                }
    518         } else if ( auto cast = dynamic_cast<ast::CastExpr const *>( arg ) ) {
    519                 // Need to move cast to pointer type out a level since address of
    520                 // pointer is not valid C code (can be introduced in prior passes,
    521                 // e.g., InstantiateGeneric)
    522                 if ( ast::getPointerBase( cast->result ) ) {
    523                         auto mutExpr = ast::mutate( expr );
    524                         auto mutCast = strict_dynamic_cast<ast::CastExpr *>(
    525                                         ast::mutate( mutExpr->arg.release() ) );
    526                         mutExpr->arg = mutCast->arg;
    527                         mutCast->arg = mutExpr;
    528                         mutCast->result = new ast::PointerType( mutCast->result );
    529                         return mutCast;
    530                 }
     532        } else {
     533                return moveAddressUnderCast( expr );
    531534        }
    532535        return expr;
  • src/GenPoly/Specialize.cpp

    rb006c51e r10a9479d  
    3030struct SpecializeCore final :
    3131                public ast::WithConstTypeSubstitution,
    32                 public ast::WithDeclsToAdd<>,
     32                public ast::WithDeclsToAdd,
    3333                public ast::WithVisitorRef<SpecializeCore> {
    3434        std::string paramPrefix = "_p";
  • src/InitTweak/FixInit.cpp

    rb006c51e r10a9479d  
    105105/// generate/resolve copy construction expressions for each, and generate/resolve destructors for both
    106106/// arguments and return value temporaries
    107 struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors>, public ast::WithConstTranslationUnit {
     107struct ResolveCopyCtors final : public ast::WithGuards, public ast::WithStmtsToAdd, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithVisitorRef<ResolveCopyCtors>, public ast::WithConstTranslationUnit {
    108108        const ast::Expr * postvisit( const ast::ImplicitCopyCtorExpr * impCpCtorExpr );
    109109        const ast::StmtExpr * previsit( const ast::StmtExpr * stmtExpr );
     
    177177/// insert destructor calls at the appropriate places.  must happen before CtorInit nodes are removed
    178178/// (currently by FixInit)
    179 struct InsertDtors final : public ObjDeclCollector, public ast::WithStmtsToAdd<> {
     179struct InsertDtors final : public ObjDeclCollector, public ast::WithStmtsToAdd {
    180180        InsertDtors( ast::Pass<LabelFinder> & finder ) : finder( finder ), labelVars( finder.core.vars ) {}
    181181
     
    194194
    195195/// expand each object declaration to use its constructor after it is declared.
    196 struct FixInit : public ast::WithStmtsToAdd<> {
     196struct FixInit : public ast::WithStmtsToAdd {
    197197        static void fixInitializers( ast::TranslationUnit &translationUnit );
    198198
     
    230230
    231231/// expands ConstructorExpr nodes into comma expressions, using a temporary for the first argument
    232 struct FixCtorExprs final : public ast::WithDeclsToAdd<>, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithConstTranslationUnit {
     232struct FixCtorExprs final : public ast::WithDeclsToAdd, public ast::WithSymbolTable, public ast::WithShortCircuiting, public ast::WithConstTranslationUnit {
    233233        const ast::Expr * postvisit( const ast::ConstructorExpr * ctorExpr );
    234234};
  • src/InitTweak/GenInit.cpp

    rb006c51e r10a9479d  
    4646        // Outer pass finds declarations, for their type could wrap a type that needs hoisting
    4747        struct HoistArrayDimension_NoResolve final :
    48                         public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting,
     48                        public ast::WithDeclsToAdd, public ast::WithShortCircuiting,
    4949                        public ast::WithGuards, public ast::WithConstTranslationUnit,
    5050                        public ast::WithVisitorRef<HoistArrayDimension_NoResolve>,
     
    205205
    206206        struct ReturnFixer final :
    207                         public ast::WithStmtsToAdd<>, ast::WithGuards, ast::WithShortCircuiting {
     207                        public ast::WithStmtsToAdd, ast::WithGuards, ast::WithShortCircuiting {
    208208                void previsit( const ast::FunctionDecl * decl );
    209209                const ast::ReturnStmt * previsit( const ast::ReturnStmt * stmt );
  • src/Parser/ExpressionNode.cpp

    rb006c51e r10a9479d  
    652652                DeclarationNode * decl_node,
    653653                ExpressionNode * expr_node,
    654                 ast::CastExpr::CastKind kind ) {
     654                ast::CastKind kind ) {
    655655        ast::Type * targetType = maybeMoveBuildType( decl_node );
    656656        if ( dynamic_cast<ast::VoidType *>( targetType ) ) {
  • src/Parser/ExpressionNode.hpp

    rb006c51e r10a9479d  
    6969ast::DimensionExpr * build_dimensionref( const CodeLocation &, const std::string * name );
    7070
    71 ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node, ast::CastExpr::CastKind kind = ast::CastExpr::Default );
     71ast::Expr * build_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node, ast::CastKind kind = ast::CCast );
    7272ast::Expr * build_keyword_cast( const CodeLocation &, ast::AggregateDecl::Aggregate target, ExpressionNode * expr_node );
    7373ast::Expr * build_virtual_cast( const CodeLocation &, DeclarationNode * decl_node, ExpressionNode * expr_node );
  • src/Parser/parser.yy

    rb006c51e r10a9479d  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Oct 13 12:18:15 2024
    13 // Update Count     : 6845
     12// Last Modified On : Fri Nov 15 15:01:33 2024
     13// Update Count     : 6915
    1414//
    1515
     
    2424// the grammar.
    2525
    26 // The root language for this grammar is ANSI99/11 C. All of ANSI99/11 is parsed, except for:
    27 //
    28 //   designation with '=' (use ':' instead)
    29 //
    30 // This incompatibility is discussed in detail before the "designation" grammar rule.  Most of the syntactic extensions
    31 // from ANSI90 to ANSI11 C are marked with the comment "C99/C11".
     26// The root language for this grammar is ANSI99/11 C. All of ANSI99/11 is parsed.  Most of the syntactic extensions from
     27// ANSI90 to ANSI11 C are marked with the comment "C99/C11".
    3228
    3329// This grammar also has two levels of extensions. The first extensions cover most of the GCC C extensions. All of the
     
    983979                { $$ = new ExpressionNode( new ast::VirtualCastExpr( yylloc, maybeMoveBuild( $5 ), maybeMoveBuildType( $3 ) ) ); }
    984980        | '(' RETURN type_no_function ')' cast_expression       // CFA
    985                 { $$ = new ExpressionNode( build_cast( yylloc, $3, $5, ast::CastExpr::Return ) ); }
     981                { $$ = new ExpressionNode( build_cast( yylloc, $3, $5, ast::ReturnCast ) ); }
    986982        | '(' COERCE type_no_function ')' cast_expression       // CFA
    987983                { SemanticError( yylloc, "Coerce cast is currently unimplemented." ); $$ = nullptr; }
     
    11701166                // comma_expression in cfa_identifier_parameter_array and cfa_abstract_array
    11711167        '[' ',' ']'
    1172                 { $$ = new ExpressionNode( build_tuple( yylloc, nullptr ) ); }
     1168                // { $$ = new ExpressionNode( build_tuple( yylloc, nullptr ) ); }
     1169                { SemanticError( yylloc, "Empty tuple is meaningless." ); $$ = nullptr; }
    11731170        | '[' assignment_expression ',' ']'
    11741171                { $$ = new ExpressionNode( build_tuple( yylloc, $2 ) ); }
     
    12241221        | DIRECTIVE
    12251222                { $$ = new StatementNode( build_directive( yylloc, $1 ) ); }
     1223//      | attribute ';'
     1224//              { $$ = new StatementNode( $1 ); }
    12261225        ;
    12271226
     
    20572056        | cfa_abstract_tuple identifier_or_type_name asm_name_opt
    20582057                { $$ = $1->addName( $2 )->addAsmName( $3 ); }
    2059         | type_qualifier_list cfa_abstract_tuple identifier_or_type_name asm_name_opt
    2060                 { $$ = $2->addQualifiers( $1 )->addName( $3 )->addAsmName( $4 ); }
     2058        | multi_array_dimension cfa_abstract_tuple identifier_or_type_name asm_name_opt
     2059                { $$ = $2->addNewArray( $1 )->addName( $3 )->addAsmName( $4 ); }
     2060        | multi_array_dimension type_qualifier_list cfa_abstract_tuple identifier_or_type_name asm_name_opt
     2061                { $$ = $3->addNewArray( $1 )->addQualifiers( $2 )->addName( $4 )->addAsmName( $5 ); }
    20612062
    20622063                // [ int s, int t ];                    // declare s and t
     
    42234224        cfa_identifier_parameter_ptr
    42244225        | cfa_identifier_parameter_array
     4226        | type_qualifier_list cfa_identifier_parameter_array
     4227                { $$ = $2->addQualifiers( $1 ); }
    42254228        ;
    42264229
     
    42464249        '[' ']' type_specifier_nobody
    42474250                { $$ = $3->addNewArray( DeclarationNode::newArray( nullptr, nullptr, false ) ); }
     4251        | '[' ']' cfa_abstract_tuple
     4252                { $$ = $3->addNewArray( DeclarationNode::newArray( nullptr, nullptr, false ) ); }
    42484253        | cfa_array_parameter_1st_dimension type_specifier_nobody
     4254                { $$ = $2->addNewArray( $1 ); }
     4255        | cfa_array_parameter_1st_dimension cfa_abstract_tuple
    42494256                { $$ = $2->addNewArray( $1 ); }
    42504257        | '[' ']' multi_array_dimension type_specifier_nobody
    42514258                { $$ = $4->addNewArray( $3 )->addNewArray( DeclarationNode::newArray( nullptr, nullptr, false ) ); }
     4259        | '[' ']' multi_array_dimension cfa_abstract_tuple
     4260                { $$ = $4->addNewArray( $3 )->addNewArray( DeclarationNode::newArray( nullptr, nullptr, false ) ); }
    42524261        | cfa_array_parameter_1st_dimension multi_array_dimension type_specifier_nobody
    42534262                { $$ = $3->addNewArray( $2 )->addNewArray( $1 ); }
     4263        | cfa_array_parameter_1st_dimension multi_array_dimension cfa_abstract_tuple
     4264                { $$ = $3->addNewArray( $2 )->addNewArray( $1 ); }
    42544265        | multi_array_dimension type_specifier_nobody
     4266                { $$ = $2->addNewArray( $1 ); }
     4267        | multi_array_dimension cfa_abstract_tuple
    42554268                { $$ = $2->addNewArray( $1 ); }
    42564269
  • src/ResolvExpr/CandidateFinder.cpp

    rb006c51e r10a9479d  
    12201220                        finder.allowVoid = true;
    12211221                }
    1222                 if ( castExpr->kind == ast::CastExpr::Return ) {
     1222                if ( ast::ReturnCast == castExpr->kind ) {
    12231223                        finder.strictMode = true;
    12241224                        finder.find( castExpr->arg, ResolveMode::withAdjustment() );
  • src/ResolvExpr/ConversionCost.cpp

    rb006c51e r10a9479d  
    250250                        newSrc = new ast::BasicType( ast::BasicKind::UnsignedInt );
    251251                }
     252                if (dstAsRef->base->is_const() ) {
     253                        auto cvtCost = conversionCost(newSrc, dstAsRef->base, srcIsLvalue, symtab, env) ;
     254                        if (cvtCost == Cost::zero) { // exact match, may use a lvalue src
     255                                if ( srcIsLvalue ) {
     256                                        if ( src->qualifiers == dstAsRef->base->qualifiers ) {
     257                                                return Cost::reference;
     258                                        } else if ( src->qualifiers < dstAsRef->base->qualifiers ) {
     259                                                return Cost::safe;
     260                                        } else {
     261                                                return Cost::unsafe;
     262                                        }
     263                                }
     264                                else {
     265                                        return Cost::reference;
     266                                }
     267                        }
     268                        else { // not exact match, conversion is needed so lvalueness of src does not matter
     269                                return cvtCost + Cost::reference;
     270                        }
     271                }
    252272                if ( typesCompatibleIgnoreQualifiers( newSrc, dstAsRef->base, env ) ) {
    253273                        if ( srcIsLvalue ) {
     
    259279                                        return Cost::unsafe;
    260280                                }
    261                         } else if ( dstAsRef->base->is_const() ) {
    262                                 return Cost::safe;
    263                         } else {
     281                        } else { // rvalue-to-NC-ref conversion
    264282                                return Cost::unsafe;
    265283                        }
  • src/ResolvExpr/Resolver.cpp

    rb006c51e r10a9479d  
    201201                                && typesCompatible( castExpr->arg->result, castExpr->result )
    202202                        ) {
    203                                 auto argAsEnum = castExpr->arg.as<ast::EnumInstType>();
    204                                 auto resultAsEnum = castExpr->result.as<ast::EnumInstType>();
    205                                 if (argAsEnum && resultAsEnum) {
    206                                         if (argAsEnum->base->name != resultAsEnum->base->name) {
    207                                                 std::cerr << "Enum Cast: " << argAsEnum->base->name << " to " << resultAsEnum->base->name << std::endl;
    208                                                 return castExpr;
    209                                         }
     203                                ast::EnumInstType const * arg, * result;
     204                                if ( ( result = castExpr->result.as<ast::EnumInstType>() ) &&
     205                                                ( arg = castExpr->arg.as<ast::EnumInstType>() ) &&
     206                                                arg->base->name != result->base->name) {
     207                                        return castExpr;
    210208                                }
    211209                                // generated cast is the same type as its argument, remove it after keeping env
     
    377375: public ast::WithSymbolTable, public ast::WithGuards,
    378376  public ast::WithVisitorRef<Resolver>, public ast::WithShortCircuiting,
    379   public ast::WithStmtsToAdd<> {
     377  public ast::WithStmtsToAdd {
    380378
    381379        ast::ptr< ast::Type > functionReturn = nullptr;
  • src/Tuples/TupleExpansion.cpp

    rb006c51e r10a9479d  
    2828};
    2929
    30 struct UniqueExprExpander final : public ast::WithDeclsToAdd<> {
     30struct UniqueExprExpander final : public ast::WithDeclsToAdd {
    3131        const ast::Expr * postvisit( const ast::UniqueExpr * unqExpr );
    3232        // Not a vector, because they may not be adding in increasing order.
     
    3737struct TupleMainExpander final :
    3838                public ast::WithCodeLocation,
    39                 public ast::WithDeclsToAdd<>,
     39                public ast::WithDeclsToAdd,
    4040                public ast::WithGuards,
    4141                public ast::WithVisitorRef<TupleMainExpander> {
  • src/Validate/Autogen.cpp

    rb006c51e r10a9479d  
    5050// --------------------------------------------------------------------------
    5151struct AutogenerateRoutines final :
    52                 public ast::WithDeclsToAdd<>,
     52                public ast::WithDeclsToAdd,
    5353                public ast::WithShortCircuiting {
    5454        void previsit( const ast::EnumDecl * enumDecl );
  • src/Validate/CompoundLiteral.cpp

    rb006c51e r10a9479d  
    2727
    2828struct CompoundLiteral final :
    29                 public ast::WithDeclsToAdd<> {
     29                public ast::WithDeclsToAdd {
    3030        ast::Storage::Classes storageClasses;
    3131
  • src/Validate/HoistStruct.cpp

    rb006c51e r10a9479d  
    6868 */
    6969struct HoistStructCore final :
    70                 public ast::WithDeclsToAdd<>, public ast::WithGuards {
     70                public ast::WithDeclsToAdd, public ast::WithGuards {
    7171        ast::StructDecl const * previsit( ast::StructDecl const * decl );
    7272        ast::StructDecl const * postvisit( ast::StructDecl const * decl );
  • src/Validate/HoistTypeDecls.cpp

    rb006c51e r10a9479d  
    2222namespace {
    2323
    24 struct HoistTypeDecls final : public ast::WithDeclsToAdd<> {
     24struct HoistTypeDecls final : public ast::WithDeclsToAdd {
    2525        void previsit( ast::SizeofExpr const * );
    2626        void previsit( ast::AlignofExpr const * );
  • src/Validate/ImplementEnumFunc.cpp

    rb006c51e r10a9479d  
    472472
    473473struct ImplementEnumFunc final :
    474                 public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting {
     474                public ast::WithDeclsToAdd, public ast::WithShortCircuiting {
    475475        void previsit(const ast::EnumDecl* enumDecl);
    476476        void previsit(const ast::FunctionDecl* functionDecl);
  • src/Validate/LinkInstanceTypes.cpp

    rb006c51e r10a9479d  
    2727struct LinkTypesCore : public WithNoIdSymbolTable,
    2828                public ast::WithCodeLocation,
    29                 public ast::WithDeclsToAdd<>,
     29                public ast::WithDeclsToAdd,
    3030                public ast::WithGuards,
    3131                public ast::WithShortCircuiting,
  • src/Validate/ReplaceTypedef.cpp

    rb006c51e r10a9479d  
    2828struct ReplaceTypedefCore final :
    2929                public ast::WithCodeLocation,
    30                 public ast::WithDeclsToAdd<>,
     30                public ast::WithDeclsToAdd,
    3131                public ast::WithGuards,
    3232                public ast::WithShortCircuiting,
  • src/Virtual/VirtualDtor.cpp

    rb006c51e r10a9479d  
    119119// collects data needed for next pass that does the circular defn resolution
    120120//     for dtor setters and delete fns (via table above)
    121 struct GenFuncsCreateTables : public ast::WithDeclsToAdd<> {
     121struct GenFuncsCreateTables : public ast::WithDeclsToAdd {
    122122        unordered_map<const StructDecl *, CtorDtor> & structDecls;
    123123        CtorDtorTable & torDecls;
     
    351351// separate pass is needed since  __CFA_set_dtor needs to be defined after
    352352//   the last dtor defn which is found in prior pass
    353 struct GenSetDtor : public ast::WithDeclsToAdd<> {
     353struct GenSetDtor : public ast::WithDeclsToAdd {
    354354        unordered_map<const StructDecl *, CtorDtor> & structDecls; // set of decls that inherit from virt dtor
    355355        CtorDtorTable & torDecls;
  • tests/concurrency/actors/dynamic.cfa

    rb006c51e r10a9479d  
    4848
    4949        executor e{ 0, 1, 1, false };
    50         start_actor_system( e );
    51 
     50        actor_start( e );
    5251        sout | "started";
    5352
     
    5857        *d_actor | *d_msg;
    5958
    60         stop_actor_system();
    61 
     59        actor_stop();
    6260        sout | "stopped";
    6361}
  • tests/concurrency/actors/executor.cfa

    rb006c51e r10a9479d  
    8484
    8585        sout | "starting";
    86 
    87         start_actor_system( e );
    88 
     86        actor_start( e );
    8987        sout | "started";
    90 
    9188        d_actor actors[ Actors ];
    92 
    9389        for ( i; Actors ) {
    9490                actors[i] | shared_msg;
    9591        } // for
    96 
    9792        sout | "stopping";
    98 
    99         stop_actor_system();
    100 
     93        actor_stop();
    10194        sout | "stopped";
    10295}
  • tests/concurrency/actors/inherit.cfa

    rb006c51e r10a9479d  
    2929        sout | "Start";
    3030        {
    31                 start_actor_system();
     31                actor_start();
    3232                D_msg * dm = alloc();
    3333                (*dm){};
     
    4040                *s | *dm;
    4141                *s2 | *dm2;
    42                 stop_actor_system();
     42                actor_stop();
    4343        }
    4444        {
    45                 start_actor_system();
     45                actor_start();
    4646                Server s[2];
    4747                D_msg * dm = alloc();
     
    5151                s[0] | *dm;
    5252                s[1] | *dm2;
    53                 stop_actor_system();
     53                actor_stop();
    5454        }
    5555        sout | "Finished";
  • tests/concurrency/actors/inline.cfa

    rb006c51e r10a9479d  
    3838        processor p;
    3939        {
    40                 start_actor_system();                                                           // sets up executor
     40                actor_start();                                                          // sets up executor
    4141                d_actor da;
    4242                d_msg * dm = alloc();
    4343                (*dm){ 42, 2423 };
    4444                da | *dm;
    45                 stop_actor_system();                                                            // waits until actors finish
     45                actor_stop();                                                           // waits until actors finish
    4646        }
    4747        {
    48                 start_actor_system();                                                           // sets up executor
     48                actor_start();                                                          // sets up executor
    4949                d_actor da;
    5050                d_msg2 dm{ 29079 };
     
    5454                virtual_dtor * v = &dm;
    5555                da | dm;
    56                 stop_actor_system();                                                            // waits until actors finish
     56                actor_stop();                                                           // waits until actors finish
    5757        }
    5858}
  • tests/concurrency/actors/matrixMultiply.cfa

    rb006c51e r10a9479d  
    8888
    8989        sout | "starting";
    90 
    91         start_actor_system( e );
    92 
     90        actor_start( e );
    9391        sout | "started";
    94 
    9592        derived_msg messages[xr];
    96 
    9793        derived_actor actors[xr];
    9894
     
    10096                messages[r]{ Z[r], X[r], Y };
    10197        } // for
    102 
    10398        for ( r; xr ) {
    10499                actors[r] | messages[r];
     
    106101
    107102        sout | "stopping";
    108 
    109         stop_actor_system();
    110 
     103        actor_stop();
    111104        sout | "stopped";
    112105
  • tests/concurrency/actors/pingpong.cfa

    rb006c51e r10a9479d  
    4747        processor p[Processors - 1];
    4848
    49         start_actor_system( Processors ); // test passing number of processors
     49        actor_start( Processors ); // test passing number of processors
    5050        ping pi_actor;
    5151        pong po_actor;
     
    5454        p_msg m;
    5555        pi_actor | m;
    56         stop_actor_system();
     56        actor_stop();
    5757
    5858        sout | "end";
  • tests/concurrency/actors/poison.cfa

    rb006c51e r10a9479d  
    1515        sout | "Finished";
    1616        {
    17                 start_actor_system();
     17                actor_start();
    1818                Server s[10];
    1919                for ( i; 10 ) {
    2020                        s[i] | finished_msg;
    2121                }
    22                 stop_actor_system();
     22                actor_stop();
    2323        }
    2424
    2525        sout | "Delete";
    2626        {
    27                 start_actor_system();
     27                actor_start();
    2828                for ( i; 10 ) {
    2929                        Server * s = alloc();
     
    3131                        (*s) | delete_msg;
    3232                }
    33                 stop_actor_system();
     33                actor_stop();
    3434        }
    3535
    3636        sout | "Destroy";
    3737        {
    38                 start_actor_system();
     38                actor_start();
    3939                Server s[10];
    4040                for ( i; 10 )
    4141                        s[i] | destroy_msg;
    42                 stop_actor_system();
     42                actor_stop();
    4343                for ( i; 10 )
    4444                        if (s[i].val != 777)
  • tests/concurrency/actors/static.cfa

    rb006c51e r10a9479d  
    4545
    4646        executor e{ 0, 1, 1, false };
    47         start_actor_system( e );
    48 
     47        actor_start( e );
    4948        sout | "started";
    50 
    5149        derived_msg msg;
    52 
    5350        derived_actor actor;
    54 
    5551        actor | msg;
    56 
    57         stop_actor_system();
    58 
     52        actor_stop();
    5953        sout | "stopped";
    6054}
  • tests/concurrency/actors/types.cfa

    rb006c51e r10a9479d  
    6767
    6868        sout | "basic test";
    69         start_actor_system( Processors ); // test passing number of processors
     69        actor_start( Processors ); // test passing number of processors
    7070        derived_actor a;
    7171        d_msg b, c;
     
    7373        c.num = 2;
    7474        a | b | c;
    75         stop_actor_system();
     75        actor_stop();
    7676
    7777        sout | "same message and different actors test";
    78         start_actor_system(); // let system detect # of processors
     78        actor_start(); // let system detect # of processors
    7979        derived_actor2 d_ac2_0, d_ac2_1;
    8080        d_msg d_ac2_msg;
     
    8282        d_ac2_0 | d_ac2_msg;
    8383        d_ac2_1 | d_ac2_msg;
    84         stop_actor_system();
     84        actor_stop();
    8585
    8686       
     
    8888                sout | "same message and different actor types test";
    8989                executor e{ 0, Processors, Processors == 1 ? 1 : Processors * 4, false };
    90                 start_actor_system( e ); // pass an explicit executor
     90                actor_start( e ); // pass an explicit executor
    9191                derived_actor2 d_ac2_2;
    9292                derived_actor3 d_ac3_0;
     
    9595                d_ac3_0 | d_ac23_msg;
    9696                d_ac2_2 | d_ac23_msg;
    97                 stop_actor_system();
     97                actor_stop();
    9898        } // RAII to clean up executor
    9999
     
    101101                sout | "different message types, one actor test";
    102102                executor e{ 1, Processors, Processors == 1 ? 1 : Processors * 4, true };
    103                 start_actor_system( Processors );
     103                actor_start( Processors );
    104104                derived_actor3 a3;
    105105                d_msg b1;
     
    108108                c2.num = 5;
    109109                a3 | b1 | c2;
    110                 stop_actor_system();
     110                actor_stop();
    111111        } // RAII to clean up executor
    112112
     
    114114                sout | "nested inheritance actor test";
    115115                executor e{ 1, Processors, Processors == 1 ? 1 : Processors * 4, true };
    116                 start_actor_system( Processors );
     116                actor_start( Processors );
    117117                derived_actor4 a4;
    118118                d_msg b1;
     
    121121                c2.num = 5;
    122122                a4 | b1 | c2;
    123                 stop_actor_system();
     123                actor_stop();
    124124        } // RAII to clean up executor
    125125
  • tests/concurrency/barrier/order.cfa

    rb006c51e r10a9479d  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // order.cfa -- validates barriers the return value of
    8 //                                 barrier block
     7// order.cfa -- validates barrier return value from barrier block
    98//
    109// Author           : Thierry Delisle
    1110// Created On       : Fri Apr 01 11:39:09 2022
    12 // Last Modified By :
    13 // Last Modified On :
    14 // Update Count     :
     11// Last Modified By : Peter A. Buhr
     12// Last Modified On : Sun Nov 10 11:22:56 2024
     13// Update Count     : 20
    1514//
    16 
    17 // Test validates barrier and block return value by checking
    18 // that no more than one thread gets the same return value
    1915
    2016#include <concurrency/barrier.hfa>
     
    2319#include <thread.hfa>
    2420
    25 const unsigned NUM_LAPS = 173;
    26 const unsigned NUM_THREADS = 11;
     21enum { NUM_LAPS = 173, NUM_THREADS = 11 };
    2722
    28 // The barrier we are testing
    2923barrier bar = { NUM_THREADS };
    3024
    31 // The return values of the previous generation.
    32 volatile unsigned * generation;
     25volatile unsigned generation = 0;                                               // count laps
     26void last() {
     27        generation += 1;                                                                        // last thread at barrier advances
     28}
     29volatile unsigned * generations;                                                // global array pointer
    3330
    3431thread Tester {};
    3532void main( Tester & this ) {
    36         // Repeat a few times
    37         for(l; NUM_LAPS) {
    38                 // Yield for chaos
    39                 yield( prng(this, 10) );
     33        for ( l; NUM_LAPS ) {
     34                yield( prng( this, 10 ) );                                              // yield for chaos
     35                unsigned int order = block( bar, last );                // block at barrier
    4036
    41                 // Block and what order we arrived
    42                 unsigned ret = block(bar);
    43 
    44                 // Check what was the last generation of that last thread in this position
    45                 unsigned g = generation[ret];
    46 
    47                 // Is it what we expect?
    48                 if(g != l) {
    49                         // Complain that they are different
    50                         sout | "Gen" | l | ": Expeced generation at" | ret | "to be" | l | "was" | g;
    51                 }
    52 
    53                 // Mark the expected next generation
    54                 generation[ret] = l+1;
    55         }
     37                // For G == T, no thread should be able to advance generation until current generation finishes.
     38                if ( generation - 1 != l || generations[order] != l ) { // generation advanced in block
     39                        mutex( sout ) sout | "mismatched generation, expected" | l | "got" | generation;
     40                } // if
     41                generations[order] = l + 1;                                             // every thread advances their current order generation
     42        } // for
    5643}
    5744
    5845int main() {
    59         // Create the data ans zero it.
    6046        volatile unsigned gen_data[NUM_THREADS];
    61         for(t; NUM_THREADS)
    62                 gen_data[t] = 0;
     47        for( t; NUM_THREADS ) gen_data[t] = 0;
     48        generations = gen_data;                                                         // global points at local
    6349
    64         generation = gen_data;
    65 
    66         // Run the experiment
    67         processor p[4];
    68         {
     50        processor p[4];                                                                         // parallelism
     51        {                                                                                                       // run experiment
    6952                Tester testers[NUM_THREADS];
    7053        }
Note: See TracChangeset for help on using the changeset viewer.