The previous method, using the C preprocessor was both nasty, and crazily resource-intensive. The new method stores stack size information in files that are read in and processed by the compiler when it comes time to link.
They are now recursed into with no type context, then afterwards the type is deduced. This seems to be how they were meant to work, and is also much faster than what I was doing.
This patch follows on from the previous change to the parser. When it spots a function-call, it looks for operators and treats them differently.
It keeps a stack of operators in scope (csOperators in CompState), and when an operator is used, it searches the stack (with all old definitions masked out) for operator definitions to resolve to.
The way it chooses which operator to use in the presence of overloadings (e.g. + on INT vs + on INT32) is simply to try them all. If one matches, it uses that. If none, or more than one match, it gives an error. This makes the code simple and seems logical, but I'm not totally confident if this is the required behaviour for resolving overloaded operators.
The idea behind this is to parse unary/binary operators into function calls with 1/2 operands. So the AST actually has a FunctionCall with the name "+". Function names may now be quoted operators, and thus you can also have function declarations with names such as "+". Resolving is *not* done in the parser for these function names, but rather every "+" is left as "+" (no matter what types it operates on, or what is in scope) by the parser (see later patches to InferTypes instead).
When parsing an occam source file, we automatically insert a bunch of PRAGMA TOCKEXTERNAL that define the default occam operators (e.g. + on INT) as external C functions (which they are!). The naming scheme for these C functions is standardised, and must be used by functions such as mulExprs (which bases the function on the type of its operands) and the new versions mulExprsInt (which are pegged to INT).
The Types module also has some new functions for dealing with operator-functions.
This is the first part of a change, suggested in #64, to remove operators in favour of functions (which will also allow user-defined operators).
The ticket suggested removed operators after the parser, but the way it's working out, it is fine to just never represent as anything other than function calls.
All the knock-on changes will be recorded in later patches.
This may seem like an odd change, but it simplifies the logic a lot. I kept having problems with passes not operating on externals (e.g. functions-to-procs, adding array sizes, constant folding in array dimensions) and adding a special case every time to also process the externals was getting silly.
Putting the externals in the AST therefore made sense, but I didn't want to just add dummy bodies as this would cause them to throw up errors (e.g. in the type-checking for functions). So I turned the bodies into a Maybe type, and that has worked out well.
I also stopped storing the formals in csExternals (since they are now in csNames, and the tree), which streamlined that nicely, and stopped me having to keep them up to date.
It now removes empty sets of (relevant) background knowledge to avoid needlessly creating lots of problems (thousands!) when it only needs one, and also stopped it checking array usage when there's at most one written-to/read-from index.
Fixes#90
There was a bug where things scoped in via pragmas were never scoped out again, which was screwing up the local names stack. I then realised/decided that pragmas were really specifications, and decided to put them there in the parser.
The rest of this patch is just some rewiring to allow the special name munging involved in pragmas (they have already got a munged version of their name) and to stop the scoped in pragmas appearing in the AST.
This is necessary because occam files often #INCLUDE something multiple times (e.g. cgtests) and want different names, but this is not the case for #USE.
Now that we support separate compilation, some of the stack sizes for PROCs depend on the stack sizes of other PROCs that were compiled in different files. We can't just assume the default 512 bytes for these "foreign" PROCs, because that often won't be enough. So instead, we must make the stack sizes for the current PROCs depend on (i.e. use in the calculation) the stack sizes of the foreign PROCs.
This dependence adds some issues though. We cannot declare in one C file a const int that depends on the value of an extern const int from another C file (not valid C, it seems). So instead, we move all the stack size declarations to header files, and use #includes and the preprocessor to make sure that the stack sizes are statically determined.
This in turn simplifies the build process in some ways. These headers only need to be compiled by the .occ file that has the main process, by including them all into a C file and compiling that as before. It means that each .occ file only has one .o file resulting (plus two C headers*, and a .inc file) so linking is a bit less confusing.
* I am keeping the two C headers for now, rather than appending the sizes one to the normal header, because I'm not entirely sure whether having one header that the C file depends on may trigger a recompilation that we don't want in some build systems. I can always merge them later if that's not a valid worry.