Adding to G++: Adding a warning on throwing unspecified exceptions.

View: New views
15 Messages — Rating Filter:   Alert me  

Adding to G++: Adding a warning on throwing unspecified exceptions.

by Simon Hill :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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.

by Brian Dessent :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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.

by NightStrike :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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.

by Kaveh R. Ghazi :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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.


Parent Message unknown Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Simon Hill :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Thanks for all the links. I knew there were people wanting this but I
didn't quite get how big an issue it was.


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? 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.


Brain Dessent wrote:
> 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.

So, once the other CUs adhere to their prototype exception specifiers,
this becomes OK. I definately intend all of my own CUs to adhere.
Also, you rely on CUs doing what they say in all other means (eg not
modifying const-passed references), why not rely on their exception
specifiers as long as they say you can. And if only some (ie your own)
CUs adhere, that's by no means worse than none adhering (as long as
you are aware that some don't).


> You should certainly look at EDoc++, mentioned in the above threads.
http://edoc.sourceforge.net/
This is definitely interesting, however our goals are a bit different.
EDoc++ is about checking that there is no chance a thrown that
actually exists in code can propogate to somewhere where it will
terminate the app due to being unhandled. It's not about enforcing
code requirements. For example, foo() throw() can call bar()
throw(zug) as long as it doesn't actually throw a zug, or call
anything that does. I would like an environment where the above
example is considered poor practice.


One thing I read there that concerned me was when it was said that
some compilers do "pessimizations" such as not inlining functions with
throw clauses or placing try/catch blocks around calls to these.
I hope that these compilers aren't mainstream. Also, I don't think
that consideration of current bad implementations should factor into
the decision. If people decide to use exception specifications more
these compilers will either get fixed or become slower.


So overall:
- Lots of people request it (for good reasons or bad). Even bad
reasons mean there's an advantage from providing this as an option, ie
showing that it doesn't do what they want.
- Nobody has yet shown me any fundamental problems with implementing
this, apart from my typedefs of function pointers not being allowed
exception specifications which, while annoying, can be avoided.
- It helps code comply with what it says (if you read the throw()
clause in what seems to be its spirit: as an indication of what may be
thrown).
- It's fully optional, and off by default.
- The only real issue is that it will complain about third party code
that doesn't yet "comply". But by this same token this helps third
party code become "compliant" if the authors desire it to be. There's
also the possibility of adding a modification that could ignore calls
to functions declared inside a whitelist of headers like <vector> to
further reduce this.
- GCC probably doesn't do the above pessimizations (I guess I'll have
to look into this).
Where's the downside?

Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Brendon Costa :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


> 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.
>
>  
One of the problems i see with this is generic template classes like the
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.


Parent Message unknown Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Simon Hill :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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.

I'm trying to think whether this would actually prevent well-designed
future code. All I can really come up with so far is copying a vector
of vectors and having bad_alloc thrown during a copy constructor
partway through, but I think bad_alloc should be allowed. Other
things, such as constructing a vector of N thread objects that can
throw on failed thread creation, might be better off undergoing a
redesign.

In other words, would you gain more from a tight exception
specification than you'd lose by not being able to do this?

Another thought that could be a workaround: Is it possible to do
anything like this?
===============
template <typename E>
int foo() throw(E) {}
===============
IE using a template parameter to specify what can be thrown. (Esp with
a C++0X variadic).
Sorry I won't be near a compiler to test this for many an hour, if I
test it and can get it working I'll reply.

Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Brendon Costa :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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?
>
You as an author of a new template class "could" define it the other way.

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.

Parent Message unknown Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Simon Hill :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Brendon Costa wrote:

> You as an author of a new template class "could" define it the other way.
>
> 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.

I definately agree with your comments here (generic components should
often allow their types to throw what they like), I'm just not sure
how to achieve it any other way.



Here's some ideas:

1) In many cases, templates can make a pretty good case for requiring
no-throw for member functions of their type, such as destructors and
move constructors.
Currently, templates can't restrict exceptions when they should and
when they shouldn't.
With the warning, templates need to restrict exceptions when they
should and when they shouldn't, (or be throw all, which requires an
ugly downstream catch(...) ).
Both aren't optimal, although preventing a legitimate thing is worse
than allowing a wrong thing.


