# Changeset 9373b6a

Ignore:
Timestamp:
Aug 9, 2021, 3:46:57 PM (16 months ago)
Branches:
enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
5438e41, e5aba4a
Parents:
478c610 (diff), b0b89a8 (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.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

Files:
4 edited

Unmodified
Removed
• ## doc/theses/andrew_beach_MMath/features.tex

 r478c610 throw/catch as a particular kind of raise/handle. These are the two parts that the user writes and may be the only two pieces of the EHM that have any syntax in the language. be the only two pieces of the EHM that have any syntax in a language. \paragraph{Raise} The raise is the starting point for exception handling. It marks the beginning of exception handling by raising an exception, which passes it to The raise is the starting point for exception handling by raising an exception, which passes it to the EHM. Some well known examples include the @throw@ statements of \Cpp and Java and the \code{Python}{raise} statement from Python. In real systems a raise may preform some other work (such as memory management) but for the the \code{Python}{raise} statement of Python. In real systems, a raise may perform some other work (such as memory management) but for the purposes of this overview that can be ignored. \paragraph{Handle} The purpose of most exception operations is to run some user code to handle that exception. This code is given, with some other information, in a handler. The primary purpose of an EHM is to run some user code to handle a raised exception. This code is given, with some other information, in a handler. A handler has three common features: the previously mentioned user code, a region of code they guard and an exception label/condition that matches certain exceptions. region of code it guards, and an exception label/condition that matches the raised exception. Only raises inside the guarded region and raising exceptions that match the label can be handled by a given handler. If multiple handlers could can handle an exception, EHMs will define a rule to pick one, such as best match" or first found". EHMs define a rule to pick one, such as best match" or first found". The @try@ statements of \Cpp, Java and Python are common examples. All three also show another common feature of handlers, they are grouped by the guarded region. show the common features of guarded region, raise, matching and handler. \begin{cfa} try {                           // guarded region ... throw exception;        // raise ... } catch( exception ) {  // matching condition, with exception label ...                             // handler code } \end{cfa} \subsection{Propagation} After an exception is raised comes what is usually the biggest step for the EHM: finding and setting up the handler. The propagation from raise to EHM: finding and setting up the handler for execution. The propagation from raise to handler can be broken up into three different tasks: searching for a handler, matching against the handler and installing the handler. \paragraph{Searching} The EHM begins by searching for handlers that might be used to handle the exception. Searching is usually independent of the exception that was thrown as it looks for handlers that have the raise site in their guarded the exception. The search is restricted to handlers that have the raise site in their guarded region. The search includes handlers in the current function, as well as any in \paragraph{Matching} Each handler found has to be matched with the raised exception. The exception label defines a condition that is used with exception and decides if Each handler found is matched with the raised exception. The exception label defines a condition that is used with the exception and decides if there is a match or not. In languages where the first match is used, this step is intertwined with searching; a match check is preformed immediately after the search finds a possible handler. searching; a match check is performed immediately after the search finds a handler. \paragraph{Installing} After a handler is chosen it must be made ready to run. After a handler is chosen, it must be made ready to run. The implementation can vary widely to fit with the rest of the design of the EHM. The installation step might be trivial or it could be If a matching handler is not guaranteed to be found, the EHM needs a different course of action for the case where no handler matches. different course of action for this case. This situation only occurs with unchecked exceptions as checked exceptions (such as in Java) can make the guarantee. This unhandled action is usually very general, such as aborting the program. (such as in Java) are guaranteed to find a matching handler. The unhandled action is usually very general, such as aborting the program. \paragraph{Hierarchy} exception hierarchy is a natural extension of the object hierarchy. Consider the following hierarchy of exceptions: Consider the following exception hierarchy: \begin{center} \input{exception-hierarchy} \end{center} A handler labeled with any given exception can handle exceptions of that type or any child type of that exception. The root of the exception hierarchy (here \code{C}{exception}) acts as a catch-all, leaf types catch single types (here \code{C}{exception}) acts as a catch-all, leaf types catch single types, and the exceptions in the middle can be used to catch different groups of related exceptions. This system has some notable advantages, such as multiple levels of grouping, the ability for libraries to add new exception types and the isolation the ability for libraries to add new exception types, and the isolation between different sub-hierarchies. This design is used in \CFA even though it is not a object-orientated is usually set up to do most of the work. The EHM can return control to many different places, The EHM can return control to many different places, where the most common are after the handler definition (termination) and after the raise (resumption). For effective exception handling, additional information is often passed from the raise to the handler and back again. So far only communication of the exceptions' identity has been covered. A common communication method is putting fields into the exception instance So far, only communication of the exception's identity is covered. A common communication method for passing more information is putting fields into the exception instance and giving the handler access to them. Passing the exception by reference instead of by value can allow data to be Using reference fields pointing to data at the raise location allows data to be passed in both directions. \section{Virtuals} Virtual types and casts are not part of \CFA's EHM nor are they required for any EHM. However, it is one of the best ways to support an exception hierarchy an EHM. However, one of the best ways to support an exception hierarchy is via a virtual hierarchy and dispatch system. Ideally, the virtual system would have been part of \CFA before the work Ideally, the virtual system should have been part of \CFA before the work on exception handling began, but unfortunately it was not. Hence, only the features and framework needed for the EHM were designed and implemented. Other features were considered to ensure that designed and implemented for this thesis. Other features were considered to ensure that the structure could accommodate other desirable features in the future but they were not implemented. The rest of this section will only discuss the implemented subset of the virtual system design. but are not implemented. The rest of this section only discusses the implemented subset of the virtual-system design. The virtual system supports multiple trees" of types. Each tree is It is important to note that these are virtual members, not virtual methods of object-orientated programming, and can be of any type. \PAB{Need to look at these when done. \CFA still supports virtual methods as a special case of virtual members. as a hidden field. \todo{Might need a diagram for virtual structure.} }% Up until this point the virtual system is similar to ones found in object-orientated languages but this where \CFA diverges. Objects encapsulate a single set of behaviours in each type, universally across the entire program, and indeed all programs that use that type definition. In this sense, the types are closed" and cannot be altered. In \CFA, types do not encapsulate any behaviour. Traits are local and types can begin to satisfy a trait, stop satisfying a trait or satisfy the same trait in a different way at any lexical location in the program. In this sense, they are open" as they can change at any time. object-orientated languages but this is where \CFA diverges. Objects encapsulate a single set of methods in each type, universally across the entire program, and indeed all programs that use that type definition. Even if a type inherits and adds methods, it still encapsulate a single set of methods. In this sense, object-oriented types are closed" and cannot be altered. In \CFA, types do not encapsulate any code. Traits are local for each function and types can satisfy a local trait, stop satisfying it or, satisfy the same trait in a different way at any lexical location in the program where a function is call. In this sense, the set of functions/variables that satisfy a trait for a type is open" as the set can change at every call site. This capability means it is impossible to pick a single set of functions that represent the type's implementation across the program. that represent a type's implementation across a program. \CFA side-steps this issue by not having a single virtual table for each type. A user can define virtual tables that are filled in at their declaration and given a name. Anywhere that name is visible, even if it is defined locally inside a function (although that means it does not have a static lifetime), it can be used. defined locally inside a function \PAB{What does this mean? (although that means it does not have a static lifetime)}, it can be used. Specifically, a virtual type is bound" to a virtual table that sets the virtual members for that object. The virtual members can be accessed completing the virtual system). The imaginary assertions would probably come from a trait defined by the virtual system, and state that the exception type is a virtual type, is a descendant of @exception_t@ (the base exception type) is a virtual type, is a descendant of @exception_t@ (the base exception type), and note its virtual table type. \end{cfa} Both traits ensure a pair of types are an exception type, its virtual table type type, and defines one of the two default handlers. The default handlers are used as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}. facing way. So these three macros are provided to wrap these traits to simplify referring to the names: @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@, and @IS_RESUMPTION_EXCEPTION@. All three take one or two arguments. The first argument is the name of the \CFA provides two kinds of exception handling: termination and resumption. These twin operations are the core of \CFA's exception handling mechanism. This section will cover the general patterns shared by the two operations and then go on to cover the details each individual operation. This section covers the general patterns shared by the two operations and then goes on to cover the details of each individual operation. Both operations follow the same set of steps. Both start with the user preforming a raise on an exception. Then the exception propagates up the stack. If a handler is found the exception is caught and the handler is run. First, a user raises an exception. Second, the exception propagates up the stack. Third, if a handler is found, the exception is caught and the handler is run. After that control continues at a raise-dependent location. If the search fails a default handler is run and, if it returns, then control Fourth, if a handler is not found, a default handler is run and, if it returns, then control continues after the raise. This general description covers what the two kinds have in common. Differences include how propagation is preformed, where exception continues after an exception is caught and handled and which default handler is run. %This general description covers what the two kinds have in common. The differences in the two operations include how propagation is performed, where execution continues after an exception is caught and handled, and which default handler is run. \subsection{Termination} \label{s:Termination} Termination handling is the familiar kind and used in most programming Termination handling is the familiar EHM and used in most programming languages with exception handling. It is a dynamic, non-local goto. If the raised exception is matched and @is_termination_exception@ at the call site. Through \CFA's trait system, the trait functions are implicitly passed into the throw code and the EHM. throw code for use by the EHM. A new @defaultTerminationHandler@ can be defined in any scope to change the throw's behaviour (see below). change the throw's behaviour when a handler is not found (see below). The throw copies the provided exception into managed memory to ensure % How to say propagation starts, its first sub-step is the search. Then propagation starts with the search. \CFA uses a first match" rule so matching is preformed with the copied exception as the search continues. It starts from the throwing function and proceeds towards base of the stack, matching is performed with the copied exception as the search key. It starts from the raise in the throwing function and proceeds towards the base of the stack, from callee to caller. At each stack frame, a check is made for resumption handlers defined by the At each stack frame, a check is made for termination handlers defined by the @catch@ clauses of a @try@ statement. \begin{cfa} \end{cfa} When viewed on its own, a try statement simply executes the statements in \snake{GUARDED_BLOCK} and when those are finished, in the \snake{GUARDED_BLOCK}, and when those are finished, the try statement finishes. invoked functions, all the handlers in these statements are included in the search path. Hence, if a termination exception is raised these handlers may be matched Hence, if a termination exception is raised, these handlers may be matched against the exception and may handle it. Exception matching checks the handler in each catch clause in the order they appear, top to bottom. If the representation of the raised exception type is the same or a descendant of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$ is the same or a descendant of @EXCEPTION_TYPE@$_i$, then @NAME@$_i$ (if provided) is bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$ freed and control continues after the try statement. If no termination handler is found during the search then the default handler (\defaultTerminationHandler) visible at the raise statement is run. Through \CFA's trait system the best match at the raise statement will be used. If no termination handler is found during the search, then the default handler (\defaultTerminationHandler) visible at the raise statement is called. Through \CFA's trait system the best match at the raise statement is used. This function is run and is passed the copied exception. If the default handler is run control continues after the raise statement. If the default handler finishes, control continues after the raise statement. There is a global @defaultTerminationHandler@ that is polymorphic over all termination exception types. Since it is so general a more specific handler can be defined and is used for those types, effectively overriding the handler for a particular exception type. The global default termination handler performs a cancellation (see \vref{s:Cancellation}) on the current stack with the copied exception. (see \vref{s:Cancellation} for the justification) on the current stack with the copied exception. Since it is so general, a more specific handler is usually defined, possibly with a detailed message, and used for specific exception type, effectively overriding the default handler. \subsection{Resumption} \label{s:Resumption} Resumption exception handling is less common than termination but is Resumption exception handling is the less familar EHM, but is just as old~\cite{Goodenough75} and is simpler in many ways. It is a dynamic, non-local function call. If the raised exception is matched a closure is taken from up the stack and executed, matched, a closure is taken from up the stack and executed, after which the raising function continues executing. The common uses for resumption exceptions include function once the error is corrected, and ignorable events, such as logging where nothing needs to happen and control should always continue from the same place. should always continue from the raise point. A resumption raise is started with the @throwResume@ statement: the exception system while handling the exception. At run-time, no exception copy is made. Resumption does not unwind the stack nor otherwise remove values from the current scope, so there is no need to manage memory to keep things in scope. The EHM then begins propagation. The search starts from the raise in the At run-time, no exception copy is made, since resumption does not unwind the stack nor otherwise remove values from the current scope, so there is no need to manage memory to keep the exception in scope. Then propagation starts with the search. It starts from the raise in the resuming function and proceeds towards the base of the stack, from callee to caller. } \end{cfa} % PAB, you say this above. % When a try statement is executed, it simply executes the statements in the % @GUARDED_BLOCK@ and then finishes. % % However, while the guarded statements are being executed, including any % invoked functions, all the handlers in these statements are included in the % search path. % Hence, if a resumption exception is raised, these handlers may be matched % against the exception and may handle it. % % Exception matching checks the handler in each catch clause in the order % they appear, top to bottom. If the representation of the raised exception type % is the same or a descendant of @EXCEPTION_TYPE@$_i$, then @NAME@$_i$ % (if provided) is bound to a pointer to the exception and the statements in % @HANDLER_BLOCK@$_i$ are executed. % If control reaches the end of the handler, execution continues after the % the raise statement that raised the handled exception. % % Like termination, if no resumption handler is found during the search, % then the default handler (\defaultResumptionHandler) visible at the raise % statement is called. It will use the best match at the raise sight according % to \CFA's overloading rules. The default handler is % passed the exception given to the raise. When the default handler finishes % execution continues after the raise statement. % % There is a global @defaultResumptionHandler{} is polymorphic over all % resumption exceptions and performs a termination throw on the exception. % The \defaultTerminationHandler{} can be overridden by providing a new % function that is a better match. The @GUARDED_BLOCK@ and its associated nested guarded statements work the same for resumption as for termination, as does exception matching at each @catchResume@. Similarly, if no resumption handler is found during the search, then the currently visible default handler (\defaultResumptionHandler) is called and control continues after the raise statement if it returns. Finally, there is also a global @defaultResumptionHandler@, which can be overridden, that is polymorphic over all resumption exceptions but performs a termination throw on the exception rather than a cancellation. Throwing the exception in @defaultResumptionHandler@ has the positive effect of walking the stack a second time for a recovery handler. Hence, a programmer has two chances for help with a problem, fixup or recovery, should either kind of handler appear on the stack. However, this dual stack walk leads to following apparent anomaly: \begin{cfa} try { throwResume E; } catch (E) { // this handler runs } \end{cfa} because the @catch@ appears to handle a @throwResume@, but a @throwResume@ only matches with @catchResume@. The anomaly results because the unmatched @catchResuem@, calls @defaultResumptionHandler@, which in turn throws @E@. % I wonder if there would be some good central place for this. Note that termination handlers and resumption handlers may be used together Note, termination and resumption handlers may be used together in a single try statement, intermixing @catch@ and @catchResume@ freely. Each type of handler only interacts with exceptions from the matching kind of raise. When a try statement is executed, it simply executes the statements in the @GUARDED_BLOCK@ and then finishes. However, while the guarded statements are being executed, including any invoked functions, all the handlers in these statements are included in the search path. Hence, if a resumption exception is raised these handlers may be matched against the exception and may handle it. Exception matching checks the handler in each catch clause in the order they appear, top to bottom. If the representation of the raised exception type is the same or a descendant of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$ (if provided) is bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$ are executed. If control reaches the end of the handler, execution continues after the the raise statement that raised the handled exception. Like termination, if no resumption handler is found during the search, the default handler (\defaultResumptionHandler) visible at the raise statement is called. It will use the best match at the raise sight according to \CFA's overloading rules. The default handler is passed the exception given to the raise. When the default handler finishes execution continues after the raise statement. There is a global \defaultResumptionHandler{} is polymorphic over all resumption exceptions and preforms a termination throw on the exception. The \defaultTerminationHandler{} can be overridden by providing a new function that is a better match. \subsubsection{Resumption Marking} \label{s:ResumptionMarking} A key difference between resumption and termination is that resumption does not unwind the stack. A side effect that is that when a handler is matched and run it's try block (the guarded statements) and every try statement not unwind the stack. A side effect is that, when a handler is matched and run, its try block (the guarded statements) and every try statement searched before it are still on the stack. There presence can lead to the recursive resumption problem. the \emph{recursive resumption problem}. The recursive resumption problem is any situation where a resumption handler When this code is executed, the guarded @throwResume@ starts a search and matches the handler in the @catchResume@ clause. This call is placed on the stack above the try-block. The second raise then searches the same try block and puts another instance of the call is placed on the stack above the try-block. Now the second raise in the handler searches the same try block, matches, and puts another instance of the same handler on the stack leading to infinite recursion. While this situation is trivial and easy to avoid, much more complex cycles can form with multiple handlers and different exception types. To prevent all of these cases, a each try statement is marked" from the time the exception search reaches it to either when the exception is being handled completes the matching handler or when the search reaches the base While this situation is trivial and easy to avoid, much more complex cycles can form with multiple handlers and different exception types.  The key point is that the programmer's intuition expects every raise in a handler to start searching \emph{below} the @try@ statement, making it difficult to understand and fix the problem. To prevent all of these cases, each try statement is marked" from the time the exception search reaches it to either when a matching handler completes or when the search reaches the base of the stack. While a try statement is marked, its handlers are never matched, effectively for instance, marking just the handlers that caught the exception, would also prevent recursive resumption. However, these rules mirror what happens with termination. The try statements that are marked are the ones that would be removed from the stack if this was a termination exception, that is those on the stack However, the rule selected mirrors what happens with termination, and hence, matches programmer intuition that a raise searches below a try. In detail, the marked try statements are the ones that would be removed from the stack for a termination exception, \ie those on the stack between the handler and the raise statement. This symmetry applies to the default handler as well, as both kinds of // Only handle IO failure for f3. } // Can't handle a failure relating to f2 here. // Handle a failure relating to f2 further down the stack. \end{cfa} In this example the file that experienced the IO error is used to decide \subsection{Comparison with Reraising} A more popular way to allow handlers to match in more detail is to reraise the exception after it has been caught, if it could not be handled here. On the surface these two features seem interchangeable. If @throw;@ (no argument) starts a termination reraise, which is the same as a raise but reuses the last caught exception, then these two statements have the same behaviour: \begin{cfa} try { do_work_may_throw(); } catch(exception_t * exc ; can_handle(exc)) { handle(exc); } \end{cfa} \begin{cfa} try { do_work_may_throw(); } catch(exception_t * exc) { if (can_handle(exc)) { handle(exc); } else { throw; } } \end{cfa} That is, they will have the same behaviour in isolation. Without conditional catch, the only approach to match in more detail is to reraise the exception after it has been caught, if it could not be handled. \begin{center} \begin{tabular}{l|l} \begin{cfa} try { do_work_may_throw(); } catch(excep_t * ex; can_handle(ex)) { handle(ex); } \end{cfa} & \begin{cfa} try { do_work_may_throw(); } catch(excep_t * ex) { if (can_handle(ex)) { handle(ex); } else { throw; } } \end{cfa} \end{tabular} \end{center} Notice catch-and-reraise increases complexity by adding additional data and code to the exception process. Nevertheless, catch-and-reraise can simulate conditional catch straightforwardly, when exceptions are disjoint, \ie no inheritance. However, catch-and-reraise simulation becomes unusable for exception inheritance. \begin{flushleft} \begin{cfa}[xleftmargin=6pt] exception E1; exception E2(E1); // inheritance \end{cfa} \begin{tabular}{l|l} \begin{cfa} try { ... foo(); ... // raise E1/E2 ... bar(); ... // raise E1/E2 } catch( E2 e; e.rtn == foo ) { ... } catch( E1 e; e.rtn == foo ) { ... } catch( E1 e; e.rtn == bar ) { ... } \end{cfa} & \begin{cfa} try { ... foo(); ... ... bar(); ... } catch( E2 e ) { if ( e.rtn == foo ) { ... } else throw; // reraise } catch( E1 e ) { if (e.rtn == foo) { ... } else if (e.rtn == bar) { ... else throw; // reraise } \end{cfa} \end{tabular} \end{flushleft} The derived exception @E2@ must be ordered first in the catch list, otherwise the base exception @E1@ catches both exceptions. In the catch-and-reraise code (right), the @E2@ handler catches exceptions from both @foo@ and @bar@. However, the reraise misses the following catch clause. To fix this problem, an enclosing @try@ statement is need to catch @E2@ for @bar@ from the reraise, and its handler must duplicate the inner handler code for @bar@. To generalize, this fix for any amount of inheritance and complexity of try statement requires a technique called \emph{try-block splitting}~\cite{Krischer02}, which is not discussed in this thesis. It is sufficient to state that conditional catch is more expressive than catch-and-reraise in terms of complexity. \begin{comment} That is, they have the same behaviour in isolation. Two things can expose differences between these cases. One is the existence of multiple handlers on a single try statement. A reraise skips all later handlers on this try statement but a conditional A reraise skips all later handlers for a try statement but a conditional catch does not. Hence, if an earlier handler contains a reraise later handlers are implicitly skipped, with a conditional catch they are not. % Hence, if an earlier handler contains a reraise later handlers are % implicitly skipped, with a conditional catch they are not. Still, they are equivalently powerful, both can be used two mimic the behaviour of the other, %   exception_ptr current_exception() noexcept; % https://www.python.org/dev/peps/pep-0343/ \end{comment} \section{Finally Clauses} The @FINALLY_BLOCK@ is executed when the try statement is removed from the stack, including when the @GUARDED_BLOCK@ finishes, any termination handler finishes or during an unwind. finishes, or during an unwind. The only time the block is not executed is if the program is exited before the stack is unwound. Not all languages with unwinding have finally clauses. Notably \Cpp does without it as descructors, and the RAII design pattern, serve a similar role. Although destructors and finally clauses can be used in the same cases, without it as destructors, and the RAII design pattern, serve a similar role. Although destructors and finally clauses can be used for the same cases, they have their own strengths, similar to top-level function and lambda functions with closures. Destructors take more work for their first use, but if there is clean-up code that needs to be run every time a type is used they soon become much easier Destructors take more work for their creation, but if there is clean-up code that needs to be run every time a type is used, they are much easier to set-up. On the other hand finally clauses capture the local context, so is easy to use when the clean-up is not dependent on the type of a variable or requires information from multiple variables. % To Peter: I think these are the main points you were going for. \section{Cancellation} raise, this exception is not used in matching only to pass information about the cause of the cancellation. (This also means matching cannot fail so there is no default handler.) Finaly, since a cancellation only unwinds and forwards, there is no default handler. After @cancel_stack@ is called the exception is copied into the EHM's memory After the main stack is unwound there is a program-level abort. There are two reasons for these semantics. The first is that it had to do this abort. in a sequential program as there is nothing else to notify and the simplicity of keeping the same behaviour in sequential and concurrent programs is good. The reasons for this semantics in a sequential program is that there is no more code to execute. This semantics also applies to concurrent programs, too, even if threads are running. That is, if any threads starts a cancellation, it implies all threads terminate. Keeping the same behaviour in sequential and concurrent programs is simple. Also, even in concurrent programs there may not currently be any other stacks and even if other stacks do exist, main has no way to know where they are. caller's context and passes it to the internal report. A coroutine knows of two other coroutines, its starter and its last resumer. A coroutine only knows of two other coroutines, its starter and its last resumer. The starter has a much more distant connection, while the last resumer just (in terms of coroutine state) called resume on this coroutine, so the message cascade an error across any number of coroutines, cleaning up each in turn, until the error is handled or a thread stack is reached. \PAB{Part of this I do not understand. A cancellation cannot be caught. But you talk about handling a cancellation in the last sentence. Which is correct?}
• ## doc/theses/andrew_beach_MMath/uw-ethesis.tex

 r478c610 \input{performance} \input{future} \input{conclusion} %----------------------------------------------------------------------
• ## tests/io/manipulatorsOutput3.cfa

 r478c610 // // Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo // // // The contents of this file are covered under the licence agreement in the // file "LICENCE" distributed with Cforall. // // manipulatorsOutput3.cfa -- // // Created On       : Tue Apr 13 17:54:23 2021 // Last Modified By : Peter A. Buhr // Last Modified On : Tue Apr 13 17:54:48 2021 // Update Count     : 1 // Last Modified On : Sun Aug  8 22:37:20 2021 // Update Count     : 2 //
• ## tests/sum.cfa

 r478c610 // Created On       : Wed May 27 17:56:53 2015 // Last Modified By : Peter A. Buhr // Last Modified On : Tue Jul 16 09:51:37 2019 // Update Count     : 336 // Last Modified On : Thu Aug  5 21:27:25 2021 // Update Count     : 346 // trait sumable( T ) { void ?{}( T &, zero_t );                                                        // 0 literal constructor void ?{}( T &, one_t );                                                         // 1 literal constructor T ?+?( T, T );                                                                          // assortment of additions T ?+=?( T &, T ); T ++?( T & ); T ?++( T & ); T ?+=?( T &, T );                                                                       // get pre/post ++ with += and one_t }; // sumable forall( T | sumable( T ) )                                              // use trait forall( T | sumable( T ) )                                                              // use trait T sum( size_t size, T a[] ) { T total = 0;                                                                            // initialize by 0 constructor int main( void ) { #if 0 const int low = 5, High = 15, size = High - low; S ?+?( S t1, S t2 ) { return (S){ t1.i + t2.i, t1.j + t2.j }; } S ?+=?( S & t1, S t2 ) { t1 = t1 + t2; return t1; } S ++?( S & t ) { t += (S){1}; return t; } S ?++( S & t ) { S temp = t; t += (S){1}; return temp; } ofstream & ?|?( ofstream & os, S v ) { return os | v.i | v.j; } void ?|?( ofstream & os, S v ) { (ofstream &)(os | v); ends( os ); } S s = (S){0}, a[size], v = { low, low }; S s = 0, a[size], v = { low, low }; for ( int i = 0; i < size; i += 1, v += (S){1} ) { s += (S)v; | sum( size, gs.x ) | ", check" | (int)s;              // add field array in generic type delete( gs.x ); #else const int low = 5, High = 15, size = High - low; signed char s = 0, a[size], v = (char)low; for ( int i = 0; i < size; i += 1, v += 1hh ) { s += v; a[i] = v; } // for printf( "sum from %d to %d is %hhd, check %hhd\n", low, High, sum( size, (signed char *)a ), (signed char)s ); unsigned char s = 0, a[size], v = low; for ( int i = 0; i < size; i += 1, v += 1hhu ) { s += (unsigned char)v; a[i] = (unsigned char)v; } // for printf( "sum from %d to %d is %hhu, check %hhu\n", low, High, sum( size, (unsigned char *)a ), (unsigned char)s ); short int s = 0, a[size], v = low; for ( int i = 0; i < size; i += 1, v += 1h ) { s += (short int)v; a[i] = (short int)v; } // for printf( "sum from %d to %d is %hd, check %hd\n", low, High, sum( size, (short int *)a ), (short int)s ); int s = 0, a[size], v = low; for ( int i = 0; i < size; i += 1, v += 1 ) { s += (int)v; a[i] = (int)v; } // for printf( "sum from %d to %d is %d, check %d\n", low, High, sum( size, (int *)a ), (int)s ); float s = 0.0f, a[size], v = low / 10.0f; for ( int i = 0; i < size; i += 1, v += 0.1f ) { s += (float)v; a[i] = (float)v; } // for printf( "sum from %g to %g is %g, check %g\n", low / 10.0f, High / 10.0f, sum( size, (float *)a ), (float)s ); double s = 0.0, a[size], v = low / 10.0; for ( int i = 0; i < size; i += 1, v += 0.1 ) { s += (double)v; a[i] = (double)v; } // for printf( "sum from %g to %g is %g, check %g\n", low / 10.0f, High / 10.0f, sum( size, (double *)a ), (double)s ); struct S { int i, j; }; void ?{}( S & s ) { s.[i, j] = 0; } void ?{}( S & s, int i ) { s.[i, j] = [i, 0]; } void ?{}( S & s, int i, int j ) { s.[i, j] = [i, j]; } void ?{}( S & s, zero_t ) { s.[i, j] = 0; } void ?{}( S & s, one_t ) { s.[i, j] = 1; } S ?+?( S t1, S t2 ) { return (S){ t1.i + t2.i, t1.j + t2.j }; } S ?+=?( S & t1, S t2 ) { t1 = t1 + t2; return t1; } S ++?( S & t ) { t += (S){1}; return t; } S ?++( S & t ) { S temp = t; t += (S){1}; return temp; } ofstream & ?|?( ofstream & os, S v ) { return os | v.i | v.j; } void ?|?( ofstream & os, S v ) { (ofstream &)(os | v); ends( os ); } S s = 0, a[size], v = { low, low }; for ( int i = 0; i < size; i += 1, v += (S){1} ) { s += (S)v; a[i] = (S)v; } // for printf( "sum from %d to %d is %d %d, check %d %d\n", low, High, sum( size, (S *)a ).[i, j], s.[i, j] ); forall( Impl | sumable( Impl ) ) struct GS { Impl * x, * y; }; GS(int) gs; // FIX ME, resolution problem with anew not picking up the LH type gs.x = (typeof(gs.x))anew( size );                                      // create array storage for field s = 0; v = low; for ( int i = 0; i < size; i += 1, v += 1 ) { s += (int)v; gs.x[i] = (int)v;                                                               // set field array in generic type } // for printf( "sum from %d to %d is %d, check %d\n", low, High, sum( size, gs.x ), (int)s );           // add field array in generic type delete( gs.x ); #endif } // main
Note: See TracChangeset for help on using the changeset viewer.