6911256: Project Coin: Support Automatic Resource Management (ARM) blocks in the compiler
authordarcy
Fri, 16 Jul 2010 19:35:24 -0700
changeset 6148 3a8158299c51
parent 6147 0074061d0efd
child 6149 48de3564aa13
6911256: Project Coin: Support Automatic Resource Management (ARM) blocks in the compiler 6964740: Project Coin: More tests for ARM compiler changes 6965277: Project Coin: Correctness issues in ARM implementation 6967065: add -Xlint warning category for Automatic Resource Management (ARM) Reviewed-by: jjb, darcy, mcimadamore, jjg, briangoetz Contributed-by: tball@google.com
langtools/make/build.properties
langtools/src/share/classes/com/sun/source/tree/TryTree.java
langtools/src/share/classes/com/sun/source/util/TreeScanner.java
langtools/src/share/classes/com/sun/tools/javac/code/Lint.java
langtools/src/share/classes/com/sun/tools/javac/code/Source.java
langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java
langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java
langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java
langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java
langtools/src/share/classes/com/sun/tools/javac/comp/TransTypes.java
langtools/src/share/classes/com/sun/tools/javac/jvm/CRTable.java
langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java
langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java
langtools/src/share/classes/com/sun/tools/javac/tree/Pretty.java
langtools/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java
langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java
langtools/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java
langtools/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java
langtools/src/share/classes/com/sun/tools/javac/util/Names.java
langtools/test/tools/javac/TryWithResources/ArmLint.java
langtools/test/tools/javac/TryWithResources/ArmLint.out
langtools/test/tools/javac/TryWithResources/BadTwr.java
langtools/test/tools/javac/TryWithResources/BadTwr.out
langtools/test/tools/javac/TryWithResources/BadTwrSyntax.java
langtools/test/tools/javac/TryWithResources/BadTwrSyntax.out
langtools/test/tools/javac/TryWithResources/DuplicateResource.java
langtools/test/tools/javac/TryWithResources/DuplicateResourceDecl.java
langtools/test/tools/javac/TryWithResources/DuplicateResourceDecl.out
langtools/test/tools/javac/TryWithResources/ImplicitFinal.java
langtools/test/tools/javac/TryWithResources/ImplicitFinal.out
langtools/test/tools/javac/TryWithResources/PlainTry.java
langtools/test/tools/javac/TryWithResources/PlainTry.out
langtools/test/tools/javac/TryWithResources/PlainTry6.out
langtools/test/tools/javac/TryWithResources/ResourceOutsideTry.java
langtools/test/tools/javac/TryWithResources/ResourceOutsideTry.out
langtools/test/tools/javac/TryWithResources/ResourceTypeVar.java
langtools/test/tools/javac/TryWithResources/TwrFlow.java
langtools/test/tools/javac/TryWithResources/TwrFlow.out
langtools/test/tools/javac/TryWithResources/TwrInference.java
langtools/test/tools/javac/TryWithResources/TwrIntersection.java
langtools/test/tools/javac/TryWithResources/TwrIntersection02.java
langtools/test/tools/javac/TryWithResources/TwrIntersection02.out
langtools/test/tools/javac/TryWithResources/TwrMultiCatch.java
langtools/test/tools/javac/TryWithResources/TwrOnNonResource.java
langtools/test/tools/javac/TryWithResources/TwrOnNonResource.out
langtools/test/tools/javac/TryWithResources/TwrTests.java
langtools/test/tools/javac/TryWithResources/WeirdTwr.java
langtools/test/tools/javac/processing/model/element/TestResourceVariable.java
--- a/langtools/make/build.properties	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/make/build.properties	Fri Jul 16 19:35:24 2010 -0700
@@ -107,7 +107,8 @@
         javax/annotation/processing/ \
         javax/lang/model/ \
         javax/tools/ \
-        com/sun/source/ com/sun/tools/javac/
+        com/sun/source/ \
+        com/sun/tools/javac/
 
 javac.tests = \
         tools/javac
--- a/langtools/src/share/classes/com/sun/source/tree/TryTree.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/source/tree/TryTree.java	Fri Jul 16 19:35:24 2010 -0700
@@ -49,4 +49,5 @@
     BlockTree getBlock();
     List<? extends CatchTree> getCatches();
     BlockTree getFinallyBlock();
+    List<? extends Tree> getResources();
 }
--- a/langtools/src/share/classes/com/sun/source/util/TreeScanner.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/source/util/TreeScanner.java	Fri Jul 16 19:35:24 2010 -0700
@@ -209,7 +209,8 @@
     }
 
     public R visitTry(TryTree node, P p) {
-        R r = scan(node.getBlock(), p);
+        R r = scan(node.getResources(), p);
+        r = scanAndReduce(node.getBlock(), p, r);
         r = scanAndReduce(node.getCatches(), p, r);
         r = scanAndReduce(node.getFinallyBlock(), p, r);
         return r;
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Lint.java	Fri Jul 16 19:35:24 2010 -0700
@@ -208,7 +208,12 @@
         /**
          * Warn about potentially unsafe vararg methods
          */
-        VARARGS("varargs");
+        VARARGS("varargs"),
+
+        /**
+         * Warn about arm resources
+         */
+        ARM("arm");
 
         LintCategory(String option) {
             this(option, false);
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Source.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Source.java	Fri Jul 16 19:35:24 2010 -0700
@@ -159,6 +159,9 @@
     public boolean enforceMandatoryWarnings() {
         return compareTo(JDK1_5) >= 0;
     }
+    public boolean allowTryWithResources() {
+        return compareTo(JDK1_7) >= 0;
+    }
     public boolean allowTypeAnnotations() {
         return compareTo(JDK1_7) >= 0;
     }
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symbol.java	Fri Jul 16 19:35:24 2010 -0700
@@ -993,12 +993,17 @@
             return data == ElementKind.EXCEPTION_PARAMETER;
         }
 
+        public boolean isResourceVariable() {
+            return data == ElementKind.RESOURCE_VARIABLE;
+        }
+
         public Object getConstValue() {
             // TODO: Consider if getConstValue and getConstantValue can be collapsed
-            if (data == ElementKind.EXCEPTION_PARAMETER) {
+            if (data == ElementKind.EXCEPTION_PARAMETER ||
+                data == ElementKind.RESOURCE_VARIABLE) {
                 return null;
             } else if (data instanceof Callable<?>) {
-                // In this case, this is final a variable, with an as
+                // In this case, this is a final variable, with an as
                 // yet unevaluated initializer.
                 Callable<?> eval = (Callable<?>)data;
                 data = null; // to make sure we don't evaluate this twice.
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java	Fri Jul 16 19:35:24 2010 -0700
@@ -148,6 +148,7 @@
     public final Type inheritedType;
     public final Type proprietaryType;
     public final Type systemType;
+    public final Type autoCloseableType;
 
     /** The symbol representing the length field of an array.
      */
@@ -159,6 +160,9 @@
     /** The symbol representing the final finalize method on enums */
     public final MethodSymbol enumFinalFinalize;
 
+    /** The symbol representing the close method on TWR AutoCloseable type */
+    public final MethodSymbol autoCloseableClose;
+
     /** The predefined type that belongs to a tag.
      */
     public final Type[] typeOfTag = new Type[TypeTags.TypeTagCount];
@@ -444,6 +448,12 @@
         suppressWarningsType = enterClass("java.lang.SuppressWarnings");
         inheritedType = enterClass("java.lang.annotation.Inherited");
         systemType = enterClass("java.lang.System");
+        autoCloseableType = enterClass("java.lang.AutoCloseable");
+        autoCloseableClose = new MethodSymbol(PUBLIC,
+                             names.close,
+                             new MethodType(List.<Type>nil(), voidType,
+                                            List.of(exceptionType), methodClass),
+                             autoCloseableType.tsym);
 
         synthesizeEmptyInterfaceIfMissing(cloneableType);
         synthesizeEmptyInterfaceIfMissing(serializableType);
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jul 16 19:35:24 2010 -0700
@@ -192,7 +192,7 @@
     Type check(JCTree tree, Type owntype, int ownkind, int pkind, Type pt) {
         if (owntype.tag != ERROR && pt.tag != METHOD && pt.tag != FORALL) {
             if ((ownkind & ~pkind) == 0) {
-                owntype = chk.checkType(tree.pos(), owntype, pt);
+                owntype = chk.checkType(tree.pos(), owntype, pt, errKey);
             } else {
                 log.error(tree.pos(), "unexpected.type",
                           kindNames(pkind),
@@ -239,7 +239,11 @@
              !((base == null ||
                (base.getTag() == JCTree.IDENT && TreeInfo.name(base) == names._this)) &&
                isAssignableAsBlankFinal(v, env)))) {
-            log.error(pos, "cant.assign.val.to.final.var", v);
+            if (v.isResourceVariable()) { //TWR resource
+                log.error(pos, "twr.resource.may.not.be.assigned", v);
+            } else {
+                log.error(pos, "cant.assign.val.to.final.var", v);
+            }
         }
     }
 
@@ -372,6 +376,10 @@
      */
     Type pt;
 
+    /** Visitor argument: the error key to be generated when a type error occurs
+     */
+    String errKey;
+
     /** Visitor result: the computed type.
      */
     Type result;
@@ -385,13 +393,19 @@
      *  @param pt      The prototype visitor argument.
      */
     Type attribTree(JCTree tree, Env<AttrContext> env, int pkind, Type pt) {
+        return attribTree(tree, env, pkind, pt, "incompatible.types");
+    }
+
+    Type attribTree(JCTree tree, Env<AttrContext> env, int pkind, Type pt, String errKey) {
         Env<AttrContext> prevEnv = this.env;
         int prevPkind = this.pkind;
         Type prevPt = this.pt;
+        String prevErrKey = this.errKey;
         try {
             this.env = env;
             this.pkind = pkind;
             this.pt = pt;
+            this.errKey = errKey;
             tree.accept(this);
             if (tree == breakTree)
                 throw new BreakAttr(env);
@@ -403,6 +417,7 @@
             this.env = prevEnv;
             this.pkind = prevPkind;
             this.pt = prevPt;
+            this.errKey = prevErrKey;
         }
     }
 
@@ -412,6 +427,10 @@
         return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType);
     }
 
+    public Type attribExpr(JCTree tree, Env<AttrContext> env, Type pt, String key) {
+        return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType, key);
+    }
+
     /** Derived visitor method: attribute an expression tree with
      *  no constraints on the computed type.
      */
@@ -976,14 +995,34 @@
     }
 
     public void visitTry(JCTry tree) {
+        // Create a new local environment with a local
+        Env<AttrContext> localEnv = env.dup(tree, env.info.dup(env.info.scope.dup()));
+        boolean isTryWithResource = tree.resources.nonEmpty();
+        // Create a nested environment for attributing the try block if needed
+        Env<AttrContext> tryEnv = isTryWithResource ?
+            env.dup(tree, localEnv.info.dup(localEnv.info.scope.dup())) :
+            localEnv;
+        // Attribute resource declarations
+        for (JCTree resource : tree.resources) {
+            if (resource.getTag() == JCTree.VARDEF) {
+                attribStat(resource, tryEnv);
+                chk.checkType(resource, resource.type, syms.autoCloseableType, "twr.not.applicable.to.type");
+                VarSymbol var = (VarSymbol)TreeInfo.symbolFor(resource);
+                var.setData(ElementKind.RESOURCE_VARIABLE);
+            } else {
+                attribExpr(resource, tryEnv, syms.autoCloseableType, "twr.not.applicable.to.type");
+            }
+        }
         // Attribute body
-        attribStat(tree.body, env.dup(tree, env.info.dup()));
+        attribStat(tree.body, tryEnv);
+        if (isTryWithResource)
+            tryEnv.info.scope.leave();
 
         // Attribute catch clauses
         for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
             JCCatch c = l.head;
             Env<AttrContext> catchEnv =
-                env.dup(c, env.info.dup(env.info.scope.dup()));
+                localEnv.dup(c, localEnv.info.dup(localEnv.info.scope.dup()));
             Type ctype = attribStat(c.param, catchEnv);
             if (TreeInfo.isMultiCatch(c)) {
                 //check that multi-catch parameter is marked as final
@@ -1003,7 +1042,9 @@
         }
 
         // Attribute finalizer
-        if (tree.finalizer != null) attribStat(tree.finalizer, env);
+        if (tree.finalizer != null) attribStat(tree.finalizer, localEnv);
+
+        localEnv.info.scope.leave();
         result = null;
     }
 
@@ -2139,6 +2180,15 @@
                 checkAssignable(tree.pos(), v, tree.selected, env);
         }
 