2) As in my last post, use template parameters to specify additional
exception types.
===================================
#include <iostream>

class foo {
  public:
    void bar() throw(float) { throw float(5); }
};

template <typename F> // or use concepts.
class zug {
  public:
    template <typename E> void call_bar() throw(int, E) { f.bar();
throw int(3); }
    F f;
};

int main() {
    zug<foo> zf;
    try {
        zf.call_bar<float>(); // here float is passed as an additional
exception.
    }
    catch(int& caught) {
        std::cout << "caught an int" << std::endl;
    }
    catch(float& caught) {
        std::cout << "caught a float" << std::endl;
    }
    return 0;
}
===================================
output:
caught a float
===================================
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.


Brendon Costa wrote:
> 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...
As for implicit exceptions that aren't manually thrown with a throw
statement, eg:
__gnu_cxx::recursive_init
(http://www.cygwin.com/ml/cygwin-developers/2005-05/msg00019.html)
or arithmetic exceptions (perhaps 1/0 etc)
I would just suggest ignoring these. Eventually the compiler _may_
declare these exceptions per implicit op or remove the exceptions, but
either way it's too difficult to manage manually.
You could still specify those exceptions if you're really masochistic.



There is something else to consider.
If this project works reasonably well and perhaps becomes popular,
it's not unreasonable to suggest some minor extensions to the C++
standard to help people code using it.
For example:
1) allowing typedef'd function pointers and concept functions to have
a throw specifier.
2) allowing: "void bar() throw(type_a, type_b, foo::throw);" or "...,
foo())" or something whereby bar inherits foo's throw specifiers,
which would mainly solve the above template problem.
3) extending 2 by giving the option of removing specific types, "void
bar() throw(foo::throw, not int);" so that you can reduce the types
thrown by catching them. I can think of some circumstances where this
could be useful but I'm not sure if they're common enough to justify
it.
Which would then make rigid exception specifying easy enough to use
everywhere. Ok I know I'm trying to fly before I can crawl here :)
Just something to think on.

Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Jonathan Wakely-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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.

by Brendon Costa :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


> 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.

Parent Message unknown Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Simon Hill :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Jonathan Wakely said:

> Simon Hill wrote:
> > 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.

Sorry about that, bad choice of words - I didn't intend to imply they
were flawed because they didn't comply, and complying was only
intended to mean voluntarily adhering to the idea of strict exception
specifiers, not complying with the C++ standard.

You're probably right that it won't happen in the near future. However
if modifications like this actually become popular and easy enough to
use they may make exception restrictions in STL, boost etc. desirable
enough for people to alter the specs of those libraries - or at least
create alternate versions.


Brendon Costa wrote:
> What is the benefit of exception specifiers that can not be provided
> with documentation
It's similar to the benefits I'm most looking forward to in c++0x
concepts - a way for the compiler to tell me off clearly when I do
something bad like accidentally allow a type of exception to propagate
far farther than I intended. (The concept analogue does things like
prevent you from declaring a std::vector of a type that has no copy or
move constructor (it spits an error)). I much prefer the compiler
enforcing things like this than have them simply be stated in
documentation that may not even be read. (Concepts are of course
slightly different in that without them you're
still unable to compile code that is missing the necessary functions,
but they're close enough for this comparison.)

Also, documentation has to be held in the head (unless you refer to
hardcopy every time you write a function) and mine has a lot of holes.

My view is that this would lead me to making more robust code that
handles the unavoidable exceptions (disk errors etc) cleanly in
_every_ case, while I spend less time thinking about this. Of course
I'm trading for the time it takes to code everything with appropriate
exception specifiers (but this isn't difficult, merely a bit tedious).



Brendon Costa wrote:
> 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'm not 100% sure how you proposed to fix this,  but couldn't you look
at rigid exception specifications as achieving this? Because there's
two parts to it:
1) calling function defines the maximum set of exceptions that callee
can throw (isn't this the same thing as you're asking?) in it's own
declaration throw() clause and inner catch() clauses. And if you
really wanted a specific set of exceptions for just one call you could
create a wrapper function with this set as it's throw() clause.
2) callee defines the minimum set of exceptions that caller must handle.

