1 | %====================================================================== |
---|
2 | \chapter{Constructors and Destructors} |
---|
3 | %====================================================================== |
---|
4 | |
---|
5 | % TODO now: as an experiment, implement Andrei Alexandrescu's ScopeGuard http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758?pgno=2 |
---|
6 | % doesn't seem possible to do this without allowing ttype on generic structs? |
---|
7 | |
---|
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. |
---|
10 | Nevertheless, one important goal is to reduce programming complexity and increase safety. |
---|
11 | To that end, \CFA provides support for implicit pre/post-execution of routines for objects, via constructors and destructors. |
---|
12 | |
---|
13 | This chapter details the design of constructors and destructors in \CFA, along with their current implementation in the translator. |
---|
14 | Generated code samples have been edited for clarity and brevity. |
---|
15 | |
---|
16 | \section{Design Criteria} |
---|
17 | \label{s:Design} |
---|
18 | In designing constructors and destructors for \CFA, the primary goals were ease of use and maintaining backwards compatibility. |
---|
19 | |
---|
20 | In C, when a variable is defined, its value is initially undefined unless it is explicitly initialized or allocated in the static area. |
---|
21 | \begin{cfacode} |
---|
22 | int main() { |
---|
23 | int x; // uninitialized |
---|
24 | int y = 5; // initialized to 5 |
---|
25 | x = y; // assigned 5 |
---|
26 | static int z; // initialized to 0 |
---|
27 | } |
---|
28 | \end{cfacode} |
---|
29 | In the example above, @x@ is defined and left uninitialized, while @y@ is defined and initialized to 5. |
---|
30 | Next, @x@ is assigned the value of @y@. |
---|
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). |
---|
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 | Use of uninitialized variables yields undefined behaviour, which is a common source of errors in C programs. |
---|
35 | |
---|
36 | Initialization of a declaration is strictly optional, permitting uninitialized variables to exist. |
---|
37 | Furthermore, declaration initialization is limited to expressions, so there is no way to insert arbitrary code before a variable is live, without delaying the declaration. |
---|
38 | Many C compilers give good warnings for uninitialized variables most of the time, but they cannot in all cases. |
---|
39 | \begin{cfacode} |
---|
40 | int f(int *); // output parameter: never reads, only writes |
---|
41 | int g(int *); // input parameter: never writes, only reads, |
---|
42 | // so requires initialized variable |
---|
43 | |
---|
44 | int x, y; |
---|
45 | f(&x); // okay - only writes to x |
---|
46 | g(&y); // uses y uninitialized |
---|
47 | \end{cfacode} |
---|
48 | Other languages are able to give errors in the case of uninitialized variable use, but due to backwards compatibility concerns, this is not the case in \CFA. |
---|
49 | |
---|
50 | In C, constructors and destructors are often mimicked by providing routines that create and tear down objects, where the tear down function is typically only necessary if the type modifies the execution environment. |
---|
51 | \begin{cfacode} |
---|
52 | struct array_int { |
---|
53 | int * x; |
---|
54 | }; |
---|
55 | struct array_int create_array(int sz) { |
---|
56 | return (struct array_int) { calloc(sizeof(int)*sz) }; |
---|
57 | } |
---|
58 | void destroy_rh(struct resource_holder * rh) { |
---|
59 | free(rh->x); |
---|
60 | } |
---|
61 | \end{cfacode} |
---|
62 | This idiom does not provide any guarantees unless the structure is opaque, which then requires that all objects are heap allocated. |
---|
63 | \begin{cfacode} |
---|
64 | struct opqaue_array_int; |
---|
65 | struct opqaue_array_int * create_opqaue_array(int sz); |
---|
66 | void destroy_opaque_array(opaque_array_int *); |
---|
67 | int opaque_get(opaque_array_int *); // subscript |
---|
68 | |
---|
69 | opaque_array_int * x = create_opaque_array(10); |
---|
70 | int x2 = opaque_get(x, 2); |
---|
71 | \end{cfacode} |
---|
72 | This pattern is cumbersome to use since every access becomes a function call. |
---|
73 | While useful in some situations, this compromise is too restrictive. |
---|
74 | Furthermore, even with this idiom it is easy to make mistakes, such as forgetting to destroy an object or destroying it multiple times. |
---|
75 | |
---|
76 | A constructor provides a way of ensuring that the necessary aspects of object initialization is performed, from setting up invariants to providing compile- and run-time checks for appropriate initialization parameters. |
---|
77 | This goal is achieved through a guarantee that a constructor is called implicitly after every object is allocated from a type with associated constructors, as part of an object's definition. |
---|
78 | Since a constructor is called on every object of a managed type, it is impossible to forget to initialize such objects, as long as all constructors perform some sensible form of initialization. |
---|
79 | |
---|
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 = { ... };@. |
---|
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 | The @this@ parameter must have a pointer type, whose base type is the type of object that the function constructs. |
---|
84 | There is precedence for enforcing the first parameter to be the @this@ parameter in other operators, such as the assignment operator, where in both cases, the left-hand side of the equals is the first parameter. |
---|
85 | There is currently a proposal to add reference types to \CFA. |
---|
86 | Once this proposal has been implemented, the @this@ parameter will become a reference type with the same restrictions. |
---|
87 | |
---|
88 | Consider the definition of a simple type encapsulating a dynamic array of @int@s. |
---|
89 | |
---|
90 | \begin{cfacode} |
---|
91 | struct Array { |
---|
92 | int * data; |
---|
93 | int len; |
---|
94 | } |
---|
95 | \end{cfacode} |
---|
96 | |
---|
97 | In C, if the user creates an @Array@ object, the fields @data@ and @len@ are uninitialized, unless an explicit initializer list is present. |
---|
98 | It is the user's responsibility to remember to initialize both of the fields to sensible values, since there are no implicit checks for invalid values or reasonable defaults. |
---|
99 | In \CFA, the user can define a constructor to handle initialization of @Array@ objects. |
---|
100 | |
---|
101 | \begin{cfacode} |
---|
102 | void ?{}(Array * arr){ |
---|
103 | arr->len = 10; // default size |
---|
104 | arr->data = malloc(sizeof(int)*arr->len); |
---|
105 | for (int i = 0; i < arr->len; ++i) { |
---|
106 | arr->data[i] = 0; |
---|
107 | } |
---|
108 | } |
---|
109 | Array x; // allocates storage for Array and calls ?{}(&x) |
---|
110 | \end{cfacode} |
---|
111 | |
---|
112 | This constructor initializes @x@ so that its @length@ field has the value 10, and its @data@ field holds a pointer to a block of memory large enough to hold 10 @int@s, and sets the value of each element of the array to 0. |
---|
113 | This particular form of constructor is called the \emph{default constructor}, because it is called on an object defined without an initializer. |
---|
114 | In other words, a default constructor is a constructor that takes a single argument: the @this@ parameter. |
---|
115 | |
---|
116 | In \CFA, a destructor is a function much like a constructor, except that its name is \lstinline!^?{}! and it takes only one argument. |
---|
117 | A destructor for the @Array@ type can be defined as: |
---|
118 | \begin{cfacode} |
---|
119 | void ^?{}(Array * arr) { |
---|
120 | free(arr->data); |
---|
121 | } |
---|
122 | \end{cfacode} |
---|
123 | The destructor is automatically called at deallocation for all objects of type @Array@. |
---|
124 | Hence, the memory associated with an @Array@ is automatically freed when the object's lifetime ends. |
---|
125 | The exact guarantees made by \CFA with respect to the calling of destructors are discussed in section \ref{sub:implicit_dtor}. |
---|
126 | |
---|
127 | As discussed previously, the distinction between initialization and assignment is important. |
---|
128 | Consider the following example. |
---|
129 | \begin{cfacode}[numbers=left] |
---|
130 | Array x, y; |
---|
131 | Array z = x; // initialization |
---|
132 | y = x; // assignment |
---|
133 | \end{cfacode} |
---|
134 | By the previous definition of the default constructor for @Array@, @x@ and @y@ are initialized to valid arrays of length 10 after their respective definitions. |
---|
135 | On line 2, @z@ is initialized with the value of @x@, while on line 3, @y@ is assigned the value of @x@. |
---|
136 | The key distinction between initialization and assignment is that a value to be initialized does not hold any meaningful values, whereas an object to be assigned might. |
---|
137 | In particular, these cases cannot be handled the same way because in the former case @z@ does not currently own an array, while @y@ does. |
---|
138 | |
---|
139 | \begin{cfacode}[emph={other}, emphstyle=\color{red}] |
---|
140 | void ?{}(Array * arr, Array other) { // copy constructor |
---|
141 | arr->len = other.len; // initialization |
---|
142 | arr->data = malloc(sizeof(int)*arr->len) |
---|
143 | for (int i = 0; i < arr->len; ++i) { |
---|
144 | arr->data[i] = other.data[i]; // copy from other object |
---|
145 | } |
---|
146 | } |
---|
147 | Array ?=?(Array * arr, Array other) { // assignment |
---|
148 | ^?{}(arr); // explicitly call destructor |
---|
149 | ?{}(arr, other); // explicitly call constructor |
---|
150 | return *arr; |
---|
151 | } |
---|
152 | \end{cfacode} |
---|
153 | The two functions above handle these cases. |
---|
154 | The first function is called a \emph{copy constructor}, because it constructs its argument by copying the values from another object of the same type. |
---|
155 | The second function is the standard copy-assignment operator. |
---|
156 | The four functions (default constructor, destructor, copy constructor, and assignment operator) are special in that they safely control the state of most objects. |
---|
157 | |
---|
158 | It is possible to define a constructor that takes any combination of parameters to provide additional initialization options. |
---|
159 | For example, a reasonable extension to the array type would be a constructor that allocates the array to a given initial capacity and initializes the elements of the array to a given @fill@ value. |
---|
160 | \begin{cfacode} |
---|
161 | void ?{}(Array * arr, int capacity, int fill) { |
---|
162 | arr->len = capacity; |
---|
163 | arr->data = malloc(sizeof(int)*arr->len); |
---|
164 | for (int i = 0; i < arr->len; ++i) { |
---|
165 | arr->data[i] = fill; |
---|
166 | } |
---|
167 | } |
---|
168 | \end{cfacode} |
---|
169 | |
---|
170 | In \CFA, constructors are called implicitly in initialization contexts. |
---|
171 | \begin{cfacode} |
---|
172 | Array x, y = { 20, 0xdeadbeef }, z = y; |
---|
173 | \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. |
---|
176 | |
---|
177 | This example generates the following code |
---|
178 | \begin{cfacode} |
---|
179 | Array x; |
---|
180 | ?{}(&x); // implicit default construct |
---|
181 | Array y; |
---|
182 | ?{}(&y, 20, 0xdeadbeef); // explicit fill construct |
---|
183 | Array z; |
---|
184 | ?{}(&z, y); // copy construct |
---|
185 | ^?{}(&z); // implicit destruct |
---|
186 | ^?{}(&y); // implicit destruct |
---|
187 | ^?{}(&x); // implicit destruct |
---|
188 | \end{cfacode} |
---|
189 | Due to the way that constructor calls are interleaved, it is impossible for @y@ to be referenced before it is initialized, except in its own constructor. |
---|
190 | This loophole is minor and exists in \CC as well. |
---|
191 | Destructors are implicitly called in reverse declaration-order so that objects with dependencies are destructed before the objects they are dependent on. |
---|
192 | |
---|
193 | \subsection{Calling Syntax} |
---|
194 | \label{sub:syntax} |
---|
195 | There are several ways to construct an object in \CFA. |
---|
196 | As previously introduced, every variable is automatically constructed at its definition, which is the most natural way to construct an object. |
---|
197 | \begin{cfacode} |
---|
198 | struct A { ... }; |
---|
199 | void ?{}(A *); |
---|
200 | void ?{}(A *, A); |
---|
201 | void ?{}(A *, int, int); |
---|
202 | |
---|
203 | A a1; // default constructed |
---|
204 | A a2 = { 0, 0 }; // constructed with 2 ints |
---|
205 | A a3 = a1; // copy constructed |
---|
206 | // implicitly destruct a3, a2, a1, in that order |
---|
207 | \end{cfacode} |
---|
208 | Since constructors and destructors are just functions, the second way is to call the function directly. |
---|
209 | \begin{cfacode} |
---|
210 | struct A { int a; }; |
---|
211 | void ?{}(A *); |
---|
212 | void ?{}(A *, A); |
---|
213 | void ^?{}(A *); |
---|
214 | |
---|
215 | A x; // implicitly default constructed: ?{}(&x) |
---|
216 | A * y = malloc(); // copy construct: ?{}(&y, malloc()) |
---|
217 | |
---|
218 | ?{}(&x); // explicit construct x, second construction |
---|
219 | ?{}(y, x); // explit construct y from x, second construction |
---|
220 | ^?{}(&x); // explicit destroy x, in different order |
---|
221 | ^?{}(y); // explicit destroy y |
---|
222 | |
---|
223 | // implicit ^?{}(&y); |
---|
224 | // implicit ^?{}(&x); |
---|
225 | \end{cfacode} |
---|
226 | Calling a constructor or destructor directly is a flexible feature that allows complete control over the management of storage. |
---|
227 | In particular, constructors double as a placement syntax. |
---|
228 | \begin{cfacode} |
---|
229 | struct A { ... }; |
---|
230 | struct memory_pool { ... }; |
---|
231 | void ?{}(memory_pool *, size_t); |
---|
232 | |
---|
233 | memory_pool pool = { 1024 }; // create an arena of size 1024 |
---|
234 | |
---|
235 | A * a = allocate(&pool); // allocate from memory pool |
---|
236 | ?{}(a); // construct an A in place |
---|
237 | |
---|
238 | for (int i = 0; i < 10; i++) { |
---|
239 | // reuse storage rather than reallocating |
---|
240 | ^?{}(a); |
---|
241 | ?{}(a); |
---|
242 | // use a ... |
---|
243 | } |
---|
244 | ^?{}(a); |
---|
245 | deallocate(&pool, a); // return to memory pool |
---|
246 | \end{cfacode} |
---|
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. |
---|
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 | \begin{cfacode} |
---|
251 | struct A { ... }; |
---|
252 | struct B { A a; }; |
---|
253 | |
---|
254 | A x, y, * z = &x; |
---|
255 | (&x){} // default construct |
---|
256 | (&x){ y } // copy construct |
---|
257 | (&x){ 1, 2, 3 } // construct with 3 arguments |
---|
258 | z{ y }; // copy construct x through a pointer |
---|
259 | ^(&x){} // destruct |
---|
260 | |
---|
261 | void ?{}(B * b) { |
---|
262 | (&b->a){ 11, 17, 13 }; // construct a member |
---|
263 | } |
---|
264 | \end{cfacode} |
---|
265 | Constructor operator syntax has relatively high precedence, requiring parentheses around an address-of expression. |
---|
266 | Destructor operator syntax is actually an statement, and requires parentheses for symmetry with constructor syntax. |
---|
267 | |
---|
268 | One of these three syntactic forms should appeal to either C or \CC programmers using \CFA. |
---|
269 | |
---|
270 | \subsection{Constructor Expressions} |
---|
271 | In \CFA, it is possible to use a constructor as an expression. |
---|
272 | Like other operators, the function name @?{}@ matches its operator syntax. |
---|
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@. |
---|
275 | \begin{cfacode} |
---|
276 | struct X { ... }; |
---|
277 | void ?{}(X *, double); |
---|
278 | X * x = malloc(){ 1.5 }; |
---|
279 | \end{cfacode} |
---|
280 | In this example, @malloc@ dynamically allocates storage and initializes it using a constructor, all before assigning it into the variable @x@. |
---|
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 | \begin{cfacode} |
---|
283 | X * x = malloc(); |
---|
284 | x{ 1.5 }; |
---|
285 | \end{cfacode} |
---|
286 | Not only is this verbose, but it is also more error prone, since this form allows maintenance code to easily sneak in between the initialization of @x@ and the initialization of the memory that @x@ points to. |
---|
287 | This feature is implemented via a transformation producing the value of the first argument of the constructor, since constructors do not themselves have a return value. |
---|
288 | Since this transformation results in two instances of the subexpression, care is taken to allocate a temporary variable to hold the result of the subexpression in the case where the subexpression may contain side effects. |
---|
289 | The previous example generates the following code. |
---|
290 | \begin{cfacode} |
---|
291 | struct X *_tmp_ctor; |
---|
292 | struct X *x = ?{}( // construct result of malloc |
---|
293 | _tmp_ctor=malloc_T(sizeof(struct X), _Alignof(struct X)), // store result of malloc |
---|
294 | 1.5 |
---|
295 | ), _tmp_ctor; // produce constructed result of malloc |
---|
296 | \end{cfacode} |
---|
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 | |
---|
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. |
---|
300 | |
---|
301 | \subsection{Function Generation} |
---|
302 | In \CFA, every type is defined to have the core set of four special functions described previously. |
---|
303 | Having these functions exist for every type greatly simplifies the semantics of the language, since most operations can simply be defined directly in terms of function calls. |
---|
304 | In addition to simplifying the definition of the language, it also simplifies the analysis that the translator must perform. |
---|
305 | If the translator can expect these functions to exist, then it can unconditionally attempt to resolve them. |
---|
306 | Moreover, the existence of a standard interface allows polymorphic code to interoperate with new types seamlessly. |
---|
307 | |
---|
308 | To mimic the behaviour of standard C, the default constructor and destructor for all of the basic types and for all pointer types are defined to do nothing, while the copy constructor and assignment operator perform a bitwise copy of the source parameter (as in \CC). |
---|
309 | |
---|
310 | There are several options for user-defined types: structures, unions, and enumerations. |
---|
311 | To aid in ease of use, the standard set of four functions is automatically generated for a user-defined type after its definition is completed. |
---|
312 | By auto-generating these functions, it is ensured that legacy C code continues to work correctly in every context where \CFA expects these functions to exist, since they are generated for every complete type. |
---|
313 | |
---|
314 | The generated functions for enumerations are the simplest. |
---|
315 | Since enumerations in C are essentially just another integral type, the generated functions behave in the same way that the built-in functions for the basic types work. |
---|
316 | For example, given the enumeration |
---|
317 | \begin{cfacode} |
---|
318 | enum Colour { |
---|
319 | R, G, B |
---|
320 | }; |
---|
321 | \end{cfacode} |
---|
322 | The following functions are automatically generated. |
---|
323 | \begin{cfacode} |
---|
324 | void ?{}(enum Colour *_dst){ |
---|
325 | // default constructor does nothing |
---|
326 | } |
---|
327 | void ?{}(enum Colour *_dst, enum Colour _src){ |
---|
328 | *_dst=_src; // bitwise copy |
---|
329 | } |
---|
330 | void ^?{}(enum Colour *_dst){ |
---|
331 | // destructor does nothing |
---|
332 | } |
---|
333 | enum Colour ?=?(enum Colour *_dst, enum Colour _src){ |
---|
334 | return *_dst=_src; // bitwise copy |
---|
335 | } |
---|
336 | \end{cfacode} |
---|
337 | In the future, \CFA will introduce strongly-typed enumerations, like those in \CC. |
---|
338 | The existing generated routines are sufficient to express this restriction, since they are currently set up to take in values of that enumeration type. |
---|
339 | Changes related to this feature only need to affect the expression resolution phase, where more strict rules will be applied to prevent implicit conversions from integral types to enumeration types, but should continue to permit conversions from enumeration types to @int@. |
---|
340 | In this way, it is still possible to add an @int@ to an enumeration, but the resulting value is an @int@, meaning it cannot be reassigned to an enumeration without a cast. |
---|
341 | |
---|
342 | For structures, the situation is more complicated. |
---|
343 | Given a structure @S@ with members @M$_0$@, @M$_1$@, ... @M$_{N-1}$@, each function @f@ in the standard set calls \lstinline{f(s->M$_i$, ...)} for each @$i$@. |
---|
344 | That is, a default constructor for @S@ default constructs the members of @S@, the copy constructor copy constructs them, and so on. |
---|
345 | For example, given the structure definition |
---|
346 | \begin{cfacode} |
---|
347 | struct A { |
---|
348 | B b; |
---|
349 | C c; |
---|
350 | } |
---|
351 | \end{cfacode} |
---|
352 | The following functions are implicitly generated. |
---|
353 | \begin{cfacode} |
---|
354 | void ?{}(A * this) { |
---|
355 | ?{}(&this->b); // default construct each field |
---|
356 | ?{}(&this->c); |
---|
357 | } |
---|
358 | void ?{}(A * this, A other) { |
---|
359 | ?{}(&this->b, other.b); // copy construct each field |
---|
360 | ?{}(&this->c, other.c); |
---|
361 | } |
---|
362 | A ?=?(A * this, A other) { |
---|
363 | ?=?(&this->b, other.b); // assign each field |
---|
364 | ?=?(&this->c, other.c); |
---|
365 | } |
---|
366 | void ^?{}(A * this) { |
---|
367 | ^?{}(&this->c); // destruct each field |
---|
368 | ^?{}(&this->b); |
---|
369 | } |
---|
370 | \end{cfacode} |
---|
371 | It is important to note that the destructors are called in reverse declaration order to prevent conflicts in the event there are dependencies among members. |
---|
372 | |
---|
373 | In addition to the standard set, a set of \emph{field constructors} is also generated for structures. |
---|
374 | The field constructors are constructors that consume a prefix of the structure's member-list. |
---|
375 | 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 }@. |
---|
377 | Extending the previous example, the following constructors are implicitly generated for @A@. |
---|
378 | \begin{cfacode} |
---|
379 | void ?{}(A * this, B b) { |
---|
380 | ?{}(&this->b, b); |
---|
381 | ?{}(&this->c); |
---|
382 | } |
---|
383 | void ?{}(A * this, B b, C c) { |
---|
384 | ?{}(&this->b, b); |
---|
385 | ?{}(&this->c, c); |
---|
386 | } |
---|
387 | \end{cfacode} |
---|
388 | |
---|
389 | For unions, the default constructor and destructor do nothing, as it is not obvious which member, if any, should be constructed. |
---|
390 | For copy constructor and assignment operations, a bitwise @memcpy@ is applied. |
---|
391 | In standard C, a union can also be initialized using a value of the same type as its first member, and so a corresponding field constructor is generated to perform a bitwise @memcpy@ of the object. |
---|
392 | An alternative to this design is to always construct and destruct the first member of a union, to match with the C semantics of initializing the first member of the union. |
---|
393 | This approach ultimately feels subtle and unsafe. |
---|
394 | Another option is to, like \CC, disallow unions from containing members that are themselves managed types. |
---|
395 | This restriction is a reasonable approach from a safety standpoint, but is not very C-like. |
---|
396 | Since the primary purpose of a union is to provide low-level memory optimization, it is assumed that the user has a certain level of maturity. |
---|
397 | It is therefore the responsibility of the user to define the special functions explicitly if they are appropriate, since it is impossible to accurately predict the ways that a union is intended to be used at compile-time. |
---|
398 | |
---|
399 | For example, given the union |
---|
400 | \begin{cfacode} |
---|
401 | union X { |
---|
402 | Y y; |
---|
403 | Z z; |
---|
404 | }; |
---|
405 | \end{cfacode} |
---|
406 | The following functions are automatically generated. |
---|
407 | \begin{cfacode} |
---|
408 | void ?{}(union X *_dst){ // default constructor |
---|
409 | } |
---|
410 | void ?{}(union X *_dst, union X _src){ // copy constructor |
---|
411 | __builtin_memcpy(_dst, &_src, sizeof(union X )); |
---|
412 | } |
---|
413 | void ^?{}(union X *_dst){ // destructor |
---|
414 | } |
---|
415 | union X ?=?(union X *_dst, union X _src){ // assignment |
---|
416 | __builtin_memcpy(_dst, &_src, sizeof(union X)); |
---|
417 | return _src; |
---|
418 | } |
---|
419 | void ?{}(union X *_dst, struct Y src){ // construct first field |
---|
420 | __builtin_memcpy(_dst, &src, sizeof(struct Y)); |
---|
421 | } |
---|
422 | \end{cfacode} |
---|
423 | |
---|
424 | % This feature works in the \CFA model, since constructors are simply special functions and can be called explicitly, unlike in \CC. % this sentence isn't really true => placement new |
---|
425 | In \CCeleven, unions may have managed members, with the caveat that if there are any members with a user-defined operation, then that operation is not implicitly defined, forcing the user to define the operation if necessary. |
---|
426 | This restriction could easily be added into \CFA once \emph{deleted} functions are added. |
---|
427 | |
---|
428 | \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. |
---|
430 | For example, |
---|
431 | \begin{cfacode} |
---|
432 | struct S { int i; }; |
---|
433 | void ?{}(S *, int); |
---|
434 | void ?{}(S *, S); |
---|
435 | |
---|
436 | const S s = { 11 }; |
---|
437 | volatile S s2 = s; |
---|
438 | \end{cfacode} |
---|
439 | Generates the following code |
---|
440 | \begin{cfacode} |
---|
441 | const struct S s; |
---|
442 | ?{}((struct S *)&s, 11); |
---|
443 | volatile struct S s2; |
---|
444 | ?{}((struct S *)&s2, s); |
---|
445 | \end{cfacode} |
---|
446 | Here, @&s@ and @&s2@ are cast to unqualified pointer types. |
---|
447 | This mechanism allows the same constructors and destructors to be used for qualified objects as for unqualified objects. |
---|
448 | This rule applies only to implicitly generated constructor calls. |
---|
449 | Hence, explicitly re-initializing qualified objects with a constructor requires an explicit cast. |
---|
450 | |
---|
451 | As discussed in Section \ref{sub:c_background}, compound literals create unnamed objects. |
---|
452 | This mechanism can continue to be used seamlessly in \CFA with managed types to create temporary objects. |
---|
453 | The object created by a compound literal is constructed using the provided brace-enclosed initializer-list, and is destructed at the end of the scope it is used in. |
---|
454 | For example, |
---|
455 | \begin{cfacode} |
---|
456 | struct A { int x; }; |
---|
457 | void ?{}(A *, int, int); |
---|
458 | { |
---|
459 | int x = (A){ 10, 20 }.x; |
---|
460 | } |
---|
461 | \end{cfacode} |
---|
462 | is equivalent to |
---|
463 | \begin{cfacode} |
---|
464 | struct A { int x, y; }; |
---|
465 | void ?{}(A *, int, int); |
---|
466 | { |
---|
467 | A _tmp; |
---|
468 | ?{}(&_tmp, 10, 20); |
---|
469 | int x = _tmp.x; |
---|
470 | ^?{}(&tmp); |
---|
471 | } |
---|
472 | \end{cfacode} |
---|
473 | |
---|
474 | Unlike \CC, \CFA provides an escape hatch that allows a user to decide at an object's definition whether it should be managed or not. |
---|
475 | An object initialized with \ateq is guaranteed to be initialized like a C object, and has no implicit destructor call. |
---|
476 | This feature provides all of the freedom that C programmers are used to having to optimize a program, while maintaining safety as a sensible default. |
---|
477 | \begin{cfacode} |
---|
478 | struct A { int * x; }; |
---|
479 | // RAII |
---|
480 | void ?{}(A * a) { a->x = malloc(sizeof(int)); } |
---|
481 | void ^?{}(A * a) { free(a->x); } |
---|
482 | |
---|
483 | A a1; // managed |
---|
484 | A a2 @= { 0 }; // unmanaged |
---|
485 | \end{cfacode} |
---|
486 | In this example, @a1@ is a managed object, and thus is default constructed and destructed at the start/end of @a1@'s lifetime, while @a2@ is an unmanaged object and is not implicitly constructed or destructed. |
---|
487 | Instead, @a2->x@ is initialized to @0@ as if it were a C object, because of the explicit initializer. |
---|
488 | |
---|
489 | In addition to freedom, \ateq provides a simple path for migrating legacy C code to \CFA, in that objects can be moved from C-style initialization to \CFA gradually and individually. |
---|
490 | 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 | It is recommended that most objects be managed by sensible constructors and destructors, except where absolutely necessary. |
---|
492 | |
---|
493 | When a user declares any constructor or destructor, the corresponding intrinsic/generated function and all field constructors for that type are hidden, so that they are not found during expression resolution until the user-defined function goes out of scope. |
---|
494 | Furthermore, if the user declares any constructor, then the intrinsic/generated default constructor is also hidden, precluding default construction. |
---|
495 | These semantics closely mirror the rule for implicit declaration of constructors in \CC, wherein the default constructor is implicitly declared if there is no user-declared constructor \cite[p.~186]{ANSI98:C++}. |
---|
496 | \begin{cfacode} |
---|
497 | struct S { int x, y; }; |
---|
498 | |
---|
499 | void f() { |
---|
500 | S s0, s1 = { 0 }, s2 = { 0, 2 }, s3 = s2; // okay |
---|
501 | { |
---|
502 | void ?{}(S * s, int i) { s->x = i*2; } // locally hide autogen constructors |
---|
503 | S s4; // error, no default constructor |
---|
504 | S s5 = { 3 }; // okay, local constructor |
---|
505 | S s6 = { 4, 5 }; // error, no field constructor |
---|
506 | S s7 = s5; // okay |
---|
507 | } |
---|
508 | S s8, s9 = { 6 }, s10 = { 7, 8 }, s11 = s10; // okay |
---|
509 | } |
---|
510 | \end{cfacode} |
---|
511 | 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 | |
---|
513 | When defining a constructor or destructor for a structure @S@, any members that are not explicitly constructed or destructed are implicitly constructed or destructed automatically. |
---|
514 | If an explicit call is present, then that call is taken in preference to any implicitly generated call. |
---|
515 | 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. |
---|
516 | \begin{cfacode} |
---|
517 | struct A { |
---|
518 | B w, x, y, z; |
---|
519 | }; |
---|
520 | void ?{}(A * a, int i) { |
---|
521 | (&a->x){ i }; |
---|
522 | (&a->z){ a->y }; |
---|
523 | } |
---|
524 | \end{cfacode} |
---|
525 | Generates the following |
---|
526 | \begin{cfacode} |
---|
527 | void ?{}(A * a, int i) { |
---|
528 | (&a->w){}; // implicit default ctor |
---|
529 | (&a->y){}; // implicit default ctor |
---|
530 | (&a->x){ i }; |
---|
531 | (&a->z){ a->y }; |
---|
532 | } |
---|
533 | \end{cfacode} |
---|
534 | Finally, it is illegal for a sub-object to be explicitly constructed for the first time after it is used for the first time. |
---|
535 | If the translator cannot be reasonably sure that an object is constructed prior to its first use, but is constructed afterward, an error is emitted. |
---|
536 | More specifically, the translator searches the body of a constructor to ensure that every sub-object is initialized. |
---|
537 | \begin{cfacode} |
---|
538 | void ?{}(A * a, double x) { |
---|
539 | f(a->x); |
---|
540 | (&a->x){ (int)x }; // error, used uninitialized on previous line |
---|
541 | } |
---|
542 | \end{cfacode} |
---|
543 | However, if the translator sees a sub-object used within the body of a constructor, but does not see a constructor call that uses the sub-object as the target of a constructor, then the translator assumes the object is to be implicitly constructed (copy constructed in a copy constructor and default constructed in any other constructor). |
---|
544 | \begin{cfacode} |
---|
545 | void ?{}(A * a) { |
---|
546 | // default constructs all members |
---|
547 | f(a->x); |
---|
548 | } |
---|
549 | |
---|
550 | void ?{}(A * a, A other) { |
---|
551 | // copy constructs all members |
---|
552 | f(a->y); |
---|
553 | } |
---|
554 | |
---|
555 | void ^?{}(A * a) { |
---|
556 | ^(&a->x){}; // explicit destructor call |
---|
557 | } // z, y, w implicitly destructed, in this order |
---|
558 | \end{cfacode} |
---|
559 | If at any point, the @this@ parameter is passed directly as the target of another constructor, then it is assumed that constructor handles the initialization of all of the object's members and no implicit constructor calls are added. |
---|
560 | To override this rule, \ateq can be used to force the translator to trust the programmer's discretion. |
---|
561 | This form of \ateq is not yet implemented. |
---|
562 | |
---|
563 | Despite great effort, some forms of C syntax do not work well with constructors in \CFA. |
---|
564 | In particular, constructor calls cannot contain designations (see \ref{sub:c_background}), since this is equivalent to allowing designations on the arguments to arbitrary function calls. |
---|
565 | \begin{cfacode} |
---|
566 | // all legal forward declarations in C |
---|
567 | void f(int, int, int); |
---|
568 | void f(int a, int b, int c); |
---|
569 | void f(int b, int c, int a); |
---|
570 | void f(int c, int a, int b); |
---|
571 | void f(int x, int y, int z); |
---|
572 | |
---|
573 | f(b:10, a:20, c:30); // which parameter is which? |
---|
574 | \end{cfacode} |
---|
575 | In C, function prototypes are permitted to have arbitrary parameter names, including no names at all, which may have no connection to the actual names used at function definition. |
---|
576 | Furthermore, a function prototype can be repeated an arbitrary number of times, each time using different names. |
---|
577 | As a result, it was decided that any attempt to resolve designated function calls with C's function prototype rules would be brittle, and thus it is not sensible to allow designations in constructor calls. |
---|
578 | |
---|
579 | In addition, constructor calls do not support unnamed nesting. |
---|
580 | \begin{cfacode} |
---|
581 | struct B { int x; }; |
---|
582 | struct C { int y; }; |
---|
583 | struct A { B b; C c; }; |
---|
584 | void ?{}(A *, B); |
---|
585 | void ?{}(A *, C); |
---|
586 | |
---|
587 | A a = { |
---|
588 | { 10 }, // construct B? - invalid |
---|
589 | }; |
---|
590 | \end{cfacode} |
---|
591 | In C, nesting initializers means that the programmer intends to initialize sub-objects with the nested initializers. |
---|
592 | The reason for this omission is to both simplify the mental model for using constructors, and to make initialization simpler for the expression resolver. |
---|
593 | If this were allowed, it would be necessary for the expression resolver to decide whether each argument to the constructor call could initialize to some argument in one of the available constructors, making the problem highly recursive and potentially much more expensive. |
---|
594 | That is, in the previous example the line marked as an error could mean construct using @?{}(A *, B)@ or with @?{}(A *, C)@, since the inner initializer @{ 10 }@ could be taken as an intermediate object of type @B@ or @C@. |
---|
595 | 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 | |
---|
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. |
---|
598 | For example, |
---|
599 | \begin{cfacode} |
---|
600 | struct A; |
---|
601 | void ?{}(A *, int); |
---|
602 | void ?{}(A *, A, A); |
---|
603 | |
---|
604 | A a1[3] = { { 3 }, { 4 }, { 5 } }; |
---|
605 | A a2[2][2] = { |
---|
606 | { { 9 }, { 10 } }, // a2[0] |
---|
607 | { {14 }, { 15 } } // a2[1] |
---|
608 | }; |
---|
609 | A a3[4] = { // 1 dimension => max depth 2 |
---|
610 | { { 11 }, { 12 } }, // error, three levels deep |
---|
611 | { 80 }, { 90 }, { 100 } |
---|
612 | } |
---|
613 | \end{cfacode} |
---|
614 | The body of @A@ has been omitted, since only the constructor interfaces are important. |
---|
615 | |
---|
616 | It should be noted that unmanaged objects can still make use of designations and nested initializers in \CFA. |
---|
617 | It is simple to overcome this limitation for managed objects by making use of compound literals, so that the arguments to the constructor call are explicitly typed. |
---|
618 | |
---|
619 | \subsection{Implicit Destructors} |
---|
620 | \label{sub:implicit_dtor} |
---|
621 | 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. |
---|
623 | The example below demonstrates a simple routine with multiple return statements. |
---|
624 | \begin{cfacode} |
---|
625 | struct A; |
---|
626 | void ^?{}(A *); |
---|
627 | |
---|
628 | void f(int i) { |
---|
629 | A x; // construct x |
---|
630 | { |
---|
631 | A y; // construct y |
---|
632 | { |
---|
633 | A z; // construct z |
---|
634 | { |
---|
635 | if (i == 0) return; // destruct x, y, z |
---|
636 | } |
---|
637 | if (i == 1) return; // destruct x, y, z |
---|
638 | } // destruct z |
---|
639 | if (i == 2) return; // destruct x, y |
---|
640 | } // destruct y |
---|
641 | } // destruct x |
---|
642 | \end{cfacode} |
---|
643 | |
---|
644 | The next example illustrates the use of simple continue and break statements and the manner that they interact with implicit destructors. |
---|
645 | \begin{cfacode} |
---|
646 | for (int i = 0; i < 10; i++) { |
---|
647 | A x; |
---|
648 | if (i == 2) { |
---|
649 | continue; // destruct x |
---|
650 | } else if (i == 3) { |
---|
651 | break; // destruct x |
---|
652 | } |
---|
653 | } // destruct x |
---|
654 | \end{cfacode} |
---|
655 | Since a destructor call is automatically inserted at the end of the block, nothing special needs to happen to destruct @x@ in the case where control reaches the end of the loop. |
---|
656 | In the case where @i@ is @2@, the continue statement runs the loop update expression and attempts to begin the next iteration of the loop. |
---|
657 | Since continue is a C statement, which does not understand destructors, it is transformed into a @goto@ statement that branches to the end of the loop, just before the block's destructors, to ensure that @x@ is destructed. |
---|
658 | When @i@ is @3@, the break statement moves control to just past the end of the loop. |
---|
659 | Unlike the previous case, the destructor for @x@ cannot be reused, so a destructor call for @x@ is inserted just before the break statement. |
---|
660 | |
---|
661 | \CFA also supports labeled break and continue statements, which allow more precise manipulation of control flow. |
---|
662 | Labeled break and continue allow the programmer to specify which control structure to target by using a label attached to a control structure. |
---|
663 | \begin{cfacode}[emph={L1,L2}, emphstyle=\color{red}] |
---|
664 | L1: for (int i = 0; i < 10; i++) { |
---|
665 | A x; |
---|
666 | for (int j = 0; j < 10; j++) { |
---|
667 | A y; |
---|
668 | if (i == 1) { |
---|
669 | continue L1; // destruct y |
---|
670 | } else if (i == 2) { |
---|
671 | break L1; // destruct x,y |
---|
672 | } |
---|
673 | } // destruct y |
---|
674 | } // destruct X |
---|
675 | \end{cfacode} |
---|
676 | The statement @continue L1@ begins the next iteration of the outer for-loop. |
---|
677 | Since the semantics of continue require the loop update expression to execute, control branches to the end of the outer for loop, meaning that the block destructor for @x@ can be reused, and it is only necessary to generate the destructor for @y@. |
---|
678 | Break, on the other hand, requires jumping out of both loops, so the destructors for both @x@ and @y@ are generated and inserted before the @break L1@ statement. |
---|
679 | |
---|
680 | Finally, an example which demonstrates goto. |
---|
681 | Since goto is a general mechanism for jumping to different locations in the program, a more comprehensive approach is required. |
---|
682 | For each goto statement $G$ and each target label $L$, let $S_G$ be the set of all managed variables alive at $G$, and let $S_L$ be the set of all managed variables alive at $L$. |
---|
683 | If at any $G$, $S_L \setminus S_G = \emptyset$, then the translator emits an error, because control flow branches from a point where the object is not yet live to a point where it is live, skipping the object's constructor. |
---|
684 | Then, for every $G$, the destructors for each variable in the set $S_G \setminus S_L$ is inserted directly before $G$, which ensures each object that is currently live at $G$, but not at $L$, is destructed before control branches. |
---|
685 | \begin{cfacode} |
---|
686 | int i = 0; |
---|
687 | { |
---|
688 | L0: ; // S_L0 = { x } |
---|
689 | A y; |
---|
690 | L1: ; // S_L1 = { x } |
---|
691 | A x; |
---|
692 | L2: ; // S_L2 = { y, x } |
---|
693 | if (i == 0) { |
---|
694 | ++i; |
---|
695 | goto L1; // S_G = { y, x } |
---|
696 | // S_G-S_L1 = { x } => destruct x |
---|
697 | } else if (i == 1) { |
---|
698 | ++i; |
---|
699 | goto L2; // S_G = { y, x } |
---|
700 | // S_G-S_L2 = {} => destruct nothing |
---|
701 | } else if (i == 2) { |
---|
702 | ++i; |
---|
703 | goto L3; // S_G = { y, x } |
---|
704 | // S_G-S_L3 = {} |
---|
705 | } else if (false) { |
---|
706 | ++i; |
---|
707 | A z; |
---|
708 | goto L3; // S_G = { z, y, x } |
---|
709 | // S_G-S_L3 = { z } => destruct z |
---|
710 | } else { |
---|
711 | ++i; |
---|
712 | goto L4; // S_G = { y, x } |
---|
713 | // S_G-S_L4 = { y, x } => destruct y, x |
---|
714 | } |
---|
715 | L3: ; // S_L3 = { y, x } |
---|
716 | goto L2; // S_G = { y, x } |
---|
717 | // S_G-S_L2 = {} |
---|
718 | } |
---|
719 | L4: ; // S_L4 = {} |
---|
720 | if (i == 4) { |
---|
721 | goto L0; // S_G = {} |
---|
722 | // S_G-S_L0 = {} |
---|
723 | } |
---|
724 | \end{cfacode} |
---|
725 | All break and continue statements are implemented in \CFA in terms of goto statements, so the more constrained forms are precisely governed by these rules. |
---|
726 | |
---|
727 | The next example demonstrates the error case. |
---|
728 | \begin{cfacode} |
---|
729 | { |
---|
730 | goto L1; // S_G = {} |
---|
731 | // S_L1-S_G = { y } => error |
---|
732 | A y; |
---|
733 | L1: ; // S_L1 = { y } |
---|
734 | A x; |
---|
735 | L2: ; // S_L2 = { y, x } |
---|
736 | } |
---|
737 | goto L2; // S_G = {} |
---|
738 | // S_L2-S_G = { y, x } => error |
---|
739 | \end{cfacode} |
---|
740 | |
---|
741 | \subsection{Implicit Copy Construction} |
---|
742 | \label{s:implicit_copy_construction} |
---|
743 | When a function is called, the arguments supplied to the call are subject to implicit copy construction (and destruction of the generated temporary), and the return value is subject to destruction. |
---|
744 | When a value is returned from a function, the copy constructor is called to pass the value back to the call site. |
---|
745 | Exempt from these rules are intrinsic and built-in functions. |
---|
746 | 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 is 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. |
---|
748 | These semantics are important to bear in mind when using unmanaged objects, and could produce unexpected results when mixed with objects that are explicitly constructed. |
---|
749 | \begin{cfacode} |
---|
750 | struct A; |
---|
751 | void ?{}(A *); |
---|
752 | void ?{}(A *, A); |
---|
753 | void ^?{}(A *); |
---|
754 | |
---|
755 | A identity(A x) { // pass by value => need local copy |
---|
756 | return x; // return by value => make call-site copy |
---|
757 | } |
---|
758 | |
---|
759 | A y, z @= {}; |
---|
760 | identity(y); // copy construct y into x |
---|
761 | identity(z); // copy construct z into x |
---|
762 | \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 | |
---|
766 | This generates the following |
---|
767 | \begin{cfacode} |
---|
768 | struct A f(struct A x){ |
---|
769 | struct A _retval_f; // return value |
---|
770 | ?{}((&_retval_f), x); // copy construct return value |
---|
771 | return _retval_f; |
---|
772 | } |
---|
773 | |
---|
774 | struct A y; |
---|
775 | ?{}(&y); // default construct |
---|
776 | struct A z = { 0 }; // C default |
---|
777 | |
---|
778 | struct A _tmp_cp1; // argument 1 |
---|
779 | struct A _tmp_cp_ret0; // return value |
---|
780 | _tmp_cp_ret0=f( |
---|
781 | (?{}(&_tmp_cp1, y) , _tmp_cp1) // argument is a comma expression |
---|
782 | ), _tmp_cp_ret0; // return value for cascading |
---|
783 | ^?{}(&_tmp_cp_ret0); // destruct return value |
---|
784 | ^?{}(&_tmp_cp1); // destruct argument 1 |
---|
785 | |
---|
786 | struct A _tmp_cp2; // argument 1 |
---|
787 | struct A _tmp_cp_ret1; // return value |
---|
788 | _tmp_cp_ret1=f( |
---|
789 | (?{}(&_tmp_cp2, z), _tmp_cp2) // argument is a common expression |
---|
790 | ), _tmp_cp_ret1; // return value for cascading |
---|
791 | ^?{}(&_tmp_cp_ret1); // destruct return value |
---|
792 | ^?{}(&_tmp_cp2); // destruct argument 1 |
---|
793 | ^?{}(&y); |
---|
794 | \end{cfacode} |
---|
795 | |
---|
796 | A special syntactic form, such as a variant of \ateq, can be implemented to specify at the call site that an argument should not be copy constructed, to regain some control for the C programmer. |
---|
797 | \begin{cfacode} |
---|
798 | identity(z@); // do not copy construct argument |
---|
799 | // - will copy construct/destruct return value |
---|
800 | A@ identity_nocopy(A @ x) { // argument not copy constructed or destructed |
---|
801 | return x; // not copy constructed |
---|
802 | // return type marked @ => not destructed |
---|
803 | } |
---|
804 | \end{cfacode} |
---|
805 | It should be noted that reference types will allow specifying that a value does not need to be copied, however reference types do not provide a means of preventing implicit copy construction from uses of the reference, so the problem is still present when passing or returning the reference by value. |
---|
806 | |
---|
807 | A known issue with this implementation is that the argument and return value temporaries are not guaranteed to have the same address for their entire lifetimes. |
---|
808 | In the previous example, since @_retval_f@ is allocated and constructed in @f@, then returned by value, the internal data is bitwise copied into the caller's stack frame. |
---|
809 | This approach works out most of the time, because typically destructors need to only access the fields of the object and recursively destroy. |
---|
810 | It is currently the case that constructors and destructors that use the @this@ pointer as a unique identifier to store data externally do not work correctly for return value objects. |
---|
811 | Thus, it is currently not safe to rely on an object's @this@ pointer to remain constant throughout execution of the program. |
---|
812 | \begin{cfacode} |
---|
813 | A * external_data[32]; |
---|
814 | int ext_count; |
---|
815 | struct A; |
---|
816 | void ?{}(A * a) { |
---|
817 | // ... |
---|
818 | external_data[ext_count++] = a; |
---|
819 | } |
---|
820 | void ^?{}(A * a) { |
---|
821 | for (int i = 0; i < ext_count) { |
---|
822 | if (a == external_data[i]) { // may never be true |
---|
823 | // ... |
---|
824 | } |
---|
825 | } |
---|
826 | } |
---|
827 | |
---|
828 | A makeA() { |
---|
829 | A x; // stores &x in external_data |
---|
830 | return x; |
---|
831 | } |
---|
832 | makeA(); // return temporary has a different address than x |
---|
833 | // equivalent to: |
---|
834 | // A _tmp; |
---|
835 | // _tmp = makeA(), _tmp; |
---|
836 | // ^?{}(&_tmp); |
---|
837 | \end{cfacode} |
---|
838 | In the above example, a global array of pointers is used to keep track of all of the allocated @A@ objects. |
---|
839 | Due to copying on return, the current object being destructed does not exist in the array if an @A@ object is ever returned by value from a function, such as in @makeA@. |
---|
840 | |
---|
841 | This problem could be solved in the translator by changing the function signatures so that the return value is moved into the parameter list. |
---|
842 | For example, the translator could restructure the code like so |
---|
843 | \begin{cfacode} |
---|
844 | void f(struct A x, struct A * _retval_f){ |
---|
845 | ?{}(_retval_f, x); // construct directly into caller's stack frame |
---|
846 | } |
---|
847 | |
---|
848 | struct A y; |
---|
849 | ?{}(&y); |
---|
850 | struct A z = { 0 }; |
---|
851 | |
---|
852 | struct A _tmp_cp1; // argument 1 |
---|
853 | struct A _tmp_cp_ret0; // return value |
---|
854 | f((?{}(&_tmp_cp1, y) , _tmp_cp1), &_tmp_cp_ret0), _tmp_cp_ret0; |
---|
855 | ^?{}(&_tmp_cp_ret0); // return value |
---|
856 | ^?{}(&_tmp_cp1); // argument 1 |
---|
857 | \end{cfacode} |
---|
858 | This transformation provides @f@ with the address of the return variable so that it can be constructed into directly. |
---|
859 | 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, \eg |
---|
861 | \begin{cfacode} |
---|
862 | struct A { int v; }; |
---|
863 | A x; // unmanaged, since only trivial constructors are available |
---|
864 | { |
---|
865 | void ?{}(A * a) { ... } |
---|
866 | void ^?{}(A * a) { ... } |
---|
867 | A y; // managed |
---|
868 | } |
---|
869 | A z; // unmanaged |
---|
870 | \end{cfacode} |
---|
871 | Hence there is not enough information to determine at function declaration whether a type is managed or not, and thus it is the case that all signatures have to be rewritten to account for possible copy constructor and destructor calls. |
---|
872 | Even with this change, it would still be possible to declare backwards compatible function prototypes with an @extern "C"@ block, which allows for the definition of C-compatible functions within \CFA code, however this would require actual changes to the way code inside of an @extern "C"@ function is generated as compared with normal code generation. |
---|
873 | Furthermore, it is not possible to overload C functions, so using @extern "C"@ to declare functions is of limited use. |
---|
874 | |
---|
875 | It would be possible to regain some control by adding an attribute to structures 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, structures should be manageable by default, since otherwise the default case becomes more verbose. |
---|
877 | 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 | \begin{cfacode} |
---|
879 | __attribute__((manageable)) struct A { ... }; // can declare constructors |
---|
880 | __attribute__((unmanageable)) struct B { ... }; // cannot declare constructors |
---|
881 | struct C { ... }; // can declare constructors |
---|
882 | |
---|
883 | A f(); // rewritten void f(A *); |
---|
884 | B g(); // not rewritten |
---|
885 | C h(); // rewritten void h(C *); |
---|
886 | \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 | 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 | Furthermore, no restrictions would need to be placed on whether objects can be constructed. |
---|
890 | \begin{cfacode} |
---|
891 | __attribute__((identifiable)) struct A { ... }; // can declare constructors |
---|
892 | struct B { ... }; // can declare constructors |
---|
893 | |
---|
894 | A f(); // rewritten void f(A *); |
---|
895 | B g(); // not rewritten |
---|
896 | \end{cfacode} |
---|
897 | |
---|
898 | Ultimately, both of these are patchwork solutions. |
---|
899 | Since a real compiler has full control over its calling conventions, it can seamlessly allow passing the return parameter without outwardly changing the signature of a routine. |
---|
900 | As such, it has been decided that this issue is not currently a priority and will be fixed when a full \CFA compiler is implemented. |
---|
901 | |
---|
902 | \section{Implementation} |
---|
903 | \subsection{Array Initialization} |
---|
904 | Arrays are a special case in the C type-system. |
---|
905 | C arrays do not carry around their size, making it impossible to write a standalone \CFA function that constructs or destructs an array while maintaining the standard interface for constructors and destructors. |
---|
906 | Instead, \CFA defines the initialization and destruction of an array recursively. |
---|
907 | That is, when an array is defined, each of its elements is constructed in order from element 0 up to element $n-1$. |
---|
908 | When an array is to be implicitly destructed, each of its elements is destructed in reverse order from element $n-1$ down to element 0. |
---|
909 | As in C, it is possible to explicitly provide different initializers for each element of the array through array initialization syntax. |
---|
910 | In this case, each of the initializers is taken in turn to construct a subsequent element of the array. |
---|
911 | If too many initializers are provided, only the initializers up to N are actually used. |
---|
912 | If too few initializers are provided, then the remaining elements are default constructed. |
---|
913 | |
---|
914 | For example, given the following code. |
---|
915 | \begin{cfacode} |
---|
916 | struct X { |
---|
917 | int x, y, z; |
---|
918 | }; |
---|
919 | void f() { |
---|
920 | X x[10] = { { 1, 2, 3 }, { 4 }, { 7, 8 } }; |
---|
921 | } |
---|
922 | \end{cfacode} |
---|
923 | The following code is generated for @f@. |
---|
924 | \begin{cfacode} |
---|
925 | void f(){ |
---|
926 | struct X x[((long unsigned int )10)]; |
---|
927 | // construct x |
---|
928 | { |
---|
929 | int _index0 = 0; |
---|
930 | // construct with explicit initializers |
---|
931 | { |
---|
932 | if (_index0<10) ?{}(&x[_index0], 1, 2, 3); |
---|
933 | ++_index0; |
---|
934 | if (_index0<10) ?{}(&x[_index0], 4); |
---|
935 | ++_index0; |
---|
936 | if (_index0<10) ?{}(&x[_index0], 7, 8); |
---|
937 | ++_index0; |
---|
938 | } |
---|
939 | |
---|
940 | // default construct remaining elements |
---|
941 | for (;_index0<10;++_index0) { |
---|
942 | ?{}(&x[_index0]); |
---|
943 | } |
---|
944 | } |
---|
945 | // destruct x |
---|
946 | { |
---|
947 | int _index1 = 10-1; |
---|
948 | for (;_index1>=0;--_index1) { |
---|
949 | ^?{}(&x[_index1]); |
---|
950 | } |
---|
951 | } |
---|
952 | } |
---|
953 | \end{cfacode} |
---|
954 | Multidimensional arrays require more complexity. |
---|
955 | For example, a two dimensional array |
---|
956 | \begin{cfacode} |
---|
957 | void g() { |
---|
958 | X x[10][10] = { |
---|
959 | { { 1, 2, 3 }, { 4 } }, // x[0] |
---|
960 | { { 7, 8 } } // x[1] |
---|
961 | }; |
---|
962 | }\end{cfacode} |
---|
963 | Generates the following |
---|
964 | \begin{cfacode} |
---|
965 | void g(){ |
---|
966 | struct X x[10][10]; |
---|
967 | // construct x |
---|
968 | { |
---|
969 | int _index0 = 0; |
---|
970 | for (;_index0<10;++_index0) { |
---|
971 | { |
---|
972 | int _index1 = 0; |
---|
973 | // construct with explicit initializers |
---|
974 | { |
---|
975 | switch ( _index0 ) { |
---|
976 | case 0: |
---|
977 | // construct first array |
---|
978 | if ( _index1<10 ) ?{}(&x[_index0][_index1], 1, 2, 3); |
---|
979 | ++_index1; |
---|
980 | if ( _index1<10 ) ?{}(&x[_index0][_index1], 4); |
---|
981 | ++_index1; |
---|
982 | break; |
---|
983 | case 1: |
---|
984 | // construct second array |
---|
985 | if ( _index1<10 ) ?{}(&x[_index0][_index1], 7, 8); |
---|
986 | ++_index1; |
---|
987 | break; |
---|
988 | } |
---|
989 | } |
---|
990 | // default construct remaining elements |
---|
991 | for (;_index1<10;++_index1) { |
---|
992 | ?{}(&x[_index0][_index1]); |
---|
993 | } |
---|
994 | } |
---|
995 | } |
---|
996 | } |
---|
997 | // destruct x |
---|
998 | { |
---|
999 | int _index2 = 10-1; |
---|
1000 | for (;_index2>=0;--_index2) { |
---|
1001 | { |
---|
1002 | int _index3 = 10-1; |
---|
1003 | for (;_index3>=0;--_index3) { |
---|
1004 | ^?{}(&x[_index2][_index3]); |
---|
1005 | } |
---|
1006 | } |
---|
1007 | } |
---|
1008 | } |
---|
1009 | } |
---|
1010 | \end{cfacode} |
---|
1011 | % It is possible to generate slightly simpler code for the switch cases, since the value of @_index1@ is known at compile-time within each case, however the procedure for generating constructor calls is complicated. |
---|
1012 | % It is simple to remove the increment statements for @_index1@, but it is not simple to remove the |
---|
1013 | %% technically, it's not hard either. I could easily downcast and change the second argument to ?[?], but is it really necessary/worth it?? |
---|
1014 | |
---|
1015 | \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 \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 | By default, objects within a translation unit are constructed in declaration order, and destructed in the reverse order. |
---|
1019 | 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 a user program. |
---|
1021 | |
---|
1022 | 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}. |
---|
1023 | A similar function is generated with the \emph{destructor} attribute, which handles all global destructor calls. |
---|
1024 | At the time of writing, initialization routines in the library are specified with priority \emph{101}, which is the highest priority level that GCC allows, whereas initialization routines in the user's code are implicitly given the default priority level, which ensures they have a lower priority than any code with a specified priority level. |
---|
1025 | This mechanism allows arbitrarily complicated initialization to occur before any user code runs, making it possible for library designers to initialize their modules without requiring the user to call specific startup or tear-down routines. |
---|
1026 | |
---|
1027 | For example, given the following global declarations. |
---|
1028 | \begin{cfacode} |
---|
1029 | struct X { |
---|
1030 | int y, z; |
---|
1031 | }; |
---|
1032 | void ?{}(X *); |
---|
1033 | void ?{}(X *, int, int); |
---|
1034 | void ^?{}(X *); |
---|
1035 | |
---|
1036 | X a; |
---|
1037 | X b = { 10, 3 }; |
---|
1038 | \end{cfacode} |
---|
1039 | The following code is generated. |
---|
1040 | \begin{cfacode} |
---|
1041 | __attribute__ ((constructor)) static void _init_global_ctor(void){ |
---|
1042 | ?{}(&a); |
---|
1043 | ?{}(&b, 10, 3); |
---|
1044 | } |
---|
1045 | __attribute__ ((destructor)) static void _destroy_global_ctor(void){ |
---|
1046 | ^?{}(&b); |
---|
1047 | ^?{}(&a); |
---|
1048 | } |
---|
1049 | \end{cfacode} |
---|
1050 | |
---|
1051 | % https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes |
---|
1052 | % 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. |
---|
1054 | A similar attribute can be implemented in \CFA by pulling marked objects into global constructor/destructor-attribute functions with the specified priority. |
---|
1055 | For example, |
---|
1056 | \begin{cfacode} |
---|
1057 | struct A { ... }; |
---|
1058 | void ?{}(A *, int); |
---|
1059 | void ^?{}(A *); |
---|
1060 | __attribute__((init_priority(200))) A x = { 123 }; |
---|
1061 | \end{cfacode} |
---|
1062 | would generate |
---|
1063 | \begin{cfacode} |
---|
1064 | A x; |
---|
1065 | __attribute__((constructor(200))) __init_x() { |
---|
1066 | ?{}(&x, 123); // construct x with priority 200 |
---|
1067 | } |
---|
1068 | __attribute__((destructor(200))) __destroy_x() { |
---|
1069 | ?{}(&x); // destruct x with priority 200 |
---|
1070 | } |
---|
1071 | \end{cfacode} |
---|
1072 | |
---|
1073 | \subsection{Static Local Variables} |
---|
1074 | In standard C, it is possible to mark variables that are local to a function with the @static@ storage class. |
---|
1075 | 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. |
---|
1077 | |
---|
1078 | 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 change does not preclude any valid C code. |
---|
1081 | Local objects with @static@ storage class are only implicitly constructed and destructed once for the duration of the program. |
---|
1082 | The object is constructed when its declaration is reached for the first time. |
---|
1083 | The object is destructed once at the end of the program. |
---|
1084 | |
---|
1085 | Construction of @static@ local objects is implemented via an accompanying @static bool@ variable, which records whether the variable has already been constructed. |
---|
1086 | A conditional branch checks the value of the companion @bool@, and if the variable has not yet been constructed then the object is constructed. |
---|
1087 | The object's destructor is scheduled to be run when the program terminates using @atexit@ \footnote{When using the dynamic linker, it is possible to dynamically load and unload a shared library. Since glibc 2.2.3 \cite{atexit}, functions registered with @atexit@ within the shared library are called when unloading the shared library. As such, static local objects can be destructed using this mechanism even in shared libraries on Linux systems.}, and the companion @bool@'s value is set so that subsequent invocations of the function do not reconstruct the object. |
---|
1088 | Since the parameter to @atexit@ is a parameter-less function, some additional tweaking is required. |
---|
1089 | 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 | Finally, the newly generated function is registered with @atexit@, instead of registering the destructor directly. |
---|
1093 | 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. |
---|
1094 | Extending the previous example |
---|
1095 | \begin{cfacode} |
---|
1096 | int f(int x) { |
---|
1097 | static X a; |
---|
1098 | static X b = { x, x }; // depends on parameter value |
---|
1099 | static X c = b; // depends on local variable |
---|
1100 | } |
---|
1101 | \end{cfacode} |
---|
1102 | Generates the following. |
---|
1103 | \begin{cfacode} |
---|
1104 | static struct X a_static_var0; |
---|
1105 | static void __a_dtor_atexit0(void){ |
---|
1106 | ((void)^?{}(((struct X *)(&a_static_var0)))); |
---|
1107 | } |
---|
1108 | static struct X b_static_var1; |
---|
1109 | static void __b_dtor_atexit1(void){ |
---|
1110 | ((void)^?{}(((struct X *)(&b_static_var1)))); |
---|
1111 | } |
---|
1112 | static struct X c_static_var2; |
---|
1113 | static void __c_dtor_atexit2(void){ |
---|
1114 | ((void)^?{}(((struct X *)(&c_static_var2)))); |
---|
1115 | } |
---|
1116 | int f(int x){ |
---|
1117 | int _retval_f; |
---|
1118 | __attribute__ ((unused)) static void *_dummy0; |
---|
1119 | static _Bool __a_uninitialized = 1; |
---|
1120 | if ( __a_uninitialized ) { |
---|
1121 | ((void)?{}(((struct X *)(&a_static_var0)))); |
---|
1122 | ((void)(__a_uninitialized=0)); |
---|
1123 | ((void)atexit(__a_dtor_atexit0)); |
---|
1124 | } |
---|
1125 | |
---|
1126 | __attribute__ ((unused)) static void *_dummy1; |
---|
1127 | static _Bool __b_uninitialized = 1; |
---|
1128 | if ( __b_uninitialized ) { |
---|
1129 | ((void)?{}(((struct X *)(&b_static_var1)), x, x)); |
---|
1130 | ((void)(__b_uninitialized=0)); |
---|
1131 | ((void)atexit(__b_dtor_atexit1)); |
---|
1132 | } |
---|
1133 | |
---|
1134 | __attribute__ ((unused)) static void *_dummy2; |
---|
1135 | static _Bool __c_uninitialized = 1; |
---|
1136 | if ( __c_uninitialized ) { |
---|
1137 | ((void)?{}(((struct X *)(&c_static_var2)), b_static_var1)); |
---|
1138 | ((void)(__c_uninitialized=0)); |
---|
1139 | ((void)atexit(__c_dtor_atexit2)); |
---|
1140 | } |
---|
1141 | } |
---|
1142 | \end{cfacode} |
---|
1143 | |
---|
1144 | \subsection{Polymorphism} |
---|
1145 | As mentioned in section \ref{sub:polymorphism}, \CFA currently has 3 type-classes that are used to designate polymorphic data types: @otype@, @dtype@, and @ftype@. |
---|
1146 | In previous versions of \CFA, @otype@ was syntactic sugar for @dtype@ with known size/alignment information and an assignment function. |
---|
1147 | That is, |
---|
1148 | \begin{cfacode} |
---|
1149 | forall(otype T) |
---|
1150 | void f(T); |
---|
1151 | \end{cfacode} |
---|
1152 | was equivalent to |
---|
1153 | \begin{cfacode} |
---|
1154 | forall(dtype T | sized(T) | { T ?=?(T *, T); }) |
---|
1155 | void f(T); |
---|
1156 | \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. |
---|
1160 | As such, @otype@ has been augmented to include assertions for a default constructor, copy constructor, and destructor. |
---|
1161 | That is, the previous example is now equivalent to |
---|
1162 | \begin{cfacode} |
---|
1163 | forall(dtype T | sized(T) | |
---|
1164 | { T ?=?(T *, T); void ?{}(T *); void ?{}(T *, T); void ^?{}(T *); }) |
---|
1165 | void f(T); |
---|
1166 | \end{cfacode} |
---|
1167 | These 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. |
---|