1 | % take of review (for line numbers) and anonymous (for anonymization) on submission |
---|
2 | \documentclass[format=acmlarge, anonymous, review]{acmart} |
---|
3 | |
---|
4 | \usepackage{listings} % For code listings |
---|
5 | |
---|
6 | % Useful macros |
---|
7 | \newcommand{\CFA}{C$\mathbf\forall$} % Cforall symbolic name |
---|
8 | \newcommand{\CC}{\rm C\kern-.1em\hbox{+\kern-.25em+}} % C++ symbolic name |
---|
9 | \newcommand{\CCeleven}{\rm C\kern-.1em\hbox{+\kern-.25em+}11} % C++11 symbolic name |
---|
10 | \newcommand{\CCfourteen}{\rm C\kern-.1em\hbox{+\kern-.25em+}14} % C++14 symbolic name |
---|
11 | \newcommand{\CCseventeen}{\rm C\kern-.1em\hbox{+\kern-.25em+}17} % C++17 symbolic name |
---|
12 | |
---|
13 | \newcommand{\TODO}{\textbf{TODO}} |
---|
14 | \newcommand{\eg}{\textit{e}.\textit{g}.} |
---|
15 | \newcommand{\ie}{\textit{i}.\textit{e}.} |
---|
16 | \newcommand{\etc}{\textit{etc}.} |
---|
17 | |
---|
18 | % CFA programming language, based on ANSI C (with some gcc additions) |
---|
19 | \lstdefinelanguage{CFA}[ANSI]{C}{ |
---|
20 | morekeywords={_Alignas,_Alignof,__alignof,__alignof__,asm,__asm,__asm__,_At,_Atomic,__attribute,__attribute__,auto, |
---|
21 | _Bool,catch,catchResume,choose,_Complex,__complex,__complex__,__const,__const__,disable,dtype,enable,__extension__, |
---|
22 | fallthrough,fallthru,finally,forall,ftype,_Generic,_Imaginary,inline,__label__,lvalue,_Noreturn,one_t,otype,restrict,size_t,sized,_Static_assert, |
---|
23 | _Thread_local,throw,throwResume,trait,try,typeof,__typeof,__typeof__,zero_t}, |
---|
24 | }% |
---|
25 | |
---|
26 | \lstset{ |
---|
27 | language=CFA, |
---|
28 | columns=fullflexible, |
---|
29 | basicstyle=\linespread{0.9}\sf, % reduce line spacing and use sanserif font |
---|
30 | stringstyle=\tt, % use typewriter font |
---|
31 | tabsize=4, % 4 space tabbing |
---|
32 | xleftmargin=\parindent, % indent code to paragraph indentation |
---|
33 | % extendedchars=true, % allow ASCII characters in the range 128-255 |
---|
34 | % escapechar=§, % LaTeX escape in CFA code §...§ (section symbol), emacs: C-q M-' |
---|
35 | mathescape=true, % LaTeX math escape in CFA code $...$ |
---|
36 | keepspaces=true, % |
---|
37 | showstringspaces=false, % do not show spaces with cup |
---|
38 | showlines=true, % show blank lines at end of code |
---|
39 | aboveskip=4pt, % spacing above/below code block |
---|
40 | belowskip=3pt, |
---|
41 | % replace/adjust listing characters that look bad in sanserif |
---|
42 | literate={-}{\raisebox{-0.15ex}{\texttt{-}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1 |
---|
43 | {~}{\raisebox{0.3ex}{$\scriptstyle\sim\,$}}1 {_}{\makebox[1.2ex][c]{\rule{1ex}{0.1ex}}}1 {`}{\ttfamily\upshape\hspace*{-0.1ex}`}1 |
---|
44 | {<-}{$\leftarrow$}2 {=>}{$\Rightarrow$}2 {->}{$\rightarrow$}2, |
---|
45 | % moredelim=**[is][\color{red}]{®}{®}, % red highlighting ®...® (registered trademark symbol) emacs: C-q M-. |
---|
46 | % moredelim=**[is][\color{blue}]{ß}{ß}, % blue highlighting ß...ß (sharp s symbol) emacs: C-q M-_ |
---|
47 | % moredelim=**[is][\color{OliveGreen}]{¢}{¢}, % green highlighting ¢...¢ (cent symbol) emacs: C-q M-" |
---|
48 | % moredelim=[is][\lstset{keywords={}}]{¶}{¶}, % keyword escape ¶...¶ (pilcrow symbol) emacs: C-q M-^ |
---|
49 | }% lstset |
---|
50 | |
---|
51 | % inline code @...@ |
---|
52 | \lstMakeShortInline@ |
---|
53 | |
---|
54 | % ACM Information |
---|
55 | \citestyle{acmauthoryear} |
---|
56 | |
---|
57 | \acmJournal{PACMPL} |
---|
58 | |
---|
59 | \title{Generic and Tuple Types with Efficient Dynamic Layout in \CFA{}} |
---|
60 | |
---|
61 | \author{Aaron Moss} |
---|
62 | \affiliation{% |
---|
63 | \institution{University of Waterloo} |
---|
64 | \department{David R. Cheriton School of Computer Science} |
---|
65 | \streetaddress{Davis Centre, University of Waterloo} |
---|
66 | \city{Waterloo} |
---|
67 | \state{ON} |
---|
68 | \postcode{N2L 3G1} |
---|
69 | \country{Canada} |
---|
70 | } |
---|
71 | \email{a3moss@uwaterloo.ca} |
---|
72 | |
---|
73 | \author{Robert Schluntz} |
---|
74 | \affiliation{% |
---|
75 | \institution{University of Waterloo} |
---|
76 | \department{David R. Cheriton School of Computer Science} |
---|
77 | \streetaddress{Davis Centre, University of Waterloo} |
---|
78 | \city{Waterloo} |
---|
79 | \state{ON} |
---|
80 | \postcode{N2L 3G1} |
---|
81 | \country{Canada} |
---|
82 | } |
---|
83 | \email{rschlunt@uwaterloo.ca} |
---|
84 | |
---|
85 | \author{Peter Buhr} |
---|
86 | \affiliation{% |
---|
87 | \institution{University of Waterloo} |
---|
88 | \department{David R. Cheriton School of Computer Science} |
---|
89 | \streetaddress{Davis Centre, University of Waterloo} |
---|
90 | \city{Waterloo} |
---|
91 | \state{ON} |
---|
92 | \postcode{N2L 3G1} |
---|
93 | \country{Canada} |
---|
94 | } |
---|
95 | \email{pabuhr@uwaterloo.ca} |
---|
96 | |
---|
97 | \terms{generic, tuple, types} |
---|
98 | \keywords{generic types, tuple types, polymorphic functions, C, Cforall} |
---|
99 | |
---|
100 | \begin{CCSXML} |
---|
101 | <ccs2012> |
---|
102 | <concept> |
---|
103 | <concept_id>10011007.10011006.10011008.10011024.10011025</concept_id> |
---|
104 | <concept_desc>Software and its engineering~Polymorphism</concept_desc> |
---|
105 | <concept_significance>500</concept_significance> |
---|
106 | </concept> |
---|
107 | <concept> |
---|
108 | <concept_id>10011007.10011006.10011008.10011024.10011028</concept_id> |
---|
109 | <concept_desc>Software and its engineering~Data types and structures</concept_desc> |
---|
110 | <concept_significance>500</concept_significance> |
---|
111 | </concept> |
---|
112 | <concept> |
---|
113 | <concept_id>10011007.10011006.10011041.10011047</concept_id> |
---|
114 | <concept_desc>Software and its engineering~Source code generation</concept_desc> |
---|
115 | <concept_significance>300</concept_significance> |
---|
116 | </concept> |
---|
117 | </ccs2012> |
---|
118 | \end{CCSXML} |
---|
119 | |
---|
120 | \ccsdesc[500]{Software and its engineering~Polymorphism} |
---|
121 | \ccsdesc[500]{Software and its engineering~Data types and structures} |
---|
122 | \ccsdesc[300]{Software and its engineering~Source code generation} |
---|
123 | |
---|
124 | \begin{abstract} |
---|
125 | \TODO{} Write abstract. |
---|
126 | \end{abstract} |
---|
127 | |
---|
128 | \begin{document} |
---|
129 | |
---|
130 | \maketitle |
---|
131 | |
---|
132 | \section{Introduction \& Background} |
---|
133 | |
---|
134 | \CFA{}\footnote{Pronounced ``C-for-all'', and written \CFA{} or Cforall.} is an evolutionary extension of the C programming language which aims to add modern language features to C while maintaining both source compatibility with C and a familiar mental model for programmers. Four key design goals were set out in the original design of \CFA{} \citep{Bilson03}: |
---|
135 | \begin{enumerate} |
---|
136 | \item The behaviour of standard C code must remain the same when translated by a \CFA{} compiler as when translated by a C compiler. |
---|
137 | \item Standard C code must be as fast and as small when translated by a \CFA{} compiler as when translated by a C compiler. |
---|
138 | \item \CFA{} code must be at least as portable as standard C code. |
---|
139 | \item Extensions introduced by \CFA{} must be translated in the most efficient way possible. |
---|
140 | \end{enumerate} |
---|
141 | The purpose of these goals is to ensure that existing C codebases can be converted to \CFA{} incrementally and with minimal effort, and that programmers who already know C can productively produce \CFA{} code without extensive training beyond the extension features they wish to employ. |
---|
142 | |
---|
143 | \CFA{} has been previously extended with polymorphic functions and name overloading (including operator overloading) \citep{Bilson03}, and deterministically-executed constructors and destructors \citep{Schluntz17}. This paper describes how generic and tuple types are designed and implemented in \CFA{} in accordance with both the backward compatibility goals and existing features described above. |
---|
144 | |
---|
145 | \subsection{Polymorphic Functions} |
---|
146 | \label{sec:poly-fns} |
---|
147 | |
---|
148 | \CFA{}'s polymorphism was originally formalized by \citet{Ditchfield92}, and first implemented by \citet{Bilson03}. The signature feature of \CFA{} is parametric-polymorphic functions; such functions are written using a @forall@ clause (which gives the language its name): |
---|
149 | \begin{lstlisting} |
---|
150 | forall(otype T) |
---|
151 | T identity(T x) { |
---|
152 | return x; |
---|
153 | } |
---|
154 | |
---|
155 | int forty_two = identity(42); // T is bound to int, forty_two == 42 |
---|
156 | \end{lstlisting} |
---|
157 | The @identity@ function above can be applied to any complete object type (or ``@otype@''). The type variable @T@ is transformed into a set of additional implicit parameters to @identity@, which encode sufficient information about @T@ to create and return a variable of that type. The \CFA{} implementation passes the size and alignment of the type represented by an @otype@ parameter, as well as an assignment operator, constructor, copy constructor and destructor. If this extra information is not needed, the type parameter can be declared as @dtype T@, where @dtype@ is short for ``data type''. |
---|
158 | |
---|
159 | Here, the runtime cost of polymorphism is spread over each polymorphic call, due to passing more arguments to polymorphic functions; preliminary experiments have shown this overhead to be similar to \CC{} virtual function calls. An advantage of this design is that, unlike \CC{} template functions, \CFA{} @forall@ functions are compatible with separate compilation. |
---|
160 | |
---|
161 | Since bare polymorphic types do not provide a great range of available operations, \CFA{} provides a \emph{type assertion} mechanism to provide further information about a type: |
---|
162 | \begin{lstlisting} |
---|
163 | forall(otype T | { T twice(T); }) |
---|
164 | T four_times(T x) { |
---|
165 | return twice( twice(x) ); |
---|
166 | } |
---|
167 | |
---|
168 | double twice(double d) { return d * 2.0; } // (1) |
---|
169 | |
---|
170 | double magic = four_times(10.5); // T is bound to double, uses (1) to satisfy type assertion |
---|
171 | \end{lstlisting} |
---|
172 | These type assertions may be either variable or function declarations that depend on a polymorphic type variable. @four_times@ can only be called with an argument for which there exists a function named @twice@ that can take that argument and return another value of the same type; a pointer to the appropriate @twice@ function is passed as an additional implicit parameter to the call of @four_times@. |
---|
173 | |
---|
174 | Monomorphic specializations of polymorphic functions can themselves be used to satisfy type assertions. For instance, @twice@ could have been defined using the \CFA{} syntax for operator overloading as: |
---|
175 | \begin{lstlisting} |
---|
176 | forall(otype S | { S ?+?(S, S); }) |
---|
177 | S twice(S x) { return x + x; } // (2) |
---|
178 | \end{lstlisting} |
---|
179 | This version of @twice@ works for any type @S@ that has an addition operator defined for it, and it could have been used to satisfy the type assertion on @four_times@. |
---|
180 | The compiler accomplishes this by creating a wrapper function calling @twice // (2)@ with @S@ bound to @double@, then providing this wrapper function to @four_times@\footnote{\lstinline@twice // (2)@ could also have had a type parameter named \lstinline@T@; \CFA{} specifies renaming of the type parameters, which would avoid the name conflict with the type variable \lstinline@T@ of \lstinline@four_times@.}. |
---|
181 | |
---|
182 | \subsection{Traits} |
---|
183 | |
---|
184 | \CFA{} provides \emph{traits} as a means to name a group of type assertions, as in the example below: |
---|
185 | \begin{lstlisting} |
---|
186 | trait has_magnitude(otype T) { |
---|
187 | bool ?<?(T, T); // comparison operator for T |
---|
188 | T -?(T); // negation operator for T |
---|
189 | void ?{}(T*, zero_t); // constructor from 0 literal |
---|
190 | }; |
---|
191 | |
---|
192 | forall(otype M | has_magnitude(M)) |
---|
193 | M abs( M m ) { |
---|
194 | M zero = { 0 }; // uses zero_t constructor from trait |
---|
195 | return m < zero ? -m : m; |
---|
196 | } |
---|
197 | |
---|
198 | forall(otype M | has_magnitude(M)) |
---|
199 | M max_magnitude( M a, M b ) { |
---|
200 | return abs(a) < abs(b) ? b : a; |
---|
201 | } |
---|
202 | \end{lstlisting} |
---|
203 | This capability allows specifying the same set of assertions in multiple locations, without the repetition and likelihood of mistakes that come with manually writing them out for each function declaration. |
---|
204 | |
---|
205 | @otype@ is essentially syntactic sugar for the following trait: |
---|
206 | \begin{lstlisting} |
---|
207 | trait otype(dtype T | sized(T)) { |
---|
208 | // sized is a compiler-provided pseudo-trait for types with known size & alignment |
---|
209 | void ?{}(T*); // default constructor |
---|
210 | void ?{}(T*, T); // copy constructor |
---|
211 | void ?=?(T*, T); // assignment operator |
---|
212 | void ^?{}(T*); // destructor |
---|
213 | }; |
---|
214 | \end{lstlisting} |
---|
215 | Given this information, variables of polymorphic type can be treated as if they were a complete struct type -- they can be stack-allocated using the @alloca@ compiler builtin, default or copy-initialized, assigned, and deleted. As an example, the @abs@ function above would produce generated code something like the following (simplified for clarity and brevity): |
---|
216 | \begin{lstlisting} |
---|
217 | void abs( size_t _sizeof_M, size_t _alignof_M, |
---|
218 | void (*_ctor_M)(void*), void (*_copy_M)(void*, void*), |
---|
219 | void (*_assign_M)(void*, void*), void (*_dtor_M)(void*), |
---|
220 | bool (*_lt_M)(void*, void*), void (*_neg_M)(void*, void*), |
---|
221 | void (*_ctor_M_zero)(void*, int), |
---|
222 | void* m, void* _rtn ) { // polymorphic parameter and return passed as void* |
---|
223 | // M zero = { 0 }; |
---|
224 | void* zero = alloca(_sizeof_M); // stack allocate 0 temporary |
---|
225 | _ctor_M_zero(zero, 0); // initialize using zero_t constructor |
---|
226 | // return m < zero ? -m : m; |
---|
227 | void *_tmp = alloca(_sizeof_M) |
---|
228 | _copy_M( _rtn, // copy-initialize return value |
---|
229 | _lt_M( m, zero ) ? // check condition |
---|
230 | (_neg_M(m, _tmp), _tmp) : // negate m |
---|
231 | m); |
---|
232 | _dtor_M(_tmp); _dtor_M(zero); // destroy temporaries |
---|
233 | } |
---|
234 | \end{lstlisting} |
---|
235 | |
---|
236 | Semantically, traits are simply a named lists of type assertions, but they may be used for many of the same purposes that interfaces in Java or abstract base classes in \CC{} are used for. Unlike Java interfaces or \CC{} base classes, \CFA{} types do not explicitly state any inheritance relationship to traits they satisfy; this can be considered a form of structural inheritance, similar to implementation of an interface in Go, as opposed to the nominal inheritance model of Java and \CC{}. Nominal inheritance can be simulated with traits using marker variables or functions: |
---|
237 | \begin{lstlisting} |
---|
238 | trait nominal(otype T) { |
---|
239 | T is_nominal; |
---|
240 | }; |
---|
241 | |
---|
242 | int is_nominal; // int now satisfies the nominal trait |
---|
243 | { |
---|
244 | char is_nominal; // char satisfies the nominal trait |
---|
245 | } |
---|
246 | // char no longer satisfies the nominal trait here |
---|
247 | \end{lstlisting} |
---|
248 | |
---|
249 | Traits, however, are significantly more powerful than nominal-inheritance interfaces; firstly, due to the scoping rules of the declarations that satisfy a trait's type assertions, a type may not satisfy a trait everywhere that the type is declared, as with @char@ and the @nominal@ trait above. Secondly, traits may be used to declare a relationship among multiple types, a property that may be difficult or impossible to represent in nominal-inheritance type systems: |
---|
250 | \begin{lstlisting} |
---|
251 | trait pointer_like(otype Ptr, otype El) { |
---|
252 | lvalue El *?(Ptr); // Ptr can be dereferenced into a modifiable value of type El |
---|
253 | } |
---|
254 | |
---|
255 | struct list { |
---|
256 | int value; |
---|
257 | list *next; // may omit "struct" on type names |
---|
258 | }; |
---|
259 | |
---|
260 | typedef list *list_iterator; |
---|
261 | |
---|
262 | lvalue int *?( list_iterator it ) { |
---|
263 | return it->value; |
---|
264 | } |
---|
265 | \end{lstlisting} |
---|
266 | |
---|
267 | In the example above, @(list_iterator, int)@ satisfies @pointer_like@ by the user-defined dereference function, and @(list_iterator, list)@ also satisfies @pointer_like@ by the built-in dereference operator for pointers. Given a declaration @list_iterator it@, @*it@ can be either an @int@ or a @list@, with the meaning disambiguated by context (\eg, @int x = *it;@ interprets @*it@ as an @int@, while @(*it).value = 42;@ interprets @*it@ as a @list@). |
---|
268 | While a nominal-inheritance system with associated types could model one of those two relationships by making @El@ an associated type of @Ptr@ in the @pointer_like@ implementation, few such systems could model both relationships simultaneously. |
---|
269 | |
---|
270 | \section{Generic Types} |
---|
271 | |
---|
272 | The generic types design for \CFA{} must integrate efficiently and naturally with the existing polymorphic functions in \CFA{}, while retaining backwards compatibility with C; maintaining separate compilation is a particularly important constraint on the design. However, where the concrete parameters of the generic type are known, there should not be extra overhead for the use of a generic type. |
---|
273 | |
---|
274 | A generic type can be declared by placing a @forall@ specifier on a @struct@ or @union@ declaration, and instantiated using a parenthesized list of types after the type name: |
---|
275 | \begin{lstlisting} |
---|
276 | forall(otype R, otype S) struct pair { |
---|
277 | R first; |
---|
278 | S second; |
---|
279 | }; |
---|
280 | |
---|
281 | forall(otype T) |
---|
282 | T value( pair(const char*, T) p ) { return p.second; } |
---|
283 | |
---|
284 | forall(dtype F, otype T) |
---|
285 | T value_p( pair(F*, T*) p ) { return *p.second; } |
---|
286 | |
---|
287 | pair(const char*, int) p = { "magic", 42 }; |
---|
288 | int magic = value( p ); |
---|
289 | |
---|
290 | pair(void*, int*) q = { 0, &p.second }; |
---|
291 | magic = value_p( q ); |
---|
292 | double d = 1.0; |
---|
293 | pair(double*, double*) r = { &d, &d }; |
---|
294 | d = value_p( r ); |
---|
295 | \end{lstlisting} |
---|
296 | |
---|
297 | \CFA{} classifies generic types as either \emph{concrete} or \emph{dynamic}. Dynamic generic types vary in their in-memory layout depending on their type parameters, while concrete generic types have a fixed memory layout regardless of type parameters. A type may have polymorphic parameters but still be concrete; \CFA{} refers to such types as \emph{dtype-static}. Polymorphic pointers are an example of dtype-static types -- @forall(dtype T) T*@ is a polymorphic type, but for any @T@ chosen, @T*@ will have exactly the same in-memory representation as a @void*@, and can therefore be represented by a @void*@ in code generation. |
---|
298 | |
---|
299 | The \CFA{} compiler instantiates concrete generic types by template-expanding them to fresh struct types; concrete generic types can therefore be used with zero runtime overhead. To enable interoperation between equivalent instantiations of a generic type, the compiler saves the set of instantiations currently in scope and re-uses the generated struct declarations where appropriate. As an example, the concrete instantiation for @pair(const char*, int)@ would look something like this: |
---|
300 | \begin{lstlisting} |
---|
301 | struct _pair_conc1 { |
---|
302 | const char* first; |
---|
303 | int second; |
---|
304 | }; |
---|
305 | \end{lstlisting} |
---|
306 | |
---|
307 | A concrete generic type with dtype-static parameters is also expanded to a struct type, but this struct type is used for all matching instantiations. In the example above, the @pair(F*, T*)@ parameter to @value_p@ is such a type; its expansion would look something like this, and be used as the type of the variables @q@ and @r@ as well, with casts for member access where appropriate: |
---|
308 | \begin{lstlisting} |
---|
309 | struct _pair_conc0 { |
---|
310 | void* first; |
---|
311 | void* second; |
---|
312 | }; |
---|
313 | \end{lstlisting} |
---|
314 | |
---|
315 | Though \CFA{} implements concrete generic types efficiently, it also has a fully-general system for computing with dynamic generic types. As mentioned in Section~\ref{sec:poly-fns}, @otype@ function parameters (in fact all @sized@ polymorphic parameters) come with implicit size and alignment parameters provided by the caller. Dynamic generic structs also come with an \emph{offset array} which contains the offsets of each member of the struct\footnote{Dynamic generic unions need no such offset array, as all members are at offset 0.}. Access to members\footnote{The \lstinline@offsetof@ macro is implmented similarly.} of a dynamic generic struct is provided by adding the corresponding member of the offset array to the struct pointer at runtime, essentially moving a compile-time offset calculation to runtime where neccessary. |
---|
316 | |
---|
317 | These offset arrays are staticly generated where possible. If a dynamic generic type is declared to be passed or returned by value from a polymorphic function, the compiler can safely assume that the generic type is complete (that is, has a known layout) at any call-site, and the offset array is passed from the caller; if the generic type is concrete at the call site this offset array can even be statically generated using the C @offsetof@ macro. As an example, @p.second@ in the @value@ function above is implemented as @*(p + _offsetof_pair[1])@, where @p@ is a @void*@, and @_offsetof_pair@ is the offset array passed in to @value@ for @pair(const char*, T)@. The offset array @_offsetof_pair@ is generated at the call site as @size_t _offsetof_pair[] = { offsetof(_pair_conc1, first), offsetof(_pair_conc1, second) };@. |
---|
318 | |
---|
319 | In some cases the offset arrays cannot be staticly generated. For instance, modularity is generally produced in C by including an opaque forward-declaration of a struct and associated accessor and mutator routines in a header file, with the actual implementations in a separately-compiled \texttt{.c} file. \CFA{} supports this pattern for generic types, and in tis instance the caller does not know the actual layout or size of the dynamic generic type, and only holds it by pointer. The \CFA{} compiler automatically generates \emph{layout functions} for cases where the size, alignment, and offset array of a generic struct cannot be passed in to a function from that function's caller. These layout functions take as arguments pointers to size and alignment variables and a caller-allocated array of member offsets, as well as the size and alignment of all @sized@ parameters to the generic struct (un-@sized@ parameters are forbidden from the language from being used in a context which affects layout). Results of these layout functions are cached so that they are only computed once per type per function.%, as in the example below for @pair@. |
---|
320 | % \begin{lstlisting} |
---|
321 | % static inline void _layoutof_pair(size_t* _szeof_pair, size_t* _alignof_pair, size_t* _offsetof_pair, |
---|
322 | % size_t _szeof_R, size_t _alignof_R, size_t _szeof_S, size_t _alignof_S) { |
---|
323 | % *_szeof_pair = 0; // default values |
---|
324 | % *_alignof_pair = 1; |
---|
325 | |
---|
326 | % // add offset, size, and alignment of first field |
---|
327 | % _offsetof_pair[0] = *_szeof_pair; |
---|
328 | % *_szeof_pair += _szeof_R; |
---|
329 | % if ( *_alignof_pair < _alignof_R ) *_alignof_pair = _alignof_R; |
---|
330 | |
---|
331 | % // padding, offset, size, and alignment of second field |
---|
332 | % if ( *_szeof_pair & (_alignof_S - 1) ) |
---|
333 | % *_szeof_pair += (_alignof_S - ( *_szeof_pair & (_alignof_S - 1) ) ); |
---|
334 | % _offsetof_pair[1] = *_szeof_pair; |
---|
335 | % *_szeof_pair += _szeof_S; |
---|
336 | % if ( *_alignof_pair < _alignof_S ) *_alignof_pair = _alignof_S; |
---|
337 | |
---|
338 | % // pad to struct alignment |
---|
339 | % if ( *_szeof_pair & (*_alignof_pair - 1) ) |
---|
340 | % *_szeof_pair += ( *_alignof_pair - ( *_szeof_pair & (*_alignof_pair - 1) ) ); |
---|
341 | % } |
---|
342 | % \end{lstlisting} |
---|
343 | |
---|
344 | Layout functions also allow generic types to be used in a function definition without reflecting them in the function signature. For instance, a function that strips duplicate values from an unsorted @vector(T)@ would likely have a pointer to the vector as its only explicit parameter, but internally may use some sort of @set(T)@ to test for duplicate values. This function could acquire the layout for @set(T)@ by calling its layout function with the layout of @T@ implicitly passed into the function. |
---|
345 | |
---|
346 | Whether a type is concrete, dtype-static, or dynamic is decided based solely on the type parameters and @forall@ clause on the struct declaration. This allows opaque forward declarations of generic types like @forall(otype T) struct Box;@ -- like in C, all uses of @Box(T)@ can be in a separately compiled translation unit, and callers from other translation units will know the proper calling conventions to use. If the definition of a struct type was included in the determination of dynamic or concrete, some further types may be recognized as dtype-static (\eg, @forall(otype T) struct unique_ptr { T* p };@ does not depend on @T@ for its layout, but the existence of an @otype@ parameter means that it \emph{could}.), but preserving separate compilation (and the associated C compatibility) in this way is judged to be an appropriate trade-off. |
---|
347 | |
---|
348 | The re-use of dtype-static struct instantiations enables some useful programming patterns at zero runtime cost. The most important such pattern is using @forall(dtype T) T*@ as a type-checked replacement for @void*@, as in this example, which takes a @qsort@ or @bsearch@-compatible comparison routine and creates a similar lexicographic comparison for pairs of pointers: |
---|
349 | \begin{lstlisting} |
---|
350 | forall(dtype T) |
---|
351 | int lexcmp( pair(T*, T*)* a, pair(T*, T*)* b, int (*cmp)(T*, T*) ) { |
---|
352 | int c = cmp(a->first, b->first); |
---|
353 | if ( c == 0 ) c = cmp(a->second, b->second); |
---|
354 | return c; |
---|
355 | } |
---|
356 | \end{lstlisting} |
---|
357 | Since @pair(T*, T*)@ is a concrete type, there are no added implicit parameters to @lexcmp@, so the code generated by \CFA{} will be effectively identical to a version of this written in standard C using @void*@, yet the \CFA{} version will be type-checked to ensure that the fields of both pairs and the arguments to the comparison function match in type. |
---|
358 | |
---|
359 | Another useful pattern enabled by re-used dtype-static type instantiations is zero-cost ``tag'' structs. Sometimes a particular bit of information is only useful for type-checking, and can be omitted at runtime. Tag structs can be used to provide this information to the compiler without further runtime overhead, as in the following example: |
---|
360 | \begin{lstlisting} |
---|
361 | forall(dtype Unit) struct scalar { unsigned long value; }; |
---|
362 | |
---|
363 | struct metres {}; |
---|
364 | struct litres {}; |
---|
365 | |
---|
366 | forall(dtype U) |
---|
367 | scalar(U) ?+?(scalar(U) a, scalar(U) b) { |
---|
368 | return (scalar(U)){ a.value + b.value }; |
---|
369 | } |
---|
370 | |
---|
371 | scalar(metres) half_marathon = { 21093 }; |
---|
372 | scalar(litres) swimming_pool = { 2500000 }; |
---|
373 | |
---|
374 | scalar(metres) marathon = half_marathon + half_marathon; |
---|
375 | scalar(litres) two_pools = swimming_pool + swimming_pool; |
---|
376 | marathon + swimming_pool; // ERROR -- caught by compiler |
---|
377 | \end{lstlisting} |
---|
378 | @scalar@ is a dtype-static type, so all uses of it will use a single struct definition, containing only a single @unsigned long@, and can share the same implementations of common routines like @?+?@ -- these implementations may even be separately compiled, unlike \CC{} template functions. However, the \CFA{} type-checker will ensure that matching types are used by all calls to @?+?@, preventing nonsensical computations like adding the length of a marathon to the volume of an olympic pool. |
---|
379 | |
---|
380 | \section{Tuples} |
---|
381 | |
---|
382 | The @pair(R, S)@ generic type used as an example in the previous section can be considered a special case of a more general \emph{tuple} data structure. The authors have implemented tuples in \CFA{} as an extension to C. \TODO{} |
---|
383 | |
---|
384 | \TODO{} Check if we actually can use ttype parameters on generic types (if they set the complete flag, it should work, or nearly so). |
---|
385 | |
---|
386 | \section{Related Work} |
---|
387 | |
---|
388 | \TODO{} Talk about \CC{}, Cyclone, maybe D, Rust, \etc{} |
---|
389 | |
---|
390 | A major difference between the approaches of \CC{} and \CFA{} to polymorphism is that the set of assumed properties for a type is \emph{explicit} in \CFA{}. One of the major limiting factors of \CC{}'s approach is that templates cannot be separately compiled. In contrast, the explicit nature of assertions allows \CFA{}'s polymorphic functions to be separately compiled. |
---|
391 | |
---|
392 | \section{Conclusion \& Future Work} |
---|
393 | |
---|
394 | In conclusion, the authors' design for generic types and tuples imposes minimal runtime overhead while still supporting a full range of C features, including separately-compiled modules. There is ongoing work on a wide range of \CFA{} feature extensions, including reference types, exceptions, and concurent programming primitives. In addition to this work, there are some interesting future directions the polymorphism design could take. Notably, \CC{} template functions trade compile time and code bloat for optimal runtime of individual instantiations of polymorphic functions. \CFA{} polymorphic functions, by contrast, use an approach which is essentially dynamic virtual dispatch. The runtime overhead of this approach is low, but not as low as \CC{} template functions, and it may be beneficial to provide a mechanism for particularly performance-sensitive code to close this gap. Further research is needed, but two promising approaches are to allow an annotation on polymorphic function call sites that tells the compiler to create a template-specialization of the function (provided the code is visible in the current translation unit) or placing an annotation on polymorphic function definitions that instantiates a version of the polymorphic function specialized to some set of types. These approaches are not mutually exclusive, and would allow these performance optimizations to be applied only where most useful to increase performance, without suffering the code bloat or loss of generality of a template expansion approach where it is unneccessary. |
---|
395 | |
---|
396 | \bibliographystyle{ACM-Reference-Format} |
---|
397 | \bibliography{generic_types} |
---|
398 | |
---|
399 | \end{document} |
---|