Project Coin: multi-catch and final rethrow

As alluded to as a possibility previously, I'm happy to announce that improved exception handling with multi-catch and final rethrow will be part of an upcoming JDK 7 build. Improved exception handling is joining other Project Coin features available in the repository after successful experiences with a multi-catch implementation developed by Maurizio Cimadamore.

Maurizio's work also revealed and corrected a flaw in the originally proposed static analysis for the set of exception that can be rethrown; from the original proposal form for this feature:

[a] final catch parameter is treated as throwing precisely those exception types that

  • the try block can throw,
  • no previous catch clause handles, and
  • is a subtype of one of the types in the declaration of the catch parameter

Consider a final rethrow statement as below where the dynamic class of a thrown exception differs from the static type (due to a cast in this case):

class Neg04 {
  static class A extends Exception {}
  static class B extends Exception {}

  void test(boolean b1, boolean b2) throws B {
      try {
          if (b1) {
              throw new A();
          } else if (b2) {
              throw new B();
          } else {
              throw (Throwable)new Exception();
          }
      }
      catch (A e) {}
      catch (final Exception e) {
          throw e;
      }
      catch (Throwable t) {}
  }
}

The set of exceptions thrown by the try block is computed {A, B, Throwable}; therefore, the set of exceptions that can be rethrown is the set of exceptions from the try block:

  1. minus A, handled by a previous catch clause, giving {B, Throwable}
  2. minus Throwable since Throwable is not a subtype of one of the types declared for the catch parameter (just Exception in this case), leaving only {B}

However, if an Exception is thrown from the try block it should be caught in the "catch(final Exception e)" clause even if the exception is cast to Throwable since catch clauses work based on the runtime class of the exceptions being thrown.

To address this, the third clause is changed to

  • is a subtype/supertype of one of the types in the declaration of the catch parameter

More formally, this clause covers computing a join over the set of thrown exceptions, eliminating subtypes. In the example above {Throwable} is computed as the set of exceptions being throwable from the try block. This is then intersected with the exceptions that can be caught by the catch block, resulting in {Exception}, a properly sound result.

Very general exception types being thrown by a try block would reduce the utility of multi-catch since only imprecise information would be available. Fortunately, from analyzing the JDK sources, throwing a statically imprecise exception seems rare, indicating multi-catch with the amended specification should still be very be helpful in practice.

Today the adventurous can apply a changest to a copy of the JDK 7 langtools repository and do a build to get a compiler supporting this feature. Otherwise, following the integration process, improved exception handling will appear in the promoted JDK 7 builds in due course.

Comments:

What is the problem that final retrow is trying to solve?
I like the multi-catch idea, but final retrow looks like an unneeded complexity...

Posted by G on May 03, 2010 at 08:39 PM PDT #

Although at first was having the same sentiment as G, reading http://www.javac.info/Rethrown.html clear some things up. As far as I can gather now this post is more trying to implement it right and that marking final + compiler smartness does dismiss you from defining exceptions. Where currently you have to declare throwable, in the new situation(with compiler smartness) you should be able to get away with just redefining the checked - and uncaught - exceptions as being thrown from the method.

So here instead of 'void test(boolean b1, boolean b2) throws Throwable' Adding the final bit allows you to write it as void test(boolean b1, boolean b2) throws B,Exception therefore not losing your checked-exception-typesafety(nice scrabble word)

Ps. I added Exception to the signature because I think it's missing from the code above?
If the changes outlined above imply that Exception actually shouldn't be part of the method signature then I'm confused - but since darcy saids
"if an Exception is thrown from the try block it should be caught in the "catch(final Exception e)" clause even if the exception is cast to Throwable since catch clauses work based on the runtime class of the exceptions being thrown." I'm pretty sure I'm right with the Exception having to be part of the signature because of it being caught and not rethrown could _only_ occur if it was handled by the catch(Throwable t) which that statement contradicts

Posted by Michael on May 03, 2010 at 10:45 PM PDT #

I find this usage of "final" to be very confusing. In an effort to not add keywords we are totally washing away the meaning of final. Final means that a variable cannot change its value, or alternatively that a method/class cannot be subclassed. This third meaning is very confusing.

Just add another keyword or something. Don't water down a clear concept. Also note that this is an ambiguous usage. Maybe the programmer marked it "final" because he wanted to pass the info to another thread or to an annonymous class (I admit this is weird, but thusfar syntactically valid).

I oppose the use of the keyword final here. Use something else.

Posted by am on May 04, 2010 at 05:49 AM PDT #

Fantastic news Joe.

I happen to be in the 'keep checked exceptions' camp but aside from the good aspects they can certainly make for inelegant solutions and unnecessary effort.

These two exception-handling constructs make some of these cases simpler without compromising the explicit handling or declaration of exceptions (and without unnecessarily widening the declared exception types being thrown).

Great to hear this is going in.

Posted by Talden on May 04, 2010 at 06:45 AM PDT #

am, you misunderstand the use of "final" here. The use is still as it is today -- to prevent reassignment. This allows the compiler to know that the type its catching cannot be altered, which otherwise would defeat the compiler's inference on what can be rethrown.

