source: doc/theses/aaron_moss_PhD/phd/background.tex @ 44eeddd

ADTaaron-thesisarm-ehast-experimentalcleanup-dtorsenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since 44eeddd was 0e6a0beb, checked in by Aaron Moss <a3moss@…>, 6 years ago

thesis: Started Experiments chapter

  • Property mode set to 100644
File size: 30.3 KB
Line 
1\chapter{\CFA{}}
2\label{cfa-chap}
3
4\CFA{} adds a number of features to C, some of them providing significant increases to the expressive power of the language, but all designed to maintain the existing procedural programming paradigm of C and to be as orthogonal as possible to each other.
5To provide background for the contributions in subsequent chapters, this chapter provides a summary of the features of \CFA{} at the time this work was conducted.
6
7The core design of \CFA{} is laid out in Glen Ditchfield's 1992 PhD thesis, \emph{Contextual Polymorphism}\cite{Ditchfield92}; in that thesis, Ditchfield presents the theoretical underpinnings of the \CFA{} polymorphism model.
8Building on Ditchfield's design for contextual polymorphism as well as KW-C\cite{Buhr94a}, an earlier set of (largely syntactic) extensions to C, Richard Bilson\cite{Bilson03} built the first version of the \CFA{} compiler, \CFACC{}, in the early 2000's.
9This early \CFACC{} provided basic functionality, but incorporated a number of poor algorithmic choices due to a rushed implementation time frame, and as such lacked the runtime performance required for practical use; this thesis is substantially concerned with rectifying those deficits.
10
11The \CFA{} project was revived in 2015 with the intention of building a production-ready language and compiler; at the time of this writing, both \CFA{} and \CFACC{} have been under active development continuously since.
12As this development has been proceeding concurrently with the work described in this thesis, the state of \CFA{} has been somewhat of a moving target; however, Moss~\etal\cite{Moss18} provides a reasonable summary of the current design.
13Notable features added during this period include generic types (Chapter~\ref{generic-chap}), constructors and destructors\cite{Schluntz17}, improved support for tuples\cite{Schluntz17}, reference types\cite{Moss18}, first-class concurrent and parallel programming support\cite{Delisle18}, as well as numerous pieces of syntactic sugar and the start of an idiomatic standard library\cite{Moss18}.
14
15\section{\CFA{} Features}
16
17The selection of features presented in this chapter are chosen to elucidate the design constraints of the work presented in this thesis.
18In some cases the interactions of multiple features make this design a significantly more complex problem than any individual feature would; in other cases a feature that does not by itself add any complexity to expression resolution triggers previously rare edge cases more frequently.
19
20\subsection{Procedural Paradigm}
21
22It is important to note that \CFA{} is not an object-oriented language.
23This is a deliberate choice intended to maintain the applicability of the mental model and language idioms already possessed by C programmers.
24This choice is in marked contrast to \CC{}, which, though it has backward-compatibility with C on the source code level, is a much larger and more complex language, and requires extensive developer re-training to write idiomatic, efficient code in \CC{}'s object-oriented paradigm.
25
26\CFA{} does have a system of implicit type conversions derived from C's ``usual arithmetic conversions''; while these conversions may be thought of as something like an inheritance hierarchy, the underlying semantics are significantly different and such an analogy is loose at best.
27Particularly, \CFA{} has no concept of \emph{subclass}, and thus no need to integrate an inheritance-based form of polymorphism with its parametric and overloading-based polymorphism.
28The graph structure of the \CFA{} type conversions is also markedly different than an inheritance hierarchy; it has neither a top nor a bottom type, and does not satisfy the lattice properties typical of inheritance hierarchies.
29
30Another aspect of \CFA{}'s procedural paradigm is that it retains C's translation-unit-based encapsulation model, rather than class-based encapsulation such as in \CC{}.
31This choice implies that that separate compilation must be maintained to allow headers to act as an encapsulation boundary, rather than the header-only libraries used by \CC{} templates.
32
33\subsection{Name Overloading} \label{overloading-sec}
34
35In C, no more than one variable or function in the same scope may share the same name\footnote{Technically, C has multiple separated namespaces, one holding \lstinline{struct}, \lstinline{union}, and \lstinline{enum} tags, one holding labels, one holding \lstinline{typedef} names, variable, function, and enumerator identifiers, and one for each \lstinline{struct} and \lstinline{union} type holding the field names\cit{}.}, and variable or function declarations in inner scopes with the same name as a declaration in an outer scope hide the outer declaration.
36This restriction makes finding the proper declaration to match to a variable expression or function application a simple matter of symbol-table lookup, which can be easily and efficiently implemented.
37\CFA{}, on the other hand, allows overloading of variable and function names so long as the overloaded declarations do not have the same type, avoiding the multiplication of variable and function names for different types common in the C standard library, as in the following example:
38
39\begin{cfa}
40#include <limits.h>
41
42const int max = INT_MAX;  $\C[1.75in]{// (1)}$
43const double max = DBL_MAX;  $\C[1.75in]{// (2)}$
44int max(int a, int b) { return a < b ? b : a; }  $\C[1.75in]{// (3)}$
45double max(double a, double b) { return a < b ? b : a; }  $\C[1.75in]{// (4)}$
46
47max( 7, -max );  $\C[3.75in]{// uses (3) and (1), by matching int from 7}$
48max( max, 3.14 );  $\C[3.75in]{// uses (4) and (2), by matching double from 3.14}$
49max( max, -max );  $\C[3.75in]{// ERROR, ambiguous}$
50int m = max( max, -max );  $\C[3.75in]{// uses (3) and (1) twice, by matching return type}$
51\end{cfa}
52
53While the wisdom of giving both the maximum value of a type and the function to take the maximum of two values the same name is debatable, \eg{} some developers may prefer !MAX! for the former, the guiding philosophy of \CFA{} is ``describe, don't prescribe'' --- we prefer to trust programmers with powerful tools, and there is no technical reason to restrict overloading between variables and functions.
54However, the expressivity of \CFA{}'s name overloading has the consequence that simple table lookup is insufficient to match identifiers to declarations, and a type-matching algorithm must be part of expression resolution.
55
56\subsubsection{Operator Overloading}
57
58C does allow name overloading in one context: operator overloading.
59From the perspective of the type system, there is nothing special about operators as opposed to other functions, nor is it desirable to restrict the clear and readable syntax of operators to only the built-in types.
60For these reasons, \CFA{} also allows overloading of operators by writing specially-named functions where !?! stands in for the operands\footnote{This example uses \CFA{}'s reference types, described in Section~\ref{type-features-sec}}:
61
62\begin{cfa}
63struct counter { int x; };
64
65counter& `++?`(counter& c) { ++c.x; return c; }  $\C[2in]{// pre-increment}$
66counter `?++`(counter& c) {  $\C[2in]{// post-increment}$
67        counter tmp = c; ++c; return tmp;
68}
69bool `?<?`(const counter& a, const counter& b) {  $\C[2in]{// comparison}$
70        return a.x < b.x;
71}
72\end{cfa}
73
74Together, \CFA{}'s backward-compatibility with C and the inclusion of this operator overloading feature imply that \CFA{} must select among function overloads using a method compatible with C's ``usual arithmetic conversions''\cit{}, so as to present user programmers with only a single set of overloading rules.
75
76\subsubsection{Special Literal Types}
77
78Literal !0! is also used polymorphically in C; it may be either integer zero or the null value of any pointer type.
79\CFA{} provides a special type for the !0! literal, !zero_t!, so that users can define a zero value for their own types without being forced to create a conversion from an integer or pointer type (though \CFA{} also includes implicit conversions from !zero_t! to the integer and pointer types for backward compatibility).
80
81According to the C standard\cit{}, !0! is the only false value; any value that compares equal to zero is false, while any value that does not is true.
82By this rule, boolean contexts such as !if ( x )! can always be equivalently rewritten as \lstinline{if ( (x) != 0 )}.
83\CFACC{} applies this rewriting in all boolean contexts, so any type !T! can be made ``truthy'' (that is, given a boolean interpretation) in \CFA{} by defining an operator overload \lstinline{int ?!=?(T, zero_t)}; unlike \CC{} prior to the addition of explicit casts in \CCeleven{}, this design does not add comparability or convertablity to arbitrary integer types.
84
85\CFA{} also includes a special type for !1!, !one_t!; like !zero_t!, !one_t! has built-in implicit conversions to the various integral types so that !1! maintains its expected semantics in legacy code.
86The addition of !one_t! allows generic algorithms to handle the unit value uniformly for types where it is meaningful; a simple example of this is that polymorphic functions\footnote{discussed in Section~\ref{poly-func-sec}} in the \CFA{} prelude define !++x! and !x++! in terms of !x += 1!, allowing users to idiomatically define all forms of increment for a type !T! by defining the single function !T& ?+=?(T&, one_t)!; analogous overloads for the decrement operators are also present, and programmers can override any of these functions for a particular type if desired.
87
88\CFA{} previously allowed !0! and !1! to be the names of polymorphic variables, with separate overloads for !int 0!, !int 1!, and !forall(dtype T) T* 0!.
89As revealed in my own work on generic types (Chapter~\ref{generic-chap}), the parameteric polymorphic zero variable was not generalizable to other types; though all null pointers have the same in-memory representation, the same cannot be said of the zero values of arbitrary types.
90As such, variables that could represent !0! and !1! were phased out in favour of functions that could generate those values for a given type as appropriate.
91
92\subsection{Polymorphic Functions} \label{poly-func-sec}
93
94The most significant feature \CFA{} adds is parametric-polymorphic functions.
95Such functions are written using a !forall! clause (which gives the language its name):
96
97\begin{cfa}
98`forall(otype T)`
99T identity(T x) { return x; }
100\end{cfa}
101
102The !identity! function above can be applied to any complete object type (or ``!otype!'').
103The type variable !T! is transformed into a set of additional implicit parameters to !identity!, which encode sufficient information about !T! to create and return a variable of that type.
104\CFA{} passes the size and alignment of the type represented by an !otype! parameter, as well as a default constructor, copy constructor, assignment operator, and destructor.
105Types which do not have one or more of these operators visible cannot be bound to !otype! parameters.
106In this design, the runtime cost of polymorphism is spread over each polymorphic call, due to passing more arguments to polymorphic functions; experiments have shown this overhead to be similar to \CC{} virtual function calls. \TODO{rerun experiments, possibly look at vtable variant}
107
108One benefit of this design is that it allows polymorphic functions to be separately compiled.
109The forward declaration !forall(otype T) T identity(T);! uniquely defines a single callable function, which may be implemented in a different file.
110The fact that there is only one implementation of each polymorphic function also reduces compile times relative to the template-expansion approach taken by \CC{}, as well as reducing binary sizes and runtime pressure on instruction cache by re-using a single version of each function.
111
112\subsubsection{Type Assertions}
113
114Since bare polymorphic types do not provide a great range of available operations, \CFA{} provides a \emph{type assertion} mechanism to provide further information about a type:
115
116\begin{cfa}
117forall(otype T `| { T twice(T); }`)
118T four_times(T x) { return twice( twice(x) ); }
119double twice(double d) { return d * 2.0; }  $\C[2.75in]{// (1)}$
120
121double ans = four_times( 10.5 );  $\C[2.75in]{// T bound to double, ans == 42.0}$
122\end{cfa}
123
124These type assertions may be either variable or function declarations that depend on a polymorphic type variable.
125!four_times! may only be called with an argument for which there exists a function named !twice! that can take that argument and return another value of the same type; a pointer to the appropriate function is passed as an additional implicit parameter of the call to !four_times!.
126
127Monomorphic specializations of polymorphic functions can themselves be used to satisfy type assertions.
128For instance, !twice! could have been defined like this:
129
130\begin{cfa}
131forall(otype S | { S ?+?(S, S); })
132S twice(S x) { return x + x; }  $\C[2.75in]{// (2)}
133\end{cfa}
134
135This version of !twice! works for any type !S! that has an addition operator defined for it, and it could be used to satisfy the type assertion on !four_times!.
136\CFACC{} accomplishes this by creating a wrapper function calling !twice//(2)! with !S! bound to !double!, then providing this wrapper function to !four_times!\footnote{\lstinline{twice // (2)} could also have had a type parameter named \lstinline{T}; \CFA{} specifies renaming of the type parameters, which would avoid the name conflict with the type variable \lstinline{T} of \lstinline{four_times}}.
137
138Finding appropriate functions to satisfy type assertions is essentially a recursive case of expression resolution, as it takes a name (that of the type assertion) and attempts to match it to a suitable declaration in the current scope.
139If a polymorphic function can be used to satisfy one of its own type assertions, this recursion may not terminate, as it is possible that that function is examined as a candidate for its own assertion unboundedly repeatedly.
140To avoid such infinite loops, \CFACC{} imposes a fixed limit on the possible depth of recursion, similar to that employed by most \CC{} compilers for template expansion; this restriction means that there are some semantically well-typed expressions that cannot be resolved by \CFACC{}.
141\TODO{Update this with final state} One contribution made in the course of this thesis was modifying \CFACC{} to use the more flexible expression resolution algorithm for assertion matching, rather than the simpler but limited previous approach of unification on the types of the functions.
142
143\subsubsection{Deleted Declarations}
144
145Particular type combinations can be exempted from matching a given polymorphic function through use of a \emph{deleted function declaration}:
146
147\begin{cfa}
148int somefn(char) = void;
149\end{cfa}
150
151This feature is based on a \CCeleven{} feature typically used to make a type non-copyable by deleting its copy constructor and assignment operator\footnote{In previous versions of \CC{}, a type could be made non-copyable by declaring a private copy constructor and assignment operator, but not defining either. This idiom is well-known, but depends on some rather subtle and \CC{}-specific rules about private members and implicitly-generated functions; the deleted function form is both clearer and less verbose.} or forbidding some interpretations of a polymorphic function by specifically deleting the forbidden overloads\footnote{Specific polymorphic function overloads can also be forbidden in previous \CC{} versions through use of template metaprogramming techniques, though this advanced usage is beyond the skills of many programmers. A similar effect can be produced on an ad-hoc basis at the appropriate call sites through use of casts to determine the function type. In both cases, the deleted-function form is clearer and more concise.}.
152Deleted function declarations are implemented in \CFACC{} by adding them to the symbol table as usual, but with a flag set that indicates that the function is deleted.
153If this deleted declaration is selected as the unique minimal-cost interpretation of an expression than an error is produced. \TODO{Check this is implemented at print.}
154
155\subsubsection{Traits}
156
157\CFA{} provides \emph{traits} as a means to name a group of type assertions, as in the example below\footnote{This example uses \CFA{}'s reference types, constructors, and zero type, described in Section~\ref{type-features-sec}.}:
158
159\begin{cfa}
160`trait has_magnitude(otype T)` {
161        bool ?<?(T, T)$\C[4in]{// comparison operator}$
162        T -?(T)$\C[4in]{// negation operator}$
163        void ?{}(T&, zero_t)$\C[4in]{// constructor from 0}$
164};
165
166forall(otype M | `has_magnitude(M)`)
167M abs(M m) { return m < (M){0} ? -m : m; }
168
169forall(otype M | `has_magnitude(M)`)
170M max_magnitude(M a, M b) { return abs(a) < abs(b) ? b : a; }
171\end{cfa}
172
173Semantically, traits are simply a named list of type assertions, but they may be used for many of the same purposes that interfaces in Java or abstract base classes in \CC{} are used for.
174Unlike Java interfaces or \CC{} base classes, \CFA{} types do not explicitly state any inheritance relationship to traits they satisfy; this can be considered a form a structural inheritance, similar to interface implementation in Go, as opposed to the nominal inheritance model of Java and \CC{}.
175Nominal inheritance can be simulated in \CFA{} using marker variables in traits:
176
177\begin{cfa}
178trait nominal(otype T) {
179        `T is_nominal;`
180};
181
182int is_nominal;  $\C{// int now satisfies nominal}$
183{
184        char is_nominal;  $\C{// char only satisfies nominal in this scope}$
185}
186\end{cfa}
187
188Traits, however, are significantly more powerful than nominal-inheritance interfaces; firstly, due to the scoping rules of the declarations that satisfy a trait's type assertions, a type may not satisfy a trait everywhere that that type is declared, as with !char! and the !nominal! trait above.
189Secondly, because \CFA{} is not object-oriented and types do not have a closed set of methods, existing C library types can be extended to implement a trait simply by writing the requisite functions.
190Finally, traits may be used to declare a relationship among multiple types, a property that may be difficult or impossible to represent in nominal-inheritance type systems\footnote{This example uses \CFA{}'s reference types, described in Section~\ref{type-features-sec}.}:
191
192\begin{cfa}
193trait pointer_like(`otype Ptr, otype El`) {
194        El& *?(Ptr)$\C{// Ptr can be dereferenced to El}$
195};
196
197struct list {
198        int value;
199        list* next; $\C{// may omit struct on type names}$
200};
201
202typedef list* list_iterator;
203
204int& *?(list_iterator it) {
205        return it->value;
206}
207\end{cfa}
208
209In 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.
210Given 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 !int!, while !(*it).value = 42;! interprets !*it! as !list!).
211While 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.
212
213The flexibility of \CFA{}'s implicit trait-satisfaction mechanism provides programmers with a great deal of power, but also blocks some optimization approaches for expression resolution.
214The ability of types to begin or cease to satisfy traits when declarations go into or out of scope makes caching of trait satisfaction judgements difficult, and the ability of traits to take multiple type parameters can lead to a combinatorial explosion of work in any attempt to pre-compute trait satisfaction relationships.
215
216\subsection{Implicit Conversions} \label{implicit-conv-sec}
217
218In addition to the multiple interpretations of an expression produced by name overloading and polymorphic functions, for backward compatibility \CFA{} must support all of the implicit conversions present in C, producing further candidate interpretations for expressions.
219As mentioned above, C does not have an inheritance hierarchy of types, but the C standard's rules for the ``usual arithmetic conversions'\cit{} define which of the built-in types are implicitly convertible to which other types, and the relative cost of any pair of such conversions from a single source type.
220\CFA{} adds rules to the usual arithmetic conversions defining the cost of binding a polymorphic type variable in a function call; such bindings are cheaper than any \emph{unsafe} (narrowing) conversion, \eg{} !int! to !char!, but more expensive than any \emph{safe} (widening) conversion, \eg{} !int! to !double!.
221One contribution of this thesis, discussed in Section \TODO{add to resolution chapter}, is a number of refinements to this cost model to more efficiently resolve polymorphic function calls.
222
223The expression resolution problem which is the focus of Chapter~\ref{resolution-chap} is to find the unique minimal-cost interpretation of each expression in the program, where all identifiers must be matched to a declaration, and implicit conversions or polymorphic bindings of the result of an expression may increase the cost of the expression.
224While semantically valid \CFA{} code always has such a unique minimal-cost interpretation, \CFACC{} must also be able to detect and report as errors expressions which have either no interpretation or multiple ambiguous minimal-cost interpretations.
225Note that which subexpression interpretation is minimal-cost may require contextual information to disambiguate.
226For instance, in the example in Section~\ref{overloading-sec}, !max(max, -max)! cannot be unambiguously resolved, but !int m = max(max, -max)! has a single minimal-cost resolution.
227While the interpretation !int m = (int)max((double)max, -(double)max)! is also a valid interpretation, it is not minimal-cost due to the unsafe cast from the !double! result of !max! to !int!\footnote{The two \lstinline{double} casts function as type ascriptions selecting \lstinline{double max} rather than casts from \lstinline{int max} to \lstinline{double}, and as such are zero-cost.}.
228These contextual effects make the expression resolution problem for \CFA{} both theoretically and practically difficult, but the observation driving the work in Chapter~\ref{resolution-chap} is that of the many top-level expressions in a given program, most are straightforward and idiomatic so that programmers writing and maintaining the code can easily understand them; it follows that effective heuristics for common cases can bring down compiler runtime enough that a small proportion of harder-to-resolve expressions does not inordinately increase overall compiler runtime or memory usage.
229
230\subsection{Type Features} \label{type-features-sec}
231
232The name overloading and polymorphism features of \CFA{} have the greatest effect on language design and compiler runtime, but there are a number of other features in the type system which have a smaller effect but are useful for code examples.
233These features are described here.
234
235\subsubsection{Reference Types}
236
237One of the key ergonomic improvements in \CFA{} is reference types, designed and implemented by Robert Schluntz\cite{Schluntz17}.
238Given some type !T!, a !T&! (``reference to !T!'') is essentially an automatically dereferenced pointer.
239These types allow seamless pass-by-reference for function parameters, without the extraneous dereferencing syntax present in C; they also allow easy easy aliasing of nested values with a similarly convenient syntax.
240A particular improvement is removing syntactic special cases for operators which take or return mutable values; for example, the use !a += b! of a compound assignment operator now matches its signature, !int& ?+=?(int&, int)!, as opposed to the previous syntactic special cases to automatically take the address of the first argument to !+=! and to mark its return value as mutable.
241
242The C standard makes heavy use of the concept of \emph{lvalue}, an expression with a memory address; its complement, \emph{rvalue} (a non-addressable expression) is not explicitly named.
243In \CFA{}, the distinction between lvalue and rvalue can be reframed in terms of reference and non-reference types, with the benefit of being able to express the difference in user code.
244\CFA{} references preserve the existing qualifier-dropping implicit lvalue-to-rvalue conversion from C (\eg{} a !const volatile int&! can be implicitly copied to a bare !int!)
245To make reference types more easily usable in legacy pass-by-value code, \CFA{} also adds an implicit rvalue-to-lvalue conversion, implemented by storing the value in a fresh compiler-generated temporary variable and passing a reference to that temporary.
246To mitigate the ``!const! hell'' problem present in \CC{}, there is also a qualifier-dropping lvalue-to-lvalue conversion, also implemented by copying into a temporary:
247
248\begin{cfa}
249const int magic = 42;
250
251void inc_print( int& x ) { printf("%d\n", ++x); }
252
253print_inc( magic ); $\C{// legal; implicitly generated code in red below:}$
254
255`int tmp = magic;` $\C{// to safely strip const-qualifier}$
256`print_inc( tmp );` $\C{// tmp is incremented, magic is unchanged}$
257\end{cfa}
258
259Despite the similar syntax, \CFA{} references are significantly more flexible than \CC{} references.
260The primary issue with \CC{} references is that it is impossible to extract the address of the reference variable rather than the address of the referred-to variable.
261This breaks a number of the usual compositional properties of the \CC{} type system, \eg{} a reference cannot be re-bound to another variable, nor is it possible to take a pointer to, array of, or reference to a reference.
262\CFA{} supports all of these use cases \TODO{test array} without further added syntax.
263The key to this syntax-free feature support is an observation made by the author that the address of a reference is a lvalue.
264In C, the address-of operator !&x! can only be applied to lvalue expressions, and always produces an immutable rvalue; \CFA{} supports reference re-binding by assignment to the address of a reference, and pointers to references by repeating the address-of operator:
265
266\begin{cfa}
267int x = 2, y = 3;
268int& r = x;  $\C{// r aliases x}$
269&r = &y; $\C{// r now aliases y}$
270int** p = &&r; $\C{// p points to r}$
271\end{cfa}
272
273For better compatibility with C, the \CFA{} team has chosen not to differentiate function overloads based on top-level reference types, and as such their contribution to the difficulty of \CFA{} expression resolution is largely restricted to the implementation details of normalization conversions and adapters.
274
275\subsubsection{Resource Management}
276
277\CFA{} also supports the RAII (``Resource Acquisition is Initialization'') idiom originated by \CC{}, thanks to the object lifetime work of Robert Schluntz\cite{Schluntz17}.
278This idiom allows a safer and more principled approach to resource management by tying acquisition of a resource to object initialization, with the corresponding resource release executed automatically at object finalization.
279A wide variety of conceptual resources may be conveniently managed by this scheme, including heap memory, file handles, and software locks.
280
281\CFA{}'s implementation of RAII is based on special constructor and destructor operators, available via the !x{ ... }! constructor syntax and !^x{ ... }! destructor syntax.
282Each type has an overridable compiler-generated zero-argument constructor, copy constructor, assignment operator, and destructor, as well as a field-wise constructor for each appropriate prefix of the member fields of !struct! types.
283For !struct! types the default versions of these operators call their equivalents on each field of the !struct!.
284The main implication of these object lifetime functions for expression resolution is that they are all included as implicit type assertions for !otype! type variables, with a secondary effect being an increase in code size due to the compiler-generated operators.
285Due to these implicit type assertions, assertion resolution is pervasive in \CFA{} polymorphic functions, even those without explicit type assertions.
286Implicitly-generated code is shown in red in the following example:
287
288\begin{cfa}
289struct kv {
290        int key;
291        char* value;
292};
293
294`void ?{} (kv& this) {` $\C[3in]{// default constructor}$
295`       this.key{};` $\C[3in]{// call recursively on members}$
296`       this.value{};
297}
298
299void ?{} (kv& this, int key) {` $\C[3in]{// partial field constructor}$
300`       this.key{ key };
301        this.value{};` $\C[3in]{// default-construct missing fields}$
302`}
303
304void ?{} (kv& this, int key, char* value) {` $\C[3in]{// complete field constructor}$
305`       this.key{ key };
306        this.value{ value };
307}
308
309void ?{} (kv& this, kv that) {` $\C[3in]{// copy constructor}$
310`       this.key{ that.key };
311        this.value{ that.value };
312}
313
314kv ?=? (kv& this, kv that) {` $\C[3in]{// assignment operator}$
315`       this.key = that.key;
316        this.value = that.value;
317}
318
319void ^?{} (kv& this) {` $\C[3in]{// destructor}$
320`       ^this.key{};
321        ^this.value{};
322}`
323
324forall(otype T `| { void ?{}(T&); void ?{}(T&, T); T ?=?(T&, T); void ^?{}(T&); }`)
325void foo(T);
326\end{cfa}
327
328\subsubsection{Tuple Types}
329
330\CFA{} adds \emph{tuple types} to C, a syntactic facility for referring to lists of values anonymously or with a single identifier.
331An identifier may name a tuple, a function may return one, and a tuple may be implicitly \emph{destructured} into its component values.
332The implementation of tuples in \CFACC{}'s code generation is based on the generic types introduced in Chapter~\ref{generic-chap}, with one compiler-generated generic type for each tuple arity.
333This allows tuples to take advantage of the same runtime optimizations available to generic types, while reducing code bloat.
334An extended presentation of the tuple features of \CFA{} can be found in \cite{Moss18}, but the following example shows the basics:
335
336\begin{cfa}
337[char, char] x = ['!', '?']; $\C{// (1); tuple type and expression syntax}$
338int x = 2; $\C{// (2)}$
339
340forall(otype T)
341[T, T] swap( T a, T b ) { $\C{// (3)}$
342        return [b, a]; $\C{// one-line swap syntax}$
343}
344
345x = swap( x ); $\C{// destructure [char, char] x into two elements}$
346$\C{// cannot use int x, not enough arguments}$
347
348void swap( int, char, char ); $\C{// (4)}$
349
350swap( x, x ); $\C{// (4) on (2), (1)}$
351$\C{// not (3) on (2), (2) due to polymorphism cost}$
352\end{cfa}
353
354Tuple destructuring breaks the one-to-one relationship between identifiers and values.
355This precludes some argument-parameter matching strategies for expression resolution, as well as cheap interpretation filters based on comparing number of parameters and arguments.
356As an example, in the call to !swap( x, x )! above, the second !x! can be resolved starting at the second or third parameter of !swap!, depending which interpretation of !x! was chosen for the first argument.
Note: See TracBrowser for help on using the repository browser.