diff mbox

ObjC/ObjC++: bug fixes for @catch

Message ID 1290947442.187324993@192.168.2.229
State New
Headers show

Commit Message

Nicola Pero Nov. 28, 2010, 12:30 p.m. UTC
Iain,

thanks for your feedback.

>> (sadly, as it is
>> well-known (PR objc++/23616), ObjC exceptions don't actually work in  
>> ObjC++, so I had to disable the
>> ObjC++ execution one as fixing that is not part of this patch.  I  
>> don't think I'll work on that for 4.6,
>> it's too late)
>
> ... but they should work for NeXT runtime ;-)

Sure ... so what do you suggest ?  We don't want to introduce any new failures 
that simply duplicate existing ones.  That just confuses people that try to track
PRs and regressions. ;-)

Ideally exceptions-2.mm should be compiled only with the GNU runtime, and 
compiled + executed with the NeXT one, but I don't think there is an easy way 
to do this, hence I marked it just for compilation.  Do you know any way that 
doesn't require duplicating the testcase ?  Anyway, that can be done in a separate 
patch.


>>        * objc.dg/exceptions-1.m: New.
>>        * objc.dg/exceptions-2.m: New.
>>        * objc.dg/exceptions-3.m: New.
>>        * objc.dg/exceptions-4.m: New.
>>        * objc.dg/exceptions-5.m: New.
>>        * obj-c++.dg/exceptions-1.mm: New.
>>        * obj-c++.dg/exceptions-2.mm: New.
>>        * obj-c++.dg/exceptions-3.mm: New.
>>        * obj-c++.dg/exceptions-4.mm: New.
>>        * obj-c++.dg/exceptions-5.mm: New.
>
> I think we can honestly forecast needing enough exception tests to  
> warrant starting a new directory for them --
> ... also we should identify suitable candidates for torture testing -  
> which is especially weak for Obj-C++
>
> obj*.dg/exceptions/
> obj*.dg/torture/exceptions/
>
>  the .exp scripts in an existing parallel dir should serve as a  
> template
> (or let me know off-list and I'll do this and send you an increment to  
> your patch)

Ok - sounds good - in attach a revised patch:

 * I moved the new testcases into objc.dg/exceptions and obj-c++.dg/exceptions subdirectories

 * I changed the top-level dg.exp file to run the testcases in the subdirectories
as well as the testcases in the top-level (I don't want to duplicate the .exp files
in each subdirectory unless there is a reason to do so)
 
 * I renamed a variable I had added to the parser code to something better (open_paren_seen
renamed to seen_open_paren).

There are no good candidates for torture test in this patch, which is a "parser"
patch.  Even the execution test really only tests that the parser is passing the
right information to objc-act.c.  So I think the patch is now ready :-)

Ok to commit ?

Thanks

In gcc/objc/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc-act.c (objc_eh_runtime_type): Avoid ICE if error_mark_node
        is passed as argument.
        (objc_begin_catch_clause): Added code to deal with an
        error_mark_node or NULL_TREE argument.  Improved checks for
        invalid arguments.  Added code to traverse typedefs.

In gcc/testsuite/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc.dg/dg.exp: Run the tests in objc.dg/exceptions as well.
        * objc.dg/exceptions/exceptions-1.m: New.
        * objc.dg/exceptions/exceptions-2.m: New.
        * objc.dg/exceptions/exceptions-3.m: New.
        * objc.dg/exceptions/exceptions-4.m: New.
        * objc.dg/exceptions/exceptions-5.m: New.
        * obj-c++.dg/dg.exp: Run the tests in obj-c++.dg/exceptions as well.
        * obj-c++.dg/exceptions/exceptions-1.mm: New.
        * obj-c++.dg/exceptions/exceptions-2.mm: New.
        * obj-c++.dg/exceptions/exceptions-3.mm: New.
        * obj-c++.dg/exceptions/exceptions-4.mm: New.
        * obj-c++.dg/exceptions/exceptions-5.mm: New.

In gcc/cp/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * parser.c (cp_parser_objc_try_catch_finally_statement): Parse
        @catch(...)  and pass NULL_TREE to objc_begin_catch_clause() in
        that case.  Improved error recovery.  Reorganized code to be
        almost identical to c_parser_objc_try_catch_finally_statement.

In gcc/:
2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>

        * c-parser.c (c_parser_objc_try_catch_statement): Renamed to
        c_parser_objc_try_catch_finally_statement for consistency with the
        C++ parser.  Parse @catch(...) and pass NULL_TREE to
        objc_begin_catch_clause() in that case.  Improved error recovery.
        Reorganized code to be almost identical to
        cp_parser_objc_try_catch_finally_statement.

Comments

Iain Sandoe Nov. 28, 2010, 12:42 p.m. UTC | #1
On 28 Nov 2010, at 12:30, Nicola Pero wrote:

>>> (sadly, as it is
>>> well-known (PR objc++/23616), ObjC exceptions don't actually work in
>>> ObjC++, so I had to disable the
>>> ObjC++ execution one as fixing that is not part of this patch.  I
>>> don't think I'll work on that for 4.6,
>>> it's too late)
>>
>> ... but they should work for NeXT runtime ;-)
>
> Sure ... so what do you suggest ?  We don't want to introduce any  
> new failures
> that simply duplicate existing ones.  That just confuses people that  
> try to track
> PRs and regressions. ;-)

Well, I agree - but that's more-or-less what we're doing here --
-- for -next-runtime,  the new tests fail with the same fail existing  
in several of the existing ones...

