Bug 565156 - Compiler crash on switch expressions with try-catch and
throw

Change-Id: Ib0531d06d0223758b8928397a4a2e3d8b36e2a37
Signed-off-by: Manoj Palat <manpalat@in.ibm.com>
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java
index 0cb6f7e..32df1d1 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchExpressionsYieldTest.java
@@ -26,7 +26,7 @@
 	static {
 //		TESTS_NUMBERS = new int [] { 40 };
 //		TESTS_RANGE = new int[] { 1, -1 };
-//		TESTS_NAMES = new String[] { "testBug561766" };
+//		TESTS_NAMES = new String[] { "testBug545567_14" };
 	}
 
 	public static Class<?> testClass() {
@@ -5192,4 +5192,229 @@
 			"The target type of this expression must be a functional interface\n" +
 			"----------\n");
 	}
+	public void testBug565156_001() {
+		runConformTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				"  public int test() {\n"+
+				"    return switch (0) {\n"+
+				"      default -> {\n"+
+				"        try {\n"+
+				"          yield 0;\n"+
+				"        }\n"+
+				"        catch (RuntimeException e) {\n"+
+				"          throw e;\n"+
+				"        }\n"+
+				"      }\n"+
+				"    };\n"+
+				"  }    \n"+
+				"  public static void main(String[] args) {\n"+
+				"       int i = new X().test();\n"+
+				"       System.out.println(i);\n"+
+				" }\n"+
+				"}\n"
+			},
+			"0");
+	}
+	public void testBug565156_002() {
+		runConformTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				"  public int test() {\n"+
+				"    return switch (0) {\n"+
+				"      default -> {\n"+
+				"        try {\n"+
+				"          yield 0;\n"+
+				"        }\n"+
+				"        finally {\n"+
+				"          //do nothing\n"+
+				"        }\n"+
+				"      }\n"+
+				"    };\n"+
+				"  }    \n"+
+				"  public static void main(String[] args) {\n"+
+				"       int i = new X().test();\n"+
+				"       System.out.println(i);\n"+
+				" }\n"+
+				"}\n"
+			},
+			"0");
+	}
+	public void testBug565156_003() {
+		runConformTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				"  public int test() {\n"+
+				"    return switch (0) {\n"+
+				"      default -> {\n"+
+				"        try {\n"+
+				"          yield 0;\n"+
+				"        }\n"+
+				"        finally {\n"+
+				"          int i = 20;"+
+				"          yield 20;"+
+				"        }\n"+
+				"      }\n"+
+				"    };\n"+
+				"  }    \n"+
+				"  public static void main(String[] args) {\n"+
+				"       int i = new X().test();\n"+
+				"       System.out.println(i);\n"+
+				" }\n"+
+				"}\n"
+			},
+			"20");
+	}
+	public void testBug565156_004() {
+		runConformTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				"  public int test()  {\n"+
+				"    return switch (0) {\n"+
+				"      default -> {\n"+
+				"        try {\n"+
+				"          yield switch (0) {\n"+
+				"          default -> {\n"+
+				"              try {\n"+
+				"                yield 100;\n"+
+				"              }\n"+
+				"              finally {\n"+
+				"                   yield 200;       \n"+
+				"               }\n"+
+				"            }\n"+
+				"          };\n"+
+				"        }\n"+
+				"        finally {\n"+
+				"             yield 20;\n"+
+				"         }\n"+
+				"      }\n"+
+				"    };\n"+
+				"  }\n"+
+				"  public static void main(String[] args){\n"+
+				"       int i = new X().test();\n"+
+				"       System.out.println(i);\n"+
+				"  }\n"+
+				"}"
+			},
+			"20");
+	}
+	public void testBug565156_005() {
+		runConformTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				"  public int test()  {\n"+
+				"    return switch (0) {\n"+
+				"      default -> {\n"+
+				"        try {\n"+
+				"          yield switch (0) {\n"+
+				"          default -> {\n"+
+				"              try {\n"+
+				"                yield 100;\n"+
+				"              }\n"+
+				"              finally {\n"+
+				"                   // do nothing\n"+
+				"               }\n"+
+				"            }\n"+
+				"          };\n"+
+				"        }\n"+
+				"        finally {\n"+
+				"           // do nothing\n"+
+				"         }\n"+
+				"      }\n"+
+				"    };\n"+
+				"  }\n"+
+				"  public static void main(String[] args){\n"+
+				"       int i = new X().test();\n"+
+				"       System.out.println(i);\n"+
+				"  }\n"+
+				"}"
+			},
+			"100");
+	}
+	public void testBug565156_006() {
+		runConformTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				"    public static void main(String[] args) {\n"+
+				"            new X().foo(args);\n"+
+				"    }\n"+
+				"\n"+
+				"  @SuppressWarnings({ \"finally\" })\n"+
+				"  public void foo(String[] args) {\n"+
+				"     int t = switch (0) {\n"+
+				"     default -> {\n"+
+				"        try {\n"+
+				"            if (args == null)\n"+
+				"            yield 1;\n"+
+				"            else if (args.length ==2)\n"+
+				"                    yield 2; \n"+
+				"            else if (args.length == 4)\n"+
+				"                    yield 4;\n"+
+				"            else yield 5; \n"+
+				"        } finally {\n"+
+				"                yield 3; \n"+
+				"        }\n"+
+				"     }\n"+
+				"     }; \n"+
+				"     t = switch (100) {\n"+
+				"     default -> {\n"+
+				"             try {\n"+
+				"                     yield 10;\n"+
+				"             } finally {\n"+
+				"             }\n"+
+				"     }  \n"+
+				"     };      \n"+
+				"     System.out.println(t);\n"+
+				"  }\n"+
+				"}"
+			},
+			"10");
+	}
+	public void testBug565156_007() {
+		runConformTest(
+			new String[] {
+				"X.java",
+				"public class X {\n"+
+				"    public static void main(String[] args) {\n"+
+				"            new X().foo(args);\n"+
+				"    }\n"+
+				"\n"+
+				"  @SuppressWarnings({ \"finally\" })\n"+
+				"  public void foo(String[] args) {\n"+
+				"     int t = switch (0) {\n"+
+				"     case 101 -> {yield 101;}\n"+
+				"     default -> {\n"+
+				"        try {\n"+
+				"            if (args == null)\n"+
+				"            yield 1;\n"+
+				"            else if (args.length ==2)\n"+
+				"                    yield 2; \n"+
+				"            else if (args.length == 4)\n"+
+				"                    yield 4;\n"+
+				"            else yield 5; \n"+
+				"        } finally {\n"+
+				"                yield 3; \n"+
+				"        }\n"+
+				"     }\n"+
+				"     }; \n"+
+				"     t = switch (100) {\n"+
+				"     default -> {\n"+
+				"             try {\n"+
+				"                     yield 10;\n"+
+				"             } finally {\n"+
+				"             }\n"+
+				"     }  \n"+
+				"     };      \n"+
+				"     System.out.println(t);\n"+
+				"  }\n"+
+				"}"
+			},
+			"10");
+	}
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BranchStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BranchStatement.java
index 62904f9..59522df 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BranchStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BranchStatement.java
@@ -32,12 +32,6 @@
 	this.sourceEnd = sourceEnd;
 }
 
