I have been following the development of C++0x and ConceptGCC and it has got me interested in developing for G++. I've haven't yet dived far into the G++ code, but I have just been reading the GCC internals documentation at http://gcc.gnu.org/onlinedocs/gccint/index.html. (Of course I was horrified to see it's not written in C++, and it's loaded with macros --- why??).
I must admit I'm very slow at reading other people's code. However, I think it might be a good thing to try and add something to G++, mainly as it would be a great learning experience. What I'd like to add is something that I've seen many people request, and something I really want to use: Adding a -W option to check that all function calls and throws comply with the throw() clause of the function they are launched from. This means that if you have a function "void foo() throw(bar);", a warning will be produced if: - foo's source calls "bam() throw(zug);", unless inside a try block that with an associated catch(zug). - foo's source calls any function declared without a finite set of throwable exceptions. ie "void fub();" - if it throws anything except a bar, unless inside a try block with an appropriate catch. - if it calls a function pointer whose throw clause is anything except throw(bar) or throw(). - if it casts a function pointer to another function pointer type whose throw clause is less restrictive. Ignores explicit casts (unless specified by an additional warning specifier). I would expect that using this warning would cause a few annoyances. Namely: A) Common third party code (perhaps even standard library code) is likely to not follow the throw() clause rigidly. However, they probably should. B) Every function called inside a function with a throw clause (unless called from a within a try block with a catch(...)) would also have to have a restricting throw clause, and catch(...)es are not universally approved. C) typedef'd function pointer types cannot have throw() clauses, meaning you can't cast them to throwing function pointer variables without generating my warning. This pretty much prevents use of typedef'd function pointers wherever throw restrictions exist. My questions are: 1) Is my task a sensible one? Is there anything I have got fundamentally wrong? 2) Is there anyone currently doing this? I'd hate to simply duplicate their effort. 3) I've just been taking a glance at the GCC code. Unfortunately I don't have a guide that shows how g++ works or what files I should be reading so it might take some time to figure out. I can't find much readable documentation about the source. Does anyone have any good documentation links about the overall program flow through g++, files/functions etc? I need a primer. Back to the problem: I'm thinking of using -Wthrow-comply and -Wthrow-comply-explicit (which checks explicit function pointer casts). Additional useful warning flags will be: -Wthrow-dtor when a destructor isn't declared to throw nothing. -Wthrow-move for c++0x, when a move constructor isn't declared to throw nothing[#1]. -Wthrow-all: Same as -Wthrow-comply -Wthrow-dtor -Wthrow-move -Wthrow-all-explicit: Same as -Wthrow-comply-explicit -Wthrow-dtor -Wthrow-move 4) Is this too complex? Should I just stick with one or two? Or does this sound appropriate. Now, I'm starting right from the beginning so this is probably a bit of an ambitious task. My guess is that these are the steps I should go through: Step 1: Examine how, in GCC, to add and test for a parameter GCC: -Wthrow-except Step 2: Learn how to emit a warning, eg: "Warning: using -Wthrow-test flag." Step 3: Learn about how the (generic?) tree of statements inside a function is built. http://gcc.gnu.org/onlinedocs/gccint/Trees.html Learn about these objects and macros: Throw statements: ?? Function calls: CALL_EXPR Function pointers: TYPE_PTRFN_P try/catch blocks: TRY_BLOCK and HANDLER. Step 4: Write a quick (generic?) tree output to text routine that lists as much of the tree as is relevant. This tool probably already exists, but it would be good to learn how to do it. Step 5: Learn about how to query a functions' throw() clause with TREE_RAISES_EXCEPTIONS. Step 6: For each function, step through all statements and sub-statements checking for: - throw statements - function calls - try/catch blocks. And for each of these, test their exception(s) vs the allowed exceptions in the throw() clause of the function or in the catch() parameters. Also test for function pointer assignments. If any fail, emit appropriate warnings. 5) Does this sound accurate? Am I missing anything? 6) Should a test like what is done in Step 5 be built as a standalone function that is called once after the tree has been assembled (like a separate compilation step), or should I fit my per element tests beside other per-element operations, such as (I'm guessing) the code that might enforce access permissions (private, protected)? 7) To someone new to the g++ source the included documentation seems pretty poor and cryptic. README mentions non-existant files, INSTALL/README says it's obsolete and redirects to a file "index.html" which doesn't yet exist. And why does the documentation have all these .texi, .info and (unlinked) manpage .1/.7 files. What's wrong with .txt, .html or similar? Shouldn't documentation be available from the download instead of having to go online to actually find out how to build and read the documentation itself - the documentation doesn't really take up much space. In this age do manpages actually have any advantages? NOTES FROM ABOVE: [#1] I Suggest reccomending that move constructors ( T::T(T&&); ) throw nothing. Why? Because during handling of the exception the move constructor may again need to be called, which could lead to the exception handler throwing and leaving you with two exceptions to handle, which is unsupported (for good reason) and IIRC causes immediate process termination. This is the same reason that destructors shouldn't throw. The reasoning here isn't as definite as for the case for destructors, as it refers only to containers of the type. - In C++0x, move constructors are going to be used often by containers like std::vector when the container elements need to be opaquely relocated. - If a std::vector (or similar container) algorithm calls an element member function that throws, the vector (or container) should be restored to its initial state. - Because the algorithm doing the moving may require the moving of multiple container elements, the move that throws may not be the first of the algorithm, and thus other moves may need to be rolled back if the vector (or container) is to be restored. - Rolling back these earlier moves require more moves from the destination back to the source. - Since the rolling back is occuring in the exception handler, if one of the roll-back moves also throws an exception you now have two exceptions. As I recently found out, C++0x concepts are unable to specify the throw requirements of functions, so they can't fix this as I had hoped. I plan to write this up as a separate idea/proposal later. So, what do you think? Is this a good project? Do you know a good primer for g++ internals? Thanks for all help.