Updated ARM Spec
Joe Darcy
joe.darcy at oracle.com
Thu Jul 15 14:44:34 PDT 2010
Greetings.
Starting with Project Coin proposal for Automatic Resource Management
(ARM), in consultation with Josh Bloch, Maurizio, Jon, and others, Alex
and I have produced a specification for ARM blocks that is much closer
to Java Language Specification (JLS) style and rigor. The specification
involves changes to the existing JLS section 14.20 "The try statement,"
and will eventually introduce a new subsection 14.20.3 "Execution of
try-with-resources," although the specification below is not partitioned
as such. Non-normative comments about the specification text below
appear inside "[]". Differences between the new specification and the
earlier Project Coin proposal for ARM are discussed after the specification.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
SYNTAX: The existing set of grammar productions for TryStatement in JLS
14.20 is augmented with:
TryStatement:
try ResourceSpecification Block Catches_opt Finally_opt
Supporting new grammar productions are added:
ResourceSpecification:
( Resources )
Resources:
Resource
Resource ; Resources
Resource:
VariableModifiers Type VariableDeclaratorId = Expression
Expression
[An implication of the combined grammar is that a try statement must
have at least one of a catch clause, a finally block, and a resource
specification. Furthermore, it is permissible for a try statement to
have exactly one of these three components. Note that it is illegal to
have a trailing semi-colon in the resource specification.]
A try-with-resources statement has a resource specification that
expresses resources to be automatically closed at the end of the Block.
A resource specification declares one or more local variables and/or has
one or more expressions, each of whose type must be a subtype of
AutoCloseable or a compile-time error occurs.
If a resource specification declares a variable, the variable must not
have the same name as a variable declared earlier in the resource
specification, a local variable, or parameter of the method or
initializer block immediately enclosing the try statement, or a
compile-time error occurs.
The scope of a variable declared in a resource specification of a
try-with-resources statement (§14.20) is from the declaration rightward
over the remainder of the resource specification and the entire Block
associated with the try. Within the Block of the try, the name of the
variable may not be redeclared as a local variable of the directly
enclosing method or initializer block, nor may it be redeclared as an
exception parameter of a catch clause in a try statement of the directly
enclosing method or initializer block, nor may it be redeclared as a
variable in the resource specification, or a compile-time error occurs.
However, a variable declared in a resource specification may be shadowed
(§6.3.1) anywhere inside a class declaration nested within the Block of
the try.
The meaning of a try-with-resources statement with a Catches clause or
Finally block is given by translation to a try-with-resources statement
with no Catches clause or Finally block:
try ResourceSpecification
Block
Catches_opt
Finally_opt
=>
try {
try ResourceSpecification
Block
}
Catches_opt
Finally_opt
In a try-with-resources statement that manages a single resource:
* If the initialization of the resource completes abruptly because of a
throw of a value V, or if the Block of the try-with-resources statement
completes abruptly because of a throw of a value V and the automatic
closing of the resource completes normally, then the try-with-resources
statement completes abruptly because of the throw of value V.
* If the Block of the try-with-resources statement completes abruptly
because of a throw of a value V1, and the automatic closing of the
resource completes abruptly because of a throw of a value V2, then the
try-with-resources statement completes abruptly because of the throw of
value V1, provided that V2 is an Exception. In this case, V2 is added to
the suppressed exception list of V1. If V2 is an error (i.e. a Throwable
that is not an Exception), then the try-with-resources statement
completes abruptly because of the throw of value V2. In this case, V1 is
not suppressed by V2.
If a try-with-resources statement that manages multiple resources:
* If the initialization of a resource completes abruptly because of a
throw of a value V, or if the Block of the try-with-resources statement
completes abruptly because of a throw of a value V (which implies that
the initialization of all resources completed normally) and the
automatic closings of all resources completes normally, then the
try-with-resources statement completes abruptly because of the throw of
value V.
* If the Block of the try-with-resources statement completes abruptly
because of a throw of a value V1, and the automatic closings of one or
more resources (that were previously successfully initialized) complete
abruptly because of throws of values V2...Vn, then the
try-with-resources statement completes abruptly because of the throw of
a value Vi (1 <= i <= n) determined by the translation below.
The exceptions that can be thrown by a try-with-resources statement are
the exceptions that can thrown by the Block of the try-with-resources
statement plus the union of the exceptions that can be thrown by the
automatic closing of the resources themselves. Regardless of the number
of resources managed by a try-with-resources statement, it is possible
for a Catches_opt clause to catch an exception due to initialization or
automatic closing of any resource.
A try-with-resources statement with a ResourceSpecification clause that
declares multiple Resources is treated as if it were multiple
try-with-resources statements, each of which has a ResourceSpecification
clause that declares a single Resource. When a try-with-resources
statement with n Resources (n > 1) is translated, the result is a
try-with-resources statement with n-1 Resources. After n such
translations, there are n nested try-catch-finally statements, and the
overall translation is complete.
The meaning of a try-with-resources statement with a
ResourceSpecification clause and no Catches clause or Finally block is
given by translation to a local variable declaration and a
try-catch-finally statement. During translation, if the
ResourceSpecification clause declares one Resource, then the
try-catch-finally statement is not a try-with-resources statement, and
ResourceSpecification_tail is empty. If the ResourceSpecification clause
declares n Resources, then the try-catch-finally statement is treated as
if it were a try-with-resources-catch-finally statement, where
ResourceSpecificationtail is a ResourceSpecification consisting of the
2nd, 3rd, ..., nth Resources in order. The translation is as follows,
where the identifiers #primaryException, #t, and #suppressedException
are fresh:
try ResourceSpecification
Block
=>
{
final VariableModifiers_minus_final R #resource = Expression;
Throwable #primaryException = null;
try ResourceSpecification_tail
Block
catch (final Throwable #t) {
#primaryException = t;
throw #t;
} finally {
if (#primaryException != null) {
try {
#resource.close();
} catch(Exception #suppressedException) {
#primaryException.addSuppressedException(#suppressedException);
}
} else {
#resource.close();
}
}
If the Resource being translated declares a variable, then
VariableModifiers_minus_final is the set of modifiers on the variable
(except for final if present); R is the type of the variable
declaration; and #resource is the name of the variable declared in the
Resource.
Discussion: Resource declarations in a resource specification are
implicitly final. For consistency with existing declarations that have
implicit modifiers, it is legal (though discouraged) for a programmer to
provide an explicit "final" modifier. By allowing non-final modifiers,
annotations such as @SuppressWarnings will be preserved on the
translated code. It is unlikely that the Java programming language will
ever ascribe a meaning to an explicit final modifier in this location
other than the traditional meaning. [Unlike the new meaning ascribed to
a final exception parameter.]
Discussion: Unlike the fresh identifier in the translation of the
enhanced-for statement, the #resource variable is in scope in the Block
of a try-with-resources statement.
If the Resource being translated is an Expression, then the translation
includes an local variable declaration for which
VariableModifiers_minus_final is empty; the type R is the type of the
Expression (under the condition that the Expression is assigned to a
variable of type AutoCloseable); and #resource is a fresh identifier.
Discussion: The method Throwable.addSuppressedException has a parameter
of type Throwable, but the translation is such that only an Exception
from #resource.close() will be passed for suppression. In the judgment
of the designers of the Java programming language, an Error due to
automatic closing of a resource is sufficiently serious that it should
not be automatically suppressed in favor of an exception from the Block
or the initialization or automatic closing of lexically rightward
resources. [However, perhaps such an Error should instead be recorded
as suppressing an exception from the Block or other lexically rightward
component.]
Discussion: This translation exploits the improved precision of
exception analysis now triggered by the rethrow of a final exception
parameter.
The reachability and definite assignment rules for the try statement
with a resource specification are implicitly specified by the
translations above.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
Compared to the earlier proposal, this draft specification:
* Assumes the revised supporting API with java.lang.AutoCloseable as the
type indicating participation in the new language feature.
* Changes the official grammar for a declared resource from
LocalVariableDeclaration
to
VariableModifiers Type VariableDeclaratorId = Expression
The former syntactically allowed code like
AutoCloseable a, b, c
which would not be useful in this context.
* Preserves modifiers on explicitly declared resources, which implies
@SuppressWarnings on a resource should have the intended effect.
* States how the exception behavior of close methods is accounted for in
determining the set of exceptions a try-with-resource statement can throw.
* Gives a more precise determination of the type used for the local
variable holding a resource given as an Expression. This precision is
important to allow accurate exception information to be computed.
* Provides typing constraints so that type inference works as expected
if the Expression given as a Resource in a ResourceSpecification is,
say, a generic method or null.
Compiler changes implementing this revised specification remain in
progress. After experience is gained with the initial implementation, I
expect various changes to the feature to be contemplated:
* Dropping support for a resource to be specified as a general
Expression. Nontrivial specification and implementation complexities
arise from allowing a general Expression to be used as resource.
Allowing a restricted expression that was just a name may provide nearly
all the additional flexibility at marginal additional implementation and
specification impact.
* Adjustments to the suppressed exception logic: in the present
specification, an incoming primary exception will suppress an Exception
thrown by a close method; however, if the close method throws an error,
that error is propagated out without suppressing an incoming primary
exception. Possible alternatives include having a primary exception in a
try-with-resource statement suppress all subsequent Throwables
originating in the statement and having a non-Exception thrown by a
close suppress any incoming primary exception.
These alternatives could be implemented by replacing the translated code
try {
#resource.close();
} catch(Exception #suppressedException) {
#primaryException.addSuppressedException(#suppressedException);
}
with
try {
#resource.close();
} catch(Throwable #suppressedException) {
#primaryException.addSuppressedException(#suppressedException);
}
or
try {
#resource.close();
} catch(Exception #suppressedException) {
#primaryException.addSuppressedException(#suppressedException);
} catch(Throwable #throwable) {
#throwable.addSuppressedException(#primaryException);
throw #throwable;
}
respectively.
-Joe
More information about the coin-dev
mailing list