Changeset 0eb18557 for doc/rob_thesis/ctordtor.tex
- Timestamp:
- Apr 12, 2017, 3:54:28 PM (7 years ago)
- Branches:
- ADT, aaron-thesis, arm-eh, ast-experimental, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, resolv-new, with_gc
- Children:
- e869e434
- Parents:
- eaa2f3a1
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/rob_thesis/ctordtor.tex
reaa2f3a1 r0eb18557 7 7 8 8 Since \CFA is a true systems language, it does not provide a garbage collector. 9 As well, \CFA is not an object-oriented programming language, i.e., structures cannot have routine members.9 As well, \CFA is not an object-oriented programming language, \ie, structures cannot have routine members. 10 10 Nevertheless, one important goal is to reduce programming complexity and increase safety. 11 11 To that end, \CFA provides support for implicit pre/post-execution of routines for objects, via constructors and destructors. … … 30 30 Next, @x@ is assigned the value of @y@. 31 31 In the last line, @z@ is implicitly initialized to 0 since it is marked @static@. 32 The key difference between assignment and initialization being that assignment occurs on a live object ( i.e., an object that contains data).32 The key difference between assignment and initialization being that assignment occurs on a live object (\ie, an object that contains data). 33 33 It is important to note that this means @x@ could have been used uninitialized prior to being assigned, while @y@ could not be used uninitialized. 34 34 Use of uninitialized variables yields undefined behaviour, which is a common source of errors in C programs. … … 79 79 80 80 In \CFA, a constructor is a function with the name @?{}@. 81 Like other operators in \CFA, the name represents the syntax used to call the constructor, e.g., @struct S = { ... };@.81 Like other operators in \CFA, the name represents the syntax used to call the constructor, \eg, @struct S = { ... };@. 82 82 Every constructor must have a return type of @void@ and at least one parameter, the first of which is colloquially referred to as the \emph{this} parameter, as in many object-oriented programming-languages (however, a programmer can give it an arbitrary name). 83 83 The @this@ parameter must have a pointer type, whose base type is the type of object that the function constructs. … … 114 114 In other words, a default constructor is a constructor that takes a single argument: the @this@ parameter. 115 115 116 In \CFA, a destructor is a function much like a constructor, except that its name is \lstinline!^?{}! and it take only one argument.117 A destructor for the @Array@ type can be defined as such.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 118 \begin{cfacode} 119 119 void ^?{}(Array * arr) { … … 167 167 } 168 168 \end{cfacode} 169 169 170 In \CFA, constructors are called implicitly in initialization contexts. 170 171 \begin{cfacode} 171 172 Array x, y = { 20, 0xdeadbeef }, z = y; 172 173 \end{cfacode} 173 174 In \CFA, constructor calls look just like C initializers, which allows them to be inserted into legacy C code with minimal code changes, and also provides a very simple syntax that veteran C programmers are familiar with. 175 One downside of reusing C initialization syntax is that it isn't possible to determine whether an object is constructed just by looking at its declaration, since that requires knowledge of whether the type is managed at that point. 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 176 177 177 This example generates the following code … … 246 246 \end{cfacode} 247 247 Finally, constructors and destructors support \emph{operator syntax}. 248 Like other operators in \CFA, the function name mirrors the use-case, in that the first $N$ arguments fill in the place of the question mark.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 249 This syntactic form is similar to the new initialization syntax in \CCeleven, except that it is used in expression contexts, rather than declaration contexts. 250 250 \begin{cfacode} … … 272 272 Like other operators, the function name @?{}@ matches its operator syntax. 273 273 For example, @(&x){}@ calls the default constructor on the variable @x@, and produces @&x@ as a result. 274 A key example for this capability is the use of constructor expressions to initialize the result of a call to standard C routine@malloc@.274 A key example for this capability is the use of constructor expressions to initialize the result of a call to @malloc@. 275 275 \begin{cfacode} 276 276 struct X { ... }; 277 277 void ?{}(X *, double); 278 X * x = malloc( sizeof(X)){ 1.5 };278 X * x = malloc(){ 1.5 }; 279 279 \end{cfacode} 280 280 In this example, @malloc@ dynamically allocates storage and initializes it using a constructor, all before assigning it into the variable @x@. 281 281 If this extension is not present, constructing dynamically allocated objects is much more cumbersome, requiring separate initialization of the pointer and initialization of the pointed-to memory. 282 282 \begin{cfacode} 283 X * x = malloc( sizeof(X));283 X * x = malloc(); 284 284 x{ 1.5 }; 285 285 \end{cfacode} … … 291 291 struct X *_tmp_ctor; 292 292 struct X *x = ?{}( // construct result of malloc 293 _tmp_ctor=malloc (sizeof(struct X)), // store result of malloc293 _tmp_ctor=malloc_T(sizeof(struct X), _Alignof(struct X)), // store result of malloc 294 294 1.5 295 295 ), _tmp_ctor; // produce constructed result of malloc … … 297 297 It should be noted that this technique is not exclusive to @malloc@, and allows a user to write a custom allocator that can be idiomatically used in much the same way as a constructed @malloc@ call. 298 298 299 It is also possible to use operator syntax with destructors. 300 Unlike constructors, operator syntax with destructors is a statement and thus does not produce a value, since the destructed object is invalidated by the use of a destructor. 301 For example, \lstinline!^(&x){}! calls the destructor on the variable @x@. 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. 302 300 303 301 \subsection{Function Generation} … … 376 374 The field constructors are constructors that consume a prefix of the structure's member-list. 377 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. 378 The addition of field constructors allows structures in \CFA to be used naturally in the same ways as used in C ( i.e., to initialize any prefix of the structure), e.g., @A a0 = { b }, a1 = { b, c }@.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 }@. 379 377 Extending the previous example, the following constructors are implicitly generated for @A@. 380 378 \begin{cfacode} … … 429 427 430 428 \subsection{Using Constructors and Destructors} 431 Implicitly generated constructor and destructor calls ignore the outermost type qualifiers, e.g.@const@ and @volatile@, on a type by way of a cast on the first argument to the function.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. 432 430 For example, 433 431 \begin{cfacode} … … 448 446 Here, @&s@ and @&s2@ are cast to unqualified pointer types. 449 447 This mechanism allows the same constructors and destructors to be used for qualified objects as for unqualified objects. 450 This applies only to implicitly generated constructor calls.448 This rule applies only to implicitly generated constructor calls. 451 449 Hence, explicitly re-initializing qualified objects with a constructor requires an explicit cast. 452 450 … … 489 487 Instead, @a2->x@ is initialized to @0@ as if it were a C object, because of the explicit initializer. 490 488 491 In addition to freedom, \ateq provides a simple path tomigrating legacy C code to \CFA, in that objects can be moved from C-style initialization to \CFA gradually and individually.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. 492 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. 493 491 It is recommended that most objects be managed by sensible constructors and destructors, except where absolutely necessary. … … 503 501 { 504 502 void ?{}(S * s, int i) { s->x = i*2; } // locally hide autogen constructors 505 S s4; // error 506 S s5 = { 3 }; // okay 507 S s6 = { 4, 5 }; // error 503 S s4; // error, no default constructor 504 S s5 = { 3 }; // okay, local constructor 505 S s6 = { 4, 5 }; // error, no field constructor 508 506 S s7 = s5; // okay 509 507 } … … 513 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. 514 512 515 When defining a constructor or destructor for a struct @S@, any members that are not explicitly constructed or destructed are implicitly constructed or destructed automatically.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. 516 514 If an explicit call is present, then that call is taken in preference to any implicitly generated call. 517 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. … … 597 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. 598 596 599 More precisely, constructor calls cannot have a nesting depth greater than the number of array components in the type of the initialized object, plus one.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. 600 598 For example, 601 599 \begin{cfacode} … … 609 607 { {14 }, { 15 } } // a2[1] 610 608 }; 611 A a3[4] = { 612 { { 11 }, { 12 } }, // error 609 A a3[4] = { // 1 dimension => max depth 2 610 { { 11 }, { 12 } }, // error, three levels deep 613 611 { 80 }, { 90 }, { 100 } 614 612 } … … 622 620 \label{sub:implicit_dtor} 623 621 Destructors are automatically called at the end of the block in which the object is declared. 624 In addition to this, destructors are automatically called when statements manipulate control flow to leave a block in which the object is declared, e.g., with return, break, continue, and goto statements.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. 625 623 The example below demonstrates a simple routine with multiple return statements. 626 624 \begin{cfacode} … … 747 745 Exempt from these rules are intrinsic and built-in functions. 748 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. 749 That is, since the parameter is not marked as an unmanaged object using \ateq, it willbe copy constructed if it is returned by value or passed as an argument to another function, so to guarantee consistent behaviour, unmanaged objects must be copy constructed when passed as arguments.750 Th is is an important detailto bear in mind when using unmanaged objects, and could produce unexpected results when mixed with objects that are explicitly constructed.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. 751 749 \begin{cfacode} 752 750 struct A; … … 763 761 identity(z); // copy construct z into x 764 762 \end{cfacode} 765 Note that @z@ is copy constructed into a temporary variable to be passed as an argument, which is also destructed after the call. 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. 766 765 767 766 This generates the following … … 859 858 This transformation provides @f@ with the address of the return variable so that it can be constructed into directly. 860 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}. 861 A key difference in this case is that every function would need to be rewritten like this, since types can switch between managed and unmanaged at different scope levels, e.g.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 862 861 \begin{cfacode} 863 862 struct A { int v; }; … … 874 873 Furthermore, it is not possible to overload C functions, so using @extern "C"@ to declare functions is of limited use. 875 874 876 It would be possible to regain some control by adding an attribute to struct s that specifies whether they can be managed or not (perhaps \emph{manageable} or \emph{unmanageable}), and to emit an error in the case that a constructor or destructor is declared for an unmanageable type.877 Ideally, struct s should be manageable by default, since otherwise the default case becomes more verbose.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. 878 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. 879 878 \begin{cfacode} … … 886 885 C h(); // rewritten void h(C *); 887 886 \end{cfacode} 888 An alternative is to insteadmake the attribute \emph{identifiable}, which states that objects of this type use the @this@ parameter as an identity.887 An alternative is to make the attribute \emph{identifiable}, which states that objects of this type use the @this@ parameter as an identity. 889 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. 890 889 Furthermore, no restrictions would need to be placed on whether objects can be constructed. … … 1015 1014 1016 1015 \subsection{Global Initialization} 1017 In standard C, global variables can only be initialized to compile-time constant expressions. 1018 This places strict limitations on the programmer's ability to control the default values of objects. 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. 1019 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. 1020 1018 By default, objects within a translation unit are constructed in declaration order, and destructed in the reverse order. 1021 1019 The default order of construction of objects amongst translation units is unspecified. 1022 It is, however, guaranteed that any global objects in the standard library are initialized prior to the initialization of any object in theuser program.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. 1023 1021 1024 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}. … … 1053 1051 % https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes 1054 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 1055 GCC provides an attribute @init_priority@ , which allows specifying the relative priority for initialization of global objects on a per-object basis in \CC.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. 1056 1054 A similar attribute can be implemented in \CFA by pulling marked objects into global constructor/destructor-attribute functions with the specified priority. 1057 1055 For example, … … 1076 1074 In standard C, it is possible to mark variables that are local to a function with the @static@ storage class. 1077 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. 1078 Much like global variables, in C@static@ variables can only be initialized to a \emph{compile-time constant value} so that a compiler is able to create storage for the variable and initialize it at compile-time.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. 1079 1077 1080 1078 Yet again, this rule is too restrictive for a language with constructors and destructors. 1081 Instead, \CFA modifies the definition of a @static@ local variable so that objects are guaranteed to be live from the time control flow reaches their declaration, until the end of the program, since the initializer expression is not necessarily a compile-time constant, but can depend on the current execution state of the function.1082 Since standard C does not allow access to a @static@ local variable before the first time control flow reaches the declaration, this restrictiondoes not preclude any valid C code.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. 1083 1081 Local objects with @static@ storage class are only implicitly constructed and destructed once for the duration of the program. 1084 1082 The object is constructed when its declaration is reached for the first time. … … 1090 1088 Since the parameter to @atexit@ is a parameter-less function, some additional tweaking is required. 1091 1089 First, the @static@ variable must be hoisted up to global scope and uniquely renamed to prevent name clashes with other global objects. 1092 Second, a function is built which calls the destructor for the newly hoisted variable. 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. 1093 1092 Finally, the newly generated function is registered with @atexit@, instead of registering the destructor directly. 1094 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. … … 1156 1155 void f(T); 1157 1156 \end{cfacode} 1158 This allows easily specifying constraints that are common to all complete object 1159 1160 Now that \CFA has constructors and destructors, more of a complete object's behaviour can be specified bythan was previously possible.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. 1161 1160 As such, @otype@ has been augmented to include assertions for a default constructor, copy constructor, and destructor. 1162 1161 That is, the previous example is now equivalent to 1163 1162 \begin{cfacode} 1164 forall(dtype T | sized(T) | { T ?=?(T *, T); void ?{}(T *); void ?{}(T *, T); void ^?{}(T *); }) 1163 forall(dtype T | sized(T) | 1164 { T ?=?(T *, T); void ?{}(T *); void ?{}(T *, T); void ^?{}(T *); }) 1165 1165 void f(T); 1166 1166 \end{cfacode} 1167 Th is allows@f@'s body to create and destroy objects of type @T@, and pass objects of type @T@ as arguments to other functions, following the normal \CFA rules.1168 A point of note here is that objects can be missing default constructors (and eventually other functions through deleted functions), so it is important for \CFA programmers to think carefully about the operations needed by their function, as to not over-constrain the acceptable parameter types .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.
Note: See TracChangeset
for help on using the changeset viewer.