Changes in / [b78c54f:feb999f]
- Files:
-
- 1 added
- 10 edited
-
doc/theses/mike_brooks_MMath/background.tex (modified) (9 diffs)
-
doc/theses/mike_brooks_MMath/programs/bkgd-carray-arrty.c (modified) (1 diff)
-
doc/theses/mike_brooks_MMath/programs/bkgd-carray-decay.c (modified) (2 diffs)
-
src/AST/Pass.proto.hpp (modified) (3 diffs)
-
src/Parser/StatementNode.cc (modified) (10 diffs)
-
src/Parser/module.mk (modified) (1 diff)
-
src/Parser/parserutility.cc (added)
-
src/Parser/parserutility.h (modified) (1 diff)
-
src/ResolvExpr/CandidateFinder.cpp (modified) (4 diffs)
-
src/ResolvExpr/CandidateFinder.hpp (modified) (1 diff)
-
src/ResolvExpr/Resolver.cc (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/mike_brooks_MMath/background.tex
rb78c54f rfeb999f 1 1 \chapter{Background} 2 2 3 Since this work builds on C, it is necessary to explain the C mechanisms and their shortcomings for array, linked list, and string .3 Since this work builds on C, it is necessary to explain the C mechanisms and their shortcomings for array, linked list, and string, 4 4 5 5 … … 45 45 Hence, in all cases, @sizeof@ is informing about type information. 46 46 47 So, thinking of an array as a pointer to its first element is too simplistic an analogue and it is not backed up bythe type system.48 This misguided analogue works for a single-dimension arraybut there is no advantage other than possibly teaching beginning programmers about basic runtime array-access.49 50 Continuing, a short form for declaring array variables exists using length information provided implicitly by an initializer.47 So, thinking of an array as a pointer to its first element is too simplistic an analogue and it is not backed up the type system. 48 This misguided analogue can be forced onto single-dimension arrays but there is no advantage other than possibly teaching beginning programmers about basic runtime array-access. 49 50 Continuing, a shortened form for declaring local variables exists, provided that length information is given in the initializer: 51 51 \lstinput{59-62}{bkgd-carray-arrty.c} 52 The compiler counts the number of initializer elements and uses this value as the first dimension. 53 Unfortunately, the implicit element counting does not extend to dimensions beyond the first. 54 \lstinput{64-67}{bkgd-carray-arrty.c} 55 56 My contribution is recognizing: 52 In these declarations, the resulting types are both arrays, but their lengths are inferred. 53 54 My contribution is enabled by recognizing 57 55 \begin{itemize} 58 \item There is value in using a type that knows its size.56 \item There is value in using a type that knows how big the whole thing is. 59 57 \item The type pointer to (first) element does not. 60 58 \item C \emph{has} a type that knows the whole picture: array, e.g. @T[10]@. 61 \item This type has all the usual derived forms, which also know the whole picture. 62 A usefully noteworthy example is pointer to array, e.g. @T (*)[10]@.\footnote{ 63 The parenthesis are necessary because subscript has higher priority than pointer in C declarations. 64 (Subscript also has higher priority than dereference in C expressions.)} 59 \item This type has all the usual derived forms, which also know the whole picture. A usefully noteworthy example is pointer to array, e.g. @T(*)[10]@. 65 60 \end{itemize} 66 61 67 68 \section{Reading Declarations} 69 70 A significant area of confusion for reading C declarations results from embedding a declared variable in a declaration, mimicking the way the variable is used in executable statements. 71 \begin{cquote} 72 \begin{tabular}{@{}ll@{}} 73 \multicolumn{1}{@{}c}{\textbf{Array}} & \multicolumn{1}{c@{}}{\textbf{Function Pointer}} \\ 62 Each of these sections, which introduces another layer of of the C arrays' story, 63 concludes with an \emph{Unfortunate Syntactic Reference}. 64 It shows how to spell the types under discussion, 65 along with interactions with orthogonal (but easily confused) language features. 66 Alternate spellings are listed within a row. 67 The simplest occurrences of types distinguished in the preceding discussion are marked with $\triangleright$. 68 The Type column gives the spelling used in a cast or error message (though note Section TODO points out that some types cannot be casted to). 69 The Declaration column gives the spelling used in an object declaration, such as variable or aggregate member; parameter declarations (section TODO) follow entirely different rules. 70 71 After all, reading a C array type is easy: just read it from the inside out, and know when to look left and when to look right! 72 73 74 \CFA-specific spellings (not yet introduced) are also included here for referenceability; these can be skipped on linear reading. 75 The \CFA-C column gives the, more fortunate, ``new'' syntax of section TODO, for spelling \emph{exactly the same type}. 76 This fortunate syntax does not have different spellings for types vs declarations; 77 a declaration is always the type followed by the declared identifier name; 78 for the example of letting @x@ be a \emph{pointer to array}, the declaration is spelled: 74 79 \begin{cfa} 75 int @(*@ar@)[@5@]@; // definition 76 ... @(*@ar@)[@3@]@ += 1; // usage 80 [ * [10] T ] x; 77 81 \end{cfa} 78 & 79 \begin{cfa} 80 int @(*@f@())[@5@]@ { ... }; // definition 81 ... @(*@f@())[@3@]@ += 1; // usage 82 \end{cfa} 82 The \CFA-Full column gives the spelling of a different type, introduced in TODO, which has all of my contributed improvements for safety and ergonomics. 83 84 \VRef[Figure]{bkgd:ar:usr:avp} gives this reference for the discussion so far. 85 86 \begin{figure} 87 \centering 88 \setlength{\tabcolsep}{3pt} 89 \begin{tabular}{llllll} 90 & Description & Type & Declaration & \CFA-C & \CFA-Full \\ \hline 91 $\triangleright$ & val. 92 & @T@ 93 & @T x;@ 94 & @[ T ]@ 95 & 96 \\ \hline 97 & \pbox{20cm}{ \vspace{2pt} val.\\ \footnotesize{no writing the val.\ in \lstinline{x}} }\vspace{2pt} 98 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T} \\ \lstinline{T const} } 99 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T x;} \\ \lstinline{T const x;} } 100 & @[ const T ]@ 101 & 102 \\ \hline \hline 103 $\triangleright$ & ptr.\ to val. 104 & @T *@ 105 & @T * x;@ 106 & @[ * T ]@ 107 & 108 \\ \hline 109 & \pbox{20cm}{ \vspace{2pt} ptr.\ to val.\\ \footnotesize{no writing the ptr.\ in \lstinline{x}} }\vspace{2pt} 110 & @T * const@ 111 & @T * const x;@ 112 & @[ const * T ]@ 113 & 114 \\ \hline 115 & \pbox{20cm}{ \vspace{2pt} ptr.\ to val.\\ \footnotesize{no writing the val.\ in \lstinline{*x}} }\vspace{2pt} 116 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T *} \\ \lstinline{T const *} } 117 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T * x;} \\ \lstinline{T const * x;} } 118 & @[ * const T ]@ 119 & 120 \\ \hline \hline 121 $\triangleright$ & ar.\ of val. 122 & @T[10]@ 123 & @T x[10];@ 124 & @[ [10] T ]@ 125 & @[ array(T, 10) ]@ 126 \\ \hline 127 & \pbox{20cm}{ \vspace{2pt} ar.\ of val.\\ \footnotesize{no writing the val.\ in \lstinline{x[5]}} }\vspace{2pt} 128 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T[10]} \\ \lstinline{T const[10]} } 129 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T x[10];} \\ \lstinline{T const x[10];} } 130 & @[ [10] const T ]@ 131 & @[ const array(T, 10) ]@ 132 \\ \hline 133 & ar.\ of ptr.\ to val. 134 & @T*[10]@ 135 & @T *x[10];@ 136 & @[ [10] * T ]@ 137 & @[ array(* T, 10) ]@ 138 \\ \hline 139 & \pbox{20cm}{ \vspace{2pt} ar.\ of ptr.\ to val.\\ \footnotesize{no writing the ptr.\ in \lstinline{x[5]}} }\vspace{2pt} 140 & @T * const [10]@ 141 & @T * const x[10];@ 142 & @[ [10] const * T ]@ 143 & @[ array(const * T, 10) ]@ 144 \\ \hline 145 & \pbox{20cm}{ \vspace{2pt} ar.\ of ptr.\ to val.\\ \footnotesize{no writing the val.\ in \lstinline{*(x[5])}} }\vspace{2pt} 146 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T * [10]} \\ \lstinline{T const * [10]} } 147 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T * x[10];} \\ \lstinline{T const * x[10];} } 148 & @[ [10] * const T ]@ 149 & @[ array(* const T, 10) ]@ 150 \\ \hline \hline 151 $\triangleright$ & ptr.\ to ar.\ of val. 152 & @T(*)[10]@ 153 & @T (*x)[10];@ 154 & @[ * [10] T ]@ 155 & @[ * array(T, 10) ]@ 156 \\ \hline 157 & \pbox{20cm}{ \vspace{2pt} ptr.\ to ar.\ of val.\\ \footnotesize{no writing the ptr.\ in \lstinline{x}} }\vspace{2pt} 158 & @T(* const)[10]@ 159 & @T (* const x)[10];@ 160 & @[ const * [10] T ]@ 161 & @[ const * array(T, 10) ]@ 162 \\ \hline 163 & \pbox{20cm}{ \vspace{2pt} ptr.\ to ar.\ of val.\\ \footnotesize{no writing the val.\ in \lstinline{(*x)[5]}} }\vspace{2pt} 164 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T(*)[10]} \\ \lstinline{T const (*) [10]} } 165 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T (*x)[10];} \\ \lstinline{T const (*x)[10];} } 166 & @[ * [10] const T ]@ 167 & @[ * const array(T, 10) ]@ 168 \\ \hline 169 & ptr.\ to ar.\ of ptr.\ to val. 170 & @T*(*)[10]@ 171 & @T *(*x)[10];@ 172 & @[ * [10] * T ]@ 173 & @[ * array(* T, 10) ]@ 174 \\ \hline 83 175 \end{tabular} 84 \end{cquote} 85 Essentially, the type is wrapped around the name in successive layers (like an \Index{onion}). 86 While attempting to make the two contexts consistent is a laudable goal, it has not worked out in practice, even though Dennis Richie believed otherwise: 87 \begin{quote} 88 In spite of its difficulties, I believe that the C's approach to declarations remains plausible, and am comfortable with it; it is a useful unifying principle.~\cite[p.~12]{Ritchie93} 89 \end{quote} 90 After all, reading a C array type is easy: just read it from the inside out, and know when to look left and when to look right! 91 92 \CFA provides its own type, variable and routine declarations, using a simpler syntax. 93 The new declarations place qualifiers to the left of the base type, while C declarations place qualifiers to the right of the base type. 94 The qualifiers have the same meaning in \CFA as in C. 95 Then, a \CFA declaration is read left to right, where a function return type is enclosed in brackets @[@\,@]@. 96 \begin{cquote} 97 \begin{tabular}{@{}l@{\hspace{3em}}ll@{}} 98 \multicolumn{1}{c@{\hspace{3em}}}{\textbf{C}} & \multicolumn{1}{c}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{read left to right}} \\ 99 \begin{cfa} 100 int @*@ x1 @[5]@; 101 int @(*@x2@)[5]@; 102 int @(*@f( int p )@)[5]@; 103 \end{cfa} 104 & 105 \begin{cfa} 106 @[5] *@ int x1; 107 @* [5]@ int x2; 108 @[ * [5] int ]@ f( int p ); 109 \end{cfa} 110 & 111 \begin{cfa} 112 // array of 5 pointers to int 113 // pointer to array of 5 int 114 // function returning pointer to array of 5 ints 115 \end{cfa} 116 \\ 117 & & 118 \LstCommentStyle{//\ \ \ and taking an int argument} 119 \end{tabular} 120 \end{cquote} 121 As declaration complexity increases, it becomes corresponding difficult to read and understand the C declaration form. 122 Note, writing declarations left to right is common in other programming languages, where the function return-type is often placed after the parameter declarations. 123 124 \VRef[Table]{bkgd:ar:usr:avp} introduces the many layers of the C and \CFA array story, where the \CFA story is discussion in \VRef{XXX}. 125 The \CFA-thesis column shows the new array declaration form, which is my contributed improvements for safety and ergonomics. 126 The table shows there are multiple yet equivalent forms for the array types under discussion, and subsequent discussion shows interactions with orthogonal (but easily confused) language features. 127 Each row of the table shows alternate syntactic forms. 128 The simplest occurrences of types distinguished in the preceding discussion are marked with $\triangleright$. 129 Removing the declared variable @x@, gives the type used for variable, structure field, cast or error messages \PAB{(though note Section TODO points out that some types cannot be casted to)}. 130 Unfortunately, parameter declarations \PAB{(section TODO)} have more syntactic forms and rules. 131 132 \begin{table} 133 \centering 134 \caption{Syntactic Reference for Array vs Pointer. Includes interaction with \lstinline{const}ness.} 176 \caption{Unfortunate Syntactic Reference for Array vs Pointer. Includes interaction with constness.} 135 177 \label{bkgd:ar:usr:avp} 136 \begin{tabular}{ll|l|l|l} 137 & Description & \multicolumn{1}{c|}{C} & \multicolumn{1}{c|}{\CFA} & \multicolumn{1}{c}{\CFA-thesis} \\ 138 \hline 139 $\triangleright$ & value & @T x;@ & @T x;@ & \\ 140 \hline 141 & immutable value & @const T x;@ & @const T x;@ & \\ 142 & & @T const x;@ & @T const x;@ & \\ 143 \hline \hline 144 $\triangleright$ & pointer to value & @T * x;@ & @* T x;@ & \\ 145 \hline 146 & immutable ptr. to val. & @T * const x;@ & @const * T x;@ & \\ 147 \hline 148 & ptr. to immutable val. & @const T * x;@ & @* const T x;@ & \\ 149 & & @T const * x;@ & @* T const x;@ & \\ 150 \hline \hline 151 $\triangleright$ & array of value & @T x[10];@ & @[10] T x@ & @array(T, 10) x@ \\ 152 \hline 153 & ar.\ of immutable val. & @const T x[10];@ & @[10] const T x@ & @const array(T, 10) x@ \\ 154 & & @T const x[10];@ & @[10] T const x@ & @array(T, 10) const x@ \\ 155 \hline 156 & ar.\ of ptr.\ to value & @T * x[10];@ & @[10] * T x@ & @array(T *, 10) x@ \\ 157 & & & & @array(* T, 10) x@ \\ 158 \hline 159 & ar.\ of imm. ptr.\ to val. & @T * const x[10];@ & @[10] const * T x@ & @array(* const T, 10) x@ \\ 160 & & & & @array(const * T, 10) x@ \\ 161 \hline 162 & ar.\ of ptr.\ to imm. val. & @const T * x[10];@ & @[10] * const T x@ & @array(const T *, 10) x@ \\ 163 & & @T const * x[10];@ & @[10] * T const x@ & @array(* const T, 10) x@ \\ 164 \hline \hline 165 $\triangleright$ & ptr.\ to ar.\ of value & @T (*x)[10];@ & @* [10] T x@ & @* array(T, 10) x@ \\ 166 \hline 167 & imm. ptr.\ to ar.\ of val. & @T (* const x)[10];@ & @const * [10] T x@ & @const * array(T, 10) x@ \\ 168 \hline 169 & ptr.\ to ar.\ of imm. val. & @const T (*x)[10];@ & @* [10] const T x@ & @* const array(T, 10) x@ \\ 170 & & @T const (*x)[10];@ & @* [10] T const x@ & @* array(T, 10) const x@ \\ 171 \hline 172 & ptr.\ to ar.\ of ptr.\ to val. & @T *(*x)[10];@ & @* [10] * T x@ & @* array(T *, 10) x@ \\ 173 & & & & @* array(* T, 10) x@ \\ 174 \hline 175 \end{tabular} 176 \end{table} 178 \end{figure} 179 180 181 182 177 183 178 184 TODO: Address these parked unfortunate syntaxes … … 180 186 \item static 181 187 \item star as dimension 182 \item under pointer decay: @int p1[const 3]@ being @int const *p1@188 \item under pointer decay: int p1[const 3] being int const *p1 183 189 \end{itemize} 184 190 … … 197 203 \lstinput{10-10}{bkgd-carray-decay.c} 198 204 199 So, C provides an implicit conversion from @float[10]@ to @float *@.205 So, C provides an implicit conversion from @float[10]@ to @float*@, as described in ARM-6.3.2.1.3: 200 206 \begin{quote} 201 Except when it is the operand of the @sizeof@ operator, or the unary @&@ operator, or is a string literal used to 202 initialize an array an expression that has type ``array of \emph{type}'' is converted to an expression with type 203 ``pointer to \emph{type}'' that points to the initial element of the array object~\cite[\S~6.3.2.1.3]{C11} 207 Except when it is the operand of the @sizeof@ operator, or the unary @&@ operator, or is a 208 string literal used to initialize an array 209 an expression that has type ``array of type'' is 210 converted to an expression with type ``pointer to type'' that points to the initial element of 211 the array object 204 212 \end{quote} 213 205 214 This phenomenon is the famous ``pointer decay,'' which is a decay of an array-typed expression into a pointer-typed one. 215 206 216 It is worthy to note that the list of exception cases does not feature the occurrence of @ar@ in @ar[i]@. 207 Thus, subscripting happens on pointers not arrays. 208 209 Subscripting proceeds first with pointer decay, if needed. Next, \cite[\S~6.5.2.1.2]{C11} explains that @ar[i]@ is treated as if it were @(*((a)+(i)))@. 210 \cite[\S~6.5.6.8]{C11} explains that the addition, of a pointer with an integer type, is defined only when the pointer refers to an element that is in an array, with a meaning of ``@i@ elements away from,'' which is valid if @ar@ is big enough and @i@ is small enough. 211 Finally, \cite[\S~6.5.3.2.4]{C11} explains that the @*@ operator's result is the referenced element. 212 Taken together, these rules illustrate that @ar[i]@ and @i[a]@ mean the same thing! 217 Thus, subscripting happens on pointers, not arrays. 218 219 Subscripting proceeds first with pointer decay, if needed. Next, ARM-6.5.2.1.2 explains that @ar[i]@ is treated as if it were @(*((a)+(i)))@. 220 ARM-6.5.6.8 explains that the addition, of a pointer with an integer type, is defined only when the pointer refers to an element that is in an array, with a meaning of ``@i@ elements away from,'' which is valid if @ar@ is big enough and @i@ is small enough. 221 Finally, ARM-6.5.3.2.4 explains that the @*@ operator's result is the referenced element. 222 223 Taken together, these rules also happen to illustrate that @ar[i]@ and @i[a]@ mean the same thing. 213 224 214 225 Subscripting a pointer when the target is standard-inappropriate is still practically well-defined. … … 222 233 fs[5] = 3.14; 223 234 \end{cfa} 224 The @malloc@ behaviour is specified as returning a pointer to ``space for an object whose size is'' as requested (\cite[\S~7.22.3.4.2]{C11}). 225 But \emph{nothing} more is said about this pointer value, specifically that its referent might \emph{be} an array allowing subscripting. 226 227 Under this assumption, a pointer being subscripted (or added to, then dereferenced) by any value (positive, zero, or negative), gives a view of the program's entire address space, centred around the @p@ address, divided into adjacent @sizeof(*p)@ chunks, each potentially (re)interpreted as @typeof(*p)@. 228 I call this phenomenon ``array diffraction,'' which is a diffraction of a single-element pointer into the assumption that its target is in the middle of an array whose size is unlimited in both directions. 235 The @malloc@ behaviour is specified as returning a pointer to ``space for an object whose size is'' as requested (ARM-7.22.3.4.2). 236 But program says \emph{nothing} more about this pointer value, that might cause its referent to \emph{be} an array, before doing the subscript. 237 238 Under this assumption, a pointer being subscripted (or added to, then dereferenced) 239 by any value (positive, zero, or negative), gives a view of the program's entire address space, 240 centred around the @p@ address, divided into adjacent @sizeof(*p)@ chunks, 241 each potentially (re)interpreted as @typeof(*p)@. 242 243 I call this phenomenon ``array diffraction,'' which is a diffraction of a single-element pointer 244 into the assumption that its target is in the middle of an array whose size is unlimited in both directions. 245 229 246 No pointer is exempt from array diffraction. 247 230 248 No array shows its elements without pointer decay. 231 249 232 250 A further pointer--array confusion, closely related to decay, occurs in parameter declarations. 233 \cite[\S~6.7.6.3.7]{C11}explains that when an array type is written for a parameter,234 the parameter's type becomes a type that can be summarized asthe array-decayed type.235 The respective handling of the following two parameter spellings shows that the array-spelled one is really, like the other, a pointer.251 ARM-6.7.6.3.7 explains that when an array type is written for a parameter, 252 the parameter's type becomes a type that I summarize as being the array-decayed type. 253 The respective handlings of the following two parameter spellings shows that the array-spelled one is really, like the other, a pointer. 236 254 \lstinput{12-16}{bkgd-carray-decay.c} 237 255 As the @sizeof(x)@ meaning changed, compared with when run on a similarly-spelled local variable declaration, 238 @gcc@ also gives this code the warning for the first assertion: 239 \begin{cfa} 240 warning: 'sizeof' on array function parameter 'x' will return size of 'float *' 241 \end{cfa} 242 The caller of such a function is left with the reality that a pointer parameter is a pointer, no matter how it is spelled: 256 GCC also gives this code the warning: ```sizeof' on array function parameter `x' will return size of `float *'.'' 257 258 The caller of such a function is left with the reality that a pointer parameter is a pointer, no matter how it's spelled: 243 259 \lstinput{18-21}{bkgd-carray-decay.c} 244 260 This fragment gives no warnings. … … 256 272 depending on whether the object is a local variable or a parameter. 257 273 274 258 275 In summary, when a function is written with an array-typed parameter, 259 276 \begin{itemize} … … 266 283 As a result, a function with a pointer-to-array parameter sees the parameter exactly as the caller does: 267 284 \lstinput{32-42}{bkgd-carray-decay.c} 268 \VRef[Table]{bkgd:ar:usr:decay-parm} gives the reference for the decay phenomenon seen in parameter declarations. 269 270 \begin{table} 271 \caption{Syntactic Reference for Decay during Parameter-Passing. 272 Includes interaction with \lstinline{const}ness, where ``immutable'' refers to a restriction on the callee's ability.} 273 \label{bkgd:ar:usr:decay-parm} 285 286 \VRef[Figure]{bkgd:ar:usr:decay-parm} gives the reference for the decay phenomenon seen in parameter decalarations. 287 288 \begin{figure} 274 289 \centering 275 290 \begin{tabular}{llllll} 276 & Description & Type & Parameter Declaration & \CFA \\ 277 \hline 278 & & & @T * x,@ & @* T x,@ \\ 279 $\triangleright$ & pointer to value & @T *@ & @T x[10],@ & @[10] T x,@ \\ 280 & & & @T x[],@ & @[] T x,@ \\ 281 \hline 282 & & & @T * const x,@ & @const * T x@ \\ 283 & immutable ptr.\ to val. & @T * const@ & @T x[const 10],@ & @[const 10] T x,@ \\ 284 & & & @T x[const],@ & @[const] T x,@\\ 285 \hline 286 & & & @const T * x,@ & @ * const T x,@ \\ 287 & & & @T const * x,@ & @ * T const x,@ \\ 288 & ptr.\ to immutable val. & @const T *@ & @const T x[10],@ & @[10] const T x,@ \\ 289 & & @T const *@ & @T const x[10],@ & @[10] T const x,@ \\ 290 & & & @const T x[],@ & @[] const T x,@ \\ 291 & & & @T const x[],@ & @[] T const x,@ \\ 292 \hline \hline 293 & & & @T (*x)[10],@ & @* [10] T x,@ \\ 294 $\triangleright$ & ptr.\ to ar.\ of val. & @T(*)[10]@ & @T x[3][10],@ & @[3][10] T x,@ \\ 295 & & & @T x[][10],@ & @[][10] T x,@ \\ 296 \hline 297 & & & @T ** x,@ & @** T x,@ \\ 298 & ptr.\ to ptr.\ to val. & @T **@ & @T * x[10],@ & @[10] * T x,@ \\ 299 & & & @T * x[],@ & @[] * T x,@ \\ 300 \hline 301 & ptr.\ to ptr.\ to imm.\ val. & @const char **@ & @const char * argv[],@ & @[] * const char argv,@ \\ 302 & & & \emph{others elided} & \emph{others elided} \\ 303 \hline 291 & Description & Type & Param. Decl & \CFA-C \\ \hline 292 $\triangleright$ & ptr.\ to val. 293 & @T *@ 294 & \pbox{20cm}{ \vspace{2pt} \lstinline{T * x,} \\ \lstinline{T x[10],} \\ \lstinline{T x[],} }\vspace{2pt} 295 & \pbox{20cm}{ \vspace{2pt} \lstinline{[ * T ]} \\ \lstinline{[ [10] T ]} \\ \lstinline{[ [] T ]} } 296 \\ \hline 297 & \pbox{20cm}{ \vspace{2pt} ptr.\ to val.\\ \footnotesize{no writing the ptr.\ in \lstinline{x}} }\vspace{2pt} 298 & @T * const@ 299 & \pbox{20cm}{ \vspace{2pt} \lstinline{T * const x,} \\ \lstinline{T x[const 10],} \\ \lstinline{T x[const],} }\vspace{2pt} 300 & \pbox{20cm}{ \vspace{2pt} \lstinline{[ const * T ]} \\ \lstinline{[ [const 10] T ]} \\ \lstinline{[ [const] T ]} } 301 \\ \hline 302 & \pbox{20cm}{ \vspace{2pt} ptr.\ to val.\\ \footnotesize{no writing the val.\ in \lstinline{*x}} }\vspace{2pt} 303 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T *} \\ \lstinline{T const *} } 304 & \pbox{20cm}{ \vspace{2pt} \lstinline{const T * x,} \\ \lstinline{T const * x,} \\ \lstinline{const T x[10],} \\ \lstinline{T const x[10],} \\ \lstinline{const T x[],} \\ \lstinline{T const x[],} }\vspace{2pt} 305 & \pbox{20cm}{ \vspace{2pt} \lstinline{[* const T]} \\ \lstinline{[ [10] const T ]} \\ \lstinline{[ [] const T ]} } 306 \\ \hline \hline 307 $\triangleright$ & ptr.\ to ar.\ of val. 308 & @T(*)[10]@ 309 & \pbox{20cm}{ \vspace{2pt} \lstinline{T (*x)[10],} \\ \lstinline{T x[3][10],} \\ \lstinline{T x[][10],} }\vspace{2pt} 310 & \pbox{20cm}{ \vspace{2pt} \lstinline{[* [10] T]} \\ \lstinline{[ [3] [10] T ]} \\ \lstinline{[ [] [10] T ]} } 311 \\ \hline 312 & ptr.\ to ptr.\ to val. 313 & @T **@ 314 & \pbox{20cm}{ \vspace{2pt} \lstinline{T ** x,} \\ \lstinline{T *x[10],} \\ \lstinline{T *x[],} }\vspace{2pt} 315 & \pbox{20cm}{ \vspace{2pt} \lstinline{[ * * T ]} \\ \lstinline{[ [10] * T ]} \\ \lstinline{[ [] * T ]} } 316 \\ \hline 317 & \pbox{20cm}{ \vspace{2pt} ptr.\ to ptr.\ to val.\\ \footnotesize{no writing the val.\ in \lstinline{**argv}} }\vspace{2pt} 318 & @const char **@ 319 & \pbox{20cm}{ \vspace{2pt} \lstinline{const char *argv[],} \\ \footnotesize{(others elided)} }\vspace{2pt} 320 & \pbox{20cm}{ \vspace{2pt} \lstinline{[ [] * const char ]} \\ \footnotesize{(others elided)} } 321 \\ \hline 304 322 \end{tabular} 305 \end{table} 323 \caption{Unfortunate Syntactic Reference for Decay during Parameter-Passing. Includes interaction with constness, where ``no writing'' refers to a restriction on the callee's ability.} 324 \label{bkgd:ar:usr:decay-parm} 325 \end{figure} 306 326 307 327 308 328 \subsection{Lengths may vary, checking does not} 309 329 310 When the desired number of elements is unknown at compile time, a variable-length array is a solution: 330 When the desired number of elements is unknown at compile time, 331 a variable-length array is a solution: 311 332 \begin{cfa} 312 int main( int argc, const char * argv[] ) {333 int main( int argc, const char *argv[] ) { 313 334 assert( argc == 2 ); 314 335 size_t n = atol( argv[1] ); 315 assert( 0 < n ); 336 assert( 0 < n && n < 1000 ); 337 316 338 float ar[n]; 317 339 float b[10]; 340 318 341 // ... discussion continues here 319 342 } 320 343 \end{cfa} 321 This arrangement allocates @n@ elements on the @main@ stack frame for @ar@, called a \newterm{variable length array} (VLA), as well as 10 elements in the same stack frame for @b@. 322 The variable-sized allocation of @ar@ is provided by the @alloca@ routine, which bumps the stack pointer. 323 Note, the C standard supports VLAs~\cite[\S~6.7.6.2.4]{C11} as a conditional feature, but the \CC standard does not; 324 both @gcc@ and @g++@ support VLAs. 325 As well, there is misinformation about VLAs, \eg VLAs cause stack failures or are inefficient. 326 VLAs exist as far back as Algol W~\cite[\S~5.2]{AlgolW} and are a sound and efficient data type. 327 328 For high-performance applications, the stack size can be fixed and small (coroutines or user-level threads). 329 Here, VLAs can overflow the stack, so a heap allocation is used. 344 This arrangement allocates @n@ elements on the @main@ stack frame for @ar@, just as it puts 10 elements on the @main@ stack frame for @b@. 345 The variable-sized allocation of @ar@ is provided by @alloca@. 346 347 In a situation where the array sizes are not known to be small enough for stack allocation to be sensible, corresponding heap allocations are achievable as: 330 348 \begin{cfa} 331 float * ax1 = malloc( sizeof( float[n] ) );332 float * ax2 = malloc( n * sizeof( float ) );333 float * bx1 = malloc( sizeof( float[1000000] ) );334 float * bx2 = malloc( 1000000 * sizeof( float ) );349 float *ax1 = malloc( sizeof( float[n] ) ); 350 float *ax2 = malloc( n * sizeof( float ) ); 351 float *bx1 = malloc( sizeof( float[1000000] ) ); 352 float *bx2 = malloc( 1000000 * sizeof( float ) ); 335 353 \end{cfa} 336 354 355 356 VLA 357 337 358 Parameter dependency 338 359 … … 342 363 343 364 344 \subsection{Dynamically sized, multidimensional arrays} 365 366 \subsection{C has full-service, dynamically sized, multidimensional arrays (and \CC does not)} 345 367 346 368 In C and \CC, ``multidimensional array'' means ``array of arrays.'' Other meanings are discussed in TODO. … … 394 416 \section{Linked List} 395 417 396 Linked-lists are blocks of storage connected using one or more pointers.397 The storage block is logically divided into data and links (pointers), where the links are the only component used by the list structure.398 Since the data is opaque, list structures are often polymorphic over the data, which is normally homogeneous.399 400 418 401 419 \section{String} 402 403 A string is a logical sequence of symbols, where the form of the symbols can vary significantly: 7/8-bit characters (ASCII/Latin-1), or 2/4/8-byte (UNICODE) characters/symbols or variable length (UTF-8/16/32) characters.404 A string can be read left-to-right, right-to-left, top-to-bottom, and have stacked elements (Arabic).405 406 An integer character constant is a sequence of one or more multibyte characters enclosed in single-quotes, as in @'x'@.407 A wide character constant is the same, except prefixed by the letter @L@, @u@, or @U@.408 With a few exceptions detailed later, the elements of the sequence are any members of the source character set;409 they are mapped in an implementation-defined manner to members of the execution character set.410 411 A C character-string literal is a sequence of zero or more multibyte characters enclosed in double-quotes, as in @"xyz"@.412 A UTF-8 string literal is the same, except prefixed by @u8@.413 A wide string literal is the same, except prefixed by the letter @L@, @u@, or @U@.414 415 For UTF-8 string literals, the array elements have type @char@, and are initialized with the characters of the multibyte character sequence, as encoded in UTF-8.416 For wide string literals prefixed by the letter @L@, the array elements have type @wchar_t@ and are initialized with the sequence of wide characters corresponding to the multibyte character sequence, as defined by the @mbstowcs@ function with an implementation-defined current locale.417 For wide string literals prefixed by the letter @u@ or @U@, the array elements have type @char16_t@ or @char32_t@, respectively, and are initialized with the sequence of wide characters corresponding to the multibyte character sequence, as defined by successive calls to the @mbrtoc16@, or @mbrtoc32@ function as appropriate for its type, with an implementation-defined current locale.418 The value of a string literal containing a multibyte character or escape sequence not represented in the executioncharacter set is implementation-defined.419 420 421 Another bad C design decision is to have null-terminated strings rather than maintaining a separate string length.422 \begin{quote}423 Technically, a string is an array whose elements are single characters.424 The compiler automatically places the null character @\0@ at the end of each such string, so programs can conveniently find the end.425 This representation means that there is no real limit to how long a string can be, but programs have to scan one completely to determine its length.426 \end{quote} -
doc/theses/mike_brooks_MMath/programs/bkgd-carray-arrty.c
rb78c54f rfeb999f 57 57 f( &ar ); 58 58 59 float fs[] = {3.14, 1.7 7};59 float fs[] = {3.14, 1.707}; 60 60 char cs[] = "hello"; 61 61 static_assert( sizeof(fs) == 2 * sizeof(float) ); 62 62 static_assert( sizeof(cs) == 6 * sizeof(char) ); $\C{// 5 letters + 1 null terminator}$ 63 63 64 float fm[][2] = { {3.14, 1.77}, {12.4, 0.01}, {7.8, 1.23} }; $\C{// brackets define structuring}$65 char cm[][sizeof("hello")] = { "hello", "hello", "hello" };66 static_assert( sizeof(fm) == 3 * 2 * sizeof(float) );67 static_assert( sizeof(cm) == 3 * 6 * sizeof(char) );68 64 } 69 65 -
doc/theses/mike_brooks_MMath/programs/bkgd-carray-decay.c
rb78c54f rfeb999f 4 4 float (*pa)[10] = &a; $\C{// pointer to array}$ 5 5 float a0 = a[0]; $\C{// element}$ 6 float * pa0 = &(a[0]); $\C{// pointer to element}$6 float *pa0 = &(a[0]); $\C{// pointer to element}$ 7 7 8 float * pa0x = a; $\C{// (ok)}$8 float *pa0x = a; $\C{// (ok)}$ 9 9 assert( pa0 == pa0x ); 10 10 assert( sizeof(pa0x) != sizeof(a) ); 11 11 12 void f( float x[10], float * y ) {13 static_assert( sizeof(x) == sizeof(void *) );14 static_assert( sizeof(y) == sizeof(void *) );12 void f( float x[10], float *y ) { 13 static_assert( sizeof(x) == sizeof(void*) ); 14 static_assert( sizeof(y) == sizeof(void*) ); 15 15 } 16 16 f(0,0); … … 22 22 23 23 char ca[] = "hello"; $\C{// array on stack, initialized from read-only data}$ 24 char * cp = "hello";$\C{// pointer to read-only data [decay here]}$25 void edit( char c[] ) { $\C{// param eteris pointer}$24 char *cp = "hello"; $\C{// pointer to read-only data [decay here]}$ 25 void edit( char c[] ) { $\C{// param is pointer}$ 26 26 c[3] = 'p'; 27 27 } 28 28 edit( ca ); $\C{// ok [decay here]}$ 29 edit( c p );$\C{// Segmentation fault}$29 edit( c p ); $\C{// Segmentation fault}$ 30 30 edit( "hello" ); $\C{// Segmentation fault [decay here]}$ 31 31 32 32 void decay( float x[10] ) { 33 static_assert( sizeof(x) == sizeof(void *) );33 static_assert( sizeof(x) == sizeof(void*) ); 34 34 } 35 35 static_assert( sizeof(a) == 10 * sizeof(float) ); -
src/AST/Pass.proto.hpp
rb78c54f rfeb999f 226 226 // Now the original containers should still have the unchanged values 227 227 // but also contain the new values. 228 } 229 }; 230 231 /// Used by previsit implementation 232 /// We need to reassign the result to 'node', unless the function 233 /// returns void, then we just leave 'node' unchanged 234 template<bool is_void> 235 struct __assign; 236 237 template<> 238 struct __assign<true> { 239 template<typename core_t, typename node_t> 240 static inline void result( core_t & core, const node_t * & node ) { 241 core.previsit( node ); 242 } 243 }; 244 245 template<> 246 struct __assign<false> { 247 template<typename core_t, typename node_t> 248 static inline void result( core_t & core, const node_t * & node ) { 249 node = core.previsit( node ); 250 assertf(node, "Previsit must not return NULL"); 251 } 252 }; 253 254 /// Used by postvisit implementation 255 /// We need to return the result unless the function 256 /// returns void, then we just return the original node 257 template<bool is_void> 258 struct __return; 259 260 template<> 261 struct __return<true> { 262 template<typename core_t, typename node_t> 263 static inline const node_t * result( core_t & core, const node_t * & node ) { 264 core.postvisit( node ); 265 return node; 266 } 267 }; 268 269 template<> 270 struct __return<false> { 271 template<typename core_t, typename node_t> 272 static inline auto result( core_t & core, const node_t * & node ) { 273 return core.postvisit( node ); 228 274 } 229 275 }; … … 251 297 ); 252 298 253 // We need to reassign the result to 'node', unless the function 254 // returns void, then we just leave 'node' unchanged 255 if constexpr ( std::is_void_v<decltype( core.previsit( node ) )> ) { 256 core.previsit( node ); 257 } else { 258 node = core.previsit( node ); 259 assertf(node, "Previsit must not return NULL"); 260 } 299 __assign< 300 std::is_void< 301 decltype( core.previsit( node ) ) 302 >::value 303 >::result( core, node ); 261 304 } 262 305 … … 269 312 decltype( core.postvisit( node ), node->accept( *(Visitor*)nullptr ) ) 270 313 { 271 // We need to return the result unless the function 272 // returns void, then we just return the original node 273 if constexpr ( std::is_void_v<decltype( core.postvisit( node ) )> ) { 274 core.postvisit( node ); 275 return node; 276 } else { 277 return core.postvisit( node ); 278 } 314 return __return< 315 std::is_void< 316 decltype( core.postvisit( node ) ) 317 >::value 318 >::result( core, node ); 279 319 } 280 320 -
src/Parser/StatementNode.cc
rb78c54f rfeb999f 122 122 ast::Expr * cond = nullptr; 123 123 if ( ctl->condition ) { 124 cond = maybeMoveBuild( ctl->condition ); 124 // compare the provided condition against 0 125 cond = notZeroExpr( maybeMoveBuild( ctl->condition ) ); 125 126 } else { 126 127 for ( ast::ptr<ast::Stmt> & stmt : inits ) { … … 128 129 auto declStmt = stmt.strict_as<ast::DeclStmt>(); 129 130 auto dwt = declStmt->decl.strict_as<ast::DeclWithType>(); 130 ast::Expr * nze = n ew ast::VariableExpr( dwt->location, dwt);131 ast::Expr * nze = notZeroExpr( new ast::VariableExpr( dwt->location, dwt ) ); 131 132 cond = cond ? new ast::LogicalExpr( dwt->location, cond, nze, ast::AndExpr ) : nze; 132 133 } … … 199 200 // do-while cannot have declarations in the contitional, so init is always empty 200 201 return new ast::WhileDoStmt( location, 201 maybeMoveBuild( ctl),202 notZeroExpr( maybeMoveBuild( ctl ) ), 202 203 buildMoveSingle( stmt ), 203 204 buildMoveOptional( else_ ), … … 212 213 213 214 ast::Expr * astcond = nullptr; // maybe empty 214 astcond = maybeMoveBuild( forctl->condition);215 astcond = notZeroExpr( maybeMoveBuild( forctl->condition ) ); 215 216 216 217 ast::Expr * astincr = nullptr; // maybe empty … … 329 330 clause->target = maybeBuild( targetExpr ); 330 331 clause->stmt = maybeMoveBuild( stmt ); 331 clause->when_cond = maybeMoveBuild( when);332 clause->when_cond = notZeroExpr( maybeMoveBuild( when ) ); 332 333 333 334 ExpressionNode * next = targetExpr->next; … … 344 345 ast::WaitForStmt * build_waitfor_else( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt ) { 345 346 existing->else_stmt = maybeMoveBuild( stmt ); 346 existing->else_cond = maybeMoveBuild( when);347 existing->else_cond = notZeroExpr( maybeMoveBuild( when ) ); 347 348 348 349 (void)location; … … 353 354 existing->timeout_time = maybeMoveBuild( timeout ); 354 355 existing->timeout_stmt = maybeMoveBuild( stmt ); 355 existing->timeout_cond = maybeMoveBuild( when);356 existing->timeout_cond = notZeroExpr( maybeMoveBuild( when ) ); 356 357 357 358 (void)location; … … 361 362 ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) { 362 363 ast::WhenClause * clause = new ast::WhenClause( loc ); 363 clause->when_cond = maybeMoveBuild( when);364 clause->when_cond = notZeroExpr( maybeMoveBuild( when ) ); 364 365 clause->stmt = maybeMoveBuild( stmt ); 365 366 clause->target = maybeMoveBuild( targetExpr ); … … 368 369 ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) { 369 370 ast::WhenClause * clause = new ast::WhenClause( loc ); 370 clause->when_cond = maybeMoveBuild( when);371 clause->when_cond = notZeroExpr( maybeMoveBuild( when ) ); 371 372 clause->stmt = maybeMoveBuild( stmt ); 372 373 return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause ); … … 507 508 508 509 ast::Expr * astcond = nullptr; // maybe empty 509 astcond = maybeMoveBuild( forctl->condition);510 astcond = notZeroExpr( maybeMoveBuild( forctl->condition ) ); 510 511 511 512 ast::Expr * astincr = nullptr; // maybe empty -
src/Parser/module.mk
rb78c54f rfeb999f 31 31 Parser/parser.yy \ 32 32 Parser/ParserTypes.h \ 33 Parser/parserutility.cc \ 33 34 Parser/parserutility.h \ 34 35 Parser/RunParser.cpp \ -
src/Parser/parserutility.h
rb78c54f rfeb999f 17 17 18 18 #include "AST/Copy.hpp" // for shallowCopy 19 namespace ast { 20 class Expr; 21 } 22 23 ast::Expr * notZeroExpr( const ast::Expr *orig ); 19 24 20 25 template< typename T > -
src/ResolvExpr/CandidateFinder.cpp
rb78c54f rfeb999f 46 46 #include "AST/Type.hpp" 47 47 #include "Common/utility.h" // for move, copy 48 #include "Parser/parserutility.h" // for notZeroExpr 48 49 #include "SymTab/Mangler.h" 49 50 #include "Tuples/Tuples.h" // for handleTupleAssignment … … 1586 1587 void Finder::postvisit( const ast::LogicalExpr * logicalExpr ) { 1587 1588 CandidateFinder finder1( context, tenv ); 1588 ast::ptr<ast::Expr> arg1 = createCondExpr( logicalExpr->arg1 );1589 ast::ptr<ast::Expr> arg1 = notZeroExpr( logicalExpr->arg1 ); 1589 1590 finder1.find( arg1, ResolveMode::withAdjustment() ); 1590 1591 if ( finder1.candidates.empty() ) return; 1591 1592 1592 1593 CandidateFinder finder2( context, tenv ); 1593 ast::ptr<ast::Expr> arg2 = createCondExpr( logicalExpr->arg2 );1594 ast::ptr<ast::Expr> arg2 = notZeroExpr( logicalExpr->arg2 ); 1594 1595 finder2.find( arg2, ResolveMode::withAdjustment() ); 1595 1596 if ( finder2.candidates.empty() ) return; … … 1617 1618 void Finder::postvisit( const ast::ConditionalExpr * conditionalExpr ) { 1618 1619 // candidates for condition 1619 ast::ptr<ast::Expr> arg1 = createCondExpr( conditionalExpr->arg1 );1620 ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 ); 1620 1621 CandidateFinder finder1( context, tenv ); 1621 1622 finder1.find( arg1, ResolveMode::withAdjustment() ); … … 2200 2201 } 2201 2202 2202 const ast::Expr * createCondExpr( const ast::Expr * expr ) {2203 assert( expr );2204 return new ast::CastExpr( expr->location,2205 ast::UntypedExpr::createCall( expr->location,2206 "?!=?",2207 {2208 expr,2209 new ast::ConstantExpr( expr->location,2210 new ast::ZeroType(), "0", std::make_optional( 0ull )2211 ),2212 }2213 ),2214 new ast::BasicType( ast::BasicType::SignedInt )2215 );2216 }2217 2218 2203 } // namespace ResolvExpr 2219 2204 -
src/ResolvExpr/CandidateFinder.hpp
rb78c54f rfeb999f 70 70 const ast::Expr * expr, Cost & cost ); 71 71 72 /// Wrap an expression to convert the result to a conditional result.73 const ast::Expr * createCondExpr( const ast::Expr * expr );74 75 72 } // namespace ResolvExpr 76 73 -
src/ResolvExpr/Resolver.cc
rb78c54f rfeb999f 340 340 } 341 341 342 ast::ptr< ast::Expr > findCondExpression(343 const ast::Expr * untyped, const ResolveContext & context344 ) {345 if ( nullptr == untyped ) return untyped;346 ast::ptr<ast::Expr> condExpr = createCondExpr( untyped );347 return findIntegralExpression( condExpr, context );348 }349 350 342 /// check if a type is a character type 351 343 bool isCharType( const ast::Type * t ) { … … 364 356 return it != end; 365 357 } 366 } // anonymous namespace358 } 367 359 368 360 class Resolver final … … 737 729 const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) { 738 730 return ast::mutate_field( 739 ifStmt, &ast::IfStmt::cond, find CondExpression( ifStmt->cond, context ) );731 ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, context ) ); 740 732 } 741 733 742 734 const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) { 743 735 return ast::mutate_field( 744 whileDoStmt, &ast::WhileDoStmt::cond, find CondExpression( whileDoStmt->cond, context ) );736 whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, context ) ); 745 737 } 746 738 … … 748 740 if ( forStmt->cond ) { 749 741 forStmt = ast::mutate_field( 750 forStmt, &ast::ForStmt::cond, find CondExpression( forStmt->cond, context ) );742 forStmt, &ast::ForStmt::cond, findIntegralExpression( forStmt->cond, context ) ); 751 743 } 752 744 … … 1083 1075 1084 1076 // Resolve the conditions as if it were an IfStmt, statements normally 1085 clause2->when_cond = find CondExpression( clause.when_cond, context );1077 clause2->when_cond = findSingleExpression( clause.when_cond, context ); 1086 1078 clause2->stmt = clause.stmt->accept( *visitor ); 1087 1079 … … 1097 1089 new ast::BasicType{ ast::BasicType::LongLongUnsignedInt }; 1098 1090 auto timeout_time = findSingleExpression( stmt->timeout_time, target, context ); 1099 auto timeout_cond = find CondExpression( stmt->timeout_cond, context );1091 auto timeout_cond = findSingleExpression( stmt->timeout_cond, context ); 1100 1092 auto timeout_stmt = stmt->timeout_stmt->accept( *visitor ); 1101 1093 … … 1110 1102 if ( stmt->else_stmt ) { 1111 1103 // resolve the condition like IfStmt, stmts normally 1112 auto else_cond = find CondExpression( stmt->else_cond, context );1104 auto else_cond = findSingleExpression( stmt->else_cond, context ); 1113 1105 auto else_stmt = stmt->else_stmt->accept( *visitor ); 1114 1106
Note:
See TracChangeset
for help on using the changeset viewer.