Changes in / [e869e434:b14dd030]
- Location:
- doc
- Files:
-
- 9 edited
-
LaTeXmacros/common.tex (modified) (3 diffs)
-
generic_types/generic_types.tex (modified) (20 diffs)
-
rob_thesis/cfa-format.tex (modified) (1 diff)
-
rob_thesis/conclusions.tex (modified) (1 diff)
-
rob_thesis/ctordtor.tex (modified) (28 diffs)
-
rob_thesis/intro.tex (modified) (21 diffs)
-
rob_thesis/tuples.tex (modified) (22 diffs)
-
rob_thesis/variadic.tex (modified) (11 diffs)
-
user/user.tex (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/LaTeXmacros/common.tex
re869e434 rb14dd030 1 2 1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -*- Mode: Latex -*- %%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 2 %% … … 12 11 %% Created On : Sat Apr 9 10:06:17 2016 13 12 %% Last Modified By : Peter A. Buhr 14 %% Last Modified On : Wed Apr 12 11:32:26201715 %% Update Count : 25 713 %% Last Modified On : Wed Apr 5 23:19:42 2017 14 %% Update Count : 255 16 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 17 16 … … 45 44 \newcommand{\CCtwenty}{\rm C\kern-.1em\hbox{+\kern-.25em+}20} % C++20 symbolic name 46 45 \newcommand{\Celeven}{C11\xspace} % C11 symbolic name 47 \newcommand{\Csharp}{C\raisebox{ -0.65ex}{\large$^\sharp$}\xspace} % C# symbolic name46 \newcommand{\Csharp}{C\raisebox{0.4ex}{\#}\xspace} % C# symbolic name 48 47 49 48 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -
doc/generic_types/generic_types.tex
re869e434 rb14dd030 19 19 \newcommand{\C}[2][\@empty]{\ifx#1\@empty\else\global\setlength{\columnposn}{#1}\global\columnposn=\columnposn\fi\hfill\makebox[\textwidth-\columnposn][l]{\lst@commentstyle{#2}}} 20 20 \newcommand{\CRT}{\global\columnposn=\gcolumnposn} 21 22 \newcommand{\TODO}[1]{\textbf{TODO}: {\itshape #1}} % TODO included23 %\newcommand{\TODO}[1]{} % TODO elided24 % Latin abbreviation25 \newcommand{\abbrevFont}{\textit} % set empty for no italics26 \newcommand*{\eg}{%27 \@ifnextchar{,}{\abbrevFont{e}.\abbrevFont{g}.}%28 {\@ifnextchar{:}{\abbrevFont{e}.\abbrevFont{g}.}%29 {\abbrevFont{e}.\abbrevFont{g}.,\xspace}}%30 }%31 \newcommand*{\ie}{%32 \@ifnextchar{,}{\abbrevFont{i}.\abbrevFont{e}.}%33 {\@ifnextchar{:}{\abbrevFont{i}.\abbrevFont{e}.}%34 {\abbrevFont{i}.\abbrevFont{e}.,\xspace}}%35 }%36 \newcommand*{\etc}{%37 \@ifnextchar{.}{\abbrevFont{etc}}%38 {\abbrevFont{etc}.\xspace}%39 }%40 \newcommand{\etal}{%41 \@ifnextchar{.}{\abbrevFont{et~al}}%42 {\abbrevFont{et al}.\xspace}%43 }%44 % \newcommand{\eg}{\textit{e}.\textit{g}.,\xspace}45 % \newcommand{\ie}{\textit{i}.\textit{e}.,\xspace}46 % \newcommand{\etc}{\textit{etc}.,\xspace}47 21 \makeatother 48 22 … … 56 30 \newcommand{\CS}{C\raisebox{-0.7ex}{\Large$^\sharp$}\xspace} 57 31 \newcommand{\Textbf}[1]{{\color{red}\textbf{#1}}} 32 33 \newcommand{\TODO}[1]{\textbf{TODO}: {\itshape #1}} % TODO included 34 %\newcommand{\TODO}[1]{} % TODO elided 35 \newcommand{\eg}{\textit{e}.\textit{g}.,\xspace} 36 \newcommand{\ie}{\textit{i}.\textit{e}.,\xspace} 37 \newcommand{\etc}{\textit{etc}.,\xspace} 58 38 59 39 % CFA programming language, based on ANSI C (with some gcc additions) … … 157 137 & 2017 & 2012 & 2007 & 2002 & 1997 & 1992 & 1987 \\ 158 138 \hline 159 Java & 1 & 1 & 1 & 1 & 12& - & - \\139 Java & 1 & 1 & 1 & 3 & 13 & - & - \\ 160 140 \hline 161 \Textbf{C} & \Textbf{2}& \Textbf{2}& \Textbf{2}& \Textbf{ 2}& \Textbf{1}& \Textbf{1}& \Textbf{1} \\141 \Textbf{C} & \Textbf{2}& \Textbf{2}& \Textbf{2}& \Textbf{1}& \Textbf{1}& \Textbf{1}& \Textbf{1} \\ 162 142 \hline 163 143 \CC & 3 & 3 & 3 & 3 & 2 & 2 & 4 \\ … … 175 155 (4) Extensions introduced by \CFA must be translated in the most efficient way possible. 176 156 These goals ensure existing C code-bases can be converted to \CFA incrementally with minimal effort, and C programmers can productively generate \CFA code without training beyond the features being used. 177 Unfortunately, \CC is actively diverging from C, so incremental additions require significant effort and training, coupled with multiple legacy design-choices that cannot be updated.157 We claim \CC is diverging from C, and hence, incremental additions of language features require significant effort and training, while suffering from historically poor design choices. 178 158 179 159 \CFA is currently implemented as a source-to-source translator from \CFA to the GCC-dialect of C~\citep{GCCExtensions}, allowing it to leverage the portability and code optimizations provided by GCC, meeting goals (1)-(3). Ultimately, a compiler is necessary for advanced features and optimal performance. … … 199 179 int val = twice( twice( 3.7 ) ); 200 180 \end{lstlisting} 201 which works for any type @T@ with a matching addition operator. The polymorphism is achieved by creating a wrapper function for calling @+@ with @T@ bound to @double@, then passing this function to the first call of @twice@. There is now the option of using the same @twice@ and converting the result to @int@ on assignment, or creating another @twice@ with type parameter @T@ bound to @int@ because \CFA uses the return type, as in~\cite{Ada}, in its type analysis. 202 The first approach has a late conversion from @double@ to @int@ on the final assignment, while the second has an eager conversion to @int@. \CFA minimizes the number of conversions and their potential to lose information, so it selects the first approach, which corresponds with C-programmer intuition. 181 which works for any type @T@ with a matching addition operator. The polymorphism is achieved by creating a wrapper function for calling @+@ with @T@ bound to @double@, then passing this function to the first call of @twice@. There is now the option of using the same @twice@ and converting the result to @int@ on assignment, or creating another @twice@ with type parameter @T@ bound to @int@ because \CFA uses the return type (as in~\cite{Ada}) in its type analysis. The first approach has a late conversion from @int@ to @double@ on the final assignment, while the second has an eager conversion to @int@. \CFA minimizes the number of conversions and their potential to lose information, so it selects the first approach, which corresponds with C-programmer intuition. 203 182 204 183 Crucial to the design of a new programming language are the libraries to access thousands of external software features. … … 207 186 \begin{lstlisting} 208 187 void * bsearch( const void * key, const void * base, size_t nmemb, size_t size, 209 int (* compar)( const void *, const void *));188 int (* compar)(const void *, const void *)); 210 189 int comp( const void * t1, const void * t2 ) { return *(double *)t1 < *(double *)t2 ? -1 : 211 190 *(double *)t2 < *(double *)t1 ? 1 : 0; } … … 225 204 int posn = bsearch( 5.0, vals, 10 ); 226 205 \end{lstlisting} 227 The nested function @comp@ provides the hidden interface from typed \CFA to untyped (@void *@) C, plus the cast of the result. 228 Providing a hidden @comp@ function in \CC is awkward as lambdas do not use C calling-conventions and template declarations cannot appear at block scope. 206 The nested routine @comp@ (impossible in \CC as lambdas do not use C calling conventions) provides the hidden interface from typed \CFA to untyped (@void *@) C, plus the cast of the result. 229 207 As well, an alternate kind of return is made available: position versus pointer to found element. 230 208 \CC's type-system cannot disambiguate between the two versions of @bsearch@ because it does not use the return type in overload resolution, nor can \CC separately compile a templated @bsearch@. … … 270 248 Hence, the single name @MAX@ replaces all the C type-specific names: @SHRT_MAX@, @INT_MAX@, @DBL_MAX@. 271 249 As well, restricted constant overloading is allowed for the values @0@ and @1@, which have special status in C, \eg the value @0@ is both an integer and a pointer literal, so its meaning depends on context. 272 In addition, several operations are defined in terms values @0@ and @1@, \eg: 250 In addition, several operations are defined in terms values @0@ and @1@. 251 For example, 273 252 \begin{lstlisting} 274 253 int x; … … 296 275 return total; } 297 276 \end{lstlisting} 298 299 In fact, the set of trait operators is incomplete, as there is no assignment requirement for type @T@, but @otype@ is syntactic sugar for the following implicit trait: 277 A trait name plays no part in type equivalence; it is solely a macro for a list of assertions. 278 Traits may overlap assertions without conflict, and therefore, do not form a hierarchy. 279 280 In fact, the set of operators is incomplete, \eg no assignment, but @otype@ is syntactic sugar for the following implicit trait: 300 281 \begin{lstlisting} 301 282 trait otype( dtype T | sized(T) ) { // sized is a pseudo-trait for types with known size and alignment … … 327 308 % \end{lstlisting} 328 309 329 In summation, the \CFA type-system uses \emph{nominal typing} for concrete types, matching with the C type-system, and \emph{structural typing} for polymorphic types. 330 Hence, trait names play no part in type equivalence; 331 the names are simply macros for a list of polymorphic assertions, which are expanded at usage sites. 332 Nevertheless, trait names form a logical subtype-hierarchy with @dtype@ at the top, where traits often contain overlapping assertions, \eg operator @+@. 333 Traits are used like interfaces in Java or abstract base-classes in \CC, but without the nominal inheritance-relationships. 334 Instead, each polymorphic function (or generic type) defines the structural type needed for its execution (polymorphic type-key), and this key is fulfilled at each call site from the lexical environment, which is similar to Go~\citep{Go} interfaces. 335 Hence, new lexical scopes and nested functions are used extensively to create local subtypes, as in the @qsort@ example, without having to manage a nominal-inheritance hierarchy. 336 (Nominal inheritance can be approximated with traits using marker variables or functions, as is done in Go.) 337 338 % Nominal inheritance can be simulated with traits using marker variables or functions: 339 % \begin{lstlisting} 340 % trait nominal(otype T) { 341 % T is_nominal; 342 % }; 343 % int is_nominal; $\C{// int now satisfies the nominal trait}$ 344 % \end{lstlisting} 345 % 346 % Traits, however, are significantly more powerful than nominal-inheritance interfaces; most notably, traits may be used to declare a relationship \emph{among} multiple types, a property that may be difficult or impossible to represent in nominal-inheritance type systems: 347 % \begin{lstlisting} 348 % trait pointer_like(otype Ptr, otype El) { 349 % lvalue El *?(Ptr); $\C{// Ptr can be dereferenced into a modifiable value of type El}$ 350 % } 351 % struct list { 352 % int value; 353 % list *next; $\C{// may omit "struct" on type names as in \CC}$ 354 % }; 355 % typedef list *list_iterator; 356 % 357 % lvalue int *?( list_iterator it ) { return it->value; } 358 % \end{lstlisting} 359 % In the example above, @(list_iterator, int)@ satisfies @pointer_like@ by the user-defined dereference function, and @(list_iterator, list)@ also satisfies @pointer_like@ by the built-in dereference operator for pointers. Given a declaration @list_iterator it@, @*it@ can be either an @int@ or a @list@, with the meaning disambiguated by context (\eg @int x = *it;@ interprets @*it@ as an @int@, while @(*it).value = 42;@ interprets @*it@ as a @list@). 360 % While a nominal-inheritance system with associated types could model one of those two relationships by making @El@ an associated type of @Ptr@ in the @pointer_like@ implementation, few such systems could model both relationships simultaneously. 361 310 Traits may be used for many of the same purposes as interfaces in Java or abstract base classes in \CC. Unlike Java interfaces or \CC base classes, \CFA types do not explicitly state any inheritance relationship to traits they satisfy, which is a form of structural inheritance, similar to the implementation of an interface in Go~\citep{Go}, as opposed to the nominal inheritance model of Java and \CC. 311 312 Nominal inheritance can be simulated with traits using marker variables or functions: 313 \begin{lstlisting} 314 trait nominal(otype T) { 315 T is_nominal; 316 }; 317 int is_nominal; $\C{// int now satisfies the nominal trait}$ 318 \end{lstlisting} 319 320 Traits, however, are significantly more powerful than nominal-inheritance interfaces; most notably, traits may be used to declare a relationship \emph{among} multiple types, a property that may be difficult or impossible to represent in nominal-inheritance type systems: 321 \begin{lstlisting} 322 trait pointer_like(otype Ptr, otype El) { 323 lvalue El *?(Ptr); $\C{// Ptr can be dereferenced into a modifiable value of type El}$ 324 } 325 struct list { 326 int value; 327 list *next; $\C{// may omit "struct" on type names as in \CC}$ 328 }; 329 typedef list *list_iterator; 330 331 lvalue int *?( list_iterator it ) { return it->value; } 332 \end{lstlisting} 333 334 In the example above, @(list_iterator, int)@ satisfies @pointer_like@ by the user-defined dereference function, and @(list_iterator, list)@ also satisfies @pointer_like@ by the built-in dereference operator for pointers. Given a declaration @list_iterator it@, @*it@ can be either an @int@ or a @list@, with the meaning disambiguated by context (\eg @int x = *it;@ interprets @*it@ as an @int@, while @(*it).value = 42;@ interprets @*it@ as a @list@). 335 While a nominal-inheritance system with associated types could model one of those two relationships by making @El@ an associated type of @Ptr@ in the @pointer_like@ implementation, few such systems could model both relationships simultaneously. 362 336 363 337 \section{Generic Types} 364 338 365 One of the known shortcomings of standard C is that it does not provide reusable type-safe abstractions for generic data structures and algorithms. Broadly speaking, there are three approaches to create data structures in C. One approach is to write bespoke data structures for each context in which they are needed. While this approach is flexible and supports integration with the C type-checker and tooling, it is also tedious and error-prone, especially for more complex data structures. 366 A second approach is to use @void *@--based polymorphism, \eg the C standard-library functions @bsearch@ and @qsort@, and does allow the use of common code for common functionality. However, basing all polymorphism on @void *@ eliminates the type-checker's ability to ensure that argument types are properly matched, often requiring a number of extra function parameters, pointer indirection, and dynamic allocation that would not otherwise be needed. 367 A third approach to generic code is to use preprocessor macros, which does allow the generated code to be both generic and type-checked, but errors may be difficult to interpret. Furthermore, writing and using preprocessor macros can be unnatural and inflexible. 368 369 Other languages use \emph{generic types}, \eg \CC and Java, to produce type-safe abstract data-types. \CFA also implements generic types that integrate efficiently and naturally with the existing polymorphic functions, while retaining backwards compatibility with C and providing separate compilation. However, for known concrete parameters, the generic type can be inlined, like \CC templates. 339 One of the known shortcomings of standard C is that it does not provide reusable type-safe abstractions for generic data structures and algorithms. Broadly speaking, there are three approaches to create data structures in C. One approach is to write bespoke data structures for each context in which they are needed. While this approach is flexible and supports integration with the C type-checker and tooling, it is also tedious and error-prone, especially for more complex data structures. A second approach is to use @void*@-based polymorphism. This approach is taken by the C standard library functions @qsort@ and @bsearch@, and does allow the use of common code for common functionality. However, basing all polymorphism on @void*@ eliminates the type-checker's ability to ensure that argument types are properly matched, often requires a number of extra function parameters, and also adds pointer indirection and dynamic allocation to algorithms and data structures that would not otherwise require them. A third approach to generic code is to use pre-processor macros to generate it -- this approach does allow the generated code to be both generic and type-checked, though any errors produced may be difficult to interpret. Furthermore, writing and invoking C code as preprocessor macros is unnatural and somewhat inflexible. 340 341 Other C-like languages such as \CC and Java use \emph{generic types} to produce type-safe abstract data types. \CFA implements generic types with some care taken that the generic types design for \CFA integrates efficiently and naturally with the existing polymorphic functions in \CFA while retaining backwards compatibility with C; maintaining separate compilation is a particularly important constraint on the design. However, where the concrete parameters of the generic type are known, there is no extra overhead for the use of a generic type, as for \CC templates. 370 342 371 343 A generic type can be declared by placing a @forall@ specifier on a @struct@ or @union@ declaration, and instantiated using a parenthesized list of types after the type name: 372 344 \begin{lstlisting} 373 forall( otype R, otype S) struct pair {374 R first;375 S second;345 forall(otype R, otype S) struct pair { 346 R first; 347 S second; 376 348 }; 377 forall( otype T ) T value( pair( const char *, T ) p ) { return p.second; } 378 forall( dtype F, otype T ) T value_p( pair( F *, T * ) p ) { return *p.second; } 379 380 pair( const char *, int ) p = { "magic", 42 }; 349 350 forall(otype T) 351 T value( pair(const char*, T) p ) { return p.second; } 352 353 forall(dtype F, otype T) 354 T value_p( pair(F*, T*) p ) { return *p.second; } 355 356 pair(const char*, int) p = { "magic", 42 }; 381 357 int magic = value( p ); 382 pair( void *, int * ) q = { 0, &p.second }; 358 359 pair(void*, int*) q = { 0, &p.second }; 383 360 magic = value_p( q ); 384 361 double d = 1.0; 385 pair( double *, double *) r = { &d, &d };362 pair(double*, double*) r = { &d, &d }; 386 363 d = value_p( r ); 387 364 \end{lstlisting} 388 365 389 \CFA classifies generic types as either \emph{concrete} or \emph{dynamic}. Concrete have a fixed memory layout regardless of type parameters, while dynamic vary in memory layout depending on their type parameters. A type may have polymorphic parameters but still be concrete, called \emph{dtype-static}. Polymorphic pointers are an example of dtype-static types, \eg @forall(dtype T) T *@ is a polymorphic type, but for any @T@, @T *@ is a fixed-sized pointer, and therefore, can be represented by a @void*@ in code generation.390 391 \CFA generic types also allow checked argument-constraints. For example, the following declaration of a sorted set-type ensuresthe set key supports equality and relational comparison:392 \begin{lstlisting} 393 forall( otype Key | { _Bool ?==?(Key, Key); _Bool ?<?(Key, Key); } ) struct sorted_set;394 \end{lstlisting} 395 396 397 \subsection{Concrete Generic -Types}398 399 The \CFA translator template-expands concrete generic-types into new structure types, affording maximal inlining. To enable inter-operation among equivalent instantiations of a generic type, the translator saves the set of instantiations currently in scope and reuses the generated structure declarations where appropriate. For example, a function declaration that accepts or returns a concrete generic-type produces a declaration for the instantiated struct in the same scope, which all callers may reuse. For example, the concrete instantiation for @pair( const char *, int )@is:366 \CFA classifies generic types as either \emph{concrete} or \emph{dynamic}. Concrete generic types have a fixed memory layout regardless of type parameters, while dynamic generic types vary in their in-memory layout depending on their type parameters. A type may have polymorphic parameters but still be concrete; in \CFA such types are called \emph{dtype-static}. Polymorphic pointers are an example of dtype-static types -- @forall(dtype T) T*@ is a polymorphic type, but for any @T@ chosen, @T*@ has exactly the same in-memory representation as a @void*@, and can therefore be represented by a @void*@ in code generation. 367 368 \CFA generic types may also specify constraints on their argument type to be checked by the compiler. For example, consider the following declaration of a sorted set-type, which ensures that the set key supports equality and relational comparison: 369 \begin{lstlisting} 370 forall(otype Key | { _Bool ?==?(Key, Key); _Bool ?<?(Key, Key); }) 371 struct sorted_set; 372 \end{lstlisting} 373 374 \subsection{Concrete Generic Types} 375 376 The \CFA translator instantiates concrete generic types by template-expanding them to fresh struct types; concrete generic types can therefore be used with zero runtime overhead. To enable inter-operation among equivalent instantiations of a generic type, the translator saves the set of instantiations currently in scope and reuses the generated struct declarations where appropriate. For example, a function declaration that accepts or returns a concrete generic type produces a declaration for the instantiated struct in the same scope, which all callers that can see that declaration may reuse. As an example of the expansion, the concrete instantiation for @pair(const char*, int)@ looks like this: 400 377 \begin{lstlisting} 401 378 struct _pair_conc1 { 402 const char * first;379 const char* first; 403 380 int second; 404 381 }; 405 382 \end{lstlisting} 406 383 407 A concrete generic -type with dtype-static parameters is also expanded to a structure type, but this type is used for all matching instantiations. In the above example, the @pair( F *, T * )@ parameter to @value_p@ is such a type; its expansion is below and itis used as the type of the variables @q@ and @r@ as well, with casts for member access where appropriate:384 A concrete generic type with dtype-static parameters is also expanded to a struct type, but this struct type is used for all matching instantiations. In the example above, the @pair(F*, T*)@ parameter to @value_p@ is such a type; its expansion looks something like this, and is used as the type of the variables @q@ and @r@ as well, with casts for member access where appropriate: 408 385 \begin{lstlisting} 409 386 struct _pair_conc0 { 410 void * first;411 void * second;387 void* first; 388 void* second; 412 389 }; 413 390 \end{lstlisting} 414 391 415 392 416 \subsection{Dynamic Generic-Types} 417 418 Though \CFA implements concrete generic-types efficiently, it also has a fully general system for dynamic generic types. 419 As mentioned in Section~\ref{sec:poly-fns}, @otype@ function parameters (in fact all @sized@ polymorphic parameters) come with implicit size and alignment parameters provided by the caller. 420 Dynamic generic-types also have an \emph{offset array} containing structure member-offsets. 421 A dynamic generic-union needs no such offset array, as all members are at offset 0 but size and alignment are still necessary. 422 Access to members of a dynamic structure is provided at runtime via base-displacement addressing with the structure pointer and the member offset (similar to the @offsetof@ macro), moving a compile-time offset calculation to runtime. 423 424 The offset arrays are statically generated where possible. 425 If a dynamic generic-type is declared to be passed or returned by value from a polymorphic function, the translator can safely assume the generic type is complete (\ie has a known layout) at any call-site, and the offset array is passed from the caller; 426 if the generic type is concrete at the call site, the elements of this offset array can even be statically generated using the C @offsetof@ macro. 427 As an example, @p.second@ in the @value@ function above is implemented as @*(p + _offsetof_pair[1])@, where @p@ is a @void *@, and @_offsetof_pair@ is the offset array passed into @value@ for @pair( const char *, T )@. 428 The offset array @_offsetof_pair@ is generated at the call site as @size_t _offsetof_pair[] = { offsetof(_pair_conc1, first), offsetof(_pair_conc1, second) }@. 429 430 In some cases the offset arrays cannot be statically generated. For instance, modularity is generally provided in C by including an opaque forward-declaration of a structure and associated accessor and mutator functions in a header file, with the actual implementations in a separately-compiled @.c@ file. 431 \CFA supports this pattern for generic types, but the caller does not know the actual layout or size of the dynamic generic-type, and only holds it by a pointer. 432 The \CFA translator automatically generates \emph{layout functions} for cases where the size, alignment, and offset array of a generic struct cannot be passed into a function from that function's caller. 433 These layout functions take as arguments pointers to size and alignment variables and a caller-allocated array of member offsets, as well as the size and alignment of all @sized@ parameters to the generic structure (un@sized@ parameters are forbidden from being used in a context that affects layout). 434 Results of these layout functions are cached so that they are only computed once per type per function. %, as in the example below for @pair@. 393 \subsection{Dynamic Generic Types} 394 395 Though \CFA implements concrete generic types efficiently, it also has a fully general system for computing with dynamic generic types. As mentioned in Section~\ref{sec:poly-fns}, @otype@ function parameters (in fact all @sized@ polymorphic parameters) come with implicit size and alignment parameters provided by the caller. Dynamic generic structs also have implicit size and alignment parameters, and also an \emph{offset array} which contains the offsets of each member of the struct\footnote{Dynamic generic unions need no such offset array, as all members are at offset 0; the size and alignment parameters are still provided for dynamic unions, however.}. Access to members\footnote{The \lstinline@offsetof@ macro is implemented similarly.} of a dynamic generic struct is provided by adding the corresponding member of the offset array to the struct pointer at runtime, essentially moving a compile-time offset calculation to runtime where necessary. 396 397 These offset arrays are statically generated where possible. If a dynamic generic type is declared to be passed or returned by value from a polymorphic function, the translator can safely assume that the generic type is complete (that is, has a known layout) at any call-site, and the offset array is passed from the caller; if the generic type is concrete at the call site the elements of this offset array can even be statically generated using the C @offsetof@ macro. As an example, @p.second@ in the @value@ function above is implemented as @*(p + _offsetof_pair[1])@, where @p@ is a @void*@, and @_offsetof_pair@ is the offset array passed in to @value@ for @pair(const char*, T)@. The offset array @_offsetof_pair@ is generated at the call site as @size_t _offsetof_pair[] = { offsetof(_pair_conc1, first), offsetof(_pair_conc1, second) };@. 398 399 In some cases the offset arrays cannot be statically generated. For instance, modularity is generally provided in C by including an opaque forward-declaration of a struct and associated accessor and mutator routines in a header file, with the actual implementations in a separately-compiled \texttt{.c} file. \CFA supports this pattern for generic types, and in this instance the caller does not know the actual layout or size of the dynamic generic type, and only holds it by pointer. The \CFA translator automatically generates \emph{layout functions} for cases where the size, alignment, and offset array of a generic struct cannot be passed in to a function from that function's caller. These layout functions take as arguments pointers to size and alignment variables and a caller-allocated array of member offsets, as well as the size and alignment of all @sized@ parameters to the generic struct (un-@sized@ parameters are forbidden from the language from being used in a context that affects layout). Results of these layout functions are cached so that they are only computed once per type per function.%, as in the example below for @pair@. 435 400 % \begin{lstlisting} 436 401 % static inline void _layoutof_pair(size_t* _szeof_pair, size_t* _alignof_pair, size_t* _offsetof_pair, … … 438 403 % *_szeof_pair = 0; // default values 439 404 % *_alignof_pair = 1; 440 % 405 441 406 % // add offset, size, and alignment of first field 442 407 % _offsetof_pair[0] = *_szeof_pair; 443 408 % *_szeof_pair += _szeof_R; 444 409 % if ( *_alignof_pair < _alignof_R ) *_alignof_pair = _alignof_R; 445 % 410 446 411 % // padding, offset, size, and alignment of second field 447 412 % if ( *_szeof_pair & (_alignof_S - 1) ) … … 450 415 % *_szeof_pair += _szeof_S; 451 416 % if ( *_alignof_pair < _alignof_S ) *_alignof_pair = _alignof_S; 452 % 417 453 418 % // pad to struct alignment 454 419 % if ( *_szeof_pair & (*_alignof_pair - 1) ) … … 456 421 % } 457 422 % \end{lstlisting} 458 Layout functions also allow generic types to be used in a function definition without reflecting them in the function signature. 459 For instance, a function that strips duplicate values from an unsorted @vector(T)@ would likely have a pointer to the vector as its only explicit parameter, but use some sort of @set(T)@ internally to test for duplicate values. 460 This function could acquire the layout for @set(T)@ by calling its layout function with the layout of @T@ implicitly passed into the function. 461 462 Whether a type is concrete, dtype-static, or dynamic is decided solely on the type parameters and @forall@ clause on a declaration. 463 This design allows opaque forward declarations of generic types, \eg @forall(otype T) struct Box@ -- like in C, all uses of @Box(T)@ can be separately compiled, and callers from other translation units know the proper calling conventions to use. 464 If the definition of a structure type is included in deciding whether a generic type is dynamic or concrete, some further types may be recognized as dtype-static (\eg @forall(otype T) struct unique_ptr { T* p }@ does not depend on @T@ for its layout, but the existence of an @otype@ parameter means that it \emph{could}.), but preserving separate compilation (and the associated C compatibility) in the existing design is judged to be an appropriate trade-off. 465 423 424 Layout functions also allow generic types to be used in a function definition without reflecting them in the function signature. For instance, a function that strips duplicate values from an unsorted @vector(T)@ would likely have a pointer to the vector as its only explicit parameter, but use some sort of @set(T)@ internally to test for duplicate values. This function could acquire the layout for @set(T)@ by calling its layout function with the layout of @T@ implicitly passed into the function. 425 426 Whether a type is concrete, dtype-static, or dynamic is decided based solely on the type parameters and @forall@ clause on the struct declaration. This design allows opaque forward declarations of generic types like @forall(otype T) struct Box;@ -- like in C, all uses of @Box(T)@ can be in a separately compiled translation unit, and callers from other translation units know the proper calling conventions to use. If the definition of a struct type was included in the decision of whether a generic type is dynamic or concrete, some further types may be recognized as dtype-static (\eg @forall(otype T) struct unique_ptr { T* p };@ does not depend on @T@ for its layout, but the existence of an @otype@ parameter means that it \emph{could}.), but preserving separate compilation (and the associated C compatibility) in the existing design is judged to be an appropriate trade-off. 466 427 467 428 \subsection{Applications} 468 429 \label{sec:generic-apps} 469 430 470 The reuse of dtype-static structure instantiations enables useful programming patterns at zero runtime cost. The most important such pattern is using @forall(dtype T) T *@ as a type-checked replacement for @void *@, \eg creating a lexicographic comparison for pairs of pointers used by @bsearch@ or @qsort@: 471 \begin{lstlisting} 472 forall(dtype T) int lexcmp( pair( T *, T * ) * a, pair( T *, T * ) * b, int (* cmp)( T *, T * ) ) { 473 return cmp( a->first, b->first ) ? : cmp( a->second, b->second ); 474 } 475 \end{lstlisting} 476 % int c = cmp( a->first, b->first ); 477 % if ( c == 0 ) c = cmp( a->second, b->second ); 478 % return c; 479 Since @pair(T *, T * )@ is a concrete type, there are no implicit parameters passed to @lexcmp@, so the generated code is identical to a function written in standard C using @void *@, yet the \CFA version is type-checked to ensure the fields of both pairs and the arguments to the comparison function match in type. 480 481 Another useful pattern enabled by reused dtype-static type instantiations is zero-cost \emph{tag-structures}. 482 Sometimes information is only used for type-checking and can be omitted at runtime, \eg: 431 The reuse of dtype-static struct instantiations enables some useful programming patterns at zero runtime cost. The most important such pattern is using @forall(dtype T) T*@ as a type-checked replacement for @void*@, as in this example, which takes a @qsort@ or @bsearch@-compatible comparison routine and creates a similar lexicographic comparison for pairs of pointers: 432 \begin{lstlisting} 433 forall(dtype T) 434 int lexcmp( pair(T*, T*)* a, pair(T*, T*)* b, int (*cmp)(T*, T*) ) { 435 int c = cmp(a->first, b->first); 436 if ( c == 0 ) c = cmp(a->second, b->second); 437 return c; 438 } 439 \end{lstlisting} 440 Since @pair(T*, T*)@ is a concrete type, there are no added implicit parameters to @lexcmp@, so the code generated by \CFA is effectively identical to a version of this function written in standard C using @void*@, yet the \CFA version is type-checked to ensure that the fields of both pairs and the arguments to the comparison function match in type. 441 442 Another useful pattern enabled by reused dtype-static type instantiations is zero-cost ``tag'' structs. Sometimes a particular bit of information is only useful for type-checking, and can be omitted at runtime. Tag structs can be used to provide this information to the compiler without further runtime overhead, as in the following example: 483 443 \begin{lstlisting} 484 444 forall(dtype Unit) struct scalar { unsigned long value; }; 445 485 446 struct metres {}; 486 447 struct litres {}; 487 448 488 forall(dtype U) scalar(U) ?+?( scalar(U) a, scalar(U) b ) { 449 forall(dtype U) 450 scalar(U) ?+?(scalar(U) a, scalar(U) b) { 489 451 return (scalar(U)){ a.value + b.value }; 490 452 } 453 491 454 scalar(metres) half_marathon = { 21093 }; 492 455 scalar(litres) swimming_pool = { 2500000 }; 456 493 457 scalar(metres) marathon = half_marathon + half_marathon; 494 458 scalar(litres) two_pools = swimming_pool + swimming_pool; 495 marathon + swimming_pool; $\C{// compilation ERROR}$ 496 \end{lstlisting} 497 @scalar@ is a dtype-static type, so all uses have a single structure definition, containing @unsigned long@, and can share the same implementations of common functions like @?+?@. 498 These implementations may even be separately compiled, unlike \CC template functions. 499 However, the \CFA type-checker ensures matching types are used by all calls to @?+?@, preventing nonsensical computations like adding a length to a volume. 459 marathon + swimming_pool; // ERROR -- caught by compiler 460 \end{lstlisting} 461 @scalar@ is a dtype-static type, so all uses of it use a single struct definition, containing only a single @unsigned long@, and can share the same implementations of common routines like @?+?@ -- these implementations may even be separately compiled, unlike \CC template functions. However, the \CFA type-checker ensures that matching types are used by all calls to @?+?@, preventing nonsensical computations like adding the length of a marathon to the volume of an olympic pool. 500 462 501 463 \section{Tuples} … … 504 466 The @pair(R, S)@ generic type used as an example in the previous section can be considered a special case of a more general \emph{tuple} data structure. The authors have implemented tuples in \CFA, with a design particularly motivated by two use cases: \emph{multiple-return-value functions} and \emph{variadic functions}. 505 467 506 In standard C, functions can return at most one value. This restriction results in code that emulates functions with multiple return values by \emph{aggregation} or by \emph{aliasing}. In the former situation, the function designer creates a record type that combines all of the return values into a single type. Unfortunately, the designer must come up with a name for the return type and for each of its fields. Unnecessary naming is a common programming language issue, introducing verbosity and a complication of the user's mental model. As such, this technique is effective when used sparingly, but can quickly get out of hand if many functions need to return different combinations of types. In the latter approach, the designer simulates multiple return values by passing the additional return values as pointer parameters. The pointer parameters are assigned inside of the function body to emulate a return. Using this approach, the caller is directly responsible for allocating storage for the additional temporary return values. This responsibility complicates the call site with a sequence of variable declarations leading up to the call. Also, while a disciplined use of @const@ can give clues about whether a pointer parameter is going to be used as an out parameter, it is not immediately obvious from only the function signature whether the callee expects such a parameter to be initialized before the call. Furthermore, while many C functions that accept pointers are designed so that it is safe to pass @NULL@ as a parameter, there are many C functions that are not null-safe. On a related note, C does not provide a standard mechanism to state that a parameter is going to be used as an additional return value, which makes the job of ensuring that a value is returned more difficult for the compiler.468 In standard C, functions can return at most one value. This restriction results in code that emulates functions with multiple return values by \emph{aggregation} or by \emph{aliasing}. In the former situation, the function designer creates a record type that combines all of the return values into a single type. Unfortunately, the designer must come up with a name for the return type and for each of its fields. Unnecessary naming is a common programming language issue, introducing verbosity and a complication of the user's mental model. As such, this technique is effective when used sparingly, but can quickly get out of hand if many functions need to return different combinations of types. In the latter approach, the designer simulates multiple return values by passing the additional return values as pointer parameters. The pointer parameters are assigned inside of the routine body to emulate a return. Using this approach, the caller is directly responsible for allocating storage for the additional temporary return values. This responsibility complicates the call site with a sequence of variable declarations leading up to the call. Also, while a disciplined use of @const@ can give clues about whether a pointer parameter is going to be used as an out parameter, it is not immediately obvious from only the routine signature whether the callee expects such a parameter to be initialized before the call. Furthermore, while many C routines that accept pointers are designed so that it is safe to pass @NULL@ as a parameter, there are many C routines that are not null-safe. On a related note, C does not provide a standard mechanism to state that a parameter is going to be used as an additional return value, which makes the job of ensuring that a value is returned more difficult for the compiler. 507 469 508 470 C does provide a mechanism for variadic functions through manipulation of @va_list@ objects, but it is notoriously type-unsafe. A variadic function is one that contains at least one parameter, followed by @...@ as the last token in the parameter list. In particular, some form of \emph{argument descriptor} is needed to inform the function of the number of arguments and their types, commonly a format string or counter parameter. It is important to note that both of these mechanisms are inherently redundant, because they require the user to specify information that the compiler knows explicitly. This required repetition is error prone, because it is easy for the user to add or remove arguments without updating the argument descriptor. In addition, C requires the programmer to hard code all of the possible expected types. As a result, it is cumbersome to write a variadic function that is open to extension. For example, consider a simple function that sums $N$ @int@s: … … 513 475 int ret = 0; 514 476 while(N) { 515 ret += va_arg(args, int); // must specify type516 N--;477 ret += va_arg(args, int); // must specify type 478 N--; 517 479 } 518 480 va_end(args); … … 527 489 In practice, compilers can provide warnings to help mitigate some of the problems. For example, GCC provides the @format@ attribute to specify that a function uses a format string, which allows the compiler to perform some checks related to the standard format specifiers. Unfortunately, this attribute does not permit extensions to the format string syntax, so a programmer cannot extend it to warn for mismatches with custom types. 528 490 529 530 491 \subsection{Tuple Expressions} 531 492 … … 534 495 \CFA allows declaration of \emph{tuple variables}, variables of tuple type. For example: 535 496 \begin{lstlisting} 536 [int, char] most_frequent(const char *);497 [int, char] most_frequent(const char*); 537 498 538 499 const char* str = "hello, world!"; … … 778 739 Unlike C, it is not necessary to hard code the expected type. This code is naturally open to extension, in that any user-defined type with a @?+?@ operator is automatically able to be used with the @sum@ function. That is to say, the programmer who writes @sum@ does not need full program knowledge of every possible data type, unlike what is necessary to write an equivalent function using the standard C mechanisms. Summing arbitrary heterogeneous lists is possible with similar code by adding the appropriate type variables and addition operators. 779 740 780 It is also possible to write a type-safe variadic print functionwhich can replace @printf@:741 It is also possible to write a type-safe variadic print routine which can replace @printf@: 781 742 \begin{lstlisting} 782 743 struct S { int x, y; }; … … 793 754 print("s = ", (S){ 1, 2 }, "\n"); 794 755 \end{lstlisting} 795 This example function showcases a variadic-template-like decomposition of the provided argument list. The individual @print@ functions allow printing a single element of a type. The polymorphic @print@ allows printing any list of types, as long as each individual type has a @print@ function. The individual print functions can be used to build up more complicated @print@ functions, such as for @S@, which is something that cannot be done with @printf@ in C.756 This example routine showcases a variadic-template-like decomposition of the provided argument list. The individual @print@ routines allow printing a single element of a type. The polymorphic @print@ allows printing any list of types, as long as each individual type has a @print@ function. The individual print functions can be used to build up more complicated @print@ routines, such as for @S@, which is something that cannot be done with @printf@ in C. 796 757 797 758 It is also possible to use @ttype@ polymorphism to provide arbitrary argument forwarding functions. For example, it is possible to write @new@ as a library function: … … 832 793 forall(dtype T0, dtype T1, dtype T2 | sized(T0) | sized(T1) | sized(T2)) 833 794 struct _tuple3 { // generated before the first 3-tuple 834 T0 field_0;835 T1 field_1;836 T2 field_2;795 T0 field_0; 796 T1 field_1; 797 T2 field_2; 837 798 }; 838 799 _tuple3_(int, double, int) y; -
doc/rob_thesis/cfa-format.tex
re869e434 rb14dd030 131 131 style=defaultStyle 132 132 } 133 \lstMakeShortInline[basewidth=0.5em,breaklines=true ,basicstyle=\normalsize\ttfamily\color{basicCol}]@ % single-character for \lstinline133 \lstMakeShortInline[basewidth=0.5em,breaklines=true]@ % single-character for \lstinline 134 134 135 135 \lstnewenvironment{cfacode}[1][]{ -
doc/rob_thesis/conclusions.tex
re869e434 rb14dd030 45 45 46 46 A caveat of this approach is that the @cleanup@ attribute only permits a name that refers to a function that consumes a single argument of type @T *@ for a variable of type @T@. 47 This means that any destructor that consumes multiple arguments ( \eg, because it is polymorphic) or any destructor that is a function pointer (\eg, because it is an assertion parameter) must be called through a local thunk.47 This means that any destructor that consumes multiple arguments (e.g., because it is polymorphic) or any destructor that is a function pointer (e.g., because it is an assertion parameter) must be called through a local thunk. 48 48 For example, 49 49 \begin{cfacode} -
doc/rob_thesis/ctordtor.tex
re869e434 rb14dd030 7 7 8 8 Since \CFA is a true systems language, it does not provide a garbage collector. 9 As well, \CFA is not an object-oriented programming language, \ie, structures cannot have routine members.9 As well, \CFA is not an object-oriented programming language, i.e., structures cannot have routine members. 10 10 Nevertheless, one important goal is to reduce programming complexity and increase safety. 11 11 To that end, \CFA provides support for implicit pre/post-execution of routines for objects, via constructors and destructors. … … 30 30 Next, @x@ is assigned the value of @y@. 31 31 In the last line, @z@ is implicitly initialized to 0 since it is marked @static@. 32 The key difference between assignment and initialization being that assignment occurs on a live object ( \ie, an object that contains data).32 The key difference between assignment and initialization being that assignment occurs on a live object (i.e., an object that contains data). 33 33 It is important to note that this means @x@ could have been used uninitialized prior to being assigned, while @y@ could not be used uninitialized. 34 34 Use of uninitialized variables yields undefined behaviour, which is a common source of errors in C programs. … … 79 79 80 80 In \CFA, a constructor is a function with the name @?{}@. 81 Like other operators in \CFA, the name represents the syntax used to call the constructor, \eg, @struct S = { ... };@.81 Like other operators in \CFA, the name represents the syntax used to call the constructor, e.g., @struct S = { ... };@. 82 82 Every constructor must have a return type of @void@ and at least one parameter, the first of which is colloquially referred to as the \emph{this} parameter, as in many object-oriented programming-languages (however, a programmer can give it an arbitrary name). 83 83 The @this@ parameter must have a pointer type, whose base type is the type of object that the function constructs. … … 114 114 In other words, a default constructor is a constructor that takes a single argument: the @this@ parameter. 115 115 116 In \CFA, a destructor is a function much like a constructor, except that its name is \lstinline!^?{}! and it take sonly one argument.117 A destructor for the @Array@ type can be defined as :116 In \CFA, a destructor is a function much like a constructor, except that its name is \lstinline!^?{}! and it take only one argument. 117 A destructor for the @Array@ type can be defined as such. 118 118 \begin{cfacode} 119 119 void ^?{}(Array * arr) { … … 167 167 } 168 168 \end{cfacode} 169 170 169 In \CFA, constructors are called implicitly in initialization contexts. 171 170 \begin{cfacode} 172 171 Array x, y = { 20, 0xdeadbeef }, z = y; 173 172 \end{cfacode} 174 Constructor calls look just like C initializers, which allows them to be inserted into legacy C code with minimal code changes, and also provides a very simple syntax that veteran C programmers are familiar with. 175 One downside of reusing C initialization syntax is that it is not possible to determine whether an object is constructed just by looking at its declaration, since that requires knowledge of whether the type is managed at that point in the program. 173 174 In \CFA, constructor calls look just like C initializers, which allows them to be inserted into legacy C code with minimal code changes, and also provides a very simple syntax that veteran C programmers are familiar with. 175 One downside of reusing C initialization syntax is that it isn't possible to determine whether an object is constructed just by looking at its declaration, since that requires knowledge of whether the type is managed at that point. 176 176 177 177 This example generates the following code … … 246 246 \end{cfacode} 247 247 Finally, constructors and destructors support \emph{operator syntax}. 248 Like other operators in \CFA, the function name mirrors the use-case, in that the question marks are placeholders for the first $N$ arguments.248 Like other operators in \CFA, the function name mirrors the use-case, in that the first $N$ arguments fill in the place of the question mark. 249 249 This syntactic form is similar to the new initialization syntax in \CCeleven, except that it is used in expression contexts, rather than declaration contexts. 250 250 \begin{cfacode} … … 272 272 Like other operators, the function name @?{}@ matches its operator syntax. 273 273 For example, @(&x){}@ calls the default constructor on the variable @x@, and produces @&x@ as a result. 274 A key example for this capability is the use of constructor expressions to initialize the result of a call to @malloc@.274 A key example for this capability is the use of constructor expressions to initialize the result of a call to standard C routine @malloc@. 275 275 \begin{cfacode} 276 276 struct X { ... }; 277 277 void ?{}(X *, double); 278 X * x = malloc( ){ 1.5 };278 X * x = malloc(sizeof(X)){ 1.5 }; 279 279 \end{cfacode} 280 280 In this example, @malloc@ dynamically allocates storage and initializes it using a constructor, all before assigning it into the variable @x@. 281 281 If this extension is not present, constructing dynamically allocated objects is much more cumbersome, requiring separate initialization of the pointer and initialization of the pointed-to memory. 282 282 \begin{cfacode} 283 X * x = malloc( );283 X * x = malloc(sizeof(X)); 284 284 x{ 1.5 }; 285 285 \end{cfacode} … … 291 291 struct X *_tmp_ctor; 292 292 struct X *x = ?{}( // construct result of malloc 293 _tmp_ctor=malloc _T(sizeof(struct X), _Alignof(struct X)), // store result of malloc293 _tmp_ctor=malloc(sizeof(struct X)), // store result of malloc 294 294 1.5 295 295 ), _tmp_ctor; // produce constructed result of malloc … … 297 297 It should be noted that this technique is not exclusive to @malloc@, and allows a user to write a custom allocator that can be idiomatically used in much the same way as a constructed @malloc@ call. 298 298 299 It should be noted that while it is possible to use operator syntax with destructors, destructors invalidate their argument, thus operator syntax with destructors is a statement and does not produce a value. 299 It is also possible to use operator syntax with destructors. 300 Unlike constructors, operator syntax with destructors is a statement and thus does not produce a value, since the destructed object is invalidated by the use of a destructor. 301 For example, \lstinline!^(&x){}! calls the destructor on the variable @x@. 300 302 301 303 \subsection{Function Generation} … … 374 376 The field constructors are constructors that consume a prefix of the structure's member-list. 375 377 That is, $N$ constructors are built of the form @void ?{}(S *, T$_{\text{M}_0}$)@, @void ?{}(S *, T$_{\text{M}_0}$, T$_{\text{M}_1}$)@, ..., @void ?{}(S *, T$_{\text{M}_0}$, T$_{\text{M}_1}$, ..., T$_{\text{M}_{N-1}}$)@, where members are copy constructed if they have a corresponding positional argument and are default constructed otherwise. 376 The addition of field constructors allows structures in \CFA to be used naturally in the same ways as used in C ( \ie, to initialize any prefix of the structure), \eg, @A a0 = { b }, a1 = { b, c }@.378 The addition of field constructors allows structures in \CFA to be used naturally in the same ways as used in C (i.e., to initialize any prefix of the structure), e.g., @A a0 = { b }, a1 = { b, c }@. 377 379 Extending the previous example, the following constructors are implicitly generated for @A@. 378 380 \begin{cfacode} … … 427 429 428 430 \subsection{Using Constructors and Destructors} 429 Implicitly generated constructor and destructor calls ignore the outermost type qualifiers, \eg@const@ and @volatile@, on a type by way of a cast on the first argument to the function.431 Implicitly generated constructor and destructor calls ignore the outermost type qualifiers, e.g. @const@ and @volatile@, on a type by way of a cast on the first argument to the function. 430 432 For example, 431 433 \begin{cfacode} … … 446 448 Here, @&s@ and @&s2@ are cast to unqualified pointer types. 447 449 This mechanism allows the same constructors and destructors to be used for qualified objects as for unqualified objects. 448 This ruleapplies only to implicitly generated constructor calls.450 This applies only to implicitly generated constructor calls. 449 451 Hence, explicitly re-initializing qualified objects with a constructor requires an explicit cast. 450 452 … … 487 489 Instead, @a2->x@ is initialized to @0@ as if it were a C object, because of the explicit initializer. 488 490 489 In addition to freedom, \ateq provides a simple path formigrating legacy C code to \CFA, in that objects can be moved from C-style initialization to \CFA gradually and individually.491 In addition to freedom, \ateq provides a simple path to migrating legacy C code to \CFA, in that objects can be moved from C-style initialization to \CFA gradually and individually. 490 492 It is worth noting that the use of unmanaged objects can be tricky to get right, since there is no guarantee that the proper invariants are established on an unmanaged object. 491 493 It is recommended that most objects be managed by sensible constructors and destructors, except where absolutely necessary. … … 501 503 { 502 504 void ?{}(S * s, int i) { s->x = i*2; } // locally hide autogen constructors 503 S s4; // error , no default constructor504 S s5 = { 3 }; // okay , local constructor505 S s6 = { 4, 5 }; // error , no field constructor505 S s4; // error 506 S s5 = { 3 }; // okay 507 S s6 = { 4, 5 }; // error 506 508 S s7 = s5; // okay 507 509 } … … 511 513 In this example, the inner scope declares a constructor from @int@ to @S@, which hides the default constructor and field constructors until the end of the scope. 512 514 513 When defining a constructor or destructor for a struct ure@S@, any members that are not explicitly constructed or destructed are implicitly constructed or destructed automatically.515 When defining a constructor or destructor for a struct @S@, any members that are not explicitly constructed or destructed are implicitly constructed or destructed automatically. 514 516 If an explicit call is present, then that call is taken in preference to any implicitly generated call. 515 517 A consequence of this rule is that it is possible, unlike \CC, to precisely control the order of construction and destruction of sub-objects on a per-constructor basis, whereas in \CC sub-object initialization and destruction is always performed based on the declaration order. … … 595 597 In practice, however, there could be many objects that can be constructed from a given @int@ (or, indeed, any arbitrary parameter list), and thus a complete solution to this problem would require fully exploring all possibilities. 596 598 597 More precisely, constructor calls cannot have a nesting depth greater than the number of array dimensions in the type of the initialized object, plus one.599 More precisely, constructor calls cannot have a nesting depth greater than the number of array components in the type of the initialized object, plus one. 598 600 For example, 599 601 \begin{cfacode} … … 607 609 { {14 }, { 15 } } // a2[1] 608 610 }; 609 A a3[4] = { // 1 dimension => max depth 2610 { { 11 }, { 12 } }, // error , three levels deep611 A a3[4] = { 612 { { 11 }, { 12 } }, // error 611 613 { 80 }, { 90 }, { 100 } 612 614 } … … 620 622 \label{sub:implicit_dtor} 621 623 Destructors are automatically called at the end of the block in which the object is declared. 622 In addition to this, destructors are automatically called when statements manipulate control flow to leave a block in which the object is declared, \eg, with return, break, continue, and goto statements.624 In addition to this, destructors are automatically called when statements manipulate control flow to leave a block in which the object is declared, e.g., with return, break, continue, and goto statements. 623 625 The example below demonstrates a simple routine with multiple return statements. 624 626 \begin{cfacode} … … 745 747 Exempt from these rules are intrinsic and built-in functions. 746 748 It should be noted that unmanaged objects are subject to copy constructor calls when passed as arguments to a function or when returned from a function, since they are not the \emph{target} of the copy constructor call. 747 That is, since the parameter is not marked as an unmanaged object using \ateq, it isbe copy constructed if it is returned by value or passed as an argument to another function, so to guarantee consistent behaviour, unmanaged objects must be copy constructed when passed as arguments.748 Th ese semantics are importantto bear in mind when using unmanaged objects, and could produce unexpected results when mixed with objects that are explicitly constructed.749 That is, since the parameter is not marked as an unmanaged object using \ateq, it will be copy constructed if it is returned by value or passed as an argument to another function, so to guarantee consistent behaviour, unmanaged objects must be copy constructed when passed as arguments. 750 This is an important detail to bear in mind when using unmanaged objects, and could produce unexpected results when mixed with objects that are explicitly constructed. 749 751 \begin{cfacode} 750 752 struct A; … … 761 763 identity(z); // copy construct z into x 762 764 \end{cfacode} 763 Note that unmanaged argument @z@ is logically copy constructed into managed parameter @x@; however, the translator must copy construct into a temporary variable to be passed as an argument, which is also destructed after the call. 764 A compiler could by-pass the argument temporaries since it is in control of the calling conventions and knows exactly where the called-function's parameters live. 765 Note that @z@ is copy constructed into a temporary variable to be passed as an argument, which is also destructed after the call. 765 766 766 767 This generates the following … … 858 859 This transformation provides @f@ with the address of the return variable so that it can be constructed into directly. 859 860 It is worth pointing out that this kind of signature rewriting already occurs in polymorphic functions that return by value, as discussed in \cite{Bilson03}. 860 A key difference in this case is that every function would need to be rewritten like this, since types can switch between managed and unmanaged at different scope levels, \eg861 A key difference in this case is that every function would need to be rewritten like this, since types can switch between managed and unmanaged at different scope levels, e.g. 861 862 \begin{cfacode} 862 863 struct A { int v; }; … … 873 874 Furthermore, it is not possible to overload C functions, so using @extern "C"@ to declare functions is of limited use. 874 875 875 It would be possible to regain some control by adding an attribute to struct ures that specifies whether they can be managed or not (perhaps \emph{manageable} or \emph{unmanageable}), and to emit an error in the case that a constructor or destructor is declared for an unmanageable type.876 Ideally, struct ures should be manageable by default, since otherwise the default case becomes more verbose.876 It would be possible to regain some control by adding an attribute to structs that specifies whether they can be managed or not (perhaps \emph{manageable} or \emph{unmanageable}), and to emit an error in the case that a constructor or destructor is declared for an unmanageable type. 877 Ideally, structs should be manageable by default, since otherwise the default case becomes more verbose. 877 878 This means that in general, function signatures would have to be rewritten, and in a select few cases the signatures would not be rewritten. 878 879 \begin{cfacode} … … 885 886 C h(); // rewritten void h(C *); 886 887 \end{cfacode} 887 An alternative is to make the attribute \emph{identifiable}, which states that objects of this type use the @this@ parameter as an identity.888 An alternative is to instead make the attribute \emph{identifiable}, which states that objects of this type use the @this@ parameter as an identity. 888 889 This strikes more closely to the visible problem, in that only types marked as identifiable would need to have the return value moved into the parameter list, and every other type could remain the same. 889 890 Furthermore, no restrictions would need to be placed on whether objects can be constructed. … … 1014 1015 1015 1016 \subsection{Global Initialization} 1016 In standard C, global variables can only be initialized to compile-time constant expressions, which places strict limitations on the programmer's ability to control the default values of objects. 1017 In standard C, global variables can only be initialized to compile-time constant expressions. 1018 This places strict limitations on the programmer's ability to control the default values of objects. 1017 1019 In \CFA, constructors and destructors are guaranteed to be run on global objects, allowing arbitrary code to be run before and after the execution of the main routine. 1018 1020 By default, objects within a translation unit are constructed in declaration order, and destructed in the reverse order. 1019 1021 The default order of construction of objects amongst translation units is unspecified. 1020 It is, however, guaranteed that any global objects in the standard library are initialized prior to the initialization of any object in auser program.1022 It is, however, guaranteed that any global objects in the standard library are initialized prior to the initialization of any object in the user program. 1021 1023 1022 1024 This feature is implemented in the \CFA translator by grouping every global constructor call into a function with the GCC attribute \emph{constructor}, which performs most of the heavy lifting \cite[6.31.1]{GCCExtensions}. … … 1051 1053 % https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes 1052 1054 % suggestion: implement this in CFA by picking objects with a specified priority and pulling them into their own init functions (could even group them by priority level -> map<int, list<ObjectDecl*>>) and pull init_priority forward into constructor and destructor attributes with the same priority level 1053 GCC provides an attribute @init_priority@ in \CC, which allows specifying the relative priority for initialization of global objects on a per-object basis.1055 GCC provides an attribute @init_priority@, which allows specifying the relative priority for initialization of global objects on a per-object basis in \CC. 1054 1056 A similar attribute can be implemented in \CFA by pulling marked objects into global constructor/destructor-attribute functions with the specified priority. 1055 1057 For example, … … 1074 1076 In standard C, it is possible to mark variables that are local to a function with the @static@ storage class. 1075 1077 Unlike normal local variables, a @static@ local variable is defined to live for the entire duration of the program, so that each call to the function has access to the same variable with the same address and value as it had in the previous call to the function. 1076 Much like global variables, @static@ variables can only be initialized to a \emph{compile-time constant value} so that a compiler is able to create storage for the variable and initialize it at compile-time.1078 Much like global variables, in C @static@ variables can only be initialized to a \emph{compile-time constant value} so that a compiler is able to create storage for the variable and initialize it at compile-time. 1077 1079 1078 1080 Yet again, this rule is too restrictive for a language with constructors and destructors. 1079 Since the initializer expression is not necessarily a compile-time constant and can depend on the current execution state of the function, \CFA modifies the definition of a @static@ local variable so that objects are guaranteed to be live from the time control flow reaches their declaration, until the end of the program.1080 Since standard C does not allow access to a @static@ local variable before the first time control flow reaches the declaration, this changedoes not preclude any valid C code.1081 Instead, \CFA modifies the definition of a @static@ local variable so that objects are guaranteed to be live from the time control flow reaches their declaration, until the end of the program, since the initializer expression is not necessarily a compile-time constant, but can depend on the current execution state of the function. 1082 Since standard C does not allow access to a @static@ local variable before the first time control flow reaches the declaration, this restriction does not preclude any valid C code. 1081 1083 Local objects with @static@ storage class are only implicitly constructed and destructed once for the duration of the program. 1082 1084 The object is constructed when its declaration is reached for the first time. … … 1088 1090 Since the parameter to @atexit@ is a parameter-less function, some additional tweaking is required. 1089 1091 First, the @static@ variable must be hoisted up to global scope and uniquely renamed to prevent name clashes with other global objects. 1090 If necessary, a local structure may need to be hoisted, as well. 1091 Second, a function is built that calls the destructor for the newly hoisted variable. 1092 Second, a function is built which calls the destructor for the newly hoisted variable. 1092 1093 Finally, the newly generated function is registered with @atexit@, instead of registering the destructor directly. 1093 1094 Since @atexit@ calls functions in the reverse order in which they are registered, @static@ local variables are guaranteed to be destructed in the reverse order that they are constructed, which may differ between multiple executions of the same program. … … 1155 1156 void f(T); 1156 1157 \end{cfacode} 1157 This allows easily specifying constraints that are common to all complete object -types very simply.1158 1159 Now that \CFA has constructors and destructors, more of a complete object's behaviour can be specified than was previously possible.1158 This allows easily specifying constraints that are common to all complete object types very simply. 1159 1160 Now that \CFA has constructors and destructors, more of a complete object's behaviour can be specified by than was previously possible. 1160 1161 As such, @otype@ has been augmented to include assertions for a default constructor, copy constructor, and destructor. 1161 1162 That is, the previous example is now equivalent to 1162 1163 \begin{cfacode} 1163 forall(dtype T | sized(T) | 1164 { T ?=?(T *, T); void ?{}(T *); void ?{}(T *, T); void ^?{}(T *); }) 1164 forall(dtype T | sized(T) | { T ?=?(T *, T); void ?{}(T *); void ?{}(T *, T); void ^?{}(T *); }) 1165 1165 void f(T); 1166 1166 \end{cfacode} 1167 Th ese additions allow@f@'s body to create and destroy objects of type @T@, and pass objects of type @T@ as arguments to other functions, following the normal \CFA rules.1168 A point of note here is that objects can be missing default constructors (and eventually other functions through deleted functions), so it is important for \CFA programmers to think carefully about the operations needed by their function, as to not over-constrain the acceptable parameter types and prevent potential reuse.1167 This allows @f@'s body to create and destroy objects of type @T@, and pass objects of type @T@ as arguments to other functions, following the normal \CFA rules. 1168 A point of note here is that objects can be missing default constructors (and eventually other functions through deleted functions), so it is important for \CFA programmers to think carefully about the operations needed by their function, as to not over-constrain the acceptable parameter types. -
doc/rob_thesis/intro.tex
re869e434 rb14dd030 16 16 Therefore, these design principles must be kept in mind throughout the design and development of new language features. 17 17 In order to appeal to existing C programmers, great care must be taken to ensure that new features naturally feel like C. 18 These goals ensure existing C code-bases can be converted to \CFA incrementally with minimal effort, and C programmers can productively generate \CFA code without training beyond the features being used.19 Unfortunately, \CC is actively diverging from C, so incremental additions require significant effort and training, coupled with multiple legacy design-choices that cannot be updated.20 21 18 The remainder of this section describes some of the important new features that currently exist in \CFA, to give the reader the necessary context in which the new features presented in this thesis must dovetail. 22 19 … … 56 53 \end{cfacode} 57 54 Compound literals create an unnamed object, and result in an lvalue, so it is legal to assign a value into a compound literal or to take its address \cite[p.~86]{C11}. 58 Syntactically, compound literals look like a cast operator followed by a brace-enclosed initializer, but semantically are different from a C cast, which only applies basic conversions and coercions andis never an lvalue.55 Syntactically, compound literals look like a cast operator followed by a brace-enclosed initializer, but semantically are different from a C cast, which only applies basic conversions and is never an lvalue. 59 56 60 57 \subsection{Overloading} … … 62 59 Overloading is the ability to specify multiple entities with the same name. 63 60 The most common form of overloading is function overloading, wherein multiple functions can be defined with the same name, but with different signatures. 64 C provides a small amount of built-in overloading, \eg + is overloaded for the basic types. 65 Like in \CC, \CFA allows user-defined overloading based both on the number of parameters and on the types of parameters. 61 Like in \CC, \CFA allows overloading based both on the number of parameters and on the types of parameters. 66 62 \begin{cfacode} 67 63 void f(void); // (1) … … 96 92 There are times when a function should logically return multiple values. 97 93 Since a function in standard C can only return a single value, a programmer must either take in additional return values by address, or the function's designer must create a wrapper structure to package multiple return-values. 98 For example, the first approach:99 94 \begin{cfacode} 100 95 int f(int * ret) { // returns a value through parameter ret … … 106 101 int res1 = g(&res2); // explicitly pass storage 107 102 \end{cfacode} 108 is awkward because it requires the caller to explicitly allocate memory for $n$ result variables, even if they are only temporary values used as a subexpression, or even not used at all.109 The secondapproach:103 The former solution is awkward because it requires the caller to explicitly allocate memory for $n$ result variables, even if they are only temporary values used as a subexpression, or even not used at all. 104 The latter approach: 110 105 \begin{cfacode} 111 106 struct A { … … 118 113 ... res3.x ... res3.y ... // use result values 119 114 \end{cfacode} 120 is awkward because the caller hasto either learn the field names of the structure or learn the names of helper routines to access the individual return values.121 Both approaches are syntactically unnatural.115 requires the caller to either learn the field names of the structure or learn the names of helper routines to access the individual return values. 116 Both solutions are syntactically unnatural. 122 117 123 118 In \CFA, it is possible to directly declare a function returning multiple values. … … 170 165 \begin{cfacode} 171 166 struct A { int i; }; 172 int ?+?(A x, A y); // '?'s represent operands167 int ?+?(A x, A y); 173 168 bool ?<?(A x, A y); 174 169 \end{cfacode} 175 170 Notably, the only difference is syntax. 176 171 Most of the operators supported by \CC for operator overloading are also supported in \CFA. 177 Of notable exception are the logical operators ( \eg @||@), the sequence operator (\ie @,@), and the member-access operators (\eg@.@ and \lstinline{->}).172 Of notable exception are the logical operators (e.g. @||@), the sequence operator (i.e. @,@), and the member-access operators (e.g. @.@ and \lstinline{->}). 178 173 179 174 Finally, \CFA also permits overloading variable identifiers. … … 248 243 template<typename T> 249 244 T sum(T *arr, int n) { 250 T t; // default construct => 0245 T t; 251 246 for (; n > 0; n--) t += arr[n-1]; 252 247 return t; … … 266 261 \end{cfacode} 267 262 The first thing to note here is that immediately following the declaration of @otype T@ is a list of \emph{type assertions} that specify restrictions on acceptable choices of @T@. 268 In particular, the assertions above specify that there must be a n assignment from \zero to @T@ and an addition assignment operator from @T@ to @T@.263 In particular, the assertions above specify that there must be a an assignment from \zero to @T@ and an addition assignment operator from @T@ to @T@. 269 264 The existence of an assignment operator from @T@ to @T@ and the ability to create an object of type @T@ are assumed implicitly by declaring @T@ with the @otype@ type-class. 270 265 In addition to @otype@, there are currently two other type-classes. … … 286 281 A major difference between the approaches of \CC and \CFA to polymorphism is that the set of assumed properties for a type is \emph{explicit} in \CFA. 287 282 One of the major limiting factors of \CC's approach is that templates cannot be separately compiled. 288 In contrast, the explicit nature of assertions allows \CFA's polymorphic functions to be separately compiled, as the function prototype states all necessary requirements separate from the implementation. 289 For example, the prototype for the previous sum function is 290 \begin{cfacode} 291 forall(otype T | **R**{ T ?=?(T *, zero_t); T ?+=?(T *, T); }**R**) 292 T sum(T *arr, int n); 293 \end{cfacode} 294 With this prototype, a caller in another translation unit knows all of the constraints on @T@, and thus knows all of the operations that need to be made available to @sum@. 283 In contrast, the explicit nature of assertions allows \CFA's polymorphic functions to be separately compiled. 295 284 296 285 In \CFA, a set of assertions can be factored into a \emph{trait}. … … 307 296 This capability allows specifying the same set of assertions in multiple locations, without the repetition and likelihood of mistakes that come with manually writing them out for each function declaration. 308 297 309 An interesting application of return-type resolution and polymorphism is a type-safe version of@malloc@.298 An interesting application of return-type resolution and polymorphism is with type-safe @malloc@. 310 299 \begin{cfacode} 311 300 forall(dtype T | sized(T)) … … 327 316 328 317 In object-oriented programming languages, type invariants are typically established in a constructor and maintained throughout the object's lifetime. 329 These assertions are typically achieved through a combination of access -control modifiers and a restricted interface.318 These assertions are typically achieved through a combination of access control modifiers and a restricted interface. 330 319 Typically, data which requires the maintenance of an invariant is hidden from external sources using the \emph{private} modifier, which restricts reads and writes to a select set of trusted routines, including member functions. 331 320 It is these trusted routines that perform all modifications to internal data in a way that is consistent with the invariant, by ensuring that the invariant holds true at the end of the routine call. … … 399 388 In other languages, a hybrid situation exists where resources escape the allocation block, but ownership is precisely controlled by the language. 400 389 This pattern requires a strict interface and protocol for a data structure, consisting of a pre-initialization and a post-termination call, and all intervening access is done via interface routines. 401 This kind of encapsulation is popular in object-oriented programming languages, and like the stack, it takes care of a significant portion of resource -management cases.390 This kind of encapsulation is popular in object-oriented programming languages, and like the stack, it takes care of a significant portion of resource management cases. 402 391 403 392 For example, \CC directly supports this pattern through class types and an idiom known as RAII \footnote{Resource Acquisition is Initialization} by means of constructors and destructors. … … 410 399 In the context of \CFA, a non-trivial constructor is either a user defined constructor or an auto-generated constructor that calls a non-trivial constructor. 411 400 412 For the remaining resource ownership cases, a programmer must follow a brittle, explicit protocol for freeing resources or an implicit protocol enforced bythe programming language.401 For the remaining resource ownership cases, programmer must follow a brittle, explicit protocol for freeing resources or an implicit protocol implemented via the programming language. 413 402 414 403 In garbage collected languages, such as Java, resources are largely managed by the garbage collector. 415 Still, garbage collectors typically focus only on memory management.404 Still, garbage collectors are typically focus only on memory management. 416 405 There are many kinds of resources that the garbage collector does not understand, such as sockets, open files, and database connections. 417 406 In particular, Java supports \emph{finalizers}, which are similar to destructors. 418 Unfortunately, finalizers are only guaranteed to be called before an object is reclaimed by the garbage collector \cite[p.~373]{Java8}, which may not happen if memory use is not contentious.407 Sadly, finalizers are only guaranteed to be called before an object is reclaimed by the garbage collector \cite[p.~373]{Java8}, which may not happen if memory use is not contentious. 419 408 Due to operating-system resource-limits, this is unacceptable for many long running programs. 420 409 Instead, the paradigm in Java requires programmers to manually keep track of all resources \emph{except} memory, leading many novices and experts alike to forget to close files, etc. … … 461 450 \end{javacode} 462 451 Variables declared as part of a try-with-resources statement must conform to the @AutoClosable@ interface, and the compiler implicitly calls @close@ on each of the variables at the end of the block. 463 Depending on when the exception is raised, both @out@ and @log@ are null, @log@ is null, or both are non-null, therefore, the cleanup for these variables at the end is a utomatically guarded and conditionally executed to prevent null-pointer exceptions.452 Depending on when the exception is raised, both @out@ and @log@ are null, @log@ is null, or both are non-null, therefore, the cleanup for these variables at the end is appropriately guarded and conditionally executed to prevent null-pointer exceptions. 464 453 465 454 While Rust \cite{Rust} does not enforce the use of a garbage collector, it does provide a manual memory management environment, with a strict ownership model that automatically frees allocated memory and prevents common memory management errors. … … 497 486 There is no runtime cost imposed on these restrictions, since they are enforced at compile-time. 498 487 499 Rust provides RAII through the @Drop@ trait, allowing arbitrary code to execute when the object goes out of scope, providing automatic clean up of auxiliary resources,much like a \CC program.488 Rust provides RAII through the @Drop@ trait, allowing arbitrary code to execute when the object goes out of scope, allowing Rust programs to automatically clean up auxiliary resources much like a \CC program. 500 489 \begin{rustcode} 501 490 struct S { … … 504 493 505 494 impl Drop for S { // RAII for S 506 fn drop(&mut self) { // destructor495 fn drop(&mut self) { 507 496 println!("dropped {}", self.name); 508 497 } … … 569 558 tuple<int, int, int> triple(10, 20, 30); 570 559 auto & [t1, t2, t3] = triple; 571 t2 = 0; // changes middle element oftriple560 t2 = 0; // changes triple 572 561 573 562 struct S { int x; double y; }; … … 575 564 auto [x, y] = s; // unpack s 576 565 \end{cppcode} 577 Structured bindings allow unpacking any struct urewith all public non-static data members into fresh local variables.566 Structured bindings allow unpacking any struct with all public non-static data members into fresh local variables. 578 567 The use of @&@ allows declaring new variables as references, which is something that cannot be done with @std::tie@, since \CC references do not support rebinding. 579 568 This extension requires the use of @auto@ to infer the types of the new variables, so complicated expressions with a non-obvious type must be documented with some other mechanism. 580 569 Furthermore, structured bindings are not a full replacement for @std::tie@, as it always declares new variables. 581 570 582 Like \CC, D provides tuples through a library variadic -template structure.571 Like \CC, D provides tuples through a library variadic template struct. 583 572 In D, it is possible to name the fields of a tuple type, which creates a distinct type. 584 573 % http://dlang.org/phobos/std_typecons.html … … 611 600 \end{smlcode} 612 601 Here, the function @binco@ appears to take 2 arguments, but it actually takes a single argument which is implicitly decomposed via pattern matching. 613 Tuples are a foundational tool in SML, allowing the creation of arbitrarily -complex structured data-types.602 Tuples are a foundational tool in SML, allowing the creation of arbitrarily complex structured data types. 614 603 615 604 Scala, like \CC, provides tuple types through the standard library \cite{Scala}. … … 664 653 Since the variadic arguments are untyped, it is up to the function to interpret any data that is passed in. 665 654 Additionally, the interface to manipulate @va_list@ objects is essentially limited to advancing to the next argument, without any built-in facility to determine when the last argument is read. 666 This limitationrequires the use of an \emph{argument descriptor} to pass information to the function about the structure of the argument list, including the number of arguments and their types.655 This requires the use of an \emph{argument descriptor} to pass information to the function about the structure of the argument list, including the number of arguments and their types. 667 656 The format string in @printf@ is one such example of an argument descriptor. 668 657 \begin{cfacode} -
doc/rob_thesis/tuples.tex
re869e434 rb14dd030 70 70 Furthermore, while many C routines that accept pointers are designed so that it is safe to pass @NULL@ as a parameter, there are many C routines that are not null-safe. 71 71 On a related note, C does not provide a standard mechanism to state that a parameter is going to be used as an additional return value, which makes the job of ensuring that a value is returned more difficult for the compiler. 72 Interestingly, there is a subtle bug in the previous example, in that @ret_ch@ is never assigned for a string that does not contain any letters, which can lead to undefined behaviour. 73 In this particular case, it turns out that the frequency return value also doubles as an error code, where a frequency of 0 means the character return value should be ignored. 74 Still, not every routine with multiple return values should be required to return an error code, and error codes are easily ignored, so this is not a satisfying solution. 72 There is a subtle bug in the previous example, in that @ret_ch@ is never assigned for a string that does not contain any letters, which can lead to undefined behaviour. 75 73 As with the previous approach, this technique can simulate multiple return values, but in practice it is verbose and error prone. 76 74 … … 86 84 char freqs [26] = { 0 }; 87 85 int ret_freq = 0; 88 char ret_ch = 'a'; // arbitrary default value for consistent results86 char ret_ch = 'a'; 89 87 for (int i = 0; str[i] != '\0'; ++i) { 90 88 if (isalpha(str[i])) { // only count letters … … 100 98 } 101 99 \end{cfacode} 102 This approach provides the benefits of compile-time checking for appropriate return statements as in aggregation, but without the required verbosity of declaring a new named type, which precludes the bug seen with out -parameters.100 This approach provides the benefits of compile-time checking for appropriate return statements as in aggregation, but without the required verbosity of declaring a new named type, which precludes the bug seen with out parameters. 103 101 104 102 The addition of multiple-return-value functions necessitates a syntax for accepting multiple values at the call-site. … … 210 208 For the call to @g@, the values @y@ and @10@ are structured into a single argument of type @[int, int]@ to match the type of the parameter of @g@. 211 209 Finally, in the call to @h@, @y@ is flattened to yield an argument list of length 3, of which the first component of @x@ is passed as the first parameter of @h@, and the second component of @x@ and @y@ are structured into the second argument of type @[int, int]@. 212 The flexible structure of tuples permits a simple and expressive function -call syntax to work seamlessly with both single- and multiple-return-value functions, and with any number of arguments of arbitrarily complex structure.210 The flexible structure of tuples permits a simple and expressive function call syntax to work seamlessly with both single- and multiple-return-value functions, and with any number of arguments of arbitrarily complex structure. 213 211 214 212 In \KWC \cite{Buhr94a,Till89}, a precursor to \CFA, there were 4 tuple coercions: opening, closing, flattening, and structuring. 215 213 Opening coerces a tuple value into a tuple of values, while closing converts a tuple of values into a single tuple value. 216 Flattening coerces a nested tuple into a flat tuple, \ieit takes a tuple with tuple components and expands it into a tuple with only non-tuple components.217 Structuring moves in the opposite direction, \ieit takes a flat tuple value and provides structure by introducing nested tuple components.214 Flattening coerces a nested tuple into a flat tuple, i.e. it takes a tuple with tuple components and expands it into a tuple with only non-tuple components. 215 Structuring moves in the opposite direction, i.e. it takes a flat tuple value and provides structure by introducing nested tuple components. 218 216 219 217 In \CFA, the design has been simplified to require only the two conversions previously described, which trigger only in function call and return situations. … … 260 258 A mass assignment assigns the value $R$ to each $L_i$. 261 259 For a mass assignment to be valid, @?=?(&$L_i$, $R$)@ must be a well-typed expression. 262 These semantics differ from C cascading assignment ( \eg@a=b=c@) in that conversions are applied to $R$ in each individual assignment, which prevents data loss from the chain of conversions that can happen during a cascading assignment.260 These semantics differ from C cascading assignment (e.g. @a=b=c@) in that conversions are applied to $R$ in each individual assignment, which prevents data loss from the chain of conversions that can happen during a cascading assignment. 263 261 For example, @[y, x] = 3.14@ performs the assignments @y = 3.14@ and @x = 3.14@, which results in the value @3.14@ in @y@ and the value @3@ in @x@. 264 262 On the other hand, the C cascading assignment @y = x = 3.14@ performs the assignments @x = 3.14@ and @y = x@, which results in the value @3@ in @x@, and as a result the value @3@ in @y@ as well. … … 276 274 These semantics allow cascading tuple assignment to work out naturally in any context where a tuple is permitted. 277 275 These semantics are a change from the original tuple design in \KWC \cite{Till89}, wherein tuple assignment was a statement that allows cascading assignments as a special case. 278 Restricting tuple assignment to statements was an attempt to to fix what was seen as a problem with side-effects, wherein assignment can be used in many different locations, such as in function-call argument position.276 Restricting tuple assignment to statements was an attempt to to fix what was seen as a problem with assignment, wherein it can be used in many different locations, such as in function-call argument position. 279 277 While permitting assignment as an expression does introduce the potential for subtle complexities, it is impossible to remove assignment expressions from \CFA without affecting backwards compatibility. 280 278 Furthermore, there are situations where permitting assignment as an expression improves readability by keeping code succinct and reducing repetition, and complicating the definition of tuple assignment puts a greater cognitive burden on the user. … … 291 289 \end{cfacode} 292 290 The tuple expression begins with a mass assignment of @1.5@ into @[b, d]@, which assigns @1.5@ into @b@, which is truncated to @1@, and @1.5@ into @d@, producing the tuple @[1, 1.5]@ as a result. 293 That tuple is used as the right side of the multiple assignment ( \ie, @[c, a] = [1, 1.5]@) that assigns @1@ into @c@ and @1.5@ into @a@, which is truncated to @1@, producing the result @[1, 1]@.291 That tuple is used as the right side of the multiple assignment (i.e., @[c, a] = [1, 1.5]@) that assigns @1@ into @c@ and @1.5@ into @a@, which is truncated to @1@, producing the result @[1, 1]@. 294 292 Finally, the tuple @[1, 1]@ is used as an expression in the call to @f@. 295 293 … … 309 307 In this example, @x@ is initialized by the multiple constructor calls @?{}(&x.0, 3)@ and @?{}(&x.1, 6.28)@, while @y@ is initialized by two default constructor calls @?{}(&y.0)@ and @?{}(&y.1)@. 310 308 @z@ is initialized by mass copy constructor calls @?{}(&z.0, x.0)@ and @?{}(&z.1, x.0)@. 311 Finally, @x@, @y@, and @z@ are destructed, \iethe calls @^?{}(&x.0)@, @^?{}(&x.1)@, @^?{}(&y.0)@, @^?{}(&y.1)@, @^?{}(&z.0)@, and @^?{}(&z.1)@.309 Finally, @x@, @y@, and @z@ are destructed, i.e. the calls @^?{}(&x.0)@, @^?{}(&x.1)@, @^?{}(&y.0)@, @^?{}(&y.1)@, @^?{}(&z.0)@, and @^?{}(&z.1)@. 312 310 313 311 It is possible to define constructors and assignment functions for tuple types that provide new semantics, if the existing semantics do not fit the needs of an application. … … 342 340 Then the type of @a.[x, y, z]@ is @[T_x, T_y, T_z]@. 343 341 344 Since tuple index expressions are a form of member-access expression, it is possible to use tuple-index expressions in conjunction with member tuple expressions to manually restructure a tuple ( \eg, rearrange components, drop components, duplicate components, etc.).342 Since tuple index expressions are a form of member-access expression, it is possible to use tuple-index expressions in conjunction with member tuple expressions to manually restructure a tuple (e.g., rearrange components, drop components, duplicate components, etc.). 345 343 \begin{cfacode} 346 344 [int, int, long, double] x; … … 394 392 395 393 As for @z.y@, one interpretation is to extend the meaning of member tuple expressions. 396 That is, currently the tuple must occur as the member, \ieto the right of the dot.394 That is, currently the tuple must occur as the member, i.e. to the right of the dot. 397 395 Allowing tuples to the left of the dot could distribute the member across the elements of the tuple, in much the same way that member tuple expressions distribute the aggregate across the member tuple. 398 396 In this example, @z.y@ expands to @[z.0.y, z.1.y]@, allowing what is effectively a very limited compile-time field-sections map operation, where the argument must be a tuple containing only aggregates having a member named @y@. … … 452 450 453 451 struct A { int x; }; 454 (struct A)f(); // invalid , int cannot be converted to A452 (struct A)f(); // invalid 455 453 \end{cfacode} 456 454 In C, line 4 is a valid cast, which calls @f@ and discards its result. … … 468 466 [int, [int, int], int] g(); 469 467 470 ([int, double])f(); // (1) valid471 ([int, int, int])g(); // (2) valid472 ([void, [int, int]])g(); // (3) valid473 ([int, int, int, int])g(); // (4) invalid474 ([int, [int, int, int]])g(); // (5) invalid468 ([int, double])f(); // (1) 469 ([int, int, int])g(); // (2) 470 ([void, [int, int]])g(); // (3) 471 ([int, int, int, int])g(); // (4) 472 ([int, [int, int, int]])g(); // (5) 475 473 \end{cfacode} 476 474 … … 479 477 If @g@ is free of side effects, this is equivalent to @[(int)(g().0), (int)(g().1.0), (int)(g().2)]@. 480 478 Since @void@ is effectively a 0-element tuple, (3) discards the first and third return values, which is effectively equivalent to @[(int)(g().1.0), (int)(g().1.1)]@). 479 481 480 % will this always hold true? probably, as constructors should give all of the conversion power we need. if casts become function calls, what would they look like? would need a way to specify the target type, which seems awkward. Also, C++ basically only has this because classes are closed to extension, while we don't have that problem (can have floating constructors for any type). 482 481 Note that a cast is not a function call in \CFA, so flattening and structuring conversions do not occur for cast expressions. … … 535 534 \end{cfacode} 536 535 537 Until this point, it has been assumed that assertion arguments must match the parameter type exactly, modulo polymorphic specialization ( \ie, no implicit conversions are applied to assertion arguments).536 Until this point, it has been assumed that assertion arguments must match the parameter type exactly, modulo polymorphic specialization (i.e., no implicit conversions are applied to assertion arguments). 538 537 This decision presents a conflict with the flexibility of tuples. 539 538 \subsection{Assertion Inference} … … 567 566 } 568 567 \end{cfacode} 569 is transformed into568 Is transformed into 570 569 \begin{cfacode} 571 570 forall(dtype T0, dtype T1 | sized(T0) | sized(T1)) 572 struct _tuple2 _{ // generated before the first 2-tuple571 struct _tuple2 { // generated before the first 2-tuple 573 572 T0 field_0; 574 573 T1 field_1; … … 577 576 _tuple2_(double, double) x; 578 577 forall(dtype T0, dtype T1, dtype T2 | sized(T0) | sized(T1) | sized(T2)) 579 struct _tuple3 _{ // generated before the first 3-tuple578 struct _tuple3 { // generated before the first 3-tuple 580 579 T0 field_0; 581 580 T1 field_1; … … 590 589 [5, 'x', 1.24]; 591 590 \end{cfacode} 592 becomes591 Becomes 593 592 \begin{cfacode} 594 593 (_tuple3_(int, char, double)){ 5, 'x', 1.24 }; … … 604 603 f(x, 'z'); 605 604 \end{cfacode} 606 is transformed into605 Is transformed into 607 606 \begin{cfacode} 608 607 void f(int, _tuple2_(double, char)); … … 651 650 It is possible that lazy evaluation could be exposed to the user through a lazy keyword with little additional effort. 652 651 653 Tuple -member expressions are recursively expanded into a list of member-access expressions.652 Tuple member expressions are recursively expanded into a list of member access expressions. 654 653 \begin{cfacode} 655 654 [int, [double, int, double], int]] x; 656 655 x.[0, 1.[0, 2]]; 657 656 \end{cfacode} 658 becomes657 which becomes 659 658 \begin{cfacode} 660 659 [x.0, [x.1.0, x.1.2]]; … … 671 670 [x, y, z] = 1.5; // mass assignment 672 671 \end{cfacode} 673 generates the following672 Generates the following 674 673 \begin{cfacode} 675 674 // [x, y, z] = 1.5; … … 712 711 }); 713 712 \end{cfacode} 714 A variable is generated to store the value produced by a statement expression, since its fields may need to be constructed with a non-trivial constructor and it may need to be referred to multiple time, \eg, in a unique expression.713 A variable is generated to store the value produced by a statement expression, since its fields may need to be constructed with a non-trivial constructor and it may need to be referred to multiple time, e.g., in a unique expression. 715 714 $N$ LHS variables are generated and constructed using the address of the tuple components, and a single RHS variable is generated to store the value of the RHS without any loss of precision. 716 715 A nested statement expression is generated that performs the individual assignments and constructs the return value using the results of the individual assignments. … … 721 720 [x, y, z] = [f(), 3]; // multiple assignment 722 721 \end{cfacode} 723 generates the following 722 Generates 724 723 \begin{cfacode} 725 724 // [x, y, z] = [f(), 3]; -
doc/rob_thesis/variadic.tex
re869e434 rb14dd030 12 12 In addition, C requires the programmer to hard code all of the possible expected types. 13 13 As a result, it is cumbersome to write a function that is open to extension. 14 For example, a simple function to sum$N$ @int@s,14 For example, a simple function which sums $N$ @int@s, 15 15 \begin{cfacode} 16 16 int sum(int N, ...) { … … 27 27 sum(3, 10, 20, 30); // need to keep counter in sync 28 28 \end{cfacode} 29 The @va_list@ type is a special C data type that abstracts variadic -argument manipulation.29 The @va_list@ type is a special C data type that abstracts variadic argument manipulation. 30 30 The @va_start@ macro initializes a @va_list@, given the last named parameter. 31 31 Each use of the @va_arg@ macro allows access to the next variadic argument, given a type. … … 34 34 In the case where the provided type is not compatible with the argument's actual type after default argument promotions, or if too many arguments are accessed, the behaviour is undefined \cite[p.~81]{C11}. 35 35 Furthermore, there is no way to perform the necessary error checks in the @sum@ function at run-time, since type information is not carried into the function body. 36 Since they rely on programmer convention rather than compile-time checks, variadic functions are unsafe.36 Since they rely on programmer convention rather than compile-time checks, variadic functions are generally unsafe. 37 37 38 38 In practice, compilers can provide warnings to help mitigate some of the problems. 39 For example, GCC provides the @format@ attribute to specify that a function uses a format string, which allows the compiler to perform some checks related to the standard format -specifiers.40 Unfortunately, this approach does not permit extensions to the format -string syntax, so a programmer cannot extend the attribute to warn for mismatches with custom types.39 For example, GCC provides the @format@ attribute to specify that a function uses a format string, which allows the compiler to perform some checks related to the standard format specifiers. 40 Unfortunately, this approach does not permit extensions to the format string syntax, so a programmer cannot extend the attribute to warn for mismatches with custom types. 41 41 42 42 As a result, C's variadic functions are a deficient language feature. … … 79 79 Similarly, in order to pass 0 variadic arguments, an explicit empty tuple must be passed into the argument list, otherwise the exact matching rule would not have an argument to bind against. 80 80 81 It should be otherwise noted that the addition of an exact matching rule only affects the outcome for polymorphic type -binding when tuples are involved.81 It should be otherwise noted that the addition of an exact matching rule only affects the outcome for polymorphic type binding when tuples are involved. 82 82 For non-tuple arguments, exact matching and flattening and structuring are equivalent. 83 For tuple arguments to a function without polymorphic formal -parameters, flattening and structuring work whenever an exact match would have worked, since the tuple is flattened and implicitly restructured to its original structure.83 For tuple arguments to a function without polymorphic formal parameters, flattening and structuring work whenever an exact match would have worked, since the tuple is flattened and implicitly restructured to its original structure. 84 84 Thus there is nothing to be gained from permitting the exact matching rule to take effect when a function does not contain polymorphism and none of the arguments are tuples. 85 85 … … 161 161 return x+y; 162 162 } 163 forall(otype T1, otype T2, otype T3, otype R, ttype Params163 forall(otype T1, otype T2, otype T3, ttype Params, otype R 164 164 | summable(T1, T2, T3) 165 165 | { R sum(T3, Params); }) … … 184 184 \CFA does not need an ellipsis in either case, since the type class @ttype@ is only used for variadics. 185 185 An alternative design is to use an ellipsis combined with an existing type class. 186 This approach was not taken because the largest benefit of the ellipsis token in \CC is the ability to expand a parameter pack within an expression, \eg, in fold expressions, which requires compile-time knowledge of the structure of the parameter pack, which is not available in \CFA.186 This approach was not taken because the largest benefit of the ellipsis token in \CC is the ability to expand a parameter pack within an expression, e.g., in fold expressions, which requires compile-time knowledge of the structure of the parameter pack, which is not available in \CFA. 187 187 \begin{cppcode} 188 188 template<typename... Args> … … 224 224 Array * x = new(1, 2, 3); 225 225 \end{cfacode} 226 The @new@ function provides the combination of type-safe @malloc@ with a constructor call, so that it becomes impossible to forget to construct dynamically allocated objects. 227 This approach provides the type-safety of @new@ in \CC, without the need to specify the allocated type, thanks to return-type inference. 228 226 229 In the call to @new@, @Array@ is selected to match @T@, and @Params@ is expanded to match @[int, int, int, int]@. To satisfy the assertions, a constructor with an interface compatible with @void ?{}(Array *, int, int, int)@ must exist in the current scope. 227 228 The @new@ function provides the combination of type-safe @malloc@ with a constructor call, so that it becomes impossible to forget to construct dynamically-allocated objects.229 This approach provides the type-safety of @new@ in \CC, without the need to specify the allocated type, thanks to return-type inference.230 230 231 231 \section{Implementation} … … 240 240 } 241 241 \end{cfacode} 242 generates the following242 Generates the following 243 243 \begin{cfacode} 244 244 void *malloc(long unsigned int _sizeof_T, long unsigned int _alignof_T); … … 267 267 \end{cfacode} 268 268 The constructor for @T@ is called indirectly through the adapter function on the result of @malloc@ and the parameter pack. 269 The variable that is allocated and constructed is then returned from @new@.269 The variable that was allocated and constructed is then returned from @new@. 270 270 271 271 A call to @new@ … … 337 337 } 338 338 \end{cfacode} 339 generates the following 339 Generates 340 340 \begin{cfacode} 341 341 void print_variadic( … … 382 382 print("x = ", 123, ".\n"); 383 383 \end{cfacode} 384 generates the following384 Generates the following 385 385 \begin{cfacode} 386 386 void print_string(const char *x){ -
doc/user/user.tex
re869e434 rb14dd030 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Wed Apr 12 12:18:58201714 %% Update Count : 141 513 %% Last Modified On : Wed Apr 5 23:19:40 2017 14 %% Update Count : 1412 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 64 64 % Names used in the document. 65 65 \newcommand{\Version}{\input{../../version}} 66 \newcommand{\CS}{C\raisebox{-0.9ex}{\large$^\sharp$}\xspace} 67 66 68 \newcommand{\Textbf}[2][red]{{\color{#1}{\textbf{#2}}}} 67 69 \newcommand{\Emph}[2][red]{{\color{#1}\textbf{\emph{#2}}}} … … 193 195 For system programming, where direct access to hardware and dealing with real-time issues is a requirement, C is usually the language of choice. 194 196 As well, there are millions of lines of C legacy code, forming the base for many software development projects (especially on UNIX systems). 195 The TIOBE index (\url{http://www.tiobe.com/tiobe_index}) for March 2016 shows programming-language popularity, with \Index*{Java} 20.5\%, C 14.5\%, \Index*[C++]{\CC} 6.7\%, \C sharp4.3\%, \Index*{Python} 4.3\%, and all other programming languages below 3\%.197 The TIOBE index (\url{http://www.tiobe.com/tiobe_index}) for March 2016 shows programming-language popularity, with \Index*{Java} 20.5\%, C 14.5\%, \Index*[C++]{\CC} 6.7\%, \CS 4.3\%, \Index*{Python} 4.3\%, and all other programming languages below 3\%. 196 198 As well, for 30 years, C has been the number 1 and 2 most popular programming language: 197 199 \begin{center}
Note:
See TracChangeset
for help on using the changeset viewer.