Posted by Paul on May 04, 2010 at 07:22 AM PDT #

Ok.. so you're saying that the compiler will know that "Exception e" is always a B?

Ah. Pretty cool. Then I don't need to do weird things to rethrow.. I see the light now.

Posted by am on May 04, 2010 at 07:47 AM PDT #

Joe, here is what you explained:
>> However, if an Exception is thrown from the try block it should be caught in the "catch(final Exception e)" clause even if the exception is cast to Throwable since catch clauses work based on the runtime class of the exceptions being thrown.

So I deduced:
1) Method is declared to throw checked exception B
2) A exception is caught by the "A" handler and does nothing
3) B exception is caught by the "final Exception e" handler and re-throws runtime type B
4) According to your explanation, the "(Throwable)new Exception()" is caught by "final Exception e" handler and re-throws... but it's not of type B.

Have I misunderstood #4?

Posted by Paul on May 05, 2010 at 03:13 AM PDT #

Paul, if you look at my reply above you'd see that I came to that same conclusion.

Posted by Michael on May 05, 2010 at 09:28 AM PDT #

Indeed, http://www.javac.info/Rethrown.html 's explanation is far clearer than the explanation given in this blog post.

Posted by Gili on May 05, 2010 at 02:41 PM PDT #

I'm currently at the JAX 2010 in Mainz/Germany and heard the speak of Dalibor Topic (Sun Germany) with the topic "JDK7 Update".

While he is mentioning the multi-catch feature I just wonder why there is no plan to support the catch without any special exception in its clause? Sometimes I programmed in C#/.NET and liked the catch with the "else" semantic.

Sample to show what I mean:

try {
// some code
}
catch (FirstException fe) {
// handling of fe
}
catch (SecondException se) {
// handling of se
}
catch {
// handling of all other exceptions
}

Maybe this is a synonym to "catch (Throwable t) {}", but it's very "handy" and good readable and so...

Posted by Ronald on May 05, 2010 at 05:49 PM PDT #

The problem is the difference between the static types of thrown exceptions and the dynamic types of thrown exception. The difference was not considered by sketch at javac.info. The difference can cause the static analysis to think that every possible exception type is either caught or declared in 'throws', while in fact the method can throw additional undeclared exception types at runtime. The tweak to the spec improves the static analysis so a discrepancy cannot arise.

Posted by Alex Buckley on May 06, 2010 at 01:24 PM PDT #

This is simply terrible. Please, please reconsider what you are doing to the language. The abuse of "final" as described in previous comments is just unforgivable. If this the way the language is going to evolve under Oracle's stewardship, I'll be looking to change.

Posted by Basho on May 15, 2010 at 08:35 AM PDT #

The compiler does not need the "final" annotation to know that 'e' is not rebound, it could easily scan the code in the catch(Throwable e) block to verify that. It would be a simple future enhancement to decouple this from the presence of the "final" keyword.

That said: it's been my custom for many years to write "final" there anyway, so that human readers don't have to scan the block to check that there's no strangeness afoot.

Posted by James Dennett on May 16, 2010 at 05:21 AM PDT #

The final keyword seems redundant and abusing it for exception handling is hard to justify. If we catch Throwable and allow rethrows of it to throw the actual type instead of a Throwable the code would be nicer looking and involve same amount of "magic"/syntactic sugar as the proposed solution (with final keyword). Eg:

public void test() throws IOException
{
try
{
throw new IOException();
}
catch (Throwable t)
{
throw t;
}
}

Posted by Patrik on May 16, 2010 at 04:54 PM PDT #

I agree that this is an abuse of the "final" keyword, and including this into the language standard is an incompatible change, as James Dennett pointed out above.

As currently any unchecked throwables are either RuntimeExceptions or Errors, it is currently quite simple to handle them:

public void test() {
try {
// do some productive stuff
} catch (RuntimeException e) {
handle(e);
throw e;
} catch (Error e) {
handle(e);
throw e;
}
}

private void handle(Throwable e) {
// do some error handling
}

This could be further simplified by introducing a common super-interface of both RuntimeException and Error, e. g. UncheckedThrowable:

public void test() {
try {
// do some productive stuff
} catch (UncheckedThrowable e) {
handle(e);
throw e;
}
}

private void handle(Throwable e) {
// do some error handling
}

This is equivalent in complexity with the "final" proposal.

Posted by Leo141 on May 16, 2010 at 06:59 PM PDT #

@Leo141,

Interface types cannot be caught.

Posted by Joe Darcy on May 17, 2010 at 02:58 AM PDT #

Abuse nothing. It's the same as it always was, preventing reassignment. Maybe you should complain about the fact that variables are mutable by default, since that seems your real complaint.

Or in the 21 century version, variables are not a ownership type by default (this would help the gc immensely i think)

Not null by default would be nice too.
These properties (nullable, multiple ownership and mutable) should have been added to the visibility keywords long ago.

Posted by paulolevi on May 18, 2010 at 01:23 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

darcy

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
News

No bookmarks in folder

Blogroll