+        if (sitesym != null &&
+                sitesym.kind == VAR &&
+                ((VarSymbol)sitesym).isResourceVariable() &&
+                sym.kind == MTH &&
+                sym.overrides(syms.autoCloseableClose, sitesym.type.tsym, types, true) &&
+                env.info.lint.isEnabled(Lint.LintCategory.ARM)) {
+            log.warning(tree, "twr.explicit.close.call");
+        }
+
         // Disallow selecting a type from an expression
         if (isType(sym) && (sitesym==null || (sitesym.kind&(TYP|PCK)) == 0)) {
             tree.type = check(tree.selected, pt,
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Fri Jul 16 19:35:24 2010 -0700
@@ -393,6 +393,10 @@
      *  @param req        The type that was required.
      */
     Type checkType(DiagnosticPosition pos, Type found, Type req) {
+        return checkType(pos, found, req, "incompatible.types");
+    }
+
+    Type checkType(DiagnosticPosition pos, Type found, Type req, String errKey) {
         if (req.tag == ERROR)
             return req;
         if (found.tag == FORALL)
@@ -411,7 +415,7 @@
             log.error(pos, "assignment.to.extends-bound", req);
             return types.createErrorType(found);
         }
-        return typeError(pos, diags.fragment("incompatible.types"), found, req);
+        return typeError(pos, diags.fragment(errKey), found, req);
     }
 
     /** Instantiate polymorphic type to some prototype, unless
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Jul 16 19:35:24 2010 -0700
@@ -28,6 +28,8 @@
 package com.sun.tools.javac.comp;
 
 import java.util.HashMap;
+import java.util.Map;
+import java.util.LinkedHashMap;
 
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.tree.*;
@@ -265,6 +267,10 @@
      */
     List<Type> caught;
 
+    /** The list of unreferenced automatic resources.
+     */
+    Map<VarSymbol, JCVariableDecl> unrefdResources;
+
     /** Set when processing a loop body the second time for DU analysis. */
     boolean loopPassTwo = false;
 
@@ -963,6 +969,7 @@
     public void visitTry(JCTry tree) {
         List<Type> caughtPrev = caught;
         List<Type> thrownPrev = thrown;
+        Map<VarSymbol, JCVariableDecl> unrefdResourcesPrev = unrefdResources;
         thrown = List.nil();
         for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
             List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ?
@@ -977,6 +984,32 @@
         pendingExits = new ListBuffer<PendingExit>();
         Bits initsTry = inits.dup();
         uninitsTry = uninits.dup();
+        unrefdResources = new LinkedHashMap<VarSymbol, JCVariableDecl>();
+        for (JCTree resource : tree.resources) {
+            if (resource instanceof JCVariableDecl) {
+                JCVariableDecl vdecl = (JCVariableDecl) resource;
+                visitVarDef(vdecl);
+                unrefdResources.put(vdecl.sym, vdecl);
+            } else if (resource instanceof JCExpression) {
+                scanExpr((JCExpression) resource);
+            } else {
+                throw new AssertionError(tree);  // parser error
+            }
+        }
+        for (JCTree resource : tree.resources) {
+            MethodSymbol topCloseMethod = (MethodSymbol)syms.autoCloseableType.tsym.members().lookup(names.close).sym;
+            List<Type> closeableSupertypes = resource.type.isCompound() ?
+                types.interfaces(resource.type).prepend(types.supertype(resource.type)) :
+                List.of(resource.type);
+            for (Type sup : closeableSupertypes) {
+                if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) {
+                    MethodSymbol closeMethod = types.implementation(topCloseMethod, sup.tsym, types, true);
+                    for (Type t : closeMethod.getThrownTypes()) {
+                        markThrown(tree.body, t);
+                    }
+                }
+            }
+        }
         scanStat(tree.body);
         List<Type> thrownInTry = thrown;
         thrown = thrownPrev;
@@ -987,6 +1020,14 @@
         Bits uninitsEnd = uninits;
         int nextadrCatch = nextadr;
 
+        if (!unrefdResources.isEmpty() &&
+                lint.isEnabled(Lint.LintCategory.ARM)) {
+            for (Map.Entry<VarSymbol, JCVariableDecl> e : unrefdResources.entrySet()) {
+                log.warning(e.getValue().pos(),
+                            "automatic.resource.not.referenced", e.getKey());
+            }
+        }
+
         List<Type> caughtInTry = List.nil();
         for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
             alive = true;
@@ -1070,6 +1111,7 @@
             while (exits.nonEmpty()) pendingExits.append(exits.next());
         }
         uninitsTry.andSet(uninitsTryPrev).andSet(uninits);
+        unrefdResources = unrefdResourcesPrev;
     }
 
     public void visitConditional(JCConditional tree) {
@@ -1293,8 +1335,16 @@
     }
 
     public void visitIdent(JCIdent tree) {
-        if (tree.sym.kind == VAR)
+        if (tree.sym.kind == VAR) {
             checkInit(tree.pos(), (VarSymbol)tree.sym);
+            referenced(tree.sym);
+        }
+    }
+
+    void referenced(Symbol sym) {
+        if (unrefdResources != null && unrefdResources.containsKey(sym)) {
+            unrefdResources.remove(sym);
+        }
     }
 
     public void visitTypeCast(JCTypeCast tree) {
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java	Fri Jul 16 19:35:24 2010 -0700
@@ -605,6 +605,23 @@
         s.enter(sym);
     }
 