(Sorry I don't have time now to look up that post, I gotta leave).


ASIDE:
I just realized that the most interesting coming features for me are
things that stop you doing stuff. Concepts and Contracts. That's
probably related to why I'd like this project.
Anyway, it's the weekend, time to begin soon (and soon time to
remember about all those wonderful things that C can't do :)

Parent Message unknown Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Simon Hill :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I've been reading up on GCC internals so that I can begin my project.
Initial proposal: http://gcc.gnu.org/ml/gcc/2008-09/msg00383.html
(with many follow-ups).

My idea of implementing so far is: Alter the code in gcc/cp/parser.c
so that I can check strict exception specification during parsing,
similar to how const-ness is enforced (except using warning()).

I would store two variables:
- A global vector of the exception specification of the function being parsed.
- A global supervector of vectors of possibly thrown types within each
nested try block, or in the case of vector 0, the function body.
I'm using a vector of possibly thrown types instead of a vector of
allowed types as I can't calculate allowed types until all catch(){}
blocks are read.
I guess I could try using a "tree" instead of a vector. Not sure if
that should be a supervector of trees or a supertree of trees?

When a function body is entered:
- The exception specification is written to the above vector.
- An empty vector is added to element 0 of the above supervector.
Whenever a try{} clause is met, this supervector is incremented with
an empty vector.
When a catch(){} clause is met, the caught type is removed from the
appropriate vector.
When a throw statement is met, the thrown type is added to the last
vector of the supervector if it doesn't already exist.
When a function call is met, all types in its exception specification
are added to the last vector of the supervector if they don't already
exist.
When the function body exits, the exception specifier is checked
against the possibly thrown types and warnings are emitted if
necessary.

I'm not sure yet how to represent a function that has no exception
specifier. I'm currently thinking of just using an associated boolean
for "throws anything".

I'm hoping that wrapping these in "if(warn_strict_exceptions) {}"
won't slow the parser down significantly. It shouldn't.

Templates:
Whenever a function call is made I would have to look up its
declaration to determine its specification. This becomes problematic
for template types.
I'm not sure how to deal with template expansion. I haven't yet
checked out how this happens inside GCC.
For the first version I'll probably just ignore templates where possible.


Later I can add:
- Checking for calls to function pointers.
- Exception specification checking for function pointer assignment.
- Checks on template instantiation.
- White-listing of headers whose declarations you don't want to be
checked. (so that you can use it and still use std::vector etc without
getting overloaded with warnings). Not sure of an appropriate syntax.


Does this sound like the right way to go about this?

Parent Message unknown Re: Adding to G++: Adding a warning on throwing unspecified exceptions.

by Simon Hill :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I've been planning to add a warning to GCC for a while.

I've recently begun learning the GCC internals and trying to add some code.

So far I have made very little progress.
All I have working is a warning parameter that, when enabled, emits a
warning whenever a function is declared. It's not looking promising.



What I'm having trouble with most is finding adequate documentation,
especially about "tree" usage, and about the C++ frontend.

I've read most of what's relevant in http://gcc.gnu.org/wiki &
http://gcc.gnu.org/onlinedocs/gccint/ but what I'm really looking for
is a reference manual.
On the getting started page, http://www.airs.com/dnovillo/gcc/doc/html
is meant to be a mid/back end reference. The mid part may help a
little. Except it's 404'd and google has no cache.



For example, I'm trying to temporarily emit the details of a
cp_declarator (via printf) from parser.c/make_call_declarator() - line
1013ish.

For the name, I think what I'm after is something akin to:
char* foo(cp_declarator* target) { tree decl_name =
target->u.id.unqualified_name; return SOME_MACRO(decl_name); }, but
I'm kinda stumped as to where to go from there. I got that far from
reading the source code.
Is there a full list of macros, with their associated parameters,
return types, and perhaps usage examples? I hope I didn't skip it. The
internal docs simply has "This section is not here yet.".



I guess I should really be asking:
How did most people here learn devving for GCC?
Did they take a course? just hack around and read online docs? learn
from someone else?
Are many here from the original dev team?