(I know you're intending to look at this - so this is just a 'heads  
up' not a major complaint ;-))

> Ideally exceptions-2.mm should be compiled only with the GNU  
> runtime, and
> compiled + executed with the NeXT one, but I don't think there is an  
> easy way
> to do this, hence I marked it just for compilation.  Do you know any  
> way that
> doesn't require duplicating the testcase ?

I'll think about it -- I guess the worst case is to XFAIL the run for  
gnu-runtime (like we do for the m64 NeXT ones).

>  Anyway, that can be done in a separate patch.

Indeed, I should have made it clear I wasn't suggesting holding this  
one up ...

>>>       * objc.dg/exceptions-1.m: New.
>>>       * objc.dg/exceptions-2.m: New.
>>>       * objc.dg/exceptions-3.m: New.
>>>       * objc.dg/exceptions-4.m: New.
>>>       * objc.dg/exceptions-5.m: New.
>>>       * obj-c++.dg/exceptions-1.mm: New.
>>>       * obj-c++.dg/exceptions-2.mm: New.
>>>       * obj-c++.dg/exceptions-3.mm: New.
>>>       * obj-c++.dg/exceptions-4.mm: New.
>>>       * obj-c++.dg/exceptions-5.mm: New.
>>
>> I think we can honestly forecast needing enough exception tests to
>> warrant starting a new directory for them --
>> ... also we should identify suitable candidates for torture testing -
>> which is especially weak for Obj-C++
>>
>> obj*.dg/exceptions/
>> obj*.dg/torture/exceptions/
>>
>> the .exp scripts in an existing parallel dir should serve as a
>> template
>> (or let me know off-list and I'll do this and send you an increment  
>> to
>> your patch)
>
> Ok - sounds good - in attach a revised patch:
>
> * I moved the new testcases into objc.dg/exceptions and obj-c++.dg/ 
> exceptions subdirectories
>
> * I changed the top-level dg.exp file to run the testcases in the  
> subdirectories
> as well as the testcases in the top-level (I don't want to duplicate  
> the .exp files
> in each subdirectory unless there is a reason to do so)

there is a good reason to do so --

make check-objc RUNTESTFLAGS="exceptions.exp=* "

(and the existing equivalents) are very useful when tracking down  
categories of problems..

I suspect that automatically including the sub-dirs will double- 
execute a number of existing tests...

>
> * I renamed a variable I had added to the parser code to something  
> better (open_paren_seen
> renamed to seen_open_paren).
>
> There are no good candidates for torture test in this patch, which  
> is a "parser"
> patch.  Even the execution test really only tests that the parser is  
> passing the
> right information to objc-act.c.  So I think the patch is now  
> ready :-)
>
> Ok to commit ?
>
> Thanks
>
> In gcc/objc/:
> 2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
>
>        * objc-act.c (objc_eh_runtime_type): Avoid ICE if  
> error_mark_node
>        is passed as argument.
>        (objc_begin_catch_clause): Added code to deal with an
>        error_mark_node or NULL_TREE argument.  Improved checks for
>        invalid arguments.  Added code to traverse typedefs.
>
> In gcc/testsuite/:
> 2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
>
>        * objc.dg/dg.exp: Run the tests in objc.dg/exceptions as well.
>        * objc.dg/exceptions/exceptions-1.m: New.
>        * objc.dg/exceptions/exceptions-2.m: New.
>        * objc.dg/exceptions/exceptions-3.m: New.
>        * objc.dg/exceptions/exceptions-4.m: New.
>        * objc.dg/exceptions/exceptions-5.m: New.
>        * obj-c++.dg/dg.exp: Run the tests in obj-c++.dg/exceptions  
> as well.
>        * obj-c++.dg/exceptions/exceptions-1.mm: New.
>        * obj-c++.dg/exceptions/exceptions-2.mm: New.
>        * obj-c++.dg/exceptions/exceptions-3.mm: New.
>        * obj-c++.dg/exceptions/exceptions-4.mm: New.
>        * obj-c++.dg/exceptions/exceptions-5.mm: New.
>
> In gcc/cp/:
> 2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
>
>        * parser.c (cp_parser_objc_try_catch_finally_statement): Parse
>        @catch(...)  and pass NULL_TREE to objc_begin_catch_clause() in
>        that case.  Improved error recovery.  Reorganized code to be
>        almost identical to c_parser_objc_try_catch_finally_statement.
>
> In gcc/:
> 2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
>
>        * c-parser.c (c_parser_objc_try_catch_statement): Renamed to
>        c_parser_objc_try_catch_finally_statement for consistency  
> with the
>        C++ parser.  Parse @catch(...) and pass NULL_TREE to
>        objc_begin_catch_clause() in that case.  Improved error  
> recovery.
>        Reorganized code to be almost identical to
>        cp_parser_objc_try_catch_finally_statement.
> <patch.txt>
Mike Stump Nov. 29, 2010, 12:10 a.m. UTC | #2
On Nov 28, 2010, at 4:42 AM, IainS wrote:
>> * I changed the top-level dg.exp file to run the testcases in the subdirectories
>> as well as the testcases in the top-level (I don't want to duplicate the .exp files
>> in each subdirectory unless there is a reason to do so)
> 
> there is a good reason to do so --
> 
> make check-objc RUNTESTFLAGS="exceptions.exp=* "
> 
> (and the existing equivalents) are very useful when tracking down categories of problems..
> 
> I suspect that automatically including the sub-dirs will double-execute a number of existing tests...