+    /** Create a fresh synthetic name within a given scope - the unique name is
+     *  obtained by appending '$' chars at the end of the name until no match
+     *  is found.
+     *
+     * @param name base name
+     * @param s scope in which the name has to be unique
+     * @return fresh synthetic name
+     */
+    private Name makeSyntheticName(Name name, Scope s) {
+        do {
+            name = name.append(
+                    target.syntheticNameChar(),
+                    names.empty);
+        } while (lookupSynthetic(name, s) != null);
+        return name;
+    }
+
     /** Check whether synthetic symbols generated during lowering conflict
      *  with user-defined symbols.
      *
@@ -1299,6 +1316,11 @@
      */
     Scope proxies;
 
+    /** A scope containing all unnamed resource variables/saved
+     *  exception variables for translated TWR blocks
+     */
+    Scope twrVars;
+
     /** A stack containing the this$n field of the currently translated
      *  classes (if needed) in innermost first order.
      *  Inside a constructor, proxies and any this$n symbol are duplicated
@@ -1400,6 +1422,122 @@
         }
     }
 
+    /** Optionally replace a try statement with an automatic resource
+     *  management (ARM) block.
+     * @param tree  The try statement to inspect.
+     * @return      An ARM block, or the original try block if there are no
+     *              resources to manage.
+     */
+    JCTree makeArmTry(JCTry tree) {
+        make_at(tree.pos());
+        twrVars = twrVars.dup();
+        JCBlock armBlock = makeArmBlock(tree.resources, tree.body, 0);
+        if (tree.catchers.isEmpty() && tree.finalizer == null)
+            result = translate(armBlock);
+        else
+            result = translate(make.Try(armBlock, tree.catchers, tree.finalizer));
+        twrVars = twrVars.leave();
+        return result;
+    }
+
+    private JCBlock makeArmBlock(List<JCTree> resources, JCBlock block, int depth) {
+        if (resources.isEmpty())
+            return block;
+
+        // Add resource declaration or expression to block statements
+        ListBuffer<JCStatement> stats = new ListBuffer<JCStatement>();
+        JCTree resource = resources.head;
+        JCExpression expr = null;
+        if (resource instanceof JCVariableDecl) {
+            JCVariableDecl var = (JCVariableDecl) resource;
+            expr = make.Ident(var.sym).setType(resource.type);
+            stats.add(var);
+        } else {
+            assert resource instanceof JCExpression;
+            VarSymbol syntheticTwrVar =
+            new VarSymbol(SYNTHETIC | FINAL,
+                          makeSyntheticName(names.fromString("twrVar" +
+                                           depth), twrVars),
+                          (resource.type.tag == TypeTags.BOT) ?
+                          syms.autoCloseableType : resource.type,
+                          currentMethodSym);
+            twrVars.enter(syntheticTwrVar);
+            JCVariableDecl syntheticTwrVarDecl =
+                make.VarDef(syntheticTwrVar, (JCExpression)resource);
+            expr = (JCExpression)make.Ident(syntheticTwrVar);
+            stats.add(syntheticTwrVarDecl);
+        }
+
+        // Add primaryException declaration
+        VarSymbol primaryException =
+            new VarSymbol(SYNTHETIC,
+                          makeSyntheticName(names.fromString("primaryException" +
+                          depth), twrVars),
+                          syms.throwableType,
+                          currentMethodSym);
+        twrVars.enter(primaryException);
+        JCVariableDecl primaryExceptionTreeDecl = make.VarDef(primaryException, makeNull());
+        stats.add(primaryExceptionTreeDecl);
+
+        // Create catch clause that saves exception and then rethrows it
+        VarSymbol param =
+            new VarSymbol(FINAL|SYNTHETIC,
+                          names.fromString("t" +
+                                           target.syntheticNameChar()),
+                          syms.throwableType,
+                          currentMethodSym);
+        JCVariableDecl paramTree = make.VarDef(param, null);
+        JCStatement assign = make.Assignment(primaryException, make.Ident(param));
+        JCStatement rethrowStat = make.Throw(make.Ident(param));
+        JCBlock catchBlock = make.Block(0L, List.<JCStatement>of(assign, rethrowStat));
+        JCCatch catchClause = make.Catch(paramTree, catchBlock);
+
+        int oldPos = make.pos;
+        make.at(TreeInfo.endPos(block));
+        JCBlock finallyClause = makeArmFinallyClause(primaryException, expr);
+        make.at(oldPos);
+        JCTry outerTry = make.Try(makeArmBlock(resources.tail, block, depth + 1),
+                                  List.<JCCatch>of(catchClause),
+                                  finallyClause);
+        stats.add(outerTry);
+        return make.Block(0L, stats.toList());
+    }
+
+    private JCBlock makeArmFinallyClause(Symbol primaryException, JCExpression resource) {
+        // primaryException.addSuppressedException(catchException);
+        VarSymbol catchException =
+            new VarSymbol(0, make.paramName(2),
+                          syms.throwableType,
+                          currentMethodSym);
+        JCStatement addSuppressionStatement =
+            make.Exec(makeCall(make.Ident(primaryException),
+                               names.fromString("addSuppressedException"),
+                               List.<JCExpression>of(make.Ident(catchException))));
+
+        // try { resource.close(); } catch (e) { primaryException.addSuppressedException(e); }
+        JCBlock tryBlock =
+            make.Block(0L, List.<JCStatement>of(makeResourceCloseInvocation(resource)));
+        JCVariableDecl catchExceptionDecl = make.VarDef(catchException, null);
+        JCBlock catchBlock = make.Block(0L, List.<JCStatement>of(addSuppressionStatement));
+        List<JCCatch> catchClauses = List.<JCCatch>of(make.Catch(catchExceptionDecl, catchBlock));
+        JCTry tryTree = make.Try(tryBlock, catchClauses, null);
+
+        // if (resource != null) resourceClose;
+        JCExpression nullCheck = makeBinary(JCTree.NE,
+                                            make.Ident(primaryException),
+                                            makeNull());
+        JCIf closeIfStatement = make.If(nullCheck,
+                                        tryTree,
+                                        makeResourceCloseInvocation(resource));
+        return make.Block(0L, List.<JCStatement>of(closeIfStatement));
+    }
+
+    private JCStatement makeResourceCloseInvocation(JCExpression resource) {
+        // create resource.close() method invocation
+        JCExpression resourceClose = makeCall(resource, names.close, List.<JCExpression>nil());
+        return make.Exec(resourceClose);
+    }
+
     /** Construct a tree that represents the outer instance
      *  <C.this>. Never pick the current `this'.
      *  @param pos           The source code position to be used for the tree.
@@ -3405,6 +3543,15 @@
         result = tree;
     }
 
+    @Override
+    public void visitTry(JCTry tree) {
+        if (tree.resources.isEmpty()) {
+            super.visitTry(tree);
+        } else {
+            result = makeArmTry(tree);
+        }
+    }
+
 /**************************************************************************
  * main method
  *************************************************************************/
@@ -3430,6 +3577,7 @@
             actualSymbols = new HashMap<Symbol,Symbol>();
             freevarCache = new HashMap<ClassSymbol,List<VarSymbol>>();
             proxies = new Scope(syms.noSymbol);
+            twrVars = new Scope(syms.noSymbol);
             outerThisStack = List.nil();
             accessNums = new HashMap<Symbol,Integer>();
             accessSyms = new HashMap<Symbol,MethodSymbol[]>();
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/TransTypes.java	Fri Jul 16 19:35:24 2010 -0700
@@ -535,6 +535,14 @@
         result = tree;
     }
 
+    public void visitTry(JCTry tree) {
+        tree.resources = translate(tree.resources, syms.autoCloseableType);
+        tree.body = translate(tree.body);
+        tree.catchers = translateCatchers(tree.catchers);
+        tree.finalizer = translate(tree.finalizer);
+        result = tree;
+    }
+
     public void visitConditional(JCConditional tree) {
         tree.cond = translate(tree.cond, syms.booleanType);
         tree.truepart = translate(tree.truepart, erasure(tree.type));
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/CRTable.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/CRTable.java	Fri Jul 16 19:35:24 2010 -0700
@@ -325,6 +325,7 @@
 
         public void visitTry(JCTry tree) {
             SourceRange sr = new SourceRange(startPos(tree), endPos(tree));
+            sr.mergeWith(csp(tree.resources));
             sr.mergeWith(csp(tree.body));
             sr.mergeWith(cspCatchers(tree.catchers));
             sr.mergeWith(csp(tree.finalizer));
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Fri Jul 16 19:35:24 2010 -0700
@@ -131,6 +131,7 @@
         this.allowForeach = source.allowForeach();
         this.allowStaticImport = source.allowStaticImport();
         this.allowAnnotations = source.allowAnnotations();
+        this.allowTWR = source.allowTryWithResources();
         this.allowDiamond = source.allowDiamond();
         this.allowMulticatch = source.allowMulticatch();
         this.allowTypeAnnotations = source.allowTypeAnnotations();
@@ -186,6 +187,10 @@
      */
     boolean allowTypeAnnotations;
 
+    /** Switch: should we recognize automatic resource management?
+     */
+    boolean allowTWR;
+
     /** Switch: should we keep docComments?
      */
     boolean keepDocComments;
@@ -1846,6 +1851,7 @@
      *     | WHILE ParExpression Statement
      *     | DO Statement WHILE ParExpression ";"
      *     | TRY Block ( Catches | [Catches] FinallyPart )
+     *     | TRY "(" ResourceSpecification ")" Block [Catches] [FinallyPart]
      *     | SWITCH ParExpression "{" SwitchBlockStatementGroups "}"
      *     | SYNCHRONIZED ParExpression Block
      *     | RETURN [Expression] ";"
@@ -1916,6 +1922,13 @@
         }
         case TRY: {
             S.nextToken();
+            List<JCTree> resources = List.<JCTree>nil();
+            if (S.token() == LPAREN) {
+                checkAutomaticResourceManagement();
+                S.nextToken();
+                resources = resources();
+                accept(RPAREN);
+            }
             JCBlock body = block();
             ListBuffer<JCCatch> catchers = new ListBuffer<JCCatch>();
             JCBlock finalizer = null;
@@ -1926,9 +1939,13 @@
                     finalizer = block();
                 }
             } else {
-                log.error(pos, "try.without.catch.or.finally");
+                if (allowTWR) {
+                    if (resources.isEmpty())
+                        log.error(pos, "try.without.catch.finally.or.resource.decls");
+                } else
+                    log.error(pos, "try.without.catch.or.finally");
             }
