Changes in / [b78c54f:feb999f]


Ignore:
Files:
1 added
10 edited

Legend:

Unmodified
Added
Removed
  • doc/theses/mike_brooks_MMath/background.tex

    rb78c54f rfeb999f  
    11\chapter{Background}
    22
    3 Since this work builds on C, it is necessary to explain the C mechanisms and their shortcomings for array, linked list, and string.
     3Since this work builds on C, it is necessary to explain the C mechanisms and their shortcomings for array, linked list, and string,
    44
    55
     
    4545Hence, in all cases, @sizeof@ is informing about type information.
    4646
    47 So, thinking of an array as a pointer to its first element is too simplistic an analogue and it is not backed up by the type system.
    48 This misguided analogue works for a single-dimension array but 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.
     47So, 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.
     48This 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
     50Continuing, a shortened form for declaring local variables exists, provided that length information is given in the initializer:
    5151\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:
     52In these declarations, the resulting types are both arrays, but their lengths are inferred.
     53
     54My contribution is enabled by recognizing
    5755\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.
    5957        \item The type pointer to (first) element does not.
    6058        \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]@.
    6560\end{itemize}
    6661
    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}} \\
     62Each of these sections, which introduces another layer of of the C arrays' story,
     63concludes with an \emph{Unfortunate Syntactic Reference}.
     64It shows how to spell the types under discussion,
     65along with interactions with orthogonal (but easily confused) language features.
     66Alternate spellings are listed within a row.
     67The simplest occurrences of types distinguished in the preceding discussion are marked with $\triangleright$.
     68The 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).
     69The 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
     71After 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.
     75The \CFA-C column gives the, more fortunate, ``new'' syntax of section TODO, for spelling \emph{exactly the same type}.
     76This fortunate syntax does not have different spellings for types vs declarations;
     77a declaration is always the type followed by the declared identifier name;
     78for the example of letting @x@ be a \emph{pointer to array}, the declaration is spelled:
    7479\begin{cfa}
    75 int @(*@ar@)[@5@]@; // definition
    76   ... @(*@ar@)[@3@]@ += 1; // usage
     80[ * [10] T ] x;
    7781\end{cfa}
    78 &
    79 \begin{cfa}
    80 int @(*@f@())[@5@]@ { ... }; // definition
    81   ... @(*@f@())[@3@]@ += 1; // usage
    82 \end{cfa}
     82The \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
    83175\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.}
    135177\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
    177183
    178184TODO: Address these parked unfortunate syntaxes
     
    180186        \item static
    181187        \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
    183189\end{itemize}
    184190
     
    197203\lstinput{10-10}{bkgd-carray-decay.c}
    198204
    199 So, C provides an implicit conversion from @float[10]@ to @float *@.
     205So, C provides an implicit conversion from @float[10]@ to @float*@, as described in ARM-6.3.2.1.3:
    200206\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}
     207Except when it is the operand of the @sizeof@ operator, or the unary @&@ operator, or is a
     208string literal used to initialize an array
     209an expression that has type ``array of type'' is
     210converted to an expression with type ``pointer to type'' that points to the initial element of
     211the array object
    204212\end{quote}
     213
    205214This phenomenon is the famous ``pointer decay,'' which is a decay of an array-typed expression into a pointer-typed one.
     215
    206216It 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!
     217Thus, subscripting happens on pointers, not arrays.
     218
     219Subscripting 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)))@.
     220ARM-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.
     221Finally, ARM-6.5.3.2.4 explains that the @*@ operator's result is the referenced element.
     222
     223Taken together, these rules also happen to illustrate that @ar[i]@ and @i[a]@ mean the same thing.
    213224
    214225Subscripting a pointer when the target is standard-inappropriate is still practically well-defined.
     
    222233fs[5] = 3.14;
    223234\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.
     235The @malloc@ behaviour is specified as returning a pointer to ``space for an object whose size is'' as requested (ARM-7.22.3.4.2).
     236But program says \emph{nothing} more about this pointer value, that might cause its referent to \emph{be} an array, before doing the subscript.
     237
     238Under this assumption, a pointer being subscripted (or added to, then dereferenced)
     239by any value (positive, zero, or negative), gives a view of the program's entire address space,
     240centred around the @p@ address, divided into adjacent @sizeof(*p)@ chunks,
     241each potentially (re)interpreted as @typeof(*p)@.
     242
     243I call this phenomenon ``array diffraction,''  which is a diffraction of a single-element pointer
     244into the assumption that its target is in the middle of an array whose size is unlimited in both directions.
     245
    229246No pointer is exempt from array diffraction.
     247
    230248No array shows its elements without pointer decay.
    231249
    232250A 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 as the 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.
     251ARM-6.7.6.3.7 explains that when an array type is written for a parameter,
     252the parameter's type becomes a type that I summarize as being the array-decayed type.
     253The respective handlings of the following two parameter spellings shows that the array-spelled one is really, like the other, a pointer.
    236254\lstinput{12-16}{bkgd-carray-decay.c}
    237255As 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:
     256GCC also gives this code the warning: ```sizeof' on array function parameter `x' will return size of `float *'.''
     257
     258The caller of such a function is left with the reality that a pointer parameter is a pointer, no matter how it's spelled:
    243259\lstinput{18-21}{bkgd-carray-decay.c}
    244260This fragment gives no warnings.
     
    256272depending on whether the object is a local variable or a parameter.
    257273
     274
    258275In summary, when a function is written with an array-typed parameter,
    259276\begin{itemize}
     
    266283As a result, a function with a pointer-to-array parameter sees the parameter exactly as the caller does:
    267284\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}
    274289\centering
    275290\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
    304322\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}
    306326
    307327
    308328\subsection{Lengths may vary, checking does not}
    309329
    310 When the desired number of elements is unknown at compile time, a variable-length array is a solution:
     330When the desired number of elements is unknown at compile time,
     331a variable-length array is a solution:
    311332\begin{cfa}
    312 int main( int argc, const char * argv[] ) {
     333int main( int argc, const char *argv[] ) {
    313334        assert( argc == 2 );
    314335        size_t n = atol( argv[1] );
    315         assert( 0 < n );
     336        assert( 0 < n && n < 1000 );
     337
    316338        float ar[n];
    317339        float b[10];
     340
    318341        // ... discussion continues here
    319342}
    320343\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.
     344This arrangement allocates @n@ elements on the @main@ stack frame for @ar@, just as it puts 10 elements on the @main@ stack frame for @b@.
     345The variable-sized allocation of @ar@ is provided by @alloca@.
     346
     347In 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:
    330348\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 ) );
     349float *ax1 = malloc( sizeof( float[n] ) );
     350float *ax2 = malloc( n * sizeof( float ) );
     351float *bx1 = malloc( sizeof( float[1000000] ) );
     352float *bx2 = malloc( 1000000 * sizeof( float ) );
    335353\end{cfa}
    336354
     355
     356VLA
     357
    337358Parameter dependency
    338359
     
    342363
    343364
    344 \subsection{Dynamically sized, multidimensional arrays}
     365
     366\subsection{C has full-service, dynamically sized, multidimensional arrays (and \CC does not)}
    345367
    346368In C and \CC, ``multidimensional array'' means ``array of arrays.''  Other meanings are discussed in TODO.
     
    394416\section{Linked List}
    395417
    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 
    400418
    401419\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  
    5757        f( &ar );
    5858
    59         float fs[] = {3.14, 1.77};
     59        float fs[] = {3.14, 1.707};
    6060        char cs[] = "hello";
    6161        static_assert( sizeof(fs) == 2 * sizeof(float) );
    6262        static_assert( sizeof(cs) == 6 * sizeof(char) );  $\C{// 5 letters + 1 null terminator}$
    6363
    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) );
    6864}
    6965
  • doc/theses/mike_brooks_MMath/programs/bkgd-carray-decay.c

    rb78c54f rfeb999f  
    44        float (*pa)[10] = &a;           $\C{// pointer to array}$
    55        float a0 = a[0];                        $\C{// element}$
    6         float * pa0 = &(a[0]);          $\C{// pointer to element}$
     6        float *pa0 = &(a[0]);           $\C{// pointer to element}$
    77
    8         float * pa0x = a;                       $\C{// (ok)}$
     8        float *pa0x = a;                        $\C{// (ok)}$
    99        assert( pa0 == pa0x );
    1010        assert( sizeof(pa0x) != sizeof(a) );
    1111
    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*) );
    1515        }
    1616        f(0,0);
     
    2222
    2323        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{// parameter is pointer}$
     24        char *cp = "hello";                     $\C{// pointer to read-only data [decay here]}$
     25        void edit( char c[] ) {         $\C{// param is pointer}$
    2626                c[3] = 'p';
    2727        }
    2828        edit( ca );                                     $\C{// ok [decay here]}$
    29         edit( cp );                                     $\C{// Segmentation fault}$
     29        edit( c p );                            $\C{// Segmentation fault}$
    3030        edit( "hello" );                        $\C{// Segmentation fault [decay here]}$
    3131
    3232        void decay( float x[10] ) {
    33                 static_assert( sizeof(x) == sizeof(void *) );
     33                static_assert( sizeof(x) == sizeof(void*) );
    3434        }
    3535        static_assert( sizeof(a) == 10 * sizeof(float) );
  • src/AST/Pass.proto.hpp

    rb78c54f rfeb999f  
    226226                // Now the original containers should still have the unchanged values
    227227                // 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
     234template<bool is_void>
     235struct __assign;
     236
     237template<>
     238struct __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
     245template<>
     246struct __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
     257template<bool is_void>
     258struct __return;
     259
     260template<>
     261struct __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
     269template<>
     270struct __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 );
    228274        }
    229275};
     
    251297        );
    252298
    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 );
    261304}
    262305
     
    269312        decltype( core.postvisit( node ), node->accept( *(Visitor*)nullptr ) )
    270313{
    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 );
    279319}
    280320
  • src/Parser/StatementNode.cc

    rb78c54f rfeb999f  
    122122        ast::Expr * cond = nullptr;
    123123        if ( ctl->condition ) {
    124                 cond = maybeMoveBuild( ctl->condition );
     124                // compare the provided condition against 0
     125                cond = notZeroExpr( maybeMoveBuild( ctl->condition ) );
    125126        } else {
    126127                for ( ast::ptr<ast::Stmt> & stmt : inits ) {
     
    128129                        auto declStmt = stmt.strict_as<ast::DeclStmt>();
    129130                        auto dwt = declStmt->decl.strict_as<ast::DeclWithType>();
    130                         ast::Expr * nze = new ast::VariableExpr( dwt->location, dwt );
     131                        ast::Expr * nze = notZeroExpr( new ast::VariableExpr( dwt->location, dwt ) );
    131132                        cond = cond ? new ast::LogicalExpr( dwt->location, cond, nze, ast::AndExpr ) : nze;
    132133                }
     
    199200        // do-while cannot have declarations in the contitional, so init is always empty
    200201        return new ast::WhileDoStmt( location,
    201                 maybeMoveBuild( ctl ),
     202                notZeroExpr( maybeMoveBuild( ctl ) ),
    202203                buildMoveSingle( stmt ),
    203204                buildMoveOptional( else_ ),
     
    212213
    213214        ast::Expr * astcond = nullptr;                                          // maybe empty
    214         astcond = maybeMoveBuild( forctl->condition );
     215        astcond = notZeroExpr( maybeMoveBuild( forctl->condition ) );
    215216
    216217        ast::Expr * astincr = nullptr;                                          // maybe empty
     
    329330        clause->target = maybeBuild( targetExpr );
    330331        clause->stmt = maybeMoveBuild( stmt );
    331         clause->when_cond = maybeMoveBuild( when );
     332        clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
    332333
    333334        ExpressionNode * next = targetExpr->next;
     
    344345ast::WaitForStmt * build_waitfor_else( const CodeLocation & location, ast::WaitForStmt * existing, ExpressionNode * when, StatementNode * stmt ) {
    345346        existing->else_stmt = maybeMoveBuild( stmt );
    346         existing->else_cond = maybeMoveBuild( when );
     347        existing->else_cond = notZeroExpr( maybeMoveBuild( when ) );
    347348
    348349        (void)location;
     
    353354        existing->timeout_time = maybeMoveBuild( timeout );
    354355        existing->timeout_stmt = maybeMoveBuild( stmt );
    355         existing->timeout_cond = maybeMoveBuild( when );
     356        existing->timeout_cond = notZeroExpr( maybeMoveBuild( when ) );
    356357
    357358        (void)location;
     
    361362ast::WaitUntilStmt::ClauseNode * build_waituntil_clause( const CodeLocation & loc, ExpressionNode * when, ExpressionNode * targetExpr, StatementNode * stmt ) {
    362363        ast::WhenClause * clause = new ast::WhenClause( loc );
    363         clause->when_cond = maybeMoveBuild( when );
     364        clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
    364365        clause->stmt = maybeMoveBuild( stmt );
    365366        clause->target = maybeMoveBuild( targetExpr );
     
    368369ast::WaitUntilStmt::ClauseNode * build_waituntil_else( const CodeLocation & loc, ExpressionNode * when, StatementNode * stmt ) {
    369370        ast::WhenClause * clause = new ast::WhenClause( loc );
    370         clause->when_cond = maybeMoveBuild( when );
     371        clause->when_cond = notZeroExpr( maybeMoveBuild( when ) );
    371372        clause->stmt = maybeMoveBuild( stmt );
    372373        return new ast::WaitUntilStmt::ClauseNode( ast::WaitUntilStmt::ClauseNode::Op::ELSE, clause );
     
    507508
    508509        ast::Expr * astcond = nullptr;                                          // maybe empty
    509         astcond = maybeMoveBuild( forctl->condition );
     510        astcond = notZeroExpr( maybeMoveBuild( forctl->condition ) );
    510511
    511512        ast::Expr * astincr = nullptr;                                          // maybe empty
  • src/Parser/module.mk

    rb78c54f rfeb999f  
    3131       Parser/parser.yy \
    3232       Parser/ParserTypes.h \
     33       Parser/parserutility.cc \
    3334       Parser/parserutility.h \
    3435       Parser/RunParser.cpp \
  • src/Parser/parserutility.h

    rb78c54f rfeb999f  
    1717
    1818#include "AST/Copy.hpp"            // for shallowCopy
     19namespace ast {
     20        class Expr;
     21}
     22
     23ast::Expr * notZeroExpr( const ast::Expr *orig );
    1924
    2025template< typename T >
  • src/ResolvExpr/CandidateFinder.cpp

    rb78c54f rfeb999f  
    4646#include "AST/Type.hpp"
    4747#include "Common/utility.h"       // for move, copy
     48#include "Parser/parserutility.h" // for notZeroExpr
    4849#include "SymTab/Mangler.h"
    4950#include "Tuples/Tuples.h"        // for handleTupleAssignment
     
    15861587        void Finder::postvisit( const ast::LogicalExpr * logicalExpr ) {
    15871588                CandidateFinder finder1( context, tenv );
    1588                 ast::ptr<ast::Expr> arg1 = createCondExpr( logicalExpr->arg1 );
     1589                ast::ptr<ast::Expr> arg1 = notZeroExpr( logicalExpr->arg1 );
    15891590                finder1.find( arg1, ResolveMode::withAdjustment() );
    15901591                if ( finder1.candidates.empty() ) return;
    15911592
    15921593                CandidateFinder finder2( context, tenv );
    1593                 ast::ptr<ast::Expr> arg2 = createCondExpr( logicalExpr->arg2 );
     1594                ast::ptr<ast::Expr> arg2 = notZeroExpr( logicalExpr->arg2 );
    15941595                finder2.find( arg2, ResolveMode::withAdjustment() );
    15951596                if ( finder2.candidates.empty() ) return;
     
    16171618        void Finder::postvisit( const ast::ConditionalExpr * conditionalExpr ) {
    16181619                // candidates for condition
    1619                 ast::ptr<ast::Expr> arg1 = createCondExpr( conditionalExpr->arg1 );
     1620                ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 );
    16201621                CandidateFinder finder1( context, tenv );
    16211622                finder1.find( arg1, ResolveMode::withAdjustment() );
     
    22002201}
    22012202
    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 
    22182203} // namespace ResolvExpr
    22192204
  • src/ResolvExpr/CandidateFinder.hpp

    rb78c54f rfeb999f  
    7070        const ast::Expr * expr, Cost & cost );
    7171
    72 /// Wrap an expression to convert the result to a conditional result.
    73 const ast::Expr * createCondExpr( const ast::Expr * expr );
    74 
    7572} // namespace ResolvExpr
    7673
  • src/ResolvExpr/Resolver.cc

    rb78c54f rfeb999f  
    340340        }
    341341
    342         ast::ptr< ast::Expr > findCondExpression(
    343                 const ast::Expr * untyped, const ResolveContext & context
    344         ) {
    345                 if ( nullptr == untyped ) return untyped;
    346                 ast::ptr<ast::Expr> condExpr = createCondExpr( untyped );
    347                 return findIntegralExpression( condExpr, context );
    348         }
    349 
    350342        /// check if a type is a character type
    351343        bool isCharType( const ast::Type * t ) {
     
    364356                return it != end;
    365357        }
    366 } // anonymous namespace
     358}
    367359
    368360class Resolver final
     
    737729const ast::IfStmt * Resolver::previsit( const ast::IfStmt * ifStmt ) {
    738730        return ast::mutate_field(
    739                 ifStmt, &ast::IfStmt::cond, findCondExpression( ifStmt->cond, context ) );
     731                ifStmt, &ast::IfStmt::cond, findIntegralExpression( ifStmt->cond, context ) );
    740732}
    741733
    742734const ast::WhileDoStmt * Resolver::previsit( const ast::WhileDoStmt * whileDoStmt ) {
    743735        return ast::mutate_field(
    744                 whileDoStmt, &ast::WhileDoStmt::cond, findCondExpression( whileDoStmt->cond, context ) );
     736                whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, context ) );
    745737}
    746738
     
    748740        if ( forStmt->cond ) {
    749741                forStmt = ast::mutate_field(
    750                         forStmt, &ast::ForStmt::cond, findCondExpression( forStmt->cond, context ) );
     742                        forStmt, &ast::ForStmt::cond, findIntegralExpression( forStmt->cond, context ) );
    751743        }
    752744
     
    10831075
    10841076                // Resolve the conditions as if it were an IfStmt, statements normally
    1085                 clause2->when_cond = findCondExpression( clause.when_cond, context );
     1077                clause2->when_cond = findSingleExpression( clause.when_cond, context );
    10861078                clause2->stmt = clause.stmt->accept( *visitor );
    10871079
     
    10971089                        new ast::BasicType{ ast::BasicType::LongLongUnsignedInt };
    10981090                auto timeout_time = findSingleExpression( stmt->timeout_time, target, context );
    1099                 auto timeout_cond = findCondExpression( stmt->timeout_cond, context );
     1091                auto timeout_cond = findSingleExpression( stmt->timeout_cond, context );
    11001092                auto timeout_stmt = stmt->timeout_stmt->accept( *visitor );
    11011093
     
    11101102        if ( stmt->else_stmt ) {
    11111103                // resolve the condition like IfStmt, stmts normally
    1112                 auto else_cond = findCondExpression( stmt->else_cond, context );
     1104                auto else_cond = findSingleExpression( stmt->else_cond, context );
    11131105                auto else_stmt = stmt->else_stmt->accept( *visitor );
    11141106
Note: See TracChangeset for help on using the changeset viewer.