:-(

So, dg.exp=eh\*.C is a fine way to execute testcases...  In general, there are more categories of testcases than we want either drivers or directories, so using either is flawed.
Iain Sandoe Nov. 29, 2010, 8:30 a.m. UTC | #3
On 29 Nov 2010, at 00:10, Mike Stump wrote:

> On Nov 28, 2010, at 4:42 AM, IainS wrote:
>>> * I changed the top-level dg.exp file to run the testcases in the  
>>> subdirectories
>>> as well as the testcases in the top-level (I don't want to  
>>> duplicate the .exp files
>>> in each subdirectory unless there is a reason to do so)
>>
>> there is a good reason to do so --
>>
>> make check-objc RUNTESTFLAGS="exceptions.exp=* "
>>
>> (and the existing equivalents) are very useful when tracking down  
>> categories of problems..
>>
>> I suspect that automatically including the sub-dirs will double- 
>> execute a number of existing tests...
>
> :-(
>
> So, dg.exp=eh\*.C is a fine way to execute testcases...  In general,  
> there are more categories of testcases than we want either drivers  
> or directories, so using either is flawed.

well... the way I've got things set up is a little more flexible than  
that.

FWIW, for example:  make check-objc RUNTESTFLAGS="strings.exp=* "
  --- will run all the .dg _and_ .dg/torture string tests ... which I  
think is useful.

AFAICS dejagnu will not accept wild-cards in the specification of  
the .exp files.

Of course, one needs to decide on what constitutes a 'major' category.
and I am sure that both approaches can be used in parallel ... small  
categories with dg.exp=dir/*.m ...

maybe whatever we do is flawed -
- but I believe having everything in one place is not the way to  
facilitate easy maintenance.

cheers
Iain

P.S. I guess we need to reach consensus [ or have a ruling :-)  ] since
(a) we're expanding the test-suite quite fast at the moment
(b) I was intending to document it before 4.6 goes out
Mike Stump Nov. 29, 2010, 7:40 p.m. UTC | #4
On Nov 29, 2010, at 12:30 AM, IainS wrote:
> FWIW, for example:  make check-objc RUNTESTFLAGS="strings.exp=* "
> --- will run all the .dg _and_ .dg/torture string tests ... which I think is useful.

Well, except this doesn't work for things that don't have an .exp file...  :-(  The dg.exp=cfstring\*.m method does work, always.

> Of course, one needs to decide on what constitutes a 'major' category.

In general, I'd prefer most testcases to fit in a very small number of framework drivers.  I'm not in favor of just randomly creating new drivers for the sole purpose of providing group naming.  We create them, when the existing drivers can't meet the needs and should not be extended to meet those needs.  This in general means, no major categories, rather, we have specific needs, like, lto, dg, torture.  These aren't categories of testcases, but rather, different sets of specialized needs of test cases.

Now, when to create a new driver or fold it into an existing one.  It must be handled on a case by case basis.  In general, if an existing driver would work, I prefer using it.  Only when that doesn't work, and the feature set needed doesn't fit into the framework, we create a new driver.

> - but I believe having everything in one place is not the way to facilitate easy maintenance.

I don't follow.  dg.exp=eh-*.C for example is isomorphic to dg.exp=eh/*.C, which is isomorphic to eh.exp,  Now, if your only complaint is that eh.exp is a few characters shorter, well, yes, this is true...  What are the 4 most important qualities you're interested in?

> P.S. I guess we need to reach consensus [ or have a ruling :-)  ] since
> (a) we're expanding the test-suite quite fast at the moment
> (b) I was intending to document it before 4.6 goes out

Wait, consensus, I thought we practiced benevolent dictator?  :-)

Now, as a directory balloons out, yes, we tend to split testcases out, so for example, today we might have exception-1.m, and tomorrow it may well be exception/exception-1.m...  We aren't quite there yet.
Iain Sandoe Nov. 29, 2010, 9:14 p.m. UTC | #5
On 29 Nov 2010, at 19:40, Mike Stump wrote:

> On Nov 29, 2010, at 12:30 AM, IainS wrote:
>> FWIW, for example:  make check-objc RUNTESTFLAGS="strings.exp=* "
>> --- will run all the .dg _and_ .dg/torture string tests ... which I  
>> think is useful.
>
> Well, except this doesn't work for things that don't have an .exp  
> file...  :-(  The dg.exp=cfstring\*.m method does work, always.
>
>> Of course, one needs to decide on what constitutes a 'major'  
>> category.
>
> In general, I'd prefer most testcases to fit in a very small number  
> of framework drivers.  I'm not in favor of just randomly creating  
> new drivers for the sole purpose of providing group naming.  We  
> create them, when the existing drivers can't meet the needs and  
> should not be extended to meet those needs.  This in general means,  
> no major categories, rather, we have specific needs, like, lto, dg,  
> torture.  These aren't categories of testcases, but rather,  
> different sets of specialized needs of test cases.
>
> Now, when to create a new driver or fold it into an existing one.   
> It must be handled on a case by case basis.  In general, if an  
> existing driver would work, I prefer using it.  Only when that  
> doesn't work, and the feature set needed doesn't fit into the  
> framework, we create a new driver.

OK. That's reasonable, and I can re-jig things to replace the  
additional drivers with a facility to drive a list of sub-dirs from  
the dg.exp, lto.exp and dg-torture.exp
(that doesn't seem like a major headache).

The only loss there is the ability to drive disjoint sets ..
...  as you point out, that can only be achieved if we proliferate  
drivers (which was where I was heading ;) ).

>> - but I believe having everything in one place is not the way to  
>> facilitate easy maintenance.
>
> I don't follow.  dg.exp=eh-*.C for example is isomorphic to  
> dg.exp=eh/*.C, which is isomorphic to eh.exp,  Now, if your only  
> complaint is that eh.exp is a few characters shorter, well, yes,  
> this is true...

well, I'd argue for  eh/
-- it has an interesting property that test-cases need not be named  
"eh-*" 
-- if there is a more logical name (e.g. PRxxxx) but it will still be  
clear that they are tests related to eh.

> What are the 4 most important qualities you're interested in?

thanks, that's a useful question.

1. A Plan that's clear, reasonably robust to growth and explicable to  
new and existing developers in moderate  documentation.
    (since the ObjC test-suite is currently undocumented - and it does  
take time to get to grips with it)

2. an organization that makes it clear what is grouped together
   -- so that new and existing developers can find what's been tested  
and not duplicate tests or effort.

3. if possible, the ability to run groups of related tests (preferably  
without excessive keystrokes) such that when working on one aspect of  
the FE one can reasonably quickly get feedback on problems.

4. I'd prefer to avoid dg.exp (or any other dir) growing until it is  
2000 files and the job is Too Big to fix it .. (but that might be a  
restatement of 1..3  ;-) )
    -- we are already heading for 250 tests in dg.exp (and it would be  
nearer 450 without the ones we've already split out).

Mechanically, I think this can be done with dg.exp=<group-dir>/*.m ..  
as well as group.exp=*.m ...
... so I'm happy to retire the three or so 'un-necessary' .exp files  
and figure out a change to dg/lto/dg-torture.exp to handle the sub-dirs.

I guess the pre-requisite is the bare-bones of (1) - or effort in re- 
organizing things is wasted (and time is a short-supply resource).

>> P.S. I guess we need to reach consensus [ or have a ruling :-)  ]  
>> since
>> (a) we're expanding the test-suite quite fast at the moment
>> (b) I was intending to document it before 4.6 goes out
>
> Wait, consensus, I thought we practiced benevolent dictator?  :-)

dang, and I thought this was floating anarchy :-)

> Now, as a directory balloons out, yes, we tend to split testcases  
> out, so for example, today we might have exception-1.m, and tomorrow  
> it may well be exception/exception-1.m...  We aren't quite there yet.

It creeps up on you...
... you might be surprised to count up and see that we've almost  
doubled the ObjC test-case count (and more than doubled the ObjC++  
count) since 4.5 forked.

Yes, we're still less than 10% of the C and C++ suites, but we're way  
less than 10% of their maintainer count too ;-)

cheers
Iain
Mike Stump Nov. 29, 2010, 11:15 p.m. UTC | #6
On Nov 29, 2010, at 1:14 PM, IainS wrote:
> OK. That's reasonable, and I can re-jig things to replace the additional drivers with a facility to drive a list of sub-dirs from the dg.exp, lto.exp and dg-torture.exp (that doesn't seem like a major headache).

The correct algorithm is, any directory with a .exp file, should be use that exp file.  Any directory without an .exp file, should use the .exp file above it.

Net result, no explicit list of sub dirs, no `exceptions' list for recursive finds...  The existing code, last I looked, sucked in this regard.

> well, I'd argue for  eh/
> -- it has an interesting property that test-cases need not be named "eh-*"-- if there is a more logical name (e.g. PRxxxx) but it will still be clear that they are tests related to eh.

What is old is new again, have you ever noticed:

 find . -name \*.C | xargs grep '// GROUPS'

in the testsuite?  Ever wonder what these lines were for?  Turns out that is the past, back before dejagnu (yes, there was such a time), the driver we used for testing had that feature.  The groups a testcase were in was independent of the filename and you could have 0 or more groups.  The groups persisted though file renaming and movement.

Been there, done that.  History has shown that a mere one or two groups is sufficient for most people most of the time.  In the modern world, that would be the .exp file name, and the prefix the file begins with.

Actually, I was going for the four technical needs for which exceptions.exp was the answer or which answered the question, what problems are solved by not having everything together.

> 1. A Plan that's clear, reasonably robust to growth and explicable to new and existing developers in moderate documentation.
>   (since the ObjC test-suite is currently undocumented - and it does take time to get to grips with it)

I can attempt to explain anything that's unclear...  though, I don't know precisely what is.

Roughly, developers create and use prefixes for the testsuite that serve their needs.  Generally, we respect the desires of each developer to so choose and create the prefix.  Generally, we expect developers to reuse existing prefixes rather than create random new ones.  Also, I'd encourage their use.  pr, in my book, is a poor prefix.  It conveys very, very little information.  For any specific prefix, you'd have to ask that developer.  Most aren't meant to be other than completely transparent.  For example, eh for exception handling for C++.  vbase for virtual bases and so on.  I don't think they are generally collected and documented.  Generally, the testsuite is a write once, read never sort of thing.  Though, sometimes people use it as compiler documentation and feature documentation.

For objc specifically, I don't expect I could explain the existing groups better than I suspect you already know.  If you want to ask about one, just ask.

> 2. an organization that makes it clear what is grouped together

ls -R is meant to make it somewhat clear what the tests are and how they group together.  More than this, READMEs can be added to help people along the way.  See README.gcc for an example of such a document.  After reading it, you can see just how pointless it is.  You could contribute enhancements to it that explained it all...  Generally, no one else has deemed it worth their time.

>  -- so that new and existing developers can find what's been tested and not duplicate tests or effort.

That's easy to answer, if when you fix it, no test cases then passes that failed before, then the reduced testcase isn't duplicative of any other in the testsuite.  If there are testcases, then, you'd have to examine those by hand to see if they actually test the issue you had in mind.  This is in essence the definition of a regression test suite.  If it already works, the general hope is that there is a testcase (or a group of testcases) that tested it, but this in general isn't a given.

The testsuite isn't really a comprehensive feature/functionality testsuite.  That said, it does at times take on that appearance.

Generally, the gcc list or the wiki can be used to try and avoid duplicative work. For objc work, I think you could just send an email to Nicola.

> 3. if possible, the ability to run groups of related tests (preferably without excessive keystrokes) such that when working on one aspect of the FE one can reasonably quickly get feedback on problems.

When developing, I use ^R.exp^M.  Fairly short, runs just the testsuite I'm interested in.  At times, these can be lines that wrap 5 times.  That's my recommendation on what to use minute by minute.  For preping the first one, we'll, there is an unlimited number of answers I could put here, and they are all dependent upon what one wants to do.  The open ended command would be just make check.  If you have specific needs in mind, you'd have to ask a specific question for the specific answer.  There is no document that describes all the possible answers.  For example, running with -fnext-time and -fgnu-runtime is a wonderful test suite run, and makes for an interesting command line...

> 4. I'd prefer to avoid dg.exp (or any other dir) growing until it is 2000 files and the job is Too Big to fix it .. (but that might be a restatement of 1..3  ;-) )

It is _never_ too big to fix.  In fact, the time to fix it, is just after it _has_ grown too big.  The solution is equally trivial.  Run:

	ls | sed 's/-.*//' | sort | uniq -c | sort -nr | sed 3q 

For, for example, in a gcc.dg directory, I get:

110 c99 80 uninit 61 builtins 

110 c99
 80 uninit
 61 builtins

So, trivially, the command to fix this would be something like:

	svn mkdir c99 && svn mv c99-*.c c99

We can discuss what the right n is for splitting.  I might say 50, others might say 200.

>   -- we are already heading for 250 tests in dg.exp (and it would be nearer 450 without the ones we've already split out).

I'm told that modern machines can easily handle more than 500 files in a single directory.  :-)

> Mechanically, I think this can be done with dg.exp=<group-dir>/*.m .. as well as group.exp=*.m ...
> ... so I'm happy to retire the three or so 'un-necessary' .exp files and figure out a change to dg/lto/dg-torture.exp to handle the sub-dirs.

Yes, retiring unnecessary .exp files is good.

> I guess the pre-requisite is the bare-bones of (1) - or effort in re-organizing things is wasted (and time is a short-supply resource).

I don't favor changing test case names once created.  Splitting out larger swaths to reduce 4k files in a directory is generally considered good.

>> Wait, consensus, I thought we practiced benevolent dictator?  :-)
> 
> dang, and I thought this was floating anarchy :-)

Only to the untrained eye.  :-)  Sorry, couldn't resist.

> It creeps up on you...

:-)  If you ever notice it.  I last noticed it when I wanted to split testsuite jobs up for a -j8 testsuite run, and things like builtins.exp and dg.exp (or was it compile.exp or execute.exp) were a bit larger than I wanted.  I merely used =[a-m]*.c and [n-z]*.c.... to avoid the issue.

> ... you might be surprised to count up and see that we've almost doubled the ObjC test-case count (and more than doubled the ObjC++ count) since 4.5 forked.

Wake me when we get to 2k entries or 50 in a single group.  Not there yet.
diff mbox

Patch

Index: gcc/objc/objc-act.c
===================================================================
--- gcc/objc/objc-act.c	(revision 167216)
+++ gcc/objc/objc-act.c	(working copy)
@@ -5024,7 +5024,14 @@  static GTY(()) tree objc_eh_personality_decl;
 tree
 objc_eh_runtime_type (tree type)
 {
-  return add_objc_string (OBJC_TYPE_NAME (TREE_TYPE (type)), class_names);
+  /* Use 'ErrorMarkNode' as class name when error_mark_node is found
+     to prevent an ICE.  Note that we know that the compiler will
+     terminate with an error and this 'ErrorMarkNode' class name will
+     never be actually used.  */
+  if (type == error_mark_node)
+    return add_objc_string (get_identifier ("ErrorMarkNode"), class_names);
+  else
+    return add_objc_string (OBJC_TYPE_NAME (TREE_TYPE (type)), class_names);
 }
 
 tree
@@ -5355,7 +5362,9 @@  objc_begin_try_stmt (location_t try_locus, tree bo
 
 /* Called just after parsing "@catch (parm)".  Open a binding level,
    enter DECL into the binding level, and initialize it.  Leave the
-   binding level open while the body of the compound statement is parsed.  */
+   binding level open while the body of the compound statement is
+   parsed.  If DECL is NULL_TREE, then we are compiling "@catch(...)"
+   which we compile as "@catch(id tmp_variable)".  */
 
 void
 objc_begin_catch_clause (tree decl)
@@ -5365,46 +5374,99 @@  objc_begin_catch_clause (tree decl)
   /* Begin a new scope that the entire catch clause will live in.  */
   compound = c_begin_compound_stmt (true);
 
-  /* The parser passed in a PARM_DECL, but what we really want is a VAR_DECL.  */
-  decl = build_decl (input_location,
-		     VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl));
-  lang_hooks.decls.pushdecl (decl);
+  /* Create the appropriate declaration for the argument.  */
+ if (decl == error_mark_node)
+   type = error_mark_node;
+ else
+   {
+     if (decl == NULL_TREE)
+       {
+	 /* If @catch(...) was specified, create a temporary variable of
+	    type 'id' and use it.  */
+	 decl = objc_create_temporary_var (objc_object_type, "__objc_generic_catch_var");
+	 DECL_SOURCE_LOCATION (decl) = input_location;
+       }
+     else
+       {
+	 /* The parser passed in a PARM_DECL, but what we really want is a VAR_DECL.  */
+	 decl = build_decl (input_location,
+			    VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl));
+       }
+     lang_hooks.decls.pushdecl (decl);
 
-  /* Since a decl is required here by syntax, don't warn if its unused.  */
-  /* ??? As opposed to __attribute__((unused))?  Anyway, this appears to
-     be what the previous objc implementation did.  */
-  TREE_USED (decl) = 1;
-  DECL_READ_P (decl) = 1;
+     /* Mark the declaration as used so you never any warnings whether
+	you use the exception argument or not.  TODO: Implement a
+	-Wunused-exception-parameter flag, which would cause warnings
+	if exception parameter is not used.  */
+     TREE_USED (decl) = 1;
+     DECL_READ_P (decl) = 1;
 
-  /* Verify that the type of the catch is valid.  It must be a pointer
-     to an Objective-C class, or "id" (which is catch-all).  */
-  type = TREE_TYPE (decl);
+     type = TREE_TYPE (decl);
+   }
 
-  if (POINTER_TYPE_P (type) && objc_is_object_id (TREE_TYPE (type)))
-    type = NULL;
-  else if (!POINTER_TYPE_P (type) || !TYPED_OBJECT (TREE_TYPE (type)))
+ /* Verify that the type of the catch is valid.  It must be a pointer
+    to an Objective-C class, or "id" (which is catch-all).  */
+ if (type == error_mark_node)
+   {
+     ;/* Just keep going.  */
+   }
+ else if (!objc_type_valid_for_messaging (type, false))
     {
       error ("@catch parameter is not a known Objective-C class type");
       type = error_mark_node;
     }
-  else if (cur_try_context->catch_list)
+  else if (TYPE_HAS_OBJC_INFO (TREE_TYPE (type))
+	   && TYPE_OBJC_PROTOCOL_LIST (TREE_TYPE (type)))
     {
-      /* Examine previous @catch clauses and see if we've already
-	 caught the type in question.  */
-      tree_stmt_iterator i = tsi_start (cur_try_context->catch_list);
-      for (; !tsi_end_p (i); tsi_next (&i))
+      error ("@catch parameter can not be protocol-qualified");
+      type = error_mark_node;      
+    }
+  else if (objc_is_object_id (TREE_TYPE (type)))
+    type = NULL;
+  else
+    {
+      /* If 'type' was built using typedefs, we need to get rid of
+	 them and get a simple pointer to the class.  */
+      bool is_typedef = false;
+      tree x = TYPE_MAIN_VARIANT (type);
+      
+      /* Skip from the pointer to the pointee.  */
+      if (TREE_CODE (x) == POINTER_TYPE)
+	x = TREE_TYPE (x);
+      
+      /* Traverse typedef aliases */
+      while (TREE_CODE (x) == RECORD_TYPE && OBJC_TYPE_NAME (x)
+	     && TREE_CODE (OBJC_TYPE_NAME (x)) == TYPE_DECL
+	     && DECL_ORIGINAL_TYPE (OBJC_TYPE_NAME (x)))
 	{
-	  tree stmt = tsi_stmt (i);
-	  t = CATCH_TYPES (stmt);
-	  if (t == error_mark_node)
-	    continue;
-	  if (!t || DERIVED_FROM_P (TREE_TYPE (t), TREE_TYPE (type)))
+	  is_typedef = true;
+	  x = DECL_ORIGINAL_TYPE (OBJC_TYPE_NAME (x));
+	}
+
+      /* If it was a typedef, build a pointer to the final, original
+	 class.  */
+      if (is_typedef)
+	type = build_pointer_type (x);
+
+      if (cur_try_context->catch_list)
+	{
+	  /* Examine previous @catch clauses and see if we've already
+	     caught the type in question.  */
+	  tree_stmt_iterator i = tsi_start (cur_try_context->catch_list);
+	  for (; !tsi_end_p (i); tsi_next (&i))
 	    {
-	      warning (0, "exception of type %<%T%> will be caught",
-		       TREE_TYPE (type));
-	      warning_at  (EXPR_LOCATION (stmt), 0, "   by earlier handler for %<%T%>",
-			   TREE_TYPE (t ? t : objc_object_type));
-	      break;
+	      tree stmt = tsi_stmt (i);
+	      t = CATCH_TYPES (stmt);
+	      if (t == error_mark_node)
+		continue;
+	      if (!t || DERIVED_FROM_P (TREE_TYPE (t), TREE_TYPE (type)))
+		{
+		  warning (0, "exception of type %<%T%> will be caught",
+			   TREE_TYPE (type));
+		  warning_at  (EXPR_LOCATION (stmt), 0, "   by earlier handler for %<%T%>",
+			       TREE_TYPE (t ? t : objc_object_type));
+		  break;
+		}
 	    }
 	}
     }
Index: gcc/objc/ChangeLog
===================================================================
--- gcc/objc/ChangeLog	(revision 167216)
+++ gcc/objc/ChangeLog	(working copy)
@@ -1,3 +1,11 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc-act.c (objc_eh_runtime_type): Avoid ICE if error_mark_node
+	is passed as argument.
+	(objc_begin_catch_clause): Added code to deal with an
+	error_mark_node or NULL_TREE argument.  Improved checks for
+	invalid arguments.  Added code to traverse typedefs.
+
 2010-11-27  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	Implemented optional properties.
Index: gcc/ChangeLog
===================================================================
--- gcc/ChangeLog	(revision 167216)
+++ gcc/ChangeLog	(working copy)
@@ -1,3 +1,12 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* c-parser.c (c_parser_objc_try_catch_statement): Renamed to
+	c_parser_objc_try_catch_finally_statement for consistency with the
+	C++ parser.  Parse @catch(...) and pass NULL_TREE to
+	objc_begin_catch_clause() in that case.  Improved error recovery.
+	Reorganized code to be almost identical to
+	cp_parser_objc_try_catch_finally_statement.
+	
 2010-11-27  Jan Hubicka  <jh@suse.cz>
 
 	* dwarf2out.c (dwarf2out_begin_function): Set cold_text_section
Index: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog	(revision 167216)
+++ gcc/testsuite/ChangeLog	(working copy)
@@ -1,3 +1,18 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/dg.exp: Run the tests in objc.dg/exceptions as well.
+	* objc.dg/exceptions/exceptions-1.m: New.
+	* objc.dg/exceptions/exceptions-2.m: New.
+	* objc.dg/exceptions/exceptions-3.m: New.
+	* objc.dg/exceptions/exceptions-4.m: New.
+	* objc.dg/exceptions/exceptions-5.m: New.
+	* obj-c++.dg/dg.exp: Run the tests in obj-c++.dg/exceptions as well.
+	* obj-c++.dg/exceptions/exceptions-1.mm: New.
+	* obj-c++.dg/exceptions/exceptions-2.mm: New.
+	* obj-c++.dg/exceptions/exceptions-3.mm: New.
+	* obj-c++.dg/exceptions/exceptions-4.mm: New.
+	* obj-c++.dg/exceptions/exceptions-5.mm: New.
+
 2010-11-27  Tobias Burnus  <burnus@net-b.de>
 
 	PR fortran/46638
Index: gcc/testsuite/objc.dg/exceptions/exceptions-1.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-1.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-1.m	(revision 0)
@@ -0,0 +1,42 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  At the moment, @catch (...) is identical to @catch (id
+   exception).  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions-2.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-2.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-2.m	(revision 0)
@@ -0,0 +1,52 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-do run } */
+/* { dg-options "-fobjc-exceptions -fgnu-runtime" } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+/* { dg-additional-sources "../../objc-obj-c++-shared/Object1.m" } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  Check that code using it runs correctly.  */
+
+#include "../../objc-obj-c++-shared/Object1.h"
+#include <stdlib.h>
+
+@interface MyObject : Object
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
+
+int main (void)
+{
+  if (test ([MyObject new]) != 5)
+    abort ();
+
+  if (test ([Object new]) != 6)
+    abort ();
+
+  return 0;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions-3.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-3.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-3.m	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that the compiler is checking the argument of @catch(), and
+   produce errors when invalid types are used.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int x)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *x)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id x)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol> x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *x)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *x)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject x)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "conversion to non-scalar type requested" "" { target *-*-* } 72 } */
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *x) /* { dg-error "storage class specified for" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions-4.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-4.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-4.m	(revision 0)
@@ -0,0 +1,64 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test warnings when parsing syntax errors in @catch().  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@interface MyObject2
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject2
+@end
+
+@protocol MyProtocol;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch
+    { /* { dg-error "expected ... before ... token" } */
+      dummy++;
+    }
+  @catch ()  /* { dg-error "expected declaration specifiers or ..... before ..." } */
+    {
+      dummy++;
+    }
+  @catch (i) /* { dg-error "expected declaration specifiers or ..... before .i." } */
+    {
+      dummy++;
+    }
+  @catch (id <MyProtocol x) /* { dg-error "expected ... before .x." } */
+    {                       /* { dg-error "@catch parameter can not be protocol-qualified" "" { target *-*-* } 46 } */
+      dummy++;
+    }
+  @catch MyObject *x       /* { dg-error "expected ... before .MyObject." } */
+    {
+      dummy++;
+    }
+  @catch MyObject2 *x)      /* { dg-error "expected ... before .MyObject2." } */
+   {
+     dummy++;
+   }
+
+  @try { @throw object; }
+  @catch (MyObject *x)
+  @catch (MyObject2 *y)    /* { dg-error "expected ... before .catch." } */
+
+  return dummy;
+}
Index: gcc/testsuite/objc.dg/exceptions/exceptions-5.m
===================================================================
--- gcc/testsuite/objc.dg/exceptions/exceptions-5.m	(revision 0)
+++ gcc/testsuite/objc.dg/exceptions/exceptions-5.m	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that you can use an unnamed argument with @catch.  This test is the same
+   as exceptions-3.m, but with no name for @catch arguments.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol>) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "conversion to non-scalar type requested" "" { target *-*-* } 72 } */
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *) /* { dg-error "storage class specified for" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/objc.dg/dg.exp
===================================================================
--- gcc/testsuite/objc.dg/dg.exp	(revision 167216)
+++ gcc/testsuite/objc.dg/dg.exp	(working copy)
@@ -27,9 +27,21 @@  if ![info exists DEFAULT_CFLAGS] then {
 # Initialize `dg'.
 dg-init
 
-# Gather a list of all tests.
-set tests [lsort [glob -nocomplain $srcdir/$subdir/*.m]]
 
+## Gather a list of all tests.
+
+# Take the *.m tests from the top-level directory.
+set tests [glob -nocomplain $srcdir/$subdir/*.m]
+
+# Add the exceptions/*.m tests.
+set tests [concat [glob -nocomplain $srcdir/$subdir/exceptions/*.m] $tests]
+
+# Sort the result.
+set tests [lsort $tests]
+
+
+## Run the tests.
+
 # Main loop.
 dg-runtest $tests "-fgnu-runtime" $DEFAULT_CFLAGS
 
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-1.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-1.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-1.mm	(revision 0)
@@ -0,0 +1,42 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  At the moment, @catch (...) is identical to @catch (id
+   exception).  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-2.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-2.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-2.mm	(revision 0)
@@ -0,0 +1,54 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions -fgnu-runtime" } */
+
+/* FIXME: This does not test running the code, because Objective-C exceptions at the moment
+   do not execute correctly in Objective-C++.  See PR objc++/23616.  Once that is fixed,
+   this test should be changed to use 'dg-run' instead of just 'dg-compile'.  */
+/* { dg-compile } */
+
+/* This test checks the syntax @catch (...) which catches any
+   exceptions.  Check that code using it runs correctly.  */
+
+#include "../../objc-obj-c++-shared/Object1.h"
+#include <stdlib.h>
+
+@interface MyObject : Object
+@end
+
+@implementation MyObject
+@end
+
+int test (id object)
+{
+  int i = 0;
+
+  @try
+    {
+      @throw object;
+    }
+  @catch (MyObject *o)
+    {
+      i += 1;
+    }
+  @catch (...)
+    {
+      i += 2;
+    }
+  @finally
+    {
+      i += 4;
+    }
+
+  return i;
+}
+
+int main (void)
+{
+  if (test ([MyObject new]) != 5)
+    abort ();
+
+  if (test ([Object new]) != 6)
+    abort ();
+
+  return 0;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-3.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-3.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-3.mm	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that the compiler is checking the argument of @catch(), and
+   produce errors when invalid types are used.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int x)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *x)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id x)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol> x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *x)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *x)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject x)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "no matching function" "" { target *-*-* } 72 } */
+      dummy++;            /* { dg-warning "MyObject" "" { target *-*-* } 13 } */
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *x) /* { dg-error "storage class" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *x) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef x) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class x)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-4.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-4.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-4.mm	(revision 0)
@@ -0,0 +1,64 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test warnings when parsing syntax errors in @catch().  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@interface MyObject2
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject2
+@end
+
+@protocol MyProtocol;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch
+    {          /* { dg-error "expected" } */
+      dummy++; /* { dg-error "@catch parameter is not a known Objective-C class type" "" { target *-*-* } 35 } */
+    }
+  @catch ()  /* { dg-error "expected identifier before" } */
+    {        /* { dg-error "@catch parameter is not a known Objective-C class type" "" { target *-*-* } 38 } */
+      dummy++;
+    }
+  @catch (i) /* { dg-error ".i. has not been declared" } */
+    {        /* { dg-error "@catch parameter is not a known Objective-C class type" "" { target *-*-* } 42 } */
+      dummy++;
+    }
+  @catch (id <MyProtocol x) /* { dg-error "expected ... before .x." } */
+    {                       /* { dg-error "@catch parameter can not be protocol-qualified" "" { target *-*-* } 46 } */
+      dummy++;
+    }
+  @catch MyObject *x       /* { dg-error "expected ... before .MyObject." } */
+    {
+      dummy++;
+    }
+  @catch MyObject2 *x)     /* { dg-error "expected ... before .MyObject2." } */
+   {
+     dummy++;
+   }
+
+  @try { @throw object; }
+  @catch (MyObject *x)
+  @catch (MyObject2 *y)    /* { dg-error "expected ... before .catch." } */
+
+  return dummy;            /* { dg-error "expected ... before .return." } */
+}
Index: gcc/testsuite/obj-c++.dg/exceptions/exceptions-5.mm
===================================================================
--- gcc/testsuite/obj-c++.dg/exceptions/exceptions-5.mm	(revision 0)
+++ gcc/testsuite/obj-c++.dg/exceptions/exceptions-5.mm	(revision 0)
@@ -0,0 +1,114 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, November 2010.  */
+/* { dg-options "-fobjc-exceptions" } */
+/* { dg-do compile } */
+
+/* Test that you can use an unnamed argument with @catch.  This test is the same
+   as exceptions-3.mm, but with no name for @catch arguments.  */
+
+#include <objc/objc.h>
+
+@interface MyObject
+{
+  Class isa;
+}
+@end
+
+@implementation MyObject
+@end
+
+@protocol MyProtocol;
+
+typedef MyObject MyObjectTypedef;
+typedef MyObject *MyObjectPtrTypedef;
+typedef int intTypedef;
+
+int test (id object)
+{
+  int dummy = 0;
+
+  @try { @throw object; }
+  @catch (int)          /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (intTypedef)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (int *)         /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }  
+
+  @try { @throw object; }
+  @catch (id)           /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (id <MyProtocol>) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject *)    /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject <MyProtocol> *)  /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObject)     /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {                     /* { dg-error "no matching function" "" { target *-*-* } 72 } */
+      dummy++;            /* { dg-warning "MyObject" "" { target *-*-* } 13 } */
+    }
+
+  @try { @throw object; }
+  @catch (static MyObject *) /* { dg-error "storage class" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef *) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectTypedef <MyProtocol> *) /* { dg-error "@catch parameter can not be protocol-qualified" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (MyObjectPtrTypedef) /* Ok */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (Class)   /* { dg-error "@catch parameter is not a known Objective-C class type" } */
+    {
+      dummy++;
+    }
+
+  @try { @throw object; }
+  @catch (...)            /* Ok */
+    {
+      dummy++;
+    }
+
+  return dummy;
+}
Index: gcc/testsuite/obj-c++.dg/dg.exp
===================================================================
--- gcc/testsuite/obj-c++.dg/dg.exp	(revision 167216)
+++ gcc/testsuite/obj-c++.dg/dg.exp	(working copy)
@@ -27,9 +27,21 @@  if ![info exists DEFAULT_OBJCXXFLAGS] then {
 # Initialize `dg'.
 dg-init
 
-# Gather a list of all tests.
-set tests [lsort [glob -nocomplain $srcdir/$subdir/*.mm]]
 
+## Gather a list of all tests.
+
+# Take the *.mm tests from the top-level directory.
+set tests [glob -nocomplain $srcdir/$subdir/*.mm]
+
+# Add the exceptions/*.mm tests.
+set tests [concat [glob -nocomplain $srcdir/$subdir/exceptions/*.mm] $tests]
+
+# Sort the result.
+set tests [lsort $tests]
+
+
+## Run the tests.
+
 # Main loop.
 dg-runtest $tests "-fgnu-runtime" $DEFAULT_OBJCXXFLAGS
 
Index: gcc/cp/ChangeLog
===================================================================
--- gcc/cp/ChangeLog	(revision 167216)
+++ gcc/cp/ChangeLog	(working copy)
@@ -1,3 +1,10 @@ 
+2010-11-28  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* parser.c (cp_parser_objc_try_catch_finally_statement): Parse
+	@catch(...)  and pass NULL_TREE to objc_begin_catch_clause() in
+	that case.  Improved error recovery.  Reorganized code to be
+	almost identical to c_parser_objc_try_catch_finally_statement.
+
 2010-11-27  Nicola Pero  <nicola.pero@meta-innovation.com>
 
 	PR objc++/46222
Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 167216)
+++ gcc/cp/parser.c	(working copy)
@@ -22598,15 +22598,25 @@  cp_parser_objc_declaration (cp_parser* parser, tre
      objc-catch-clause objc-catch-clause-seq [opt]
 
    objc-catch-clause:
-     @catch ( exception-declaration ) compound-statement
+     @catch ( objc-exception-declaration ) compound-statement
 
-   objc-finally-clause
+   objc-finally-clause:
      @finally compound-statement
 
-   Returns NULL_TREE.  */
+   objc-exception-declaration:
+     parameter-declaration
+     '...'
 
+   where '...' is to be interpreted literally, that is, it means CPP_ELLIPSIS.
+
+   Returns NULL_TREE.
+
+   PS: This function is identical to c_parser_objc_try_catch_finally_statement
+   for C.  Keep them in sync.  */   
+
 static tree
-cp_parser_objc_try_catch_finally_statement (cp_parser *parser) {
+cp_parser_objc_try_catch_finally_statement (cp_parser *parser)
+{
   location_t location;
   tree stmt;
 
@@ -22620,22 +22630,60 @@  static tree
 
   while (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_CATCH))
     {
-      cp_parameter_declarator *parmdecl;
-      tree parm;
+      cp_parameter_declarator *parm;
+      tree parameter_declaration = error_mark_node;
+      bool seen_open_paren = false;
 
       cp_lexer_consume_token (parser->lexer);
-      cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
-      parmdecl = cp_parser_parameter_declaration (parser, false, NULL);
-      parm = grokdeclarator (parmdecl->declarator,
-			     &parmdecl->decl_specifiers,
-			     PARM, /*initialized=*/0,
-			     /*attrlist=*/NULL);
-      cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
-      objc_begin_catch_clause (parm);
+      if (cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+	seen_open_paren = true;
+      if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+	{
+	  /* We have "@catch (...)" (where the '...' are literally
+	     what is in the code).  Skip the '...'.
+	     parameter_declaration is set to NULL_TREE, and
+	     objc_being_catch_clauses() knows that that means
+	     '...'.  */
+	  cp_lexer_consume_token (parser->lexer);
+	  parameter_declaration = NULL_TREE;
+	}
+      else
+	{
+	  /* We have "@catch (NSException *exception)" or something
+	     like that.  Parse the parameter declaration.  */
+	  parm = cp_parser_parameter_declaration (parser, false, NULL);
+	  if (parm == NULL)
+	    parameter_declaration = error_mark_node;
+	  else
+	    parameter_declaration = grokdeclarator (parm->declarator,
+						    &parm->decl_specifiers,
+						    PARM, /*initialized=*/0,
+						    /*attrlist=*/NULL);
+	}
+      if (seen_open_paren)
+	cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
+      else
+	{
+	  /* If there was no open parenthesis, we are recovering from
+	     an error, and we are trying to figure out what mistake
+	     the user has made.  */
+
+	  /* If there is an immediate closing parenthesis, the user
+	     probably forgot the opening one (ie, they typed "@catch
+	     NSException *e)".  Parse the closing parenthesis and keep
+	     going.  */
+	  if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    cp_lexer_consume_token (parser->lexer);
+	  
+	  /* If these is no immediate closing parenthesis, the user
+	     probably doesn't know that parenthesis are required at
+	     all (ie, they typed "@catch NSException *e").  So, just
+	     forget about the closing parenthesis and keep going.  */
+	}
+      objc_begin_catch_clause (parameter_declaration);
       cp_parser_compound_statement (parser, NULL, false);
       objc_finish_catch_clause ();
     }
-
   if (cp_lexer_next_token_is_keyword (parser->lexer, RID_AT_FINALLY))
     {
       cp_lexer_consume_token (parser->lexer);
Index: gcc/c-parser.c
===================================================================
--- gcc/c-parser.c	(revision 167216)
+++ gcc/c-parser.c	(working copy)
@@ -1155,7 +1155,7 @@  static void c_parser_objc_methodproto (c_parser *)
 static tree c_parser_objc_method_decl (c_parser *, bool, tree *);
 static tree c_parser_objc_type_name (c_parser *);
 static tree c_parser_objc_protocol_refs (c_parser *);
-static void c_parser_objc_try_catch_statement (c_parser *);
+static void c_parser_objc_try_catch_finally_statement (c_parser *);
 static void c_parser_objc_synchronized_statement (c_parser *);
 static tree c_parser_objc_selector (c_parser *);
 static tree c_parser_objc_selector_arg (c_parser *);
@@ -4371,7 +4371,7 @@  c_parser_statement_after_labels (c_parser *parser)
 	  break;
 	case RID_AT_TRY:
 	  gcc_assert (c_dialect_objc ());
-	  c_parser_objc_try_catch_statement (parser);
+	  c_parser_objc_try_catch_finally_statement (parser);
 	  break;
 	case RID_AT_SYNCHRONIZED:
 	  gcc_assert (c_dialect_objc ());
@@ -7468,53 +7468,97 @@  c_parser_objc_protocol_refs (c_parser *parser)
   return list;
 }
 
-/* Parse an objc-try-catch-statement.
+/* Parse an objc-try-catch-finally-statement.
 
-   objc-try-catch-statement:
+   objc-try-catch-finally-statement:
      @try compound-statement objc-catch-list[opt]
      @try compound-statement objc-catch-list[opt] @finally compound-statement
 
    objc-catch-list:
-     @catch ( parameter-declaration ) compound-statement
-     objc-catch-list @catch ( parameter-declaration ) compound-statement
-*/
+     @catch ( objc-catch-parameter-declaration ) compound-statement
+     objc-catch-list @catch ( objc-catch-parameter-declaration ) compound-statement
 
+   objc-catch-parameter-declaration:
+     parameter-declaration
+     '...'
+
+   where '...' is to be interpreted literally, that is, it means CPP_ELLIPSIS.
+
+   PS: This function is identical to cp_parser_objc_try_catch_finally_statement
+   for C++.  Keep them in sync.  */   
+
 static void
-c_parser_objc_try_catch_statement (c_parser *parser)
+c_parser_objc_try_catch_finally_statement (c_parser *parser)
 {
-  location_t loc;
+  location_t location;
   tree stmt;
+
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_TRY));
   c_parser_consume_token (parser);
-  loc = c_parser_peek_token (parser)->location;
+  location = c_parser_peek_token (parser)->location;
   stmt = c_parser_compound_statement (parser);
-  objc_begin_try_stmt (loc, stmt);
+  objc_begin_try_stmt (location, stmt);
+
   while (c_parser_next_token_is_keyword (parser, RID_AT_CATCH))
     {
       struct c_parm *parm;
+      tree parameter_declaration = error_mark_node;
+      bool seen_open_paren = false;
+
       c_parser_consume_token (parser);
       if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
-	break;
-      parm = c_parser_parameter_declaration (parser, NULL_TREE);
-      if (parm == NULL)
+	seen_open_paren = true;
+      if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
 	{
-	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
-	  break;
+	  /* We have "@catch (...)" (where the '...' are literally
+	     what is in the code).  Skip the '...'.
+	     parameter_declaration is set to NULL_TREE, and
+	     objc_being_catch_clauses() knows that that means
+	     '...'.  */
+	  c_parser_consume_token (parser);
+	  parameter_declaration = NULL_TREE;
 	}
-      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>");
-      objc_begin_catch_clause (grokparm (parm));
+      else
+	{
+	  /* We have "@catch (NSException *exception)" or something
+	     like that.  Parse the parameter declaration.  */
+	  parm = c_parser_parameter_declaration (parser, NULL_TREE);
+	  if (parm == NULL)
+	    parameter_declaration = error_mark_node;
+	  else
+	    parameter_declaration = grokparm (parm);
+	}
+      if (seen_open_paren)
+	c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>");
+      else
+	{
+	  /* If there was no open parenthesis, we are recovering from
+	     an error, and we are trying to figure out what mistake
+	     the user has made.  */
+
+	  /* If there is an immediate closing parenthesis, the user
+	     probably forgot the opening one (ie, they typed "@catch
+	     NSException *e)".  Parse the closing parenthesis and keep
+	     going.  */
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	    c_parser_consume_token (parser);
+	  
+	  /* If these is no immediate closing parenthesis, the user
+	     probably doesn't know that parenthesis are required at
+	     all (ie, they typed "@catch NSException *e").  So, just
+	     forget about the closing parenthesis and keep going.  */
+	}
+      objc_begin_catch_clause (parameter_declaration);
       if (c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>"))
 	c_parser_compound_statement_nostart (parser);
       objc_finish_catch_clause ();
     }
   if (c_parser_next_token_is_keyword (parser, RID_AT_FINALLY))
     {
-      location_t finloc;
-      tree finstmt;
       c_parser_consume_token (parser);
-      finloc = c_parser_peek_token (parser)->location;
-      finstmt = c_parser_compound_statement (parser);
-      objc_build_finally_clause (finloc, finstmt);
+      location = c_parser_peek_token (parser)->location;
+      stmt = c_parser_compound_statement (parser);
+      objc_build_finally_clause (location, stmt);
     }
   objc_finish_try_stmt ();
 }