-            return F.at(pos).Try(body, catchers.toList(), finalizer);
+            return F.at(pos).Try(resources, body, catchers.toList(), finalizer);
         }
         case SWITCH: {
             S.nextToken();
@@ -2389,6 +2406,39 @@
         return toP(F.at(pos).VarDef(mods, name, type, null));
     }
 
+    /** Resources = Resource { ";" Resources }
+     */
+    List<JCTree> resources() {
+        ListBuffer<JCTree> defs = new ListBuffer<JCTree>();
+        defs.append(resource());
+        while (S.token() == SEMI) {
+            // All but last of multiple declarators subsume a semicolon
+            storeEnd(defs.elems.last(), S.endPos());
+            S.nextToken();
+            defs.append(resource());
+        }
+        return defs.toList();
+    }
+
+    /** Resource =
+     *    VariableModifiers Type VariableDeclaratorId = Expression
+     *  | Expression
+     */
+    JCTree resource() {
+        int pos = S.pos();
+        if (S.token() == FINAL || S.token() == MONKEYS_AT) {
+            return variableDeclaratorRest(pos, optFinal(0), parseType(),
+                                          ident(), true, null);
+        } else {
+            JCExpression t = term(EXPR | TYPE);
+            if ((lastmode & TYPE) != 0 && S.token() == IDENTIFIER)
+                return variableDeclaratorRest(pos, toP(F.at(pos).Modifiers(Flags.FINAL)), t,
+                                              ident(), true, null);
+            else
+                return t;
+        }
+    }
+
     /** CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration}
      */
     public JCTree.JCCompilationUnit parseCompilationUnit() {
@@ -3220,6 +3270,12 @@
         if (!allowMulticatch) {
             log.error(S.pos(), "multicatch.not.supported.in.source", source.name);
             allowMulticatch = true;
-            }
+        }
+    }
+    void checkAutomaticResourceManagement() {
+        if (!allowTWR) {
+            log.error(S.pos(), "automatic.resource.management.not.supported.in.source", source.name);
+            allowTWR = true;
+        }
     }
 }
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Jul 16 19:35:24 2010 -0700
@@ -61,6 +61,8 @@
     anonymous class implements interface; cannot have type arguments
 compiler.err.anon.class.impl.intf.no.qual.for.new=\
     anonymous class implements interface; cannot have qualifier for new
+compiler.misc.twr.not.applicable.to.type=\
+    automatic resource management not applicable to variable type
 compiler.err.array.and.varargs=\
     cannot declare both {0} and {1} in {2}
 compiler.err.array.dimension.missing=\
@@ -172,6 +174,8 @@
 
 compiler.err.final.parameter.may.not.be.assigned=\
     final parameter {0} may not be assigned
+compiler.err.twr.resource.may.not.be.assigned=\
+    automatic resource {0} may not be assigned
 compiler.err.multicatch.parameter.may.not.be.assigned=\
     multi-catch parameter {0} may not be assigned
 compiler.err.multicatch.param.must.be.final=\
@@ -448,6 +452,8 @@
     throws clause not allowed in @interface members
 compiler.err.try.without.catch.or.finally=\
     ''try'' without ''catch'' or ''finally''
+compiler.err.try.without.catch.finally.or.resource.decls=\
+    ''try'' without ''catch'', ''finally'' or resource declarations
 compiler.err.type.doesnt.take.params=\
     type {0} does not take parameters
 compiler.err.type.var.cant.be.deref=\
@@ -797,6 +803,10 @@
 compiler.warn.proc.unmatched.processor.options=\
     The following options were not recognized by any processor: ''{0}''
 
+compiler.warn.twr.explicit.close.call=\
+    [arm] explicit call to close() on an automatic resource
+compiler.warn.automatic.resource.not.referenced=\
+    [arm] automatic resource {0} is never referenced in body of corresponding try statement
 compiler.warn.unchecked.assign=\
     [unchecked] unchecked assignment: {0} to {1}
 compiler.warn.unchecked.assign.to.var=\
@@ -1217,6 +1227,10 @@
     underscores in literals are not supported in -source {0}\n\
 (use -source 7 or higher to enable underscores in literals)
 
+compiler.err.automatic.resource.management.not.supported.in.source=\
+    automatic resource management is not supported in -source {0}\n\
+(use -source 7 or higher to enable automatic resource management)
+
 compiler.warn.enum.as.identifier=\
     as of release 5, ''enum'' is a keyword, and may not be used as an identifier\n\
 (use -source 5 or higher to use ''enum'' as a keyword)
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java	Fri Jul 16 19:35:24 2010 -0700
@@ -1021,10 +1021,15 @@
         public JCBlock body;
         public List<JCCatch> catchers;
         public JCBlock finalizer;
-        protected JCTry(JCBlock body, List<JCCatch> catchers, JCBlock finalizer) {
+        public List<JCTree> resources;
+        protected JCTry(List<JCTree> resources,
+                        JCBlock body,
+                        List<JCCatch> catchers,
+                        JCBlock finalizer) {
             this.body = body;
             this.catchers = catchers;
             this.finalizer = finalizer;
+            this.resources = resources;
         }
         @Override
         public void accept(Visitor v) { v.visitTry(this); }
@@ -1040,6 +1045,10 @@
             return v.visitTry(this, d);
         }
         @Override
+        public List<? extends JCTree> getResources() {
+            return resources;
+        }
+        @Override
         public int getTag() {
             return TRY;
         }
@@ -2162,6 +2171,10 @@
         JCCase Case(JCExpression pat, List<JCStatement> stats);
         JCSynchronized Synchronized(JCExpression lock, JCBlock body);
         JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