-protected void generateExpressionResultCode(BlockScope currentScope, CodeStream codeStream) {
-	// do nothing here
-}
-protected void adjustStackSize(BlockScope currentScope, CodeStream codeStream) {
-	// do nothing here
-}
 protected void setSubroutineSwitchExpression(SubRoutineStatement sub) {
 	// Do nothing
 }
@@ -54,7 +48,6 @@
 	if ((this.bits & ASTNode.IsReachable) == 0) {
 		return;
 	}
-	generateExpressionResultCode(currentScope, codeStream);
 	int pc = codeStream.position;
 
 	// generation of code responsible for invoking the finally
@@ -80,7 +73,6 @@
 	}
 //	checkAndLoadSyntheticVars(codeStream);
 	codeStream.goto_(this.targetLabel);
-	adjustStackSize(currentScope, codeStream);
 	codeStream.recordPositionsFrom(pc, this.sourceStart);
 	SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, -1, codeStream);
 	if (this.initStateIndex != -1) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java
index e0c9632..3e258e3 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java
@@ -302,6 +302,7 @@
 	public void loadStoredTypesAndKeep(CodeStream codeStream) {
 		List<LocalVariableBinding> tos = this.typesOnStack;
 		int sz = tos != null ? tos.size() : 0;
+		codeStream.clearTypeBindingStack();
 		int index = sz - 1;
 		while (index >= 0) {
 			LocalVariableBinding lvb = tos.get(index--);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java
index 84a9106..f626942 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java
@@ -127,44 +127,96 @@
 protected void restartExceptionLabels(CodeStream codeStream) {
 	SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, -1, codeStream);
 }
-@Override
-protected void generateExpressionResultCode(BlockScope currentScope, CodeStream codeStream) {
+protected void generateExpressionResultCodeExpanded(BlockScope currentScope, CodeStream codeStream) {
 	SwitchExpression se = this.switchExpression;
-	if (se != null && se.containsTry && se.resolvedType != null ) {
-		addSecretYieldResultValue(this.scope);
-		assert this.secretYieldResultValue != null;
-		codeStream.record(this.secretYieldResultValue);
-		SingleNameReference lhs = new SingleNameReference(this.secretYieldResultValue.name, 0);
-		lhs.binding = this.secretYieldResultValue;
-		lhs.bits &= ~ASTNode.RestrictiveFlagMASK;  // clear bits
-		lhs.bits |= Binding.LOCAL;
-		lhs.bits |= ASTNode.IsSecretYieldValueUsage;
-		((LocalVariableBinding) lhs.binding).markReferenced(); // TODO : Can be skipped?
-		Assignment assignment = new Assignment(lhs, this.expression, 0);
-		assignment.generateCode(this.scope, codeStream);
-		int l = this.subroutines == null ? 0 : this.subroutines.length;
-		boolean foundFinally = false;
-		if (l > 0) {
-			for (int i = 0; i < l; ++i) {
-				SubRoutineStatement srs = this.subroutines[i];
-				srs.exitAnyExceptionHandler();
-				srs.exitDeclaredExceptionHandlers(codeStream);
-				if (srs instanceof TryStatement) {
-					TryStatement ts = (TryStatement) srs;
-					if (ts.finallyBlock != null) {
-						foundFinally = true;
+	addSecretYieldResultValue(this.scope);
+	assert this.secretYieldResultValue != null;
+	codeStream.record(this.secretYieldResultValue);
+	SingleNameReference lhs = new SingleNameReference(this.secretYieldResultValue.name, 0);
+	lhs.binding = this.secretYieldResultValue;
+	lhs.bits &= ~ASTNode.RestrictiveFlagMASK; // clear bits
+	lhs.bits |= Binding.LOCAL;
+	lhs.bits |= ASTNode.IsSecretYieldValueUsage;
+	((LocalVariableBinding) lhs.binding).markReferenced(); // TODO : Can be skipped?
+	Assignment assignment = new Assignment(lhs, this.expression, 0);
+	assignment.generateCode(this.scope, codeStream);
+
+	int pc = codeStream.position;
+	// generation of code responsible for invoking the finally
+	// blocks in sequence
+	if (this.subroutines != null){
+		for (int i = 0, max = this.subroutines.length; i < max; i++){
+			SubRoutineStatement sub = this.subroutines[i];
+			sub.exitAnyExceptionHandler();
+			sub.exitDeclaredExceptionHandlers(codeStream);
+			SwitchExpression se1 = sub.getSwitchExpression();
+			setSubroutineSwitchExpression(sub);
+			boolean didEscape = sub.generateSubRoutineInvocation(currentScope, codeStream, this.targetLabel, this.initStateIndex, null);
+			sub.setSwitchExpression(se1);
+			if (didEscape) {
+				codeStream.removeVariable(this.secretYieldResultValue);
+					codeStream.recordPositionsFrom(pc, this.sourceStart);
+					SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, i, codeStream);
+					if (this.initStateIndex != -1) {
+						codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.initStateIndex);
+						codeStream.addDefinitelyAssignedVariables(currentScope, this.initStateIndex);
 					}
-				}
+					restartExceptionLabels(codeStream);
+					return;
 			}
 		}
-		if (!foundFinally) {
-			 // no finally - TODO: Check for SynSta?
-			se.loadStoredTypesAndKeep(codeStream);
-			codeStream.load(this.secretYieldResultValue);
+	}
+	se.loadStoredTypesAndKeep(codeStream);
+	codeStream.load(this.secretYieldResultValue);
+	codeStream.removeVariable(this.secretYieldResultValue);
+
+	codeStream.goto_(this.targetLabel);
+	codeStream.recordPositionsFrom(pc, this.sourceStart);
+	SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, -1, codeStream);
+	if (this.initStateIndex != -1) {
+		codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.initStateIndex);
+		codeStream.addDefinitelyAssignedVariables(currentScope, this.initStateIndex);
+	}
+}
+@Override
+public void generateCode(BlockScope currentScope, CodeStream codeStream) {
+	if ((this.bits & ASTNode.IsReachable) == 0) {
+		return;
+	}
+	if (this.switchExpression != null && this.switchExpression.containsTry && this.switchExpression.resolvedType != null ) {
+		generateExpressionResultCodeExpanded(currentScope, codeStream);
+		return;
+	}
+	this.expression.generateCode(this.scope, codeStream, this.switchExpression != null);
+	int pc = codeStream.position;
+
+	// generation of code responsible for invoking the finally
+	// blocks in sequence
+	if (this.subroutines != null){
+		for (int i = 0, max = this.subroutines.length; i < max; i++){
+			SubRoutineStatement sub = this.subroutines[i];
+			SwitchExpression se = sub.getSwitchExpression();
+			setSubroutineSwitchExpression(sub);
+			boolean didEscape = sub.generateSubRoutineInvocation(currentScope, codeStream, this.targetLabel, this.initStateIndex, null);
+			sub.setSwitchExpression(se);
+			if (didEscape) {
+					codeStream.recordPositionsFrom(pc, this.sourceStart);
+					SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, i, codeStream);
+					if (this.initStateIndex != -1) {
+						codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.initStateIndex);
+						codeStream.addDefinitelyAssignedVariables(currentScope, this.initStateIndex);
+					}
+					restartExceptionLabels(codeStream);
+					return;
+			}
 		}
-		codeStream.removeVariable(this.secretYieldResultValue);
-	} else {
-		this.expression.generateCode(this.scope, codeStream, se != null);
+	}
+	codeStream.goto_(this.targetLabel);
+	codeStream.recordPositionsFrom(pc, this.sourceStart);
+	SubRoutineStatement.reenterAllExceptionHandlers(this.subroutines, -1, codeStream);
+	if (this.initStateIndex != -1) {
+		codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.initStateIndex);
+		codeStream.addDefinitelyAssignedVariables(currentScope, this.initStateIndex);
 	}
 }
 private boolean isInsideTry() {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
index 393398a..ddf658f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
@@ -6823,7 +6823,7 @@
 
 public void pushExceptionOnStack(TypeBinding binding) {
 	this.stackDepth = 1;
-	clearTypeBindingStack();
+//	clearTypeBindingStack();
 	pushTypeBinding(binding);
 	if (this.stackDepth > this.stackMax)
 		this.stackMax = this.stackDepth;
@@ -7689,7 +7689,7 @@
 	for (int i = 0; i< nPop; ++i)
 		popTypeBinding();
 }
-private void clearTypeBindingStack() {
+public void clearTypeBindingStack() {
 	if (!isSwitchStackTrackingActive())
 		return;
 	this.switchSaveTypeBindings.clear();