Changeset 7042c60
- Timestamp:
- Apr 25, 2024, 3:48:17 PM (17 months ago)
- Branches:
- master
- Children:
- eb7586e
- Parents:
- cf191ac (diff), 55c97e4 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - Files:
-
- 1 added
- 1 deleted
- 46 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/LaTeXmacros/common.sty
rcf191ac r7042c60 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Sun Feb 25 23:30:09202414 %% Update Count : 6 4513 %% Last Modified On : Thu Apr 18 09:14:02 2024 14 %% Update Count : 657 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 108 108 \renewcommand\subparagraph{\@startsection{subparagraph}{4}{\z@}{-1.5ex \@plus -1ex \@minus -.2ex}{-1em}{\normalfont\normalsize\bfseries\itshape}} 109 109 110 % index macros111 110 \newcommand{\italic}[1]{\emph{\hyperpage{#1}}} 112 111 \newcommand{\Definition}[1]{\textbf{\hyperpage{#1}}} 113 \newcommand{\see}[1]{(see #1)} 112 \newcommand{\see}{\protect\@ifstar\@ssee\@see} 113 \newcommand{\@ssee}[1]{(See #1)} 114 \newcommand{\@see}[1]{(see #1)} 115 116 % index macros 114 117 115 118 % Define some commands that produce formatted index entries suitable for cross-references. … … 152 155 \newcommand{\newtermFontInline}{\emph} 153 156 \newcommand{\newterm}{\protect\@ifstar\@snewterm\@newterm} 157 \newcommand{\@snewterm}[2][\@empty]{{\newtermFontInline{#2}}\ifx#1\@empty\index{#2}\else\index{#1@{\protect#2}}\fi} 154 158 \newcommand{\@newterm}[2][\@empty]{\lowercase{\def\temp{#2}}{\newtermFontInline{#2}}\ifx#1\@empty\index{\temp}\else\index{#1@{\protect#2}}\fi} 155 \newcommand{\@snewterm}[2][\@empty]{{\newtermFontInline{#2}}\ifx#1\@empty\index{#2}\else\index{#1@{\protect#2}}\fi}156 159 157 160 % \snake{<identifier>} … … 202 205 203 206 \newenvironment{cquote}{% 204 \list{}{\ lstset{resetmargins=true,aboveskip=0pt,belowskip=0pt}\topsep=4pt\parsep=0pt\leftmargin=\parindentlnth\rightmargin\leftmargin}%207 \list{}{\topsep=\lst@aboveskip\parskip=0pt\partopsep=0pt\itemsep=0pt\parsep=0pt\listparindent=0pt\leftmargin=\parindentlnth\rightmargin=0pt}% 205 208 \item\relax 209 \lstset{resetmargins=true} 206 210 }{% 207 211 \endlist … … 345 349 \fi% 346 350 351 \usepackage{tabularx} % if @ is used for lstMakeShortInline, allows @{} 352 347 353 % Local Variables: % 348 354 % tab-width: 4 % -
doc/LaTeXmacros/common.tex
rcf191ac r7042c60 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Feb 26 08:06:05202414 %% Update Count : 6 1513 %% Last Modified On : Thu Apr 18 09:15:38 2024 14 %% Update Count : 664 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 109 109 \renewcommand\subparagraph{\@startsection{subparagraph}{4}{\z@}{-1.5ex \@plus -1ex \@minus -.2ex}{-1em}{\normalfont\normalsize\bfseries\itshape}} 110 110 111 % index macros112 111 \newcommand{\italic}[1]{\emph{\hyperpage{#1}}} 113 112 \newcommand{\Definition}[1]{\textbf{\hyperpage{#1}}} 114 \newcommand{\see}[1]{(see #1)} 113 \newcommand{\see}{\protect\@ifstar\@ssee\@see} 114 \newcommand{\@ssee}[1]{(See #1)} 115 \newcommand{\@see}[1]{(see #1)} 116 117 % index macros 115 118 116 119 % Define some commands that produce formatted index entries suitable for cross-references. … … 153 156 \newcommand{\newtermFontInline}{\emph} 154 157 \newcommand{\newterm}{\protect\@ifstar\@snewterm\@newterm} 158 \newcommand{\@snewterm}[2][\@empty]{{\newtermFontInline{#2}}\ifx#1\@empty\index{#2}\else\index{#1@{\protect#2}}\fi} 155 159 \newcommand{\@newterm}[2][\@empty]{\lowercase{\def\temp{#2}}{\newtermFontInline{#2}}\ifx#1\@empty\index{\temp}\else\index{#1@{\protect#2}}\fi} 156 \newcommand{\@snewterm}[2][\@empty]{{\newtermFontInline{#2}}\ifx#1\@empty\index{#2}\else\index{#1@{\protect#2}}\fi}157 160 158 161 % \snake{<identifier>} … … 201 204 \newcommand{\VS}{\abbrevFont{vs}} 202 205 \newcommand{\vs}{\VS\CheckPeriod} 203 \makeatother204 206 205 207 \newenvironment{cquote}{% 206 \list{}{\ lstset{resetmargins=true,aboveskip=0pt,belowskip=0pt}\topsep=4pt\parsep=0pt\leftmargin=\parindentlnth\rightmargin\leftmargin}%208 \list{}{\topsep=\lst@aboveskip\parskip=0pt\partopsep=0pt\itemsep=0pt\parsep=0pt\listparindent=0pt\leftmargin=\parindentlnth\rightmargin=0pt}% 207 209 \item\relax 210 \lstset{resetmargins=true} 208 211 }{% 209 212 \endlist 210 213 }% cquote 214 \makeatother 211 215 212 216 \newenvironment{rationale}{% … … 349 353 \fi% 350 354 355 \usepackage{tabularx} % if @ is used for lstMakeShortInline, allows @{} 356 351 357 % Local Variables: % 352 358 % tab-width: 4 % -
doc/LaTeXmacros/lstlang.sty
rcf191ac r7042c60 8 8 %% Created On : Sat May 13 16:34:42 2017 9 9 %% Last Modified By : Peter A. Buhr 10 %% Last Modified On : Tue Mar 12 17:29:58202411 %% Update Count : 4 210 %% Last Modified On : Mon Apr 15 11:28:44 2024 11 %% Update Count : 43 12 12 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 13 13 … … 116 116 alignas, _Alignas, alignof, _Alignof, __alignof, __alignof__, and, asm, __asm, __asm__, _Atomic, __attribute, __attribute__, 117 117 __auto_type, basetypeof, _Bool, catch, catchResume, choose, coerce, corun, cofor, _Complex, __complex, __complex__, 118 __const, __const__, continue, _Decimal32, _Decimal64, _Decimal128, disable, dtype, enable, exception, __extension__,118 __const, __const__, continue, coroutine, _Decimal32, _Decimal64, _Decimal128, disable, dtype, enable, exception, __extension__, 119 119 fallthrough, fallthru, finally, fixup, __float80, float80, __float128, float128, _Float16, _Float32, _Float32x, _Float64, 120 120 _Float64x, _Float128, _Float128x, forall, fortran, ftype, generator, _Generic, _Imaginary, __imag, __imag__, inline, -
doc/bibliography/pl.bib
rcf191ac r7042c60 519 519 year = 1963, 520 520 pages = {1-17}, 521 } 522 523 @misc{AlgolW, 524 keywords = {AlgolW}, 525 contributer = {pabuhr@plg}, 526 author = {Henry Bauer and Sheldon Becker and Susan L. Graham and Edwin Satterthwaite and Richard L. Sites}, 527 title = {{Algol W} Language Description}, 528 month = jun, 529 year = 1972, 530 howpublished= {\url{https://www.algol60.org/docsW/algolw.pdf}}, 521 531 } 522 532 -
doc/theses/jiada_liang_MMath/CFAenum.tex
rcf191ac r7042c60 137 137 \section{Pure Enumerators} 138 138 139 An empty enumerator type, @enum()@, implies the enumerators are pure symbols without values but set properties;139 An empty enumerator type, @enum()@, implies the enumerators are opaque symbols without values but set properties; 140 140 hence, there is no default conversion to @int@. 141 141 -
doc/theses/jiada_liang_MMath/Makefile
rcf191ac r7042c60 13 13 BibSRC = ${wildcard *.bib} 14 14 15 TeXLIB = .:${LaTMac}:${Build}: 16 BibLIB = .:${BibRep}: 15 TeXLIB = .:${LaTMac}:${Build}: # common latex macros 16 BibLIB = .:${BibRep}: # common citation repository 17 17 18 18 MAKEFLAGS = --no-print-directory # --silent -
doc/theses/jiada_liang_MMath/background.tex
rcf191ac r7042c60 1 1 \chapter{Background} 2 \lstnewenvironment{clang}[1][]{\lstset{language=[ANSI]C,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}3 2 4 3 \CFA is a backwards-compatible extension of the C programming language. … … 48 47 49 48 \section{C Enumeration} 49 \label{s:CEnumeration} 50 50 51 The C enumeration has the following syntax and semantics. 51 The C enumeration has the following syntax~\cite[\S~6.7.2.2]{C11}. 52 \begin{clang}[identifierstyle=\linespread{0.9}\it] 53 $\it enum$-specifier: 54 enum identifier$\(_{opt}\)$ { enumerator-list } 55 enum identifier$\(_{opt}\)$ { enumerator-list , } 56 enum identifier 57 enumerator-list: 58 enumerator 59 enumerator-list , enumerator 60 enumerator: 61 enumeration-constant 62 enumeration-constant = constant-expression 63 \end{clang} 64 The terms \emph{enumeration} and \emph{enumerator} used in this work \see{\VRef{s:Terminology}} come from the grammar. 65 The C enumeration semantics is discussed using examples. 66 67 An unnamed enumeration is used to provide secondary renaming, like a @const@ declaration in other languages. 68 \begin{clang} 69 enum { Size = 20, Pi = 3.14159 }; // unnamed enumeration $\(\Rightarrow\)$ no ordering 70 \end{clang} 71 This declaration form is not an enumeration even though it is declared using an @enum@ because it has none of the following enumeration properties. 72 73 A \emph{named} enumeration type is an actual enumeration. 52 74 \begin{clang} 53 75 enum Weekday { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun, }; -
doc/theses/jiada_liang_MMath/intro.tex
rcf191ac r7042c60 1 1 \chapter{Introduction} 2 2 3 All types in a programming language must have a set of constants, and these constants have \Newterm{primary names}, \eg integral types have constants @-1@, @17@, @12345@, \etc. 4 Constants can be overloaded among types, \eg @0@ is a null pointer for all pointer types, and the value zero for integral and floating-point types. 3 All types in a programming language must have a set of constants, and these constants have \Newterm{primary names}, \eg integral types have constants @-1@, @17@, @0xff@, floating-point types have constants @5.3@, @2.3E-5@, @0xff.ffp0@, character types have constants @'a'@, @"abc\n"@, \mbox{\lstinline{u8"}\texttt{\guillemotleft{na\"{i}ve}\guillemotright}\lstinline{"}}, \etc. 4 Con\-stants can be overloaded among types, \eg @0@ is a null pointer for all pointer types, and the value zero for integral and floating-point types. 5 (In \CFA, the primary constants @0@ and @1@ can be overloaded for any type.) 5 6 Hence, each primary constant has a symbolic name referring to its internal representation, and these names are dictated by language syntax related to types. 6 In theory, there are an infinite set of primary names per type.7 8 \Newterm{Secondary naming} is a common practice in mathematics and engineering, \eg $\pi$, $\tau$ (2$\pi$), $\phi$ (golden ratio), MHz (1E6), and in general situations, \eg specific times (noon, New Years), cities (Big Apple), flowers (Lily), \etc.7 In theory, there are an infinite set of primary constant names per type. 8 9 \Newterm{Secondary naming} is a common practice in mathematics, engineering and computer science, \eg $\pi$, $\tau$ (2$\pi$), $\phi$ (golden ratio), MB (megabyte, 1E6), and in general situations, \eg specific times (noon, New Years), cities (Big Apple), flowers (Lily), \etc. 9 10 Many programming languages capture this important software-engineering capability through a mechanism called \Newterm{constant} or \Newterm{literal} naming, where a secondary name is aliased to a primary name. 10 In some cases, secondary naming is \Newterm{pure}, where the matching internal representation can be chosen arbitrarily, and only equality operations are available, \eg @O_RDONLY@, @O_WRONLY@, @O_CREAT@, @O_TRUNC@, @O_APPEND@. 11 (The names the thing.) 11 Its purpose is for readability and to eliminate duplication of the primary constant throughout a program. 12 For example, a meaningful secondary name replaces a primary name throughout a program; 13 thereafter, changing the binding of the secondary to primary name automatically distributes the rebinding, preventing errors. 14 In some cases, secondary naming is \Newterm{opaque}, where the matching internal representation can be chosen arbitrarily, and only equality operations are available, \eg @O_RDONLY@, @O_WRONLY@, @O_CREAT@, @O_TRUNC@, @O_APPEND@. 12 15 Because a secondary name is a constant, it cannot appear in a mutable context, \eg \mbox{$\pi$ \lstinline{= 42}} is meaningless, and a constant has no address, \ie it is an \Newterm{rvalue}\footnote{ 13 16 The term rvalue defines an expression that can only appear on the right-hand side of an assignment expression.}. 14 17 15 Secondary names can form an (ordered) set, \eg days of theweek, months of a year, floors of a building (basement, ground, 1st), colours in a rainbow, \etc.18 Secondary names can form an (ordered) set, \eg days of a week, months of a year, floors of a building (basement, ground, 1st), colours in a rainbow, \etc. 16 19 Many programming languages capture these groupings through a mechanism called an \Newterm{enumeration}. 17 20 \begin{quote} 18 21 enumerate (verb, transitive). 19 22 To count, ascertain the number of; 20 \emph{more 21 usually, to mention (a number of things or persons) separately, as if for the 22 purpose of counting}; 23 to specify as in a list or catalogue.~\cite{OED} 23 more usually, to mention (a number of things or persons) separately, as if for the purpose of counting; 24 to specify as in a list or catalogue.~\cite{OEDenumerate} 24 25 \end{quote} 25 Within an enumeration set, the enumeration names must be unique, and instances of an enumerated type are restricted to hold only the secondary names.26 Within an enumeration set, the enumeration names must be unique, and instances of an enumerated type are \emph{often} restricted to hold only the secondary names. 26 27 It is possible to enumerate among set names without having an ordering among the set elements. 27 28 For example, the week, the weekdays, the weekend, and every second day of the week. … … 29 30 for ( cursor in Mon, Tue, Wed, Thu, Fri, Sat, Sun } ... $\C[3.75in]{// week}$ 30 31 for ( cursor in Mon, Tue, Wed, Thu, Fri } ... $\C{// weekday}$ 31 for ( cursor in Thu, Fri} ... $\C{// weekend}$32 for ( cursor in Sat, Sun } ... $\C{// weekend}$ 32 33 for ( cursor in Mon, Wed, Fri, Sun } ... $\C{// every second day of week}\CRT$ 33 34 \end{cfa} 34 This independence from internal representation allows multiple names to have the same representation (eight note, quaver), giving synonyms.35 This independence from internal representation allows multiple names to have the same representation (eighth note, quaver), giving synonyms. 35 36 A set can have a partial or total ordering, making it possible to compare set elements, \eg Monday is before Friday and Friday is after. 36 Ordering allows iterating among the enumeration set using relational operators and advancement, \eg 37 Ordering allows iterating among the enumeration set using relational operators and advancement, \eg: 37 38 \begin{cfa} 38 39 for ( cursor = Monday; cursor @<=@ Friday; cursor = @succ@( cursor ) ) ... 39 40 \end{cfa} 40 Here the internal representations for the secondary names are \emph{generated} rather than listing a subset of names. 41 Here the internal representation for the secondary names are logically \emph{generated} rather than listing a subset of names. 42 43 Hence, the fundamental aspects of an enumeration are: 44 \begin{enumerate} 45 \item 46 \begin{sloppypar} 47 It provides a finite set of secondary names, which become its primary constants. 48 This differentiates an enumeration from general types with an infinite set 49 of primary constants. 50 \end{sloppypar} 51 \item 52 The secondary names are constants, which follows transitively from their binding (aliasing) to primary names, which are constants. 53 \item 54 Defines a type for generating instants (variables). 55 \item 56 For safety, an enumeration instance should be restricted to hold only its type's secondary names. 57 \item 58 There is a mechanism for \emph{enumerating} over the secondary names, where the ordering can be implicit from the type, explicitly listed, or generated arithmetically. 59 \end{enumerate} 41 60 42 61 43 62 \section{Terminology} 44 45 The term \Newterm{enumeration} defines the set of secondary names, and the term \Newterm{enumerator} represents an arbitrary secondary name. 46 As well, an enumerated type has three fundamental properties, \Newterm{label}, \Newterm{order}, and \Newterm{value}. 63 \label{s:Terminology} 64 65 The term \Newterm{enumeration} defines a type with a set of secondary names, and the term \Newterm{enumerator} represents an arbitrary secondary name \see{\VRef{s:CEnumeration} for the name derivation}. 66 As well, an enumerated type can have three fundamental properties, \Newterm{label}, \Newterm{order}, and \Newterm{value}. 47 67 \begin{cquote} 48 68 \sf\setlength{\tabcolsep}{3pt} 49 69 \begin{tabular}{rcccccccr} 50 70 \it\color{red}enumeration & \multicolumn{8}{c}{\it\color{red}enumerators} \\ 51 $\downarrow$\hspace*{ 25pt} & \multicolumn{8}{c}{$\downarrow$} \\52 @enum@ Week \{ & Mon, & Tue, & Wed, & Thu, & Fri, & Sat, & Sun = 42& \}; \\71 $\downarrow$\hspace*{15pt} & \multicolumn{8}{c}{$\downarrow$} \\ 72 @enum@ Week \{ & Mon, & Tue, & Wed, & Thu, & Fri, & Sat, & Sun {\color{red}= 42} & \}; \\ 53 73 \it\color{red}label & Mon & Tue & Wed & Thu & Fri & Sat & Sun & \\ 54 74 \it\color{red}order & 0 & 1 & 2 & 3 & 4 & 5 & 6 & \\ 55 \it\color{red}value & 0 & 1 & 2 & 3 & 4 & 5 & 42&75 \it\color{red}value & 0 & 1 & 2 & 3 & 4 & 5 & {\color{red}42} & 56 76 \end{tabular} 57 77 \end{cquote} … … 72 92 \section{Motivation} 73 93 74 Some programming languages only provide secondary renaming, which can be simulated by an enumeration without ordering. 75 \begin{cfa} 76 const Size = 20, Pi = 3.14159; 77 enum { Size = 20, Pi = 3.14159 }; // unnamed enumeration $\(\Rightarrow\)$ no ordering 78 \end{cfa} 79 In both cases, it is possible to compare the secondary names, \eg @Size < Pi@, if that is meaningful; 80 however, without an enumeration type-name, it is impossible to create an iterator cursor. 81 82 Secondary renaming can similate an enumeration, but with extra effort. 94 Many programming languages provide an enumeration-like mechanism, which may or may not cover the previous five fundamental enumeration aspects. 95 Hence, the term \emph{enumeration} can be confusing and misunderstood. 96 Furthermore, some languages conjoin the enumeration with other type features, making it difficult to tease apart which featuring is being used. 97 This section discusses some language features that are sometimes called an enumeration but do not provide all enumeration aspects. 98 99 100 \subsection{Aliasing} 101 102 Some languages provide simple secondary aliasing (renaming), \eg: 103 \begin{cfa} 104 const Size = 20, Pi = 3.14159, Name = "Jane"; 105 \end{cfa} 106 The secondary name is logically replaced in the program text by its corresponding primary name. 107 Therefore, it is possible to compare the secondary names, \eg @Size < Pi@, only because the primary constants allow it, whereas \eg @Pi < Name@ might be disallowed depending on the language. 108 109 Aliasing is not macro substitution, \eg @#define Size 20@, where a name is replaced by its value \emph{before} compilation, so the name is invisible to the programming language. 110 With aliasing, each secondary name is part of the language, and hence, participates fully, such as name overloading in the type system. 111 Aliasing is not an immutable variable, \eg: 112 \begin{cfa} 113 extern @const@ int Size = 20; 114 extern void foo( @const@ int @&@ size ); 115 foo( Size ); // take the address of (reference) Size 116 \end{cfa} 117 Taking the address of an immutable variable makes it an \Newterm{lvalue}, which implies it has storage. 118 With separate compilation, it is necessary to choose one translation unit to perform the initialization. 119 If aliasing does require storage, its address and initialization are opaque (compiler only), similar to \CC rvalue reference @&&@. 120 121 Aliasing does provide readability and automatic resubstitution. 122 It also provides simple enumeration properties, but with extra effort. 83 123 \begin{cfa} 84 124 const Mon = 1, Tue = 2, Wed = 3, Thu = 4, Fri = 5, Sat = 6, Sun = 7; 85 125 \end{cfa} 86 Furthermore, reorderingthe enumerators requires manual renumbering.126 Any reordering of the enumerators requires manual renumbering. 87 127 \begin{cfa} 88 128 const Sun = 1, Mon = 2, Tue = 3, Wed = 4, Thu = 5, Fri = 6, Sat = 7; 89 129 \end{cfa} 90 Finally, there is no common type to create a type-checked instance or iterator cursor. 91 Hence, there is only a weak equivalence between secondary naming and enumerations, justifying the enumeration type in a programming language. 92 93 A variant (algebraic) type is often promoted as a kind of enumeration, \ie a varient type can simulate an enumeration. 94 A variant type is a tagged-union, where the possible types may be heterogeneous. 95 \begin{cfa} 96 @variant@ Variant { 97 @int tag;@ // optional/implicit: 0 => int, 1 => double, 2 => S 98 @union {@ // implicit 99 case int i; 100 case double d; 101 case struct S { int i, j; } s; 102 @};@ 103 }; 104 \end{cfa} 105 Crucially, the union implies instance storage is shared by all of the variant types. 106 Hence, a variant is dynamically typed, as in a dynamic-typed programming-language, but the set of types is statically bound, similar to some aspects of dynamic gradual-typing~\cite{Gradual Typing}. 107 Knowing which type is in a variant instance is crucial for correctness. 108 Occasionally, it is possible to statically determine all regions where each variant type is used, so a tag and runtime checking is unnecessary; 109 otherwise, a tag is required to denote the particular type in the variant and the tag checked at runtime using some form of type pattern-matching. 110 111 The tag can be implicitly set by the compiler on assignment, or explicitly set by the program\-mer. 112 Type pattern-matching is then used to dynamically test the tag and branch to a section of code to safely manipulate the value, \eg: 113 \begin{cfa}[morekeywords={match}] 114 Variant v = 3; // implicitly set tag to 0 115 @match@( v ) { // know the type or test the tag 116 case int { /* only access i field in v */ } 117 case double { /* only access d field in v */ } 118 case S { /* only access s field in v */ } 119 } 120 \end{cfa} 121 For safety, either all variant types must be listed or a @default@ case must exist with no field accesses. 122 123 To simulate an enumeration with a variant, the tag is \emph{re-purposed} for either ordering or value and the variant types are omitted. 124 \begin{cfa} 125 variant Weekday { 126 int tag; // implicit 0 => Mon, ..., 6 => Sun 127 @case Mon;@ // no type 128 ... 129 @case Sun;@ 130 }; 131 \end{cfa} 132 The type system ensures tag setting and testing are correctly done. 133 However, the enumeration operations are limited to the available tag operations, \eg pattern matching. 134 \begin{cfa} 135 Week week = Mon; 136 if ( @dynamic_cast(Mon)@week ) ... // test tag == Mon 137 \end{cfa} 138 While enumerating among tag names is possible: 139 \begin{cfa}[morekeywords={in}] 140 for ( cursor in Mon, Wed, Fri, Sun ) ... 141 \end{cfa} 142 ordering for iteration would require a \emph{magic} extension, such as a special @enum@ variant, because it has no meaning for a regular variant, \ie @int@ < @double@. 143 144 However, if a special @enum@ variant allows the tags to be heterogeneously typed, ordering must fall back on case positioning, as many types have incomparable values. 145 Iterating using tag ordering and heterogeneous types, also requires pattern matching. 146 \begin{cfa}[morekeywords={match}] 147 for ( cursor = Mon; cursor <= Fri; cursor = succ( cursor) ) { 148 match( cursor ) { 149 case Mon { /* access special type for Mon */ } 150 ... 151 case Fri { /* access special type for Fri */ } 152 default 153 } 154 } 155 \end{cfa} 156 If the variant type is changed by adding/removing types or the loop range changes, the pattern matching must be adjusted. 157 As well, if the start/stop values are dynamic, it may be impossible to statically determine if all variant types are listed. 158 159 Re-purposing the notion of enumerating into variant types is ill formed and confusing. 160 Hence, there is only a weak equivalence between an enumeration and variant type, justifying the enumeration type in a programming language. 130 For these reasons, aliasing is sometimes called an enumeration. 131 However, there is no type to create a type-checked instance or iterator cursor, so there is no ability for enumerating. 132 Hence, there are multiple enumeration aspects not provided by aliasing, justifying a separate enumeration type in a programming language. 133 134 135 \subsection{Algebraic Data Type} 136 137 An algebraic data type (ADT)\footnote{ADT is overloaded with abstract data type.} is another language feature often linked with enumeration, where an ADT conjoins an arbitrary type, possibly a \lstinline[language=C++]{class} or @union@, and a named constructor. 138 For example, in Haskell: 139 \begin{haskell} 140 data S = S { i::Int, d::Double } $\C{// structure}$ 141 data @Foo@ = A Int | B Double | C S $\C{// ADT, composed of three types}$ 142 foo = A 3; $\C{// type Foo is inferred}$ 143 bar = B 3.5 144 baz = C S{ i = 7, d = 7.5 } 145 \end{haskell} 146 the ADT has three variants (constructors), @A@, @B@, @C@ with associated types @Int@, @Double@, and @S@. 147 The constructors create an initialized value of the specific type that is bound to the immutable variables @foo@, @bar@, and @baz@. 148 Hence, the ADT @Foo@ is like a union containing values of the associated types, and a constructor name is used to access the value using dynamic pattern-matching. 149 \begin{cquote} 150 \setlength{\tabcolsep}{15pt} 151 \begin{tabular}{@{}ll@{}} 152 \begin{haskell} 153 prtfoo val = -- function 154 -- pattern match on constructor 155 case val of 156 @A@ a -> print a 157 @B@ b -> print b 158 @C@ (S i d) -> do 159 print i 160 print d 161 \end{haskell} 162 & 163 \begin{haskell} 164 main = do 165 prtfoo foo 166 prtfoo bar 167 prtfoo baz 168 3 169 3.5 170 7 171 7.5 172 \end{haskell} 173 \end{tabular} 174 \end{cquote} 175 For safety, most languages require all assocaited types to be listed or a default case with no field accesses. 176 177 A less frequent case is multiple constructors with the same type. 178 \begin{haskell} 179 data Bar = X Int | Y Int | Z Int; 180 foo = X 3; 181 bar = Y 3; 182 baz = Z 5; 183 \end{haskell} 184 Here, the constructor name gives different meaning to the values in the common \lstinline[language=Haskell]{Int} type, \eg the value @3@ has different interpretations depending on the constructor name in the pattern matching. 185 186 Note, the term \Newterm{variant} is often associated with ADTs. 187 However, there are multiple languages with a @variant@ type that is not an ADT \see{Algol68~\cite{Algol68} or \CC \lstinline{variant}}. 188 In these languages, the variant is often a union using RTTI tags, which cannot be used to simulate an enumeration. 189 Hence, in this work the term variant is not a synonym for ADT. 190 191 % https://downloads.haskell.org/ghc/latest/docs/libraries/base-4.19.1.0-179c/GHC-Enum.html 192 % https://hackage.haskell.org/package/base-4.19.1.0/docs/GHC-Enum.html 193 194 The association between ADT and enumeration occurs if all the constructors have a unit (empty) type, \eg @struct unit {}@. 195 Note, the unit type is not the same as \lstinline{void}, \eg: 196 \begin{cfa} 197 void foo( void ); 198 struct unit {} u; // empty type 199 unit bar( unit ); 200 foo( foo() ); // void argument does not match with void parameter 201 bar( bar( u ) ); // unit argument does match with unit parameter 202 \end{cfa} 203 204 For example, in the Haskell ADT: 205 \begin{haskell} 206 data Week = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving(Enum, Eq, Show) 207 \end{haskell} 208 the default type for each constructor is the unit type, and deriving from @Enum@ enforces no other type, @Eq@ allows equality comparison, and @Show@ is for printing. 209 The nullary constructors for the unit types are numbered left-to-right from $0$ to @maxBound@$- 1$, and provides enumerating operations @succ@, @pred@, @enumFrom@ @enumFromTo@. 210 \VRef[Figure]{f:HaskellEnumeration} shows enumeration comparison and iterating (enumerating). 211 212 \begin{figure} 213 \begin{cquote} 214 \setlength{\tabcolsep}{15pt} 215 \begin{tabular}{@{}ll@{}} 216 \begin{haskell} 217 day = Tue 218 main = do 219 if day == Tue then 220 print day 221 else 222 putStr "not Tue" 223 print (enumFrom Mon) -- week 224 print (enumFromTo Mon Fri) -- weekday 225 print (enumFromTo Sat Sun) -- weekend 226 \end{haskell} 227 & 228 \begin{haskell} 229 Tue 230 [Mon,Tue,Wed,Thu,Fri,Sat,Sun] 231 [Mon,Tue,Wed,Thu,Fri] 232 [Sat,Sun] 233 234 235 236 237 238 \end{haskell} 239 \end{tabular} 240 \end{cquote} 241 \caption{Haskell Enumeration} 242 \label{f:HaskellEnumeration} 243 \end{figure} 244 245 The key observation is the dichotomy between an ADT and enumeration: the ADT uses the associated type resulting in a union-like data structure, and the enumeration does not use the associated type, and hence, is not a union. 246 While the enumeration is constructed using the ADT mechanism, it is so restricted it is not really an ADT. 247 Furthermore, a general ADT cannot be an enumeration because the constructors generate different values making enumerating meaningless. 248 While functional programming languages regularly repurpose the ADT type into an enumeration type, this process seems contrived and confusing. 249 Hence, there is only a weak equivalence between an enumeration and ADT, justifying a separate enumeration type in a programming language. 161 250 162 251 163 252 \section{Contributions} 164 253 165 The goal of this work is to to extend the simple and unsafe enumeration type in the C programming-language into a sophisticated and safe type in the \CFA programming-language, while maintainbackwards compatibility with C.254 The goal of this work is to to extend the simple and unsafe enumeration type in the C programming-language into a complex and safe enumeration type in the \CFA programming-language, while maintaining backwards compatibility with C. 166 255 On the surface, enumerations seem like a simple type. 167 256 However, when extended with advanced features, enumerations become complex for both the type system and the runtime implementation. 168 257 258 The contribution of this work are: 169 259 \begin{enumerate} 170 260 \item … … 175 265 typing 176 266 \item 177 subset 267 subseting 178 268 \item 179 269 inheritance -
doc/theses/jiada_liang_MMath/relatedwork.tex
rcf191ac r7042c60 23 23 \section{Pascal} 24 24 \label{s:Pascal} 25 \lstnewenvironment{pascal}[1][]{\lstset{language=pascal,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}26 25 27 26 Classic Pascal has the \lstinline[language=pascal]{const} declaration binding a name to a constant literal/expression. … … 51 50 52 51 \section{Ada} 53 \lstnewenvironment{ada}[1][]{\lstset{language=[2005]Ada,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},literate={'}{\ttfamily'\!}1}\lstset{#1}}{} 54 55 A n Ada enumeration type is an ordered list of constants, called \Newterm{literals} (enumerators).52 53 An Ada enumeration type is a set of ordered unscoped identifiers (enumerators) bound to \emph{unique} \Newterm{literals}.\footnote{% 54 Ada is \emph{case-insensitive} so identifiers may appear in multiple forms and still be the same, \eg \lstinline{Mon}, \lstinline{moN}, and \lstinline{MON} (a questionable design decision).} 56 55 \begin{ada} 57 type RGB is ( Red, Green, Blue ); -- 3literals (enumerators)56 type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun ); -- literals (enumerators) 58 57 \end{ada} 59 58 Object initialization and assignment are restricted to the enumerators of this type. 60 Enumerators without an explicitly designated constant value are auto-initialized: from left to right, starting at zero or the next explicitly initialized constant, incrementing by 1. 61 To explicitly set enumerator values, \emph{all} enumerators must be set in \emph{ascending} order, \ie there is no auto-initialization. 59 While Ada enumerators are unscoped, like C, Ada enumerators are overloadable. 62 60 \begin{ada} 63 type RGB is ( Red, Green, Blue ); 64 @for RGB use ( Red => 10, Green => 20, Blue => 30 );@ -- ascending order 65 \end{ada} 66 Hence, the position, value, label tuples are: 67 \begin{ada} 68 (0, 10, RED) (1, 20, GREEN) (2, 30, BLUE) 69 \end{ada} 70 Note, Ada is case-\emph{insensitive} so names may appear in multiple forms and still be the same, \eg @Red@ and @RED@ (a questionable design decision). 71 72 Like C, Ada enumerators are unscoped, \ie enumerators declared inside of an enum are visible (projected) into the enclosing scope. 73 The enumeration operators are the ordering operators, @=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relationship is given implicitly by the sequence of enumerators, which is always ascending. 74 75 Ada enumerators are overloadable. 76 \begin{ada} 61 type RGB is ( @Red@, @Green@, Blue ); 77 62 type Traffic_Light is ( @Red@, Yellow, @Green@ ); 78 63 \end{ada} 79 Like \CFA, Ada uses an advanced type-resolution algorithm, including the left-hand side of assignment, to disambiguate among overloaded names.64 Like \CFA, Ada uses an advanced type-resolution algorithm, including the left-hand side of assignment, to disambiguate among overloaded identifiers. 80 65 \VRef[Figure]{f:AdaEnumeration} shows how ambiguity is handled using a cast, \ie \lstinline[language=ada]{RGB'(Red)}. 81 66 … … 102 87 \end{figure} 103 88 104 Ada provides an alias mechanism, \lstinline[language=ada]{renames}, for aliasing types, which is useful to shorten package names. 89 Enumerators without initialization are auto-initialized from left to right, starting at zero, incrementing by 1. 90 Enumerators with initialization must set \emph{all} enumerators in \emph{ascending} order, \ie there is no auto-initialization. 91 \begin{ada} 92 type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun ); 93 for Week use ( Mon => 0, Tue => 1, Wed => 2, Thu => @10@, Fri => 11, Sat => 14, Sun => 15 ); 94 \end{ada} 95 The enumeration operators are the equality and relational operators, @=@, @/=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relationship is given implicitly by the sequence of acsending enumerators. 96 97 Ada provides an alias mechanism, \lstinline[language=ada]{renames}, for aliasing types, which is useful to shorten package identifiers. 105 98 \begin{ada} 106 99 OtherRed : RGB renames Red; … … 113 106 There are three pairs of inverse enumeration pseudo-functions (attributes): @'Pos@ and @'Val@, @'Enum_Rep@ and @'Enum_Val@, and @'Image@ and @'Value@, 114 107 \begin{cquote} 115 \lstDeleteShortInline@116 108 \setlength{\tabcolsep}{15pt} 117 109 \begin{tabular}{@{}ll@{}} … … 128 120 \end{ada} 129 121 \end{tabular} 130 \lstMakeShortInline@131 122 \end{cquote} 132 123 These attributes are important for IO. … … 138 129 \end{ada} 139 130 which is syntactic sugar for the label and not character literals from the predefined type @Character@. 140 The purpose is strictly readability using character literals rather than names.131 The purpose is strictly readability using character literals rather than identifiers. 141 132 \begin{ada} 142 133 Op : Operator := '+'; … … 171 162 An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a default) or iterating constructs. 172 163 \begin{cquote} 173 \lstDeleteShortInline@174 164 \setlength{\tabcolsep}{15pt} 175 165 \begin{tabular}{@{}ll@{}} … … 211 201 \end{ada} 212 202 \end{tabular} 213 \lstMakeShortInline@214 203 \end{cquote} 215 204 … … 225 214 \section{\CC} 226 215 \label{s:C++RelatedWork} 227 \lstnewenvironment{c++}[1][]{\lstset{language=[GNU]C++,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}228 216 229 217 \CC has the equivalent of Pascal typed @const@ declarations \see{\VRef{s:Pascal}}, with static and dynamic initialization. 230 218 \begin{c++} 231 const auto one = 0 + 1; $\C{// static in tialization}$219 const auto one = 0 + 1; $\C{// static initialization}$ 232 220 const auto NULL = nullptr; 233 221 const auto PI = 3.14159; … … 237 225 Sat = Fri + 1, Sun = Sat + 1; 238 226 int sa[Sun]; 239 const auto r = random(); $\C{// dynamic in tialization}$227 const auto r = random(); $\C{// dynamic initialization}$ 240 228 int da[r]; $\C{// VLA}$ 241 229 \end{c++} … … 319 307 \section{C\raisebox{-0.7ex}{\LARGE$^\sharp$}\xspace} % latex bug: cannot use \relsize{2} so use \LARGE 320 308 \label{s:Csharp} 321 \lstnewenvironment{csharp}[1][]{\lstset{language=[Sharp]C,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}322 309 323 310 % https://www.tutorialsteacher.com/codeeditor?cid=cs-mk8Ojx … … 362 349 \begin{figure} 363 350 \centering 364 \lstDeleteShortInline@365 351 \begin{tabular}{@{}l|l@{}} 366 352 \multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\ … … 414 400 \end{csharp} 415 401 \end{tabular} 416 \lstMakeShortInline@417 402 \caption{\Csharp: Free Routine Versus Class Enumeration} 418 403 \label{CsharpFreeVersusClass} … … 421 406 422 407 \section{Golang} 423 \lstnewenvironment{Go}[1][]{\lstset{language=Go,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}424 408 425 409 Golang provides pseudo-enumeration similar to classic Pascal \lstinline[language=pascal]{const}, binding a name to a constant literal/expression. … … 429 413 const ( S = 0; T; USA = "USA"; U; V = 3.1; W ) $\C{// type change, implicit/explicit: 0 0 USA USA 3.1 3.1}$ 430 414 \end{Go} 431 Constant names are unscoped and must be unique (no overloading).415 Constant identifiers are unscoped and must be unique (no overloading). 432 416 The first enumerator \emph{must} be explicitly initialized; 433 417 subsequent enumerators can be implicitly or explicitly initialized. … … 459 443 Basic switch and looping are possible. 460 444 \begin{cquote} 461 \lstDeleteShortInline@462 445 \setlength{\tabcolsep}{15pt} 463 446 \begin{tabular}{@{}ll@{}} … … 482 465 \end{Go} 483 466 \end{tabular} 484 \lstMakeShortInline@485 467 \end{cquote} 486 468 However, the loop prints the values from 0 to 13 because there is no actual enumeration. … … 488 470 489 471 \section{Java} 490 \lstnewenvironment{Java}[1][]{\lstset{language=Java,morekeywords={enum,assert,strictfp},491 escapechar=\$,moredelim=**[is][\color{red}]{!}{!},}\lstset{#1}}{}492 472 493 473 Every enumeration in Java is an enumeration class. … … 513 493 \begin{figure} 514 494 \centering 515 \lstDeleteShortInline@516 495 \begin{tabular}{@{}l|l@{}} 517 496 \multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\ … … 553 532 \end{Java} 554 533 \end{tabular} 555 \lstMakeShortInline@556 534 \caption{Java: Free Routine Versus Class Enumeration} 557 535 \label{f:JavaFreeVersusClass} … … 606 584 607 585 \section{Rust} 608 \lstnewenvironment{rust}[1][]{\lstset{language=Rust,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 586 % https://doc.rust-lang.org/reference/items/enumerations.html 609 587 610 588 Rust provides a scoped enumeration based on variant types. … … 652 630 653 631 \section{Swift} 654 \lstnewenvironment{swift}[1][]{\lstset{language=Swift,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}655 632 656 633 % https://www.programiz.com/swift/online-compiler … … 1010 987 1011 988 1012 \section{Python} 1013 \lstnewenvironment{python}[1][]{\lstset{language=Python,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 1014 1015 A Python enumeration is a set of symbolic names bound to \emph{unique} values. 1016 They are similar to global variables, but offer a more useful @repr()@, grouping, type-safety, and additional features. 1017 Enumerations inherits from the @Enum@ class, \eg: 1018 \begin{python} 1019 class Weekday(@Enum@): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 1020 class RGB(@Enum@): Red = 1; Green = 2; Blue = 3 1021 \end{python} 1022 1023 Depending on the nature of the enum a member's value may or may not be important, but either way that value can be used to get the corresponding member: 1024 \begin{python} 1025 print( repr( Weekday( 3 ) ) ) 1026 <Weekday.Wed: 3> 1027 \end{python} 1028 As you can see, the @repr()@ of a member shows the enum name, the member name, and the value. 1029 The @str()@ of a member shows only the enum name and member name: 1030 \begin{python} 1031 print( str( Weekday.Thu ), Weekday.Thu ) 1032 Weekday.Thu Weekday.Thu 1033 \end{python} 1034 The type of an enumeration member is the enum it belongs to: 1035 \begin{python} 1036 print( type( Weekday.Thu ) ) 1037 <enum 'Weekday'> 1038 print( isinstance(Weekday.Fri, Weekday) ) 1039 True 1040 \end{python} 1041 Enum members have an attribute that contains just their name: 1042 \begin{python} 1043 print(Weekday.TUESDAY.name) 1044 TUESDAY 1045 \end{python} 1046 Likewise, they have an attribute for their value: 1047 \begin{python} 1048 Weekday.WEDNESDAY.value 1049 3 1050 \end{python} 1051 1052 Unlike many languages that treat enumerations solely as name/value pairs, Python @Enum@s can have behavior added. 1053 For example, @datetime.date@ has two methods for returning the weekday: @weekday()@ and @isoweekday()@. 1054 The difference is that one of them counts from 0-6 and the other from 1-7. 1055 Rather than keep track of that ourselves we can add a method to the @Weekday@ enum to extract the day from the date instance and return the matching enum member: 1056 \begin{python} 1057 class Weekday(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = 15; Sat = 16; Sun = 17 1058 $@$classmethod 1059 def from_date(cls, date): 1060 return cls(date.isoweekday()) 1061 \end{python} 1062 Now we can find out what today is! Observe: 1063 \begin{python} 1064 >>> from datetime import date 1065 >>> Weekday.from_date(date.today()) 1066 <Weekday.TUESDAY: 2> 1067 \end{python} 1068 Of course, if you're reading this on some other day, you'll see that day instead. 1069 1070 This Weekday enum is great if our variable only needs one day, but what if we need several? Maybe we're writing a function to plot chores during a week, and don't want to use a @list@ -- we could use a different type of @Enum@: 1071 \begin{python} 1072 from enum import Flag 1073 class WeekdayF(@Flag@): Mon = @1@; Tue = @2@; Wed = @4@; Thu = @8@; Fri = @16@; Sat = @32@; Sun = @64@ 1074 \end{python} 1075 We've changed two things: we're inherited from @Flag@, and the values are all powers of 2. 989 \section{Python 3.13} 990 % https://docs.python.org/3/howto/enum.html 991 992 Python is a dynamically-typed reflexive programming language with multiple versions, and hence, it is possible to extend existing or build new language features within the language. 993 As a result, discussing Python enumerations is a moving target, because if a features does not exist, if can often be created with varying levels of complexity. 994 Nevertheless, an attempt has been made to discuss core enumeration features that come with Python 3.13. 995 996 A Python enumeration type is a set of ordered scoped identifiers (enumerators) bound to \emph{unique} values. 997 An enumeration is not a basic type; 998 it is a @class@ inheriting from the @Enum@ class, where the enumerators must be explicitly initialized, \eg: 999 \begin{python} 1000 class Week(@Enum@): Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 1001 \end{python} 1002 and/or explicitly auto initialized, \eg: 1003 \begin{python} 1004 class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = @auto()@; Sat = 4; Sun = @auto()@ 1005 \end{python} 1006 where @auto@ increments by 1 from the previous enumerator value. 1007 Object initialization and assignment are restricted to the enumerators of this type. 1008 An enumerator initialized with same value is an alias and invisible at the enumeration level, \ie the alias it substituted for its aliasee. 1009 \begin{python} 1010 class Week(Enum): Mon = 1; Tue = 2; Wed = 3; Thu = 10; Fri = @10@; Sat = @10@; Sun = @10@ 1011 \end{python} 1012 Here, the enumeration has only 4 enumerators and 3 aliases. 1013 An alias is only visible by dropping down to the @class@ level and asking for class members. 1014 @Enum@ only supports equality comparison between enumerator values; 1015 the extended class @OrderedEnum@ adds relational operators @<@, @<=@, @>@, and @>=@. 1016 1017 There are bidirectional enumeration pseudo-functions for label and value, but there is no concept of access using ordering (position). 1018 \begin{cquote} 1019 \setlength{\tabcolsep}{15pt} 1020 \begin{tabular}{@{}ll@{}} 1021 \begin{python} 1022 Week.Thu.value == 10; 1023 Week.Thu.name == 'Thu'; 1024 \end{python} 1025 & 1026 \begin{python} 1027 Week( 10 ) == Thu 1028 Week['Thu'].value = 10 1029 \end{python} 1030 \end{tabular} 1031 \end{cquote} 1032 1033 As an enumeration is a \lstinline[language=python]{class}, its own methods. 1034 \begin{python} 1035 class Week(Enum): 1036 Mon = 1; Tue = 2; Wed = 3; Thu = 4; Fri = 5; Sat = 6; Sun = 7 1037 $\\@$classmethod 1038 def today(cls, date): 1039 return cls(date.isoweekday()) 1040 print( "today:", Week.today(date.today())) 1041 today: Week.Mon 1042 \end{python} 1043 The method @today@ retrieves the day of the week and uses it as an index to print out the corresponding label of @Week@. 1076 1044 1077 1045 @Flag@ allows combining several members into a single variable: 1078 1046 \begin{python} 1079 print( repr(Week dayF.Sat | WeekdayF.Sun) )1080 <Week dayF.Sun|Sat: 96>1047 print( repr(WeekF.Sat | WeekF.Sun) ) 1048 <WeekF.Sun|Sat: 96> 1081 1049 \end{python} 1082 1050 You can even iterate over a @Flag@ variable: … … 1084 1052 for day in weekend: 1085 1053 print(day) 1086 Week day.SATURDAY1087 Week day.SUNDAY1054 WeekF.Sat 1055 WeekF.Sun 1088 1056 \end{python} 1089 1057 Okay, let's get some chores set up: 1090 1058 \begin{python} 1091 1059 >>> chores_for_ethan = { 1092 ... 'feed the cat': Week day.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY,1093 ... 'do the dishes': Week day.TUESDAY | Weekday.THURSDAY,1094 ... 'answer SO questions': Week day.SATURDAY,1060 ... 'feed the cat': Week.MONDAY | Week.WEDNESDAY | Week.FRIDAY, 1061 ... 'do the dishes': Week.TUESDAY | Week.THURSDAY, 1062 ... 'answer SO questions': Week.SATURDAY, 1095 1063 ... } 1096 1064 \end{python} … … 1101 1069 ... if day in days: 1102 1070 ... print(chore) 1103 >>> show_chores(chores_for_ethan, Week day.SATURDAY)1071 >>> show_chores(chores_for_ethan, Week.SATURDAY) 1104 1072 answer SO questions 1105 1073 \end{python} 1106 In cases where the actual values of the members do not matter, you can save yourself some work and use @auto()@ for the values: 1107 \begin{python} 1108 >>> from enum import auto 1109 >>> class Weekday(Flag): 1110 ... MONDAY = auto() 1111 ... TUESDAY = auto() 1112 ... WEDNESDAY = auto() 1113 ... THURSDAY = auto() 1114 ... FRIDAY = auto() 1115 ... SATURDAY = auto() 1116 ... SUNDAY = auto() 1117 ... WEEKEND = SATURDAY | SUNDAY 1074 Auto incrmenet for @Flag@ is by powers of 2. 1075 \begin{python} 1076 class WeekF(Flag): Mon = auto(); Tue = auto(); Wed = auto(); Thu = auto(); Fri = auto(); \ 1077 Sat = auto(); Sun = auto(); Weekend = Sat | Sun 1078 for d in WeekF: 1079 print( f"{d.name}: {d.value}", end=" ") 1080 Mon: 1 Tue: 2 Wed: 4 Thu: 8 Fri: 16 Sat: 32 Sun: 64 WeekA.Weekend 1118 1081 \end{python} 1119 1082 … … 1123 1086 @Enum@ allows such access: 1124 1087 \begin{python} 1125 >>> Color(1) 1126 <Color.RED: 1> 1127 >>> Color(3) 1128 <Color.BLUE: 3> 1088 print(RGB(1), RGB(3), ) 1089 RGB.RED RGB.GREEN 1129 1090 \end{python} 1130 1091 If you want to access enum members by name, use item access: 1131 1092 \begin{python} 1132 Color['RED'] 1133 <Color.RED: 1> 1134 1135 Color['GREEN'] 1136 <Color.GREEN: 2> 1093 print( RGBa['RED'], RGBa['GREEN'] ) 1094 RGB.RED RGB.GREEN 1137 1095 \end{python} 1138 1096 If you have an enum member and need its name or value: 1139 1097 \begin{python} 1140 >>> member = Color.RED 1141 >>> member.name 1142 'RED' 1143 >>> member.value 1144 1 1145 \end{python} 1146 1147 \subsection{Duplicating enum members and values} 1148 1149 An enum member can have other names associated with it. 1150 Given two entries @A@ and @B@ with the same value (and @A@ defined first), @B@ is an alias for the member @A@. 1151 By-value lookup of the value of @A@ will return the member @A@. 1152 By-name lookup of @A@ will return the member @A@. 1153 By-name lookup of @B@ will also return the member @A@: 1154 \begin{python} 1155 class Shape(Enum): SQUARE = 2; DIAMOND = 1; CIRCLE = 3; ALIAS_FOR_SQUARE = 2 1156 >>> Shape.SQUARE 1157 <Shape.SQUARE: 2> 1158 >>> Shape.ALIAS_FOR_SQUARE 1159 <Shape.SQUARE: 2> 1160 >>> Shape(2) 1161 <Shape.SQUARE: 2> 1162 \end{python} 1163 1164 Note: Attempting to create a member with the same name as an already defined attribute (another member, a method, etc.) or attempting to create an attribute with the same name as a member is not allowed. 1098 member = RGBa.RED 1099 print( f"{member.name} {member.value}" ) 1100 RED 1 1101 \end{python} 1102 1165 1103 1166 1104 \subsection{Ensuring unique enumeration values} … … 1207 1145 >>> list(Shape) 1208 1146 [<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>] 1209 >>> list(Week day)1210 [<Week day.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>,1211 <Week day.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>]1212 \end{python} 1213 Note that the aliases @Shape.ALIAS_FOR_SQUARE@ and @Week day.WEEKEND@ aren't shown.1147 >>> list(Week) 1148 [<Week.MONDAY: 1>, <Week.TUESDAY: 2>, <Week.WEDNESDAY: 4>, <Week.THURSDAY: 8>, 1149 <Week.FRIDAY: 16>, <Week.SATURDAY: 32>, <Week.SUNDAY: 64>] 1150 \end{python} 1151 Note that the aliases @Shape.ALIAS_FOR_SQUARE@ and @Week.WEEKEND@ aren't shown. 1214 1152 1215 1153 The special attribute @__members__@ is a read-only ordered mapping of names to members. … … 2212 2150 2213 2151 \section{OCaml} 2214 \lstnewenvironment{ocaml}[1][]{\lstset{language=OCaml,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{}2215 2152 2216 2153 % https://ocaml.org/docs/basic-data-types#enumerated-data-types … … 2218 2155 2219 2156 OCaml provides a variant (union) type, where multiple heterogeneously-typed objects share the same storage. 2220 The simplest form of the variant type is a list of nullary datatype constructors, which is like an unscoped, pure enumeration. 2221 2222 (I think the value of a ocaml variants are types not object, so I am not sure about this line) 2157 The simplest form of the variant type is a list of nullary datatype constructors, which is like an unscoped, opaque enumeration. 2158 2223 2159 OCaml provides a variant (union) type, which is an aggregation of heterogeneous types. 2224 A basic variant is a list of nullary datatype constructors, which is like an unscoped, pure enumeration.2160 A basic variant is a list of nullary datatype constructors, which is like an unscoped, opaque enumeration. 2225 2161 \begin{ocaml} 2226 2162 type weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun … … 2246 2182 type colour = Red | Green of @string@ | Blue of @int * float@ 2247 2183 \end{ocaml} 2248 A variant with parameter is stored in a memory block, prefixed by an int tag and has its parameters stores as words in the block. 2184 A variant with parameter is stored in a memory block, prefixed by an int tag and has its parameters stores as words in the block. 2249 2185 @colour@ is a summation of a nullary type, a unary product type of @string@, and a cross product of @int@ and @float@. 2250 2186 (Mathematically, a @Blue@ value is a Cartesian product of the types @int@ type and @float@.) … … 2259 2195 @Red, abc, 1 1.5@ 2260 2196 \end{ocaml} 2261 2262 2197 2263 2198 A variant type can have a recursive definition. … … 2280 2215 2281 2216 In summary, an OCaml variant is a singleton value rather than a set of possibly ordered values, and hence, has no notion of enumerabilty. 2282 Therefore it is not an enumeration, except for the simple pure (nullary) case.2217 Therefore it is not an enumeration, except for the simple opaque (nullary) case. 2283 2218 2284 2219 \begin{comment} … … 2466 2401 With valediction, 2467 2402 - Gregor Richards 2403 2404 2405 Date: Tue, 16 Apr 2024 11:04:51 -0400 2406 Subject: Re: C unnamed enumeration 2407 To: "Peter A. Buhr" <pabuhr@uwaterloo.ca> 2408 CC: <ajbeach@uwaterloo.ca>, <j82liang@uwaterloo.ca>, <mlbrooks@uwaterloo.ca>, 2409 <f37yu@uwaterloo.ca> 2410 From: Gregor Richards <gregor.richards@uwaterloo.ca> 2411 2412 On 4/16/24 09:55, Peter A. Buhr wrote: 2413 > So what is a variant? Is it a set of tag names, which might be a union or is it 2414 > a union, which might have tag names? 2415 2416 Your tagless variant bears no resemblance to variants in any functional 2417 programming language. A variant is a tag AND a union. You might not need to put 2418 anything in the union, in which case it's a pointless union, but the named tag 2419 is absolutely mandatory. That's the thing that varies. 2420 2421 I was unaware of std::variant. As far as functional languages are concerned, 2422 std::variant IS NOT A VARIANT. Perhaps it would be best to use the term ADT for 2423 the functional language concept, because that term has no other meanings. 2424 2425 An ADT cannot not have a named tag. That's meaningless. The tag is the data 2426 constructor, which is the thing you actually define when you define an ADT. It 2427 is strictly the union that's optional. 2428 2429 With valediction, 2430 - Gregor Richards 2468 2431 \end{comment} 2469 2432 … … 2487 2450 \hline 2488 2451 \hline 2489 pure & & & & & & & & & & & & & \CM \\2452 opaque & & & & & & & & & & & & & \CM \\ 2490 2453 \hline 2491 2454 typed & & & & & & & & & & & @int@ & integral & @T@ \\ -
doc/theses/jiada_liang_MMath/uw-ethesis.bib
rcf191ac r7042c60 2 2 % For use with BibTeX 3 3 4 Oxford English Dictionary, s.v. ``enumerate (v.), sense 3,'' September 2023, https://doi.org/10.1093/OED/1113960777. 5 @misc{OEDenumerate, 6 keywords = {enumerate}, 7 key = {enumerate}, 8 title = {enumerate (v.), sense 3}, 9 author = {Oxford English Dictionary}, 10 howpublished= {\url{https://doi.org/10.1093/OED/1113960777}}, 11 month = sep, 12 year = 2023, 13 } -
doc/theses/jiada_liang_MMath/uw-ethesis.tex
rcf191ac r7042c60 95 95 \CFAStyle % CFA code-style 96 96 \lstset{language=cfa,belowskip=-1pt} % set default language to CFA 97 \lstnewenvironment{ada}[1][]{\lstset{language=[2005]Ada,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},literate={'}{\ttfamily'\!}1}\lstset{#1}}{} 98 \lstnewenvironment{c++}[1][]{\lstset{language=[GNU]C++,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 99 \lstnewenvironment{pascal}[1][]{\lstset{language=pascal,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 100 \lstnewenvironment{csharp}[1][]{\lstset{language=[Sharp]C,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 101 \lstnewenvironment{clang}[1][]{\lstset{language=[ANSI]C,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 102 \lstnewenvironment{Go}[1][]{\lstset{language=Go,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 103 \lstnewenvironment{haskell}[1][]{\lstset{language=Haskell,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 104 \lstnewenvironment{Java}[1][]{\lstset{language=Java,morekeywords={enum,assert,strictfp}, 105 escapechar=\$,moredelim=**[is][\color{red}]{!}{!},}\lstset{#1}}{} 106 \lstnewenvironment{rust}[1][]{\lstset{language=Rust,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 107 \lstnewenvironment{swift}[1][]{\lstset{language=Swift,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 108 \lstnewenvironment{python}[1][]{\lstset{language=Python,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 109 \lstnewenvironment{ocaml}[1][]{\lstset{language=OCaml,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} 110 111 \newsavebox{\myboxA} 112 \newsavebox{\myboxB} 97 113 98 114 \newcommand{\newtermFont}{\emph} -
doc/uC++toCFA/Makefile
rcf191ac r7042c60 56 56 dvips ${Build}/$< -o $@ 57 57 58 ${BASE}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} \59 ${Macros}/ common.sty ${Macros}/lstlang.sty ${Macros}/indexstyle ../bibliography/pl.bib build/version | ${Build}58 ${BASE}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} ${Macros}/common.tex ${Macros}/common.sty \ 59 ${Macros}/lstlang.sty ${Macros}/indexstyle ../bibliography/pl.bib build/version | ${Build} 60 60 # Conditionally create an empty *.ind (index) file for inclusion until makeindex is run. 61 61 if [ ! -r ${basename $@}.ind ] ; then touch ${Build}/${basename $@}.ind ; fi -
doc/uC++toCFA/uC++toCFA.tex
rcf191ac r7042c60 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Thu Jan 11 14:46:14202414 %% Update Count : 59 4213 %% Last Modified On : Sat Apr 13 11:11:39 2024 14 %% Update Count : 5969 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 141 141 \CFA uses parametric polymorphism and allows overloading of variables and routines: 142 142 \begin{cfa} 143 int i; char i; double i; // overload name i143 int i; char i; double i; $\C[2in]{// overload name i}$ 144 144 int i(); double i(); char i(); 145 i += 1; $\C[1.5in]{// int i}$146 i += 1.0; $\C{// double i}$147 i += 'a'; $\C{// char i}$148 int j = i(); $\C{// int i()}$149 double j = i(); $\C{// double i();}$150 char j = i(); $\C{// char i()}\CRT$145 i += 1; $\C{// int i}$ 146 i += 1.0; $\C{// double i}$ 147 i += 'a'; $\C{// char i}$ 148 int j = i(); $\C{// int i()}$ 149 double j = i(); $\C{// double i();}$ 150 char j = i(); $\C{// char i()}\CRT$ 151 151 \end{cfa} 152 152 \CFA has rebindable references. 153 154 \begin{cquote} 155 \begin{tabular}{l|l} 156 \multicolumn{2}{l}{\lstinline{ int x = 1, y = 2, * p1x = &x, * p1y = &y, ** p2i = &p1x,}} \\ 157 \multicolumn{2}{l}{\lstinline{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ && r1x = x, & r1y = y, && r2i = r1x;}} \\ 153 \begin{cquote} 154 \begin{tabular}{@{}l|l@{}} 155 \multicolumn{2}{@{}l}{\lstinline{ int x = 1, y = 2, * p1x = &x, * p1y = &y, ** p2i = &p1x,}} \\ 156 \multicolumn{2}{@{}l}{\lstinline{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ && r1x = x, & r1y = y, && r2i = r1x;}} \\ 158 157 \begin{uC++} 159 158 **p2i = 3; … … 201 200 202 201 \CFA output streams automatically separate values and insert a newline at the end of the print. 203 204 \begin{cquote} 205 \begin{tabular}{l|l} 202 \begin{cquote} 203 \begin{tabular}{@{}l|l@{}} 206 204 \begin{uC++} 207 205 #include <@iostream@> … … 226 224 227 225 \begin{cquote} 228 \begin{tabular}{ l|l}226 \begin{tabular}{@{}l|l@{}} 229 227 \begin{uC++} 230 228 for ( @;;@ ) { ... } / while ( @true@ ) { ... } … … 280 278 Currently, \CFA uses macros @ExceptionDecl@ and @ExceptionInst@ to declare and instantiate an exception. 281 279 \begin{cquote} 282 \begin{tabular}{ l|ll}280 \begin{tabular}{@{}l|ll@{}} 283 281 \begin{uC++} 284 282 … … 321 319 322 320 \begin{cquote} 323 \begin{tabular}{ l|ll}321 \begin{tabular}{@{}l|ll@{}} 324 322 \begin{uC++} 325 323 … … 360 358 361 359 \begin{cquote} 362 \begin{tabular}{ l|l}360 \begin{tabular}{@{}l|l@{}} 363 361 \begin{uC++} 364 362 struct S { … … 383 381 384 382 \begin{cquote} 385 \begin{tabular}{ l|l}386 \multicolumn{2}{ l}{\lstinline{string s1, s2;}} \\383 \begin{tabular}{@{}l|l@{}} 384 \multicolumn{2}{@{}l@{}}{\lstinline{string s1, s2;}} \\ 387 385 \begin{uC++} 388 386 s1 = "hi"; … … 425 423 426 424 \begin{cquote} 427 \begin{tabular}{ l|l}425 \begin{tabular}{@{}l|l@{}} 428 426 \begin{uC++} 429 427 struct S { … … 456 454 457 455 \begin{cquote} 458 \begin{tabular}{ l|l}456 \begin{tabular}{@{}l|l@{}} 459 457 \begin{uC++} 460 458 … … 493 491 494 492 \begin{cquote} 495 \begin{tabular}{ l|ll}493 \begin{tabular}{@{}l|ll@{}} 496 494 \begin{uC++} 497 495 … … 532 530 533 531 \begin{cquote} 534 \begin{tabular}{ l|ll}532 \begin{tabular}{@{}l|ll@{}} 535 533 \begin{uC++} 536 534 … … 567 565 568 566 \begin{cquote} 569 \begin{tabular}{ l|ll}567 \begin{tabular}{@{}l|ll@{}} 570 568 \begin{uC++} 571 569 … … 604 602 605 603 \begin{cquote} 606 \begin{tabular}{ l|ll}604 \begin{tabular}{@{}l|ll@{}} 607 605 \begin{uC++} 608 606 -
doc/user/Makefile
rcf191ac r7042c60 60 60 dvips ${Build}/$< -o $@ 61 61 62 ${BASE}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} \63 ${Macros}/ common.sty ${Macros}/lstlang.sty ${Macros}/indexstyle ../bibliography/pl.bib build/version | ${Build}62 ${BASE}.dvi : Makefile ${GRAPHS} ${PROGRAMS} ${PICTURES} ${FIGURES} ${SOURCES} ${Macros}/common.tex ${Macros}/common.sty \ 63 ${Macros}/lstlang.sty ${Macros}/indexstyle ../bibliography/pl.bib build/version | ${Build} 64 64 # Conditionally create an empty *.ind (index) file for inclusion until makeindex is run. 65 65 if [ ! -r ${basename $@}.ind ] ; then touch ${Build}/${basename $@}.ind ; fi … … 73 73 makeindex -s ${Macros}/indexstyle ${Build}/${basename $@}.idx 74 74 # Run again to finish citations 75 ${LaTeX} ${basename $@}.tex75 # ${LaTeX} ${basename $@}.tex 76 76 # Run again to get index title into table of contents 77 77 # ${LaTeX} ${basename $@}.tex -
doc/user/user.tex
rcf191ac r7042c60 11 11 %% Created On : Wed Apr 6 14:53:29 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Mon Feb 12 11:50:26202414 %% Update Count : 6 19913 %% Last Modified On : Tue Apr 23 14:13:10 2024 14 %% Update Count : 6623 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 69 69 \lstset{language=CFA} % CFA default lnaguage 70 70 \lstnewenvironment{C++}[1][] % use C++ style 71 {\lstset{language=C++, moredelim=**[is][\protect\color{red}]{®}{®},#1}}71 {\lstset{language=C++,escapechar=§,moredelim=**[is][\protect\color{red}]{®}{®},#1}} 72 72 {} 73 73 … … 130 130 \vspace*{\fill} 131 131 \noindent 132 \copyright\,2016, 2018, 2021 \CFA Project \\ \\132 \copyright\,2016, 2018, 2021, 2024 \CFA Project \\ \\ 133 133 \noindent 134 134 This work is licensed under the Creative Commons Attribution 4.0 International License. … … 312 312 For example, it is possible to write a type-safe \CFA wrapper ©malloc© based on the C ©malloc©: 313 313 \begin{cfa} 314 forall( dtype T| sized(T) ) T * malloc( void ) { return (T *)malloc( sizeof(T) ); }314 forall( T & | sized(T) ) T * malloc( void ) { return (T *)malloc( sizeof(T) ); } 315 315 int * ip = malloc(); §\C{// select type and size from left-hand side}§ 316 316 double * dp = malloc(); … … 1023 1023 while () { sout | "empty"; break; } 1024 1024 do { sout | "empty"; break; } while (); 1025 for () { sout | "empty"; break; } §\C{sout | nl | nlOff;}§1026 1027 for ( 0 ) { sout | "A"; } sout | "zero"; 1028 for ( 1 ) { sout | "A"; } 1029 for ( 10 ) { sout | "A"; } 1030 for ( ~= 10 ) { sout | "A"; } 1031 for ( 1 ~= 10 ~ 2 ) { sout | "B"; } 1032 for ( 1 -~= 10 ~ 2 ) { sout | "C"; } 1033 for ( 0.5 ~ 5.5 ) { sout | "D"; } 1034 for ( 0.5 -~ 5.5 ) { sout | "E"; } 1035 for ( i; 10 ) { sout | i; } 1036 for ( i; ~= 10 ) { sout | i; } 1037 for ( i; 1 ~= 10 ~ 2 ) { sout | i; } 1038 for ( i; 1 -~= 10 ~ 2 ) { sout | i; } 1039 for ( i; 0.5 ~ 5.5 ) { sout | i; } 1040 for ( i; 0.5 -~ 5.5 ) { sout | i; } 1041 for ( ui; 2u ~= 10u ~ 2u ) { sout | ui; } 1042 for ( ui; 2u -~= 10u ~ 2u ) { sout | ui; } 1025 for () { sout | "empty"; break; } §\C[3in]{sout | nl | nlOff;}§ 1026 1027 for ( 0 ) { sout | "A"; } sout | "zero"; §\C{sout | nl;}§ 1028 for ( 1 ) { sout | "A"; } §\C{sout | nl;}§ 1029 for ( 10 ) { sout | "A"; } §\C{sout | nl;}§ 1030 for ( ~= 10 ) { sout | "A"; } §\C{sout | nl;}§ 1031 for ( 1 ~= 10 ~ 2 ) { sout | "B"; } §\C{sout | nl;}§ 1032 for ( 1 -~= 10 ~ 2 ) { sout | "C"; } §\C{sout | nl;}§ 1033 for ( 0.5 ~ 5.5 ) { sout | "D"; } §\C{sout | nl;}§ 1034 for ( 0.5 -~ 5.5 ) { sout | "E"; } §\C{sout | nl;}§ 1035 for ( i; 10 ) { sout | i; } §\C{sout | nl;}§ 1036 for ( i; ~= 10 ) { sout | i; } §\C{sout | nl;}§ 1037 for ( i; 1 ~= 10 ~ 2 ) { sout | i; } §\C{sout | nl;}§ 1038 for ( i; 1 -~= 10 ~ 2 ) { sout | i; } §\C{sout | nl;}§ 1039 for ( i; 0.5 ~ 5.5 ) { sout | i; } §\C{sout | nl;}§ 1040 for ( i; 0.5 -~ 5.5 ) { sout | i; } §\C{sout | nl;}§ 1041 for ( ui; 2u ~= 10u ~ 2u ) { sout | ui; } §\C{sout | nl;}§ 1042 for ( ui; 2u -~= 10u ~ 2u ) { sout | ui; } §\C{sout | nl | nl | nl;}§ 1043 1043 1044 1044 enum { N = 10 }; 1045 for ( N ) { sout | "N"; } 1046 for ( i; N ) { sout | i; } 1047 for ( i; -~ N ) { sout | i; } 1045 for ( N ) { sout | "N"; } §\C{sout | nl;}§ 1046 for ( i; N ) { sout | i; } §\C{sout | nl;}§ 1047 for ( i; -~ N ) { sout | i; } §\C{sout | nl | nl | nl;}§ 1048 1048 1049 1049 const int low = 3, high = 10, inc = 2; 1050 for ( i; low ~ high ~ inc + 1 ) { sout | i; } 1051 for ( i; 1 ~ @ ) { if ( i > 10 ) break; sout | i; } 1052 for ( i; @ -~ 10 ) { if ( i < 0 ) break; sout | i; } 1053 for ( i; 2 ~ @ ~ 2 ) { if ( i > 10 ) break; sout | i; } 1050 for ( i; low ~ high ~ inc + 1 ) { sout | i; } §\C{sout | nl;}§ 1051 for ( i; 1 ~ @ ) { if ( i > 10 ) break; sout | i; } §\C{sout | nl;}§ 1052 for ( i; @ -~ 10 ) { if ( i < 0 ) break; sout | i; } §\C{sout | nl;}§ 1053 for ( i; 2 ~ @ ~ 2 ) { if ( i > 10 ) break; sout | i; } §\C{sout | nl;}§ 1054 1054 for ( i; 2.1 ~ @ ~ @ ) { if ( i > 10.5 ) break; sout | i; i += 1.7; } §\C{sout | nl;}§ 1055 1055 for ( i; @ -~ 10 ~ 2 ) { if ( i < 0 ) break; sout | i; } §\C{sout | nl;}§ 1056 1056 for ( i; 12.1 ~ @ ~ @ ) { if ( i < 2.5 ) break; sout | i; i -= 1.7; } §\C{sout | nl;}§ 1057 for ( i; 5 : j; -5 ~ @ ) { sout | i | j; } 1058 for ( i; 5 : j; @ -~ -5 ) { sout | i | j; } 1059 for ( i; 5 : j; -5 ~ @ ~ 2 ) { sout | i | j; } 1060 for ( i; 5 : j; @ -~ -5 ~ 2 ) { sout | i | j; } 1061 for ( i; 5 : j; -5 ~ @ ) { sout | i | j; } 1062 for ( i; 5 : j; @ -~ -5 ) { sout | i | j; } 1063 for ( i; 5 : j; -5 ~ @ ~ 2 ) { sout | i | j; } 1064 for ( i; 5 : j; @ -~ -5 ~ 2 ) { sout | i | j; } 1057 for ( i; 5 : j; -5 ~ @ ) { sout | i | j; } §\C{sout | nl;}§ 1058 for ( i; 5 : j; @ -~ -5 ) { sout | i | j; } §\C{sout | nl;}§ 1059 for ( i; 5 : j; -5 ~ @ ~ 2 ) { sout | i | j; } §\C{sout | nl;}§ 1060 for ( i; 5 : j; @ -~ -5 ~ 2 ) { sout | i | j; } §\C{sout | nl;}§ 1061 for ( i; 5 : j; -5 ~ @ ) { sout | i | j; } §\C{sout | nl;}§ 1062 for ( i; 5 : j; @ -~ -5 ) { sout | i | j; } §\C{sout | nl;}§ 1063 for ( i; 5 : j; -5 ~ @ ~ 2 ) { sout | i | j; } §\C{sout | nl;}§ 1064 for ( i; 5 : j; @ -~ -5 ~ 2 ) { sout | i | j; } §\C{sout | nl;}§ 1065 1065 for ( i; 5 : j; @ -~ -5 ~ 2 : k; 1.5 ~ @ ) { sout | i | j | k; } §\C{sout | nl;}§ 1066 1066 for ( i; 5 : j; @ -~ -5 ~ 2 : k; 1.5 ~ @ ) { sout | i | j | k; } §\C{sout | nl;}§ 1067 for ( i; 5 : k; 1.5 ~ @ : j; @ -~ -5 ~ 2 ) { sout | i | j | k; } §\C{sout | nl;} §1067 for ( i; 5 : k; 1.5 ~ @ : j; @ -~ -5 ~ 2 ) { sout | i | j | k; } §\C{sout | nl;}\CRT§ 1068 1068 \end{cfa} 1069 1069 & … … 2960 2960 The string ``©int (*f(x))[ 5 ]©'' declares a K\&R style routine of type returning a pointer to an array of 5 integers, while the string ``©[ 5 ] int x©'' declares a \CFA style parameter ©x© of type array of 5 integers. 2961 2961 Since the strings overlap starting with the open bracket, ©[©, there is an ambiguous interpretation for the string. 2962 2962 2963 As well, \CFA-style declarations cannot be used to declare parameters for C-style routine-definitions because of the following ambiguity: 2963 2964 \begin{cfa} … … 2965 2966 int f( int (* foo) ); §\C{// foo is redefined as a parameter name}§ 2966 2967 \end{cfa} 2967 The string ``©int (* foo)©'' declares a C-style named-parameter of type pointer to an integer (the parenthesis are superfluous), while the same string declares a \CFA style unnamed parameter of type routine returning integer with unnamed parameter of type pointer to foo.2968 The string ``©int (* foo)©'' declares a C-style named-parameter of type pointer to an integer (the parenthesis are superfluous), while the same string declares a \CFA style unnamed parameter of type routine returning integer with unnamed parameter of type pointer to ©foo©. 2968 2969 The redefinition of a type name in a parameter list is the only context in C where the character ©*© can appear to the left of a type name, and \CFA relies on all type qualifier characters appearing to the right of the type name. 2969 2970 The inability to use \CFA declarations in these two contexts is probably a blessing because it precludes programmers from arbitrarily switching between declarations forms within a declaration contexts. … … 3055 3056 static [ int ] g ( int ); 3056 3057 \end{cfa} 3058 3059 3060 \subsection{Postfix Function} 3061 \label{s:PostfixFunction} 3062 \index{postfix function} 3063 3064 \CFA provides an alternative call syntax where the argument appears before the function name. 3065 The syntax uses the backquote ©`© to separate the parameters/arguments and function name: ©?`© denotes a postfix-function name, \eg ©int ?`h( int s )© and ©`© denotes a postfix-function call, \eg ©0`h© meaning ©h( 0 )©. 3066 \begin{cquote} 3067 \begin{tabular}{@{}l|l|l|l@{}} 3068 postfix function & constant argument call & variable argument call & postfix function pointer \\ 3069 \hline 3070 \begin{cfa} 3071 int ?`h( int s ); 3072 int ?`h( double s ); 3073 int ?`m( char c ); 3074 int ?`m( const char * s ); 3075 int ?`t( int a, int b, int c ); 3076 \end{cfa} 3077 & 3078 \begin{cfa} 3079 0`h; 3080 3.5`h; 3081 '1'`m; 3082 "123" "456"`m; 3083 [1, 2, 3]`t; 3084 \end{cfa} 3085 & 3086 \begin{cfa} 3087 int i = 7; 3088 i`h; 3089 (i + 3)`h; 3090 (i + 3.5)`h; 3091 \end{cfa} 3092 & 3093 \begin{cfa} 3094 int (* ?`p)( int i ); 3095 ?`p = ?`h; 3096 3`p; 3097 i`p; 3098 (i + 3)`p; 3099 \end{cfa} 3100 \end{tabular} 3101 \end{cquote} 3102 Note, to pass \emph{multiple} arguments to a postfix function requires a \Index{tuple}, \eg ©[1, 2, 3]`t©, which forms a single argument that is flattened into the multiple arguments \see{\VRef{s:Tuple}}. 3103 Similarly, if the argument is an expression, it must be parenthesized, \eg ©(i + 3)`h©, or only the last operand of the expression is the argument, \eg ©i + (3`h)©. 3104 3105 \VRef[Figure]{f:UnitsComparison} shows a common usage for postfix functions: converting basic literals into user literals. 3106 \see*{\VRef{s:DynamicStorageManagement} for other uses for postfix functions.} 3107 The \CFA example (left) stores a mass in units of stones (1 stone = 14 lb or 6.35 kg) and provides an addition operator (imagine a full set of arithmetic operators). 3108 The three postfixing function names ©st©, ©lb©, and ©kg©, represent units stones, pounds, and kilograms, respectively. 3109 Each name has two forms that bidirectional convert: a value of a specified unit to stones, \eg ©w = 14`lb© $\Rightarrow$ ©w == 1© stone or a ©Weight© from stones back to specific units, \eg ©w`lb© (1 stone) to ©14©. 3110 All arithmetic operations manipulate stones and the postfix operations convert to the different units. 3111 A similar group of postfix functions provide user constants for converting time units into nanoseconds, which is the basic time unit, \eg ©ns©, ©us©, ©ms©, ©s©, ©m©, ©h©, ©d©, and ©w©, for nanosecond, microsecond, millisecond, second, minute, hour, day, and week, respectively. 3112 (Note, month is not a fixed period of time nor is year because of leap years.) 3113 3114 \begin{figure} 3115 \centering 3116 \begin{tabular}{@{}l|l@{}} 3117 \multicolumn{1}{@{}c|}{\textbf{\CFA Postfix Routine}} & \multicolumn{1}{c@{}}{\textbf{\CC User Literals}} \\ 3118 \hline 3119 \begin{cfa} 3120 struct Weight { 3121 double stones; 3122 }; 3123 3124 3125 Weight ?+?( Weight l, Weight r ) { 3126 return l.stones + r.stones; 3127 } 3128 Weight ®?`st®( double w ) { return w; } 3129 double ®?`st®( Weight w ) { return w.stones; } 3130 Weight ®?`lb®( double w ) { return w / 14.0; } 3131 double ®?`lb®( Weight w ) { return w.stones * 14.0; } 3132 Weight ®?`kg®( double w ) { return w / 6.35; } 3133 double ®?`kg®( Weight w ) { return w.stones * 6.35; } 3134 int main() { 3135 Weight w, heavy = { 20 }; // stones 3136 w = 155®`lb®; 3137 w = 0b_1111®`st®; 3138 w = 0_233®`lb®; 3139 w = 0x_9b®`kg®; 3140 w = 5.5®`st® + 8®`kg® + 25.01®`lb® + heavy; 3141 } 3142 \end{cfa} 3143 & 3144 \begin{C++} 3145 struct Weight { 3146 double stones; 3147 Weight() {} 3148 Weight( double w ) { stones = w; } 3149 }; 3150 Weight operator+( Weight l, Weight r ) { 3151 return l.stones + r.stones; 3152 } 3153 Weight operator®""_st®( long double w ) { return w; } 3154 Weight operator®""_lb®( long double w ) { return w / 14.0; } 3155 Weight operator®""_kg®( long double w ) { return w / 6.35; } 3156 Weight operator®""_st®( unsigned long long int w ) { return w; } 3157 Weight operator®""_lb®( unsigned long long int w ) { return w / 14.0; } 3158 Weight operator®""_kg®( unsigned long long int w ) { return w / 6.35; } 3159 int main() { 3160 Weight w, heavy = { 20 }; // stones 3161 w = 155®_lb®; 3162 w = 0b1111®_st®; 3163 w = 0'233®_lb®; // quote separator 3164 w = 0x9b®_kg®; 3165 w = 5.5®_st® + 8®_kg® + 25.01®_lb® + heavy; 3166 } 3167 \end{C++} 3168 \end{tabular} 3169 3170 \begin{comment} 3171 Time : comparison of time units. \\ 3172 \begin{tabular}{@{}ll@{}} 3173 \CFA & \CC \\ 3174 \begin{cfa} 3175 #include <fstream.hfa> 3176 #include <time.hfa> 3177 3178 3179 Duration s = 1`h + 2 * 10`m + 70`s / 10; 3180 sout | "1 hour + 2*10 min + 70/10 sec = " | s | "seconds"; 3181 sout | "Dividing that by 2 minutes gives" | s / 2`m; 3182 sout | "Dividing that by 2 gives" | s / 2 | "seconds\n"; 3183 sout | s | "seconds is" 3184 | s`h | "hours," 3185 | (s % 1`h)`m | "minutes," 3186 | (s % 1`m)`s | "seconds"; 3187 \end{cfa} 3188 & 3189 \begin{C++} 3190 #include <iostream> 3191 #include <chrono> 3192 using namespace std; 3193 using namespace std::chrono; 3194 seconds s = hours(1) + 2 * minutes(10) + seconds(70) / 10; 3195 cout << "1 hour + 2*10 min + 70/10 sec = " << s.count() << " seconds\n"; 3196 cout << "Dividing that by 2 minutes gives " << s / minutes(2) << '\n'; 3197 cout << "Dividing that by 2 gives " << (s / 2).count() << " seconds\n"; 3198 cout << s.count() << " seconds is " 3199 << duration_cast<hours>( s ).count() << " hours, " 3200 << duration_cast<minutes>( s % hours(1) ).count() << " minutes, " 3201 << duration_cast<seconds>( s % minutes(1) ).count() << " seconds\n"; 3202 \end{C++} 3203 \end{tabular} 3204 \end{comment} 3205 3206 \caption{Units: Stone, Pound, Kilogram Comparison} 3207 \label{f:UnitsComparison} 3208 \end{figure} 3209 3210 The \CC example (right) provides a \emph{restricted} capability via user literals. 3211 The \lstinline[language=C++]{operator ""} only takes a constant argument (\ie no variable argument), and the constant type must be the highest-level constant-type, \eg ©long double© for all floating-point constants. 3212 As well, there is no constant conversion, \ie ©int© to ©double© constants, so integral constants are handled by a separate set of routines, with maximal integral type ©unsigned long long int©. 3213 Finally, there is no mechanism to use this syntax for a bidirectional conversion because \lstinline[language=C++]{operator ""} does not accept variable arguments. 3057 3214 3058 3215 … … 3389 3546 3390 3547 \section{Tuple} 3548 \label{s:Tuple} 3391 3549 3392 3550 In C and \CFA, lists of elements appear in several contexts, such as the parameter list of a routine call. … … 3809 3967 \subsection{Polymorphism} 3810 3968 3811 Due to the implicit flattening and structuring conversions involved in argument passing, ©otype© and ©dtype©parameters are restricted to matching only with non-tuple types.3969 Due to the implicit flattening and structuring conversions involved in argument passing, object and opaque parameters are restricted to matching only with non-tuple types. 3812 3970 The integration of polymorphism, type assertions, and monomorphic specialization of tuple-assertions are a primary contribution of this thesis to the design of tuples. 3813 3971 \begin{cfa} 3814 forall(T, dtype U)3972 forall(T, U &) 3815 3973 void f(T x, U * y); 3816 3974 … … 4047 4205 [ §\emph{lvalue}§, ... , §\emph{lvalue}§ ] = §\emph{expr}§; 4048 4206 \end{cfa} 4049 \index{lvalue} 4050 The left-hand side is a tuple of \LstBasicStyle{\emph{lvalues}}, which is a list of expressions each yielding an address, \ie any data object that can appear on the left-hand side of a conventional assignment statement. 4207 The left-hand side is a tuple of \LstBasicStyle{\emph{\Index{lvalue}}}s, which is a list of expressions each yielding an address, \ie any data object that can appear on the left-hand side of a conventional assignment statement. 4051 4208 \LstBasicStyle{\emph{expr}} is any standard arithmetic expression. 4052 4209 Clearly, the types of the entities being assigned must be type compatible with the value of the expression. … … 4086 4243 Multiple assignment has the following form: 4087 4244 \begin{cfa} 4088 [ §\emph{lvalue}§, ... , §\emph{lvalue}§ ] = [ §\emph{expr}§, ... , §\emph{expr}§ ]; 4089 \end{cfa} 4090 \index{lvalue} 4091 The left-hand side is a tuple of \emph{lvalues}, and the right-hand side is a tuple of \emph{expr}s. 4092 Each \emph{expr} appearing on the right-hand side of a multiple assignment statement is assigned to the corresponding \emph{lvalues} on the left-hand side of the statement using parallel semantics for each assignment. 4245 [ §\emph{lvalue}§, ... , §\emph{lvalue}§ ] = [ §\emph{expr}§, ... , §\emph{expr}§ ];§ 4246 \end{cfa} 4247 The left-hand side is a tuple of \LstBasicStyle{\emph{\Index{lvalue}}}s, and the right-hand side is a tuple of \LstBasicStyle{\emph{expr}}s. 4248 Each \LstBasicStyle{\emph{expr}} appearing on the right-hand side of a multiple assignment statement is assigned to the corresponding \LstBasicStyle{\emph{lvalues}} on the left-hand side of the statement using parallel semantics for each assignment. 4093 4249 An example of multiple assignment is: 4094 4250 \begin{cfa} … … 4925 5081 sout | '1' | '2' | '3'; 4926 5082 sout | 1 | "" | 2 | "" | 3; 4927 sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x £" | 6 | "x Â¥"4928 | 7 | "x ¡" | 8 | "x ¿" | 9 | "x «" | 10;5083 sout | "x (" | 1 | "x [" | 2 | "x {" | 3 | "x =" | 4 | "x $" | 5 | "x £" | 6 | "x ¥" 5084 | 7 | "x ¡" | 8 | "x ¿" | 9 | "x «" | 10; 4929 5085 sout | 1 | ", x" | 2 | ". x" | 3 | "; x" | 4 | "! x" | 5 | "? x" | 6 | "% x" 4930 | 7 | " ¢ x" | 8 | "» x" | 9 | ") x" | 10 | "] x" | 11 | "} x";5086 | 7 | "¢ x" | 8 | "» x" | 9 | ") x" | 10 | "] x" | 11 | "} x"; 4931 5087 sout | "x`" | 1 | "`x'" | 2 | "'x\"" | 3 | "\"x:" | 4 | ":x " | 5 | " x\t" | 6 | "\tx"; 4932 5088 sout | "x ( " | 1 | " ) x" | 2 | " , x" | 3 | " :x: " | 4; … … 7775 7931 \item[Rationale:] increase type safety 7776 7932 \item[Effect on original feature:] deletion of semantically well-defined feature. 7777 \item[Difficulty of converting:] requires adding a cast \see{\VRef{s: StorageManagement} for better alternatives}:7933 \item[Difficulty of converting:] requires adding a cast \see{\VRef{s:DynamicStorageManagement} for better alternatives}: 7778 7934 \begin{cfa} 7779 7935 int * b = (int *)malloc( sizeof(int) ); … … 7987 8143 \section{Standard Library} 7988 8144 \label{s:StandardLibrary} 7989 7990 The \CFA standard-library wraps explicitly-polymorphic C routines into implicitly-polymorphic versions. 7991 7992 7993 \subsection{Storage Management} 7994 \label{s:StorageManagement} 7995 7996 The storage-management routines extend their C equivalents by overloading, alternate names, providing shallow type-safety, and removing the need to specify the allocation size for non-array types. 7997 7998 C storage management provides the following capabilities: 7999 \begin{description} 8000 \item[filled] 8001 after allocation with a specified character or value. 8145 \index{standard library} 8146 8147 The \CFA standard-library extends existing C library routines by adding new function, wrapping existing explicitly-polymorphic C routines into implicitly-polymorphic versions, and adding new \CFA extensions. 8148 8149 8150 \subsection{Dynamic Storage-Management} 8151 \label{s:DynamicStorageManagement} 8152 \index{dynamic storage-management}\index{storage management} 8153 8154 Dynamic storage-management in C is based on explicit allocation and deallocation (©malloc©/©free©). 8155 Programmer's must manage all allocated storage via its address (pointer) and subsequently deallocate the storage via this address. 8156 Storage that is not deallocated becomes inaccessible, called a \newterm{memory leak}, which can only be detected at program termination. 8157 Storage freed twice is an error, called a \newterm{duplicate free}, which can sometimes be detected. 8158 Storage used after it is deallocated is an error, called using a \newterm{dangling pointer}, which can sometimes be detected. 8159 8160 8161 \subsubsection{C Interface} 8162 8163 C dynamic storage-management provides the following properties. 8164 \begin{description}[leftmargin=*] 8165 \item[fill] 8166 storage after an allocation with a specified character or value. 8167 \item[align] 8168 an allocation on a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes. 8169 \item[scale] 8170 an allocation size to the specified number of array elements. 8171 An array may be filled, resized, or aligned. 8002 8172 \item[resize] 8003 8173 an existing allocation to decreased or increased its size. 8004 In either case, new storage may or may not be allocated and, if there is a new allocation, as much data from the existing allocation is copied into the new allocation. 8005 For an increase in storage size, new storage after the copied data may be filled. 8006 \item[align] 8007 an allocation on a specified memory boundary, \eg, an address multiple of 64 or 128 for cache-line purposes. 8008 \item[array] 8009 the allocation size is scaled to the specified number of array elements. 8010 An array may be filled, resized, or aligned. 8174 In either direction, new storage may or may not be allocated, but if there is a new allocation, as much data from the existing allocation is copied into the new allocation. 8175 When new storage is allocated, it may be aligned and storage after copied data may be filled. 8011 8176 \end{description} 8012 \VRef[Table]{t:AllocationVersusCapabilities} shows allocation routines supporting different combinations of storage-management capabilities. 8177 \VRef[Table]{t:AllocationVersusProperties} shows different combinations of storage-management properties provided by the C and \CFA allocation routines. 8178 8013 8179 \begin{table} 8180 \caption{Allocation Routines versus Storage-Management Properties} 8181 \label{t:AllocationVersusProperties} 8014 8182 \centering 8015 8183 \begin{minipage}{0.75\textwidth} 8016 8184 \begin{tabular}{@{}r|l|l|l|l|l@{}} 8017 \multicolumn{1}{c}{}& & \multicolumn{1}{c|}{fill} & resize & alignment & array\\8185 & \multicolumn{1}{c|}{routine} & \multicolumn{1}{c|}{\textbf{fill}} & \textbf{alignment} & \textbf{scale} & \textbf{resize} \\ 8018 8186 \hline 8019 8187 C & ©malloc© & no & no & no & no \\ 8020 & ©calloc© & yes (0 only) & no & no & yes \\ 8021 & ©realloc© & copy & yes & no & no \\ 8022 & ©memalign© & no & no & yes & no \\ 8023 & ©aligned_alloc©\footnote{Same as ©memalign© but size is an integral multiple of alignment, which is universally ignored.} 8024 & no & no & yes & no \\ 8025 & ©posix_memalign© & no & no & yes & no \\ 8026 & ©valloc© & no & no & yes (page size)& no \\ 8188 & ©calloc© & yes (0 only) & no & yes & no \\ 8189 & ©realloc© & copy & no & no & yes \\ 8190 & ©reallocarray© & copy & no & yes & yes \\ 8191 & ©memalign© & no & yes & no & no \\ 8192 & ©aligned_alloc©\footnote{Same as ©memalign© but size is an integral multiple of alignment.} 8193 & no & yes & no & no \\ 8194 & ©posix_memalign© & no & yes & no & no \\ 8195 & ©valloc© & no & yes (page size)& no & no \\ 8027 8196 & ©pvalloc©\footnote{Same as ©valloc© but rounds size to multiple of page size.} 8028 & no & no & yes (page size)& no \\ 8029 \hline 8030 \CFA & ©cmemalign© & yes (0 only) & no & yes & yes \\ 8031 & ©realloc© & copy & yes & yes & no \\ 8032 & ©alloc© & no & yes & no & yes \\ 8033 & ©alloc_set© & yes & yes & no & yes \\ 8034 & ©alloc_align© & no & yes & yes & yes \\ 8035 & ©alloc_align_set© & yes & yes & yes & yes \\ 8197 & no & yes (page size)& no & no \\ 8198 \hline 8199 \CFA & ©cmemalign© & yes (0 only) & yes & yes & no \\ 8200 & ©resize© & no copy & yes & no & yes \\ 8201 & ©realloc© & copy & yes & no & yes \\ 8202 & ©alloc©\footnote{Multiple overloads with different parameters.} 8203 & yes & yes & yes & yes 8036 8204 \end{tabular} 8037 8205 \end{minipage} 8038 \caption{Allocation Routines versus Storage-Management Capabilities} 8039 \label{t:AllocationVersusCapabilities} 8206 \vspace*{-10pt} 8040 8207 \end{table} 8041 8208 8042 \CFA memory management extends the type safety of all allocations by using the type of the left-hand-side type to determine the allocation size and return a matching type for the new storage. 8043 Type-safe allocation is provided for all C allocation routines and new \CFA allocation routines, \eg in 8044 \begin{cfa} 8045 int * ip = (int *)malloc( sizeof(int) ); §\C{// C}§ 8046 int * ip = malloc(); §\C{// \CFA type-safe version of C malloc}§ 8047 int * ip = alloc(); §\C{// \CFA type-safe uniform alloc}§ 8048 \end{cfa} 8049 the latter two allocations determine the allocation size from the type of ©p© (©int©) and cast the pointer to the allocated storage to ©int *©. 8050 8051 \CFA memory management extends allocation safety by implicitly honouring all alignment requirements, \eg in 8052 \begin{cfa} 8053 struct S { int i; } __attribute__(( aligned( 128 ) )); // cache-line alignment 8054 S * sp = malloc(); §\C{// honour type alignment}§ 8055 \end{cfa} 8056 the storage allocation is implicitly aligned to 128 rather than the default 16. 8057 The alignment check is performed at compile time so there is no runtime cost. 8058 8059 \CFA memory management extends the resize capability with the notion of \newterm{sticky properties}. 8060 Hence, initial allocation capabilities are remembered and maintained when resize requires copying. 8061 For example, an initial alignment and fill capability are preserved during a resize copy so the copy has the same alignment and extended storage is filled. 8062 Without sticky properties it is dangerous to use ©realloc©, resulting in an idiom of manually performing the reallocation to maintain correctness. 8063 \begin{cfa} 8064 8065 \end{cfa} 8066 8067 \CFA memory management extends allocation to support constructors for initialization of allocated storage, \eg in 8068 \begin{cfa} 8069 struct S { int i; }; §\C{// cache-line alignment}§ 8070 void ?{}( S & s, int i ) { s.i = i; } 8071 // assume ?|? operator for printing an S 8072 8073 S & sp = *®new®( 3 ); §\C{// call constructor after allocation}§ 8074 sout | sp.i; 8075 ®delete®( &sp ); 8076 8077 S * spa = ®anew®( 10, 5 ); §\C{// allocate array and initialize each array element}§ 8078 for ( i; 10 ) sout | spa[i] | nonl; 8079 sout | nl; 8080 ®adelete®( 10, spa ); 8209 8210 \subsubsection{\CFA Interface} 8211 8212 \CFA dynamic memory management: 8213 \begin{enumerate}[leftmargin=\parindent] 8214 \item 8215 extends type safety of all allocation routines by using the left-hand assignment type to determine the allocation size and alignment, and return a matching type for the new storage, which removes many common allocation errors. 8216 \begin{cfa} 8217 int * ip = (int *)malloc( sizeof(int) ); §\C[2.3in]{// C}§ 8218 int * ip = malloc(); §\C{// \CFA type-safe call of C malloc}§ 8219 int * ip = calloc(); §\C{// \CFA type-safe call of C calloc}§ 8220 struct __attribute__(( aligned(128) )) spinlock { ... }; // cache alignment 8221 spinlock * slp = malloc(); §\C{// correct size, alignment, and return type}\CRT§ 8222 \end{cfa} 8223 Here, the alignment of the ©ip© storage is 16 (default) and 128 for ©slp©. 8224 8225 \item 8226 introduces the notion of \newterm{sticky properties} used in resizing. 8227 All initial allocation properties are remembered and maintained for use should resize require new storage. 8228 For example, the initial alignment and fill properties in the initial allocation 8229 \begin{cfa} 8230 struct __attribute__(( aligned(4096) )) S { ... }; 8231 S * sp = calloc( 10 ); §\C{// align 4K and zero fill}§ 8232 sp = reallocarray( sp, 100 ); §\C{// preserve 4K alignment and zero fill new storage}§ 8233 \end{cfa} 8234 are preserved in the resize so the new storage has the same alignment and extra storage after the data copy is zero filled. 8235 Without sticky properties it is dangerous to resize, resulting in the C idiom of manually performing the reallocation to maintain correctness, which is error prone. 8236 8237 \item 8238 provides resizing without data copying, which is useful to repurpose an existing block of storage, rather than freeing the old storage and performing a new allocation. 8239 A resize can take advantage of unused storage after the data to preventing a free/reallocation step altogether. 8240 8241 \item 8242 provides ©free©/©delete© functions that delete a variable number of allocations. 8243 \begin{cfa} 8244 int * ip = malloc(), * jp = malloc(), * kp = malloc(); 8245 double * xp = malloc(), * yp = malloc(), * zp = malloc(); 8246 free( ®ip, jp, kp, xp, yp, zp® ); §\C{// multiple deallocations}§ 8247 \end{cfa} 8248 8249 \item 8250 supports constructors for initialization of allocated storage and destructors for deallocation (like \CC). 8251 \begin{cfa} 8252 struct S { int v; }; §\C{// default constructors}§ 8253 void ^?{}( S & ) { ... } §\C{// destructor}§ 8254 S & sp = *®new®( 3 ); §\C{// allocate and call constructor}§ 8255 sout | sp.v; 8256 ®delete®( &sp ); §\C{// call destructor}§ 8257 S * spa1 = ®anew®( 10, 5 ), * spa2 = ®anew®( 10, 8 ); §\C{// allocate array and call constructor for each array element}§ 8258 for ( i; 10 ) sout | spa1[i].v | spa2[i].v | nonl; sout | nl; 8259 ®adelete®( spa1, spa2 ); §\C{// call destructors on all array objects}§ 8260 8261 3 8262 5 8 5 8 5 8 5 8 5 8 5 8 5 8 5 8 5 8 5 8 8081 8263 \end{cfa} 8082 8264 Allocation routines ©new©/©anew© allocate a variable/array and initialize storage using the allocated type's constructor. 8083 8265 Note, the matching deallocation routines ©delete©/©adelete©. 8084 8085 \leavevmode 8266 \CC only supports the default constructor for intializing array elements. 8267 \begin{C++} 8268 S * sp = new S[10]®{5}®; §\C{// disallowed}§ 8269 \end{C++} 8270 \end{enumerate} 8271 8272 In addition, \CFA provides a new allocator interface to further increase orthogonality and usability of dynamic-memory allocation. 8273 This interface helps programmers in three ways. 8274 \begin{enumerate} 8275 \item 8276 naming: \CFA regular and ©ttype© polymorphism (similar to \CC variadic templates) is used to encapsulate a wide range of allocation functionality into a single routine name, so programmers do not have to remember multiple routine names for different kinds of dynamic allocations. 8277 \item 8278 named arguments: individual allocation properties are specified using postfix function call \see{\VRef{s:PostfixFunction}}, so programmers do not have to remember parameter positions in allocation calls. 8279 \item 8280 safe usage: like the \CFA's C-interface, programmers do not have to specify object size or cast allocation results. 8281 \end{enumerate} 8282 8283 The polymorphic functions 8284 \begin{cfa} 8285 T * alloc( ... ); 8286 T * alloc( size_t dim, ... ); 8287 \end{cfa} 8288 are overloaded with a variable number of allocation properties. 8289 These allocation properties can be passed as named arguments when calling the \Indexc{alloc} routine. 8290 A call without parameters returns an uninitialized dynamically allocated object of type ©T© (\Indexc{malloc}). 8291 A call with only the dimension (dim) parameter returns an uninitialized dynamically allocated array of objects with type ©T© (\Indexc{aalloc}). 8292 The variable number of arguments consist of allocation properties to specialize the allocation. 8293 The properties ©resize© and ©realloc© are associated with an existing allocation variable indicating how its storage is modified. 8294 8295 The following allocation property functions may be combined and appear in any order as arguments to ©alloc©, 8296 \begin{itemize} 8297 \item 8298 ©T_align ?`align( size_t alignment )© to align an allocation. 8299 The alignment parameter must be $\ge$ the default alignment (©libAlign()© in \CFA) and a power of two, \eg the following return a dynamic object and object array aligned on a 256 and 4096-byte boundary. 8300 \begin{cfa} 8301 int * i0 = alloc( ®256`align® ); sout | i0 | nl; 8302 int * i1 = alloc( 3, ®4096`align® ); for (i; 3 ) sout | &i1[i] | nonl; sout | nl; 8303 free( i0, i1 ); 8304 8305 0x5555565699®00® // 256 alignment 8306 0x55555656c®000® 0x5656c004 0x5656c008 // 4K array alignment 8307 \end{cfa} 8308 8309 \item 8310 ©T_fill(T) ?`fill( /* various types */ )© to initialize storage. 8311 There are three ways to fill storage: 8312 \begin{enumerate} 8313 \item 8314 A ©char© fills every byte of each object. 8315 \item 8316 An object of the returned type fills each object. 8317 \item 8318 An object array pointer fills some or all of the corresponding object array. 8319 \end{enumerate} 8320 For example: 8321 \begin{cfa}[numbers=left] 8322 int * i0 = alloc( ®0n`fill® ); sout | *i0 | nl; // 0n disambiguates 0p 8323 int * i1 = alloc( ®5`fill® ); sout | *i1 | nl; 8324 int * i2 = alloc( ®'\xfe'`fill® ); sout | hex( *i2 ) | nl; 8325 int * i3 = alloc( 5, ®5`fill® ); for ( i; 5 ) sout | i3[i] | nonl; sout | nl; 8326 int * i4 = alloc( 5, ®0xdeadbeefN`fill® ); for ( i; 5 ) sout | hex( i4[i] ) | nonl; sout | nl; 8327 int * i5 = alloc( 5, ®i3`fill® ); for ( i; 5 ) sout | i5[i] | nonl; sout | nl; // completely fill from i3 8328 int * i6 = alloc( 5, ®[i3, 3]`fill® ); for ( i; 5 ) sout | i6[i] | nonl; sout | nl; // partial fill from i3 8329 free( i0, i1, i2, i3, i4, i5, i6 ); 8330 \end{cfa} 8331 \begin{lstlisting}[numbers=left] 8332 0 8333 5 8334 0xfefefefe 8335 5 5 5 5 5 8336 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 0xdeadbeef 8337 5 5 5 5 5 8338 5 5 5 -555819298 -555819298 // two undefined values 8339 \end{lstlisting} 8340 Examples 1 to 3 fill an object with a value or characters. 8341 Examples 4 to 7 fill an array of objects with values, another array, or part of an array. 8342 8343 \item 8344 ©S_resize(T) ?`resize( void * oaddr )© used to resize, realign, and fill, where the old object data is not copied to the new object. 8345 The old object type may be different from the new object type, since the values are not used. 8346 For example: 8347 \begin{cfa}[numbers=left] 8348 int * ip = alloc( ®5`fill® ); sout | ip | *ip; 8349 ip = alloc( ®ip`resize®, ®256`align®, ®7`fill® ); sout | ip | *ip; 8350 double * dp = alloc( ®ip`resize®, ®4096`align®, ®13.5`fill® ); sout | dp | *dp; 8351 free( dp ); // DO NOT FREE ip AS ITS STORAGE IS MOVED TO dp 8352 \end{cfa} 8353 \begin{lstlisting}[numbers=left] 8354 0x555555580a80 5 8355 0x555555581100 7 8356 0x555555587000 13.5 8357 \end{lstlisting} 8358 Examples 2 to 3 change the alignment, fill, and size for the initial storage of ©i©. 8359 8360 \begin{cfa}[numbers=left] 8361 int * ia = alloc( 5, ®5`fill® ); sout | ia | nonl; for ( i; 5 ) sout | ia[i] | nonl; sout | nl; 8362 ia = alloc( 10, ®ia`resize®, ®7`fill® ); sout | ia | nonl; for ( i; 10 ) sout | ia[i] | nonl; sout | nl; 8363 ia = alloc( 5, ®ia`resize®, ®512`align®, ®13`fill® ); sout | ia | nonl; for ( i; 5 ) sout | ia[i] | nonl; sout | nl;; 8364 ia = alloc( 3, ®ia`resize®, ®4096`align®, ®2`fill® ); for ( i; 3 ) sout | &ia[i] | ia[i] | nonl; sout | nl; 8365 free( ia ); 8366 \end{cfa} 8367 \begin{lstlisting}[numbers=left] 8368 0x55555656d540 5 5 5 5 5 8369 0x55555656d480 7 7 7 7 7 7 7 7 7 7 8370 0x55555656fe00 13 13 13 13 13 8371 0x555556570000 2 0x555556570004 2 0x555556570008 2 8372 \end{lstlisting} 8373 Examples 2 to 4 change the array size, alignment, and fill initializes all storage because no data is copied. 8374 8375 \item 8376 ©S_realloc(T) ?`realloc( T * a ))© 8377 used to resize, realign, and fill, where the old object data is copied to the new object. 8378 The old object type must be the same as the new object type, since the value is used. 8379 Note, for ©fill©, only the extra space after copying the data from the old object is filled with the given parameter. 8380 For example: 8381 \begin{cfa}[numbers=left] 8382 int * ip = alloc( ®5`fill® ); sout | ip | *ip; 8383 ip = alloc( ®ip`realloc®, ®256`align® ); sout | ip | *ip; 8384 ip = alloc( ®ip`realloc®, ®4096`align®, ®13`fill® ); sout | ip | *ip; 8385 free( ip ); 8386 \end{cfa} 8387 \begin{lstlisting}[numbers=left] 8388 0x55555556d5c0 5 8389 0x555555570000 5 8390 0x555555571000 5 8391 \end{lstlisting} 8392 Examples 2 to 3 change the alignment for the initial storage of ©i©. 8393 The ©13`fill© in example 3 does nothing because no new storage is added. 8394 8395 \begin{cfa}[numbers=left] 8396 int * ia = alloc( 5, ®5`fill® ); sout | ia | nonl; for ( i; 5 ) sout | ia[i] | nonl; sout | nl; 8397 ia = alloc( 10, ®ia`realloc®, ®7`fill® ); sout | ia | nonl; for ( i; 10 ) sout | ia[i] | nonl; sout | nl; 8398 ia = alloc( 5, ®ia`realloc®, ®512`align®, ®13`fill® ); sout | ia | nonl; for ( i; 5 ) sout | ia[i] | nonl; sout | nl;; 8399 ia = alloc( 3, ®ia`realloc®, ®4096`align®, ®2`fill® ); for ( i; 3 ) sout | &ia[i] | ia[i] | nonl; sout | nl; 8400 free( ia ); 8401 \end{cfa} 8402 \begin{lstlisting}[numbers=left] 8403 0x55555656d540 5 5 5 5 5 8404 0x55555656d480 7 7 7 7 7 7 7 7 7 7 8405 0x555556570e00 5 5 5 5 5 8406 0x5555556571000 5 0x555556571004 5 0x555556571008 5 8407 \end{lstlisting} 8408 Examples 2 to 4 change the array size, alignment, and fill does no initialization after the copied data, as no new storage is added. 8409 \end{itemize} 8410 8411 \medskip 8086 8412 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 8087 8413 extern "C" { 8088 // C unsafe allocation 8089 void * malloc( size_t size );§\indexc{malloc}§ 8090 void * calloc( size_t dim, size_t size );§\indexc{calloc}§ 8091 void * realloc( void * ptr, size_t size );§\indexc{realloc}§ 8092 void * memalign( size_t align, size_t size );§\indexc{memalign}§ 8093 void * aligned_alloc( size_t align, size_t size );§\indexc{aligned_alloc}§ 8094 int posix_memalign( void ** ptr, size_t align, size_t size );§\indexc{posix_memalign}§ 8095 void * cmemalign( size_t alignment, size_t noOfElems, size_t elemSize );§\indexc{cmemalign}§ // CFA 8096 8097 // C unsafe initialization/copy 8098 void * memset( void * dest, int c, size_t size );§\indexc{memset}§ 8099 void * memcpy( void * dest, const void * src, size_t size );§\indexc{memcpy}§ 8100 } 8101 8102 void * realloc( void * oaddr, size_t nalign, size_t size ); // CFA heap 8103 8104 forall( dtype T | sized(T) ) { 8105 // §\CFA§ safe equivalents, i.e., implicit size specification 8106 T * malloc( void ); 8107 T * calloc( size_t dim ); 8108 T * realloc( T * ptr, size_t size ); 8109 T * memalign( size_t align ); 8110 T * cmemalign( size_t align, size_t dim ); 8111 T * aligned_alloc( size_t align ); 8112 int posix_memalign( T ** ptr, size_t align ); 8414 // New C allocation operations. 8415 void * aalloc( size_t dim, size_t elemSize );§\indexc{aalloc}§ 8416 void * resize( void * oaddr, size_t size );§\indexc{resize}§ 8417 void * amemalign( size_t align, size_t dim, size_t elemSize );§\indexc{amemalign}§ 8418 void * cmemalign( size_t align, size_t dim, size_t elemSize );§\indexc{cmemalign}§ 8419 size_t malloc_alignment( void * addr );§\indexc{malloc_alignment}§ 8420 bool malloc_zero_fill( void * addr );§\indexc{malloc_zero_fill}§ 8421 size_t malloc_size( void * addr );§\indexc{malloc_size}§ 8422 int malloc_stats_fd( int fd );§\indexc{malloc_stats_fd}§ 8423 size_t malloc_expansion();§\indexc{malloc_expansion}§ §\C{// heap expansion size (bytes)}§ 8424 size_t malloc_mmap_start();§\indexc{malloc_mmap_start}§ §\C{// crossover allocation size from sbrk to mmap}§ 8425 size_t malloc_unfreed();§\indexc{malloc_unfreed()}§ §\C{// heap unfreed size (bytes)}§ 8426 void malloc_stats_clear();§\indexc{malloc_stats_clear}§ §\C{// clear heap statistics}§ 8427 } 8428 8429 // New allocation operations. 8430 void * resize( void * oaddr, size_t alignment, size_t size ); 8431 void * realloc( void * oaddr, size_t alignment, size_t size ); 8432 void * reallocarray( void * oaddr, size_t nalign, size_t dim, size_t elemSize ); 8433 8434 forall( T & | sized(T) ) { 8435 // §\CFA§ safe equivalents, i.e., implicit size specification, eliminate return-type cast 8436 T * malloc( void );§\indexc{malloc}§ 8437 T * aalloc( size_t dim );§\indexc{aalloc}§ 8438 T * calloc( size_t dim );§\indexc{calloc}§ 8439 T * resize( T * ptr, size_t size );§\indexc{resize}§ 8440 T * resize( T * ptr, size_t alignment, size_t size ); 8441 T * realloc( T * ptr, size_t size );§\indexc{realloc}§ 8442 T * realloc( T * ptr, size_t alignment, size_t size ); 8443 T * reallocarray( T * ptr, size_t dim );§\indexc{reallocarray}§ 8444 T * reallocarray( T * ptr, size_t alignment, size_t dim ); 8445 T * memalign( size_t align );§\indexc{memalign}§ 8446 T * amemalign( size_t align, size_t dim );§\indexc{amemalign}§ 8447 T * cmemalign( size_t align, size_t dim );§\indexc{aalloc}§ 8448 T * aligned_alloc( size_t align );§\indexc{aligned_alloc}§ 8449 int posix_memalign( T ** ptr, size_t align );§\indexc{posix_memalign}§ 8450 T * valloc( void );§\indexc{valloc}§ 8451 T * pvalloc( void );§\indexc{pvalloc}§ 8113 8452 8114 8453 // §\CFA§ safe general allocation, fill, resize, alignment, array 8115 T * alloc( void );§\indexc{alloc}§ §\C[3.5in]{// variable, T size}§ 8116 T * alloc( size_t dim ); §\C{// array[dim], T size elements}§ 8117 T * alloc( T ptr[], size_t dim ); §\C{// realloc array[dim], T size elements}§ 8118 8119 T * alloc_set( char fill );§\indexc{alloc_set}§ §\C{// variable, T size, fill bytes with value}§ 8120 T * alloc_set( T fill ); §\C{// variable, T size, fill with value}§ 8121 T * alloc_set( size_t dim, char fill ); §\C{// array[dim], T size elements, fill bytes with value}§ 8122 T * alloc_set( size_t dim, T fill ); §\C{// array[dim], T size elements, fill elements with value}§ 8123 T * alloc_set( size_t dim, const T fill[] ); §\C{// array[dim], T size elements, fill elements with array}§ 8124 T * alloc_set( T ptr[], size_t dim, char fill ); §\C{// realloc array[dim], T size elements, fill bytes with value}§ 8125 8126 T * alloc_align( size_t align ); §\C{// aligned variable, T size}§ 8127 T * alloc_align( size_t align, size_t dim ); §\C{// aligned array[dim], T size elements}§ 8128 T * alloc_align( T ptr[], size_t align ); §\C{// realloc new aligned array}§ 8129 T * alloc_align( T ptr[], size_t align, size_t dim ); §\C{// realloc new aligned array[dim]}§ 8130 8131 T * alloc_align_set( size_t align, char fill ); §\C{// aligned variable, T size, fill bytes with value}§ 8132 T * alloc_align_set( size_t align, T fill ); §\C{// aligned variable, T size, fill with value}§ 8133 T * alloc_align_set( size_t align, size_t dim, char fill ); §\C{// aligned array[dim], T size elements, fill bytes with value}§ 8134 T * alloc_align_set( size_t align, size_t dim, T fill ); §\C{// aligned array[dim], T size elements, fill elements with value}§ 8135 T * alloc_align_set( size_t align, size_t dim, const T fill[] ); §\C{// aligned array[dim], T size elements, fill elements with array}§ 8136 T * alloc_align_set( T ptr[], size_t align, size_t dim, char fill ); §\C{// realloc new aligned array[dim], fill new bytes with value}§ 8137 8138 // §\CFA§ safe initialization/copy, i.e., implicit size specification 8139 T * memset( T * dest, char fill );§\indexc{memset}§ 8140 T * memcpy( T * dest, const T * src );§\indexc{memcpy}§ 8141 8142 // §\CFA§ safe initialization/copy, i.e., implicit size specification, array types 8143 T * amemset( T dest[], char fill, size_t dim ); 8454 T * alloc( ... );§\indexc{alloc}§ §\C{// variable, T size}§ 8455 T * alloc( size_t dim, ... ); 8456 T_align ?`align( size_t alignment );§\indexc{align}§ 8457 T_fill(T) ?`fill( /* various types */ );§\indexc{fill}§ 8458 T_resize ?`resize( void * oaddr );§\indexc{resize}§ 8459 T_realloc ?`realloc( void * oaddr ));§\indexc{realloc}§ 8460 } 8461 8462 forall( T &, List ... ) void free( T * ptr, ... ) // deallocation list 8463 8464 // §\CFA§ allocation/deallocation and constructor/destructor, non-array types 8465 forall( T &, Parms ... | { void ?{}( T &, Parms ); } ) T * new( Parms ... );§\indexc{new}§ 8466 forall( T &, List ... | { void ^?{}( T & ); void delete( List ... ); } );§\indexc{delete}§ 8467 // §\CFA§ allocation/deallocation and constructor/destructor, array types 8468 forall( T & | sized(T), Parms ... | { void ?{}( T &, Parms ); } ) T * anew( size_t dim, Parms ... );§\indexc{anew}§ 8469 forall( T & | sized(T) | { void ^?{}( T & ); }, List ... } ) void adelete( T arr[], List ... );§\indexc{adelete}§ 8470 \end{cfa} 8471 8472 8473 \subsection{Memory Set and Copy} 8474 8475 Like safe memory allocation, \CFA provides safe block initialization and copy. 8476 While objects should be initialized/copied with constructors/assignment, block operations can be very performant. 8477 In certain cases the compiler generates block copy operations, such as assigning structures ©s = t©, however C arrays cannot be assigned. 8478 \begin{cquote} 8479 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 8480 struct S { int i, j, k; }; 8481 S s, t, *sp = &s, * tp = &t, sa[10], ta[10]; 8482 \end{cfa} 8483 \noindent 8484 \begin{tabular}{@{}l|l@{}} 8485 \multicolumn{1}{@{}c|}{\textbf{\CFA}} & \multicolumn{1}{c@{}}{\textbf{C}} \\ 8486 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 8487 memset( s, '\0' ); 8488 memset( sp, '\0' ); 8489 8490 memcpy( s, t ); 8491 memcpy( sp, tp ); 8492 8493 amemset( sa, '\0', 10 ); 8494 amemcpy( sa, ta, 10 ); 8495 \end{cfa} 8496 & 8497 \begin{cfa}[aboveskip=0pt,belowskip=0pt] 8498 memset( &s, '\0', sizeof(s) ); 8499 memset( sp, '\0', sizeof(s) ); 8500 8501 memcpy( &s, &t, sizeof(s) ); 8502 memcpy( sp, tp, sizeof(s) ); 8503 8504 memset( sa, '\0', sizeof(sa) ); 8505 memcpy( sa, ta, sizeof(sa) ); 8506 \end{cfa} 8507 \end{tabular} 8508 \end{cquote} 8509 These operations provide uniformity between reference and pointer, so object dereferencing, '©&©', is unnecessary. 8510 8511 \begin{cfa} 8512 static inline forall( T & | sized(T) ) { 8513 // CFA safe initialization/copy, i.e., implicit size specification, non-array types 8514 T * memset( T * dest, char fill );§\indexc{memset}§ §\C{// all combinations of pointer/reference}§ 8515 T * memset( T & dest, char fill ); 8516 8517 T * memcpy( T * dest, const T * src );§\indexc{memcpy}§ §\C{// all combinations of pointer/reference}§ 8518 T * memcpy( T & dest, const T & src ); 8519 T * memcpy( T * dest, const T & src ); 8520 T * memcpy( T & dest, const T * src ); 8521 8522 // CFA safe initialization/copy, i.e., implicit size specification, array types 8523 T * amemset( T dest[], char fill, size_t dim );§\indexc{memcpy}§ 8144 8524 T * amemcpy( T dest[], const T src[], size_t dim ); 8145 8525 } 8146 8147 // §\CFA§ allocation/deallocation and constructor/destructor, non-array types8148 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * new( Params p );§\indexc{new}§8149 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr );§\indexc{delete}§8150 forall( dtype T, ttype Params | sized(T) | { void ^?{}( T & ); void delete( Params ); } )8151 void delete( T * ptr, Params rest );8152 8153 // §\CFA§ allocation/deallocation and constructor/destructor, array types8154 forall( dtype T | sized(T), ttype Params | { void ?{}( T &, Params ); } ) T * anew( size_t dim, Params p );§\indexc{anew}§8155 forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void adelete( size_t dim, T arr[] );§\indexc{adelete}§8156 forall( dtype T | sized(T) | { void ^?{}( T & ); }, ttype Params | { void adelete( Params ); } )8157 void adelete( size_t dim, T arr[], Params rest );8158 8526 \end{cfa} 8159 8527 … … 9290 9658 Int sqrt( Int oper ); 9291 9659 9292 forall( dtype istype| istream( istype ) ) istype * ?|?( istype * is, Int * mp ); §\C{// I/O}§9293 forall( dtype ostype| ostream( ostype ) ) ostype * ?|?( ostype * os, Int mp );9660 forall( istype & | istream( istype ) ) istype * ?|?( istype * is, Int * mp ); §\C{// I/O}§ 9661 forall( ostype & | ostream( ostype ) ) ostype * ?|?( ostype * os, Int mp ); 9294 9662 \end{cfa} 9295 9663 \VRef[Figure]{f:MultiPrecisionFactorials} shows \CFA and C factorial programs using the GMP interfaces. … … 9299 9667 \begin{cquote} 9300 9668 \begin{tabular}{@{}l@{\hspace{\parindentlnth}}|@{\hspace{\parindentlnth}}l@{}} 9301 \multicolumn{1}{@{}c|@{\hspace{\parindentlnth}}}{\textbf{ C}} & \multicolumn{1}{@{\hspace{\parindentlnth}}c@{}}{\textbf{\CFA}} \\9669 \multicolumn{1}{@{}c|@{\hspace{\parindentlnth}}}{\textbf{\CFA}} & \multicolumn{1}{@{\hspace{\parindentlnth}}c@{}}{\textbf{C}} \\ 9302 9670 \hline 9671 \begin{cfa} 9672 #include <gmp.hfa>§\indexc{gmp}§ 9673 int main( void ) { 9674 sout | "Factorial Numbers"; 9675 ®Int® fact = 1; 9676 9677 sout | 0 | fact; 9678 for ( i; 40 ) { 9679 fact *= i; 9680 sout | i | fact; 9681 } 9682 } 9683 \end{cfa} 9684 & 9303 9685 \begin{cfa} 9304 9686 #include <gmp.h>§\indexc{gmp.h}§ … … 9311 9693 ®mpz_mul_ui®( fact, fact, i ); 9312 9694 ®gmp_printf®( "%d %Zd\n", i, fact ); 9313 }9314 }9315 \end{cfa}9316 &9317 \begin{cfa}9318 #include <gmp.hfa>§\indexc{gmp}§9319 int main( void ) {9320 sout | "Factorial Numbers";9321 Int fact = 1;9322 9323 sout | 0 | fact;9324 for ( i; 40 ) {9325 fact *= i;9326 sout | i | fact;9327 9695 } 9328 9696 } … … 9419 9787 Rational narrow( double f, long int md ); 9420 9788 9421 forall( dtype istype| istream( istype ) ) istype * ?|?( istype *, Rational * ); // I/O9422 forall( dtype ostype| ostream( ostype ) ) ostype * ?|?( ostype *, Rational );9789 forall( istype & | istream( istype ) ) istype * ?|?( istype *, Rational * ); // I/O 9790 forall( ostype & | ostream( ostype ) ) ostype * ?|?( ostype *, Rational ); 9423 9791 \end{cfa} 9424 9792 … … 9440 9808 \end{document} 9441 9809 9810 From: Michael Leslie Brooks <mlbrooks@uwaterloo.ca> 9811 To: Peter Buhr <pabuhr@uwaterloo.ca>, 9812 Andrew James Beach 9813 <ajbeach@uwaterloo.ca>, 9814 Fangren Yu <f37yu@uwaterloo.ca>, Jiada Liang 9815 <j82liang@uwaterloo.ca> 9816 Subject: The White House on Memory-Safe programming 9817 Date: Mon, 4 Mar 2024 16:49:53 +0000 9818 9819 I heard tell of this announcement last night. Haven't read the actual report yet. 9820 9821 Most mainstream article I can find: https://me.pcmag.com/en/security/22413/white-house-to-developers-using-c-or-c-invites-cybersecurity-risks 9822 Less fluffy summary: https://www.developer-tech.com/news/2024/feb/27/white-house-urges-adoption-memory-safe-programming-languages/ 9823 Horse's Mouth: https://www.whitehouse.gov/wp-content/uploads/2024/02/Final-ONCD-Technical-Report.pdf 9824 "This report focuses on the programming language as a primary building block, and explores hardware architecture and formal methods as complementary approaches" 9825 9826 A contrary analysis: https://hackaday.com/2024/02/29/the-white-house-memory-safety-appeal-is-a-security-red-herring/ 9827 9442 9828 % Local Variables: % 9443 9829 % tab-width: 4 % -
libcfa/src/concurrency/actor.hfa
rcf191ac r7042c60 299 299 300 300 if ( seperate_clus ) { 301 cluster = alloc();301 this.cluster = alloc(); 302 302 (*cluster){}; 303 303 } else cluster = active_cluster(); … … 360 360 adelete( worker_req_queues ); 361 361 adelete( processors ); 362 if ( seperate_clus ) delete( cluster );362 if ( seperate_clus ) delete( this.cluster ); 363 363 364 364 #ifdef ACTOR_STATS // print formatted stats -
libcfa/src/concurrency/kernel/fwd.hfa
rcf191ac r7042c60 374 374 // since if that is the case, the oneshot was fulfilled (unparking this thread) 375 375 // and the oneshot should not be needed any more 376 __attribute__((unused)) struct oneshot * was = this.ptr;376 struct oneshot * was __attribute__((unused)) = this.ptr; // used in option verify 377 377 /* paranoid */ verifyf( was == future_FULFILLED, "Expected this.ptr to be 1p, was %p\n", was ); 378 378 -
libcfa/src/device/cpu.cfa
rcf191ac r7042c60 239 239 // Returns a 2D array of instances of size [cpu count][cache levels] 240 240 // where cache level doesn't include instruction caches 241 raw_cache_instance ** build_raw_cache_table(unsigned cpus_c, idx_range_t cpus, unsigned idxs, unsigned cache_levels) 242 { 241 raw_cache_instance ** build_raw_cache_table(unsigned cpus_c, idx_range_t cpus, unsigned idxs, unsigned cache_levels) { 243 242 raw_cache_instance ** raw = alloc(cpus_c, '\0'`fill); 244 243 -
libcfa/src/iostream.hfa
rcf191ac r7042c60 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 6 18:35:54202413 // Update Count : 74 312 // Last Modified On : Sun Apr 21 07:32:19 2024 13 // Update Count : 744 14 14 // 15 15 … … 160 160 161 161 // tuples 162 forall( ostype &, T, Params... | writeable( T, ostype ) | { ostype & ?|?( ostype &, Params); } ) {163 ostype & ?|?( ostype & os, T arg, Paramsrest );164 void ?|?( ostype & os, T arg, Paramsrest );162 forall( ostype &, T, List ... | writeable( T, ostype ) | { ostype & ?|?( ostype &, List ); } ) { 163 ostype & ?|?( ostype & os, T arg, List rest ); 164 void ?|?( ostype & os, T arg, List rest ); 165 165 } // distribution 166 166 -
libcfa/src/stdlib.cfa
rcf191ac r7042c60 10 10 // Created On : Thu Jan 28 17:10:29 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sun Mar 17 08:25:32 202413 // Update Count : 69912 // Last Modified On : Sun Apr 21 16:17:22 2024 13 // Update Count : 700 14 14 // 15 15 … … 31 31 // Cforall allocation/deallocation and constructor/destructor, array types 32 32 33 forall( T & | sized(T), TT... | { void ?{}( T &, TT); } )34 T * anew( size_t dim, TTp ) {33 forall( T & | sized(T), Parms ... | { void ?{}( T &, Parms ); } ) 34 T * anew( size_t dim, Parms p ) { 35 35 T * arr = alloc( dim ); 36 36 for ( i; dim ) { … … 51 51 } // adelete 52 52 53 forall( T & | sized(T) | { void ^?{}( T & ); }, TT... | { void adelete( TT); } )54 void adelete( T arr[], TTrest ) {53 forall( T & | sized(T) | { void ^?{}( T & ); }, List ... | { void adelete( List ); } ) 54 void adelete( T arr[], List rest ) { 55 55 if ( arr ) { // ignore null 56 56 size_t dim = malloc_size( arr ) / sizeof( T ); -
libcfa/src/stdlib.hfa
rcf191ac r7042c60 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Apr 15 22:11:51 202413 // Update Count : 81712 // Last Modified On : Tue Apr 23 14:05:21 2024 13 // Update Count : 963 14 14 // 15 15 … … 47 47 48 48 static inline forall( T & | sized(T) ) { 49 // CFA safe equivalents, i.e., implicit size specification 49 // CFA safe equivalents, i.e., implicit size specification, eliminate return-type cast 50 50 51 51 T * malloc( void ) { … … 64 64 } // calloc 65 65 66 T * resize( T * ptr, size_t size ) { // CFA resize67 if ( _Alignof(T) <= libAlign() ) return (T *)resize( (void *)ptr, size ); // C FAresize66 T * resize( T * ptr, size_t size ) { 67 if ( _Alignof(T) <= libAlign() ) return (T *)resize( (void *)ptr, size ); // C resize 68 68 else return (T *)resize( (void *)ptr, _Alignof(T), size ); // CFA resize 69 } // resize 70 71 T * resize( T * ptr, size_t alignment, size_t size ) { 72 return (T *)resize( (void *)ptr, alignment, size ); // CFA resize 69 73 } // resize 70 74 … … 74 78 } // realloc 75 79 80 T * realloc( T * ptr, size_t alignment, size_t size ) { 81 return (T *)realloc( (void *)ptr, alignment, size ); // CFA realloc 82 } // realloc 83 76 84 T * reallocarray( T * ptr, size_t dim ) { // CFA reallocarray 77 85 if ( _Alignof(T) <= libAlign() ) return (T *)reallocarray( (void *)ptr, dim, sizeof(T) ); // C reallocarray … … 79 87 } // realloc 80 88 89 T * reallocarray( T * ptr, size_t alignment, size_t dim ) { 90 return (T *)reallocarray( (void *)ptr, alignment, dim ); // CFA reallocarray 91 } // realloc 92 81 93 T * memalign( size_t align ) { 82 94 return (T *)memalign( align, sizeof(T) ); // C memalign … … 87 99 } // amemalign 88 100 89 T * cmemalign( size_t align, size_t dim 101 T * cmemalign( size_t align, size_t dim ) { 90 102 return (T *)cmemalign( align, dim, sizeof(T) ); // CFA cmemalign 91 103 } // cmemalign … … 109 121 110 122 /* 111 FIX ME : fix alloc interface after Ticker Number 214 is resolved, define and add union to S_fill. Then, modify postfix-fill functions to support T * with nmemb, char, and T object of any size. Finally, change alloc_internal. 123 FIX ME : fix alloc interface after Ticker Number 214 is resolved, define and add union to S_fill. Then, modify 124 postfix-fill functions to support T * with nmemb, char, and T object of any size. Finally, change alloc_internal. 112 125 Or, just follow the instructions below for that. 113 126 … … 153 166 */ 154 167 155 typedef struct S_align { inline size_t; } T_align; 156 typedef struct S_resize { inline void *; } T_resize; 157 158 forall( T & ) { 159 struct S_fill { char tag; char c; size_t size; T * at; char t[50]; }; 160 struct S_realloc { inline T *; }; 168 #pragma GCC diagnostic push 169 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 170 #pragma GCC diagnostic ignored "-Wuninitialized" 171 172 struct T_align { size_t align; }; 173 struct T_resize { void * addr; }; 174 struct T_realloc { void * addr; }; 175 forall( T & ) struct T_fill { 176 // 'N' => no fill, 'c' => fill with character c, 'a' => fill first N array elements from another array, 177 // 'A' => fill all array elements from another array, 'T' => fill using a T value. 178 char tag; 179 size_t nelem; // number of elements copied from "at" (used with tag 'a') 180 // union { 181 char c; 182 T * at; 183 char t[64]; // T t; 184 // }; 185 }; 186 187 #pragma GCC diagnostic pop 188 189 static inline { 190 T_align ?`align( size_t a ) { return (T_align){ a }; } 191 T_resize ?`resize( void * a ) { return (T_resize){ a }; } 192 T_realloc ?`realloc( void * a ) { return (T_realloc){ a }; } 161 193 } 162 194 163 static inline T_align ?`align( size_t a ) { return (T_align){a}; }164 static inline T_resize ?`resize( void * a ) { return (T_resize){a}; }165 166 extern "C" ssize_t write(int fd, const void *buf, size_t count);167 195 static inline forall( T & | sized(T) ) { 168 S_fill(T) ?`fill ( T t ) { 169 S_fill(T) ret = { 't' }; 196 T_fill(T) ?`fill( char c ) { return (T_fill(T)){ 'c', 0, c }; } 197 T_fill(T) ?`fill( T t ) { 198 T_fill(T) ret = { 'T' }; 170 199 size_t size = sizeof(T); 171 200 if ( size > sizeof(ret.t) ) { … … 175 204 return ret; 176 205 } 177 S_fill(T) ?`fill ( zero_t ) = void; // FIX ME: remove this once ticket 214 is resolved 178 S_fill(T) ?`fill ( T * a ) { return (S_fill(T)){ 'T', '0', 0, a }; } // FIX ME: remove this once ticket 214 is resolved 179 S_fill(T) ?`fill ( char c ) { return (S_fill(T)){ 'c', c }; } 180 S_fill(T) ?`fill ( T a[], size_t nmemb ) { return (S_fill(T)){ 'a', '0', nmemb * sizeof(T), a }; } 181 182 S_realloc(T) ?`realloc ( T * a ) { return (S_realloc(T)){a}; } 183 184 T * alloc_internal$( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T) Fill ) { 185 T * ptr = NULL; 186 size_t size = sizeof(T); 206 T_fill(T) ?`fill( T a[] ) { return (T_fill(T)){ 'A', 0, '\0', a }; } // FIX ME: remove this once ticket 214 is resolved 207 T_fill(T) ?`fill( T a[], size_t nelem ) { return (T_fill(T)){ 'a', nelem * sizeof(T), '\0', a }; } 208 209 // private interface 210 T * alloc_internal$( size_t Dim, T_resize Resize, T_realloc Realloc, size_t Align, T_fill(T) Fill ) { 211 T * ptr; 212 size_t tsize = sizeof(T); 187 213 size_t copy_end = 0; 188 214 189 if ( Resize ) {190 ptr = (T *)(void *)resize( (void *)Resize, Align, Dim *size );191 } else if ( Realloc ) {192 if ( Fill.tag != ' 0' ) copy_end = min(malloc_size( Realloc ), Dim *size );193 ptr = (T *)(void *)realloc( (void *)Realloc, Align, Dim *size );215 if ( Resize.addr ) { 216 ptr = (T *)(void *)resize( Resize.addr, Align, Dim * tsize ); 217 } else if ( Realloc.addr ) { 218 if ( Fill.tag != 'N' ) copy_end = min(malloc_size( Realloc.addr ), Dim * tsize ); 219 ptr = (T *)(void *)realloc( Realloc.addr, Align, Dim * tsize ); 194 220 } else { 195 ptr = (T *)(void *) memalign( Align, Dim *size );196 } 221 ptr = (T *)(void *)memalign( Align, Dim * tsize ); 222 } // if 197 223 198 224 if ( Fill.tag == 'c' ) { 199 memset( (char *)ptr + copy_end, (int)Fill.c, Dim * size - copy_end ); 200 } else if ( Fill.tag == 't' ) { 201 for ( i; copy_end ~ Dim * size ~ size ) { 202 #pragma GCC diagnostic push 203 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 204 assert( size <= sizeof(Fill.t) ); 205 memcpy( (char *)ptr + i, &Fill.t, size ); 206 #pragma GCC diagnostic pop 207 } 225 memset( (char *)ptr + copy_end, (int)Fill.c, Dim * tsize - copy_end ); 226 } else if ( Fill.tag == 'T' ) { 227 for ( i; copy_end ~ Dim * tsize ~ tsize ) { 228 assert( tsize <= sizeof(Fill.t) ); 229 memcpy( (char *)ptr + i, &Fill.t, tsize ); 230 } // for 208 231 } else if ( Fill.tag == 'a' ) { 209 memcpy( (char *)ptr + copy_end, Fill.at, min(Dim * size - copy_end, Fill.size) ); 210 } else if ( Fill.tag == 'T' ) { 211 memcpy( (char *)ptr + copy_end, Fill.at, Dim * size ); 212 } 213 232 memcpy( (char *)ptr + copy_end, Fill.at, min( Dim * tsize - copy_end, Fill.nelem ) ); 233 } else if ( Fill.tag == 'A' ) { 234 memcpy( (char *)ptr + copy_end, Fill.at, Dim * tsize ); 235 } // if 214 236 return ptr; 215 237 } // alloc_internal$ 216 238 217 forall( TT ... | { T * alloc_internal$( void *, T *, size_t, size_t, S_fill(T), TT ); } ) { 218 T * alloc_internal$( void *, T *, size_t Align, size_t Dim, S_fill(T) Fill, T_resize Resize, TT rest ) { 219 return alloc_internal$( Resize, (T*)0p, Align, Dim, Fill, rest); 239 // Dim is a fixed (optional first) parameter, and hence is not set using a postfix function. A dummy parameter is 240 // being overwritten by the postfix argument in the ttype. 241 forall( List ... | { T * alloc_internal$( size_t Dim, T_resize Resize, T_realloc Realloc, size_t Align, T_fill(T) Fill, List ); } ) { 242 // middle interface 243 T * alloc_internal$( size_t Dim, T_resize dummy, T_realloc Realloc, size_t Align, T_fill(T) Fill, T_resize Resize, List rest ) { 244 return alloc_internal$( Dim, Resize, (T_realloc){0p}, Align, Fill, rest ); 220 245 } 221 222 T * alloc_internal$( void *, T *, size_t Align, size_t Dim, S_fill(T) Fill, S_realloc(T) Realloc, TT rest ) { 223 return alloc_internal$( (void*)0p, Realloc, Align, Dim, Fill, rest); 246 T * alloc_internal$( size_t Dim, T_resize Resize, T_realloc dummy, size_t Align, T_fill(T) Fill, T_realloc Realloc, List rest ) { 247 return alloc_internal$( Dim, (T_resize){0p}, Realloc, Align, Fill, rest ); 224 248 } 225 226 T * alloc_internal$( void * Resize, T * Realloc, size_t, size_t Dim, S_fill(T) Fill, T_align Align, TT rest ) { 227 return alloc_internal$( Resize, Realloc, Align, Dim, Fill, rest); 249 T * alloc_internal$( size_t Dim, T_resize Resize, T_realloc Realloc, size_t dummy, T_fill(T) Fill, T_align Align, List rest ) { 250 return alloc_internal$( Dim, Resize, Realloc, Align.align, Fill, rest ); 228 251 } 229 230 T * alloc_internal$( void * Resize, T * Realloc, size_t Align, size_t Dim, S_fill(T), S_fill(T) Fill, TT rest ) { 231 return alloc_internal$( Resize, Realloc, Align, Dim, Fill, rest ); 252 T * alloc_internal$( size_t Dim, T_resize Resize, T_realloc Realloc, size_t Align, T_fill(T) dummy, T_fill(T) Fill, List rest ) { 253 return alloc_internal$( Dim, Resize, Realloc, Align, Fill, rest ); 232 254 } 233 234 T * alloc( TT all) {235 return alloc_internal$( ( void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), (size_t)1, (S_fill(T)){'0'}, all);255 // public interface 256 T * alloc( List rest ) { 257 return alloc_internal$( (size_t)1, (T_resize){0p}, (T_realloc){0p}, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), (T_fill(T)){'N'}, rest ); 236 258 } 237 238 T * alloc( size_t dim, TT all ) { 239 return alloc_internal$( (void*)0p, (T*)0p, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), dim, (S_fill(T)){'0'}, all ); 259 T * alloc( size_t Dim, List rest ) { 260 return alloc_internal$( Dim, (T_resize){0p}, (T_realloc){0p}, (_Alignof(T) > libAlign() ? _Alignof(T) : libAlign()), (T_fill(T)){'N'}, rest ); 240 261 } 241 } // distribution TT262 } // distribution List 242 263 } // distribution T 243 264 244 265 static inline forall( T & | sized(T) ) { 245 266 // CFA safe initialization/copy, i.e., implicit size specification, non-array types 246 T * memset( T * dest, char fill ) { 247 return (T *)memset( dest, fill, sizeof(T) ); 267 T * memset( T * dest, char fill ) { // all combinations of pointer/reference 268 return (T *)memset( dest, fill, sizeof(T) ); // C memset 248 269 } // memset 249 250 T * memcpy( T * dest, const T * src ) { 251 return (T *)memcpy( dest, src, sizeof(T) ); 270 T * memset( T & dest, char fill ) { 271 return (T *)memset( &dest, fill, sizeof(T) ); // C memset 272 } // memset 273 274 T * memcpy( T * dest, const T * src ) { // all combinations of pointer/reference 275 return (T *)memcpy( dest, src, sizeof(T) ); // C memcpy 276 } // memcpy 277 T * memcpy( T & dest, const T & src ) { 278 return (T *)memcpy( &dest, &src, sizeof(T) ); // C memcpy 279 } // memcpy 280 T * memcpy( T * dest, const T & src ) { 281 return (T *)memcpy( dest, &src, sizeof(T) ); // C memcpy 282 } // memcpy 283 T * memcpy( T & dest, const T * src ) { 284 return (T *)memcpy( &dest, src, sizeof(T) ); // C memcpy 252 285 } // memcpy 253 286 … … 263 296 264 297 // CFA deallocation for multiple objects 265 static inline forall( T & ) // FIX ME, problems with 0p in list298 static inline forall( T & ) 266 299 void free( T * ptr ) { 267 300 free( (void *)ptr ); // C free 268 301 } // free 269 static inline forall( T &, TT ... | { void free( TT); } )270 void free( T * ptr, TTrest ) {302 static inline forall( T &, List ... | { void free( List ); } ) 303 void free( T * ptr, List rest ) { 271 304 free( ptr ); 272 305 free( rest ); … … 274 307 275 308 // CFA allocation/deallocation and constructor/destructor, non-array types 276 static inline forall( T & | sized(T), TT ... | { void ?{}( T &, TT); } )277 T * new( TTp ) {309 static inline forall( T & | sized(T), Parms ... | { void ?{}( T &, Parms ); } ) 310 T * new( Parms p ) { 278 311 return &(*(T *)malloc()){ p }; // run constructor 279 312 } // new … … 287 320 free( ptr ); // always call free 288 321 } // delete 289 static inline forall( T &, TT ... | { void ^?{}( T & ); void delete( TT); } )290 void delete( T * ptr, TTrest ) {322 static inline forall( T &, List ... | { void ^?{}( T & ); void delete( List ); } ) 323 void delete( T * ptr, List rest ) { 291 324 delete( ptr ); 292 325 delete( rest ); … … 294 327 295 328 // CFA allocation/deallocation and constructor/destructor, array types 296 forall( T & | sized(T), TT ... | { void ?{}( T &, TT ); } ) T * anew( size_t dim, TTp );329 forall( T & | sized(T), Parms ... | { void ?{}( T &, Parms ); } ) T * anew( size_t dim, Parms p ); 297 330 forall( T & | sized(T) | { void ^?{}( T & ); } ) void adelete( T arr[] ); 298 forall( T & | sized(T) | { void ^?{}( T & ); }, TT ... | { void adelete( TT ); } ) void adelete( T arr[], TT rest ); 331 forall( T & | sized(T) | { void ^?{}( T & ); }, List ... | { void adelete( List ); } ) void adelete( T arr[], List rest ); 332 299 333 //--------------------------------------- 300 334 -
src/AST/Pass.hpp
rcf191ac r7042c60 113 113 static auto read( node_type const * node, Args&&... args ) { 114 114 Pass<core_t> visitor( std::forward<Args>( args )... ); 115 auto const * temp = node->accept( visitor );116 assert( temp == node );117 return visitor.get_result();118 }119 120 // Versions of the above for older compilers.121 template< typename... Args >122 static void run( TranslationUnit & decls ) {123 Pass<core_t> visitor;124 accept_all( decls, visitor );125 }126 127 template< typename node_type, typename... Args >128 static auto read( node_type const * node ) {129 Pass<core_t> visitor;130 115 auto const * temp = node->accept( visitor ); 131 116 assert( temp == node ); -
src/AST/Print.cpp
rcf191ac r7042c60 1579 1579 preprint( node ); 1580 1580 os << "enum attr "; 1581 1582 1583 1584 1585 1586 1587 1581 if ( node->attr == ast::EnumAttribute::Label ) { 1582 os << "Label "; 1583 } else if ( node->attr == ast::EnumAttribute::Value ) { 1584 os << "Value "; 1585 } else { 1586 os << "Posn "; 1587 } 1588 1588 (*(node->instance)).accept( *this ); 1589 1589 return node; -
src/AST/Type.hpp
rcf191ac r7042c60 31 31 // Must be included in *all* AST classes; should be #undef'd at the end of the file 32 32 #define MUTATE_FRIEND \ 33 33 template<typename node_t> friend node_t * mutate(const node_t * node); \ 34 34 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 35 35 … … 322 322 public: 323 323 readonly<EnumInstType> instance; 324 324 EnumAttribute attr; 325 325 const Type * accept( Visitor & v ) const override { return v.visit( this ); } 326 326 EnumAttrType( const EnumInstType * instance, EnumAttribute attr = EnumAttribute::Posn ) 327 327 : instance(instance), attr(attr) {} 328 329 330 331 328 329 bool match( const ast::EnumAttrType * other) const { 330 return instance->base->name == other->instance->base->name && attr == other->attr; 331 } 332 332 private: 333 333 EnumAttrType * clone() const override { return new EnumAttrType{ *this }; } -
src/BasicTypes-gen.cc
rcf191ac r7042c60 415 415 code << "\t" << BYMK << endl; 416 416 code << "\t#define BT ast::BasicKind::" << endl; 417 code << "\tstatic const BTKind commonTypes[BT NUMBER_OF_BASIC_TYPES][BT NUMBER_OF_BASIC_TYPES] = { // nearest common ancestor" << endl417 code << "\tstatic const ast::BasicKind commonTypes[BT NUMBER_OF_BASIC_TYPES][BT NUMBER_OF_BASIC_TYPES] = { // nearest common ancestor" << endl 418 418 << "\t\t/*\t\t "; 419 419 for ( int r = 0; r < NUMBER_OF_BASIC_TYPES; r += 1 ) { // titles -
src/CodeGen/CodeGenerator.cpp
rcf191ac r7042c60 167 167 ast::Pass<CodeGenerator> subCG( acc, subOptions ); 168 168 // Add the forall clause. 169 // TODO: These probably should be removed by now and the assert used.170 169 if ( !decl->type_params.empty() ) { 171 170 assertf( !options.genC, "FunctionDecl forall should not reach code generation." ); … … 174 173 acc << ")" << std::endl; 175 174 } 175 // The forall clause should be printed early as part of the preamble. 176 output << acc.str(); 177 acc.str(""); 176 178 177 179 acc << mangleName( decl ); -
src/Common/PersistentMap.h
rcf191ac r7042c60 23 23 #include <utility> // for forward, move 24 24 25 /// Wraps a hash table in a persistent data structure, using a technique based 26 /// on the persistent array in Conchon & Filliatre "A Persistent Union-Find 25 /// Wraps a hash table in a persistent data structure, using a technique based 26 /// on the persistent array in Conchon & Filliatre "A Persistent Union-Find 27 27 /// Data Structure" 28 28 29 29 template<typename Key, typename Val, 30 31 class PersistentMap 30 typename Hash = std::hash<Key>, typename Eq = std::equal_to<Key>> 31 class PersistentMap 32 32 : public std::enable_shared_from_this<PersistentMap<Key, Val, Hash, Eq>> { 33 33 public: … … 38 38 39 39 /// Types of version nodes 40 enum Mode { 40 enum Mode { 41 41 BASE, ///< Root node of version tree 42 42 REM, ///< Key removal node … … 63 63 Ptr base; ///< Modified map 64 64 Key key; ///< Key removed 65 65 66 66 template<typename P, typename K> 67 67 Rem(P&& p, K&& k) : base(std::forward<P>(p)), key(std::forward<K>(k)) {} … … 155 155 auto it = base_map.find( self.key ); 156 156 157 base->template init<Ins>( 157 base->template init<Ins>( 158 158 mut_this->shared_from_this(), std::move(self.key), std::move(it->second) ); 159 159 base->mode = INS; … … 175 175 auto it = base_map.find( self.key ); 176 176 177 base->template init<Ins>( 177 base->template init<Ins>( 178 178 mut_this->shared_from_this(), std::move(self.key), std::move(it->second) ); 179 179 base->mode = UPD; … … 267 267 Ptr erase(const Key& k) { 268 268 reroot(); 269 269 270 270 // exit early if key does not exist in map 271 271 if ( ! as<Base>().count( k ) ) return this->shared_from_this(); -
src/Common/VectorMap.h
rcf191ac r7042c60 36 36 typedef const value_type* pointer; 37 37 typedef const const_value_type* const_pointer; 38 39 class iterator : public std::iterator< std::random_access_iterator_tag, 40 value_type, 41 difference_type, 42 pointer, 43 reference > { 44 friend class VectorMap; 45 friend class const_iterator; 46 38 39 class iterator : public std::iterator< 40 std::random_access_iterator_tag, 41 value_type, difference_type, pointer, reference > { 42 friend class VectorMap; 43 friend class const_iterator; 44 47 45 value_type data; 48 46 … … 99 97 return data.first == o.data.first && &data.second == &o.data.second; 100 98 } 101 99 102 100 bool operator!= (const iterator& that) const { return !(*this == that); } 103 101 … … 111 109 }; 112 110 113 class const_iterator : public std::iterator< std::bidirectional_iterator_tag, 114 const_value_type, 115 difference_type, 116 const_pointer, 117 const_reference > { 118 friend class VectorMap; 111 class const_iterator : public std::iterator< 112 std::bidirectional_iterator_tag, 113 const_value_type, difference_type, const_pointer, const_reference > { 114 friend class VectorMap; 119 115 const_value_type data; 120 116 … … 181 177 return data.first == o.data.first && &data.second == &o.data.second; 182 178 } 183 179 184 180 bool operator!= (const const_iterator& that) const { return !(*this == that); } 185 181 … … 233 229 234 230 template<typename T> 235 typename VectorMap<T>::iterator operator+ (typename VectorMap<T>::difference_type i, 236 const typename VectorMap<T>::iterator& it) { 231 typename VectorMap<T>::iterator operator+( 232 typename VectorMap<T>::difference_type i, 233 const typename VectorMap<T>::iterator& it) { 237 234 return it + i; 238 235 } 239 236 240 237 template<typename T> 241 typename VectorMap<T>::const_iterator operator+ (typename VectorMap<T>::difference_type i, 242 const typename VectorMap<T>::const_iterator& it) { 238 typename VectorMap<T>::const_iterator operator+( 239 typename VectorMap<T>::difference_type i, 240 const typename VectorMap<T>::const_iterator& it) { 243 241 return it + i; 244 242 } -
src/Concurrency/Actors.cpp
rcf191ac r7042c60 28 28 29 29 struct CollectactorStructDecls : public ast::WithGuards { 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 } 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 auto actorIter = actorStructDecls.find( node->aggr() ); 78 79 80 81 82 83 84 85 86 30 unordered_set<const StructDecl *> & actorStructDecls; 31 unordered_set<const StructDecl *> & messageStructDecls; 32 const StructDecl ** requestDecl; 33 const EnumDecl ** allocationDecl; 34 const StructDecl ** actorDecl; 35 const StructDecl ** msgDecl; 36 StructDecl * parentDecl; 37 bool insideStruct = false; 38 bool namedDecl = false; 39 40 // finds and sets a ptr to the allocation enum, which is needed in the next pass 41 void previsit( const EnumDecl * decl ) { 42 if( decl->name == "allocation" ) *allocationDecl = decl; 43 } 44 45 // finds and sets a ptr to the actor, message, and request structs, which are needed in the next pass 46 void previsit( const StructDecl * decl ) { 47 if ( !decl->body ) return; 48 if ( decl->name == "actor" ) { 49 actorStructDecls.insert( decl ); // skip inserting fwd decl 50 *actorDecl = decl; 51 } else if( decl->name == "message" ) { 52 messageStructDecls.insert( decl ); // skip inserting fwd decl 53 *msgDecl = decl; 54 } else if( decl->name == "request" ) *requestDecl = decl; 55 else { 56 GuardValue(insideStruct); 57 insideStruct = true; 58 parentDecl = mutate( decl ); 59 } 60 } 61 62 // this catches structs of the form: 63 // struct dummy_actor { actor a; }; 64 // since they should be: 65 // struct dummy_actor { inline actor; }; 66 void previsit ( const ObjectDecl * decl ) { 67 if ( insideStruct && ! decl->name.empty() ) { 68 GuardValue(namedDecl); 69 namedDecl = true; 70 } 71 } 72 73 // this collects the derived actor and message struct decl ptrs 74 void postvisit( const StructInstType * node ) { 75 if ( ! *actorDecl || ! *msgDecl ) return; 76 if ( insideStruct && !namedDecl ) { 77 auto actorIter = actorStructDecls.find( node->aggr() ); 78 if ( actorIter != actorStructDecls.end() ) { 79 actorStructDecls.insert( parentDecl ); 80 return; 81 } 82 auto messageIter = messageStructDecls.find( node->aggr() ); 83 if ( messageIter != messageStructDecls.end() ) { 84 messageStructDecls.insert( parentDecl ); 85 } 86 } 87 87 } 88 88 89 89 public: 90 91 const StructDecl ** requestDecl, const EnumDecl ** allocationDecl, const StructDecl ** actorDecl, const StructDecl ** msgDecl ) 92 : actorStructDecls( actorStructDecls ), messageStructDecls( messageStructDecls ), requestDecl( requestDecl ), 93 90 CollectactorStructDecls( unordered_set<const StructDecl *> & actorStructDecls, unordered_set<const StructDecl *> & messageStructDecls, 91 const StructDecl ** requestDecl, const EnumDecl ** allocationDecl, const StructDecl ** actorDecl, const StructDecl ** msgDecl ) 92 : actorStructDecls( actorStructDecls ), messageStructDecls( messageStructDecls ), requestDecl( requestDecl ), 93 allocationDecl( allocationDecl ), actorDecl(actorDecl), msgDecl(msgDecl) {} 94 94 }; 95 95 … … 97 97 class FwdDeclTable { 98 98 99 100 struct FwdDeclData { 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 99 // tracks which decls we have seen so that we can hoist the FunctionDecl to the highest point possible 100 struct FwdDeclData { 101 const StructDecl * actorDecl; 102 const StructDecl * msgDecl; 103 FunctionDecl * fwdDecl; 104 bool actorFound; 105 bool msgFound; 106 107 bool readyToInsert() { return actorFound && msgFound; } 108 bool foundActor() { actorFound = true; return readyToInsert(); } 109 bool foundMsg() { msgFound = true; return readyToInsert(); } 110 111 FwdDeclData( const StructDecl * actorDecl, const StructDecl * msgDecl, FunctionDecl * fwdDecl ) : 112 actorDecl(actorDecl), msgDecl(msgDecl), fwdDecl(fwdDecl), actorFound(false), msgFound(false) {} 113 }; 114 115 // map indexed by actor struct ptr 116 // value is map of all FwdDeclData that contains said actor struct ptr 117 // inner map is indexed by the message struct ptr of FwdDeclData 118 unordered_map<const StructDecl *, unordered_map<const StructDecl *, FwdDeclData *>> actorMap; 119 120 // this map is the same except the outer map is indexed by message ptr and the inner is indexed by actor ptr 121 unordered_map<const StructDecl *, unordered_map<const StructDecl *, FwdDeclData *>> msgMap; 122 123 void insert( const StructDecl * decl, const StructDecl * otherDecl, unordered_map<const StructDecl *, unordered_map<const StructDecl *, FwdDeclData *>> & map, FwdDeclData * data ) { 124 auto iter = map.find( decl ); 125 if ( iter != map.end() ) { // if decl exists in map append data to existing inner map 126 iter->second.emplace( make_pair( otherDecl, data ) ); 127 } else { // else create inner map for key 128 map.emplace( make_pair( decl, unordered_map<const StructDecl *, FwdDeclData *>( { make_pair( otherDecl, data ) } ) ) ); 129 } 130 } 131 131 132 132 public: 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 133 // insert decl into table so that we can fwd declare it later (average cost: O(1)) 134 void insertDecl( const StructDecl * actorDecl, const StructDecl * msgDecl, FunctionDecl * fwdDecl ) { 135 FwdDeclData * declToInsert = new FwdDeclData( actorDecl, msgDecl, fwdDecl ); 136 insert( actorDecl, msgDecl, actorMap, declToInsert ); 137 insert( msgDecl, actorDecl, msgMap, declToInsert ); 138 } 139 140 // returns list of decls to insert after current struct decl 141 // Over the entire pass the runtime of this routine is O(r) where r is the # of receive routines 142 list<FunctionDecl *> updateDecl( const StructDecl * decl, bool isMsg ) { 143 unordered_map<const StructDecl *, unordered_map<const StructDecl *, FwdDeclData *>> & map = isMsg ? msgMap : actorMap; 144 unordered_map<const StructDecl *, unordered_map<const StructDecl *, FwdDeclData *>> & otherMap = isMsg ? actorMap : msgMap; 145 auto iter = map.find( decl ); 146 list<FunctionDecl *> toInsertAfter; // this is populated with decls that are ready to insert 147 if ( iter == map.end() ) return toInsertAfter; 148 149 // iterate over inner map 150 unordered_map<const StructDecl *, FwdDeclData *> & currInnerMap = iter->second; 151 for ( auto innerIter = currInnerMap.begin(); innerIter != currInnerMap.end(); ) { 152 FwdDeclData * currentDatum = innerIter->second; 153 bool readyToInsert = isMsg ? currentDatum->foundMsg() : currentDatum->foundActor(); 154 if ( ! readyToInsert ) { ++innerIter; continue; } 155 156 // readyToInsert is true so we are good to insert the forward decl of the message fn 157 toInsertAfter.push_back( currentDatum->fwdDecl ); 158 159 // need to remove from other map before deleting 160 // find inner map in other map ( other map is actor map if original is msg map and vice versa ) 161 const StructDecl * otherDecl = isMsg ? currentDatum->actorDecl : currentDatum->msgDecl; 162 auto otherMapIter = otherMap.find( otherDecl ); 163 164 unordered_map<const StructDecl *, FwdDeclData *> & otherInnerMap = otherMapIter->second; 165 166 // find the FwdDeclData we need to remove in the other inner map 167 auto otherInnerIter = otherInnerMap.find( decl ); 168 169 // remove references to deleted FwdDeclData from current inner map 170 innerIter = currInnerMap.erase( innerIter ); // this does the increment so no explicit inc needed 171 172 // remove references to deleted FwdDeclData from other inner map 173 otherInnerMap.erase( otherInnerIter ); 174 175 // if other inner map is now empty, remove key from other outer map 176 if ( otherInnerMap.empty() ) 177 otherMap.erase( otherDecl ); 178 179 // now we are safe to delete the FwdDeclData since we are done with it 180 // and we have removed all references to it from our data structures 181 delete currentDatum; 182 } 183 184 // if current inner map is now empty, remove key from outer map. 185 // Have to do this after iterating for safety 186 if ( currInnerMap.empty() ) 187 map.erase( decl ); 188 189 return toInsertAfter; 190 } 191 191 }; 192 192 193 193 // generates the definitions of send operators for actors 194 // collects data needed for next pass that does the circular defn resolution 194 // collects data needed for next pass that does the circular defn resolution 195 195 // for message send operators (via table above) 196 196 struct GenFuncsCreateTables : public ast::WithDeclsToAdd<> { 197 198 199 200 201 202 203 204 205 197 unordered_set<const StructDecl *> & actorStructDecls; 198 unordered_set<const StructDecl *> & messageStructDecls; 199 const StructDecl ** requestDecl; 200 const EnumDecl ** allocationDecl; 201 const StructDecl ** actorDecl; 202 const StructDecl ** msgDecl; 203 FwdDeclTable & forwardDecls; 204 205 // generates the operator for actor message sends 206 206 void postvisit( const FunctionDecl * decl ) { 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 UntypedExpr::createAssign( decl->location, 238 239 240 241 242 243 244 245 UntypedExpr::createAssign( decl->location, 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 */ 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 // More readable synonymous code: 358 359 360 361 362 363 364 365 366 new SingleInit( decl->location, 367 368 369 370 371 372 373 374 207 // return if not of the form receive( param1, param2 ) or if it is a forward decl 208 if ( decl->name != "receive" || decl->params.size() != 2 || !decl->stmts ) return; 209 210 // the params should be references 211 const ReferenceType * derivedActorRef = dynamic_cast<const ReferenceType *>(decl->params.at(0)->get_type()); 212 const ReferenceType * derivedMsgRef = dynamic_cast<const ReferenceType *>(decl->params.at(1)->get_type()); 213 if ( !derivedActorRef || !derivedMsgRef ) return; 214 215 // the references should be to struct instances 216 const StructInstType * arg1InstType = dynamic_cast<const StructInstType *>(derivedActorRef->base.get()); 217 const StructInstType * arg2InstType = dynamic_cast<const StructInstType *>(derivedMsgRef->base.get()); 218 if ( !arg1InstType || !arg2InstType ) return; 219 220 // If the struct instances are derived actor and message types then generate the message send routine 221 auto actorIter = actorStructDecls.find( arg1InstType->aggr() ); 222 auto messageIter = messageStructDecls.find( arg2InstType->aggr() ); 223 if ( actorIter != actorStructDecls.end() && messageIter != messageStructDecls.end() ) { 224 ////////////////////////////////////////////////////////////////////// 225 // The following generates this wrapper for all receive(derived_actor &, derived_msg &) functions 226 /* base_actor and base_msg are output params 227 static inline allocation __CFA_receive_wrap( derived_actor & receiver, derived_msg & msg, actor ** base_actor, message ** base_msg ) { 228 base_actor = &receiver; 229 base_msg = &msg; 230 return receive( receiver, msg ); 231 } 232 */ 233 CompoundStmt * wrapBody = new CompoundStmt( decl->location ); 234 235 // generates: base_actor = &receiver; 236 wrapBody->push_back( new ExprStmt( decl->location, 237 UntypedExpr::createAssign( decl->location, 238 UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "base_actor" ) ), 239 new AddressExpr( decl->location, new NameExpr( decl->location, "receiver" ) ) 240 ) 241 )); 242 243 // generates: base_msg = &msg; 244 wrapBody->push_back( new ExprStmt( decl->location, 245 UntypedExpr::createAssign( decl->location, 246 UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "base_msg" ) ), 247 new AddressExpr( decl->location, new NameExpr( decl->location, "msg" ) ) 248 ) 249 )); 250 251 // generates: return receive( receiver, msg ); 252 wrapBody->push_back( new ReturnStmt( decl->location, 253 new UntypedExpr ( decl->location, 254 new NameExpr( decl->location, "receive" ), 255 { 256 new NameExpr( decl->location, "receiver" ), 257 new NameExpr( decl->location, "msg" ) 258 } 259 ) 260 )); 261 262 // create receive wrapper to extract base message and actor pointer 263 // put it all together into the complete function decl from above 264 FunctionDecl * receiveWrapper = new FunctionDecl( 265 decl->location, 266 "__CFA_receive_wrap", 267 { 268 new ObjectDecl( 269 decl->location, 270 "receiver", 271 ast::deepCopy( derivedActorRef ) 272 ), 273 new ObjectDecl( 274 decl->location, 275 "msg", 276 ast::deepCopy( derivedMsgRef ) 277 ), 278 new ObjectDecl( 279 decl->location, 280 "base_actor", 281 new PointerType( new PointerType( new StructInstType( *actorDecl ) ) ) 282 ), 283 new ObjectDecl( 284 decl->location, 285 "base_msg", 286 new PointerType( new PointerType( new StructInstType( *msgDecl ) ) ) 287 ) 288 }, // params 289 { 290 new ObjectDecl( 291 decl->location, 292 "__CFA_receive_wrap_ret", 293 new EnumInstType( *allocationDecl ) 294 ) 295 }, 296 wrapBody, // body 297 { Storage::Static }, // storage 298 Linkage::Cforall, // linkage 299 {}, // attributes 300 { Function::Inline } 301 ); 302 303 declsToAddAfter.push_back( receiveWrapper ); 304 305 ////////////////////////////////////////////////////////////////////// 306 // The following generates this send message operator routine for all receive(derived_actor &, derived_msg &) functions 307 /* 308 static inline derived_actor & ?|?( derived_actor & receiver, derived_msg & msg ) { 309 request new_req; 310 allocation (*my_work_fn)( derived_actor &, derived_msg & ) = receive; 311 __receive_fn fn = (__receive_fn)my_work_fn; 312 new_req{ &receiver, &msg, fn }; 313 send( receiver, new_req ); 314 return receiver; 315 } 316 */ 317 CompoundStmt * sendBody = new CompoundStmt( decl->location ); 318 319 // Generates: request new_req; 320 sendBody->push_back( new DeclStmt( 321 decl->location, 322 new ObjectDecl( 323 decl->location, 324 "new_req", 325 new StructInstType( *requestDecl ) 326 ) 327 )); 328 329 // Function type is: allocation (*)( derived_actor &, derived_msg &, actor **, message ** ) 330 FunctionType * derivedReceive = new FunctionType(); 331 derivedReceive->params.push_back( ast::deepCopy( derivedActorRef ) ); 332 derivedReceive->params.push_back( ast::deepCopy( derivedMsgRef ) ); 333 derivedReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *actorDecl ) ) ) ); 334 derivedReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *msgDecl ) ) ) ); 335 derivedReceive->returns.push_back( new EnumInstType( *allocationDecl ) ); 336 337 // Generates: allocation (*my_work_fn)( derived_actor &, derived_msg &, actor **, message ** ) = receive; 338 sendBody->push_back( new DeclStmt( 339 decl->location, 340 new ObjectDecl( 341 decl->location, 342 "my_work_fn", 343 new PointerType( derivedReceive ), 344 new SingleInit( decl->location, new NameExpr( decl->location, "__CFA_receive_wrap" ) ) 345 ) 346 )); 347 348 // Function type is: allocation (*)( actor &, message & ) 349 FunctionType * genericReceive = new FunctionType(); 350 genericReceive->params.push_back( new ReferenceType( new StructInstType( *actorDecl ) ) ); 351 genericReceive->params.push_back( new ReferenceType( new StructInstType( *msgDecl ) ) ); 352 genericReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *actorDecl ) ) ) ); 353 genericReceive->params.push_back( new PointerType( new PointerType( new StructInstType( *msgDecl ) ) ) ); 354 genericReceive->returns.push_back( new EnumInstType( *allocationDecl ) ); 355 356 // Generates: allocation (*fn)( actor &, message & ) = (allocation (*)( actor &, message & ))my_work_fn; 357 // More readable synonymous code: 358 // typedef allocation (*__receive_fn)(actor &, message &); 359 // __receive_fn fn = (__receive_fn)my_work_fn; 360 sendBody->push_back( new DeclStmt( 361 decl->location, 362 new ObjectDecl( 363 decl->location, 364 "fn", 365 new PointerType( genericReceive ), 366 new SingleInit( decl->location, 367 new CastExpr( decl->location, new NameExpr( decl->location, "my_work_fn" ), new PointerType( genericReceive ), ExplicitCast ) 368 ) 369 ) 370 )); 371 372 // Generates: new_req{ (actor *)&receiver, (message *)&msg, fn }; 373 sendBody->push_back( new ExprStmt( 374 decl->location, 375 375 new UntypedExpr ( 376 decl->location, 376 decl->location, 377 377 new NameExpr( decl->location, "?{}" ), 378 378 { 379 379 new NameExpr( decl->location, "new_req" ), 380 381 382 380 new CastExpr( decl->location, new AddressExpr( new NameExpr( decl->location, "receiver" ) ), new PointerType( new StructInstType( *actorDecl ) ), ExplicitCast ), 381 new CastExpr( decl->location, new AddressExpr( new NameExpr( decl->location, "msg" ) ), new PointerType( new StructInstType( *msgDecl ) ), ExplicitCast ), 382 new NameExpr( decl->location, "fn" ) 383 383 } 384 384 ) 385 385 )); 386 386 387 388 389 387 // Generates: send( receiver, new_req ); 388 sendBody->push_back( new ExprStmt( 389 decl->location, 390 390 new UntypedExpr ( 391 391 decl->location, 392 392 new NameExpr( decl->location, "send" ), 393 393 { 394 394 { 395 396 397 395 new NameExpr( decl->location, "receiver" ), 396 new NameExpr( decl->location, "new_req" ) 397 } 398 398 } 399 399 ) 400 400 )); 401 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 402 // Generates: return receiver; 403 sendBody->push_back( new ReturnStmt( decl->location, new NameExpr( decl->location, "receiver" ) ) ); 404 405 // put it all together into the complete function decl from above 406 FunctionDecl * sendOperatorFunction = new FunctionDecl( 407 decl->location, 408 "?|?", 409 { 410 new ObjectDecl( 411 decl->location, 412 "receiver", 413 ast::deepCopy( derivedActorRef ) 414 ), 415 new ObjectDecl( 416 decl->location, 417 "msg", 418 ast::deepCopy( derivedMsgRef ) 419 ) 420 }, // params 421 { 422 new ObjectDecl( 423 decl->location, 424 "receiver_ret", 425 ast::deepCopy( derivedActorRef ) 426 ) 427 }, 428 nullptr, // body 429 { Storage::Static }, // storage 430 Linkage::Cforall, // linkage 431 {}, // attributes 432 { Function::Inline } 433 ); 434 435 // forward decls to resolve use before decl problem for '|' routines 436 forwardDecls.insertDecl( *actorIter, *messageIter , ast::deepCopy( sendOperatorFunction ) ); 437 438 sendOperatorFunction->stmts = sendBody; 439 declsToAddAfter.push_back( sendOperatorFunction ); 440 } 441 441 } 442 442 443 443 public: 444 445 const StructDecl ** requestDecl, const EnumDecl ** allocationDecl, const StructDecl ** actorDecl, const StructDecl ** msgDecl, 446 FwdDeclTable & forwardDecls ) : actorStructDecls(actorStructDecls), messageStructDecls(messageStructDecls), 447 444 GenFuncsCreateTables( unordered_set<const StructDecl *> & actorStructDecls, unordered_set<const StructDecl *> & messageStructDecls, 445 const StructDecl ** requestDecl, const EnumDecl ** allocationDecl, const StructDecl ** actorDecl, const StructDecl ** msgDecl, 446 FwdDeclTable & forwardDecls ) : actorStructDecls(actorStructDecls), messageStructDecls(messageStructDecls), 447 requestDecl(requestDecl), allocationDecl(allocationDecl), actorDecl(actorDecl), msgDecl(msgDecl), forwardDecls(forwardDecls) {} 448 448 }; 449 449 … … 452 452 // generates the forward declarations of the send operator for actor routines 453 453 struct FwdDeclOperator : public ast::WithDeclsToAdd<> { 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 454 unordered_set<const StructDecl *> & actorStructDecls; 455 unordered_set<const StructDecl *> & messageStructDecls; 456 FwdDeclTable & forwardDecls; 457 458 // handles forward declaring the message operator 459 void postvisit( const StructDecl * decl ) { 460 list<FunctionDecl *> toAddAfter; 461 auto actorIter = actorStructDecls.find( decl ); 462 if ( actorIter != actorStructDecls.end() ) { // this is a derived actor decl 463 // get list of fwd decls that we can now insert 464 toAddAfter = forwardDecls.updateDecl( decl, false ); 465 466 // get rid of decl from actorStructDecls since we no longer need it 467 actorStructDecls.erase( actorIter ); 468 } else { 469 auto messageIter = messageStructDecls.find( decl ); 470 if ( messageIter == messageStructDecls.end() ) return; 471 472 toAddAfter = forwardDecls.updateDecl( decl, true ); 473 474 // get rid of decl from messageStructDecls since we no longer need it 475 messageStructDecls.erase( messageIter ); 476 } 477 478 // add the fwd decls to declsToAddAfter 479 for ( FunctionDecl * func : toAddAfter ) { 480 declsToAddAfter.push_back( func ); 481 } 482 } 483 483 484 484 public: 485 FwdDeclOperator( unordered_set<const StructDecl *> & actorStructDecls, unordered_set<const StructDecl *> & messageStructDecls, 486 485 FwdDeclOperator( unordered_set<const StructDecl *> & actorStructDecls, unordered_set<const StructDecl *> & messageStructDecls, 486 FwdDeclTable & forwardDecls ) : actorStructDecls(actorStructDecls), messageStructDecls(messageStructDecls), forwardDecls(forwardDecls) {} 487 487 }; 488 488 489 489 void implementActors( TranslationUnit & translationUnit ) { 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 Pass<CollectactorStructDecls>::run( translationUnit, actorStructDecls, messageStructDecls, requestDecl, 511 512 513 514 if ( !allocationDeclPtr || !requestDeclPtr || !actorDeclPtr || !msgDeclPtr ) 515 516 517 518 519 Pass<GenFuncsCreateTables>::run( translationUnit, actorStructDecls, messageStructDecls, requestDecl, 520 521 522 523 490 // unordered_maps to collect all derived actor and message types 491 unordered_set<const StructDecl *> actorStructDecls; 492 unordered_set<const StructDecl *> messageStructDecls; 493 FwdDeclTable forwardDecls; 494 495 // for storing through the passes 496 // these are populated with various important struct decls 497 const StructDecl * requestDeclPtr = nullptr; 498 const EnumDecl * allocationDeclPtr = nullptr; 499 const StructDecl * actorDeclPtr = nullptr; 500 const StructDecl * msgDeclPtr = nullptr; 501 502 // double pointer to modify local ptrs above 503 const StructDecl ** requestDecl = &requestDeclPtr; 504 const EnumDecl ** allocationDecl = &allocationDeclPtr; 505 const StructDecl ** actorDecl = &actorDeclPtr; 506 const StructDecl ** msgDecl = &msgDeclPtr; 507 508 // first pass collects ptrs to allocation enum, request type, and generic receive fn typedef 509 // also populates maps of all derived actors and messages 510 Pass<CollectactorStructDecls>::run( translationUnit, actorStructDecls, messageStructDecls, requestDecl, 511 allocationDecl, actorDecl, msgDecl ); 512 513 // check that we have found all the decls we need from <actor.hfa>, if not no need to run the rest of this pass 514 if ( !allocationDeclPtr || !requestDeclPtr || !actorDeclPtr || !msgDeclPtr ) 515 return; 516 517 // second pass locates all receive() routines that overload the generic receive fn 518 // it then generates the appropriate operator '|' send routines for the receive routines 519 Pass<GenFuncsCreateTables>::run( translationUnit, actorStructDecls, messageStructDecls, requestDecl, 520 allocationDecl, actorDecl, msgDecl, forwardDecls ); 521 522 // The third pass forward declares operator '|' send routines 523 Pass<FwdDeclOperator>::run( translationUnit, actorStructDecls, messageStructDecls, forwardDecls ); 524 524 } 525 526 525 527 526 } // namespace Concurrency -
src/Concurrency/Corun.cpp
rcf191ac r7042c60 26 26 27 27 struct CorunKeyword : public WithDeclsToAdd<>, public WithStmtsToAdd<> { 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 fnBody->push_back( new DeclStmt( loc, 90 91 92 93 94 95 new CastExpr( loc, 96 new NameExpr( loc, coforArgName ), 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 new SingleInit( loc, 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 new SingleInit( loc, 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 28 UniqueName CorunFnNamer = "__CFA_corun_lambda_"s; 29 UniqueName CoforFnNamer = "__CFA_cofor_lambda_"s; 30 // UniqueName CoforFnVarNamer = "__CFA_cofor_lambda_var"s; 31 UniqueName RunnerBlockNamer = "__CFA_corun_block_"s; 32 33 string coforArgName = "__CFA_cofor_lambda_arg"; 34 string numProcsName = "__CFA_cofor_num_procs"; 35 string currProcsName = "__CFA_cofor_curr_procs"; 36 string thdArrName = "__CFA_cofor_thread_array"; 37 string loopTempName = "__CFA_cofor_loop_temp"; 38 39 40 const StructDecl * runnerBlockDecl = nullptr; 41 const StructDecl * coforRunnerDecl = nullptr; 42 43 // Finds runner_block (corun task) and cofor_runner (cofor task) decls 44 void previsit( const StructDecl * decl ) { 45 if ( !decl->body ) { 46 return; 47 } else if ( "runner_block" == decl->name ) { 48 assert( !runnerBlockDecl ); 49 runnerBlockDecl = decl; 50 } else if ( "cofor_runner" == decl->name ) { 51 assert( !coforRunnerDecl ); 52 coforRunnerDecl = decl; 53 } 54 } 55 56 // codegen for cofor statements 57 Stmt * postvisit( const CoforStmt * stmt ) { 58 if ( !runnerBlockDecl || !coforRunnerDecl ) 59 SemanticError( stmt->location, "To use cofor statements add #include <cofor.hfa>" ); 60 61 if ( stmt->inits.size() != 1 ) 62 SemanticError( stmt->location, "Cofor statements must have a single initializer in the loop control" ); 63 64 if ( !stmt->body ) 65 return nullptr; 66 67 const CodeLocation & loc = stmt->location; 68 const string fnName = CoforFnNamer.newName(); 69 70 CompoundStmt * body = new CompoundStmt( loc ); 71 72 // push back cofor initializer to generated body 73 body->push_back( deepCopy( stmt->inits.at(0) ) ); 74 75 CompoundStmt * fnBody = new CompoundStmt( loc ); 76 77 const DeclStmt * declStmtPtr = dynamic_cast<const DeclStmt *>(stmt->inits.at(0).get()); 78 if ( ! declStmtPtr ) 79 SemanticError( stmt->location, "Cofor statement initializer is somehow not a decl statement?" ); 80 81 const Decl * declPtr = dynamic_cast<const Decl *>(declStmtPtr->decl.get()); 82 if ( ! declPtr ) 83 SemanticError( stmt->location, "Cofor statement initializer is somehow not a decl?" ); 84 85 Type * initType = new TypeofType( new NameExpr( loc, declPtr->name ) ); 86 87 // Generates: 88 // typeof(init) __CFA_cofor_lambda_var = *((typeof(init) *)val); 89 fnBody->push_back( new DeclStmt( loc, 90 new ObjectDecl( loc, 91 declPtr->name, 92 initType, 93 new SingleInit( loc, 94 UntypedExpr::createDeref( loc, 95 new CastExpr( loc, 96 new NameExpr( loc, coforArgName ), 97 new PointerType( initType ), ExplicitCast 98 ) 99 ) 100 ) 101 ) 102 )); 103 104 // push rest of cofor body into loop lambda 105 fnBody->push_back( deepCopy( stmt->body ) ); 106 107 // Generates: 108 // void __CFA_cofor_lambda_() { 109 // typeof(init) __CFA_cofor_lambda_var = *((typeof(init) *)val); 110 // stmt->body; 111 // } 112 Stmt * coforLambda = new DeclStmt( loc, 113 new FunctionDecl( loc, 114 fnName, // name 115 { 116 new ObjectDecl( loc, 117 coforArgName, 118 new ast::PointerType( new ast::VoidType() ) 119 ) 120 }, // params 121 {}, // return 122 fnBody // body 123 ) 124 ); 125 body->push_back( coforLambda ); 126 127 // Generates: 128 // unsigned __CFA_cofor_num_procs = get_proc_count(); 129 body->push_back( new DeclStmt( loc, 130 new ObjectDecl( loc, 131 numProcsName, 132 new BasicType( BasicKind::UnsignedInt ), 133 new SingleInit( loc, 134 new UntypedExpr( loc, 135 new NameExpr( loc, "get_proc_count" ), 136 {} 137 ) 138 ) 139 ) 140 ) 141 ); 142 143 // Generates: 144 // unsigned __CFA_cofor_curr_procs = 0; 145 body->push_back( new DeclStmt( loc, 146 new ObjectDecl( loc, 147 currProcsName, 148 new BasicType( BasicKind::UnsignedInt ), 149 new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) ) 150 ) 151 ) 152 ); 153 154 // Generates: 155 // unsigned cofor_runner __CFA_cofor_thread_array[nprocs]; 156 body->push_back( new DeclStmt( loc, 157 new ObjectDecl( loc, 158 thdArrName, 159 new ast::ArrayType( 160 new StructInstType( coforRunnerDecl ), 161 new NameExpr( loc, numProcsName ), 162 ast::FixedLen, 163 ast::DynamicDim 164 ) 165 ) 166 ) 167 ); 168 169 // Generates: 170 // start_runners( __CFA_cofor_thread_array, __CFA_cofor_num_procs, __CFA_cofor_lambda_ ); 171 body->push_back( new ExprStmt( loc, 172 new UntypedExpr( loc, 173 new NameExpr( loc, "start_runners" ), 174 { 175 new NameExpr( loc, thdArrName ), 176 new NameExpr( loc, numProcsName ), 177 new NameExpr( loc, fnName ) 178 } 179 ) 180 )); 181 182 // Generates: 183 // typeof(initializer) * __CFA_cofor_loop_temp = malloc(); 184 CompoundStmt * forLoopBody = new CompoundStmt( loc ); 185 forLoopBody->push_back( new DeclStmt( loc, 186 new ObjectDecl( loc, 187 loopTempName, 188 new PointerType( initType ), 189 new SingleInit( loc, 190 new UntypedExpr( loc, 191 new NameExpr( loc, "malloc" ), 192 {} 193 ) 194 ) 195 ) 196 ) 197 ); 198 199 // Generates: 200 // *__CFA_cofor_loop_temp = initializer; 201 forLoopBody->push_back( new ExprStmt( loc, 202 UntypedExpr::createAssign( loc, 203 UntypedExpr::createDeref( loc, new NameExpr( loc, loopTempName ) ), 204 new NameExpr( loc, declPtr->name ) 205 ) 206 )); 207 208 // Generates: 209 // send_work( __CFA_cofor_thread_array, __CFA_cofor_num_procs, 210 // __CFA_cofor_curr_procs, __CFA_cofor_loop_temp ); 211 forLoopBody->push_back( new ExprStmt( loc, 212 new UntypedExpr( loc, 213 new NameExpr( loc, "send_work" ), 214 { 215 new NameExpr( loc, thdArrName ), 216 new NameExpr( loc, numProcsName ), 217 new NameExpr( loc, currProcsName ), 218 new NameExpr( loc, loopTempName ) 219 } 220 ) 221 )); 222 223 body->push_back( new ForStmt( loc, 224 {}, 225 deepCopy( stmt->cond ), 226 deepCopy( stmt->inc ), 227 forLoopBody 228 )); 229 230 // Generates: 231 // end_runners( __CFA_cofor_thread_array, __CFA_cofor_num_procs ); 232 body->push_back( new ExprStmt( loc, 233 new UntypedExpr( loc, 234 new NameExpr( loc, "end_runners" ), 235 { 236 new NameExpr( loc, thdArrName ), 237 new NameExpr( loc, numProcsName ) 238 } 239 ) 240 )); 241 242 return body; 243 } 244 245 // codegen for corun statements 246 Stmt * postvisit( const CorunStmt * stmt ) { 247 if ( !runnerBlockDecl || !coforRunnerDecl ) 248 SemanticError( stmt->location, "To use corun statements add #include <cofor.hfa>" ); 249 250 if ( !stmt->stmt ) 251 return nullptr; 252 253 const CodeLocation & loc = stmt->location; 254 const string fnName = CorunFnNamer.newName(); 255 const string objName = RunnerBlockNamer.newName(); 256 257 // Generates: 258 // void __CFA_corun_lambda_() { ... stmt->stmt ... } 259 Stmt * runnerLambda = new DeclStmt( loc, 260 new FunctionDecl( loc, 261 fnName, // name 262 {}, // params 263 {}, // return 264 new CompoundStmt( loc, { deepCopy(stmt->stmt) } ) // body 265 ) 266 ); 267 268 // Generates: 269 // runner_block __CFA_corun_block_; 270 Stmt * objDecl = new DeclStmt( loc, 271 new ObjectDecl( loc, 272 objName, 273 new StructInstType( runnerBlockDecl ) 274 ) 275 ); 276 277 // Generates: 278 // __CFA_corun_block_{ __CFA_corun_lambda_ }; 279 Stmt * threadStart = new ExprStmt( loc, 280 new UntypedExpr ( loc, 281 new NameExpr( loc, "?{}" ), 282 { 283 new NameExpr( loc, objName ), 284 new NameExpr( loc, fnName ) 285 } 286 ) 287 ); 288 289 stmtsToAddBefore.push_back( runnerLambda ); 290 stmtsToAddBefore.push_back( objDecl ); 291 292 return threadStart; 293 } 294 294 }; 295 295 296 296 void implementCorun( TranslationUnit & translationUnit ) { 297 297 Pass<CorunKeyword>::run( translationUnit ); 298 298 } 299 299 -
src/Concurrency/Keywords.cpp
rcf191ac r7042c60 991 991 ast::CompoundStmt * body = 992 992 new ast::CompoundStmt( stmt->location, { stmt->stmt } ); 993 993 994 994 return addStatements( body, stmt->mutexObjs );; 995 995 } … … 1180 1180 1181 1181 // generates a cast to the void ptr to the appropriate lock type and dereferences it before calling lock or unlock on it 1182 // used to undo the type erasure done by storing all the lock pointers as void 1182 // used to undo the type erasure done by storing all the lock pointers as void 1183 1183 ast::ExprStmt * MutexKeyword::genVirtLockUnlockExpr( const std::string & fnName, ast::ptr<ast::Expr> expr, const CodeLocation & location, ast::Expr * param ) { 1184 1184 return new ast::ExprStmt( location, … … 1187 1187 ast::UntypedExpr::createDeref( 1188 1188 location, 1189 new ast::CastExpr( location, 1189 new ast::CastExpr( location, 1190 1190 param, 1191 1191 new ast::PointerType( new ast::TypeofType( new ast::UntypedExpr( … … 1208 1208 //adds an if/elif clause for each lock to assign type from void ptr based on ptr address 1209 1209 for ( long unsigned int i = 0; i < args.size(); i++ ) { 1210 1210 1211 1211 ast::UntypedExpr * ifCond = new ast::UntypedExpr( location, 1212 1212 new ast::NameExpr( location, "?==?" ), { … … 1216 1216 ); 1217 1217 1218 ast::IfStmt * currLockIf = new ast::IfStmt( 1218 ast::IfStmt * currLockIf = new ast::IfStmt( 1219 1219 location, 1220 1220 ifCond, 1221 1221 genVirtLockUnlockExpr( fnName, args.at(i), location, ast::deepCopy( thisParam ) ) 1222 1222 ); 1223 1223 1224 1224 if ( i == 0 ) { 1225 1225 outerLockIf = currLockIf; … … 1235 1235 1236 1236 void flattenTuple( const ast::UntypedTupleExpr * tuple, std::vector<ast::ptr<ast::Expr>> & output ) { 1237 1238 1239 1240 1241 1237 for ( auto & expr : tuple->exprs ) { 1238 const ast::UntypedTupleExpr * innerTuple = dynamic_cast<const ast::UntypedTupleExpr *>(expr.get()); 1239 if ( innerTuple ) flattenTuple( innerTuple, output ); 1240 else output.emplace_back( ast::deepCopy( expr )); 1241 } 1242 1242 } 1243 1243 … … 1255 1255 // std::string unlockFnName = mutex_func_namer.newName(); 1256 1256 1257 1258 1259 1260 1261 1262 1263 1257 // If any arguments to the mutex stmt are tuples, flatten them 1258 std::vector<ast::ptr<ast::Expr>> flattenedArgs; 1259 for ( auto & arg : args ) { 1260 const ast::UntypedTupleExpr * tuple = dynamic_cast<const ast::UntypedTupleExpr *>(args.at(0).get()); 1261 if ( tuple ) flattenTuple( tuple, flattenedArgs ); 1262 else flattenedArgs.emplace_back( ast::deepCopy( arg )); 1263 } 1264 1264 1265 1265 // Make pointer to the monitors. … … 1302 1302 // adds a nested try stmt for each lock we are locking 1303 1303 for ( long unsigned int i = 0; i < flattenedArgs.size(); i++ ) { 1304 ast::UntypedExpr * innerAccess = new ast::UntypedExpr( 1304 ast::UntypedExpr * innerAccess = new ast::UntypedExpr( 1305 1305 location, 1306 1306 new ast::NameExpr( location,"?[?]" ), { … … 1426 1426 // ); 1427 1427 1428 // ast::IfStmt * currLockIf = new ast::IfStmt( 1428 // ast::IfStmt * currLockIf = new ast::IfStmt( 1429 1429 // location, 1430 1430 // ast::deepCopy( ifCond ), … … 1432 1432 // ); 1433 1433 1434 // ast::IfStmt * currUnlockIf = new ast::IfStmt( 1434 // ast::IfStmt * currUnlockIf = new ast::IfStmt( 1435 1435 // location, 1436 1436 // ifCond, 1437 1437 // genVirtLockUnlockExpr( "unlock", args.at(i), location, ast::deepCopy( thisParam ) ) 1438 1438 // ); 1439 1439 1440 1440 // if ( i == 0 ) { 1441 1441 // outerLockIf = currLockIf; … … 1450 1450 // lastUnlockIf = currUnlockIf; 1451 1451 // } 1452 1452 1453 1453 // // add pointer typing if/elifs to body of routines 1454 1454 // lock_decl->stmts = new ast::CompoundStmt( location, { outerLockIf } ); -
src/Concurrency/Waituntil.cpp
rcf191ac r7042c60 31 31 /* So this is what this pass dones: 32 32 { 33 when ( condA ) waituntil( A ){ doA(); } 34 or when ( condB ) waituntil( B ){ doB(); } 35 33 when ( condA ) waituntil( A ){ doA(); } 34 or when ( condB ) waituntil( B ){ doB(); } 35 and when ( condC ) waituntil( C ) { doC(); } 36 36 } 37 37 || … … 42 42 Generates these two routines: 43 43 static inline bool is_full_sat_1( int * clause_statuses ) { 44 return clause_statuses[0] 45 46 44 return clause_statuses[0] 45 || clause_statuses[1] 46 && clause_statuses[2]; 47 47 } 48 48 49 49 static inline bool is_done_sat_1( int * clause_statuses ) { 50 51 52 50 return has_run(clause_statuses[0]) 51 || has_run(clause_statuses[1]) 52 && has_run(clause_statuses[2]); 53 53 } 54 54 55 55 Replaces the waituntil statement above with the following code: 56 56 { 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 ... repeat ^ for B and C ... 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 doA(); 127 128 57 // used with atomic_dec/inc to get binary semaphore behaviour 58 int park_counter = 0; 59 60 // status (one for each clause) 61 int clause_statuses[3] = { 0 }; 62 63 bool whenA = condA; 64 bool whenB = condB; 65 bool whenC = condC; 66 67 if ( !whenB ) clause_statuses[1] = __SELECT_RUN; 68 if ( !whenC ) clause_statuses[2] = __SELECT_RUN; 69 70 // some other conditional settors for clause_statuses are set here, see genSubtreeAssign and related routines 71 72 // three blocks 73 // for each block, create, setup, then register select_node 74 select_node clause1; 75 select_node clause2; 76 select_node clause3; 77 78 try { 79 if ( whenA ) { register_select(A, clause1); setup_clause( clause1, &clause_statuses[0], &park_counter ); } 80 ... repeat ^ for B and C ... 81 82 // if else clause is defined a separate branch can occur here to set initial values, see genWhenStateConditions 83 84 // loop & park until done 85 while( !is_full_sat_1( clause_statuses ) ) { 86 87 // binary sem P(); 88 if ( __atomic_sub_fetch( &park_counter, 1, __ATOMIC_SEQ_CST) < 0 ) 89 park(); 90 91 // execute any blocks available with status set to 0 92 for ( int i = 0; i < 3; i++ ) { 93 if (clause_statuses[i] == __SELECT_SAT) { 94 switch (i) { 95 case 0: 96 try { 97 on_selected( A, clause1 ); 98 doA(); 99 } 100 finally { clause_statuses[i] = __SELECT_RUN; unregister_select(A, clause1); } 101 break; 102 case 1: 103 ... same gen as A but for B and clause2 ... 104 break; 105 case 2: 106 ... same gen as A but for C and clause3 ... 107 break; 108 } 109 } 110 } 111 } 112 113 // ensure that the blocks that triggered is_full_sat_1 are run 114 // by running every un-run block that is SAT from the start until 115 // the predicate is SAT when considering RUN status = true 116 for ( int i = 0; i < 3; i++ ) { 117 if (is_done_sat_1( clause_statuses )) break; 118 if (clause_statuses[i] == __SELECT_SAT) 119 ... Same if body here as in loop above ... 120 } 121 } finally { 122 // the unregister and on_selected calls are needed to support primitives where the acquire has side effects 123 // so the corresponding block MUST be run for those primitives to not lose state (example is channels) 124 if ( !has_run(clause_statuses[0]) && whenA && unregister_select(A, clause1) ) 125 on_selected( A, clause1 ) 126 doA(); 127 ... repeat if above for B and C ... 128 } 129 129 } 130 130 … … 134 134 135 135 class GenerateWaitUntilCore final { 136 136 vector<FunctionDecl *> & satFns; 137 137 UniqueName namer_sat = "__is_full_sat_"s; 138 138 UniqueName namer_run = "__is_run_sat_"s; 139 139 UniqueName namer_park = "__park_counter_"s; 140 140 UniqueName namer_status = "__clause_statuses_"s; 141 141 UniqueName namer_node = "__clause_"s; 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 // This first set of routines are all used to do the complicated job of 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 142 UniqueName namer_target = "__clause_target_"s; 143 UniqueName namer_when = "__when_cond_"s; 144 UniqueName namer_label = "__waituntil_label_"s; 145 146 string idxName = "__CFA_clause_idx_"; 147 148 struct ClauseData { 149 string nodeName; 150 string targetName; 151 string whenName; 152 int index; 153 string & statusName; 154 ClauseData( int index, string & statusName ) : index(index), statusName(statusName) {} 155 }; 156 157 const StructDecl * selectNodeDecl = nullptr; 158 159 // This first set of routines are all used to do the complicated job of 160 // dealing with how to set predicate statuses with certain when_conds T/F 161 // so that the when_cond == F effectively makes that clause "disappear" 162 void updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow ); 163 void paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow ); 164 bool paintWhenTree( WaitUntilStmt::ClauseNode * currNode ); 165 void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd ); 166 void collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs ); 167 void updateWhenState( WaitUntilStmt::ClauseNode * currNode ); 168 void genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ); 169 void genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ); 170 CompoundStmt * getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData ); 171 Stmt * genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx ); 172 173 // These routines are just code-gen helpers 174 void addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName ); 175 void setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body ); 176 CompoundStmt * genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName ); 177 Expr * genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName ); 178 CompoundStmt * genStmtBlock( const WhenClause * clause, const ClauseData * data ); 179 Stmt * genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData ); 180 Stmt * genNoElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData ); 181 void genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName ); 182 Stmt * recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName ); 183 Stmt * buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data ); 184 Stmt * genAllOr( const WaitUntilStmt * stmt ); 185 185 186 186 public: 187 187 void previsit( const StructDecl * decl ); 188 188 Stmt * postvisit( const WaitUntilStmt * stmt ); 189 189 GenerateWaitUntilCore( vector<FunctionDecl *> & satFns ): satFns(satFns) {} 190 190 }; 191 191 192 192 // Finds select_node decl 193 193 void GenerateWaitUntilCore::previsit( const StructDecl * decl ) { 194 194 if ( !decl->body ) { 195 195 return; 196 196 } else if ( "select_node" == decl->name ) { … … 201 201 202 202 void GenerateWaitUntilCore::updateAmbiguousWhen( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool andBelow, bool orBelow ) { 203 204 205 206 207 203 // all children when-ambiguous 204 if ( currNode->left->ambiguousWhen && currNode->right->ambiguousWhen ) 205 // true iff an ancestor/descendant has a different operation 206 currNode->ambiguousWhen = (orAbove || orBelow) && (andBelow || andAbove); 207 // ambiguousWhen is initially false so theres no need to set it here 208 208 } 209 209 … … 215 215 // - All of its descendent clauses are optional, i.e. they have a when_cond defined on the WhenClause 216 216 void GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode, bool andAbove, bool orAbove, bool & andBelow, bool & orBelow ) { 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 217 bool aBelow = false; // updated by child nodes 218 bool oBelow = false; // updated by child nodes 219 switch (currNode->op) { 220 case WaitUntilStmt::ClauseNode::AND: 221 paintWhenTree( currNode->left, true, orAbove, aBelow, oBelow ); 222 paintWhenTree( currNode->right, true, orAbove, aBelow, oBelow ); 223 224 // update currNode's when flag based on conditions listed in fn signature comment above 225 updateAmbiguousWhen(currNode, true, orAbove, aBelow, oBelow ); 226 227 // set return flags to tell parents which decendant ops have been seen 228 andBelow = true; 229 orBelow = oBelow; 230 return; 231 case WaitUntilStmt::ClauseNode::OR: 232 paintWhenTree( currNode->left, andAbove, true, aBelow, oBelow ); 233 paintWhenTree( currNode->right, andAbove, true, aBelow, oBelow ); 234 235 // update currNode's when flag based on conditions listed in fn signature comment above 236 updateAmbiguousWhen(currNode, andAbove, true, aBelow, oBelow ); 237 238 // set return flags to tell parents which decendant ops have been seen 239 andBelow = aBelow; 240 orBelow = true; 241 return; 242 case WaitUntilStmt::ClauseNode::LEAF: 243 if ( currNode->leaf->when_cond ) 244 currNode->ambiguousWhen = true; 245 return; 246 default: 247 assertf(false, "Unreachable waituntil clause node type. How did you get here???"); 248 } 249 249 } 250 250 … … 252 252 // returns true if entire tree is OR's (special case) 253 253 bool GenerateWaitUntilCore::paintWhenTree( WaitUntilStmt::ClauseNode * currNode ) { 254 255 256 254 bool aBelow = false, oBelow = false; // unused by initial call 255 paintWhenTree( currNode, false, false, aBelow, oBelow ); 256 return !aBelow; 257 257 } 258 258 259 259 // Helper: returns Expr that represents arrName[index] 260 260 Expr * genArrAccessExpr( const CodeLocation & loc, int index, string arrName ) { 261 return new UntypedExpr ( loc, 262 263 264 265 266 267 261 return new UntypedExpr ( loc, 262 new NameExpr( loc, "?[?]" ), 263 { 264 new NameExpr( loc, arrName ), 265 ConstantExpr::from_int( loc, index ) 266 } 267 ); 268 268 } 269 269 … … 273 273 // - updates LEAF nodes to be when-ambiguous if their direct parent is when-ambiguous. 274 274 void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs, int & index, bool parentAmbig, bool parentAnd ) { 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 275 switch (currNode->op) { 276 case WaitUntilStmt::ClauseNode::AND: 277 collectWhens( currNode->left, ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true ); 278 collectWhens( currNode->right, ambigIdxs, andIdxs, index, currNode->ambiguousWhen, true ); 279 return; 280 case WaitUntilStmt::ClauseNode::OR: 281 collectWhens( currNode->left, ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false ); 282 collectWhens( currNode->right, ambigIdxs, andIdxs, index, currNode->ambiguousWhen, false ); 283 return; 284 case WaitUntilStmt::ClauseNode::LEAF: 285 if ( parentAmbig ) { 286 ambigIdxs.push_back(make_pair(index, currNode)); 287 } 288 if ( parentAnd && currNode->leaf->when_cond ) { 289 currNode->childOfAnd = true; 290 andIdxs.push_back(index); 291 } 292 index++; 293 return; 294 default: 295 assertf(false, "Unreachable waituntil clause node type. How did you get here???"); 296 } 297 297 } 298 298 299 299 // overloaded wrapper for collectWhens that sets initial values 300 300 void GenerateWaitUntilCore::collectWhens( WaitUntilStmt::ClauseNode * currNode, vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigIdxs, vector<int> & andIdxs ) { 301 302 303 } 304 305 // recursively updates ClauseNode whenState on internal nodes so that next pass can see which 301 int idx = 0; 302 collectWhens( currNode, ambigIdxs, andIdxs, idx, false, false ); 303 } 304 305 // recursively updates ClauseNode whenState on internal nodes so that next pass can see which 306 306 // subtrees are "turned off" 307 307 // sets whenState = false iff both children have whenState == false. … … 309 309 // since the ambiguous clauses were filtered in paintWhenTree we don't need to worry about that here 310 310 void GenerateWaitUntilCore::updateWhenState( WaitUntilStmt::ClauseNode * currNode ) { 311 312 313 314 315 316 else 317 311 if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) return; 312 updateWhenState( currNode->left ); 313 updateWhenState( currNode->right ); 314 if ( !currNode->left->whenState && !currNode->right->whenState ) 315 currNode->whenState = false; 316 else 317 currNode->whenState = true; 318 318 } 319 319 … … 321 321 // assumes that this will only be called on subtrees that are entirely whenState == false 322 322 void GenerateWaitUntilCore::genSubtreeAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, bool status, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) { 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 new ExprStmt( loc, 353 354 355 356 357 358 359 360 361 new ExprStmt( loc, 362 363 364 365 366 367 368 369 370 371 372 373 374 375 323 if ( ( currNode->op == WaitUntilStmt::ClauseNode::AND && status ) 324 || ( currNode->op == WaitUntilStmt::ClauseNode::OR && !status ) ) { 325 // need to recurse on both subtrees if && subtree needs to be true or || subtree needs to be false 326 genSubtreeAssign( stmt, currNode->left, status, idx, retStmt, clauseData ); 327 genSubtreeAssign( stmt, currNode->right, status, idx, retStmt, clauseData ); 328 } else if ( ( currNode->op == WaitUntilStmt::ClauseNode::OR && status ) 329 || ( currNode->op == WaitUntilStmt::ClauseNode::AND && !status ) ) { 330 // only one subtree needs to evaluate to status if && subtree needs to be true or || subtree needs to be false 331 CompoundStmt * leftStmt = new CompoundStmt( stmt->location ); 332 CompoundStmt * rightStmt = new CompoundStmt( stmt->location ); 333 334 // only one side needs to evaluate to status so we recurse on both subtrees 335 // but only keep the statements from the subtree with minimal statements 336 genSubtreeAssign( stmt, currNode->left, status, idx, leftStmt, clauseData ); 337 genSubtreeAssign( stmt, currNode->right, status, idx, rightStmt, clauseData ); 338 339 // append minimal statements to retStmt 340 if ( leftStmt->kids.size() < rightStmt->kids.size() ) { 341 retStmt->kids.splice( retStmt->kids.end(), leftStmt->kids ); 342 } else { 343 retStmt->kids.splice( retStmt->kids.end(), rightStmt->kids ); 344 } 345 346 delete leftStmt; 347 delete rightStmt; 348 } else if ( currNode->op == WaitUntilStmt::ClauseNode::LEAF ) { 349 const CodeLocation & loc = stmt->location; 350 if ( status && !currNode->childOfAnd ) { 351 retStmt->push_back( 352 new ExprStmt( loc, 353 UntypedExpr::createAssign( loc, 354 genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ), 355 new NameExpr( loc, "__SELECT_RUN" ) 356 ) 357 ) 358 ); 359 } else if ( !status && currNode->childOfAnd ) { 360 retStmt->push_back( 361 new ExprStmt( loc, 362 UntypedExpr::createAssign( loc, 363 genArrAccessExpr( loc, idx, clauseData.at(idx)->statusName ), 364 new NameExpr( loc, "__SELECT_UNSAT" ) 365 ) 366 ) 367 ); 368 } 369 370 // No need to generate statements for the following cases since childOfAnd are always set to true 371 // and !childOfAnd are always false 372 // - status && currNode->childOfAnd 373 // - !status && !currNode->childOfAnd 374 idx++; 375 } 376 376 } 377 377 378 378 void GenerateWaitUntilCore::genStatusAssign( const WaitUntilStmt * stmt, WaitUntilStmt::ClauseNode * currNode, int & idx, CompoundStmt * retStmt, vector<ClauseData *> & clauseData ) { 379 380 381 382 383 // this case can only occur when whole tree is disabled since otherwise 384 385 386 387 388 389 390 391 392 393 394 395 } else { 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 } else { 412 413 414 415 416 417 418 419 420 421 422 379 switch (currNode->op) { 380 case WaitUntilStmt::ClauseNode::AND: 381 // check which subtrees have all whenState == false (disabled) 382 if (!currNode->left->whenState && !currNode->right->whenState) { 383 // this case can only occur when whole tree is disabled since otherwise 384 // genStatusAssign( ... ) isn't called on nodes with whenState == false 385 assert( !currNode->whenState ); // paranoidWWW 386 // whole tree disabled so pass true so that select is SAT vacuously 387 genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData ); 388 } else if ( !currNode->left->whenState ) { 389 // pass true since x && true === x 390 genSubtreeAssign( stmt, currNode->left, true, idx, retStmt, clauseData ); 391 genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData ); 392 } else if ( !currNode->right->whenState ) { 393 genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData ); 394 genSubtreeAssign( stmt, currNode->right, true, idx, retStmt, clauseData ); 395 } else { 396 // if no children with whenState == false recurse normally via break 397 break; 398 } 399 return; 400 case WaitUntilStmt::ClauseNode::OR: 401 if (!currNode->left->whenState && !currNode->right->whenState) { 402 assert( !currNode->whenState ); // paranoid 403 genSubtreeAssign( stmt, currNode, true, idx, retStmt, clauseData ); 404 } else if ( !currNode->left->whenState ) { 405 // pass false since x || false === x 406 genSubtreeAssign( stmt, currNode->left, false, idx, retStmt, clauseData ); 407 genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData ); 408 } else if ( !currNode->right->whenState ) { 409 genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData ); 410 genSubtreeAssign( stmt, currNode->right, false, idx, retStmt, clauseData ); 411 } else { 412 break; 413 } 414 return; 415 case WaitUntilStmt::ClauseNode::LEAF: 416 idx++; 417 return; 418 default: 419 assertf(false, "Unreachable waituntil clause node type. How did you get here???"); 420 } 421 genStatusAssign( stmt, currNode->left, idx, retStmt, clauseData ); 422 genStatusAssign( stmt, currNode->right, idx, retStmt, clauseData ); 423 423 } 424 424 425 425 // generates a minimal set of assignments for status arr based on which whens are toggled on/off 426 426 CompoundStmt * GenerateWaitUntilCore::getStatusAssignment( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData ) { 427 428 429 430 431 427 updateWhenState( stmt->predicateTree ); 428 CompoundStmt * retval = new CompoundStmt( stmt->location ); 429 int idx = 0; 430 genStatusAssign( stmt, stmt->predicateTree, idx, retval, clauseData ); 431 return retval; 432 432 } 433 433 434 434 // generates nested if/elses for all possible assignments of ambiguous when_conds 435 435 // exponential size of code gen but linear runtime O(n), where n is number of ambiguous whens() 436 Stmt * GenerateWaitUntilCore::genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 436 Stmt * GenerateWaitUntilCore::genWhenStateConditions( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, 437 vector<pair<int, WaitUntilStmt::ClauseNode *>> & ambigClauses, vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type ambigIdx ) { 438 // I hate C++ sometimes, using vector<pair<int, WaitUntilStmt::ClauseNode *>>::size_type for size() comparison seems silly. 439 // Why is size_type parameterized on the type stored in the vector????? 440 441 const CodeLocation & loc = stmt->location; 442 int clauseIdx = ambigClauses.at(ambigIdx).first; 443 WaitUntilStmt::ClauseNode * currNode = ambigClauses.at(ambigIdx).second; 444 Stmt * thenStmt; 445 Stmt * elseStmt; 446 447 if ( ambigIdx == ambigClauses.size() - 1 ) { // base case 448 currNode->whenState = true; 449 thenStmt = getStatusAssignment( stmt, clauseData ); 450 currNode->whenState = false; 451 elseStmt = getStatusAssignment( stmt, clauseData ); 452 } else { 453 // recurse both with when enabled and disabled to generate all possible cases 454 currNode->whenState = true; 455 thenStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 ); 456 currNode->whenState = false; 457 elseStmt = genWhenStateConditions( stmt, clauseData, ambigClauses, ambigIdx + 1 ); 458 } 459 460 // insert first recursion result in if ( __when_cond_ ) { ... } 461 // insert second recursion result in else { ... } 462 return new CompoundStmt ( loc, 463 { 464 new IfStmt( loc, 465 new NameExpr( loc, clauseData.at(clauseIdx)->whenName ), 466 thenStmt, 467 elseStmt 468 ) 469 } 470 ); 471 471 } 472 472 … … 478 478 // mutates index to be index + 1 479 479 Expr * genSatExpr( const CodeLocation & loc, int & index ) { 480 480 return genArrAccessExpr( loc, index++, "clause_statuses" ); 481 481 } 482 482 483 483 // return Expr that represents has_run(clause_statuses[index]) 484 484 Expr * genRunExpr( const CodeLocation & loc, int & index ) { 485 return new UntypedExpr ( loc, 486 487 488 485 return new UntypedExpr ( loc, 486 new NameExpr( loc, "__CFA_has_clause_run" ), 487 { genSatExpr( loc, index ) } 488 ); 489 489 } 490 490 … … 492 492 // the predicate expr used inside the predicate functions 493 493 Expr * genPredExpr( const CodeLocation & loc, WaitUntilStmt::ClauseNode * currNode, int & idx, GenLeafExpr genLeaf ) { 494 495 496 497 498 499 return new LogicalExpr( loc, 500 501 new CastExpr( loc, rightExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ), 502 LogicalFlag::AndExpr 503 504 505 506 507 508 509 510 new CastExpr( loc, rightExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ), 511 512 513 514 515 516 517 518 519 520 521 494 Expr * leftExpr, * rightExpr; 495 switch (currNode->op) { 496 case WaitUntilStmt::ClauseNode::AND: 497 leftExpr = genPredExpr( loc, currNode->left, idx, genLeaf ); 498 rightExpr = genPredExpr( loc, currNode->right, idx, genLeaf ); 499 return new LogicalExpr( loc, 500 new CastExpr( loc, leftExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ), 501 new CastExpr( loc, rightExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ), 502 LogicalFlag::AndExpr 503 ); 504 break; 505 case WaitUntilStmt::ClauseNode::OR: 506 leftExpr = genPredExpr( loc, currNode->left, idx, genLeaf ); 507 rightExpr = genPredExpr( loc, currNode->right, idx, genLeaf ); 508 return new LogicalExpr( loc, 509 new CastExpr( loc, leftExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ), 510 new CastExpr( loc, rightExpr, new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast ), 511 LogicalFlag::OrExpr ); 512 break; 513 case WaitUntilStmt::ClauseNode::LEAF: 514 return genLeaf( loc, idx ); 515 break; 516 default: 517 assertf(false, "Unreachable waituntil clause node type. How did you get here???");\ 518 return nullptr; 519 break; 520 } 521 return nullptr; 522 522 } 523 523 … … 526 526 /* Ex: 527 527 { 528 waituntil( A ){ doA(); } 529 or waituntil( B ){ doB(); } 530 528 waituntil( A ){ doA(); } 529 or waituntil( B ){ doB(); } 530 and waituntil( C ) { doC(); } 531 531 } 532 532 generates => 533 533 static inline bool is_full_sat_1( int * clause_statuses ) { 534 return clause_statuses[0] 535 536 534 return clause_statuses[0] 535 || clause_statuses[1] 536 && clause_statuses[2]; 537 537 } 538 538 539 539 static inline bool is_done_sat_1( int * clause_statuses ) { 540 541 542 540 return has_run(clause_statuses[0]) 541 || has_run(clause_statuses[1]) 542 && has_run(clause_statuses[2]); 543 543 } 544 544 */ … … 546 546 // predName and genLeaf determine if this generates an is_done or an is_full predicate 547 547 FunctionDecl * buildPredicate( const WaitUntilStmt * stmt, GenLeafExpr genLeaf, string & predName ) { 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 548 int arrIdx = 0; 549 const CodeLocation & loc = stmt->location; 550 CompoundStmt * body = new CompoundStmt( loc ); 551 body->push_back( new ReturnStmt( loc, genPredExpr( loc, stmt->predicateTree, arrIdx, genLeaf ) ) ); 552 553 return new FunctionDecl( loc, 554 predName, 555 { 556 new ObjectDecl( loc, 557 "clause_statuses", 558 new PointerType( new BasicType( BasicKind::LongUnsignedInt ) ) 559 ) 560 }, 561 { 562 new ObjectDecl( loc, 563 "sat_ret", 564 new BasicType( BasicKind::Bool ) 565 ) 566 }, 567 body, // body 568 { Storage::Static }, // storage 569 Linkage::Cforall, // linkage 570 {}, // attributes 571 { Function::Inline } 572 ); 573 573 } 574 574 575 575 // Creates is_done and is_full predicates 576 576 void GenerateWaitUntilCore::addPredicates( const WaitUntilStmt * stmt, string & satName, string & runName ) { 577 578 satFns.push_back( Concurrency::buildPredicate( stmt, genSatExpr, satName ) ); 579 577 if ( !stmt->else_stmt || stmt->else_cond ) // don't need SAT predicate when else variation with no else_cond 578 satFns.push_back( Concurrency::buildPredicate( stmt, genSatExpr, satName ) ); 579 satFns.push_back( Concurrency::buildPredicate( stmt, genRunExpr, runName ) ); 580 580 } 581 581 … … 585 585 // register_select(A, clause1); 586 586 // } 587 void GenerateWaitUntilCore::setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body ) { 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 body->push_back( 613 614 615 616 617 587 void GenerateWaitUntilCore::setUpClause( const WhenClause * clause, ClauseData * data, string & pCountName, CompoundStmt * body ) { 588 CompoundStmt * currBody = body; 589 const CodeLocation & loc = clause->location; 590 591 // If we have a when_cond make the initialization conditional 592 if ( clause->when_cond ) 593 currBody = new CompoundStmt( loc ); 594 595 // Generates: setup_clause( clause1, &clause_statuses[0], &park_counter ); 596 currBody->push_back( new ExprStmt( loc, 597 new UntypedExpr ( loc, 598 new NameExpr( loc, "setup_clause" ), 599 { 600 new NameExpr( loc, data->nodeName ), 601 new AddressExpr( loc, genArrAccessExpr( loc, data->index, data->statusName ) ), 602 new AddressExpr( loc, new NameExpr( loc, pCountName ) ) 603 } 604 ) 605 )); 606 607 // Generates: register_select(A, clause1); 608 currBody->push_back( new ExprStmt( loc, genSelectTraitCall( clause, data, "register_select" ) ) ); 609 610 // generates: if ( when_cond ) { ... currBody ... } 611 if ( clause->when_cond ) 612 body->push_back( 613 new IfStmt( loc, 614 new NameExpr( loc, data->whenName ), 615 currBody 616 ) 617 ); 618 618 } 619 619 620 620 // Used to generate a call to one of the select trait routines 621 621 Expr * GenerateWaitUntilCore::genSelectTraitCall( const WhenClause * clause, const ClauseData * data, string fnName ) { 622 623 624 625 626 627 628 629 622 const CodeLocation & loc = clause->location; 623 return new UntypedExpr ( loc, 624 new NameExpr( loc, fnName ), 625 { 626 new NameExpr( loc, data->targetName ), 627 new NameExpr( loc, data->nodeName ) 628 } 629 ); 630 630 } 631 631 632 632 // Generates: 633 /* on_selected( target_1, node_1 ); ... corresponding body of target_1 ... 633 /* on_selected( target_1, node_1 ); ... corresponding body of target_1 ... 634 634 */ 635 635 CompoundStmt * GenerateWaitUntilCore::genStmtBlock( const WhenClause * clause, const ClauseData * data ) { 636 637 638 639 640 641 642 643 644 636 const CodeLocation & cLoc = clause->location; 637 return new CompoundStmt( cLoc, 638 { 639 new IfStmt( cLoc, 640 genSelectTraitCall( clause, data, "on_selected" ), 641 ast::deepCopy( clause->stmt ) 642 ) 643 } 644 ); 645 645 } 646 646 647 647 // this routine generates and returns the following 648 648 /*for ( int i = 0; i < numClauses; i++ ) { 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 649 if ( predName(clause_statuses) ) break; 650 if (clause_statuses[i] == __SELECT_SAT) { 651 switch (i) { 652 case 0: 653 try { 654 on_selected( target1, clause1 ); 655 dotarget1stmt(); 656 } 657 finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); } 658 break; 659 ... 660 case N: 661 ... 662 break; 663 } 664 } 665 665 }*/ 666 666 CompoundStmt * GenerateWaitUntilCore::genStatusCheckFor( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, string & predName ) { 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 new ast::FinallyClause( cLoc, 700 701 702 703 704 705 706 new UntypedExpr ( loc, 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 new UntypedExpr ( loc, 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 667 CompoundStmt * ifBody = new CompoundStmt( stmt->location ); 668 const CodeLocation & loc = stmt->location; 669 670 string switchLabel = namer_label.newName(); 671 672 /* generates: 673 switch (i) { 674 case 0: 675 try { 676 on_selected( target1, clause1 ); 677 dotarget1stmt(); 678 } 679 finally { clause_statuses[i] = __SELECT_RUN; unregister_select(target1, clause1); } 680 break; 681 ... 682 case N: 683 ... 684 break; 685 }*/ 686 std::vector<ptr<CaseClause>> switchCases; 687 int idx = 0; 688 for ( const auto & clause: stmt->clauses ) { 689 const CodeLocation & cLoc = clause->location; 690 switchCases.push_back( 691 new CaseClause( cLoc, 692 ConstantExpr::from_int( cLoc, idx ), 693 { 694 new CompoundStmt( cLoc, 695 { 696 new ast::TryStmt( cLoc, 697 genStmtBlock( clause, clauseData.at(idx) ), 698 {}, 699 new ast::FinallyClause( cLoc, 700 new CompoundStmt( cLoc, 701 { 702 new ExprStmt( loc, 703 new UntypedExpr ( loc, 704 new NameExpr( loc, "?=?" ), 705 { 706 new UntypedExpr ( loc, 707 new NameExpr( loc, "?[?]" ), 708 { 709 new NameExpr( loc, clauseData.at(0)->statusName ), 710 new NameExpr( loc, idxName ) 711 } 712 ), 713 new NameExpr( loc, "__SELECT_RUN" ) 714 } 715 ) 716 ), 717 new ExprStmt( loc, genSelectTraitCall( clause, clauseData.at(idx), "unregister_select" ) ) 718 } 719 ) 720 ) 721 ), 722 new BranchStmt( cLoc, BranchStmt::Kind::Break, Label( cLoc, switchLabel ) ) 723 } 724 ) 725 } 726 ) 727 ); 728 idx++; 729 } 730 731 ifBody->push_back( 732 new SwitchStmt( loc, 733 new NameExpr( loc, idxName ), 734 std::move( switchCases ), 735 { Label( loc, switchLabel ) } 736 ) 737 ); 738 739 // gens: 740 // if (clause_statuses[i] == __SELECT_SAT) { 741 // ... ifBody ... 742 // } 743 IfStmt * ifSwitch = new IfStmt( loc, 744 new UntypedExpr ( loc, 745 new NameExpr( loc, "?==?" ), 746 { 747 new UntypedExpr ( loc, 748 new NameExpr( loc, "?[?]" ), 749 { 750 new NameExpr( loc, clauseData.at(0)->statusName ), 751 new NameExpr( loc, idxName ) 752 } 753 ), 754 new NameExpr( loc, "__SELECT_SAT" ) 755 } 756 ), // condition 757 ifBody // body 758 ); 759 760 string forLabel = namer_label.newName(); 761 762 // we hoist init here so that this pass can happen after hoistdecls pass 763 return new CompoundStmt( loc, 764 { 765 new DeclStmt( loc, 766 new ObjectDecl( loc, 767 idxName, 768 new BasicType( BasicKind::SignedInt ), 769 new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) ) 770 ) 771 ), 772 new ForStmt( loc, 773 {}, // inits 774 new UntypedExpr ( loc, 775 new NameExpr( loc, "?<?" ), 776 { 777 new NameExpr( loc, idxName ), 778 ConstantExpr::from_int( loc, stmt->clauses.size() ) 779 } 780 ), // cond 781 new UntypedExpr ( loc, 782 new NameExpr( loc, "?++" ), 783 { new NameExpr( loc, idxName ) } 784 ), // inc 785 new CompoundStmt( loc, 786 { 787 new IfStmt( loc, 788 new UntypedExpr ( loc, 789 new NameExpr( loc, predName ), 790 { new NameExpr( loc, clauseData.at(0)->statusName ) } 791 ), 792 new BranchStmt( loc, BranchStmt::Kind::Break, Label( loc, forLabel ) ) 793 ), 794 ifSwitch 795 } 796 ), // body 797 { Label( loc, forLabel ) } 798 ) 799 } 800 ); 801 801 } 802 802 803 803 // Generates: !is_full_sat_n() / !is_run_sat_n() 804 804 Expr * genNotSatExpr( const WaitUntilStmt * stmt, string & satName, string & arrName ) { 805 806 807 808 809 810 811 812 813 814 805 const CodeLocation & loc = stmt->location; 806 return new UntypedExpr ( loc, 807 new NameExpr( loc, "!?" ), 808 { 809 new UntypedExpr ( loc, 810 new NameExpr( loc, satName ), 811 { new NameExpr( loc, arrName ) } 812 ) 813 } 814 ); 815 815 } 816 816 … … 819 819 // If not enough have run to satisfy predicate after one pass then the else is run 820 820 Stmt * GenerateWaitUntilCore::genElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, vector<ClauseData *> & clauseData ) { 821 822 823 824 825 826 827 828 829 821 return new CompoundStmt( stmt->else_stmt->location, 822 { 823 genStatusCheckFor( stmt, clauseData, runName ), 824 new IfStmt( stmt->else_stmt->location, 825 genNotSatExpr( stmt, runName, arrName ), 826 ast::deepCopy( stmt->else_stmt ) 827 ) 828 } 829 ); 830 830 } 831 831 832 832 Stmt * GenerateWaitUntilCore::genNoElseClauseBranch( const WaitUntilStmt * stmt, string & runName, string & arrName, string & pCountName, vector<ClauseData *> & clauseData ) { 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 833 CompoundStmt * whileBody = new CompoundStmt( stmt->location ); 834 const CodeLocation & loc = stmt->location; 835 836 // generates: __CFA_maybe_park( &park_counter ); 837 whileBody->push_back( 838 new ExprStmt( loc, 839 new UntypedExpr ( loc, 840 new NameExpr( loc, "__CFA_maybe_park" ), 841 { new AddressExpr( loc, new NameExpr( loc, pCountName ) ) } 842 ) 843 ) 844 ); 845 846 whileBody->push_back( genStatusCheckFor( stmt, clauseData, runName ) ); 847 848 return new CompoundStmt( loc, 849 { 850 new WhileDoStmt( loc, 851 genNotSatExpr( stmt, runName, arrName ), 852 whileBody, // body 853 {} // no inits 854 ) 855 } 856 ); 857 857 } 858 858 … … 862 862 // select_node clause1; 863 863 void GenerateWaitUntilCore::genClauseInits( const WaitUntilStmt * stmt, vector<ClauseData *> & clauseData, CompoundStmt * body, string & statusName, string & elseWhenName ) { 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 new ReferenceType( 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 864 ClauseData * currClause; 865 for ( vector<ClauseData*>::size_type i = 0; i < stmt->clauses.size(); i++ ) { 866 currClause = new ClauseData( i, statusName ); 867 currClause->nodeName = namer_node.newName(); 868 currClause->targetName = namer_target.newName(); 869 currClause->whenName = namer_when.newName(); 870 clauseData.push_back(currClause); 871 const CodeLocation & cLoc = stmt->clauses.at(i)->location; 872 873 // typeof(target) & __clause_target_0 = target; 874 body->push_back( 875 new DeclStmt( cLoc, 876 new ObjectDecl( cLoc, 877 currClause->targetName, 878 new ReferenceType( 879 new TypeofType( new UntypedExpr( cLoc, 880 new NameExpr( cLoc, "__CFA_select_get_type" ), 881 { ast::deepCopy( stmt->clauses.at(i)->target ) } 882 )) 883 ), 884 new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->target ) ) 885 ) 886 ) 887 ); 888 889 // bool __when_cond_0 = when_cond; // only generated if when_cond defined 890 if ( stmt->clauses.at(i)->when_cond ) 891 body->push_back( 892 new DeclStmt( cLoc, 893 new ObjectDecl( cLoc, 894 currClause->whenName, 895 new BasicType( BasicKind::Bool ), 896 new SingleInit( cLoc, ast::deepCopy( stmt->clauses.at(i)->when_cond ) ) 897 ) 898 ) 899 ); 900 901 // select_node clause1; 902 body->push_back( 903 new DeclStmt( cLoc, 904 new ObjectDecl( cLoc, 905 currClause->nodeName, 906 new StructInstType( selectNodeDecl ) 907 ) 908 ) 909 ); 910 } 911 912 if ( stmt->else_stmt && stmt->else_cond ) { 913 body->push_back( 914 new DeclStmt( stmt->else_cond->location, 915 new ObjectDecl( stmt->else_cond->location, 916 elseWhenName, 917 new BasicType( BasicKind::Bool ), 918 new SingleInit( stmt->else_cond->location, ast::deepCopy( stmt->else_cond ) ) 919 ) 920 ) 921 ); 922 } 923 923 } 924 924 … … 929 929 */ 930 930 Stmt * GenerateWaitUntilCore::buildOrCaseSwitch( const WaitUntilStmt * stmt, string & statusName, vector<ClauseData *> & data ) { 931 932 933 931 const CodeLocation & loc = stmt->location; 932 933 IfStmt * outerIf = nullptr; 934 934 IfStmt * lastIf = nullptr; 935 935 936 936 //adds an if/elif clause for each select clause address to run the corresponding clause stmt 937 937 for ( long unsigned int i = 0; i < data.size(); i++ ) { 938 938 const CodeLocation & cLoc = stmt->clauses.at(i)->location; 939 939 940 940 IfStmt * currIf = new IfStmt( cLoc, 941 new UntypedExpr( cLoc, 942 new NameExpr( cLoc, "?==?" ), 943 944 945 new CastExpr( cLoc, 946 947 new BasicType( BasicKind::LongUnsignedInt ), GeneratedFlag::ExplicitCast 948 949 950 951 952 ); 953 941 new UntypedExpr( cLoc, 942 new NameExpr( cLoc, "?==?" ), 943 { 944 new NameExpr( cLoc, statusName ), 945 new CastExpr( cLoc, 946 new AddressExpr( cLoc, new NameExpr( cLoc, data.at(i)->nodeName ) ), 947 new BasicType( BasicKind::LongUnsignedInt ), GeneratedFlag::ExplicitCast 948 ) 949 } 950 ), 951 genStmtBlock( stmt->clauses.at(i), data.at(i) ) 952 ); 953 954 954 if ( i == 0 ) { 955 955 outerIf = currIf; … … 962 962 } 963 963 964 965 966 967 968 969 964 return new CompoundStmt( loc, 965 { 966 new ExprStmt( loc, new UntypedExpr( loc, new NameExpr( loc, "park" ) ) ), 967 outerIf 968 } 969 ); 970 970 } 971 971 972 972 Stmt * GenerateWaitUntilCore::recursiveOrIfGen( const WaitUntilStmt * stmt, vector<ClauseData *> & data, vector<ClauseData*>::size_type idx, string & elseWhenName ) { 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 989 990 991 992 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 new NameExpr( cLoc, data.at(idx)->whenName ), 1018 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1019 1020 1021 1022 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 973 if ( idx == data.size() ) { // base case, gen last else 974 const CodeLocation & cLoc = stmt->else_stmt->location; 975 if ( !stmt->else_stmt ) // normal non-else gen 976 return buildOrCaseSwitch( stmt, data.at(0)->statusName, data ); 977 978 Expr * raceFnCall = new UntypedExpr( stmt->location, 979 new NameExpr( stmt->location, "__select_node_else_race" ), 980 { new NameExpr( stmt->location, data.at(0)->nodeName ) } 981 ); 982 983 if ( stmt->else_stmt && stmt->else_cond ) { // return else conditional on both when and race 984 return new IfStmt( cLoc, 985 new LogicalExpr( cLoc, 986 new CastExpr( cLoc, 987 new NameExpr( cLoc, elseWhenName ), 988 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 989 ), 990 new CastExpr( cLoc, 991 raceFnCall, 992 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 993 ), 994 LogicalFlag::AndExpr 995 ), 996 ast::deepCopy( stmt->else_stmt ), 997 buildOrCaseSwitch( stmt, data.at(0)->statusName, data ) 998 ); 999 } 1000 1001 // return else conditional on race 1002 return new IfStmt( stmt->else_stmt->location, 1003 raceFnCall, 1004 ast::deepCopy( stmt->else_stmt ), 1005 buildOrCaseSwitch( stmt, data.at(0)->statusName, data ) 1006 ); 1007 } 1008 const CodeLocation & cLoc = stmt->clauses.at(idx)->location; 1009 1010 Expr * baseCond = genSelectTraitCall( stmt->clauses.at(idx), data.at(idx), "register_select" ); 1011 Expr * ifCond; 1012 1013 // If we have a when_cond make the register call conditional on it 1014 if ( stmt->clauses.at(idx)->when_cond ) { 1015 ifCond = new LogicalExpr( cLoc, 1016 new CastExpr( cLoc, 1017 new NameExpr( cLoc, data.at(idx)->whenName ), 1018 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1019 ), 1020 new CastExpr( cLoc, 1021 baseCond, 1022 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1023 ), 1024 LogicalFlag::AndExpr 1025 ); 1026 } else ifCond = baseCond; 1027 1028 return new CompoundStmt( cLoc, 1029 { // gens: setup_clause( clause1, &status, 0p ); 1030 new ExprStmt( cLoc, 1031 new UntypedExpr ( cLoc, 1032 new NameExpr( cLoc, "setup_clause" ), 1033 { 1034 new NameExpr( cLoc, data.at(idx)->nodeName ), 1035 new AddressExpr( cLoc, new NameExpr( cLoc, data.at(idx)->statusName ) ), 1036 ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicKind::SignedInt ) ) ) 1037 } 1038 ) 1039 ), 1040 // gens: if (__when_cond && register_select()) { clause body } else { ... recursiveOrIfGen ... } 1041 new IfStmt( cLoc, 1042 ifCond, 1043 genStmtBlock( stmt->clauses.at(idx), data.at(idx) ), 1044 recursiveOrIfGen( stmt, data, idx + 1, elseWhenName ) 1045 ) 1046 } 1047 ); 1048 1048 } 1049 1049 1050 1050 // This gens the special case of an all OR waituntil: 1051 /* 1051 /* 1052 1052 int status = 0; 1053 1053 … … 1058 1058 1059 1059 try { 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 // after winning the race and before unpark() clause_status is set to be the winning clause index + 1 1074 1075 1076 1077 1078 1079 1080 } 1081 finally { 1082 1083 1084 1060 setup_clause( clause1, &status, 0p ); 1061 if ( __when_cond_0 && register_select( 1 ) ) { 1062 ... clause 1 body ... 1063 } else { 1064 ... recursively gen for each of n clauses ... 1065 setup_clause( clausen, &status, 0p ); 1066 if ( __when_cond_n-1 && register_select( n ) ) { 1067 ... clause n body ... 1068 } else { 1069 if ( else_when ) ... else clause body ... 1070 else { 1071 park(); 1072 1073 // after winning the race and before unpark() clause_status is set to be the winning clause index + 1 1074 if ( clause_status == &clause1) ... clause 1 body ... 1075 ... 1076 elif ( clause_status == &clausen ) ... clause n body ... 1077 } 1078 } 1079 } 1080 } 1081 finally { 1082 if ( __when_cond_1 && clause1.status != 0p) unregister_select( 1 ); // if registered unregister 1083 ... 1084 if ( __when_cond_n && clausen.status != 0p) unregister_select( n ); 1085 1085 } 1086 1086 */ 1087 1087 Stmt * GenerateWaitUntilCore::genAllOr( const WaitUntilStmt * stmt ) { 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 UntypedExpr * statusPtrCheck = new UntypedExpr( cLoc, 1114 new NameExpr( cLoc, "?!=?" ), 1115 1116 1117 new UntypedExpr( cLoc, 1118 new NameExpr( cLoc, "__get_clause_status" ), 1119 { new NameExpr( cLoc, clauseData.at(i)->nodeName ) } 1120 ) 1121 1122 1123 1124 1125 1126 1127 1128 1129 new NameExpr( cLoc, clauseData.at(i)->whenName ), 1130 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1131 1132 1133 1134 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1135 1136 1137 1138 1139 1140 1141 1142 1143 new ExprStmt( cLoc, genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ) ) 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 new NameExpr( loc, clauseData.at( whenIndices.at(i) )->whenName ), 1164 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1165 1166 1167 1168 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1169 1170 1171 1172 1173 1174 1175 1176 1177 new CompoundStmt( loc, 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1088 const CodeLocation & loc = stmt->location; 1089 string statusName = namer_status.newName(); 1090 string elseWhenName = namer_when.newName(); 1091 int numClauses = stmt->clauses.size(); 1092 CompoundStmt * body = new CompoundStmt( stmt->location ); 1093 1094 // Generates: unsigned long int status = 0; 1095 body->push_back( new DeclStmt( loc, 1096 new ObjectDecl( loc, 1097 statusName, 1098 new BasicType( BasicKind::LongUnsignedInt ), 1099 new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) ) 1100 ) 1101 )); 1102 1103 vector<ClauseData *> clauseData; 1104 genClauseInits( stmt, clauseData, body, statusName, elseWhenName ); 1105 1106 vector<int> whenIndices; // track which clauses have whens 1107 1108 CompoundStmt * unregisters = new CompoundStmt( loc ); 1109 Expr * ifCond; 1110 for ( int i = 0; i < numClauses; i++ ) { 1111 const CodeLocation & cLoc = stmt->clauses.at(i)->location; 1112 // Gens: node.status != 0p 1113 UntypedExpr * statusPtrCheck = new UntypedExpr( cLoc, 1114 new NameExpr( cLoc, "?!=?" ), 1115 { 1116 ConstantExpr::null( cLoc, new PointerType( new BasicType( BasicKind::LongUnsignedInt ) ) ), 1117 new UntypedExpr( cLoc, 1118 new NameExpr( cLoc, "__get_clause_status" ), 1119 { new NameExpr( cLoc, clauseData.at(i)->nodeName ) } 1120 ) 1121 } 1122 ); 1123 1124 // If we have a when_cond make the unregister call conditional on it 1125 if ( stmt->clauses.at(i)->when_cond ) { 1126 whenIndices.push_back(i); 1127 ifCond = new LogicalExpr( cLoc, 1128 new CastExpr( cLoc, 1129 new NameExpr( cLoc, clauseData.at(i)->whenName ), 1130 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1131 ), 1132 new CastExpr( cLoc, 1133 statusPtrCheck, 1134 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1135 ), 1136 LogicalFlag::AndExpr 1137 ); 1138 } else ifCond = statusPtrCheck; 1139 1140 unregisters->push_back( 1141 new IfStmt( cLoc, 1142 ifCond, 1143 new ExprStmt( cLoc, genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ) ) 1144 ) 1145 ); 1146 } 1147 1148 if ( whenIndices.empty() || whenIndices.size() != stmt->clauses.size() ) { 1149 body->push_back( 1150 new ast::TryStmt( loc, 1151 new CompoundStmt( loc, { recursiveOrIfGen( stmt, clauseData, 0, elseWhenName ) } ), 1152 {}, 1153 new ast::FinallyClause( loc, unregisters ) 1154 ) 1155 ); 1156 } else { // If all clauses have whens, we need to skip the waituntil if they are all false 1157 Expr * outerIfCond = new NameExpr( loc, clauseData.at( whenIndices.at(0) )->whenName ); 1158 Expr * lastExpr = outerIfCond; 1159 1160 for ( vector<int>::size_type i = 1; i < whenIndices.size(); i++ ) { 1161 outerIfCond = new LogicalExpr( loc, 1162 new CastExpr( loc, 1163 new NameExpr( loc, clauseData.at( whenIndices.at(i) )->whenName ), 1164 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1165 ), 1166 new CastExpr( loc, 1167 lastExpr, 1168 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1169 ), 1170 LogicalFlag::OrExpr 1171 ); 1172 lastExpr = outerIfCond; 1173 } 1174 1175 body->push_back( 1176 new ast::TryStmt( loc, 1177 new CompoundStmt( loc, 1178 { 1179 new IfStmt( loc, 1180 outerIfCond, 1181 recursiveOrIfGen( stmt, clauseData, 0, elseWhenName ) 1182 ) 1183 } 1184 ), 1185 {}, 1186 new ast::FinallyClause( loc, unregisters ) 1187 ) 1188 ); 1189 } 1190 1191 for ( ClauseData * datum : clauseData ) 1192 delete datum; 1193 1194 return body; 1195 1195 } 1196 1196 1197 1197 Stmt * GenerateWaitUntilCore::postvisit( const WaitUntilStmt * stmt ) { 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 body->push_back( 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 new UntypedExpr ( cLoc, 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 // for each clause: 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 new UntypedExpr ( cLoc, 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 statusExpr, 1333 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1334 1335 1336 1337 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 new NameExpr( cLoc, clauseData.at(i)->whenName ), 1348 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1349 1350 1351 1352 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1353 1354 1355 1356 1357 1358 1359 unregisters->push_back( 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 // unregisters->push_back( 1376 1377 1378 1379 1380 1381 1382 1383 body->push_back( 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1198 if ( !selectNodeDecl ) 1199 SemanticError( stmt, "waituntil statement requires #include <waituntil.hfa>" ); 1200 1201 // Prep clause tree to figure out how to set initial statuses 1202 // setTreeSizes( stmt->predicateTree ); 1203 if ( paintWhenTree( stmt->predicateTree ) ) // if this returns true we can special case since tree is all OR's 1204 return genAllOr( stmt ); 1205 1206 CompoundStmt * tryBody = new CompoundStmt( stmt->location ); 1207 CompoundStmt * body = new CompoundStmt( stmt->location ); 1208 string statusArrName = namer_status.newName(); 1209 string pCountName = namer_park.newName(); 1210 string satName = namer_sat.newName(); 1211 string runName = namer_run.newName(); 1212 string elseWhenName = namer_when.newName(); 1213 int numClauses = stmt->clauses.size(); 1214 addPredicates( stmt, satName, runName ); 1215 1216 const CodeLocation & loc = stmt->location; 1217 1218 // Generates: int park_counter = 0; 1219 body->push_back( new DeclStmt( loc, 1220 new ObjectDecl( loc, 1221 pCountName, 1222 new BasicType( BasicKind::SignedInt ), 1223 new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) ) 1224 ) 1225 )); 1226 1227 // Generates: int clause_statuses[3] = { 0 }; 1228 body->push_back( new DeclStmt( loc, 1229 new ObjectDecl( loc, 1230 statusArrName, 1231 new ArrayType( new BasicType( BasicKind::LongUnsignedInt ), ConstantExpr::from_int( loc, numClauses ), LengthFlag::FixedLen, DimensionFlag::DynamicDim ), 1232 new ListInit( loc, 1233 { 1234 new SingleInit( loc, ConstantExpr::from_int( loc, 0 ) ) 1235 } 1236 ) 1237 ) 1238 )); 1239 1240 vector<ClauseData *> clauseData; 1241 genClauseInits( stmt, clauseData, body, statusArrName, elseWhenName ); 1242 1243 vector<pair<int, WaitUntilStmt::ClauseNode *>> ambiguousClauses; // list of ambiguous clauses 1244 vector<int> andWhenClauses; // list of clauses that have an AND op as a direct parent and when_cond defined 1245 1246 collectWhens( stmt->predicateTree, ambiguousClauses, andWhenClauses ); 1247 1248 // This is only needed for clauses that have AND as a parent and a when_cond defined 1249 // generates: if ( ! when_cond_0 ) clause_statuses_0 = __SELECT_RUN; 1250 for ( int idx : andWhenClauses ) { 1251 const CodeLocation & cLoc = stmt->clauses.at(idx)->location; 1252 body->push_back( 1253 new IfStmt( cLoc, 1254 new UntypedExpr ( cLoc, 1255 new NameExpr( cLoc, "!?" ), 1256 { new NameExpr( cLoc, clauseData.at(idx)->whenName ) } 1257 ), // IfStmt cond 1258 new ExprStmt( cLoc, 1259 new UntypedExpr ( cLoc, 1260 new NameExpr( cLoc, "?=?" ), 1261 { 1262 new UntypedExpr ( cLoc, 1263 new NameExpr( cLoc, "?[?]" ), 1264 { 1265 new NameExpr( cLoc, statusArrName ), 1266 ConstantExpr::from_int( cLoc, idx ) 1267 } 1268 ), 1269 new NameExpr( cLoc, "__SELECT_RUN" ) 1270 } 1271 ) 1272 ) // IfStmt then 1273 ) 1274 ); 1275 } 1276 1277 // Only need to generate conditional initial state setting for ambiguous when clauses 1278 if ( !ambiguousClauses.empty() ) { 1279 body->push_back( genWhenStateConditions( stmt, clauseData, ambiguousClauses, 0 ) ); 1280 } 1281 1282 // generates the following for each clause: 1283 // setup_clause( clause1, &clause_statuses[0], &park_counter ); 1284 // register_select(A, clause1); 1285 for ( int i = 0; i < numClauses; i++ ) { 1286 setUpClause( stmt->clauses.at(i), clauseData.at(i), pCountName, tryBody ); 1287 } 1288 1289 // generate satisfy logic based on if there is an else clause and if it is conditional 1290 if ( stmt->else_stmt && stmt->else_cond ) { // gen both else/non else branches 1291 tryBody->push_back( 1292 new IfStmt( stmt->else_cond->location, 1293 new NameExpr( stmt->else_cond->location, elseWhenName ), 1294 genElseClauseBranch( stmt, runName, statusArrName, clauseData ), 1295 genNoElseClauseBranch( stmt, runName, statusArrName, pCountName, clauseData ) 1296 ) 1297 ); 1298 } else if ( !stmt->else_stmt ) { // normal gen 1299 tryBody->push_back( genNoElseClauseBranch( stmt, runName, statusArrName, pCountName, clauseData ) ); 1300 } else { // generate just else 1301 tryBody->push_back( genElseClauseBranch( stmt, runName, statusArrName, clauseData ) ); 1302 } 1303 1304 // Collection of unregister calls on resources to be put in finally clause 1305 // for each clause: 1306 // if ( !__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ) ) { ... clausei stmt ... } 1307 // OR if when( ... ) defined on resource 1308 // if ( when_cond_i && (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ) ) { ... clausei stmt ... } 1309 CompoundStmt * unregisters = new CompoundStmt( loc ); 1310 1311 Expr * statusExpr; // !__CFA_has_clause_run( clause_statuses[i] ) 1312 for ( int i = 0; i < numClauses; i++ ) { 1313 const CodeLocation & cLoc = stmt->clauses.at(i)->location; 1314 1315 // Generates: !__CFA_has_clause_run( clause_statuses[i] ) 1316 statusExpr = new UntypedExpr ( cLoc, 1317 new NameExpr( cLoc, "!?" ), 1318 { 1319 new UntypedExpr ( cLoc, 1320 new NameExpr( cLoc, "__CFA_has_clause_run" ), 1321 { 1322 genArrAccessExpr( cLoc, i, statusArrName ) 1323 } 1324 ) 1325 } 1326 ); 1327 1328 // Generates: 1329 // (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ); 1330 statusExpr = new LogicalExpr( cLoc, 1331 new CastExpr( cLoc, 1332 statusExpr, 1333 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1334 ), 1335 new CastExpr( cLoc, 1336 genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "unregister_select" ), 1337 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1338 ), 1339 LogicalFlag::AndExpr 1340 ); 1341 1342 // if when cond defined generates: 1343 // when_cond_i && (!__CFA_has_clause_run( clause_statuses[i] )) && unregister_select( ... , clausei ); 1344 if ( stmt->clauses.at(i)->when_cond ) 1345 statusExpr = new LogicalExpr( cLoc, 1346 new CastExpr( cLoc, 1347 new NameExpr( cLoc, clauseData.at(i)->whenName ), 1348 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1349 ), 1350 new CastExpr( cLoc, 1351 statusExpr, 1352 new BasicType( BasicKind::Bool ), GeneratedFlag::ExplicitCast 1353 ), 1354 LogicalFlag::AndExpr 1355 ); 1356 1357 // generates: 1358 // if ( statusExpr ) { ... clausei stmt ... } 1359 unregisters->push_back( 1360 new IfStmt( cLoc, 1361 statusExpr, 1362 new CompoundStmt( cLoc, 1363 { 1364 new IfStmt( cLoc, 1365 genSelectTraitCall( stmt->clauses.at(i), clauseData.at(i), "on_selected" ), 1366 ast::deepCopy( stmt->clauses.at(i)->stmt ) 1367 ) 1368 } 1369 ) 1370 ) 1371 ); 1372 1373 // // generates: 1374 // // if ( statusExpr ) { ... clausei stmt ... } 1375 // unregisters->push_back( 1376 // new IfStmt( cLoc, 1377 // statusExpr, 1378 // genStmtBlock( stmt->clauses.at(i), clauseData.at(i) ) 1379 // ) 1380 // ); 1381 } 1382 1383 body->push_back( 1384 new ast::TryStmt( 1385 loc, 1386 tryBody, 1387 {}, 1388 new ast::FinallyClause( loc, unregisters ) 1389 ) 1390 ); 1391 1392 for ( ClauseData * datum : clauseData ) 1393 delete datum; 1394 1395 return body; 1396 1396 } 1397 1397 … … 1399 1399 // Predicates are added after "struct select_node { ... };" 1400 1400 class AddPredicateDecls final : public WithDeclsToAdd<> { 1401 1402 1401 vector<FunctionDecl *> & satFns; 1402 const StructDecl * selectNodeDecl = nullptr; 1403 1403 1404 1404 public: 1405 1406 1407 1408 1409 1410 1411 1412 declsToAddAfter.push_back(fn); 1413 1414 1415 1405 void previsit( const StructDecl * decl ) { 1406 if ( !decl->body ) { 1407 return; 1408 } else if ( "select_node" == decl->name ) { 1409 assert( !selectNodeDecl ); 1410 selectNodeDecl = decl; 1411 for ( FunctionDecl * fn : satFns ) 1412 declsToAddAfter.push_back(fn); 1413 } 1414 } 1415 AddPredicateDecls( vector<FunctionDecl *> & satFns ): satFns(satFns) {} 1416 1416 }; 1417 1417 1418 1418 void generateWaitUntil( TranslationUnit & translationUnit ) { 1419 1419 vector<FunctionDecl *> satFns; 1420 1420 Pass<GenerateWaitUntilCore>::run( translationUnit, satFns ); 1421 1421 Pass<AddPredicateDecls>::run( translationUnit, satFns ); 1422 1422 } 1423 1423 -
src/Parser/parser.yy
rcf191ac r7042c60 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Sat Mar 16 18:19:23202413 // Update Count : 66 1712 // Last Modified On : Tue Apr 23 15:39:29 2024 13 // Update Count : 6620 14 14 // 15 15 … … 493 493 %type<decl> exception_declaration 494 494 495 %type<decl> field_declaration_list_opt field_declaration field_declaring_list_opt field_declar ator field_abstract_list_opt field_abstract495 %type<decl> field_declaration_list_opt field_declaration field_declaring_list_opt field_declaring_list field_declarator field_abstract_list_opt field_abstract 496 496 %type<expr> field field_name_list field_name fraction_constants_opt 497 497 … … 2682 2682 // empty 2683 2683 { $$ = nullptr; } 2684 | field_declarator 2685 | field_declaring_list_opt ',' attribute_list_opt field_declarator 2684 | field_declaring_list 2685 ; 2686 2687 field_declaring_list: 2688 field_declarator 2689 | field_declaring_list ',' attribute_list_opt field_declarator 2686 2690 { $$ = $1->set_last( $4->addQualifiers( $3 ) ); } 2687 2691 ; -
src/ResolvExpr/CandidateFinder.cpp
rcf191ac r7042c60 1412 1412 } 1413 1413 1414 1415 1416 1417 1418 1419 1414 void Finder::postvisit(const ast::VariableExpr *variableExpr) { 1415 // not sufficient to just pass `variableExpr` here, type might have changed 1416 1417 auto cand = new Candidate(variableExpr, tenv); 1418 candidates.emplace_back(cand); 1419 } 1420 1420 1421 1421 void Finder::postvisit( const ast::ConstantExpr * constantExpr ) { … … 2133 2133 2134 2134 // get the valueE(...) ApplicationExpr that returns the enum value 2135 const ast::Expr * getValueEnumCall( 2136 const ast::Expr * expr, 2135 const ast::Expr * getValueEnumCall( 2136 const ast::Expr * expr, 2137 2137 const ResolvExpr::ResolveContext & context, const ast::TypeEnvironment & env ) { 2138 2138 auto callExpr = new ast::UntypedExpr( -
src/ResolvExpr/CommonType.cc
rcf191ac r7042c60 36 36 namespace ResolvExpr { 37 37 38 namespace { 39 38 40 // GENERATED START, DO NOT EDIT 39 41 // GENERATED BY BasicTypes-gen.cc … … 397 399 } 398 400 } else if ( auto type2AsAttr = dynamic_cast< const ast::EnumAttrType * >( type2 ) ) { 399 400 401 402 403 404 405 406 407 408 409 401 if ( type2AsAttr->attr == ast::EnumAttribute::Posn ) { 402 ast::BasicKind kind = commonTypes[ basic->kind ][ ast::BasicKind::SignedInt ]; 403 if ( 404 ( ( kind == basic->kind && basic->qualifiers >= type2->qualifiers ) 405 || widen.first ) 406 && ( ( kind != basic->kind && basic->qualifiers <= type2->qualifiers ) 407 || widen.second ) 408 ) { 409 result = new ast::BasicType{ kind, basic->qualifiers | type2->qualifiers }; 410 } 411 } 410 412 } 411 413 } … … 519 521 // xxx - assume LHS is always the target type 520 522 521 if ( ! ((widen.second && ref2->qualifiers.is_mutex) 523 if ( ! ((widen.second && ref2->qualifiers.is_mutex) 522 524 || (ref1->qualifiers.is_mutex == ref2->qualifiers.is_mutex ))) return; 523 525 … … 710 712 // size_t CommonType::traceId = Stats::Heap::new_stacktrace_id("CommonType"); 711 713 712 namespace { 713 ast::ptr< ast::Type > handleReference( 714 const ast::ptr< ast::Type > & t1, const ast::ptr< ast::Type > & t2, WidenMode widen, 715 ast::TypeEnvironment & env, 716 const ast::OpenVarSet & open 717 ) { 718 ast::ptr<ast::Type> common; 719 ast::AssertionSet have, need; 720 ast::OpenVarSet newOpen{ open }; 721 722 // need unify to bind type variables 723 if ( unify( t1, t2, env, have, need, newOpen, common ) ) { 724 ast::CV::Qualifiers q1 = t1->qualifiers, q2 = t2->qualifiers; 714 ast::ptr< ast::Type > handleReference( 715 const ast::ptr< ast::Type > & t1, const ast::ptr< ast::Type > & t2, WidenMode widen, 716 ast::TypeEnvironment & env, 717 const ast::OpenVarSet & open 718 ) { 719 ast::ptr<ast::Type> common; 720 ast::AssertionSet have, need; 721 ast::OpenVarSet newOpen( open ); 722 723 // need unify to bind type variables 724 if ( unify( t1, t2, env, have, need, newOpen, common ) ) { 725 ast::CV::Qualifiers q1 = t1->qualifiers, q2 = t2->qualifiers; 726 PRINT( 727 std::cerr << "unify success: " << widenFirst << " " << widenSecond << std::endl; 728 ) 729 if ( ( widen.first || q2 <= q1 ) && ( widen.second || q1 <= q2 ) ) { 725 730 PRINT( 726 std::cerr << " unify success: " << widenFirst << " " << widenSecond<< std::endl;731 std::cerr << "widen okay" << std::endl; 727 732 ) 728 if ( ( widen.first || q2 <= q1 ) && ( widen.second || q1 <= q2 ) ) { 729 PRINT( 730 std::cerr << "widen okay" << std::endl; 731 ) 732 add_qualifiers( common, q1 | q2 ); 733 return common; 734 } 735 } 736 737 PRINT( 738 std::cerr << "exact unify failed: " << t1 << " " << t2 << std::endl; 739 ) 740 return { nullptr }; 741 } 733 add_qualifiers( common, q1 | q2 ); 734 return common; 735 } 736 } 737 738 PRINT( 739 std::cerr << "exact unify failed: " << t1 << " " << t2 << std::endl; 740 ) 741 return { nullptr }; 742 742 } 743 744 } // namespace 743 745 744 746 ast::ptr< ast::Type > commonType( … … 781 783 } 782 784 // otherwise both are reference types of the same depth and this is handled by the visitor 783 ast::Pass<CommonType> visitor{ type2, widen, env, open, need, have }; 784 type1->accept( visitor ); 785 // ast::ptr< ast::Type > result = visitor.core.result; 786 787 return visitor.core.result; 785 return ast::Pass<CommonType>::read( type1.get(), 786 type2, widen, env, open, need, have ); 788 787 } 789 788 -
src/ResolvExpr/ConversionCost.cc
rcf191ac r7042c60 31 31 #define PRINT(x) 32 32 #endif 33 34 namespace { 33 35 34 36 // GENERATED START, DO NOT EDIT … … 152 154 ); 153 155 154 namespace {155 156 int localPtrsAssignable(const ast::Type * t1, const ast::Type * t2, 156 157 const ast::SymbolTable &, const ast::TypeEnvironment & env ) { … … 379 380 380 381 void ConversionCost::postvisit( const ast::EnumAttrType * src ) { 381 382 auto dstAsEnumAttrType = dynamic_cast<const ast::EnumAttrType *>(dst); 382 383 assert( src->attr != ast::EnumAttribute::Label ); 383 384 385 386 387 388 384 if ( src->attr == ast::EnumAttribute::Value ) { 385 if ( dstAsEnumAttrType && dstAsEnumAttrType->attr == ast::EnumAttribute::Value) { 386 cost = costCalc( src->instance, dstAsEnumAttrType->instance, srcIsLvalue, symtab, env ); 387 } else { 388 auto baseType = src->instance->base->base; 389 cost = costCalc( baseType, dst, srcIsLvalue, symtab, env ); 389 390 if ( cost < Cost::infinity ) { 390 391 cost.incUnsafe(); 391 392 } 392 393 394 395 396 397 398 399 400 401 402 403 404 393 } 394 } else { // ast::EnumAttribute::Posn 395 if ( auto dstBase = dynamic_cast<const ast::EnumInstType *>( dst ) ) { 396 cost = costCalc( src->instance, dstBase, srcIsLvalue, symtab, env ); 397 if ( cost < Cost::unsafe ) cost.incSafe(); 398 } else { 399 static ast::ptr<ast::BasicType> integer = { new ast::BasicType( ast::BasicKind::SignedInt ) }; 400 cost = costCalc( integer, dst, srcIsLvalue, symtab, env ); 401 if ( cost < Cost::unsafe ) { 402 cost.incSafe(); 403 } 404 } 405 } 405 406 } 406 407 -
src/ResolvExpr/PolyCost.cc
rcf191ac r7042c60 29 29 const ast::TypeEnvironment &env_; 30 30 31 PolyCost( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) 31 PolyCost( const ast::SymbolTable & symtab, const ast::TypeEnvironment & env ) 32 32 : symtab( symtab ), result( 0 ), env_( env ) {} 33 33 34 34 void previsit( const ast::TypeInstType * type ) { 35 if ( const ast::EqvClass * eqv = env_.lookup( *type ) ) /* && */ if (eqv->bound ) {35 if ( const ast::EqvClass * eqv = env_.lookup( *type ) ; eqv && eqv->bound ) { 36 36 if ( const ast::TypeInstType * otherType = eqv->bound.as< ast::TypeInstType >() ) { 37 37 if ( symtab.lookupType( otherType->name ) ) { 38 38 // Bound to opaque type. 39 result += 1;39 result = 1; 40 40 } 41 41 } else { 42 42 // Bound to concrete type. 43 result += 1;43 result = 1; 44 44 } 45 45 } … … 52 52 const ast::Type * type, const ast::SymbolTable & symtab, const ast::TypeEnvironment & env 53 53 ) { 54 ast::Pass<PolyCost> costing( symtab, env ); 55 type->accept( costing ); 56 return (costing.core.result > 0) ? 1 : 0; 54 return ast::Pass<PolyCost>::read( type, symtab, env ); 57 55 } 58 56 -
src/ResolvExpr/RenameVars.cc
rcf191ac r7042c60 30 30 31 31 namespace { 32 class RenamingData {33 int level = 0;34 int resetCount = 0;35 32 36 int next_expr_id = 1; 37 int next_usage_id = 1; 38 ScopedMap< std::string, std::string > nameMap; 39 ScopedMap< std::string, ast::TypeEnvKey > idMap; 40 public: 41 void reset() { 42 level = 0; 43 ++resetCount; 33 class RenamingData { 34 int level = 0; 35 int resetCount = 0; 36 37 int next_expr_id = 1; 38 int next_usage_id = 1; 39 ScopedMap< std::string, std::string > nameMap; 40 ScopedMap< std::string, ast::TypeEnvKey > idMap; 41 public: 42 void reset() { 43 level = 0; 44 ++resetCount; 45 } 46 47 void nextUsage() { 48 ++next_usage_id; 49 } 50 51 const ast::TypeInstType * rename( const ast::TypeInstType * type ) { 52 auto it = idMap.find( type->name ); 53 if ( it == idMap.end() ) return type; 54 55 // Unconditionally mutate because map will *always* have different name. 56 ast::TypeInstType * mut = ast::shallowCopy( type ); 57 // Reconcile base node since some copies might have been made. 58 mut->base = it->second.base; 59 mut->formal_usage = it->second.formal_usage; 60 mut->expr_id = it->second.expr_id; 61 return mut; 62 } 63 64 const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) { 65 if ( type->forall.empty() ) return type; 66 idMap.beginScope(); 67 68 // Load new names from this forall clause and perform renaming. 69 auto mutType = ast::shallowCopy( type ); 70 // assert( type == mutType && "mutated type must be unique from ForallSubstitutor" ); 71 for ( auto & td : mutType->forall ) { 72 auto mut = ast::shallowCopy( td.get() ); 73 // assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" ); 74 75 if ( mode == GEN_EXPR_ID ) { 76 mut->expr_id = next_expr_id; 77 mut->formal_usage = -1; 78 ++next_expr_id; 79 } else if ( mode == GEN_USAGE ) { 80 assertf( mut->expr_id, "unfilled expression id in generating candidate type" ); 81 mut->formal_usage = next_usage_id; 82 } else { 83 assert(false); 84 } 85 idMap[ td->name ] = ast::TypeEnvKey( *mut ); 86 87 td = mut; 44 88 } 45 89 46 void nextUsage() { 47 ++next_usage_id; 48 } 90 return mutType; 91 } 49 92 50 const ast::TypeInstType * rename( const ast::TypeInstType * type ) { 51 auto it = idMap.find( type->name ); 52 if ( it == idMap.end() ) return type; 93 void closeLevel( const ast::FunctionType * type ) { 94 if ( type->forall.empty() ) return; 95 idMap.endScope(); 96 } 97 }; 53 98 54 // Unconditionally mutate because map will *always* have different name. 55 ast::TypeInstType * mut = ast::shallowCopy( type ); 56 // Reconcile base node since some copies might have been made. 57 mut->base = it->second.base; 58 mut->formal_usage = it->second.formal_usage; 59 mut->expr_id = it->second.expr_id; 60 return mut; 61 } 99 // Global State: 100 RenamingData renaming; 62 101 63 const ast::FunctionType * openLevel( const ast::FunctionType * type, RenameMode mode ) { 64 if ( type->forall.empty() ) return type; 65 idMap.beginScope(); 102 struct RenameVars final : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ { 103 RenameMode mode; 66 104 67 // Load new names from this forall clause and perform renaming. 68 auto mutType = ast::shallowCopy( type ); 69 // assert( type == mutType && "mutated type must be unique from ForallSubstitutor" ); 70 for ( auto & td : mutType->forall ) { 71 auto mut = ast::shallowCopy( td.get() ); 72 // assert( td == mutDecl && "mutated decl must be unique from ForallSubstitutor" ); 105 const ast::FunctionType * previsit( const ast::FunctionType * type ) { 106 return renaming.openLevel( type, mode ); 107 } 73 108 74 if (mode == GEN_EXPR_ID) { 75 mut->expr_id = next_expr_id; 76 mut->formal_usage = -1; 77 ++next_expr_id; 78 } 79 else if (mode == GEN_USAGE) { 80 assertf(mut->expr_id, "unfilled expression id in generating candidate type"); 81 mut->formal_usage = next_usage_id; 82 } 83 else { 84 assert(false); 85 } 86 idMap[ td->name ] = ast::TypeEnvKey( *mut ); 109 /* 110 const ast::StructInstType * previsit( const ast::StructInstType * type ) { 111 return renaming.openLevel( type ); 112 } 113 const ast::UnionInstType * previsit( const ast::UnionInstType * type ) { 114 return renaming.openLevel( type ); 115 } 116 const ast::TraitInstType * previsit( const ast::TraitInstType * type ) { 117 return renaming.openLevel( type ); 118 } 119 */ 87 120 88 td = mut; 89 } 90 91 return mutType; 92 } 93 94 void closeLevel( const ast::FunctionType * type ) { 95 if ( type->forall.empty() ) return; 96 idMap.endScope(); 97 } 98 }; 99 100 // Global State: 101 RenamingData renaming; 102 103 struct RenameVars final : public ast::PureVisitor /*: public ast::WithForallSubstitutor*/ { 104 RenameMode mode; 105 106 const ast::FunctionType * previsit( const ast::FunctionType * type ) { 107 return renaming.openLevel( type, mode ); 108 } 109 110 /* 111 const ast::StructInstType * previsit( const ast::StructInstType * type ) { 112 return renaming.openLevel( type ); 113 } 114 const ast::UnionInstType * previsit( const ast::UnionInstType * type ) { 115 return renaming.openLevel( type ); 116 } 117 const ast::TraitInstType * previsit( const ast::TraitInstType * type ) { 118 return renaming.openLevel( type ); 119 } 120 */ 121 122 const ast::TypeInstType * previsit( const ast::TypeInstType * type ) { 123 if (mode == GEN_USAGE && !type->formal_usage) return type; // do not rename an actual type 124 return renaming.rename( type ); 125 } 126 void postvisit( const ast::FunctionType * type ) { 127 renaming.closeLevel( type ); 128 } 129 }; 121 const ast::TypeInstType * previsit( const ast::TypeInstType * type ) { 122 // Do not rename an actual type. 123 if ( mode == GEN_USAGE && !type->formal_usage ) return type; 124 return renaming.rename( type ); 125 } 126 void postvisit( const ast::FunctionType * type ) { 127 renaming.closeLevel( type ); 128 } 129 }; 130 130 131 131 } // namespace -
src/ResolvExpr/Unify.cc
rcf191ac r7042c60 47 47 namespace ResolvExpr { 48 48 49 bool typesCompatible( 50 const ast::Type * first, const ast::Type * second, 51 const ast::TypeEnvironment & env ) { 52 ast::TypeEnvironment newEnv; 53 ast::OpenVarSet open, closed; 54 ast::AssertionSet need, have; 55 56 ast::ptr<ast::Type> newFirst{ first }, newSecond{ second }; 57 env.apply( newFirst ); 58 env.apply( newSecond ); 59 60 // findOpenVars( newFirst, open, closed, need, have, FirstClosed ); 61 findOpenVars( newSecond, open, closed, need, have, newEnv, FirstOpen ); 62 63 return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden() ); 64 } 65 66 bool typesCompatibleIgnoreQualifiers( 67 const ast::Type * first, const ast::Type * second, 68 const ast::TypeEnvironment & env ) { 69 ast::TypeEnvironment newEnv; 70 ast::OpenVarSet open; 71 ast::AssertionSet need, have; 72 73 ast::Type * newFirst = shallowCopy( first ); 74 ast::Type * newSecond = shallowCopy( second ); 75 76 newFirst ->qualifiers = {}; 77 newSecond->qualifiers = {}; 78 ast::ptr< ast::Type > t1_(newFirst ); 79 ast::ptr< ast::Type > t2_(newSecond); 80 81 ast::ptr< ast::Type > subFirst = env.apply(newFirst).node; 82 ast::ptr< ast::Type > subSecond = env.apply(newSecond).node; 83 84 return unifyExact( 85 subFirst, 86 subSecond, 87 newEnv, need, have, open, noWiden() ); 88 } 89 90 namespace { 91 /// Replaces ttype variables with their bound types. 92 /// If this isn't done when satifying ttype assertions, then argument lists can have 93 /// different size and structure when they should be compatible. 94 struct TtypeExpander : public ast::WithShortCircuiting, public ast::PureVisitor { 95 ast::TypeEnvironment & tenv; 96 97 TtypeExpander( ast::TypeEnvironment & env ) : tenv( env ) {} 98 99 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) { 100 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) { 101 // expand ttype parameter into its actual type 102 if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) { 103 return clz->bound; 104 } 49 bool typesCompatible( 50 const ast::Type * first, const ast::Type * second, 51 const ast::TypeEnvironment & env ) { 52 ast::TypeEnvironment newEnv; 53 ast::OpenVarSet open, closed; 54 ast::AssertionSet need, have; 55 56 ast::ptr<ast::Type> newFirst( first ), newSecond( second ); 57 env.apply( newFirst ); 58 env.apply( newSecond ); 59 60 // findOpenVars( newFirst, open, closed, need, have, FirstClosed ); 61 findOpenVars( newSecond, open, closed, need, have, newEnv, FirstOpen ); 62 63 return unifyExact(newFirst, newSecond, newEnv, need, have, open, noWiden() ); 64 } 65 66 bool typesCompatibleIgnoreQualifiers( 67 const ast::Type * first, const ast::Type * second, 68 const ast::TypeEnvironment & env ) { 69 ast::TypeEnvironment newEnv; 70 ast::OpenVarSet open; 71 ast::AssertionSet need, have; 72 73 ast::Type * newFirst = shallowCopy( first ); 74 ast::Type * newSecond = shallowCopy( second ); 75 76 newFirst ->qualifiers = {}; 77 newSecond->qualifiers = {}; 78 ast::ptr< ast::Type > t1_(newFirst ); 79 ast::ptr< ast::Type > t2_(newSecond); 80 81 ast::ptr< ast::Type > subFirst = env.apply(newFirst).node; 82 ast::ptr< ast::Type > subSecond = env.apply(newSecond).node; 83 84 return unifyExact( 85 subFirst, 86 subSecond, 87 newEnv, need, have, open, noWiden() ); 88 } 89 90 namespace { 91 /// Replaces ttype variables with their bound types. 92 /// If this isn't done when satifying ttype assertions, then argument lists can have 93 /// different size and structure when they should be compatible. 94 struct TtypeExpander : public ast::WithShortCircuiting, public ast::PureVisitor { 95 ast::TypeEnvironment & tenv; 96 97 TtypeExpander( ast::TypeEnvironment & env ) : tenv( env ) {} 98 99 const ast::Type * postvisit( const ast::TypeInstType * typeInst ) { 100 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) { 101 // expand ttype parameter into its actual type 102 if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) { 103 return clz->bound; 105 104 } 106 return typeInst;107 105 } 108 }; 109 } 110 111 std::vector< ast::ptr< ast::Type > > flattenList( 112 const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env 106 return typeInst; 107 } 108 }; 109 } 110 111 std::vector< ast::ptr< ast::Type > > flattenList( 112 const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env 113 ) { 114 std::vector< ast::ptr< ast::Type > > dst; 115 dst.reserve( src.size() ); 116 for ( const auto & d : src ) { 117 ast::Pass<TtypeExpander> expander( env ); 118 // TtypeExpander pass is impure (may mutate nodes in place) 119 // need to make nodes shared to prevent accidental mutation 120 ast::ptr<ast::Type> dc = d->accept(expander); 121 auto types = flatten( dc ); 122 for ( ast::ptr< ast::Type > & t : types ) { 123 // outermost const, volatile, _Atomic qualifiers in parameters should not play 124 // a role in the unification of function types, since they do not determine 125 // whether a function is callable. 126 // NOTE: **must** consider at least mutex qualifier, since functions can be 127 // overloaded on outermost mutex and a mutex function has different 128 // requirements than a non-mutex function 129 remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic ); 130 dst.emplace_back( t ); 131 } 132 } 133 return dst; 134 } 135 136 // Unification of Expressions 137 // 138 // Boolean outcome (obvious): Are they basically spelled the same? 139 // Side effect of binding variables (subtle): if `sizeof(int)` ===_expr `sizeof(T)` then `int` ===_ty `T` 140 // 141 // Context: if `float[VAREXPR1]` ===_ty `float[VAREXPR2]` then `VAREXPR1` ===_expr `VAREXPR2` 142 // where the VAREXPR are meant as notational metavariables representing the fact that unification always 143 // sees distinct ast::VariableExpr objects at these positions 144 145 static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env, 146 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 147 WidenMode widen ); 148 149 class UnifyExpr final : public ast::WithShortCircuiting { 150 const ast::Expr * e2; 151 ast::TypeEnvironment & tenv; 152 ast::AssertionSet & need; 153 ast::AssertionSet & have; 154 const ast::OpenVarSet & open; 155 WidenMode widen; 156 public: 157 bool result; 158 159 private: 160 161 void tryMatchOnStaticValue( const ast::Expr * e1 ) { 162 Evaluation r1 = eval(e1); 163 Evaluation r2 = eval(e2); 164 165 if ( !r1.hasKnownValue ) return; 166 if ( !r2.hasKnownValue ) return; 167 168 if ( r1.knownValue != r2.knownValue ) return; 169 170 visit_children = false; 171 result = true; 172 } 173 174 public: 175 176 void previsit( const ast::Node * ) { assert(false); } 177 178 void previsit( const ast::Expr * e1 ) { 179 tryMatchOnStaticValue( e1 ); 180 visit_children = false; 181 } 182 183 void previsit( const ast::CastExpr * e1 ) { 184 tryMatchOnStaticValue( e1 ); 185 186 if ( result ) { 187 assert( visit_children == false ); 188 } else { 189 assert( visit_children == true ); 190 visit_children = false; 191 192 auto e2c = dynamic_cast< const ast::CastExpr * >( e2 ); 193 if ( !e2c ) return; 194 195 // inspect casts' target types 196 if ( !unifyExact( 197 e1->result, e2c->result, tenv, need, have, open, widen ) ) return; 198 199 // inspect casts' inner expressions 200 result = unify( e1->arg, e2c->arg, tenv, need, have, open, widen ); 201 } 202 } 203 204 void previsit( const ast::VariableExpr * e1 ) { 205 tryMatchOnStaticValue( e1 ); 206 207 if ( result ) { 208 assert( visit_children == false ); 209 } else { 210 assert( visit_children == true ); 211 visit_children = false; 212 213 auto e2v = dynamic_cast< const ast::VariableExpr * >( e2 ); 214 if ( !e2v ) return; 215 216 assert(e1->var); 217 assert(e2v->var); 218 219 // conservative: variable exprs match if their declarations are represented by the same C++ AST object 220 result = (e1->var == e2v->var); 221 } 222 } 223 224 void previsit( const ast::SizeofExpr * e1 ) { 225 tryMatchOnStaticValue( e1 ); 226 227 if ( result ) { 228 assert( visit_children == false ); 229 } else { 230 assert( visit_children == true ); 231 visit_children = false; 232 233 auto e2so = dynamic_cast< const ast::SizeofExpr * >( e2 ); 234 if ( !e2so ) return; 235 236 assert((e1->type != nullptr) ^ (e1->expr != nullptr)); 237 assert((e2so->type != nullptr) ^ (e2so->expr != nullptr)); 238 if ( !(e1->type && e2so->type) ) return; 239 240 // expression unification calls type unification (mutual recursion) 241 result = unifyExact( e1->type, e2so->type, tenv, need, have, open, widen ); 242 } 243 } 244 245 UnifyExpr( const ast::Expr * e2, ast::TypeEnvironment & env, ast::AssertionSet & need, 246 ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen ) 247 : e2( e2 ), tenv(env), need(need), have(have), open(open), widen(widen), result(false) {} 248 }; 249 250 static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env, 251 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 252 WidenMode widen ) { 253 assert( e1 && e2 ); 254 return ast::Pass<UnifyExpr>::read( e1, e2, env, need, have, open, widen ); 255 } 256 257 class Unify final : public ast::WithShortCircuiting { 258 const ast::Type * type2; 259 ast::TypeEnvironment & tenv; 260 ast::AssertionSet & need; 261 ast::AssertionSet & have; 262 const ast::OpenVarSet & open; 263 WidenMode widen; 264 public: 265 static size_t traceId; 266 bool result; 267 268 Unify( 269 const ast::Type * type2, ast::TypeEnvironment & env, ast::AssertionSet & need, 270 ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen ) 271 : type2(type2), tenv(env), need(need), have(have), open(open), widen(widen), 272 result(false) {} 273 274 void previsit( const ast::Node * ) { visit_children = false; } 275 276 void postvisit( const ast::VoidType * vt) { 277 result = dynamic_cast< const ast::VoidType * >( type2 ) 278 || tryToUnifyWithEnumValue(vt, type2, tenv, need, have, open, noWiden()); 279 ; 280 } 281 282 void postvisit( const ast::BasicType * basic ) { 283 if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) { 284 result = basic->kind == basic2->kind; 285 } 286 result = result || tryToUnifyWithEnumValue(basic, type2, tenv, need, have, open, noWiden()); 287 } 288 289 void postvisit( const ast::PointerType * pointer ) { 290 if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) { 291 result = unifyExact( 292 pointer->base, pointer2->base, tenv, need, have, open, 293 noWiden()); 294 } 295 result = result || tryToUnifyWithEnumValue(pointer, type2, tenv, need, have, open, noWiden()); 296 } 297 298 void postvisit( const ast::ArrayType * array ) { 299 auto array2 = dynamic_cast< const ast::ArrayType * >( type2 ); 300 if ( !array2 ) return; 301 302 if ( array->isVarLen != array2->isVarLen ) return; 303 if ( (array->dimension != nullptr) != (array2->dimension != nullptr) ) return; 304 305 if ( array->dimension ) { 306 assert( array2->dimension ); 307 // type unification calls expression unification (mutual recursion) 308 if ( !unify(array->dimension, array2->dimension, 309 tenv, need, have, open, widen) ) return; 310 } 311 312 result = unifyExact( 313 array->base, array2->base, tenv, need, have, open, noWiden()) 314 || tryToUnifyWithEnumValue(array, type2, tenv, need, have, open, noWiden()); 315 } 316 317 void postvisit( const ast::ReferenceType * ref ) { 318 if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) { 319 result = unifyExact( 320 ref->base, ref2->base, tenv, need, have, open, noWiden()); 321 } 322 } 323 324 private: 325 326 template< typename Iter > 327 static bool unifyTypeList( 328 Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env, 329 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open 113 330 ) { 114 std::vector< ast::ptr< ast::Type > > dst; 115 dst.reserve( src.size() ); 116 for ( const auto & d : src ) { 117 ast::Pass<TtypeExpander> expander{ env }; 118 // TtypeExpander pass is impure (may mutate nodes in place) 119 // need to make nodes shared to prevent accidental mutation 120 ast::ptr<ast::Type> dc = d->accept(expander); 121 auto types = flatten( dc ); 122 for ( ast::ptr< ast::Type > & t : types ) { 123 // outermost const, volatile, _Atomic qualifiers in parameters should not play 124 // a role in the unification of function types, since they do not determine 125 // whether a function is callable. 126 // NOTE: **must** consider at least mutex qualifier, since functions can be 127 // overloaded on outermost mutex and a mutex function has different 128 // requirements than a non-mutex function 129 remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic ); 130 dst.emplace_back( t ); 131 } 132 } 133 return dst; 134 } 135 136 // Unification of Expressions 137 // 138 // Boolean outcome (obvious): Are they basically spelled the same? 139 // Side effect of binding variables (subtle): if `sizeof(int)` ===_expr `sizeof(T)` then `int` ===_ty `T` 140 // 141 // Context: if `float[VAREXPR1]` ===_ty `float[VAREXPR2]` then `VAREXPR1` ===_expr `VAREXPR2` 142 // where the VAREXPR are meant as notational metavariables representing the fact that unification always 143 // sees distinct ast::VariableExpr objects at these positions 144 145 static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env, 146 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 147 WidenMode widen ); 148 149 class UnifyExpr final : public ast::WithShortCircuiting { 150 const ast::Expr * e2; 151 ast::TypeEnvironment & tenv; 152 ast::AssertionSet & need; 153 ast::AssertionSet & have; 154 const ast::OpenVarSet & open; 155 WidenMode widen; 156 public: 157 bool result; 158 159 private: 160 161 void tryMatchOnStaticValue( const ast::Expr * e1 ) { 162 Evaluation r1 = eval(e1); 163 Evaluation r2 = eval(e2); 164 165 if ( ! r1.hasKnownValue ) return; 166 if ( ! r2.hasKnownValue ) return; 167 168 if (r1.knownValue != r2.knownValue) return; 169 170 visit_children = false; 171 result = true; 172 } 173 174 public: 175 176 void previsit( const ast::Node * ) { assert(false); } 177 178 void previsit( const ast::Expr * e1 ) { 179 tryMatchOnStaticValue( e1 ); 180 visit_children = false; 181 } 182 183 void previsit( const ast::CastExpr * e1 ) { 184 tryMatchOnStaticValue( e1 ); 185 186 if (result) { 187 assert (visit_children == false); 188 } else { 189 assert (visit_children == true); 190 visit_children = false; 191 192 auto e2c = dynamic_cast< const ast::CastExpr * >( e2 ); 193 if ( ! e2c ) return; 194 195 // inspect casts' target types 196 if ( ! unifyExact( 197 e1->result, e2c->result, tenv, need, have, open, widen ) ) return; 198 199 // inspect casts' inner expressions 200 result = unify( e1->arg, e2c->arg, tenv, need, have, open, widen ); 201 } 202 } 203 204 void previsit( const ast::VariableExpr * e1 ) { 205 tryMatchOnStaticValue( e1 ); 206 207 if (result) { 208 assert (visit_children == false); 209 } else { 210 assert (visit_children == true); 211 visit_children = false; 212 213 auto e2v = dynamic_cast< const ast::VariableExpr * >( e2 ); 214 if ( ! e2v ) return; 215 216 assert(e1->var); 217 assert(e2v->var); 218 219 // conservative: variable exprs match if their declarations are represented by the same C++ AST object 220 result = (e1->var == e2v->var); 221 } 222 } 223 224 void previsit( const ast::SizeofExpr * e1 ) { 225 tryMatchOnStaticValue( e1 ); 226 227 if (result) { 228 assert (visit_children == false); 229 } else { 230 assert (visit_children == true); 231 visit_children = false; 232 233 auto e2so = dynamic_cast< const ast::SizeofExpr * >( e2 ); 234 if ( ! e2so ) return; 235 236 assert((e1->type != nullptr) ^ (e1->expr != nullptr)); 237 assert((e2so->type != nullptr) ^ (e2so->expr != nullptr)); 238 if ( ! (e1->type && e2so->type) ) return; 239 240 // expression unification calls type unification (mutual recursion) 241 result = unifyExact( e1->type, e2so->type, tenv, need, have, open, widen ); 242 } 243 } 244 245 UnifyExpr( const ast::Expr * e2, ast::TypeEnvironment & env, ast::AssertionSet & need, 246 ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen ) 247 : e2( e2 ), tenv(env), need(need), have(have), open(open), widen(widen), result(false) {} 248 }; 249 250 static bool unify( const ast::Expr * e1, const ast::Expr * e2, ast::TypeEnvironment & env, 251 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 252 WidenMode widen ) { 253 assert( e1 && e2 ); 254 return ast::Pass<UnifyExpr>::read( e1, e2, env, need, have, open, widen ); 255 } 256 257 class Unify final : public ast::WithShortCircuiting { 258 const ast::Type * type2; 259 ast::TypeEnvironment & tenv; 260 ast::AssertionSet & need; 261 ast::AssertionSet & have; 262 const ast::OpenVarSet & open; 263 WidenMode widen; 264 public: 265 static size_t traceId; 266 bool result; 267 268 Unify( 269 const ast::Type * type2, ast::TypeEnvironment & env, ast::AssertionSet & need, 270 ast::AssertionSet & have, const ast::OpenVarSet & open, WidenMode widen ) 271 : type2(type2), tenv(env), need(need), have(have), open(open), widen(widen), 272 result(false) {} 273 274 void previsit( const ast::Node * ) { visit_children = false; } 275 276 void postvisit( const ast::VoidType * vt) { 277 result = dynamic_cast< const ast::VoidType * >( type2 ) 278 || tryToUnifyWithEnumValue(vt, type2, tenv, need, have, open, noWiden()); 279 ; 280 } 281 282 void postvisit( const ast::BasicType * basic ) { 283 if ( auto basic2 = dynamic_cast< const ast::BasicType * >( type2 ) ) { 284 result = basic->kind == basic2->kind; 285 } 286 result = result || tryToUnifyWithEnumValue(basic, type2, tenv, need, have, open, noWiden()); 287 } 288 289 void postvisit( const ast::PointerType * pointer ) { 290 if ( auto pointer2 = dynamic_cast< const ast::PointerType * >( type2 ) ) { 291 result = unifyExact( 292 pointer->base, pointer2->base, tenv, need, have, open, 293 noWiden()); 294 } 295 result = result || tryToUnifyWithEnumValue(pointer, type2, tenv, need, have, open, noWiden()); 296 } 297 298 void postvisit( const ast::ArrayType * array ) { 299 auto array2 = dynamic_cast< const ast::ArrayType * >( type2 ); 300 if ( ! array2 ) return; 301 302 if ( array->isVarLen != array2->isVarLen ) return; 303 if ( (array->dimension != nullptr) != (array2->dimension != nullptr) ) return; 304 305 if ( array->dimension ) { 306 assert( array2->dimension ); 307 // type unification calls expression unification (mutual recursion) 308 if ( ! unify(array->dimension, array2->dimension, 309 tenv, need, have, open, widen) ) return; 310 } 311 312 result = unifyExact( 313 array->base, array2->base, tenv, need, have, open, noWiden()) 314 || tryToUnifyWithEnumValue(array, type2, tenv, need, have, open, noWiden()); 315 } 316 317 void postvisit( const ast::ReferenceType * ref ) { 318 if ( auto ref2 = dynamic_cast< const ast::ReferenceType * >( type2 ) ) { 319 result = unifyExact( 320 ref->base, ref2->base, tenv, need, have, open, noWiden()); 321 } 322 } 323 324 private: 325 326 template< typename Iter > 327 static bool unifyTypeList( 328 Iter crnt1, Iter end1, Iter crnt2, Iter end2, ast::TypeEnvironment & env, 329 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open 330 ) { 331 while ( crnt1 != end1 && crnt2 != end2 ) { 332 const ast::Type * t1 = *crnt1; 333 const ast::Type * t2 = *crnt2; 334 bool isTuple1 = Tuples::isTtype( t1 ); 335 bool isTuple2 = Tuples::isTtype( t2 ); 336 337 // assumes here that ttype *must* be last parameter 338 if ( isTuple1 && ! isTuple2 ) { 339 // combine remainder of list2, then unify 340 return unifyExact( 341 t1, tupleFromTypes( crnt2, end2 ), env, need, have, open, 342 noWiden() ); 343 } else if ( ! isTuple1 && isTuple2 ) { 344 // combine remainder of list1, then unify 345 return unifyExact( 346 tupleFromTypes( crnt1, end1 ), t2, env, need, have, open, 347 noWiden() ); 348 } 349 350 if ( ! unifyExact( 351 t1, t2, env, need, have, open, noWiden() ) 352 ) return false; 353 354 ++crnt1; ++crnt2; 355 } 356 357 // May get to the end of one argument list before the other. This is only okay if the 358 // other is a ttype 359 if ( crnt1 != end1 ) { 360 // try unifying empty tuple with ttype 361 const ast::Type * t1 = *crnt1; 362 if ( ! Tuples::isTtype( t1 ) ) return false; 331 while ( crnt1 != end1 && crnt2 != end2 ) { 332 const ast::Type * t1 = *crnt1; 333 const ast::Type * t2 = *crnt2; 334 bool isTuple1 = Tuples::isTtype( t1 ); 335 bool isTuple2 = Tuples::isTtype( t2 ); 336 337 // assumes here that ttype *must* be last parameter 338 if ( isTuple1 && !isTuple2 ) { 339 // combine remainder of list2, then unify 363 340 return unifyExact( 364 341 t1, tupleFromTypes( crnt2, end2 ), env, need, have, open, 365 342 noWiden() ); 366 } else if ( crnt2 != end2 ) { 367 // try unifying empty tuple with ttype 368 const ast::Type * t2 = *crnt2; 369 if ( ! Tuples::isTtype( t2 ) ) return false; 343 } else if ( !isTuple1 && isTuple2 ) { 344 // combine remainder of list1, then unify 370 345 return unifyExact( 371 346 tupleFromTypes( crnt1, end1 ), t2, env, need, have, open, … … 373 348 } 374 349 375 return true; 376 } 377 378 static bool unifyTypeList( 379 const std::vector< ast::ptr< ast::Type > > & list1, 380 const std::vector< ast::ptr< ast::Type > > & list2, 381 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 382 const ast::OpenVarSet & open 383 ) { 384 return unifyTypeList( 385 list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open); 386 } 387 388 static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) { 389 auto i = assns.find( assn ); 390 if ( i != assns.end() ) { 391 i->second.isUsed = true; 350 if ( !unifyExact( 351 t1, t2, env, need, have, open, noWiden() ) 352 ) return false; 353 354 ++crnt1; ++crnt2; 355 } 356 357 // May get to the end of one argument list before the other. This is only okay if the 358 // other is a ttype 359 if ( crnt1 != end1 ) { 360 // try unifying empty tuple with ttype 361 const ast::Type * t1 = *crnt1; 362 if ( !Tuples::isTtype( t1 ) ) return false; 363 return unifyExact( 364 t1, tupleFromTypes( crnt2, end2 ), env, need, have, open, 365 noWiden() ); 366 } else if ( crnt2 != end2 ) { 367 // try unifying empty tuple with ttype 368 const ast::Type * t2 = *crnt2; 369 if ( !Tuples::isTtype( t2 ) ) return false; 370 return unifyExact( 371 tupleFromTypes( crnt1, end1 ), t2, env, need, have, open, 372 noWiden() ); 373 } 374 375 return true; 376 } 377 378 static bool unifyTypeList( 379 const std::vector< ast::ptr< ast::Type > > & list1, 380 const std::vector< ast::ptr< ast::Type > > & list2, 381 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 382 const ast::OpenVarSet & open 383 ) { 384 return unifyTypeList( 385 list1.begin(), list1.end(), list2.begin(), list2.end(), env, need, have, open); 386 } 387 388 static void markAssertionSet( ast::AssertionSet & assns, const ast::VariableExpr * assn ) { 389 auto i = assns.find( assn ); 390 if ( i != assns.end() ) { 391 i->second.isUsed = true; 392 } 393 } 394 395 /// mark all assertions in `type` used in both `assn1` and `assn2` 396 static void markAssertions( 397 ast::AssertionSet & assn1, ast::AssertionSet & assn2, 398 const ast::FunctionType * type 399 ) { 400 for ( auto & assert : type->assertions ) { 401 markAssertionSet( assn1, assert ); 402 markAssertionSet( assn2, assert ); 403 } 404 } 405 406 bool tryToUnifyWithEnumValue( const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env, 407 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 408 WidenMode widen) { 409 if ( auto attrType2 = dynamic_cast<const ast::EnumAttrType *>(type2)) { 410 if (attrType2->attr == ast::EnumAttribute::Value) { 411 return unifyExact( type1, attrType2->instance->base->base, env, need, have, open, 412 widen); 413 } else if (attrType2->attr == ast::EnumAttribute::Posn) { 414 return unifyExact( type1, attrType2->instance, env, need, have, open, widen ); 392 415 } 393 416 } 394 395 /// mark all assertions in `type` used in both `assn1` and `assn2` 396 static void markAssertions( 397 ast::AssertionSet & assn1, ast::AssertionSet & assn2, 398 const ast::FunctionType * type 399 ) { 400 for ( auto & assert : type->assertions ) { 401 markAssertionSet( assn1, assert ); 402 markAssertionSet( assn2, assert ); 417 return false; 418 } 419 420 public: 421 void postvisit( const ast::FunctionType * func ) { 422 auto func2 = dynamic_cast< const ast::FunctionType * >( type2 ); 423 if ( !func2 ) return; 424 425 if ( func->isVarArgs != func2->isVarArgs ) return; 426 427 // Flatten the parameter lists for both functions so that tuple structure does not 428 // affect unification. Does not actually mutate function parameters. 429 auto params = flattenList( func->params, tenv ); 430 auto params2 = flattenList( func2->params, tenv ); 431 432 // sizes don't have to match if ttypes are involved; need to be more precise w.r.t. 433 // where the ttype is to prevent errors 434 if ( 435 ( params.size() != params2.size() || func->returns.size() != func2->returns.size() ) 436 && !func->isTtype() 437 && !func2->isTtype() 438 ) return; 439 440 if ( !unifyTypeList( params, params2, tenv, need, have, open ) ) return; 441 if ( !unifyTypeList( 442 func->returns, func2->returns, tenv, need, have, open ) ) return; 443 444 markAssertions( have, need, func ); 445 markAssertions( have, need, func2 ); 446 447 result = true; 448 } 449 450 private: 451 // Returns: other, cast as XInstType 452 // Assigns this->result: whether types are compatible (up to generic parameters) 453 template< typename XInstType > 454 const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) { 455 // check that the other type is compatible and named the same 456 auto otherInst = dynamic_cast< const XInstType * >( other ); 457 if ( otherInst && inst->name == otherInst->name ) { 458 this->result = otherInst; 459 } 460 return otherInst; 461 } 462 463 /// Creates a tuple type based on a list of TypeExpr 464 template< typename Iter > 465 static const ast::Type * tupleFromExprs( 466 const ast::TypeExpr * param, Iter & crnt, Iter end, ast::CV::Qualifiers qs 467 ) { 468 std::vector< ast::ptr< ast::Type > > types; 469 do { 470 types.emplace_back( param->type ); 471 472 ++crnt; 473 if ( crnt == end ) break; 474 param = strict_dynamic_cast< const ast::TypeExpr * >( crnt->get() ); 475 } while(true); 476 477 return new ast::TupleType( std::move(types), qs ); 478 } 479 480 template< typename XInstType > 481 void handleGenericRefType( const XInstType * inst, const ast::Type * other ) { 482 // check that other type is compatible and named the same 483 const XInstType * otherInst = handleRefType( inst, other ); 484 if ( !this->result ) return; 485 486 // check that parameters of types unify, if any 487 const std::vector< ast::ptr< ast::Expr > > & params = inst->params; 488 const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params; 489 490 auto it = params.begin(); 491 auto jt = params2.begin(); 492 for ( ; it != params.end() && jt != params2.end(); ++it, ++jt ) { 493 auto param = strict_dynamic_cast< const ast::TypeExpr * >( it->get() ); 494 auto param2 = strict_dynamic_cast< const ast::TypeExpr * >( jt->get() ); 495 496 ast::ptr< ast::Type > pty = param->type; 497 ast::ptr< ast::Type > pty2 = param2->type; 498 499 bool isTuple = Tuples::isTtype( pty ); 500 bool isTuple2 = Tuples::isTtype( pty2 ); 501 502 if ( isTuple && isTuple2 ) { 503 ++it; ++jt; // skip ttype parameters before break 504 } else if ( isTuple ) { 505 // bundle remaining params into tuple 506 pty2 = tupleFromExprs( param2, jt, params2.end(), pty->qualifiers ); 507 ++it; // skip ttype parameter for break 508 } else if ( isTuple2 ) { 509 // bundle remaining params into tuple 510 pty = tupleFromExprs( param, it, params.end(), pty2->qualifiers ); 511 ++jt; // skip ttype parameter for break 403 512 } 404 } 405 406 bool tryToUnifyWithEnumValue( const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env, 407 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 408 WidenMode widen) { 409 if ( auto attrType2 = dynamic_cast<const ast::EnumAttrType *>(type2)) { 410 if (attrType2->attr == ast::EnumAttribute::Value) { 411 return unifyExact( type1, attrType2->instance->base->base, env, need, have, open, 412 widen); 413 } else if (attrType2->attr == ast::EnumAttribute::Posn) { 414 return unifyExact( type1, attrType2->instance, env, need, have, open, widen ); 415 } 513 514 if ( !unifyExact( 515 pty, pty2, tenv, need, have, open, noWiden() ) ) { 516 result = false; 517 return; 416 518 } 417 return false; 418 } 419 420 public: 421 void postvisit( const ast::FunctionType * func ) { 422 auto func2 = dynamic_cast< const ast::FunctionType * >( type2 ); 423 if ( ! func2 ) return; 424 425 if ( func->isVarArgs != func2->isVarArgs ) return; 426 427 // Flatten the parameter lists for both functions so that tuple structure does not 428 // affect unification. Does not actually mutate function parameters. 429 auto params = flattenList( func->params, tenv ); 430 auto params2 = flattenList( func2->params, tenv ); 431 432 // sizes don't have to match if ttypes are involved; need to be more precise w.r.t. 433 // where the ttype is to prevent errors 434 if ( 435 ( params.size() != params2.size() || func->returns.size() != func2->returns.size() ) 436 && ! func->isTtype() 437 && ! func2->isTtype() 438 ) return; 439 440 if ( ! unifyTypeList( params, params2, tenv, need, have, open ) ) return; 441 if ( ! unifyTypeList( 442 func->returns, func2->returns, tenv, need, have, open ) ) return; 443 444 markAssertions( have, need, func ); 445 markAssertions( have, need, func2 ); 446 447 result = true; 448 } 449 450 private: 451 // Returns: other, cast as XInstType 452 // Assigns this->result: whether types are compatible (up to generic parameters) 453 template< typename XInstType > 454 const XInstType * handleRefType( const XInstType * inst, const ast::Type * other ) { 455 // check that the other type is compatible and named the same 456 auto otherInst = dynamic_cast< const XInstType * >( other ); 457 if (otherInst && inst->name == otherInst->name) 458 this->result = otherInst; 459 return otherInst; 460 } 461 462 /// Creates a tuple type based on a list of TypeExpr 463 template< typename Iter > 464 static const ast::Type * tupleFromExprs( 465 const ast::TypeExpr * param, Iter & crnt, Iter end, ast::CV::Qualifiers qs 466 ) { 467 std::vector< ast::ptr< ast::Type > > types; 468 do { 469 types.emplace_back( param->type ); 470 471 ++crnt; 472 if ( crnt == end ) break; 473 param = strict_dynamic_cast< const ast::TypeExpr * >( crnt->get() ); 474 } while(true); 475 476 return new ast::TupleType{ std::move(types), qs }; 477 } 478 479 template< typename XInstType > 480 void handleGenericRefType( const XInstType * inst, const ast::Type * other ) { 481 // check that other type is compatible and named the same 482 const XInstType * otherInst = handleRefType( inst, other ); 483 if ( ! this->result ) return; 484 485 // check that parameters of types unify, if any 486 const std::vector< ast::ptr< ast::Expr > > & params = inst->params; 487 const std::vector< ast::ptr< ast::Expr > > & params2 = otherInst->params; 488 489 auto it = params.begin(); 490 auto jt = params2.begin(); 491 for ( ; it != params.end() && jt != params2.end(); ++it, ++jt ) { 492 auto param = strict_dynamic_cast< const ast::TypeExpr * >( it->get() ); 493 auto param2 = strict_dynamic_cast< const ast::TypeExpr * >( jt->get() ); 494 495 ast::ptr< ast::Type > pty = param->type; 496 ast::ptr< ast::Type > pty2 = param2->type; 497 498 bool isTuple = Tuples::isTtype( pty ); 499 bool isTuple2 = Tuples::isTtype( pty2 ); 500 501 if ( isTuple && isTuple2 ) { 502 ++it; ++jt; // skip ttype parameters before break 503 } else if ( isTuple ) { 504 // bundle remaining params into tuple 505 pty2 = tupleFromExprs( param2, jt, params2.end(), pty->qualifiers ); 506 ++it; // skip ttype parameter for break 507 } else if ( isTuple2 ) { 508 // bundle remaining params into tuple 509 pty = tupleFromExprs( param, it, params.end(), pty2->qualifiers ); 510 ++jt; // skip ttype parameter for break 511 } 512 513 if ( ! unifyExact( 514 pty, pty2, tenv, need, have, open, noWiden() ) ) { 515 result = false; 516 return; 517 } 518 519 // ttype parameter should be last 520 if ( isTuple || isTuple2 ) break; 519 520 // ttype parameter should be last 521 if ( isTuple || isTuple2 ) break; 522 } 523 result = it == params.end() && jt == params2.end(); 524 } 525 526 public: 527 void postvisit( const ast::StructInstType * aggrType ) { 528 handleGenericRefType( aggrType, type2 ); 529 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 530 } 531 532 void postvisit( const ast::UnionInstType * aggrType ) { 533 handleGenericRefType( aggrType, type2 ); 534 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 535 } 536 537 void postvisit( const ast::EnumInstType * aggrType ) { 538 handleRefType( aggrType, type2 ); 539 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 540 } 541 542 void postvisit( const ast::EnumAttrType * enumAttr ) { 543 // Lazy approach for now 544 if ( auto otherPos = dynamic_cast< const ast::EnumAttrType *>( type2 ) ) { 545 if ( enumAttr->match(otherPos) ) { 546 result = otherPos; 521 547 } 522 result = it == params.end() && jt == params2.end(); 523 } 524 525 public: 526 void postvisit( const ast::StructInstType * aggrType ) { 527 handleGenericRefType( aggrType, type2 ); 528 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 529 } 530 531 void postvisit( const ast::UnionInstType * aggrType ) { 532 handleGenericRefType( aggrType, type2 ); 533 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 534 } 535 536 void postvisit( const ast::EnumInstType * aggrType ) { 537 handleRefType( aggrType, type2 ); 538 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 539 } 540 541 void postvisit( const ast::EnumAttrType * enumAttr ) { 542 // Lazy approach for now 543 if ( auto otherPos = dynamic_cast< const ast::EnumAttrType *>(type2) ) { 544 if ( enumAttr->match(otherPos) ) { 545 result = otherPos; 546 } 547 } 548 } 549 550 void postvisit( const ast::TraitInstType * aggrType ) { 551 handleRefType( aggrType, type2 ); 552 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 553 } 554 555 void postvisit( const ast::TypeInstType * typeInst ) { 556 // assert( open.find( *typeInst ) == open.end() ); 557 auto otherInst = dynamic_cast< const ast::TypeInstType * >( type2 ); 558 if ( otherInst && typeInst->name == otherInst->name ) { 559 this->result = otherInst; 548 } 549 } 550 551 void postvisit( const ast::TraitInstType * aggrType ) { 552 handleRefType( aggrType, type2 ); 553 result = result || tryToUnifyWithEnumValue(aggrType, type2, tenv, need, have, open, noWiden()); 554 } 555 556 void postvisit( const ast::TypeInstType * typeInst ) { 557 // assert( open.find( *typeInst ) == open.end() ); 558 auto otherInst = dynamic_cast< const ast::TypeInstType * >( type2 ); 559 if ( otherInst && typeInst->name == otherInst->name ) { 560 this->result = otherInst; 561 } 562 result = result || tryToUnifyWithEnumValue(typeInst, type2, tenv, need, have, open, noWiden()); 563 } 564 565 private: 566 /// Creates a tuple type based on a list of Type 567 static bool unifyList( 568 const std::vector< ast::ptr< ast::Type > > & list1, 569 const std::vector< ast::ptr< ast::Type > > & list2, ast::TypeEnvironment & env, 570 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open 571 ) { 572 auto crnt1 = list1.begin(); 573 auto crnt2 = list2.begin(); 574 while ( crnt1 != list1.end() && crnt2 != list2.end() ) { 575 const ast::Type * t1 = *crnt1; 576 const ast::Type * t2 = *crnt2; 577 bool isTuple1 = Tuples::isTtype( t1 ); 578 bool isTuple2 = Tuples::isTtype( t2 ); 579 580 // assumes ttype must be last parameter 581 if ( isTuple1 && !isTuple2 ) { 582 // combine entirety of list2, then unify 583 return unifyExact( 584 t1, tupleFromTypes( list2 ), env, need, have, open, 585 noWiden() ); 586 } else if ( !isTuple1 && isTuple2 ) { 587 // combine entirety of list1, then unify 588 return unifyExact( 589 tupleFromTypes( list1 ), t2, env, need, have, open, 590 noWiden() ); 560 591 } 561 result = result || tryToUnifyWithEnumValue(typeInst, type2, tenv, need, have, open, noWiden()); 562 } 563 564 private: 565 /// Creates a tuple type based on a list of Type 566 567 static bool unifyList( 568 const std::vector< ast::ptr< ast::Type > > & list1, 569 const std::vector< ast::ptr< ast::Type > > & list2, ast::TypeEnvironment & env, 570 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open 571 ) { 572 auto crnt1 = list1.begin(); 573 auto crnt2 = list2.begin(); 574 while ( crnt1 != list1.end() && crnt2 != list2.end() ) { 575 const ast::Type * t1 = *crnt1; 576 const ast::Type * t2 = *crnt2; 577 bool isTuple1 = Tuples::isTtype( t1 ); 578 bool isTuple2 = Tuples::isTtype( t2 ); 579 580 // assumes ttype must be last parameter 581 if ( isTuple1 && ! isTuple2 ) { 582 // combine entirety of list2, then unify 583 return unifyExact( 584 t1, tupleFromTypes( list2 ), env, need, have, open, 585 noWiden() ); 586 } else if ( ! isTuple1 && isTuple2 ) { 587 // combine entirety of list1, then unify 588 return unifyExact( 589 tupleFromTypes( list1 ), t2, env, need, have, open, 590 noWiden() ); 591 } 592 593 if ( ! unifyExact( 594 t1, t2, env, need, have, open, noWiden() ) 595 ) return false; 596 597 ++crnt1; ++crnt2; 598 } 599 600 if ( crnt1 != list1.end() ) { 601 // try unifying empty tuple type with ttype 602 const ast::Type * t1 = *crnt1; 603 if ( ! Tuples::isTtype( t1 ) ) return false; 604 // xxx - this doesn't generate an empty tuple, contrary to comment; both ported 605 // from Rob's code 606 return unifyExact( 607 t1, tupleFromTypes( list2 ), env, need, have, open, 608 noWiden() ); 609 } else if ( crnt2 != list2.end() ) { 610 // try unifying empty tuple with ttype 611 const ast::Type * t2 = *crnt2; 612 if ( ! Tuples::isTtype( t2 ) ) return false; 613 // xxx - this doesn't generate an empty tuple, contrary to comment; both ported 614 // from Rob's code 615 return unifyExact( 616 tupleFromTypes( list1 ), t2, env, need, have, open, 617 noWiden() ); 618 } 619 620 return true; 621 } 622 623 public: 624 void postvisit( const ast::TupleType * tuple ) { 625 auto tuple2 = dynamic_cast< const ast::TupleType * >( type2 ); 626 if ( ! tuple2 ) return; 627 628 ast::Pass<TtypeExpander> expander{ tenv }; 629 630 const ast::Type * flat = tuple->accept( expander ); 631 const ast::Type * flat2 = tuple2->accept( expander ); 632 633 auto types = flatten( flat ); 634 auto types2 = flatten( flat2 ); 635 636 result = unifyList( types, types2, tenv, need, have, open ) 637 || tryToUnifyWithEnumValue(tuple, type2, tenv, need, have, open, noWiden()); 638 } 639 640 void postvisit( const ast::VarArgsType * vat) { 641 result = dynamic_cast< const ast::VarArgsType * >( type2 ) 642 || tryToUnifyWithEnumValue(vat, type2, tenv, need, have, open, noWiden()); 643 } 644 645 void postvisit( const ast::ZeroType * zt) { 646 result = dynamic_cast< const ast::ZeroType * >( type2 ) 647 || tryToUnifyWithEnumValue(zt, type2, tenv, need, have, open, noWiden()); 648 } 649 650 void postvisit( const ast::OneType * ot) { 651 result = dynamic_cast< const ast::OneType * >( type2 ) 652 || tryToUnifyWithEnumValue(ot, type2, tenv, need, have, open, noWiden()); 653 } 654 }; 655 656 // size_t Unify::traceId = Stats::Heap::new_stacktrace_id("Unify"); 657 658 bool unify( 659 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 660 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 661 ast::OpenVarSet & open 662 ) { 663 ast::ptr<ast::Type> common; 664 return unify( type1, type2, env, need, have, open, common ); 665 } 666 667 bool unify( 668 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 669 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 670 ast::OpenVarSet & open, ast::ptr<ast::Type> & common 671 ) { 672 ast::OpenVarSet closed; 673 // findOpenVars( type1, open, closed, need, have, FirstClosed ); 674 findOpenVars( type2, open, closed, need, have, env, FirstOpen ); 675 return unifyInexact( 676 type1, type2, env, need, have, open, WidenMode{ true, true }, common ); 677 } 678 679 bool unifyExact( 680 const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env, 681 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 682 WidenMode widen 683 ) { 684 if ( type1->qualifiers != type2->qualifiers ) return false; 685 686 auto var1 = dynamic_cast< const ast::TypeInstType * >( type1 ); 687 auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 ); 688 bool isopen1 = var1 && env.lookup(*var1); 689 bool isopen2 = var2 && env.lookup(*var2); 690 691 if ( isopen1 && isopen2 ) { 692 if ( var1->base->kind != var2->base->kind ) return false; 693 return env.bindVarToVar( 694 var1, var2, ast::TypeData{ var1->base->kind, var1->base->sized||var2->base->sized }, need, have, 695 open, widen ); 696 } else if ( isopen1 ) { 697 return env.bindVar( var1, type2, ast::TypeData{var1->base}, need, have, open, widen ); 698 } else if ( isopen2 ) { 699 return env.bindVar( var2, type1, ast::TypeData{var2->base}, need, have, open, widen ); 700 } else { 701 return ast::Pass<Unify>::read( 702 type1, type2, env, need, have, open, widen ); 703 } 704 } 705 706 bool unifyInexact( 707 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 708 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 709 const ast::OpenVarSet & open, WidenMode widen, 710 ast::ptr<ast::Type> & common 711 ) { 712 ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers; 713 714 // force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and 715 // type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1 716 ast::Type * t1 = shallowCopy(type1.get()); 717 ast::Type * t2 = shallowCopy(type2.get()); 718 t1->qualifiers = {}; 719 t2->qualifiers = {}; 720 ast::ptr< ast::Type > t1_(t1); 721 ast::ptr< ast::Type > t2_(t2); 722 723 if ( unifyExact( t1, t2, env, need, have, open, widen ) ) { 724 // if exact unification on unqualified types, try to merge qualifiers 725 if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) { 726 t1->qualifiers = q1 | q2; 727 common = t1; 728 return true; 729 } else { 730 return false; 731 } 732 733 } else if (( common = commonType( t1, t2, env, need, have, open, widen ))) { 734 // no exact unification, but common type 735 auto c = shallowCopy(common.get()); 736 c->qualifiers = q1 | q2; 737 common = c; 592 593 if ( !unifyExact( 594 t1, t2, env, need, have, open, noWiden() ) 595 ) return false; 596 597 ++crnt1; ++crnt2; 598 } 599 600 if ( crnt1 != list1.end() ) { 601 // try unifying empty tuple type with ttype 602 const ast::Type * t1 = *crnt1; 603 if ( !Tuples::isTtype( t1 ) ) return false; 604 // xxx - this doesn't generate an empty tuple, contrary to comment; both ported 605 // from Rob's code 606 return unifyExact( 607 t1, tupleFromTypes( list2 ), env, need, have, open, 608 noWiden() ); 609 } else if ( crnt2 != list2.end() ) { 610 // try unifying empty tuple with ttype 611 const ast::Type * t2 = *crnt2; 612 if ( !Tuples::isTtype( t2 ) ) return false; 613 // xxx - this doesn't generate an empty tuple, contrary to comment; both ported 614 // from Rob's code 615 return unifyExact( 616 tupleFromTypes( list1 ), t2, env, need, have, open, 617 noWiden() ); 618 } 619 620 return true; 621 } 622 623 public: 624 void postvisit( const ast::TupleType * tuple ) { 625 auto tuple2 = dynamic_cast< const ast::TupleType * >( type2 ); 626 if ( ! tuple2 ) return; 627 628 ast::Pass<TtypeExpander> expander{ tenv }; 629 630 const ast::Type * flat = tuple->accept( expander ); 631 const ast::Type * flat2 = tuple2->accept( expander ); 632 633 auto types = flatten( flat ); 634 auto types2 = flatten( flat2 ); 635 636 result = unifyList( types, types2, tenv, need, have, open ) 637 || tryToUnifyWithEnumValue(tuple, type2, tenv, need, have, open, noWiden()); 638 } 639 640 void postvisit( const ast::VarArgsType * vat) { 641 result = dynamic_cast< const ast::VarArgsType * >( type2 ) 642 || tryToUnifyWithEnumValue(vat, type2, tenv, need, have, open, noWiden()); 643 } 644 645 void postvisit( const ast::ZeroType * zt) { 646 result = dynamic_cast< const ast::ZeroType * >( type2 ) 647 || tryToUnifyWithEnumValue(zt, type2, tenv, need, have, open, noWiden()); 648 } 649 650 void postvisit( const ast::OneType * ot) { 651 result = dynamic_cast< const ast::OneType * >( type2 ) 652 || tryToUnifyWithEnumValue(ot, type2, tenv, need, have, open, noWiden()); 653 } 654 }; 655 656 // size_t Unify::traceId = Stats::Heap::new_stacktrace_id("Unify"); 657 658 bool unify( 659 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 660 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 661 ast::OpenVarSet & open 662 ) { 663 ast::ptr<ast::Type> common; 664 return unify( type1, type2, env, need, have, open, common ); 665 } 666 667 bool unify( 668 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 669 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 670 ast::OpenVarSet & open, ast::ptr<ast::Type> & common 671 ) { 672 ast::OpenVarSet closed; 673 // findOpenVars( type1, open, closed, need, have, FirstClosed ); 674 findOpenVars( type2, open, closed, need, have, env, FirstOpen ); 675 return unifyInexact( 676 type1, type2, env, need, have, open, WidenMode{ true, true }, common ); 677 } 678 679 bool unifyExact( 680 const ast::Type * type1, const ast::Type * type2, ast::TypeEnvironment & env, 681 ast::AssertionSet & need, ast::AssertionSet & have, const ast::OpenVarSet & open, 682 WidenMode widen 683 ) { 684 if ( type1->qualifiers != type2->qualifiers ) return false; 685 686 auto var1 = dynamic_cast< const ast::TypeInstType * >( type1 ); 687 auto var2 = dynamic_cast< const ast::TypeInstType * >( type2 ); 688 bool isopen1 = var1 && env.lookup(*var1); 689 bool isopen2 = var2 && env.lookup(*var2); 690 691 if ( isopen1 && isopen2 ) { 692 if ( var1->base->kind != var2->base->kind ) return false; 693 return env.bindVarToVar( 694 var1, var2, ast::TypeData{ var1->base->kind, var1->base->sized||var2->base->sized }, need, have, 695 open, widen ); 696 } else if ( isopen1 ) { 697 return env.bindVar( var1, type2, ast::TypeData{var1->base}, need, have, open, widen ); 698 } else if ( isopen2 ) { 699 return env.bindVar( var2, type1, ast::TypeData{var2->base}, need, have, open, widen ); 700 } else { 701 return ast::Pass<Unify>::read( 702 type1, type2, env, need, have, open, widen ); 703 } 704 } 705 706 bool unifyInexact( 707 const ast::ptr<ast::Type> & type1, const ast::ptr<ast::Type> & type2, 708 ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have, 709 const ast::OpenVarSet & open, WidenMode widen, 710 ast::ptr<ast::Type> & common 711 ) { 712 ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers; 713 714 // force t1 and t2 to be cloned if their qualifiers must be stripped, so that type1 and 715 // type2 are left unchanged; calling convention forces type{1,2}->strong_ref >= 1 716 ast::Type * t1 = shallowCopy(type1.get()); 717 ast::Type * t2 = shallowCopy(type2.get()); 718 t1->qualifiers = {}; 719 t2->qualifiers = {}; 720 ast::ptr< ast::Type > t1_(t1); 721 ast::ptr< ast::Type > t2_(t2); 722 723 if ( unifyExact( t1, t2, env, need, have, open, widen ) ) { 724 // if exact unification on unqualified types, try to merge qualifiers 725 if ( q1 == q2 || ( ( q1 > q2 || widen.first ) && ( q2 > q1 || widen.second ) ) ) { 726 t1->qualifiers = q1 | q2; 727 common = t1; 738 728 return true; 739 729 } else { 740 730 return false; 741 731 } 742 } 743 744 ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) { 745 if ( func->returns.empty() ) return new ast::VoidType{}; 746 if ( func->returns.size() == 1 ) return func->returns[0]; 747 748 std::vector<ast::ptr<ast::Type>> tys; 749 for ( const auto & decl : func->returns ) { 750 tys.emplace_back( decl ); 751 } 752 return new ast::TupleType{ std::move(tys) }; 753 } 732 } else if (( common = commonType( t1, t2, env, need, have, open, widen ))) { 733 // no exact unification, but common type 734 auto c = shallowCopy(common.get()); 735 c->qualifiers = q1 | q2; 736 common = c; 737 return true; 738 } else { 739 return false; 740 } 741 } 742 743 ast::ptr<ast::Type> extractResultType( const ast::FunctionType * func ) { 744 if ( func->returns.empty() ) return new ast::VoidType(); 745 if ( func->returns.size() == 1 ) return func->returns[0]; 746 747 std::vector<ast::ptr<ast::Type>> tys; 748 for ( const auto & decl : func->returns ) { 749 tys.emplace_back( decl ); 750 } 751 return new ast::TupleType( std::move(tys) ); 752 } 753 754 754 } // namespace ResolvExpr 755 755 -
src/ResolvExpr/typeops.h
rcf191ac r7042c60 21 21 22 22 namespace ResolvExpr { 23 class TypeEnvironment;24 23 25 // combos: takes a list of sets and returns a set of lists representing every possible way of forming a list by 26 // picking one element out of each set 27 template< typename InputIterator, typename OutputIterator > 28 void combos( InputIterator begin, InputIterator end, OutputIterator out ) { 29 typedef typename InputIterator::value_type SetType; 30 typedef typename std::vector< typename SetType::value_type > ListType; 24 class TypeEnvironment; 31 25 32 if ( begin == end ) { 33 *out++ = ListType(); 34 return; 35 } // if 26 // combos: takes a list of sets and returns a set of lists representing every possible way of forming a list by 27 // picking one element out of each set 28 template< typename InputIterator, typename OutputIterator > 29 void combos( InputIterator begin, InputIterator end, OutputIterator out ) { 30 typedef typename InputIterator::value_type SetType; 31 typedef typename std::vector< typename SetType::value_type > ListType; 36 32 37 InputIterator current = begin; 38 begin++; 33 if ( begin == end ) { 34 *out++ = ListType(); 35 return; 36 } // if 39 37 40 std::vector< ListType > recursiveResult;41 combos( begin, end, back_inserter( recursiveResult ) );38 InputIterator current = begin; 39 begin++; 42 40 43 for ( const auto& i : recursiveResult ) for ( const auto& j : *current ) { 44 ListType result; 45 std::back_insert_iterator< ListType > inserter = back_inserter( result ); 46 *inserter++ = j; 47 std::copy( i.begin(), i.end(), inserter ); 48 *out++ = result; 41 std::vector< ListType > recursiveResult; 42 combos( begin, end, back_inserter( recursiveResult ) ); 43 44 for ( const auto& i : recursiveResult ) for ( const auto& j : *current ) { 45 ListType result; 46 std::back_insert_iterator< ListType > inserter = back_inserter( result ); 47 *inserter++ = j; 48 std::copy( i.begin(), i.end(), inserter ); 49 *out++ = result; 50 } 51 } 52 53 /// Flatten tuple type into existing list of types. 54 inline void flatten( 55 const ast::Type * type, std::vector< ast::ptr< ast::Type > > & out 56 ) { 57 if ( auto tupleType = dynamic_cast< const ast::TupleType * >( type ) ) { 58 for ( const ast::Type * t : tupleType->types ) { 59 flatten( t, out ); 49 60 } 61 } else { 62 out.emplace_back( type ); 63 } 64 } 65 66 /// Flatten tuple type into list of types. 67 inline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) { 68 std::vector< ast::ptr< ast::Type > > out; 69 out.reserve( type->size() ); 70 flatten( type, out ); 71 return out; 72 } 73 74 template< typename Iter > 75 const ast::Type * tupleFromTypes( Iter crnt, Iter end ) { 76 std::vector< ast::ptr< ast::Type > > types; 77 while ( crnt != end ) { 78 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure 79 // that this results in a flat tuple 80 flatten( *crnt, types ); 81 82 ++crnt; 50 83 } 51 84 52 /// flatten tuple type into existing list of types 53 inline void flatten( 54 const ast::Type * type, std::vector< ast::ptr< ast::Type > > & out 55 ) { 56 if ( auto tupleType = dynamic_cast< const ast::TupleType * >( type ) ) { 57 for ( const ast::Type * t : tupleType->types ) { 58 flatten( t, out ); 59 } 60 } else { 61 out.emplace_back( type ); 62 } 63 } 85 return new ast::TupleType( std::move(types) ); 86 } 64 87 65 /// flatten tuple type into list of types 66 inline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) { 67 std::vector< ast::ptr< ast::Type > > out; 68 out.reserve( type->size() ); 69 flatten( type, out ); 70 return out; 71 } 88 inline const ast::Type * tupleFromTypes( 89 const std::vector< ast::ptr< ast::Type > > & tys 90 ) { 91 return tupleFromTypes( tys.begin(), tys.end() ); 92 } 72 93 73 template< typename Iter >74 const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {75 std::vector< ast::ptr< ast::Type > > types;76 while ( crnt != end ) {77 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure78 // that this results in a flat tuple79 flatten( *crnt, types );80 81 ++crnt;82 }83 84 return new ast::TupleType{ std::move(types) };85 }86 87 inline const ast::Type * tupleFromTypes(88 const std::vector< ast::ptr< ast::Type > > & tys89 ) {90 return tupleFromTypes( tys.begin(), tys.end() );91 }92 94 } // namespace ResolvExpr 93 95 -
src/SymTab/Mangler.cc
rcf191ac r7042c60 283 283 postvisit( enumAttr->instance ); 284 284 // mangleName += "_pos"; 285 286 287 288 289 290 285 switch ( enumAttr->attr ) 286 { 287 case ast::EnumAttribute::Label: 288 mangleName += "_label_"; 289 break; 290 case ast::EnumAttribute::Posn: 291 291 mangleName += "_posn_"; 292 293 294 295 296 292 break; 293 case ast::EnumAttribute::Value: 294 mangleName += "_value_"; 295 break; 296 } 297 297 298 298 } -
src/Validate/ForallPointerDecay.hpp
rcf191ac r7042c60 39 39 /// Expand all traits in an assertion list. 40 40 std::vector<ast::ptr<ast::DeclWithType>> expandAssertions( 41 41 std::vector<ast::ptr<ast::DeclWithType>> const & ); 42 42 43 43 } -
src/Validate/HoistStruct.cpp
rcf191ac r7042c60 149 149 template<typename InstType> 150 150 InstType const * HoistStructCore::preCollectionInstType( InstType const * type ) { 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 151 if ( !type->base->parent ) return type; 152 if ( type->base->params.empty() ) return type; 153 154 InstType * mut = ast::mutate( type ); 155 ast::AggregateDecl const * parent = 156 commonParent( this->parent, mut->base->parent ); 157 assert( parent ); 158 159 std::vector<ast::ptr<ast::Expr>> args; 160 for ( const ast::ptr<ast::TypeDecl> & param : parent->params ) { 161 args.emplace_back( new ast::TypeExpr( param->location, 162 new ast::TypeInstType( param ) 163 ) ); 164 } 165 spliceBegin( mut->params, args ); 166 return mut; 167 167 } 168 168 -
src/Validate/ImplementEnumFunc.cpp
rcf191ac r7042c60 8 8 namespace { 9 9 class EnumAttrFuncGenerator { 10 const ast::EnumDecl* decl; 11 const ast::EnumInstType* instType; 12 // const ast::EnumAttrType* attrType; 13 unsigned int functionNesting; 14 ast::Linkage::Spec proto_linkage; 15 16 public: 17 std::list<ast::ptr<ast::Decl>> forwards; 18 std::list<ast::ptr<ast::Decl>> definitions; 19 20 void generateAndAppendFunctions(std::list<ast::ptr<ast::Decl>>&); 21 22 EnumAttrFuncGenerator(const ast::EnumDecl* decl, 23 const ast::EnumInstType* instType, 24 // const ast::EnumAttrType* enumAttrType, 25 unsigned int functionNesting) 26 : decl(decl), 27 instType{instType}, 28 // attrType{enumAttrType}, 29 functionNesting{functionNesting}, 30 proto_linkage{ast::Linkage::Cforall} {} 31 32 void genAttrFunctions(); 33 void genSuccPredPosn(); 34 void genSuccPredDecl(); 35 36 void appendReturnThis(ast::FunctionDecl* decl) { 37 assert(1 <= decl->params.size()); 38 assert(1 == decl->returns.size()); 39 assert(decl->stmts); 40 41 const CodeLocation& location = (decl->stmts->kids.empty()) 42 ? decl->stmts->location 43 : decl->stmts->kids.back()->location; 44 const ast::DeclWithType* thisParam = decl->params.front(); 45 decl->stmts.get_and_mutate()->push_back(new ast::ReturnStmt( 46 location, new ast::VariableExpr(location, thisParam))); 47 } 48 void genAttrStandardFuncs() { 49 ast::FunctionDecl* (EnumAttrFuncGenerator::*standardProtos[4])() 50 const = {&EnumAttrFuncGenerator::genCtorProto, 51 &EnumAttrFuncGenerator::genCopyProto, 52 &EnumAttrFuncGenerator::genDtorProto, 53 &EnumAttrFuncGenerator::genAssignProto}; 54 for (auto& generator : standardProtos) { 55 ast::FunctionDecl* decl = (this->*generator)(); 56 produceForwardDecl(decl); 57 genFuncBody(decl); 58 if (CodeGen::isAssignment(decl->name)) { 59 appendReturnThis(decl); 60 } 61 produceDecl(decl); 62 } 63 } 64 65 private: 66 const CodeLocation& getLocation() const { return decl->location; } 67 68 ast::FunctionDecl* genProto( 69 std::string&& name, std::vector<ast::ptr<ast::DeclWithType>>&& params, 70 std::vector<ast::ptr<ast::DeclWithType>>&& returns) const; 71 72 void produceDecl(const ast::FunctionDecl* decl); 73 void produceForwardDecl(const ast::FunctionDecl* decl); 74 75 const ast::Decl* getDecl() const { return decl; } 76 77 ast::FunctionDecl* genPosnProto() const; 78 ast::FunctionDecl* genLabelProto() const; 79 ast::FunctionDecl* genValueProto() const; 80 ast::FunctionDecl* genSuccProto() const; 81 ast::FunctionDecl* genPredProto() const; 82 83 ast::FunctionDecl* genSuccPosProto() const; 84 ast::FunctionDecl* genPredPosProto() const; 85 86 // --------------------------------------------------- 87 // ast::FunctionDecl* genAttrCtorProto() const; 88 /// Changes the node inside a pointer so that it has the unused attribute. 89 void addUnusedAttribute(ast::ptr<ast::DeclWithType>& declPtr) { 90 ast::DeclWithType* decl = declPtr.get_and_mutate(); 91 decl->attributes.push_back(new ast::Attribute("unused")); 92 } 93 94 ast::ObjectDecl* dstParam() const { 95 return new ast::ObjectDecl(getLocation(), "_dst", 96 new ast::ReferenceType(new ast::EnumAttrType( 97 ast::deepCopy(instType)))); 98 } 99 100 ast::ObjectDecl* srcParam() const { 101 return new ast::ObjectDecl( 102 getLocation(), "_src", 103 new ast::EnumAttrType(ast::deepCopy(instType))); 104 } 105 106 /// E = EnumAttrType<T>` 107 /// `void ?{}(E & _dst)`. 108 ast::FunctionDecl* genCtorProto() const { 109 return genProto("?{}", {dstParam()}, {}); 110 } 111 112 /// void ?{}(E & _dst, E _src)`. 113 ast::FunctionDecl* genCopyProto() const { 114 return genProto("?{}", {dstParam(), srcParam()}, {}); 115 } 116 117 ///`void ^?{}(E & _dst)`. 118 ast::FunctionDecl* genDtorProto() const { 119 // The destructor must be mutex on a concurrent type. 120 return genProto("^?{}", {dstParam()}, {}); 121 } 122 123 /// `E ?{}(E & _dst, E _src)`. 124 ast::FunctionDecl* genAssignProto() const { 125 // Only the name is different, so just reuse the generation function. 126 auto retval = srcParam(); 127 retval->name = "_ret"; 128 return genProto("?=?", {dstParam(), srcParam()}, {retval}); 129 } 130 131 void genFuncBody(ast::FunctionDecl* func) { 132 const CodeLocation& location = func->location; 133 auto& params = func->params; 134 if (InitTweak::isCopyConstructor(func) || 135 InitTweak::isAssignment(func)) { 136 assert(2 == params.size()); 137 auto dstParam = params.front().strict_as<ast::ObjectDecl>(); 138 auto srcParam = params.back().strict_as<ast::ObjectDecl>(); 139 func->stmts = genCopyBody(location, dstParam, srcParam); 140 } else { 141 assert(1 == params.size()); 142 // Default constructor and destructor is empty. 143 func->stmts = new ast::CompoundStmt(location); 144 // Add unused attribute to parameter to silence warnings. 145 addUnusedAttribute(params.front()); 146 147 // Just an extra step to make the forward and declaration match. 148 if (forwards.empty()) return; 149 ast::FunctionDecl* fwd = strict_dynamic_cast<ast::FunctionDecl*>( 150 forwards.back().get_and_mutate()); 151 addUnusedAttribute(fwd->params.front()); 152 } 153 } 154 155 const ast::CompoundStmt* genCopyBody(const CodeLocation& location, 156 const ast::ObjectDecl* dstParam, 157 const ast::ObjectDecl* srcParam) { 158 return new ast::CompoundStmt( 159 location, 160 {new ast::ExprStmt( 161 location, 162 new ast::UntypedExpr( 163 location, new ast::NameExpr(location, "__builtin_memcpy"), 164 { 165 new ast::AddressExpr(location, new ast::VariableExpr( 166 location, dstParam)), 167 new ast::AddressExpr(location, new ast::VariableExpr( 168 location, srcParam)), 169 new ast::SizeofExpr(location, srcParam->type), 170 }))}); 171 } 172 173 void genDtorBody(ast::FunctionDecl* func) { 174 const CodeLocation& location = func->location; 175 auto& params = func->params; 176 assert(1 == params.size()); 177 func->stmts = new ast::CompoundStmt(location); 178 addUnusedAttribute(params.front()); 179 180 // Just an extra step to make the forward and declaration match. 181 if (forwards.empty()) return; 182 ast::FunctionDecl* fwd = strict_dynamic_cast<ast::FunctionDecl*>( 183 forwards.back().get_and_mutate()); 184 addUnusedAttribute(fwd->params.front()); 185 } 186 187 // ast::FunctionDecl* 188 // ---------------------------------------------------- 189 190 ast::FunctionDecl* genSuccPredFunc(bool succ); 191 192 const ast::Init* getAutoInit(const ast::Init* prev) const; 193 194 std::vector<ast::ptr<ast::Init>> genLabelInit() const; 195 196 std::vector<ast::ptr<ast::Init>> genValueInit() const; 197 ast::ObjectDecl* genAttrArrayProto( 198 const ast::EnumAttribute attr, const CodeLocation& location, 199 std::vector<ast::ptr<ast::Init>>& inits) const; 200 void genValueOrLabelBody(ast::FunctionDecl* func, 201 ast::ObjectDecl* arrDecl) const; 202 void genPosnBody(ast::FunctionDecl* func) const; 203 void genAttributesDecls(const ast::EnumAttribute attr); 10 const ast::EnumDecl* decl; 11 const ast::EnumInstType* instType; 12 unsigned int functionNesting; 13 ast::Linkage::Spec proto_linkage; 14 15 public: 16 std::list<ast::ptr<ast::Decl>> forwards; 17 std::list<ast::ptr<ast::Decl>> definitions; 18 19 void generateAndAppendFunctions(std::list<ast::ptr<ast::Decl>>&); 20 21 EnumAttrFuncGenerator( 22 const ast::EnumDecl* decl, 23 const ast::EnumInstType* instType, 24 unsigned int functionNesting ) 25 : decl(decl), 26 instType{instType}, 27 functionNesting{functionNesting}, 28 proto_linkage{ast::Linkage::Cforall} {} 29 30 void genAttrFunctions(); 31 void genSuccPredPosn(); 32 void genSuccPredDecl(); 33 34 void appendReturnThis(ast::FunctionDecl* decl) { 35 assert(1 <= decl->params.size()); 36 assert(1 == decl->returns.size()); 37 assert(decl->stmts); 38 39 const CodeLocation& location = (decl->stmts->kids.empty()) 40 ? decl->stmts->location 41 : decl->stmts->kids.back()->location; 42 const ast::DeclWithType* thisParam = decl->params.front(); 43 decl->stmts.get_and_mutate()->push_back(new ast::ReturnStmt( 44 location, new ast::VariableExpr(location, thisParam))); 45 } 46 void genAttrStandardFuncs() { 47 ast::FunctionDecl* (EnumAttrFuncGenerator::*standardProtos[4])() 48 const = {&EnumAttrFuncGenerator::genCtorProto, 49 &EnumAttrFuncGenerator::genCopyProto, 50 &EnumAttrFuncGenerator::genDtorProto, 51 &EnumAttrFuncGenerator::genAssignProto}; 52 for (auto& generator : standardProtos) { 53 ast::FunctionDecl* decl = (this->*generator)(); 54 produceForwardDecl(decl); 55 genFuncBody(decl); 56 if (CodeGen::isAssignment(decl->name)) { 57 appendReturnThis(decl); 58 } 59 produceDecl(decl); 60 } 61 } 62 63 private: 64 const CodeLocation& getLocation() const { return decl->location; } 65 66 ast::FunctionDecl* genProto( 67 std::string&& name, std::vector<ast::ptr<ast::DeclWithType>>&& params, 68 std::vector<ast::ptr<ast::DeclWithType>>&& returns) const; 69 70 void produceDecl(const ast::FunctionDecl* decl); 71 void produceForwardDecl(const ast::FunctionDecl* decl); 72 73 const ast::Decl* getDecl() const { return decl; } 74 75 ast::FunctionDecl* genPosnProto() const; 76 ast::FunctionDecl* genLabelProto() const; 77 ast::FunctionDecl* genValueProto() const; 78 ast::FunctionDecl* genSuccProto() const; 79 ast::FunctionDecl* genPredProto() const; 80 81 ast::FunctionDecl* genSuccPosProto() const; 82 ast::FunctionDecl* genPredPosProto() const; 83 84 // --------------------------------------------------- 85 // ast::FunctionDecl* genAttrCtorProto() const; 86 /// Changes the node inside a pointer so that it has the unused attribute. 87 void addUnusedAttribute(ast::ptr<ast::DeclWithType>& declPtr) { 88 ast::DeclWithType* decl = declPtr.get_and_mutate(); 89 decl->attributes.push_back(new ast::Attribute("unused")); 90 } 91 92 ast::ObjectDecl* dstParam() const { 93 return new ast::ObjectDecl(getLocation(), "_dst", 94 new ast::ReferenceType(new ast::EnumAttrType( 95 ast::deepCopy(instType)))); 96 } 97 98 ast::ObjectDecl* srcParam() const { 99 return new ast::ObjectDecl( 100 getLocation(), "_src", 101 new ast::EnumAttrType(ast::deepCopy(instType))); 102 } 103 104 /// E = EnumAttrType<T>` 105 /// `void ?{}(E & _dst)`. 106 ast::FunctionDecl* genCtorProto() const { 107 return genProto("?{}", {dstParam()}, {}); 108 } 109 110 /// void ?{}(E & _dst, E _src)`. 111 ast::FunctionDecl* genCopyProto() const { 112 return genProto("?{}", {dstParam(), srcParam()}, {}); 113 } 114 115 ///`void ^?{}(E & _dst)`. 116 ast::FunctionDecl* genDtorProto() const { 117 // The destructor must be mutex on a concurrent type. 118 return genProto("^?{}", {dstParam()}, {}); 119 } 120 121 /// `E ?{}(E & _dst, E _src)`. 122 ast::FunctionDecl* genAssignProto() const { 123 // Only the name is different, so just reuse the generation function. 124 auto retval = srcParam(); 125 retval->name = "_ret"; 126 return genProto("?=?", {dstParam(), srcParam()}, {retval}); 127 } 128 129 void genFuncBody(ast::FunctionDecl* func) { 130 const CodeLocation& location = func->location; 131 auto& params = func->params; 132 if (InitTweak::isCopyConstructor(func) || 133 InitTweak::isAssignment(func)) { 134 assert(2 == params.size()); 135 auto dstParam = params.front().strict_as<ast::ObjectDecl>(); 136 auto srcParam = params.back().strict_as<ast::ObjectDecl>(); 137 func->stmts = genCopyBody(location, dstParam, srcParam); 138 } else { 139 assert(1 == params.size()); 140 // Default constructor and destructor is empty. 141 func->stmts = new ast::CompoundStmt(location); 142 // Add unused attribute to parameter to silence warnings. 143 addUnusedAttribute(params.front()); 144 145 // Just an extra step to make the forward and declaration match. 146 if (forwards.empty()) return; 147 ast::FunctionDecl* fwd = strict_dynamic_cast<ast::FunctionDecl*>( 148 forwards.back().get_and_mutate()); 149 addUnusedAttribute(fwd->params.front()); 150 } 151 } 152 153 const ast::CompoundStmt* genCopyBody( const CodeLocation& location, 154 const ast::ObjectDecl* dstParam, const ast::ObjectDecl* srcParam) { 155 return new ast::CompoundStmt( 156 location, 157 {new ast::ExprStmt( 158 location, 159 new ast::UntypedExpr( 160 location, new ast::NameExpr(location, "__builtin_memcpy"), 161 { 162 new ast::AddressExpr( location, 163 new ast::VariableExpr( location, dstParam ) ), 164 new ast::AddressExpr( location, 165 new ast::VariableExpr( location, srcParam ) ), 166 new ast::SizeofExpr( location, srcParam->type ), 167 }))}); 168 } 169 170 void genDtorBody(ast::FunctionDecl* func) { 171 const CodeLocation& location = func->location; 172 auto& params = func->params; 173 assert(1 == params.size()); 174 func->stmts = new ast::CompoundStmt(location); 175 addUnusedAttribute(params.front()); 176 177 // Just an extra step to make the forward and declaration match. 178 if (forwards.empty()) return; 179 ast::FunctionDecl* fwd = strict_dynamic_cast<ast::FunctionDecl*>( 180 forwards.back().get_and_mutate()); 181 addUnusedAttribute(fwd->params.front()); 182 } 183 184 // ast::FunctionDecl* 185 // ---------------------------------------------------- 186 187 ast::FunctionDecl* genSuccPredFunc(bool succ); 188 189 const ast::Init* getAutoInit(const ast::Init* prev) const; 190 191 std::vector<ast::ptr<ast::Init>> genLabelInit() const; 192 193 std::vector<ast::ptr<ast::Init>> genValueInit() const; 194 ast::ObjectDecl* genAttrArrayProto( 195 const ast::EnumAttribute attr, const CodeLocation& location, 196 std::vector<ast::ptr<ast::Init>>& inits) const; 197 void genValueOrLabelBody( 198 ast::FunctionDecl* func, ast::ObjectDecl* arrDecl) const; 199 void genPosnBody(ast::FunctionDecl* func) const; 200 void genAttributesDecls(const ast::EnumAttribute attr); 204 201 }; 205 202 206 203 std::vector<ast::ptr<ast::Init>> EnumAttrFuncGenerator::genLabelInit() const { 207 208 209 210 211 212 213 214 215 216 204 std::vector<ast::ptr<ast::Init>> inits; 205 for (size_t i = 0; i < decl->members.size(); i++) { 206 ast::ptr<ast::Decl> mem = decl->members.at(i); 207 auto memAsObjectDecl = mem.as<ast::ObjectDecl>(); 208 assert(memAsObjectDecl); 209 inits.emplace_back(new ast::SingleInit( 210 mem->location, 211 ast::ConstantExpr::from_string(mem->location, mem->name))); 212 } 213 return inits; 217 214 } 218 215 219 216 std::vector<ast::ptr<ast::Init>> EnumAttrFuncGenerator::genValueInit() const { 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 217 std::vector<ast::ptr<ast::Init>> inits; 218 for (size_t i = 0; i < decl->members.size(); i++) { 219 ast::ptr<ast::Decl> mem = decl->members.at(i); 220 auto memAsObjectDecl = mem.as<ast::ObjectDecl>(); 221 assert(memAsObjectDecl); 222 if (memAsObjectDecl->init) { 223 inits.emplace_back(memAsObjectDecl->init); 224 } else { 225 const CodeLocation& location = mem->location; 226 if (i == 0) { 227 inits.emplace_back(new ast::SingleInit( 228 location, ast::ConstantExpr::from_int(mem->location, 0))); 229 } else { 230 inits.emplace_back(getAutoInit(inits.at(i - 1))); 231 } 232 } 233 } 234 return inits; 238 235 } 239 236 const ast::Init* EnumAttrFuncGenerator::getAutoInit( 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 237 const ast::Init* prev) const { 238 if (prev == nullptr) { 239 return new ast::SingleInit( 240 getLocation(), ast::ConstantExpr::from_int(getLocation(), 0)); 241 } 242 auto prevInit = dynamic_cast<const ast::SingleInit*>(prev); 243 assert(prevInit); 244 auto prevInitExpr = prevInit->value; 245 if (auto constInit = prevInitExpr.as<ast::ConstantExpr>()) { 246 // Assume no string literal for now 247 return new ast::SingleInit( 248 getLocation(), ast::ConstantExpr::from_int( 249 getLocation(), constInit->intValue() + 1)); 250 } else { 251 auto untypedThisInit = new ast::UntypedExpr( 252 getLocation(), new ast::NameExpr(getLocation(), "?++"), 253 {prevInitExpr}); 254 return new ast::SingleInit(getLocation(), untypedThisInit); 255 } 259 256 } 260 257 261 258 ast::FunctionDecl* EnumAttrFuncGenerator::genProto( 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 259 std::string&& name, std::vector<ast::ptr<ast::DeclWithType>>&& params, 260 std::vector<ast::ptr<ast::DeclWithType>>&& returns) const { 261 ast::FunctionDecl* decl = new ast::FunctionDecl( 262 // Auto-generated routines use the type declaration's location. 263 getLocation(), std::move(name), {}, {}, std::move(params), 264 std::move(returns), 265 // Only a prototype, no body. 266 nullptr, 267 // Use static storage if we are at the top level. 268 (0 < functionNesting) ? ast::Storage::Classes() : ast::Storage::Static, 269 proto_linkage, std::vector<ast::ptr<ast::Attribute>>(), 270 // Auto-generated routines are inline to avoid conflicts. 271 ast::Function::Specs(ast::Function::Inline)); 272 decl->fixUniqueId(); 273 return decl; 277 274 } 278 275 279 276 void EnumAttrFuncGenerator::produceDecl(const ast::FunctionDecl* decl) { 280 281 282 277 assert(nullptr != decl->stmts); 278 279 definitions.push_back(decl); 283 280 } 284 281 285 282 void EnumAttrFuncGenerator::produceForwardDecl(const ast::FunctionDecl* decl) { 286 287 288 289 290 283 if (0 != functionNesting) return; 284 ast::FunctionDecl* fwd = 285 (decl->stmts) ? ast::asForward(decl) : ast::deepCopy(decl); 286 fwd->fixUniqueId(); 287 forwards.push_back(fwd); 291 288 } 292 289 … … 300 297 301 298 ast::FunctionDecl* EnumAttrFuncGenerator::genLabelProto() const { 302 303 304 305 306 307 299 return genProto( 300 "labelE", 301 {new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl))}, 302 {new ast::ObjectDecl( 303 getLocation(), "_ret", 304 new ast::PointerType(new ast::BasicType{ast::BasicKind::Char}))}); 308 305 } 309 306 310 307 ast::FunctionDecl* EnumAttrFuncGenerator::genValueProto() const { 311 312 313 314 315 308 return genProto( 309 "valueE", 310 {new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl))}, 311 {new ast::ObjectDecl(getLocation(), "_ret", 312 ast::deepCopy(decl->base))}); 316 313 } 317 314 318 315 ast::FunctionDecl* EnumAttrFuncGenerator::genSuccProto() const { 319 320 321 322 323 316 return genProto( 317 "succ", 318 {new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl))}, 319 {new ast::ObjectDecl(getLocation(), "_ret", 320 new ast::EnumInstType(decl))}); 324 321 } 325 322 326 323 ast::FunctionDecl* EnumAttrFuncGenerator::genPredProto() const { 327 328 329 330 331 324 return genProto( 325 "pred", 326 {new ast::ObjectDecl(getLocation(), "_i", new ast::EnumInstType(decl))}, 327 {new ast::ObjectDecl(getLocation(), "_ret", 328 new ast::EnumInstType(decl))}); 332 329 } 333 330 334 331 inline ast::EnumAttrType * getPosnType( const ast::EnumDecl * decl ) { 335 332 return new ast::EnumAttrType(new ast::EnumInstType(decl), ast::EnumAttribute::Posn); 336 333 } 337 334 338 335 ast::FunctionDecl* EnumAttrFuncGenerator::genSuccPosProto() const { 339 340 341 342 343 336 return genProto( 337 "_successor_", 338 {new ast::ObjectDecl(getLocation(), "_i", getPosnType(decl))}, 339 {new ast::ObjectDecl(getLocation(), "_ret", getPosnType(decl))} 340 ); 344 341 } 345 342 346 343 ast::FunctionDecl* EnumAttrFuncGenerator::genPredPosProto() const { 347 348 349 350 351 344 return genProto( 345 "_predessor_", 346 {new ast::ObjectDecl(getLocation(), "_i", getPosnType(decl))}, 347 {new ast::ObjectDecl(getLocation(), "_ret", getPosnType(decl))} 348 ); 352 349 } 353 350 354 351 ast::ObjectDecl* EnumAttrFuncGenerator::genAttrArrayProto( 355 const ast::EnumAttribute attr, const CodeLocation& location, 356 std::vector<ast::ptr<ast::Init>>& inits) const { 357 ast::ArrayType* arrT = new ast::ArrayType( 358 attr == ast::EnumAttribute::Value 359 ? decl->base 360 : new ast::PointerType(new ast::BasicType{ast::BasicKind::Char}), 361 ast::ConstantExpr::from_int(decl->location, decl->members.size()), 362 ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim); 363 364 ast::ObjectDecl* objDecl = 365 new ast::ObjectDecl(decl->location, decl->getUnmangeldArrayName(attr), 366 arrT, new ast::ListInit(location, std::move(inits)), 367 ast::Storage::Static, ast::Linkage::AutoGen); 368 369 return objDecl; 352 const ast::EnumAttribute attr, const CodeLocation& location, 353 std::vector<ast::ptr<ast::Init>>& inits) const { 354 ast::ArrayType* arrT = new ast::ArrayType( 355 attr == ast::EnumAttribute::Value 356 ? decl->base 357 : new ast::PointerType(new ast::BasicType{ast::BasicKind::Char}), 358 ast::ConstantExpr::from_int(decl->location, decl->members.size()), 359 ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim); 360 361 ast::ObjectDecl* objDecl = 362 new ast::ObjectDecl( 363 decl->location, decl->getUnmangeldArrayName( attr ), 364 arrT, new ast::ListInit( location, std::move( inits ) ), 365 ast::Storage::Static, ast::Linkage::AutoGen ); 366 367 return objDecl; 370 368 } 371 369 372 370 void EnumAttrFuncGenerator::genValueOrLabelBody( 373 374 375 376 377 378 379 new ast::VariableExpr(func->location, func->params.front()),380 new ast::EnumAttrType(new ast::EnumInstType(decl),381 382 383 371 ast::FunctionDecl* func, ast::ObjectDecl* arrDecl) const { 372 ast::UntypedExpr* untyped = ast::UntypedExpr::createCall( 373 func->location, "?[?]", 374 {new ast::NameExpr(func->location, arrDecl->name), 375 new ast::CastExpr( 376 func->location, 377 new ast::VariableExpr( func->location, func->params.front() ), 378 new ast::EnumAttrType( new ast::EnumInstType(decl), 379 ast::EnumAttribute::Posn))}); 380 func->stmts = new ast::CompoundStmt( 381 func->location, {new ast::ReturnStmt(func->location, untyped)}); 384 382 } 385 383 386 384 void EnumAttrFuncGenerator::genPosnBody(ast::FunctionDecl* func) const { 387 388 389 390 391 392 393 385 auto castExpr = new ast::CastExpr( 386 func->location, 387 new ast::VariableExpr(func->location, func->params.front()), 388 new ast::EnumAttrType(new ast::EnumInstType(decl), 389 ast::EnumAttribute::Posn)); 390 func->stmts = new ast::CompoundStmt( 391 func->location, {new ast::ReturnStmt(func->location, castExpr)}); 394 392 } 395 393 396 394 void EnumAttrFuncGenerator::genAttributesDecls(const ast::EnumAttribute attr) { 397 398 399 400 401 402 403 404 405 ast::FunctionDecl* funcProto = attr == ast::EnumAttribute::Value 406 407 408 409 410 411 412 413 414 415 416 395 if (attr == ast::EnumAttribute::Value || 396 attr == ast::EnumAttribute::Label) { 397 std::vector<ast::ptr<ast::Init>> inits = 398 attr == ast::EnumAttribute::Value ? genValueInit() : genLabelInit(); 399 ast::ObjectDecl* arrayProto = 400 genAttrArrayProto(attr, getLocation(), inits); 401 forwards.push_back(arrayProto); 402 403 ast::FunctionDecl* funcProto = ( attr == ast::EnumAttribute::Value ) 404 ? genValueProto() 405 : genLabelProto(); 406 produceForwardDecl(funcProto); 407 genValueOrLabelBody(funcProto, arrayProto); 408 produceDecl(funcProto); 409 } else { 410 ast::FunctionDecl* funcProto = genPosnProto(); 411 produceForwardDecl(funcProto); 412 genPosnBody(funcProto); 413 produceDecl(funcProto); 414 } 417 415 } 418 416 419 417 ast::FunctionDecl* EnumAttrFuncGenerator::genSuccPredFunc(bool succ) { 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 location, 445 new ast::CastExpr(location, addOneExpr, retType) 446 447 448 449 450 418 ast::FunctionDecl* funcDecl = succ ? genSuccPosProto() : genPredPosProto(); 419 produceForwardDecl(funcDecl); 420 421 const CodeLocation& location = getLocation(); 422 423 auto& params = funcDecl->params; 424 assert(params.size() == 1); 425 auto param = params.front().strict_as<ast::ObjectDecl>(); 426 427 428 auto rets = funcDecl->returns; 429 assert(params.size() == 1); 430 auto ret = rets.front().strict_as<ast::ObjectDecl>(); 431 auto retType = ret->type.strict_as<ast::EnumAttrType>(); 432 433 auto addOneExpr = ast::UntypedExpr::createCall( location, 434 succ? "?+?": "?-?", 435 {new ast::VariableExpr(location, param), 436 ast::ConstantExpr::from_int(location, 1)} 437 ); 438 439 funcDecl->stmts = new ast::CompoundStmt( 440 location, { 441 new ast::ReturnStmt( 442 location, 443 new ast::CastExpr(location, addOneExpr, retType) 444 ) 445 } 446 ); 447 448 return funcDecl; 451 449 } 452 450 453 451 void EnumAttrFuncGenerator::genAttrFunctions() { 454 455 456 457 458 452 if (decl->base) { 453 genAttributesDecls(ast::EnumAttribute::Value); 454 genAttributesDecls(ast::EnumAttribute::Label); 455 genAttributesDecls(ast::EnumAttribute::Posn); 456 } 459 457 } 460 458 461 459 void EnumAttrFuncGenerator::genSuccPredDecl() { 462 463 464 465 466 467 468 460 if (decl->base) { 461 auto succProto = genSuccProto(); 462 auto predProto = genPredProto(); 463 464 produceForwardDecl(succProto); 465 produceForwardDecl(predProto); 466 } 469 467 } 470 468 471 469 void EnumAttrFuncGenerator::genSuccPredPosn() { 472 473 474 475 476 477 478 470 if (decl->base) { 471 ast::FunctionDecl* succ = genSuccPredFunc(true); 472 ast::FunctionDecl* pred = genSuccPredFunc(false); 473 474 produceDecl(succ); 475 produceDecl(pred); 476 } 479 477 } 480 478 481 479 void EnumAttrFuncGenerator::generateAndAppendFunctions( 482 483 484 485 486 487 488 489 490 480 std::list<ast::ptr<ast::Decl>>& decls) { 481 // Generate the functions (they go into forwards and definitions). 482 genAttrStandardFuncs(); 483 genAttrFunctions(); 484 genSuccPredDecl(); 485 genSuccPredPosn(); // Posn 486 // Now export the lists contents. 487 decls.splice(decls.end(), forwards); 488 decls.splice(decls.end(), definitions); 491 489 } 492 490 493 491 // --------------------------------------------------------- 494 492 495 struct ImplementEnumFunc final : public ast::WithDeclsToAdd<>,496 497 498 499 500 501 502 503 493 struct ImplementEnumFunc final : 494 public ast::WithDeclsToAdd<>, public ast::WithShortCircuiting { 495 void previsit(const ast::EnumDecl* enumDecl); 496 void previsit(const ast::FunctionDecl* functionDecl); 497 void postvisit(const ast::FunctionDecl* functionDecl); 498 499 private: 500 // Current level of nested functions. 501 unsigned int functionNesting = 0; 504 502 }; 505 503 506 504 void ImplementEnumFunc::previsit(const ast::EnumDecl* enumDecl) { 507 508 509 510 511 512 513 514 505 if (!enumDecl->body) return; 506 if (!enumDecl->base) return; 507 508 ast::EnumInstType enumInst(enumDecl->name); 509 enumInst.base = enumDecl; 510 511 EnumAttrFuncGenerator gen(enumDecl, &enumInst, functionNesting); 512 gen.generateAndAppendFunctions(declsToAddAfter); 515 513 } 516 514 517 515 void ImplementEnumFunc::previsit(const ast::FunctionDecl*) { 518 516 functionNesting += 1; 519 517 } 520 518 521 519 void ImplementEnumFunc::postvisit(const ast::FunctionDecl*) { 522 523 } 524 525 } 520 functionNesting -= 1; 521 } 522 523 } // namespace 526 524 527 525 void implementEnumFunc(ast::TranslationUnit& translationUnit) { 528 ast::Pass<ImplementEnumFunc>::run(translationUnit); 529 } 530 } // namespace Validate 526 ast::Pass<ImplementEnumFunc>::run(translationUnit); 527 } 528 529 } // namespace Validate -
src/Virtual/VirtualDtor.cpp
rcf191ac r7042c60 28 28 29 29 struct CtorDtor { 30 31 32 33 34 30 FunctionDecl * dtorSetup; // dtor init routine to add after last dtor for a struct 31 FunctionDecl * deleteFn; 32 FunctionDecl * lastDtor; // pointer to last occurence of dtor to know where to insert after 33 34 CtorDtor() : dtorSetup(nullptr), deleteFn(nullptr), lastDtor(nullptr) {} 35 35 }; 36 36 37 37 class CtorDtorTable { 38 unordered_map<const StructDecl *, CtorDtor> & structMap; 38 unordered_map<const StructDecl *, CtorDtor> & structMap; 39 40 public: 41 // if dtor is last dtor for this decl return the routine to add afterwards 42 // otherwise return nullptr 43 FunctionDecl * getToAddLater( const StructDecl * decl, FunctionDecl * dtor, FunctionDecl ** retDeleteFn ) { 44 auto iter = structMap.find( decl ); 45 if ( iter == structMap.end() || iter->second.lastDtor != dtor ) return nullptr; // check if this is needed 46 *retDeleteFn = iter->second.deleteFn; 47 return iter->second.dtorSetup; 48 } 49 50 // return if the dtorSetup field has been defined for this decl 51 bool inTable( const StructDecl * decl ) { 52 auto iter = structMap.find( decl ); 53 return iter->second.dtorSetup != nullptr; 54 } 55 56 void addLater( const StructDecl * decl, FunctionDecl * dtorSetup, FunctionDecl * deleteFn ) { 57 auto iter = structMap.find( decl ); 58 iter->second.dtorSetup = dtorSetup; 59 iter->second.deleteFn = deleteFn; 60 } 61 62 void addDtor( const StructDecl * decl, FunctionDecl * dtor ) { 63 auto iter = structMap.find( decl ); 64 iter->second.lastDtor = dtor; 65 } 66 67 CtorDtorTable( unordered_map<const StructDecl *, CtorDtor> & structMap ) : structMap(structMap) {} 68 }; 69 70 struct CollectStructDecls : public ast::WithGuards { 71 unordered_map<const StructDecl *, CtorDtor> & structDecls; 72 StructDecl * parentDecl; 73 bool insideStruct = false; 74 bool namedDecl = false; 75 76 const StructDecl ** virtualDtor; 77 78 // finds and sets a ptr to the actor, message, and request structs, which are needed in the next pass 79 void previsit( const StructDecl * decl ) { 80 if ( !decl->body ) return; 81 if( decl->name == "virtual_dtor" ) { 82 structDecls.emplace( make_pair( decl, CtorDtor() ) ); 83 *virtualDtor = decl; 84 } else { 85 GuardValue(insideStruct); 86 insideStruct = true; 87 parentDecl = mutate( decl ); 88 } 89 } 90 91 // this catches structs of the form: 92 // struct derived_type { virtual_dtor a; }; 93 // since they should be: 94 // struct derived_type { inline virtual_dtor; }; 95 void previsit ( const ObjectDecl * decl ) { 96 if ( insideStruct && ! decl->name.empty() ) { 97 GuardValue(namedDecl); 98 namedDecl = true; 99 } 100 } 101 102 // this collects the derived actor and message struct decl ptrs 103 void postvisit( const StructInstType * node ) { 104 if ( ! *virtualDtor ) return; 105 if ( insideStruct && !namedDecl ) { 106 auto structIter = structDecls.find( node->aggr() ); 107 if ( structIter != structDecls.end() ) 108 structDecls.emplace( make_pair( parentDecl, CtorDtor() ) ); 109 } 110 } 39 111 40 112 public: 41 // if dtor is last dtor for this decl return the routine to add afterwards 42 // otherwise return nullptr 43 FunctionDecl * getToAddLater( const StructDecl * decl, FunctionDecl * dtor, FunctionDecl ** retDeleteFn ) { 44 auto iter = structMap.find( decl ); 45 if ( iter == structMap.end() || iter->second.lastDtor != dtor ) return nullptr; // check if this is needed 46 *retDeleteFn = iter->second.deleteFn; 47 return iter->second.dtorSetup; 48 } 49 50 // return if the dtorSetup field has been defined for this decl 51 bool inTable( const StructDecl * decl ) { 52 auto iter = structMap.find( decl ); 53 return iter->second.dtorSetup != nullptr; 54 } 55 56 void addLater( const StructDecl * decl, FunctionDecl * dtorSetup, FunctionDecl * deleteFn ) { 57 auto iter = structMap.find( decl ); 58 iter->second.dtorSetup = dtorSetup; 59 iter->second.deleteFn = deleteFn; 60 } 61 62 void addDtor( const StructDecl * decl, FunctionDecl * dtor ) { 63 auto iter = structMap.find( decl ); 64 iter->second.lastDtor = dtor; 65 } 66 67 CtorDtorTable( unordered_map<const StructDecl *, CtorDtor> & structMap ) : structMap(structMap) {} 68 }; 69 70 struct CollectStructDecls : public ast::WithGuards { 71 unordered_map<const StructDecl *, CtorDtor> & structDecls; 72 StructDecl * parentDecl; 73 bool insideStruct = false; 74 bool namedDecl = false; 75 76 const StructDecl ** virtualDtor; 77 78 // finds and sets a ptr to the actor, message, and request structs, which are needed in the next pass 79 void previsit( const StructDecl * decl ) { 80 if ( !decl->body ) return; 81 if( decl->name == "virtual_dtor" ) { 82 structDecls.emplace( make_pair( decl, CtorDtor() ) ); 83 *virtualDtor = decl; 84 } else { 85 GuardValue(insideStruct); 86 insideStruct = true; 87 parentDecl = mutate( decl ); 88 } 89 } 90 91 // this catches structs of the form: 92 // struct derived_type { virtual_dtor a; }; 93 // since they should be: 94 // struct derived_type { inline virtual_dtor; }; 95 void previsit ( const ObjectDecl * decl ) { 96 if ( insideStruct && ! decl->name.empty() ) { 97 GuardValue(namedDecl); 98 namedDecl = true; 99 } 100 } 101 102 // this collects the derived actor and message struct decl ptrs 103 void postvisit( const StructInstType * node ) { 104 if ( ! *virtualDtor ) return; 105 if ( insideStruct && !namedDecl ) { 106 auto structIter = structDecls.find( node->aggr() ); 107 if ( structIter != structDecls.end() ) 108 structDecls.emplace( make_pair( parentDecl, CtorDtor() ) ); 109 } 110 } 111 112 public: 113 CollectStructDecls( unordered_map<const StructDecl *, CtorDtor> & structDecls, const StructDecl ** virtualDtor ): 114 structDecls( structDecls ), virtualDtor(virtualDtor) {} 113 CollectStructDecls( unordered_map<const StructDecl *, CtorDtor> & structDecls, const StructDecl ** virtualDtor ): 114 structDecls( structDecls ), virtualDtor(virtualDtor) {} 115 115 }; 116 116 117 117 // generates the forward decl of virtual dtor setting routine and delete routine 118 118 // generates the call to the virtual dtor routine in each appropriate ctor 119 // collects data needed for next pass that does the circular defn resolution 119 // collects data needed for next pass that does the circular defn resolution 120 120 // for dtor setters and delete fns (via table above) 121 121 struct GenFuncsCreateTables : public ast::WithDeclsToAdd<> { 122 123 124 125 126 127 128 129 if ( (decl->name != "?{}" && decl->name != "^?{}") || decl->params.size() == 0 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 dtorBody->push_front( 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 decl->location, 234 235 236 237 238 decl->location, 239 new AddressExpr( decl->location, new NameExpr( decl->location, "this" )), 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 UntypedExpr::createCall( 294 decl->location, 295 296 297 298 299 300 301 302 303 304 305 306 307 308 UntypedExpr::createCall( 309 decl->location, 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 122 unordered_map<const StructDecl *, CtorDtor> & structDecls; 123 CtorDtorTable & torDecls; 124 const StructDecl ** virtualDtor; 125 126 // collects the dtor info for actors/messages 127 // gens the dtor fwd decl and dtor call in ctor 128 void previsit( const FunctionDecl * decl ) { 129 if ( (decl->name != "?{}" && decl->name != "^?{}") || decl->params.size() == 0 130 || !decl->stmts || (decl->name == "^?{}" && decl->params.size() != 1)) return; 131 132 // the first param should be a reference 133 const ReferenceType * ref = dynamic_cast<const ReferenceType *>(decl->params.at(0)->get_type()); 134 if ( !ref ) return; 135 136 // the reference should be to a struct instance 137 const StructInstType * instType = dynamic_cast<const StructInstType *>(ref->base.get()); 138 if ( !instType ) return; 139 140 // return if not ctor/dtor for an actor or message 141 auto structIter = structDecls.find( instType->aggr() ); 142 if ( structIter == structDecls.end() ) return; 143 144 // If first param not named we need to name it to use it 145 if ( decl->params.at(0)->name == "" ) 146 mutate( decl->params.at(0).get() )->name = "__CFA_Virt_Dtor_param"; 147 148 if ( decl->name == "^?{}") { 149 torDecls.addDtor( structIter->first, mutate( decl ) ); 150 151 CompoundStmt * dtorBody = mutate( decl->stmts.get() ); 152 // Adds the following to the start of any actor/message dtor: 153 // __CFA_dtor_shutdown( this ); 154 dtorBody->push_front( 155 new IfStmt( decl->location, 156 new UntypedExpr ( 157 decl->location, 158 new NameExpr( decl->location, "__CFA_dtor_shutdown" ), 159 { 160 new NameExpr( decl->location, decl->params.at(0)->name ) 161 } 162 ), 163 new ReturnStmt( decl->location, nullptr ) 164 ) 165 ); 166 return; 167 } 168 169 // not dtor by this point so must be ctor 170 CompoundStmt * ctorBody = mutate( decl->stmts.get() ); 171 // Adds the following to the end of any actor/message ctor: 172 // __CFA_set_dtor( this ); 173 ctorBody->push_back( new ExprStmt( 174 decl->location, 175 new UntypedExpr ( 176 decl->location, 177 new NameExpr( decl->location, "__CFA_set_dtor" ), 178 { 179 new NameExpr( decl->location, decl->params.at(0)->name ) 180 } 181 ) 182 )); 183 184 if ( torDecls.inTable( structIter->first ) ) return; 185 186 // Generates the following: 187 // void __CFA_set_dtor( Derived_type & this ){ 188 // void (*__my_dtor)( Derived_type & ) = ^?{}; 189 // this.__virtual_dtor = (void (*)( Base_type & ))__my_dtor; 190 // this.__virtual_obj_start = (void *)(&this); 191 // } 192 CompoundStmt * setDtorBody = new CompoundStmt( decl->location ); 193 194 // Function type is: (void (*)(Derived_type &)) 195 FunctionType * derivedDtor = new FunctionType(); 196 derivedDtor->params.push_back( ast::deepCopy( ref ) ); 197 198 // Generates: 199 // void (*__my_dtor)( Derived_type & ) = ^?{}; 200 setDtorBody->push_back( new DeclStmt( 201 decl->location, 202 new ObjectDecl( 203 decl->location, 204 "__my_dtor", 205 new PointerType( derivedDtor ), 206 new SingleInit( decl->location, new NameExpr( decl->location, "^?{}" ) ) 207 ) 208 )); 209 210 // Function type is: (void (*)( Base_type & )) 211 FunctionType * baseDtor = new FunctionType(); 212 baseDtor->params.push_back( new ReferenceType( new StructInstType( *virtualDtor ) ) ); 213 214 // Generates: 215 // __CFA_set_virt_dtor( this, (void (*)( Base_type & ))__my_dtor ) 216 setDtorBody->push_back( new ExprStmt( 217 decl->location, 218 new UntypedExpr ( 219 decl->location, 220 new NameExpr( decl->location, "__CFA_set_virt_dtor" ), 221 { 222 new NameExpr( decl->location, "this" ), 223 new CastExpr( decl->location, new NameExpr( decl->location, "__my_dtor" ), new PointerType( baseDtor ), ExplicitCast ) 224 } 225 ) 226 )); 227 228 // Generates: 229 // __CFA_set_virt_start( (void *)(&this) ); 230 setDtorBody->push_back( new ExprStmt( 231 decl->location, 232 new UntypedExpr ( 233 decl->location, 234 new NameExpr( decl->location, "__CFA_set_virt_start" ), 235 { 236 new NameExpr( decl->location, "this" ), 237 new CastExpr( 238 decl->location, 239 new AddressExpr( decl->location, new NameExpr( decl->location, "this" )), 240 new PointerType( new ast::VoidType() ), ExplicitCast 241 ) 242 } 243 ) 244 )); 245 246 // put it all together into the complete function decl from above 247 FunctionDecl * setDtorFunction = new FunctionDecl( 248 decl->location, 249 "__CFA_set_dtor", 250 { 251 new ObjectDecl( 252 decl->location, 253 "this", 254 ast::deepCopy( ref ) 255 ), 256 }, // params 257 {}, 258 nullptr, // body 259 { Storage::Static }, // storage 260 Linkage::Cforall, // linkage 261 {}, // attributes 262 { Function::Inline } 263 ); 264 265 declsToAddBefore.push_back( ast::deepCopy( setDtorFunction ) ); 266 267 setDtorFunction->stmts = setDtorBody; 268 269 // The following generates the following specialized delete routine: 270 // static inline void delete( derived_type * ptr ) { 271 // if ( ptr ) 272 // ^(*ptr){}; 273 // __CFA_virt_free( *ptr ); 274 // } 275 CompoundStmt * deleteFnBody = new CompoundStmt( decl->location ); 276 277 // Generates: 278 // if ( ptr ) 279 // ^(*ptr){}; 280 deleteFnBody->push_back( 281 new IfStmt( 282 decl->location, 283 UntypedExpr::createCall( 284 decl->location, 285 "?!=?", 286 { 287 new NameExpr( decl->location, "ptr" ), 288 ConstantExpr::null( decl->location, new PointerType( ast::deepCopy( instType ) ) ) 289 } 290 ), 291 new ExprStmt( 292 decl->location, 293 UntypedExpr::createCall( 294 decl->location, 295 "^?{}", 296 { 297 UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "ptr" )) 298 } 299 ) 300 ) 301 ) 302 ); 303 304 // Generates: 305 // __CFA_virt_free( *ptr ); 306 deleteFnBody->push_back( new ExprStmt( 307 decl->location, 308 UntypedExpr::createCall( 309 decl->location, 310 "__CFA_virt_free", 311 { 312 UntypedExpr::createDeref( decl->location, new NameExpr( decl->location, "ptr" )) 313 } 314 ) 315 ) 316 ); 317 318 FunctionDecl * deleteFn = new FunctionDecl( 319 decl->location, 320 "delete", 321 { 322 new ObjectDecl( 323 decl->location, 324 "ptr", 325 new PointerType( ast::deepCopy( instType ) ) 326 ), 327 }, // params 328 {}, 329 nullptr, // body 330 { Storage::Static }, // storage 331 Linkage::Cforall, // linkage 332 {}, // attributes 333 { Function::Inline } 334 ); 335 336 declsToAddBefore.push_back( ast::deepCopy( deleteFn ) ); 337 338 deleteFn->stmts = deleteFnBody; 339 340 torDecls.addLater( structIter->first, setDtorFunction, deleteFn ); 341 } 342 342 343 343 public: 344 345 344 GenFuncsCreateTables( unordered_map<const StructDecl *, CtorDtor> & structDecls, CtorDtorTable & torDecls, const StructDecl ** virtualDtor ): 345 structDecls(structDecls), torDecls(torDecls), virtualDtor(virtualDtor) {} 346 346 }; 347 347 … … 349 349 // generates the trailing definitions of dtor setting routines for virtual dtors on messages and actors 350 350 // generates the function defns of __CFA_set_dtor 351 // separate pass is needed since __CFA_set_dtor needs to be defined after 351 // separate pass is needed since __CFA_set_dtor needs to be defined after 352 352 // the last dtor defn which is found in prior pass 353 353 struct GenSetDtor : public ast::WithDeclsToAdd<> { 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 354 unordered_map<const StructDecl *, CtorDtor> & structDecls; // set of decls that inherit from virt dtor 355 CtorDtorTable & torDecls; 356 357 // handles adding the declaration of the dtor init routine after the last dtor detected 358 void postvisit( const FunctionDecl * decl ) { 359 if ( decl->name != "^?{}" || !decl->stmts || decl->params.size() != 1 ) return; 360 361 // the one param should be a reference 362 const ReferenceType * ref = dynamic_cast<const ReferenceType *>(decl->params.at(0)->get_type()); 363 if ( !ref ) return; 364 365 // the reference should be to a struct instance 366 const StructInstType * instType = dynamic_cast<const StructInstType *>(ref->base.get()); 367 if ( !instType ) return; 368 369 FunctionDecl * deleteRtn; 370 371 // returns nullptr if not in table 372 FunctionDecl * maybeAdd = torDecls.getToAddLater( instType->aggr(), mutate( decl ), &deleteRtn ); 373 if ( maybeAdd ) { 374 declsToAddAfter.push_back( maybeAdd ); 375 declsToAddAfter.push_back( deleteRtn ); 376 } 377 } 378 379 public: 380 GenSetDtor( unordered_map<const StructDecl *, CtorDtor> & structDecls, CtorDtorTable & torDecls ): 381 structDecls(structDecls), torDecls(torDecls) {} 382 382 }; 383 383 384 384 void implementVirtDtors( TranslationUnit & translationUnit ) { 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 385 // unordered_map to collect all derived types and associated data 386 unordered_map<const StructDecl *, CtorDtor> structDecls; 387 CtorDtorTable torDecls( structDecls ); 388 389 const StructDecl * virtualDtorPtr = nullptr; 390 const StructDecl ** virtualDtor = &virtualDtorPtr; 391 392 // first pass collects all structs that inherit from virtual_dtor 393 Pass<CollectStructDecls>::run( translationUnit, structDecls, virtualDtor ); 394 395 // second pass locates all dtor/ctor routines that need modifying or need fns inserted before/after 396 Pass<GenFuncsCreateTables>::run( translationUnit, structDecls, torDecls, virtualDtor ); 397 398 // The third pass adds the forward decls needed to resolve circular defn problems 399 Pass<GenSetDtor>::run( translationUnit, structDecls, torDecls ); 400 400 } 401 402 401 403 402 } // namespace Virtual -
tests/alloc.cfa
rcf191ac r7042c60 10 10 // Created On : Wed Feb 3 07:56:22 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Oct 14 09:31:39 202213 // Update Count : 49 112 // Last Modified On : Tue Apr 23 14:04:11 2024 13 // Update Count : 492 14 14 // 15 15 … … 240 240 free( stp ); 241 241 242 posix_memalign( &stp, Alignment ); 242 posix_memalign( &stp, Alignment ); // CFA posix_memalign 243 243 *stp = (Struct){ 42, 42.5 }; 244 244 assert( (uintptr_t)stp % Alignment == 0 ); … … 275 275 sout | nl; 276 276 277 stp = alloc( dim, Alignment`align ); 277 stp = alloc( dim, Alignment`align ); // CFA array memalign 278 278 assert( (uintptr_t)stp % Alignment == 0 ); 279 279 for ( i; dim ) { stp[i] = (Struct){ 42, 42.5 }; } … … 316 316 sout | nl; 317 317 318 memset( &st, fill );// CFA memset, type safe318 memset( st, fill ); // CFA memset, type safe 319 319 sout | "CFA memset" | hex(st.x) | hex(st.y); 320 memcpy( &st1, &st );// CFA memcpy, type safe320 memcpy( st1, st ); // CFA memcpy, type safe 321 321 sout | "CFA memcpy" | hex(st1.x) | hex(st1.y); 322 322
Note:
See TracChangeset
for help on using the changeset viewer.