+        JCTry Try(List<JCTree> resources,
+                  JCBlock body,
+                  List<JCCatch> catchers,
+                  JCBlock finalizer);
         JCCatch Catch(JCVariableDecl param, JCBlock body);
         JCConditional Conditional(JCExpression cond,
                                 JCExpression thenpart,
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/Pretty.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/Pretty.java	Fri Jul 16 19:35:24 2010 -0700
@@ -691,6 +691,19 @@
     public void visitTry(JCTry tree) {
         try {
             print("try ");
+            if (tree.resources.nonEmpty()) {
+                print("(");
+                boolean first = true;
+                for (JCTree var : tree.resources) {
+                    if (!first) {
+                        println();
+                        indent();
+                    }
+                    printStat(var);
+                    first = false;
+                }
+                print(") ");
+            }
             printStat(tree.body);
             for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
                 printStat(l.head);
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Fri Jul 16 19:35:24 2010 -0700
@@ -332,10 +332,11 @@
 
     public JCTree visitTry(TryTree node, P p) {
         JCTry t = (JCTry) node;
+        List<JCTree> resources = copy(t.resources, p);
         JCBlock body = copy(t.body, p);
         List<JCCatch> catchers = copy(t.catchers, p);
         JCBlock finalizer = copy(t.finalizer, p);
-        return M.at(t.pos).Try(body, catchers, finalizer);
+        return M.at(t.pos).Try(resources, body, catchers, finalizer);
     }
 
     public JCTree visitParameterizedType(ParameterizedTypeTree node, P p) {
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Fri Jul 16 19:35:24 2010 -0700
@@ -269,7 +269,14 @@
     }
 
     public JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer) {
-        JCTry tree = new JCTry(body, catchers, finalizer);
+        return Try(List.<JCTree>nil(), body, catchers, finalizer);
+    }
+
+    public JCTry Try(List<JCTree> resources,
+                     JCBlock body,
+                     List<JCCatch> catchers,
+                     JCBlock finalizer) {
+        JCTry tree = new JCTry(resources, body, catchers, finalizer);
         tree.pos = pos;
         return tree;
     }
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java	Fri Jul 16 19:35:24 2010 -0700
@@ -147,6 +147,7 @@
     }
 
     public void visitTry(JCTry tree) {
+        scan(tree.resources);
         scan(tree.body);
         scan(tree.catchers);
         scan(tree.finalizer);
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java	Fri Jul 16 19:35:24 2010 -0700
@@ -212,6 +212,7 @@
     }
 
     public void visitTry(JCTry tree) {
+        tree.resources = translate(tree.resources);
         tree.body = translate(tree.body);
         tree.catchers = translateCatchers(tree.catchers);
         tree.finalizer = translate(tree.finalizer);
--- a/langtools/src/share/classes/com/sun/tools/javac/util/Names.java	Thu Jul 15 16:31:56 2010 +0100
+++ b/langtools/src/share/classes/com/sun/tools/javac/util/Names.java	Fri Jul 16 19:35:24 2010 -0700
@@ -148,6 +148,8 @@
     public final Name getDeclaringClass;
     public final Name ex;
     public final Name finalize;
+    public final Name java_lang_AutoCloseable;
+    public final Name close;
 
     public final Name.Table table;
 
@@ -263,6 +265,9 @@
         getDeclaringClass = fromString("getDeclaringClass");
         ex = fromString("ex");
         finalize = fromString("finalize");
+
+        java_lang_AutoCloseable = fromString("java.lang.AutoCloseable");
+        close = fromString("close");
     }
 
     protected Name.Table createTable(Options options) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/ArmLint.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,55 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740 6965277 6967065
+ * @author Joseph D. Darcy
+ * @summary Check that -Xlint:arm warnings are generated as expected
+ * @compile/ref=ArmLint.out -Xlint:arm,deprecation -XDrawDiagnostics ArmLint.java
+ */
+
+class ArmLint implements AutoCloseable {
+    private static void test1() {
+        try(ArmLint r1 = new ArmLint();
+            ArmLint r2 = new ArmLint();
+            ArmLint r3 = new ArmLint()) {
+            r1.close();   // The resource's close
+            r2.close(42); // *Not* the resource's close
+            // r3 not referenced
+        }
+
+    }
+
+    @SuppressWarnings("arm")
+    private static void test2() {
+        try(@SuppressWarnings("deprecation") AutoCloseable r4 =
+            new DeprecatedAutoCloseable()) {
+            // r4 not referenced
+        } catch(Exception e) {
+            ;
+        }
+    }
+
+    /**
+     * The AutoCloseable method of a resource.
+     */
+    @Override
+    public void close () {
+        return;
+    }
+
+    /**
+     * <em>Not</em> the AutoCloseable method of a resource.
+     */
+    public void close (int arg) {
+        return;
+    }
+}
+
+@Deprecated
+class DeprecatedAutoCloseable implements AutoCloseable {
+    public DeprecatedAutoCloseable(){super();}
+
+    @Override
+    public void close () {
+        return;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/ArmLint.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,3 @@
+ArmLint.java:14:15: compiler.warn.twr.explicit.close.call
+ArmLint.java:13:13: compiler.warn.automatic.resource.not.referenced: r3
+2 warnings
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/BadTwr.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,36 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740
+ * @author Joseph D. Darcy
+ * @summary Verify bad TWRs don't compile
+ * @compile/fail -source 6 TwrFlow.java
+ * @compile/fail/ref=BadTwr.out -XDrawDiagnostics BadTwr.java
+ */
+
+public class BadTwr implements AutoCloseable {
+    public static void main(String... args) {
+        // illegal repeated name
+        try(BadTwr r1 = new BadTwr(); BadTwr r1 = new BadTwr()) {
+            System.out.println(r1.toString());
+        }
+
+        // illegal duplicate name of method argument
+        try(BadTwr args = new BadTwr()) {
+            System.out.println(args.toString());
+            final BadTwr thatsIt = new BadTwr();
+            thatsIt = null;
+        }
+
+        try(BadTwr name = new BadTwr()) {
+            // illegal duplicate name of enclosing try
+            try(BadTwr name = new BadTwr()) {
+                System.out.println(name.toString());
+            }
+        }
+
+    }
+
+    public void close() {
+        ;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/BadTwr.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,5 @@
+BadTwr.java:13:39: compiler.err.already.defined: r1, main(java.lang.String...)
+BadTwr.java:18:13: compiler.err.already.defined: args, main(java.lang.String...)
+BadTwr.java:21:13: compiler.err.cant.assign.val.to.final.var: thatsIt
+BadTwr.java:26:17: compiler.err.already.defined: name, main(java.lang.String...)
+4 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/BadTwrSyntax.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,22 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740
+ * @author Joseph D. Darcy
+ * @summary Verify bad TWRs don't compile
+ * @compile/fail -source 6 BadTwrSyntax.java
+ * @compile/fail/ref=BadTwrSyntax.out  -XDrawDiagnostics BadTwrSyntax.java
+ */
+
+import java.io.IOException;
+public class BadTwrSyntax implements AutoCloseable {
+    public static void main(String... args) throws Exception {
+        // illegal semicolon ending resources
+        try(BadTwr twrflow = new BadTwr();) {
+            System.out.println(twrflow.toString());
+        }
+    }
+
+    public void close() {
+        ;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/BadTwrSyntax.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,2 @@
+BadTwrSyntax.java:14:43: compiler.err.illegal.start.of.expr
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/DuplicateResource.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Check that lowered arm block does not end up creating resource twice
+ */
+
+import java.util.ArrayList;
+
+public class DuplicateResource {
+
+    static class TestResource implements AutoCloseable {
+        TestResource() {
+            resources.add(this);
+        }
+        boolean isClosed = false;
+        public void close() throws Exception {
+            isClosed = true;
+        }
+    }
+
+    static ArrayList<TestResource> resources = new ArrayList<TestResource>();
+
+    public static void main(String[] args) {
+        try(new TestResource()) {
+           //do something
+        } catch (Exception e) {
+            throw new AssertionError("Shouldn't reach here", e);
+        }
+        check();
+    }
+
+    public static void check() {
+       if (resources.size() != 1) {
+           throw new AssertionError("Expected one resource, found: " + resources.size());
+       }
+       TestResource resource = resources.get(0);
+       if (!resource.isClosed) {
+           throw new AssertionError("Resource used in ARM block has not been automatically closed");
+       }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/DuplicateResourceDecl.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,20 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Check that resource variable is not accessible from catch/finally clause
+ * @compile/fail/ref=DuplicateResourceDecl.out -XDrawDiagnostics DuplicateResourceDecl.java
+ */
+
+class DuplicateResourceDecl {
+
+    public static void main(String[] args) {
+        try(MyResource c = new MyResource();MyResource c = new MyResource()) {
+        //do something
+        } catch (Exception e) { }
+    }
+
+    static class MyResource implements AutoCloseable {
+        public void close() throws Exception {}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/DuplicateResourceDecl.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,2 @@
+DuplicateResourceDecl.java:12:45: compiler.err.already.defined: c, main(java.lang.String[])
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/ImplicitFinal.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,27 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Test that resource variables are implicitly final
+ * @compile/fail/ref=ImplicitFinal.out -XDrawDiagnostics ImplicitFinal.java
+ */
+
+import java.io.IOException;
+
+class ImplicitFinal implements AutoCloseable {
+    public static void main(String... args) {
+        try(ImplicitFinal r = new ImplicitFinal()) {
+            r = null; //disallowed
+        } catch (IOException ioe) { // Not reachable
+            throw new AssertionError("Shouldn't reach here", ioe);
+        }
+    }
+
+
+     // A close method, but the class is <em>not</em> Closeable or
+     // AutoCloseable.
+
+    public void close() throws IOException {
+        throw new IOException();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/ImplicitFinal.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,2 @@
+ImplicitFinal.java:14:13: compiler.err.twr.resource.may.not.be.assigned: r
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/PlainTry.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,15 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740
+ * @author Joseph D. Darcy
+ * @summary Test error messages for an unadorned try
+ * @compile/fail/ref=PlainTry6.out -XDrawDiagnostics -source 6 PlainTry.java
+ * @compile/fail/ref=PlainTry.out  -XDrawDiagnostics           PlainTry.java
+ */
+public class PlainTry {
+    public static void main(String... args) {
+        try {
+            ;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/PlainTry.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,2 @@
+PlainTry.java:11:9: compiler.err.try.without.catch.finally.or.resource.decls
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/PlainTry6.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,2 @@
+PlainTry.java:11:9: compiler.err.try.without.catch.or.finally
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/ResourceOutsideTry.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,23 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Check that resource variable is not accessible from catch/finally clause
+ * @compile/fail/ref=ResourceOutsideTry.out -XDrawDiagnostics ResourceOutsideTry.java
+ */
+
+class ResourceOutsideTry {
+    void test() {
+        try(MyResource c = new MyResource()) {
+        //do something
+        } catch (Exception e) {
+            c.test();
+        } finally {
+            c.test();
+        }
+    }
+    static class MyResource implements AutoCloseable {
+        public void close() throws Exception {}
+        void test() {}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/ResourceOutsideTry.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,3 @@
+ResourceOutsideTry.java:14:13: compiler.err.cant.resolve.location: kindname.variable, c, , , kindname.class, ResourceOutsideTry
+ResourceOutsideTry.java:16:13: compiler.err.cant.resolve.location: kindname.variable, c, , , kindname.class, ResourceOutsideTry
+2 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/ResourceTypeVar.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Resource of a type-variable type crashes Flow
+ * @compile ResourceTypeVar.java
+ */
+
+class ResourceTypeVar<X extends AutoCloseable> {
+
+    public void test() {
+        try(X armflow = getX()) {
+            //do something
+        } catch (Exception e) { // Not reachable
+            throw new AssertionError("Shouldn't reach here", e);
+        }
+    }
+
+    X getX() { return null; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrFlow.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,39 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740
+ * @author Joseph D. Darcy
+ * @summary Test exception analysis of ARM blocks
+ * @compile/fail/ref=TwrFlow.out -XDrawDiagnostics TwrFlow.java
+ */
+
+import java.io.IOException;
+public class TwrFlow implements AutoCloseable {
+    public static void main(String... args) {
+        try(TwrFlow armflow = new TwrFlow()) {
+            System.out.println(armflow.toString());
+        } catch (IOException ioe) { // Not reachable
+            throw new AssertionError("Shouldn't reach here", ioe);
+        }
+        // CustomCloseException should be caught or added to throws clause
+
+        // Also check behavior on a resource expression rather than a
+        // declaration.
+        TwrFlow armflowexpr = new TwrFlow();
+        try(armflowexpr) {
+            System.out.println(armflowexpr.toString());
+        } catch (IOException ioe) { // Not reachable
+            throw new AssertionError("Shouldn't reach here", ioe);
+        }
+        // CustomCloseException should be caught or added to throws clause
+    }
+
+    /*
+     * A close method, but the class is <em>not</em> Closeable or
+     * AutoCloseable.
+     */
+    public void close() throws CustomCloseException {
+        throw new CustomCloseException();
+    }
+}
+
+class CustomCloseException extends Exception {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrFlow.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,5 @@
+TwrFlow.java:14:11: compiler.err.except.never.thrown.in.try: java.io.IOException
+TwrFlow.java:24:11: compiler.err.except.never.thrown.in.try: java.io.IOException
+TwrFlow.java:12:46: compiler.err.unreported.exception.need.to.catch.or.throw: CustomCloseException
+TwrFlow.java:22:26: compiler.err.unreported.exception.need.to.catch.or.throw: CustomCloseException
+4 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrInference.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Verify that method type-inference works as expected in TWR context
+ * @compile TwrInference.java
+ */
+
+class TwrInference {
+
+    public void test() {
+        try(getX()) {
+            //do something
+        } catch (Exception e) { // Not reachable
+            throw new AssertionError("Shouldn't reach here", e);
+        }
+    }
+
+    <X> X getX() { return null; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrIntersection.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Resource of an intersection type crashes Flow
+ * @compile TwrIntersection.java
+ */
+
+interface MyCloseable extends AutoCloseable {
+   void close() throws java.io.IOException;
+}
+
+class ResourceTypeVar {
+
+    public void test() {
+        try(getX()) {
+            //do something
+        } catch (java.io.IOException e) { // Not reachable
+            throw new AssertionError("Shouldn't reach here", e);
+        }
+    }
+
+    <X extends Number & MyCloseable> X getX() { return null; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrIntersection02.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,37 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740 6965277
+ * @author Maurizio Cimadamore
+ * @summary Check that resources of an intersection type forces union of exception types
+ *          to be caught outside twr block
+ * @compile/fail/ref=TwrIntersection02.out -XDrawDiagnostics TwrIntersection02.java
+ */
+
+class TwrIntersection02 {
+
+    static class Exception1 extends Exception {}
+    static class Exception2 extends Exception {}
+
+
+    interface MyResource1 extends AutoCloseable {
+       void close() throws Exception1;
+    }
+
+    interface MyResource2 extends AutoCloseable {
+       void close() throws Exception2;
+    }
+
+    public void test1() throws Exception1 {
+        try(getX()) {
+            //do something
+        }
+    }
+
+    public void test2() throws Exception2 {
+        try(getX()) {
+            //do something
+        }
+    }
+
+    <X extends MyResource1 & MyResource2> X getX() { return null; }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrIntersection02.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,3 @@
+TwrIntersection02.java:25:21: compiler.err.unreported.exception.need.to.catch.or.throw: TwrIntersection02.Exception2
+TwrIntersection02.java:31:21: compiler.err.unreported.exception.need.to.catch.or.throw: TwrIntersection02.Exception1
+2 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrMultiCatch.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6911256 6964740
+ * @author Joseph D. Darcy
+ * @summary Test that TWR and multi-catch play well together
+ * @compile TwrMultiCatch.java
+ * @run main TwrMultiCatch
+ */
+
+import java.io.IOException;
+public class TwrMultiCatch implements AutoCloseable {
+    private final Class<? extends Exception> exceptionClass;
+
+    private TwrMultiCatch(Class<? extends Exception> exceptionClass) {
+        this.exceptionClass = exceptionClass;
+    }
+
+    public static void main(String... args) {
+        test(new TwrMultiCatch(CustomCloseException1.class),
+             CustomCloseException1.class);
+
+        test(new TwrMultiCatch(CustomCloseException2.class),
+             CustomCloseException2.class);
+    }
+
+    private static void test(TwrMultiCatch twrMultiCatch,
+                     Class<? extends Exception> expected) {
+        try(twrMultiCatch) {
+            System.out.println(twrMultiCatch.toString());
+        } catch (final CustomCloseException1 |
+                 CustomCloseException2 exception) {
+            if (!exception.getClass().equals(expected) ) {
+                throw new RuntimeException("Unexpected catch!");
+            }
+        }
+    }
+
+    public void close() throws CustomCloseException1, CustomCloseException2 {
+        Throwable t;
+        try {
+             t = exceptionClass.newInstance();
+        } catch(ReflectiveOperationException rfe) {
+            throw new RuntimeException(rfe);
+        }
+
+        try {
+            throw t;
+        } catch (final CustomCloseException1 |
+                 CustomCloseException2 exception) {
+            throw exception;
+        } catch (Throwable throwable) {
+            throw new RuntimeException(throwable);
+        }
+    }
+}
+
+class CustomCloseException1 extends Exception {}
+class CustomCloseException2 extends Exception {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrOnNonResource.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,42 @@
+/*
+ * @test  /nodynamiccopyright/
+ * @bug 6911256 6964740
+ * @author Joseph D. Darcy
+ * @summary Verify invalid TWR block is not accepted.
+ * @compile/fail -source 6 TwrOnNonResource.java
+ * @compile/fail/ref=TwrOnNonResource.out -XDrawDiagnostics TwrOnNonResource.java
+ */
+
+class TwrOnNonResource {
+    public static void main(String... args) {
+        try(TwrOnNonResource aonr = new TwrOnNonResource()) {
+            System.out.println(aonr.toString());
+        }
+        try(TwrOnNonResource aonr = new TwrOnNonResource()) {
+            System.out.println(aonr.toString());
+        } finally {;}
+        try(TwrOnNonResource aonr = new TwrOnNonResource()) {
+            System.out.println(aonr.toString());
+        } catch (Exception e) {;}
+
+        // Also check expression form
+        TwrOnNonResource aonr = new TwrOnNonResource();
+        try(aonr) {
+            System.out.println(aonr.toString());
+        }
+        try(aonr) {
+            System.out.println(aonr.toString());
+        } finally {;}
+        try(aonr) {
+            System.out.println(aonr.toString());
+        } catch (Exception e) {;}
+    }
+
+    /*
+     * A close method, but the class is <em>not</em> Closeable or
+     * AutoCloseable.
+     */
+    public void close() {
+        throw new AssertionError("I'm not Closable!");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrOnNonResource.out	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,7 @@
+TwrOnNonResource.java:12:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:15:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:18:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:24:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:27:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:30:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+6 errors
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/TwrTests.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,742 @@
+/*
+ * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6911256 6964740
+ * @summary Tests of generated TWR code.
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class TwrTests {
+    public static void main(String[] args) {
+        testCreateFailure1();
+        testCreateFailure2();
+        testCreateFailure2Nested();
+        testCreateFailure3();
+        testCreateFailure3Nested();
+        testCreateFailure4();
+        testCreateFailure4Nested();
+        testCreateFailure5();
+        testCreateFailure5Nested();
+
+        testCreateSuccess1();
+        testCreateSuccess2();
+        testCreateSuccess2Nested();
+        testCreateSuccess3();
+        testCreateSuccess3Nested();
+        testCreateSuccess4();
+        testCreateSuccess4Nested();
+        testCreateSuccess5();
+        testCreateSuccess5Nested();
+    }
+
+    /*
+     * The following tests simulate a creation failure of every possible
+     * resource in an TWR block, and check to make sure that the failure
+     * prevents creation of subsequent resources, and that all created
+     * resources are properly closed, even if one or more of the close
+     * attempts fails.
+     */
+
+    public static void testCreateFailure1() {
+        int creationFailuresDetected = 0;
+        List<Integer> closedList = new ArrayList<Integer>(0);
+        try (Resource r0 = createResource(0, 0, 0, closedList)) {
+            throw new AssertionError("Resource creation succeeded");
+        } catch (Resource.CreateFailException e) {
+            creationFailuresDetected++;
+            if (e.resourceId() != 0) {
+                throw new AssertionError("Wrong resource creation "
+                                         + e.resourceId() + " failed");
+            }
+        } catch (Resource.CloseFailException e) {
+            throw new AssertionError("Unexpected CloseFailException: " + e.resourceId());
+        }
+        checkForSingleCreationFailure(creationFailuresDetected);
+        checkClosedList(closedList, 0);
+    }
+
+    public static void testCreateFailure2() {
+        for (int createFailureId = 0; createFailureId < 2; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList);
+                     Resource r1 = createResource(1, createFailureId, bitMap, closedList)) {
+                    throw new AssertionError("Entire resource creation succeeded");
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed");
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    public static void testCreateFailure2Nested() {
+        for (int createFailureId = 0; createFailureId < 2; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) {
+                    try(Resource r1 = createResource(1, createFailureId, bitMap, closedList)) {
+                        throw new AssertionError("Entire resource creation succeeded");
+                    }
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed");
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    public static void testCreateFailure3() {
+        for (int createFailureId = 0; createFailureId < 3; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList);
+                     Resource r1 = createResource(1, createFailureId, bitMap, closedList);
+                     Resource r2 = createResource(2, createFailureId, bitMap, closedList)) {
+                    throw new AssertionError("Entire resource creation succeeded");
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed:" + e);
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    public static void testCreateFailure3Nested() {
+        for (int createFailureId = 0; createFailureId < 3; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) {
+                    try (Resource r1 = createResource(1, createFailureId, bitMap, closedList)) {
+                        try (Resource r2 = createResource(2, createFailureId, bitMap, closedList)) {
+                            throw new AssertionError("Entire resource creation succeeded");
+                        }
+                    }
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed:" + e);
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    public static void testCreateFailure4() {
+        for (int createFailureId = 0; createFailureId < 4; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList);
+                     Resource r1 = createResource(1, createFailureId, bitMap, closedList);
+                     Resource r2 = createResource(2, createFailureId, bitMap, closedList);
+                     Resource r3 = createResource(3, createFailureId, bitMap, closedList)) {
+                    throw new AssertionError("Entire resource creation succeeded");
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed:" + e);
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    public static void testCreateFailure4Nested() {
+        for (int createFailureId = 0; createFailureId < 4; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) {
+                    try (Resource r1 = createResource(1, createFailureId, bitMap, closedList)) {
+                        try (Resource r2 = createResource(2, createFailureId, bitMap, closedList)) {
+                            try (Resource r3 = createResource(3, createFailureId, bitMap, closedList)) {
+                                throw new AssertionError("Entire resource creation succeeded");
+                            }
+                        }
+                    }
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed:" + e);
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    public static void testCreateFailure5() {
+        for (int createFailureId = 0; createFailureId < 5; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList);
+                     Resource r1 = createResource(1, createFailureId, bitMap, closedList);
+                     Resource r2 = createResource(2, createFailureId, bitMap, closedList);
+                     Resource r3 = createResource(3, createFailureId, bitMap, closedList);
+                     Resource r4 = createResource(4, createFailureId, bitMap, closedList)) {
+                    throw new AssertionError("Entire resource creation succeeded");
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed:" + e);
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    public static void testCreateFailure5Nested() {
+        for (int createFailureId = 0; createFailureId < 5; createFailureId++) {
+            for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) {
+                int creationFailuresDetected = 0;
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) {
+                    try (Resource r1 = createResource(1, createFailureId, bitMap, closedList)) {
+                        try (Resource r2 = createResource(2, createFailureId, bitMap, closedList)) {
+                            try (Resource r3 = createResource(3, createFailureId, bitMap, closedList)) {
+                                try (Resource r4 = createResource(4, createFailureId, bitMap, closedList)) {
+                                    throw new AssertionError("Entire resource creation succeeded");
+                                }
+                            }
+                        }
+                    }
+                } catch (Resource.CreateFailException e) {
+                    creationFailuresDetected++;
+                    checkCreateFailureId(e.resourceId(), createFailureId);
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    throw new AssertionError("Secondary exception suppression failed:" + e);
+                }
+                checkForSingleCreationFailure(creationFailuresDetected);
+                checkClosedList(closedList, createFailureId);
+            }
+        }
+    }
+
+    /**
+     * Create a resource with the specified ID. The ID must be less than createFailureId.
+     * A subsequent attempt to close the resource will fail iff the corresponding bit
+     * is set in closeFailureBitMap.  When an attempt is made to close this resource,
+     * its ID will be added to closedList, regardless of whether the attempt succeeds.
+     *
+     * @param id the ID of this resource
+     * @param createFailureId the ID of the resource whose creation will fail
+     * @param closeFailureBitMap a bit vector describing which resources should throw an
+     *        exception when close is attempted
+     * @param closedList a list on which to record resource close attempts
+     * @throws AssertionError if no attempt should be made to create this resource
+     */
+    private static Resource createResource(int id,
+                                           int createFailureId,
+                                           int closeFailureBitMap,
+                                           List<Integer> closedList) throws Resource.CreateFailException {
+        if (id > createFailureId)
+            throw new AssertionError("Resource " + id + " shouldn't be created");
+        boolean createSucceeds = id != createFailureId;
+        boolean closeSucceeds = (closeFailureBitMap & (1 << id)) == 0;
+        return new Resource(id, createSucceeds, closeSucceeds, closedList);
+    }
+
+
+    /**
+     * Check that an observed creation failure has the expected resource ID.
+     *
+     * @param foundId the ID of the resource whose creation failed
+     * @param expectedId the ID of the resource whose creation should have failed
+     */
+    private static void checkCreateFailureId(int foundId, int expectedId) {
+        if (foundId != expectedId)
+            throw new AssertionError("Wrong resource creation failed. Found ID "
+                                     + foundId + " expected " + expectedId);
+    }
+
+    /**
+     * Check for proper suppressed exceptions in proper order.
+     *
+     * @param suppressedExceptions the suppressed exceptions array returned by
+     *        getSuppressedExceptions()
+     * @bitmap a bitmap indicating which suppressed exceptions are expected.
+     *         Bit i is set iff id should throw a CloseFailException.
+     */
+    private static void checkSuppressedExceptions(Throwable[] suppressedExceptions, int bitMap) {
+        if (suppressedExceptions.length != Integer.bitCount(bitMap))
+            throw new AssertionError("Expected " + Integer.bitCount(bitMap)
+                                     + " suppressed exceptions, got " +  suppressedExceptions.length);
+
+        int prevCloseFailExceptionId = Integer.MAX_VALUE;
+        for (Throwable t : suppressedExceptions) {
+            int id = ((Resource.CloseFailException) t).resourceId();
+            if ((1 << id  & bitMap) == 0)
+                throw new AssertionError("Unexpected suppressed CloseFailException: " + id);
+            if (id > prevCloseFailExceptionId)
+                throw new AssertionError("Suppressed CloseFailException" + id
+                                         + " followed " + prevCloseFailExceptionId);
+        }
+    }
+
+    /**
+     * Check that exactly one resource creation failed.
+     *
+     * @param numCreationFailuresDetected the number of creation failures detected
+     */
+    private static void checkForSingleCreationFailure(int numCreationFailuresDetected) {
+        if (numCreationFailuresDetected != 1)
+            throw new AssertionError("Wrong number of creation failures: "
+                                     + numCreationFailuresDetected);
+    }
+
+    /**
+     * Check that a close was attempted on every resourced that was successfully opened,
+     * and that the close attempts occurred in the proper order.
+     *
+     * @param closedList the resource IDs of the close attempts, in the order they occurred
+     * @param the ID of the resource whose creation failed.  Close attempts should occur
+     *        for all previous resources, in reverse order.
+     */
+    private static void checkClosedList(List<Integer> closedList, int createFailureId) {
+        List<Integer> expectedList = new ArrayList<Integer>(createFailureId);
+        for (int i = createFailureId - 1; i >= 0; i--)
+            expectedList.add(i);
+        if (!closedList.equals(expectedList))
+            throw new AssertionError("Closing sequence " + closedList + " != " + expectedList);
+    }
+
+    /*
+     * The following tests simulate the creation of several resources, followed
+     * by success or failure of forward processing.  They test that all resources
+     * are properly closed, even if one or more of the close attempts fails.
+     */
+
+    public static void testCreateSuccess1() {
+        for (int bitMap = 0, n = 1 << 1; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList)) {
+                    if (failure != 0)
+                        throw new MyKindOfException();
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError(
+                                             "Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 1);
+            }
+        }
+    }
+
+    public static void testCreateSuccess2() {
+        for (int bitMap = 0, n = 1 << 2; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList);
+                     Resource r1 = createResource(1, bitMap, closedList)) {
+                    if (failure != 0)
+                        throw new MyKindOfException();
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError(
+                                             "Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 2);
+            }
+        }
+    }
+
+    public static void testCreateSuccess2Nested() {
+        for (int bitMap = 0, n = 1 << 2; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList)) {
+                    try (Resource r1 = createResource(1, bitMap, closedList)) {
+                        if (failure != 0)
+                            throw new MyKindOfException();
+                    }
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError(
+                                             "Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 2);
+            }
+        }
+    }
+
+    public static void testCreateSuccess3() {
+        for (int bitMap = 0, n = 1 << 3; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList);
+                     Resource r1 = createResource(1, bitMap, closedList);
+                     Resource r2 = createResource(2, bitMap, closedList)) {
+                    if (failure != 0)
+                        throw new MyKindOfException();
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError(
+                                             "Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 3);
+            }
+        }
+    }
+
+    public static void testCreateSuccess3Nested() {
+        for (int bitMap = 0, n = 1 << 3; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList)) {
+                    try (Resource r1 = createResource(1, bitMap, closedList)) {
+                        try (Resource r2 = createResource(2, bitMap, closedList)) {
+                            if (failure != 0)
+                                throw new MyKindOfException();
+                        }
+                    }
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError(
+                                             "Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 3);
+            }
+        }
+    }
+
+    public static void testCreateSuccess4() {
+        for (int bitMap = 0, n = 1 << 4; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList);
+                     Resource r1 = createResource(1, bitMap, closedList);
+                     Resource r2 = createResource(2, bitMap, closedList);
+                     Resource r3 = createResource(3, bitMap, closedList)) {
+                    if (failure != 0)
+                        throw new MyKindOfException();
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError(
+                                             "Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 4);
+            }
+        }
+    }
+
+    public static void testCreateSuccess4Nested() {
+        for (int bitMap = 0, n = 1 << 4; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList)) {
+                    try (Resource r1 = createResource(1, bitMap, closedList)) {
+                        try (Resource r2 = createResource(2, bitMap, closedList)) {
+                            try (Resource r3 = createResource(3, bitMap, closedList)) {
+                                if (failure != 0)
+                                    throw new MyKindOfException();
+                            }
+                        }
+                    }
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError(
+                                             "Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 4);
+            }
+        }
+    }
+
+    public static void testCreateSuccess5() {
+        for (int bitMap = 0, n = 1 << 5; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList);
+                     Resource r1 = createResource(1, bitMap, closedList);
+                     Resource r2 = createResource(2, bitMap, closedList);
+                     Resource r3 = createResource(3, bitMap, closedList);
+                     Resource r4 = createResource(4, bitMap, closedList)) {
+                    if (failure != 0)
+                        throw new MyKindOfException();
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError("Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 5);
+            }
+        }
+    }
+
+    public static void testCreateSuccess5Nested() {
+        for (int bitMap = 0, n = 1 << 5; bitMap < n; bitMap++) {
+            for (int failure = 0; failure < 2; failure++) {
+                List<Integer> closedList = new ArrayList<Integer>();
+                try (Resource r0 = createResource(0, bitMap, closedList)) {
+                    try (Resource r1 = createResource(1, bitMap, closedList)) {
+                        try (Resource r2 = createResource(2, bitMap, closedList)) {
+                            try (Resource r3 = createResource(3, bitMap, closedList)) {
+                                try (Resource r4 = createResource(4, bitMap, closedList)) {
+                                    if (failure != 0)
+                                        throw new MyKindOfException();
+                                }
+                            }
+                        }
+                    }
+                } catch (Resource.CreateFailException e) {
+                    throw new AssertionError("Resource creation failed: " + e.resourceId());
+                } catch (MyKindOfException e) {
+                    if (failure == 0)
+                        throw new AssertionError("Unexpected MyKindOfException");
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap);
+                } catch (Resource.CloseFailException e) {
+                    if (failure == 1)
+                        throw new AssertionError("Secondary exception suppression failed");
+                    int id = e.resourceId();
+                    if (bitMap == 0)
+                        throw new AssertionError("Unexpected CloseFailException: " + id);
+                    int highestCloseFailBit = Integer.highestOneBit(bitMap);
+                    if (1 << id != highestCloseFailBit) {
+                        throw new AssertionError("CloseFailException: got id " + id
+                                                 + ", expected lg(" + highestCloseFailBit +")");
+                    }
+                    checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit);
+                }
+                checkClosedList(closedList, 5);
+            }
+        }
+    }
+
+    private static Resource createResource(int id,
+                                           int closeFailureBitMap,
+                                           List<Integer> closedList) throws Resource.CreateFailException {
+        boolean closeSucceeds = (closeFailureBitMap & (1 << id)) == 0;
+        return new Resource(id, true, closeSucceeds, closedList);
+    }
+
+    private static class MyKindOfException extends Exception {
+    }
+}
+
+class Resource implements AutoCloseable {
+    /** A number identifying this resource */
+    private final int resourceId;
+
+    /** Whether the close call on this resource should succeed or fail */
+    private final boolean closeSucceeds;
+
+    /** When resource is closed, it records its ID in this list */
+    private final List<Integer> closedList;
+
+    Resource(int resourceId, boolean createSucceeds, boolean closeSucceeds,
+             List<Integer> closedList) throws CreateFailException {
+        if (!createSucceeds)
+            throw new CreateFailException(resourceId);
+        this.resourceId = resourceId;
+        this.closeSucceeds = closeSucceeds;
+        this.closedList = closedList;
+    }
+
+    public void close() throws CloseFailException {
+        closedList.add(resourceId);
+        if (!closeSucceeds)
+            throw new CloseFailException(resourceId);
+    }
+
+    public static class ResourceException extends RuntimeException {
+        private final int resourceId;
+
+        public ResourceException(int resourceId) {
+            super("Resource ID = " + resourceId);
+            this.resourceId = resourceId;
+        }
+
+        public int resourceId() {
+            return resourceId;
+        }
+    }
+
+    public static class CreateFailException extends ResourceException {
+        public CreateFailException(int resourceId) {
+            super(resourceId);
+        }
+    }
+
+    public static class CloseFailException extends ResourceException {
+        public CloseFailException(int resourceId) {
+            super(resourceId);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/TryWithResources/WeirdTwr.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6911256 6964740
+ * @author Joseph D. Darcy
+ * @summary Strange TWRs
+ * @compile/fail -source 6 WeirdTwr.java
+ * @compile WeirdTwr.java
+ * @run main WeirdTwr
+ */
+
+public class WeirdTwr implements AutoCloseable {
+    private static int closeCount = 0;
+    public static void main(String... args) {
+        try(WeirdTwr r1 = new WeirdTwr(); WeirdTwr r2 = r1) {
+            if (r1 != r2)
+                throw new RuntimeException("Unexpected inequality.");
+        }
+        if (closeCount != 2)
+            throw new RuntimeException("bad closeCount" + closeCount);
+    }
+
+    public void close() {
+        closeCount++;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/element/TestResourceVariable.java	Fri Jul 16 19:35:24 2010 -0700
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug  6911256 6964740
+ * @summary Test that the resource variable kind is appropriately set
+ * @author  Joseph D. Darcy
+ * @build TestResourceVariable
+ * @compile/fail -processor TestResourceVariable -proc:only TestResourceVariable.java
+ */
+
+// Bug should be filed for this misbehavior
+
+import java.io.*;
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.lang.model.util.*;
+import java.util.*;
+import com.sun.source.tree.*;
+import com.sun.source.util.*;
+import static javax.tools.Diagnostic.Kind.*;
+
+/**
+ * Using the tree API, retrieve element representations of the
+ * resource of an ARM block and verify their kind tags are set
+ * appropriately.
+ */
+@SupportedAnnotationTypes("*")
+public class TestResourceVariable extends AbstractProcessor implements AutoCloseable {
+    int resourceVariableCount = 0;
+
+    public boolean process(Set<? extends TypeElement> annotations,
+                          RoundEnvironment roundEnv) {
+       if (!roundEnv.processingOver()) {
+           Trees trees = Trees.instance(processingEnv);
+
+           for(Element rootElement : roundEnv.getRootElements()) {
+               TreePath treePath = trees.getPath(rootElement);
+
+               (new ResourceVariableScanner(trees)).
+                   scan(trees.getTree(rootElement),
+                        treePath.getCompilationUnit());
+           }
+           if (resourceVariableCount != 3)
+               throw new RuntimeException("Bad resource variable count " +
+                                          resourceVariableCount);
+       }
+       return true;
+    }
+
+    @Override
+    public void close() {}
+
+    private void test1() {
+        try(TestResourceVariable trv = this) {}
+    }
+
+    private void test2() {
+        try(TestResourceVariable trv1 = this; TestResourceVariable trv2 = trv1) {}
+    }
+
+    class ResourceVariableScanner extends TreeScanner<Void, CompilationUnitTree> {
+       private Trees trees;
+
+       public ResourceVariableScanner(Trees trees) {
+           super();
+           this.trees = trees;
+       }
+       @Override
+       public Void visitVariable(VariableTree node, CompilationUnitTree cu) {
+           Element element = trees.getElement(trees.getPath(cu, node));
+           if (element == null) {
+               System.out.println("Null variable element: " + node);
+           } else {
+               System.out.println("Name: " + element.getSimpleName() +
+                                  "\tKind: " + element.getKind());
+           }
+           if (element != null &&
+               element.getKind() == ElementKind.RESOURCE_VARIABLE) {
+               resourceVariableCount++;
+           }
+           return super.visitVariable(node, cu);
+       }
+   }
+
+   @Override
+   public SourceVersion getSupportedSourceVersion() {
+       return SourceVersion.latest();
+   }
+}