|
View:
New views
15 Messages
—
Rating Filter:
Alert me
|
|
|
Adding to G++: Adding a warning on throwing unspecified exceptions.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. |
|
|
Re: Adding to G++: Adding a warning on throwing unspecified exceptions.Simon Hill wrote:
> 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??). You seem to refer to g++ as if it's a separate program from gcc but it's really not. All of the middle- and back-end code is shared between the language front-ends, so if you introduce C++ there you now require a pre-existing C++ bootstrap compiler even for people that have no interest or need for C++ language support and are only building a C (or Fortran or Java or Ada) compiler. A lot of people have a problem with that because some systems may not have or even want anything more than a C compiler. You could of course limit use of C++ to the C++ frontend, similar to how the Ada frontend is written in Ada. But I don't think that would do much to alleviate the issue that you're complaining about because it would still have to use all the same macros to represent trees and types and so on to the language independent parts of the compiler. Nevertheless it's a topic that keeps coming up and there are a number of people that would like the advantages of better compile time type checking and so on. This debate always rages on because there is constant fear that it will open the door for the more exotic and unmaintainable C++ features to sneak in, in addition to the higher bootstrap requirements. At the moment there is slow progress in getting things to the point where gcc can at least be built with a C++ compiler. See also the gcc-in-cxx branch. > 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. You really ought to read the past threads on this topic, for example: <http://gcc.gnu.org/ml/gcc/2007-01/threads.html#00499> <http://gcc.gnu.org/ml/gcc/2007-03/threads.html#01162> <http://gcc.gnu.org/ml/gcc/2007-04/threads.html#00024> <http://gcc.gnu.org/ml/gcc/2007-04/threads.html#00265> The general consensus is that doing this at compile time cannot give you anything useful, because the compiler can only see one compilation unit at a time and so the only thing it can know of all the downstream functions that are in other CUs is what is provided in the exception specifiers of the prototypes. You're essentially trusting that all exception specifiers for every function in the program and *all* library code are always present and always correct which is a huge leap of faith that I don't think is supported by reality. For example the Boost rationale basically says they are unusable in practical code except in special cases: <http://www.boost.org/development/requirements.html#Exception-specification>. > 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. You should certainly look at EDoc++, mentioned in the above threads. > 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. Don't neglect the wiki: <http://gcc.gnu.org/wiki/GettingStarted> > 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? The documentation's canonical form is texinfo, i.e. gcc/doc/*.texi. Everything else (HTML, PDF, DVI, .info, manpages) is auto-generated from the texinfo, e.g. "make html", "make pdf", etc. Whether the generated files are in your tree depends on whether you're working from a SVN checkout or a release tarball, but the texinfo sources are always there. Brian |
|
|
Re: Adding to G++: Adding a warning on throwing unspecified exceptions.On Sun, Sep 21, 2008 at 12:36 PM, Brian Dessent <brian@...> wrote:
> Simon Hill wrote: > >> 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??). > > You seem to refer to g++ as if it's a separate program from gcc but it's > really not. All of the middle- and back-end code is shared between the > language front-ends, so if you introduce C++ there you now require a > pre-existing C++ bootstrap compiler even for people that have no > interest or need for C++ language support and are only building a C (or > Fortran or Java or Ada) compiler. A lot of people have a problem with I think java requires c++. |
|
|
Re: Adding to G++: Adding a warning on throwing unspecified exceptions.On Sun, 21 Sep 2008, NightStrike wrote:
> On Sun, Sep 21, 2008 at 12:36 PM, Brian Dessent <brian@...> wrote: > > Simon Hill wrote: > > > >> 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??). > > > > You seem to refer to g++ as if it's a separate program from gcc but it's > > really not. All of the middle- and back-end code is shared between the > > language front-ends, so if you introduce C++ there you now require a > > pre-existing C++ bootstrap compiler even for people that have no > > interest or need for C++ language support and are only building a C (or > > Fortran or Java or Ada) compiler. A lot of people have a problem with > > I think java requires c++. It does, but only to build the target libraries, not to bootstrap. It uses the just-built G++ to do that. Ada is the only frontend right now where you need a pre-existing compiler other than C to *bootstrap* it. |
|
|
|
|
|
Re: Adding to G++: Adding a warning on throwing unspecified exceptions.> I agree that it won't be very useful initially due to lots of third > party code like boost neither defining nor adhering exception > restrictions 100% of the time (STL may be guilty also). However, this > is a catch 22. Why not provide the mechanism for verifying exception > specifications so that these libraries can, in future, become fully > compliant? It won't take much work, especially when you can get a > warning telling you exactly what you need to change, and the only > thing you need to do 99% of the time is add throw() clauses to > definitions and declarations. I bet you could get boost & STL > compliant within a week even if they had 0 throw() clauses to begin > with. > > STL containers. The author of the template class or container can't know what types of exceptions will be thrown from them, so you must define them as being able to throw all exceptions (which is how they are currently). Then if those containers are used all over the place then you are back to square one. Personally i am starting to see that usage of the exception specifier in C++ is "almost" useless (I find them a bit more useful with EDoc++ but i still only use them very rarely). I initially thought the same way as you are now, but in the end decided it was necessary to construct a complete list of exceptions that can be thrown before then applying such rules. This is what EDoc++ does. It first calculates all possible exceptions, then it performs all sorts of analysis to enforce things like exception specifiers (and much more). After developing EDoc++, i have also realized how pervasive exceptions can be in C++ code, which works against the sorts of checks that can be done within a single translation unit. I had to go to lengths to provide a "suppressions" system for EDoc++, simply because almost all code can throw all sorts of exceptions i had never heard of before. A developer "generally" is not interested in exceptions like: __gnu_cxx::recursive_init. But after writing EDoc++ i found exceptions like this turn up all over the place, though they should not occur in normal operation... With all this said though. If your purpose is to learn more about GCC internals, then i think it would be a good project to undertake to achieve that. You may have to do so though, with the understanding that the maintainers may not choose to accept your patch into the official GCC release. I don't know if they would or not, as i am not a GCC maintainer but it would be worth getting clarification before doing the task. On a slight side note, there is a feature i have been postponing from EDoc++ until "the distant future". It is to provide "meta-data" or code "markup" which can be used to enforce certain restrictions on code being used. This is primarily of use for defining a "contract" when writing template code that wants to define a certain "exception guarantee". For example, i assume you are familiar with the various "guarantees" that have been described in literature about exceptions in C++ (if not then it is worth looking up. It sure opened my eyes to writing safer and better code in the presence of exceptions). To make some generic container satisfy the "strong guarantee" will require certain restrictions on the type used to instantiate the template. A good example of this is that the destructor for the type must not throw an exception. For a given generic implementation, there may be additional restrictions as well. If these restrictions are not met, then the container will no longer meet the "strong guarantee". I wish to define a method of marking up source code such that those requirements are described inline in the code of the template implementation such that someone who instantiates the template with a particular type can then run EDoc++ over their code and be warned when that particular instantiation may break the requirements. Anyhow, this is a preliminary idea and i just don't have the time now to look at implementing it. It will likely require changes to the C++ parser and front end to insert extra nodes into the tree for this markup. I am also not sure if others would accept such a modification into the official GCC release unless it was generic enough to be used for other purposes (maybe such as providing optimization hints for certain segments of code or other forms of markup). Thanks, Brendon. |
|
|
|
|
|
Re: Adding to G++: Adding a warning on throwing unspecified exceptions.Simon Hill wrote:
> Brendon Costa said: >> The author of the template class or container can't know >> what types of exceptions will be thrown from them, so you must define >> them as being able to throw all exceptions (which is how they are >> currently). > Ouch, you have a point. But couldn't you put this round the other way. > Place the onus on the user of the template to comply with the > exception guarantees inside the template. Unfortunately that would > likely cause problems with some existing code. > > ... > > In other words, would you gain more from a tight exception > specification than you'd lose by not being able to do this? > The issue here is that doing so restricts usage of the generic component. In specific cases this may be desirable, but not for generic components like STL containers or those in boost. For generic components you want to make them as useful as possible. Even if at the time of writing the container you do not foresee that throwing any type of exception is a good idea, users may come up with some way of using it that you just never thought about. I.e. I think the general philosophy is to define only what restrictions are necessary to implement the template, not necessarily what we think constitute good uses of the template at the time we created it. Brendon. |
|
|
|
|
|
Re: Adding to G++: Adding a warning on throwing unspecified exceptions.2008/9/24 Simon Hill:
> Brain Dessent wrote: >> You're essentially trusting that all >> exception specifiers for every function in the program and *all* library >> code are always present and always correct which is a huge leap of faith >> that I don't think is supported by reality. > > I agree that it won't be very useful initially due to lots of third > party code like boost neither defining nor adhering exception > restrictions 100% of the time (STL may be guilty also). However, this > is a catch 22. Why not provide the mechanism for verifying exception > specifications so that these libraries can, in future, become fully > compliant? That won't happen. Boost and the standard library are not "guilty" of anything. The standard library components *are* fully compliant to the ISO standard by definition, and the omission of exception specifications is (in almost all cases) completely intentional. Some people might find the warning useful, but please don't assume that code without exception specifications is incomplete, or would necessarily benefit from having them. Jonathan |
|
|
Re: Adding to G++: Adding a warning on throwing unspecified exceptions.> The above works on code::blocks, which uses some form of GCC, and > looks OK to me. > Of course this only works for exactly one exception type. > You'd have to wait for C++0X variadic templates (and hope you can > throw them) if you need zero or more than one. > It's also very verbose, a little cryptic, and nested templates could > make variadic versions horrifically long. > However it's a start. > All these are reasons why such a feature will not be used. Trying to write code with C++ exception specifications has a LOT of problems in addition to things you mentioned above i can think of a few: * It is VERY difficult to get correct (compounded across platforms and versions of libraries/compilers, or with different compile time options etc that may cause different exceptions to be thrown) * It has the great feature, where if you do get it wrong it likes to call that lovely terminate function (Dripping with sarcasm...) * Maintaining code with such specifications is cumbersome and a LOT of work (I tried it). * A lot of difficulties arise in the presence of templates in determining what exceptions may be thrown (And solutions described are cumbersome) * There is no compile time assertion of compliance (This is solved by EDoc++, but may also be solved in part by the warning feature being proposed). This is where Java differs, it has a compile time compliance that "non-runtime" exceptions are specified/handled. But as i understand it no runtime assertion (it is not needed). Where as C++ is the exact opposite. This brings across the important point about "runtime" exceptions. In Java they do not need to be specified in an exception specifier, they will just propagate out the function and may be handled somewhere (or crash out the main entry point). In C++ if you add some exceptions to the specifier, but forget/omit the runtime ones, then your program will be terminated early, there is no option for other code to actually handle that exception (std::bad_exception in my opinion is a bad substitute in this situation). What is the benefit of exception specifiers that can not be provided with documentation (I am sure there are some or they wouldn't have been added to the standard, but i have become dis-enchanted with exception specifiers)? The only thing i can think of is optimization, and that is discussed by the boost page on exception specifiers mentioned earlier. It has been a while since i have looked in detail on this topic. Initially EDoc++ was written so that i could then go and write all my C++ code with exception specifiers and then use EDoc++ to enforce them (I wanted to reproduce what Java had for C++). But even though EDoc++ can enforce such usage, to actually write code that lists all its exception specifiers is just WAY too much work and there is no or little gain that i can see from doing so. As a result, EDoc++ has evolved to cover different aspects of exception usage analysis and the initial purposes of it i rarely use. What i mentioned in an earlier post is that rather than a function specify what it can throw, i propose that for the C++ community a more useful compile time assertion is to allow code that uses a function to define what exceptions it can deal with that function throwing. I.e. moving the contract from the used to the user. I noticed when i started to become aware of writing exception safe code, that i would be acutely aware that certain portions of code i was using needed to have particular exception requirements (Such as the nothrow destructor, or i use certain members of the std::list or pointers as they have a number of no-throw operations). I can not enforce that say some client code include exception specifiers, but i can modify my code to define the contract that i require of them. Then i could use EDoc++ or some other similar tool to tell me if those contracts have not been met. If they are not, then i either request change in the client code or change the way i use them. With all this in mind, i think that the proposed warning will not be used by many and will not shape the future of C++ due primarily with the issues inherent in the C++ language definition of how exception specifiers work. I do however think as i mentioned before that it would make a great project to learn more about GCC internals and would encourage giving it a try on those grounds. Brendon. |
|
|
|
|
|
|
|
|
|