Changes in / [4520b77e:a065f1f]


Ignore:
Files:
2 added
24 edited

Legend:

Unmodified
Added
Removed
  • doc/bibliography/pl.bib

    r4520b77e ra065f1f  
    188188        Unstructured {\it sends\/} and {\it accepts\/} are forbidden.  To
    189189        this the mechanisms of {\it delegation\/} and {\it delay queues\/}
    190         are added to enable switching and triggering of activities. 
    191         Concurrent subactivities and atomic actions are provided for 
     190        are added to enable switching and triggering of activities.
     191        Concurrent subactivities and atomic actions are provided for
    192192        compactness and simplicity.  We show how solutions to many important
    193193        concurrent problems [sic], such as pipelining, constraint management
    194         and ``administration'' can be compactly expressed using these 
     194        and ``administration'' can be compactly expressed using these
    195195        mechanisms.
    196196   },
     
    529529        like ``c is a collection with element type e'', but how such things
    530530        are used isn't explained.
    531        
     531
    532532        For each descriptive class used in a parameter list, an implicit
    533533        parameter is created that is passed a vector of procedures.
     
    11721172@techreport{Prokopec11,
    11731173    keywords    = {ctrie, concurrent map},
    1174     contributer = {a3moss@uwaterloo.ca}, 
     1174    contributer = {a3moss@uwaterloo.ca},
    11751175    title       = {Cache-aware lock-free concurrent hash tries},
    11761176    author      = {Prokopec, Aleksandar and Bagwell, Phil and Odersky, Martin},
     
    15001500    year        = 2001,
    15011501    url         = {http://citeseer.ist.psu.edu/berger01composing.html}
    1502 } 
     1502}
    15031503
    15041504@article{Andrews83,
     
    15451545        We give a rationale for our decisions and compare Concurrent C
    15461546        extensions with the concurrent programming facilities in Ada.
    1547         Concurrent C has been implemented on the UNIX system running on a 
     1547        Concurrent C has been implemented on the UNIX system running on a
    15481548        single processor.  A distributed version of Concurrent C is being
    15491549        implemented.
     
    18141814    keywords    = {objects, concurrency},
    18151815    contributer = {gjditchfield@plg},
    1816     author      = {P. A. Buhr and G. J. Ditchfield and B. M. Younger and C. R. Zarnke}, 
     1816    author      = {P. A. Buhr and G. J. Ditchfield and B. M. Younger and C. R. Zarnke},
    18171817    title       = {Concurrency in {C}{\kern-.1em\hbox{\large\texttt{+\kern-.25em+}}}},
    18181818    institution = {Department of Computer Science, University of Waterloo},
     
    20442044    series      = {Lecture Notes in Computer Science, Ed. by G. Goos and J. Hartmanis}
    20452045}
    2046  
     2046
    20472047@article{Wang71,
    20482048    keywords    = {coroutines},
     
    20562056    pages       = {425-449},
    20572057}
    2058  
     2058
    20592059@article{Castagna95,
    20602060    keywords    = {type-systems, covariance, contravariance},
     
    23902390    year        = 1996,
    23912391}
    2392  
     2392
    23932393@article{Richardson93,
    23942394    keywords    = {C++, persistence, database},
     
    24732473    publisher   = {ACM},
    24742474    address     = {New York, NY, USA},
    2475 } 
     2475}
    24762476
    24772477@article{design,
     
    27002700    publisher   = {ACM},
    27012701    address     = {New York, NY, USA},
    2702 } 
     2702}
    27032703
    27042704@book{Eiffel,
     
    33573357    publisher   = {ACM},
    33583358    address     = {New York, NY, USA},
    3359 } 
     3359}
    33603360
    33613361@manual{Fortran95,
     
    37403740    keywords    = {processes, distributed computing},
    37413741    contributer = {pabuhr@plg},
    3742     author      = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini}, 
     3742    author      = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini},
    37433743    title       = {Hermes: A Language for Distributed Computing},
    37443744    institution = {IBM T. J. Watson Research Center},
     
    37513751    keywords    = {processes, distributed computing},
    37523752    contributer = {pabuhr@plg},
    3753     author      = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini}, 
     3753    author      = {Robert E. Strom and David F. Bacon and Arthur P. Goldberg and Andy Lowry and Daniel M. Yellin and Shaula Alexander Yemini},
    37543754    title       = {Hermes: A Language for Distributed Computing},
    37553755    publisher   = {Prentice-Hall},
     
    43024302    pages       = {85-103}
    43034303}
    4304    
     4304
    43054305@article{Murer96,
    43064306    keywords    = {interators, generators, cursors},
     
    43304330
    43314331% J
    4332                  
     4332
    43334333@book{Java,
    43344334    keywords    = {Java},
     
    46274627    publisher   = {ACM},
    46284628    address     = {New York, NY, USA},
    4629 } 
     4629}
    46304630
    46314631@article{Dice15,
     
    49784978    number      = 31
    49794979}
    4980  
     4980
    49814981@article{Dueck90,
    49824982    keywords    = {attribute grammars},
     
    51075107    keywords    = {multiple inheritance},
    51085108    contributer = {pabuhr@plg},
    5109     author      = {Harry Bretthauer and Thomas Christaller and J\"{u}rgen Kopp}, 
     5109    author      = {Harry Bretthauer and Thomas Christaller and J\"{u}rgen Kopp},
    51105110    title       = {Multiple vs. Single Inheritance in Object-oriented Programming Languages. What do we really want?},
    51115111    institution = {Gesellschaft F\"{u}r Mathematik und Datenverarbeitung mbH},
     
    56505650    publisher   = {ACM},
    56515651    address     = {New York, NY, USA},
    5652 } 
     5652}
    56535653
    56545654@book{Deitel04,
     
    58275827    year        = 1980,
    58285828    month       = nov, volume = 15, number = 11, pages = {47-56},
    5829     note        = {Proceedings of the ACM-SIGPLAN Symposium on the {Ada} Programming Language}, 
     5829    note        = {Proceedings of the ACM-SIGPLAN Symposium on the {Ada} Programming Language},
    58305830    comment     = {
    58315831        The two-pass (bottom-up, then top-down) algorithm, with a proof
     
    59575957        Given a base typed lambda calculus with function types, type
    59585958        abstractions, and a recursive expression \(\mbox{fix } x:t.e\),
    5959         then type inference for the partially typed language 
     5959        then type inference for the partially typed language
    59605960        \begin{eqnarray}
    59615961        \lambda x:\tau.e        &\Rightarrow& \lambda x.e       \\
     
    66036603        manner.  The paper also discusses efficient composition of
    66046604        sequences of asynchronous calls to different locations in a
    6605         network. 
     6605        network.
    66066606    }
    66076607}
     
    66166616    volume      = 32, number = 4, pages = {305-311},
    66176617    abstract    = {
    6618        
     6618
    66196619    }
    66206620}
     
    69346934        partitioning switch statements into dense tables.  It also
    69356935        implements target-independent function tracing and expression-level
    6936         profiling. 
     6936        profiling.
    69376937    }
    69386938}
     
    71507150    publisher   = {ACM},
    71517151    address     = {New York, NY, USA},
    7152 } 
     7152}
    71537153
    71547154@inproceedings{Leissa14,
     
    72687268    keywords    = {Smalltalk, abstract class, protocol},
    72697269    contributer = {gjditchfield@plg},
    7270     author      = {A. Goldberg and D. Robson}, 
     7270    author      = {A. Goldberg and D. Robson},
    72717271    title       = {Smalltalk-80: The Language and its Implementation},
    72727272    publisher   = {Addison-Wesley},
     
    78457845    title       = {Thread (computing)},
    78467846    author      = {{Threading Model}},
    7847     howpublished= {\href{https://en.wikipedia.org/wiki/Thread_(computing)}{https://\-en.wikipedia.org/\-wiki/\-Thread\_(computing)}},
     7847    howpublished= {\href{https://en.wikipedia.org/wiki/Thread_(computing)}{https://\-en.wikipedia.org/\-wiki/\-Thread\_\-(computing)}},
    78487848}
    78497849
     
    78897889    note        = {Lecture Notes in Computer Science, v. 19},
    78907890    abstract    = {
    7891        
     7891
    78927892    }
    78937893}
     
    80088008    publisher   = {USENIX Association},
    80098009    address     = {Berkeley, CA, USA},
    8010 } 
     8010}
    80118011
    80128012@article{Leroy00,
     
    83548354    author      = {Bjarne Stroustrup},
    83558355    title       = {What is ``Object-Oriented Programming''?},
    8356     booktitle   = {Proceedings of the First European Conference on Object Oriented Programming}, 
     8356    booktitle   = {Proceedings of the First European Conference on Object Oriented Programming},
    83578357    month       = jun,
    83588358    year        = 1987
     
    83968396    publisher   = {ACM},
    83978397    address     = {New York, NY, USA},
    8398 } 
     8398}
    83998399
    84008400% X
  • doc/theses/thierry_delisle_PhD/thesis/local.bib

    r4520b77e ra065f1f  
    583583  title={Per-entity load tracking},
    584584  author={Corbet, Jonathan},
    585   journal={LWN article, available at: https://lwn.net/Articles/531853},
     585  journal={LWN article, available at: {\href{https://lwn.net/Articles/531853}{https://\-lwn.net/\-Articles/\-531853}}},
    586586  year={2013}
    587587}
     
    717717  title = {Scheduling Benchmarks},
    718718  author = {Thierry Delisle},
    719   howpublished = {\href{https://github.com/cforall/SchedulingBenchmarks_PhD22}{https://\-github.com/\-cforall/\-SchedulingBenchmarks\_\-PhD22}},
     719  howpublished = {\href{https://github.com/cforall/SchedulingBenchmarks_PhD22}{https://\-github.com/\-cforall/\-Scheduling\-Benchmarks\_\-PhD22}},
    720720}
    721721
     
    832832  title      = "eventfd(2) Linux User's Manual",
    833833  year       = "2019",
    834   month      = "MArch",
     834  month      = "March",
    835835}
    836836
     
    10601060  year = "2020",
    10611061  month = "June",
    1062   howpublished = "\href{https://xkcd.com/2318/}",
     1062  howpublished = "\href{https://xkcd.com/2318/}{https://\-xkcd.com/\-2318/}",
    10631063  note = "[Online; accessed 10-June-2020]"
    10641064}
     
    10691069  year = "2011",
    10701070  month = "June",
    1071   howpublished = "\href{https://xkcd.com/908/}",
     1071  howpublished = "\href{https://xkcd.com/908/}{https://\-xkcd.com/\-908/}",
    10721072  note = "[Online; accessed 25-August-2022]"
    10731073}
  • doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex

    r4520b77e ra065f1f  
    187187Looking at the left column on AMD, Figures~\ref{fig:cycle:nasus:ops} and \ref{fig:cycle:nasus:ns} all 4 runtimes achieve very similar throughput and scalability.
    188188However, as the number of \procs grows higher, the results on AMD show notably more variability than on Intel.
    189 The different performance improvements and plateaus are due to cache topology and appear at the expected: \proc counts of 64, 128 and 192, for the same reasons as on Intel.
     189The different performance improvements and plateaus are due to cache topology and appear at the expected \proc counts of 64, 128 and 192, for the same reasons as on Intel.
    190190Looking next at the right column on AMD, Figures~\ref{fig:cycle:nasus:low:ops} and \ref{fig:cycle:nasus:low:ns}, Tokio and Go have the same throughput performance, while \CFA is slightly slower.
    191191This result is different than on Intel, where Tokio behaved like \CFA rather than behaving like Go.
     
    491491\section{Locality}
    492492
    493 As mentioned in the churn benchmark, when \glslink{atsched}{unparking} a \at, it is possible to either \unpark to the local or remote ready-queue.\footnote{
    494 It is also possible to \unpark to a third unrelated ready-queue, but without additional knowledge about the situation, it is likely to degrade performance.}
     493As mentioned in the churn benchmark, when \glslink{atsched}{unparking} a \at, it is possible to either \unpark to the local or remote sub-queue.\footnote{
     494It is also possible to \unpark to a third unrelated sub-queue, but without additional knowledge about the situation, it is likely to degrade performance.}
    495495The locality experiment includes two variations of the churn benchmark, where a data array is added.
    496496In both variations, before @V@ing the semaphore, each \at calls a @work@ function which increments random cells inside the data array.
     
    720720
    721721In both variations, the experiment effectively measures how long it takes for all \ats to run once after a given synchronization point.
    722 In an ideal scenario where the scheduler is strictly FIFO, every thread would run once after the synchronization and therefore the delay between leaders would be given by, $(CSL + SL) / (NP - 1)$,
    723 where $CSL$ is the context-switch latency, $SL$ is the cost for enqueueing and dequeuing a \at, and $NP$ is the number of \procs.
     722In an ideal scenario where the scheduler is strictly FIFO, every thread would run once after the synchronization and therefore the delay between leaders would be given by, $NT(CSL + SL) / (NP - 1)$,
     723where $CSL$ is the context-switch latency, $SL$ is the cost for enqueueing and dequeuing a \at, $NT$ is the number of \ats, and $NP$ is the number of \procs.
    724724However, if the scheduler allows \ats to run many times before other \ats can run once, this delay increases.
    725725The semaphore version is an approximation of strictly FIFO scheduling, where none of the \ats \emph{attempt} to run more than once.
     
    734734
    735735\begin{table}
    736 \caption[Transfer Benchmark on Intel and AMD]{Transfer Benchmark on Intel and AMD\smallskip\newline Average measurement of how long it takes for all \ats to acknowledge the leader \at.
    737 DNC stands for ``did not complete'', meaning that after 5 seconds of a new leader being decided, some \ats still had not acknowledged the new leader.}
    738 \label{fig:transfer:res}
    739736\setlength{\extrarowheight}{2pt}
    740737\setlength{\tabcolsep}{5pt}
     
    753750\end{tabular}
    754751\end{centering}
     752\caption[Transfer Benchmark on Intel and AMD]{Transfer Benchmark on Intel and AMD\smallskip\newline Average measurement of how long it takes for all \ats to acknowledge the leader \at.
     753DNC stands for ``did not complete'', meaning that after 5 seconds of a new leader being decided, some \ats still had not acknowledged the new leader.}
     754\label{fig:transfer:res}
    755755\end{table}
    756756
     
    768768
    769769Looking at the next two columns, the results for the yield variation on Intel, the story is very different.
    770 \CFA achieves better latencies, presumably due to no synchronization with the yield.
     770\CFA achieves better latencies, presumably due to the lack of synchronization with the yield.
    771771Go does complete the experiment, but with drastically higher latency:
    772772latency at 2 \procs is $350\times$ higher than \CFA and $70\times$ higher at 192 \procs.
    773 This difference is because Go has a classic work-stealing scheduler, but it adds coarse-grain preemption
    774 , which interrupts the spinning leader after a period.
     773This difference is because Go has a classic work-stealing scheduler, but it adds coarse-grain preemption, which interrupts the spinning leader after a period.
    775774Neither Libfibre nor Tokio complete the experiment.
    776775Both runtimes also use classical work-stealing scheduling without preemption, and therefore, none of the work queues are ever emptied so no load balancing occurs.
  • doc/theses/thierry_delisle_PhD/thesis/text/io.tex

    r4520b77e ra065f1f  
    2525\paragraph{\lstinline{select}} is the oldest of these options, and takes as input a contiguous array of bits, where each bit represents a file descriptor of interest.
    2626Hence, the array length must be as long as the largest FD currently of interest.
    27 On return, it outputs the set in place to identify which of the file descriptors changed state.
     27On return, it outputs the set motified in place to identify which of the file descriptors changed state.
    2828This destructive change means selecting in a loop requires re-initializing the array for each iteration.
    2929Another limit of @select@ is that calls from different \glspl{kthrd} sharing FDs are independent.
     
    3535\paragraph{\lstinline{poll}} is the next oldest option, and takes as input an array of structures containing the FD numbers rather than their position in an array of bits, allowing a more compact input for interest sets that contain widely spaced FDs.
    3636For small interest sets with densely packed FDs, the @select@ bit mask can take less storage, and hence, copy less information into the kernel.
    37 Furthermore, @poll@ is non-destructive, so the array of structures does not have to be re-initialized on every call.
     37However, @poll@ is non-destructive, so the array of structures does not have to be re-initialized on every call.
    3838Like @select@, @poll@ suffers from the limitation that the interest set cannot be changed by other \glspl{kthrd}, while a manager thread is blocked in @poll@.
    3939
     
    314314A simple approach to polling is to allocate a \at per @io_uring@ instance and simply let the poller \ats poll their respective instances when scheduled.
    315315
    316 With the pool of SEQ instances approach, the big advantage is that it is fairly flexible.
     316With the pool of SQE instances approach, the big advantage is that it is fairly flexible.
    317317It does not impose restrictions on what \ats submitting \io operations can and cannot do between allocations and submissions.
    318318It also can gracefully handle running out of resources, SQEs or the kernel returning @EBUSY@.
     
    320320The routing and allocation algorithm needs to keep track of which ring instances have available SQEs, block incoming requests if no instance is available, prevent barging if \ats are already queued up waiting for SQEs and handle SQEs being freed.
    321321The submission side needs to safely append SQEs to the ring buffer, correctly handle chains, make sure no SQE is dropped or left pending forever, notify the allocation side when SQEs can be reused, and handle the kernel returning @EBUSY@.
    322 Compared to the private-instance approach, all this synchronization has a significant cost this synchronization is entirely overhead.
     322Compared to the private-instance approach, all this synchronization has a significant cost and this synchronization is entirely overhead.
    323323
    324324\subsubsection{Instance borrowing}
  • doc/theses/thierry_delisle_PhD/thesis/text/practice.tex

    r4520b77e ra065f1f  
    101101This leaves too many \procs when there are not enough \ats for all the \procs to be useful.
    102102These idle \procs cannot be removed because their lifetime is controlled by the application, and only the application knows when the number of \ats may increase or decrease.
    103 While idle \procs can spin until work appears, this approach wastes energy, unnecessarily produces heat and prevents other applications from using the processor.
     103While idle \procs can spin until work appears, this approach wastes energy, unnecessarily produces heat and prevents other applications from using the \gls{hthrd}.
    104104Therefore, idle \procs are put into an idle state, called \newterm{Idle-Sleep}, where the \gls{kthrd} is blocked until the scheduler deems it is needed.
    105105
     
    107107First, a data structure needs to keep track of all \procs that are in idle sleep.
    108108Because idle sleep is spurious, this data structure has strict performance requirements, in addition to strict correctness requirements.
    109 Next, some mechanism is needed to block \glspl{kthrd}, \eg @pthread_cond_wait@ on a pthread semaphore.
     109Next, some mechanism is needed to block \glspl{kthrd}, \eg @pthread_cond_wait@ or a pthread semaphore.
    110110The complexity here is to support \at \glslink{atblock}{parking} and \glslink{atsched}{unparking}, user-level locking, timers, \io operations, and all other \CFA features with minimal complexity.
    111111Finally, the scheduler needs a heuristic to determine when to block and unblock an appropriate number of \procs.
  • libcfa/src/bits/locks.hfa

    r4520b77e ra065f1f  
    1313// Created On       : Tue Oct 31 15:14:38 2017
    1414// Last Modified By : Peter A. Buhr
    15 // Last Modified On : Wed Aug 12 14:18:07 2020
    16 // Update Count     : 13
     15// Last Modified On : Mon Sep 19 18:51:53 2022
     16// Update Count     : 17
    1717//
    1818
     
    6060
    6161                disable_interrupts();
    62                 for ( unsigned int i = 1;; i += 1 ) {
     62                for ( i; 1 ~ @ ) {
    6363                        if ( (this.lock == 0) && (__atomic_test_and_set( &this.lock, __ATOMIC_ACQUIRE ) == 0) ) break;
    6464                        #ifndef NOEXPBACK
    6565                                // exponential spin
    66                                 for ( volatile unsigned int s = 0; s < spin; s += 1 ) Pause();
     66                        for ( volatile unsigned int s; 0 ~ spin ) Pause();
    6767
    6868                                // slowly increase by powers of 2
  • libcfa/src/concurrency/kernel/cluster.hfa

    r4520b77e ra065f1f  
    6363                }
    6464        }
    65         return (max + 2 * max) / 2;
     65        return 8 * max;
    6666}
    6767
  • libcfa/src/concurrency/kernel/fwd.hfa

    r4520b77e ra065f1f  
    179179                // Similar to a binary semaphore with a 'one shot' semantic
    180180                // is expected to be discarded after each party call their side
     181                enum(struct thread$ *) { oneshot_ARMED = 0p, oneshot_FULFILLED = 1p };
    181182                struct oneshot {
    182183                        // Internal state :
    183                         //     0p     : is initial state (wait will block)
    184                         //     1p     : fulfilled (wait won't block)
     184                        // armed      : initial state, wait will block
     185                        // fulfilled  : wait won't block
    185186                        // any thread : a thread is currently waiting
    186187                        struct thread$ * volatile ptr;
     
    189190                static inline {
    190191                        void  ?{}(oneshot & this) {
    191                                 this.ptr = 0p;
     192                                this.ptr = oneshot_ARMED;
    192193                        }
    193194
     
    199200                                for() {
    200201                                        struct thread$ * expected = this.ptr;
    201                                         if(expected == 1p) return false;
     202                                        if(expected == oneshot_FULFILLED) return false;
    202203                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, active_thread(), false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
    203204                                                park();
    204                                                 /* paranoid */ verify( this.ptr == 1p );
     205                                                /* paranoid */ verify( this.ptr == oneshot_FULFILLED );
    205206                                                return true;
    206207                                        }
     
    211212                        // return true if a thread was unparked
    212213                        thread$ * post(oneshot & this, bool do_unpark = true) {
    213                                 struct thread$ * got = __atomic_exchange_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
    214                                 if( got == 0p || got == 1p ) return 0p;
     214                                struct thread$ * got = __atomic_exchange_n( &this.ptr, oneshot_FULFILLED, __ATOMIC_SEQ_CST);
     215                                if( got == oneshot_ARMED || got == oneshot_FULFILLED ) return 0p;
    215216                                if(do_unpark) unpark( got );
    216217                                return got;
     
    223224                // thread on "any of" [a given set of] futures.
    224225                // does not support multiple threads waiting on the same future
     226                enum(struct oneshot *) { future_ARMED = 0p, future_FULFILLED = 1p, future_PROGRESS = 2p, future_ABANDONED = 3p };
    225227                struct future_t {
    226228                        // Internal state :
    227                         //     0p      : is initial state (wait will block)
    228                         //     1p      : fulfilled (wait won't block)
    229                         //     2p      : in progress ()
    230                         //     3p      : abandoned, server should delete
     229                        // armed       : initial state, wait will block
     230                        // fulfilled   : result is ready, wait won't block
     231                        // progress    : someone else is in the process of fulfilling this
     232                        // abandoned   : client no longer cares, server should delete
    231233                        // any oneshot : a context has been setup to wait, a thread could wait on it
    232234                        struct oneshot * volatile ptr;
     
    235237                static inline {
    236238                        void  ?{}(future_t & this) {
    237                                 this.ptr = 0p;
     239                                this.ptr = future_ARMED;
    238240                        }
    239241
     
    242244                        void reset(future_t & this) {
    243245                                // needs to be in 0p or 1p
    244                                 __atomic_exchange_n( &this.ptr, 0p, __ATOMIC_SEQ_CST);
     246                                __atomic_exchange_n( &this.ptr, future_ARMED, __ATOMIC_SEQ_CST);
    245247                        }
    246248
    247249                        // check if the future is available
    248250                        bool available( future_t & this ) {
    249                                 while( this.ptr == 2p ) Pause();
    250                                 return this.ptr == 1p;
     251                                while( this.ptr == future_PROGRESS ) Pause();
     252                                return this.ptr == future_FULFILLED;
    251253                        }
    252254
     
    254256                        // intented to be use by wait, wait_any, waitfor, etc. rather than used directly
    255257                        bool setup( future_t & this, oneshot & wait_ctx ) {
    256                                 /* paranoid */ verify( wait_ctx.ptr == 0p || wait_ctx.ptr == 1p );
     258                                /* paranoid */ verify( wait_ctx.ptr == oneshot_ARMED || wait_ctx.ptr == oneshot_FULFILLED );
    257259                                // The future needs to set the wait context
    258260                                for() {
    259261                                        struct oneshot * expected = this.ptr;
    260262                                        // Is the future already fulfilled?
    261                                         if(expected == 1p) return false; // Yes, just return false (didn't block)
     263                                        if(expected == future_FULFILLED) return false; // Yes, just return false (didn't block)
    262264
    263265                                        // The future is not fulfilled, try to setup the wait context
     
    277279
    278280                                // attempt to remove the context so it doesn't get consumed.
    279                                 if(__atomic_compare_exchange_n( &this.ptr, &expected, 0p, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
     281                                if(__atomic_compare_exchange_n( &this.ptr, &expected, future_ARMED, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
    280282                                        // we still have the original context, then no one else saw it
    281283                                        return false;
    282284                                }
    283285
    284                                 // expected == 0p: future was never actually setup, just return
    285                                 if( expected == 0p ) return false;
    286 
    287                                 // expected == 1p: the future is ready and the context was fully consumed
     286                                // expected == ARMED: future was never actually setup, just return
     287                                if( expected == future_ARMED ) return false;
     288
     289                                // expected == FULFILLED: the future is ready and the context was fully consumed
    288290                                // the server won't use the pointer again
    289291                                // It is safe to delete (which could happen after the return)
    290                                 if( expected == 1p ) return true;
    291 
    292                                 // expected == 2p: the future is ready but the context hasn't fully been consumed
     292                                if( expected == future_FULFILLED ) return true;
     293
     294                                // expected == PROGRESS: the future is ready but the context hasn't fully been consumed
    293295                                // spin until it is safe to move on
    294                                 if( expected == 2p ) {
    295                                         while( this.ptr != 1p ) Pause();
    296                                         /* paranoid */ verify( this.ptr == 1p );
     296                                if( expected == future_PROGRESS ) {
     297                                        while( this.ptr != future_FULFILLED ) Pause();
     298                                        /* paranoid */ verify( this.ptr == future_FULFILLED );
    297299                                        return true;
    298300                                }
     
    305307                        // Mark the future as abandoned, meaning it will be deleted by the server
    306308                        bool abandon( future_t & this ) {
    307                                 /* paranoid */ verify( this.ptr != 3p );
     309                                /* paranoid */ verify( this.ptr != future_ABANDONED );
    308310
    309311                                // Mark the future as abandonned
    310                                 struct oneshot * got = __atomic_exchange_n( &this.ptr, 3p, __ATOMIC_SEQ_CST);
     312                                struct oneshot * got = __atomic_exchange_n( &this.ptr, future_ABANDONED, __ATOMIC_SEQ_CST);
    311313
    312314                                // If the future isn't already fulfilled, let the server delete it
    313                                 if( got == 0p ) return false;
    314 
    315                                 // got == 2p: the future is ready but the context hasn't fully been consumed
     315                                if( got == future_ARMED ) return false;
     316
     317                                // got == PROGRESS: the future is ready but the context hasn't fully been consumed
    316318                                // spin until it is safe to move on
    317                                 if( got == 2p ) {
    318                                         while( this.ptr != 1p ) Pause();
    319                                         got = 1p;
     319                                if( got == future_PROGRESS ) {
     320                                        while( this.ptr != future_FULFILLED ) Pause();
     321                                        got = future_FULFILLED;
    320322                                }
    321323
    322324                                // The future is completed delete it now
    323                                 /* paranoid */ verify( this.ptr != 1p );
     325                                /* paranoid */ verify( this.ptr != future_FULFILLED );
    324326                                free( &this );
    325327                                return true;
     
    336338                                                #pragma GCC diagnostic ignored "-Wfree-nonheap-object"
    337339                                        #endif
    338                                                 if( expected == 3p ) { free( &this ); return 0p; }
     340                                                if( expected == future_ABANDONED ) { free( &this ); return 0p; }
    339341                                        #if defined(__GNUC__) && __GNUC__ >= 7
    340342                                                #pragma GCC diagnostic pop
    341343                                        #endif
    342344
    343                                         /* paranoid */ verify( expected != 1p ); // Future is already fulfilled, should not happen
    344                                         /* paranoid */ verify( expected != 2p ); // Future is bein fulfilled by someone else, this is even less supported then the previous case.
     345                                        /* paranoid */ verify( expected != future_FULFILLED ); // Future is already fulfilled, should not happen
     346                                        /* paranoid */ verify( expected != future_PROGRESS ); // Future is bein fulfilled by someone else, this is even less supported then the previous case.
    345347
    346348                                        // If there is a wait context, we need to consume it and mark it as consumed after
    347349                                        // If there is no context then we can skip the in progress phase
    348                                         struct oneshot * want = expected == 0p ? 1p : 2p;
     350                                        struct oneshot * want = expected == future_ARMED ? future_FULFILLED : future_PROGRESS;
    349351                                        if(__atomic_compare_exchange_n(&this.ptr, &expected, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
    350                                                 if( expected == 0p ) { return 0p; }
     352                                                if( expected == future_ARMED ) { return 0p; }
    351353                                                thread$ * ret = post( *expected, do_unpark );
    352                                                 __atomic_store_n( &this.ptr, 1p, __ATOMIC_SEQ_CST);
     354                                                __atomic_store_n( &this.ptr, future_FULFILLED, __ATOMIC_SEQ_CST);
    353355                                                return ret;
    354356                                        }
     
    366368
    367369                                // Wait for the future to tru
    368                                 while( this.ptr == 2p ) Pause();
     370                                while( this.ptr == future_PROGRESS ) Pause();
    369371                                // Make sure the state makes sense
    370372                                // Should be fulfilled, could be in progress but it's out of date if so
     
    372374                                // and the oneshot should not be needed any more
    373375                                __attribute__((unused)) struct oneshot * was = this.ptr;
    374                                 /* paranoid */ verifyf( was == 1p, "Expected this.ptr to be 1p, was %p\n", was );
     376                                /* paranoid */ verifyf( was == future_FULFILLED, "Expected this.ptr to be 1p, was %p\n", was );
    375377
    376378                                // Mark the future as fulfilled, to be consistent
  • libcfa/src/concurrency/monitor.hfa

    r4520b77e ra065f1f  
    6060void ^?{}( monitor_dtor_guard_t & this );
    6161
     62/*
    6263static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } )
    6364void delete( T * th ) {
     
    6566        free( th );
    6667}
     68*/
    6769
    6870static inline forall( T & | sized(T) | { void ^?{}( T & mutex ); } )
  • libcfa/src/iostream.cfa

    r4520b77e ra065f1f  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Aug 25 18:05:49 2022
    13 // Update Count     : 1354
     12// Last Modified On : Sat Aug 27 15:04:15 2022
     13// Update Count     : 1358
    1414//
    1515
     
    765765                        fmtuc.flags.pc = f.flags.pc;
    766766                        fmtuc.flags.nobsdp = f.flags.nobsdp;
    767                         for ( unsigned int i = 0; f.val[i] != '\0'; i += 1 ) {
     767                        for ( i; 0 ~ @ : @; f.val[i] != '\0' ) {
    768768                                fmtuc.val = f.val[i];
    769769//                              os | fmtuc | nonl;
     
    931931                if ( fmt( is, "%39[0-9]%*[0-9]", s ) == 1 ) {   // take first 39 characters, ignore remaining
    932932                        ullli = 0;
    933                         for ( unsigned int i = 0; s[i] != '\0'; i += 1 ) {
     933                        for ( i; 0 ~ @ : @; s[i] != '\0' ) {
    934934                                ullli = ullli * 10 + s[i] - '0';
    935935                        } // for
  • src/AST/Print.cpp

    r4520b77e ra065f1f  
    9090
    9191                static constexpr auto Qualifiers = make_array<const char*>(
    92                         "const", "restrict", "volatile", "lvalue", "mutex", "_Atomic"
     92                        "const", "restrict", "volatile", "mutex", "_Atomic"
    9393                );
    9494        };
     
    16351635constexpr array<const char*, 3> Printer::Names::FuncSpecifiers;
    16361636constexpr array<const char*, 6> Printer::Names::StorageClasses;
    1637 constexpr array<const char*, 6> Printer::Names::Qualifiers;
     1637constexpr array<const char*, 5> Printer::Names::Qualifiers;
    16381638}
  • src/AST/Type.hpp

    r4520b77e ra065f1f  
    412412                std::string typeString() const { return std::string("_") + std::to_string(formal_usage) + "_" + std::to_string(expr_id) + "_" + base->name; }
    413413                bool operator==(const TypeEnvKey & other) const { return base == other.base && formal_usage == other.formal_usage && expr_id == other.expr_id; }
    414 
    415414        };
    416415
  • src/AST/TypeEnvironment.hpp

    r4520b77e ra065f1f  
    5656struct AssertCompare {
    5757        bool operator()( const VariableExpr * d1, const VariableExpr * d2 ) const {
     58                auto kind1 = ast::SymbolTable::getSpecialFunctionKind(d1->var->name);
     59                auto kind2 = ast::SymbolTable::getSpecialFunctionKind(d2->var->name);
     60                // heuristics optimization: force special functions to go last
     61                if (kind1 > kind2) return true;
     62                else if (kind1 < kind2) return false;
     63
    5864                int cmp = d1->var->name.compare( d2->var->name );
    5965                return cmp < 0 || ( cmp == 0 && d1->result < d2->result );
  • src/AST/module.mk

    r4520b77e ra065f1f  
    2424        AST/Copy.cpp \
    2525        AST/Copy.hpp \
     26        AST/Create.cpp \
     27        AST/Create.hpp \
    2628        AST/CVQualifiers.hpp \
    2729        AST/Decl.cpp \
  • src/CodeGen/CodeGenerator.cc

    r4520b77e ra065f1f  
    494494                                        assert( false );
    495495                                } // switch
     496                        } else if( varExpr->get_var()->get_linkage() == LinkageSpec::BuiltinCFA && varExpr->get_var()->get_name() == "intptr" ) {
     497                                // THIS is a hack to make it a constant until a proper constexpr solution is created
     498                                output << "((void*)";
     499                                std::list< Expression* >::iterator arg = applicationExpr->get_args().begin();
     500                                (*arg++)->accept( *visitor );
     501                                output << ")";
    496502                        } else {
    497503                                varExpr->accept( *visitor );
  • src/Concurrency/Waitfor.cc

    r4520b77e ra065f1f  
    402402
    403403                clause.target.function = nullptr;
    404                 clause.target.arguments.empty();
     404                clause.target.arguments.clear();
    405405                clause.condition = nullptr;
    406406        }
  • src/GenPoly/InstantiateGenericNew.cpp

    r4520b77e ra065f1f  
    2222
    2323#include "AST/Copy.hpp"                // for deepCopy
     24#include "AST/Create.hpp"              // for asForward
    2425#include "AST/Pass.hpp"                // for Pass, WithGuard, WithShortCi...
    2526#include "AST/TranslationUnit.hpp"     // for TranslationUnit
     
    255256void stripInstParams( ast::BaseInstType * inst ) {
    256257        inst->params.clear();
    257 }
    258 
    259 // TODO: I think this should become a generic helper.
    260 template<typename Aggr>
    261 Aggr * asForward( Aggr const * decl ) {
    262         if ( !decl->body ) {
    263                 return nullptr;
    264         }
    265         Aggr * mut = ast::deepCopy( decl );
    266         mut->body = false;
    267         mut->members.clear();
    268         return mut;
    269258}
    270259
     
    553542                        // Forward declare before recursion. (TODO: Only when needed, #199.)
    554543                        insert( inst, typeSubs, newDecl );
    555                         if ( AggrDecl const * forwardDecl = asForward( newDecl ) ) {
     544                        if ( AggrDecl const * forwardDecl = ast::asForward( newDecl ) ) {
    556545                                declsToAddBefore.push_back( forwardDecl );
    557546                        }
  • src/GenPoly/Lvalue2.cc

    r4520b77e ra065f1f  
    2323}
    2424
    25 
    2625}
  • src/ResolvExpr/CommonType.cc

    r4520b77e ra065f1f  
    2828#include "Unify.h"                       // for unifyExact, WidenMode
    2929#include "typeops.h"                     // for isFtype
     30#include "Tuples/Tuples.h"
    3031
    3132// #define DEBUG
     
    675676                ast::TypeEnvironment & tenv;
    676677                const ast::OpenVarSet & open;
     678                ast::AssertionSet & need;
     679                ast::AssertionSet & have;
    677680        public:
    678681                static size_t traceId;
     
    681684                CommonType_new(
    682685                        const ast::Type * t2, WidenMode w, const ast::SymbolTable & st,
    683                         ast::TypeEnvironment & env, const ast::OpenVarSet & o )
    684                 : type2( t2 ), widen( w ), symtab( st ), tenv( env ), open( o ), result() {}
     686                        ast::TypeEnvironment & env, const ast::OpenVarSet & o,
     687                        ast::AssertionSet & need, ast::AssertionSet & have )
     688                : type2( t2 ), widen( w ), symtab( st ), tenv( env ), open( o ), need (need), have (have) ,result() {}
    685689
    686690                void previsit( const ast::Node * ) { visit_children = false; }
     
    753757                bool tryResolveWithTypedEnum( const ast::Type * type1 ) {
    754758                        if (auto enumInst = dynamic_cast<const ast::EnumInstType *> (type2) ) {
    755                                 ast::AssertionSet have, need; // unused
    756759                                ast::OpenVarSet newOpen{ open };
    757760                                if (enumInst->base->base
     
    792795                                        reset_qualifiers( t2 );
    793796
    794                                         ast::AssertionSet have, need;
    795797                                        ast::OpenVarSet newOpen{ open };
    796798                                        if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) {
     
    803805                                                }
    804806                                        }
     807                                        else if ( isFtype (t1) && isFtype (t2) ) {
     808                                                auto f1 = t1.as<ast::FunctionType>();
     809                                                if (!f1) return;
     810                                                auto f2 = t2.strict_as<ast::FunctionType>();
     811
     812                                                assertf(f1->returns.size() <= 1, "Function return should not be a list");
     813                                                assertf(f2->returns.size() <= 1, "Function return should not be a list");
     814
     815                                                if (
     816                                                        ( f1->params.size() != f2->params.size() || f1->returns.size() != f2->returns.size() )
     817                                                        && ! f1->isTtype()
     818                                                        && ! f2->isTtype()
     819                                                ) return;
     820
     821                                                auto params1 = flattenList( f1->params, tenv );
     822                                                auto params2 = flattenList( f2->params, tenv );
     823
     824                                                auto crnt1 = params1.begin();
     825                                                auto crnt2 = params2.begin();
     826                                                auto end1 = params1.end();
     827                                                auto end2 = params2.end();
     828
     829                                                while (crnt1 != end1 && crnt2 != end2 ) {
     830                                                        const ast::Type * arg1 = *crnt1;
     831                                                        const ast::Type * arg2 = *crnt2;
     832
     833                                                        bool isTuple1 = Tuples::isTtype( t1 );
     834                                                        bool isTuple2 = Tuples::isTtype( t2 );
     835
     836                                                        // assumes here that ttype *must* be last parameter
     837                                                        if ( isTuple1 && ! isTuple2 ) {
     838                                                                // combine remainder of list2, then unify
     839                                                                if (unifyExact(
     840                                                                        arg1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open,
     841                                                                        noWiden(), symtab )) {
     842                                                                                break;
     843
     844                                                                }
     845                                                                else return;
     846                                                        } else if ( ! isTuple1 && isTuple2 ) {
     847                                                                // combine remainder of list1, then unify
     848                                                                if (unifyExact(
     849                                                                        tupleFromTypes( crnt1, end1 ), arg2, tenv, need, have, open,
     850                                                                        noWiden(), symtab )) {
     851                                                                                break;
     852
     853                                                                }
     854                                                                else return;
     855                                                        }
     856
     857                                                        // allow qualifiers of pointer and reference base to become more specific
     858                                                        if (auto ref1 = dynamic_cast<const ast::ReferenceType *> (arg1)) {
     859                                                                if (auto ref2 = dynamic_cast<const ast::ReferenceType *> (arg2)) {
     860                                                                        ast::ptr<ast::Type> base1 = ref1->base;
     861                                                                        ast::ptr<ast::Type> base2 = ref2->base;
     862
     863                                                                        // xxx - assume LHS is always the target type
     864
     865                                                                        if ( ! ((widen.second && ref2->qualifiers.is_mutex)
     866                                                                        || (ref1->qualifiers.is_mutex == ref2->qualifiers.is_mutex ))) return;
     867
     868                                                                        if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) {
     869
     870                                                                                reset_qualifiers(base1);
     871                                                                                reset_qualifiers(base2);
     872
     873                                                                                if ( ! unifyExact(
     874                                                                                        base1, base2, tenv, need, have, open, noWiden(), symtab )
     875                                                                                ) return;
     876                                                                        }       
     877                                                                }
     878                                                                else return;
     879                                                        }
     880                                                        else if (auto ptr1 = dynamic_cast<const ast::PointerType *> (arg1)) {
     881                                                                if (auto ptr2 = dynamic_cast<const ast::PointerType *> (arg2)) {
     882                                                                        ast::ptr<ast::Type> base1 = ptr1->base;
     883                                                                        ast::ptr<ast::Type> base2 = ptr2->base;
     884
     885                                                                        // xxx - assume LHS is always the target type
     886                                                                        // a function accepting const can always be called by non-const arg
     887
     888                                                                        if ( (widen.second && base1->qualifiers <= base2->qualifiers ) || (base2->qualifiers == base1->qualifiers) ) {
     889
     890                                                                                reset_qualifiers(base1);
     891                                                                                reset_qualifiers(base2);
     892
     893                                                                                if ( ! unifyExact(
     894                                                                                        base1, base2, tenv, need, have, open, noWiden(), symtab )
     895                                                                                ) return;
     896                                                                        }       
     897                                                                }
     898                                                                else return;
     899
     900                                                        }
     901                                                        else if (! unifyExact(
     902                                                                arg1, arg2, tenv, need, have, open, noWiden(), symtab )) return;
     903
     904                                                        ++crnt1; ++crnt2;
     905                                                }
     906                                                if ( crnt1 != end1 ) {
     907                                                        // try unifying empty tuple with ttype
     908                                                        const ast::Type * t1 = *crnt1;
     909                                                        if (! Tuples::isTtype( t1 ) ) return;
     910                                                        if (! unifyExact(
     911                                                                t1, tupleFromTypes( crnt2, end2 ), tenv, need, have, open,
     912                                                                noWiden(), symtab )) return;
     913                                                } else if ( crnt2 != end2 ) {
     914                                                        // try unifying empty tuple with ttype
     915                                                        const ast::Type * t2 = *crnt2;
     916                                                        if (! Tuples::isTtype( t2 ) ) return;
     917                                                        if (! unifyExact(
     918                                                                tupleFromTypes( crnt1, end1 ), t2, tenv, need, have, open,
     919                                                                noWiden(), symtab )) return;
     920                                                }
     921                                                if ((f1->returns.size() == 0 && f2->returns.size() == 0)
     922                                                  || (f1->returns.size() == 1 && f2->returns.size() == 1 && unifyExact(f1->returns[0], f2->returns[0], tenv, need, have, open, noWiden(), symtab))) {
     923                                                        result = pointer;
     924
     925                                                        for (auto & assn : f1->assertions) {
     926                                                                auto i = need.find(assn);
     927                                                                if (i != need.end()) i->second.isUsed = true;
     928                                                                auto j = have.find(assn);
     929                                                                if (j != have.end()) j->second.isUsed = true;
     930                                                        }
     931
     932                                                        for (auto & assn : f2->assertions) {
     933                                                                auto i = need.find(assn);
     934                                                                if (i != need.end()) i->second.isUsed = true;
     935                                                                auto j = have.find(assn);
     936                                                                if (j != have.end()) j->second.isUsed = true;
     937                                                        }
     938
     939                                                }
     940                                        } // if ftype
     941                                       
    805942                                }
    806943                        } else if ( widen.second && dynamic_cast< const ast::ZeroType * >( type2 ) ) {
     
    839976                                        reset_qualifiers( t2 );
    840977
    841                                         ast::AssertionSet have, need;
    842978                                        ast::OpenVarSet newOpen{ open };
    843979                                        if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) {
     
    857993                                // xxx - does unifying a ref with typed enumInst makes sense?
    858994                                if (!dynamic_cast<const ast::EnumInstType *>(type2))
    859                                         result = commonType( type2, ref, widen, symtab, tenv, open );
     995                                        result = commonType( type2, ref, tenv, need, have, open, widen, symtab );
    860996                        }
    861997                }
     
    8771013                        // xxx - is this already handled by unify?
    8781014                        if (!dynamic_cast<const ast::EnumInstType *>(type2))
    879                                 result = commonType( type2, enumInst, widen, symtab, tenv, open );
     1015                                result = commonType( type2, enumInst, tenv, need, have, open, widen, symtab);
    8801016                }
    8811017
     
    8951031                                        reset_qualifiers( t2 );
    8961032
    897                                         ast::AssertionSet have, need;
    8981033                                        ast::OpenVarSet newOpen{ open };
    8991034                                        if ( unifyExact( t1, t2, tenv, have, need, newOpen, noWiden(), symtab ) ) {
     
    9991134        ast::ptr< ast::Type > commonType(
    10001135                        const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2,
    1001                         WidenMode widen, const ast::SymbolTable & symtab, ast::TypeEnvironment & env,
    1002                         const ast::OpenVarSet & open
     1136                        ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
     1137                        const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab
    10031138        ) {
    10041139                unsigned depth1 = type1->referenceDepth();
     
    10361171                }
    10371172                // otherwise both are reference types of the same depth and this is handled by the visitor
    1038                 ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open };
     1173                ast::Pass<CommonType_new> visitor{ type2, widen, symtab, env, open, need, have };
    10391174                type1->accept( visitor );
    10401175                ast::ptr< ast::Type > result = visitor.core.result;
     
    10471182                                        if ( type->base ) {
    10481183                                                ast::CV::Qualifiers q1 = type1->qualifiers, q2 = type2->qualifiers;
    1049                                                 ast::AssertionSet have, need;
    10501184                                                ast::OpenVarSet newOpen{ open };
    10511185
  • src/ResolvExpr/ConversionCost.cc

    r4520b77e ra065f1f  
    2222#include "ResolvExpr/Cost.h"             // for Cost
    2323#include "ResolvExpr/TypeEnvironment.h"  // for EqvClass, TypeEnvironment
     24#include "ResolvExpr/Unify.h"
    2425#include "SymTab/Indexer.h"              // for Indexer
    2526#include "SynTree/Declaration.h"         // for TypeDecl, NamedTypeDecl
    2627#include "SynTree/Type.h"                // for Type, BasicType, TypeInstType
    2728#include "typeops.h"                     // for typesCompatibleIgnoreQualifiers
     29
    2830
    2931namespace ResolvExpr {
     
    657659                                cost = Cost::safe;
    658660                        }
    659                 } else {
     661                }
     662                /*
     663                else if ( const ast::FunctionType * dstFunc = dstAsPtr->base.as<ast::FunctionType>()) {
     664                        if (const ast::FunctionType * srcFunc = pointerType->base.as<ast::FunctionType>()) {
     665                                if (dstFunc->params.empty() && dstFunc->isVarArgs ) {
     666                                        cost = Cost::unsafe; // assign any function to variadic fptr
     667                                }
     668                        }
     669                        else {
     670                                ast::AssertionSet need, have; // unused
     671                                ast::OpenVarSet open;
     672                                env.extractOpenVars(open);
     673                                ast::TypeEnvironment tenv = env;
     674                                if ( unify(dstAsPtr->base, pointerType->base, tenv, need, have, open, symtab) ) {
     675                                        cost = Cost::safe;
     676                                }
     677                        }
     678                        // else infinity
     679                }
     680                */
     681                else {
    660682                        int assignResult = ptrsAssignable( pointerType->base, dstAsPtr->base, env );
    661683                        if ( 0 < assignResult && tq1 <= tq2 ) {
  • src/ResolvExpr/SatisfyAssertions.cpp

    r4520b77e ra065f1f  
    3636#include "AST/SymbolTable.hpp"
    3737#include "AST/TypeEnvironment.hpp"
     38#include "FindOpenVars.h"
    3839#include "Common/FilterCombos.h"
    3940#include "Common/Indenter.h"
     
    161162
    162163        /// Satisfy a single assertion
    163         bool satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat ) {
     164        bool satisfyAssertion( ast::AssertionList::value_type & assn, SatState & sat, bool allowConversion = false, bool skipUnbound = false) {
    164165                // skip unused assertions
    165166                if ( ! assn.second.isUsed ) return true;
     
    180181                        if (thisArgType.as<ast::PointerType>()) otypeKey = Mangle::Encoding::pointer;
    181182                        else if (!isUnboundType(thisArgType)) otypeKey = Mangle::mangle(thisArgType, Mangle::Type | Mangle::NoGenericParams);
     183                        else if (skipUnbound) return false;
    182184
    183185                        candidates = sat.symtab.specialLookupId(kind, otypeKey);
     
    205207
    206208                        // only keep candidates which unify
    207                         if ( unify( toType, adjType, newEnv, newNeed, have, newOpen, sat.symtab ) ) {
    208                                 // set up binding slot for recursive assertions
    209                                 ast::UniqueId crntResnSlot = 0;
    210                                 if ( ! newNeed.empty() ) {
    211                                         crntResnSlot = ++globalResnSlot;
    212                                         for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; }
    213                                 }
    214 
    215                                 matches.emplace_back(
    216                                         cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
    217                                         std::move( newOpen ), crntResnSlot );
     209
     210                        ast::OpenVarSet closed;
     211                        findOpenVars( toType, newOpen, closed, newNeed, have, FirstClosed );
     212                        findOpenVars( adjType, newOpen, closed, newNeed, have, FirstOpen );
     213                        if ( allowConversion ) {
     214                                if ( auto c = commonType( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true}, sat.symtab ) ) {
     215                                        // set up binding slot for recursive assertions
     216                                        ast::UniqueId crntResnSlot = 0;
     217                                        if ( ! newNeed.empty() ) {
     218                                                crntResnSlot = ++globalResnSlot;
     219                                                for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; }
     220                                        }
     221
     222                                        matches.emplace_back(
     223                                                cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
     224                                                std::move( newOpen ), crntResnSlot );
     225                                }
     226                        }
     227                        else {
     228                                if ( unifyExact( toType, adjType, newEnv, newNeed, have, newOpen, WidenMode {true, true}, sat.symtab ) ) {
     229                                        // set up binding slot for recursive assertions
     230                                        ast::UniqueId crntResnSlot = 0;
     231                                        if ( ! newNeed.empty() ) {
     232                                                crntResnSlot = ++globalResnSlot;
     233                                                for ( auto & a : newNeed ) { a.second.resnSlot = crntResnSlot; }
     234                                        }
     235
     236                                        matches.emplace_back(
     237                                                cdata, adjType, std::move( newEnv ), std::move( have ), std::move( newNeed ),
     238                                                std::move( newOpen ), crntResnSlot );
     239                                }
    218240                        }
    219241                }
     
    413435                // for each current mutually-compatible set of assertions
    414436                for ( SatState & sat : sats ) {
     437                        bool allowConversion = false;
    415438                        // stop this branch if a better option is already found
    416439                        auto it = thresholds.find( pruneKey( *sat.cand ) );
     
    418441
    419442                        // should a limit be imposed? worst case here is O(n^2) but very unlikely to happen.
     443
    420444                        for (unsigned resetCount = 0; ; ++resetCount) {
    421445                                ast::AssertionList next;
     
    424448                                for ( auto & assn : sat.need ) {
    425449                                        // fail early if any assertion is not satisfiable
    426                                         if ( ! satisfyAssertion( assn, sat ) ) {
     450                                        if ( ! satisfyAssertion( assn, sat, allowConversion, !next.empty() ) ) {
    427451                                                next.emplace_back(assn);
    428452                                                // goto nextSat;
     
    433457                                // fail if nothing resolves
    434458                                else if (next.size() == sat.need.size()) {
    435                                         Indenter tabs{ 3 };
    436                                         std::ostringstream ss;
    437                                         ss << tabs << "Unsatisfiable alternative:\n";
    438                                         print( ss, *sat.cand, ++tabs );
    439                                         ss << (tabs-1) << "Could not satisfy assertion:\n";
    440                                         ast::print( ss, next[0].first, tabs );
    441 
    442                                         errors.emplace_back( ss.str() );
    443                                         goto nextSat;
    444                                 }
     459                                        if (allowConversion) {
     460                                                Indenter tabs{ 3 };
     461                                                std::ostringstream ss;
     462                                                ss << tabs << "Unsatisfiable alternative:\n";
     463                                                print( ss, *sat.cand, ++tabs );
     464                                                ss << (tabs-1) << "Could not satisfy assertion:\n";
     465                                                ast::print( ss, next[0].first, tabs );
     466
     467                                                errors.emplace_back( ss.str() );
     468                                                goto nextSat;
     469                                        }
     470
     471                                        else {
     472                                                allowConversion = true;
     473                                                continue;
     474                                        }
     475                                }
     476                                allowConversion = false;
    445477                                sat.need = std::move(next);
    446478                        }
  • src/ResolvExpr/Unify.cc

    r4520b77e ra065f1f  
    707707        }
    708708
     709        namespace {
     710                                /// Replaces ttype variables with their bound types.
     711                /// If this isn't done when satifying ttype assertions, then argument lists can have
     712                /// different size and structure when they should be compatible.
     713                struct TtypeExpander_new : public ast::WithShortCircuiting, public ast::PureVisitor {
     714                        ast::TypeEnvironment & tenv;
     715
     716                        TtypeExpander_new( ast::TypeEnvironment & env ) : tenv( env ) {}
     717
     718                        const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
     719                                if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {
     720                                        // expand ttype parameter into its actual type
     721                                        if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {
     722                                                return clz->bound;
     723                                        }
     724                                }
     725                                return typeInst;
     726                        }
     727                };
     728        }
     729       
     730        std::vector< ast::ptr< ast::Type > > flattenList(
     731                const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env
     732        ) {
     733                std::vector< ast::ptr< ast::Type > > dst;
     734                dst.reserve( src.size() );
     735                for ( const auto & d : src ) {
     736                        ast::Pass<TtypeExpander_new> expander{ env };
     737                        // TtypeExpander pass is impure (may mutate nodes in place)
     738                        // need to make nodes shared to prevent accidental mutation
     739                        ast::ptr<ast::Type> dc = d->accept(expander);
     740                        auto types = flatten( dc );
     741                        for ( ast::ptr< ast::Type > & t : types ) {
     742                                // outermost const, volatile, _Atomic qualifiers in parameters should not play
     743                                // a role in the unification of function types, since they do not determine
     744                                // whether a function is callable.
     745                                // NOTE: **must** consider at least mutex qualifier, since functions can be
     746                                // overloaded on outermost mutex and a mutex function has different
     747                                // requirements than a non-mutex function
     748                                remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
     749                                dst.emplace_back( t );
     750                        }
     751                }
     752                return dst;
     753        }
     754
    709755        class Unify_new final : public ast::WithShortCircuiting {
    710756                const ast::Type * type2;
     
    778824
    779825        private:
    780                 /// Replaces ttype variables with their bound types.
    781                 /// If this isn't done when satifying ttype assertions, then argument lists can have
    782                 /// different size and structure when they should be compatible.
    783                 struct TtypeExpander_new : public ast::WithShortCircuiting, public ast::PureVisitor {
    784                         ast::TypeEnvironment & tenv;
    785 
    786                         TtypeExpander_new( ast::TypeEnvironment & env ) : tenv( env ) {}
    787 
    788                         const ast::Type * postvisit( const ast::TypeInstType * typeInst ) {
    789                                 if ( const ast::EqvClass * clz = tenv.lookup( *typeInst ) ) {
    790                                         // expand ttype parameter into its actual type
    791                                         if ( clz->data.kind == ast::TypeDecl::Ttype && clz->bound ) {
    792                                                 return clz->bound;
    793                                         }
    794                                 }
    795                                 return typeInst;
    796                         }
    797                 };
    798 
    799                 /// returns flattened version of `src`
    800                 static std::vector< ast::ptr< ast::Type > > flattenList(
    801                         const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env
    802                 ) {
    803                         std::vector< ast::ptr< ast::Type > > dst;
    804                         dst.reserve( src.size() );
    805                         for ( const auto & d : src ) {
    806                                 ast::Pass<TtypeExpander_new> expander{ env };
    807                                 // TtypeExpander pass is impure (may mutate nodes in place)
    808                                 // need to make nodes shared to prevent accidental mutation
    809                                 ast::ptr<ast::Type> dc = d->accept(expander);
    810                                 auto types = flatten( dc );
    811                                 for ( ast::ptr< ast::Type > & t : types ) {
    812                                         // outermost const, volatile, _Atomic qualifiers in parameters should not play
    813                                         // a role in the unification of function types, since they do not determine
    814                                         // whether a function is callable.
    815                                         // NOTE: **must** consider at least mutex qualifier, since functions can be
    816                                         // overloaded on outermost mutex and a mutex function has different
    817                                         // requirements than a non-mutex function
    818                                         remove_qualifiers( t, ast::CV::Const | ast::CV::Volatile | ast::CV::Atomic );
    819                                         dst.emplace_back( t );
    820                                 }
    821                         }
    822                         return dst;
    823                 }
    824 
    825                 /// Creates a tuple type based on a list of DeclWithType
    826                 template< typename Iter >
    827                 static const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {
    828                         std::vector< ast::ptr< ast::Type > > types;
    829                         while ( crnt != end ) {
    830                                 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
    831                                 // that this results in a flat tuple
    832                                 flatten( *crnt, types );
    833 
    834                                 ++crnt;
    835                         }
    836 
    837                         return new ast::TupleType{ std::move(types) };
    838                 }
    839826
    840827                template< typename Iter >
     
    10481035        private:
    10491036                /// Creates a tuple type based on a list of Type
    1050                 static const ast::Type * tupleFromTypes(
    1051                         const std::vector< ast::ptr< ast::Type > > & tys
    1052                 ) {
    1053                         std::vector< ast::ptr< ast::Type > > out;
    1054                         for ( const ast::Type * ty : tys ) {
    1055                                 // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
    1056                                 // that this results in a flat tuple
    1057                                 flatten( ty, out );
    1058                         }
    1059 
    1060                         return new ast::TupleType{ std::move(out) };
    1061                 }
     1037               
    10621038
    10631039                static bool unifyList(
     
    12311207                        }
    12321208
    1233                 } else if (( common = commonType( t1, t2, widen, symtab, env, open ) )) {
     1209                } else if ( common = commonType( t1, t2, env, need, have, open, widen, symtab )) {
    12341210                        // no exact unification, but common type
    12351211                        auto c = shallowCopy(common.get());
  • src/ResolvExpr/typeops.h

    r4520b77e ra065f1f  
    138138        Type * commonType( Type * type1, Type * type2, bool widenFirst, bool widenSecond, const SymTab::Indexer & indexer, TypeEnvironment & env, const OpenVarSet & openVars );
    139139        ast::ptr< ast::Type > commonType(
    140                 const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2, WidenMode widen,
    141                 const ast::SymbolTable & symtab, ast::TypeEnvironment & env, const ast::OpenVarSet & open );
     140                const ast::ptr< ast::Type > & type1, const ast::ptr< ast::Type > & type2,
     141                        ast::TypeEnvironment & env, ast::AssertionSet & need, ast::AssertionSet & have,
     142                        const ast::OpenVarSet & open, WidenMode widen, const ast::SymbolTable & symtab
     143        );
     144        // in Unify.cc
     145        std::vector< ast::ptr< ast::Type > > flattenList(
     146                const std::vector< ast::ptr< ast::Type > > & src, ast::TypeEnvironment & env
     147        );
    142148
    143149        // in PolyCost.cc
     
    181187
    182188        /// flatten tuple type into existing list of types
    183         static inline void flatten(
     189        inline void flatten(
    184190                const ast::Type * type, std::vector< ast::ptr< ast::Type > > & out
    185191        ) {
     
    194200
    195201        /// flatten tuple type into list of types
    196         static inline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) {
     202        inline std::vector< ast::ptr< ast::Type > > flatten( const ast::Type * type ) {
    197203                std::vector< ast::ptr< ast::Type > > out;
    198204                out.reserve( type->size() );
     
    200206                return out;
    201207        }
     208
     209        template< typename Iter >
     210        const ast::Type * tupleFromTypes( Iter crnt, Iter end ) {
     211                std::vector< ast::ptr< ast::Type > > types;
     212                while ( crnt != end ) {
     213                        // it is guaranteed that a ttype variable will be bound to a flat tuple, so ensure
     214                        // that this results in a flat tuple
     215                        flatten( *crnt, types );
     216
     217                        ++crnt;
     218                }
     219
     220
     221                return new ast::TupleType{ std::move(types) };
     222        }
     223
     224        inline const ast::Type * tupleFromTypes(
     225                const std::vector< ast::ptr< ast::Type > > & tys
     226        ) {
     227                return tupleFromTypes( tys.begin(), tys.end() );
     228        }
     229
     230       
    202231
    203232        // in TypeEnvironment.cc
  • tests/concurrent/.expect/ctor-check.txt

    r4520b77e ra065f1f  
    22?{}: function
    33... with parameters
    4   this: lvalue reference to instance of struct Empty with body
     4  this: mutex reference to instance of struct Empty with body
    55... returning nothing
    66 with body
Note: See TracChangeset for help on using the changeset viewer.