1414
1515package com .google .devtools .j2objc .javac ;
1616
17+ import static com .google .common .base .Preconditions .checkArgument ;
18+
1719import com .google .common .collect .Lists ;
1820import com .google .devtools .j2objc .Options ;
1921import com .google .devtools .j2objc .ast .AbstractTypeDeclaration ;
9496import com .google .devtools .j2objc .ast .SuperMethodInvocation ;
9597import com .google .devtools .j2objc .ast .SuperMethodReference ;
9698import com .google .devtools .j2objc .ast .SwitchCase ;
99+ import com .google .devtools .j2objc .ast .SwitchConstruct ;
97100import com .google .devtools .j2objc .ast .SwitchExpression ;
98101import com .google .devtools .j2objc .ast .SwitchStatement ;
99102import com .google .devtools .j2objc .ast .SynchronizedStatement ;
206209import com .sun .tools .javac .util .Position ;
207210import java .io .IOException ;
208211import java .util .ArrayList ;
209- import java .util .Iterator ;
210212import java .util .List ;
211213import java .util .Objects ;
212214import javax .lang .model .element .AnnotationMirror ;
@@ -318,8 +320,6 @@ private TreeNode convertInner(Tree javacNode, TreePath parent) {
318320 return convertBlock ((BlockTree ) javacNode , parent );
319321 case "BREAK" :
320322 return convertBreakStatement ((BreakTree ) javacNode );
321- case "CASE" :
322- return convertCase ((CaseTree ) javacNode , parent );
323323 case "CATCH" :
324324 return convertCatch ((CatchTree ) javacNode , parent );
325325 case "CLASS" :
@@ -675,19 +675,6 @@ private TreeNode convertBreakStatement(BreakTree node) {
675675 return newNode ;
676676 }
677677
678- private TreeNode convertCase (CaseTree node , TreePath parent ) {
679- // Case statements are converted in convertSwitch().
680- // TODO(b/456297342): Implement handling for patterns in case statements.
681- SwitchCase newNode = new SwitchCase ();
682- List <? extends ExpressionTree > expressions = node .getExpressions ();
683- for (ExpressionTree expressionTree : expressions ) {
684- TreePath path = getTreePath (parent , node );
685- newNode .addExpression ((Expression ) convert (expressionTree , path ));
686- }
687- newNode .setIsDefault (expressions .isEmpty ());
688- return newNode ;
689- }
690-
691678 private TreeNode convertCatch (CatchTree node , TreePath parent ) {
692679 TreePath path = getTreePath (parent , node );
693680 return new CatchClause ()
@@ -1371,12 +1358,6 @@ private TreeNode convertRecord(ClassTree node, TreePath parent) {
13711358
13721359 private TreeNode convertReturn (ReturnTree node , TreePath parent ) {
13731360 Expression expr = (Expression ) convert (node .getExpression (), getTreePath (parent , node ));
1374- if (expr != null && expr .getKind () == TreeNode .Kind .SWITCH_EXPRESSION ) {
1375- // This is returning just a switch expression and the construction transforms yields to
1376- // returns in this case so it is safe to just return the switch.
1377- // TODO(b/456285695): Move this to a pass.
1378- return new ExpressionStatement (expr );
1379- }
13801361 return new ReturnStatement (expr );
13811362 }
13821363
@@ -1386,139 +1367,59 @@ private TreeNode convertStringLiteral(LiteralTree node, TreePath parent) {
13861367
13871368 private TreeNode convertSwitch (SwitchTree node , TreePath parent ) {
13881369 TreePath path = getTreePath (parent , node );
1389- SwitchStatement newNode =
1370+ SwitchStatement switchStatement =
13901371 new SwitchStatement ().setExpression (convertWithoutParens (node .getExpression (), path ));
1391- for (CaseTree switchCase : node .getCases ()) {
1392- TreePath switchCasePath = getTreePath (path , switchCase );
1393- if (switchCase .getCaseKind () == CaseKind .RULE ) {
1394- SwitchCase switchCaseStmt = convertCaseRule (switchCase , switchCasePath );
1395- newNode .addStatement (switchCaseStmt );
1396- newNode .addStatement (new BreakStatement ());
1397- } else {
1398- newNode .addStatement ((SwitchCase ) convert (switchCase , path ));
1399- for (StatementTree s : switchCase .getStatements ()) {
1400- newNode .addStatement ((Statement ) convert (s , switchCasePath ));
1401- }
1402- }
1372+ for (CaseTree caseTree : node .getCases ()) {
1373+ TreePath switchCasePath = getTreePath (parent , caseTree );
1374+ SwitchCase switchCase = convertSwitchCaseLabel (caseTree , switchCasePath );
1375+ switchStatement .addStatement (switchCase );
1376+ convertSwitchCaseBody (caseTree , switchCasePath , switchStatement , switchCase );
14031377 }
1404- return newNode ;
1378+
1379+ return switchStatement ;
14051380 }
14061381
14071382 private SwitchExpression convertSwitchExpression (SwitchExpressionTree node , TreePath parent ) {
14081383 TreePath path = getTreePath (parent , node );
1409- SwitchExpression newNode =
1410- new SwitchExpression ().setExpression (convertWithoutParens (node .getExpression (), path ));
1411- newNode .setTypeMirror (getTypeMirror (path ));
1412-
1413- Tree parentTree = parent .getLeaf ();
1414- // TODO(b/456285695): Move the optimization of the two cases to a pass. And implement the
1415- // general case, possibly using block expressions.
1416- boolean exprReturned = parentTree .getKind () == Tree .Kind .RETURN ;
1417- boolean exprSaved = parentTree .getKind () == Tree .Kind .VARIABLE ;
1418- VariableElement yieldSymbol = null ;
1419- if (parentTree instanceof JCVariableDecl varDecl ) {
1420- yieldSymbol = varDecl .sym ;
1421- }
1422-
1423- List <? extends CaseTree > cases = node .getCases ();
1424- for (CaseTree caseTree : cases ) {
1425- TreePath switchCasePath = getTreePath (path , caseTree );
1426-
1427- SwitchCase switchCase = convertCaseRule (caseTree , switchCasePath );
1428-
1429- // Convert any cases that have multiple expressions into a list of
1430- // SwitchCases for all but the last expression, remove them from
1431- // the SwitchExpressionCase and insert their SwitchCase equivalent
1432- // ahead of the current case.
1433- List <Expression > caseExprs = switchCase .getExpressions ();
1434- if (caseExprs .size () > 1 ) {
1435- Iterator <Expression > caseExprIter = caseExprs .iterator ();
1436- Expression caseExpr = caseExprIter .next ();
1437- do {
1438- caseExprIter .remove ();
1439- SwitchCase newCase = new SwitchCase ();
1440- newCase .addExpression (caseExpr .copy ());
1441- newNode .addStatement (newCase );
1442- caseExpr = caseExprIter .next ();
1443- } while (caseExprIter .hasNext ());
1444- }
14451384
1446- TreeNode body = switchCase .getBody ();
1447- if (body .getKind () == TreeNode .Kind .YIELD_STATEMENT ) {
1448- Expression yield = ((YieldStatement ) body ).getExpression ().copy ();
1449- if (exprReturned ) {
1450- switchCase .setBody (new ReturnStatement (yield ));
1451- } else if (exprSaved ) {
1452- Assignment assignment = new Assignment (new SimpleName (yieldSymbol ), yield );
1453- switchCase .setBody (new ExpressionStatement (assignment ));
1454- }
1455- } else if (body .getKind () == TreeNode .Kind .BLOCK ) {
1456- List <Statement > blockStmts = ((Block ) body ).getStatements ();
1457- if (!blockStmts .isEmpty ()) {
1458- int lastIndex = blockStmts .size () - 1 ;
1459- Statement lastStmt = blockStmts .get (lastIndex );
1460- if (lastStmt .getKind () == TreeNode .Kind .YIELD_STATEMENT ) {
1461- Expression yield = ((YieldStatement ) lastStmt ).getExpression ().copy ();
1462- if (exprReturned ) {
1463- switchCase .setBody (new ReturnStatement (yield ));
1464- } else if (exprSaved ) {
1465- Assignment assignment = new Assignment (new SimpleName (yieldSymbol ), yield );
1466- switchCase .setBody (new ExpressionStatement (assignment ));
1467- }
1468- }
1469- }
1470- }
1471- newNode .addStatement (switchCase );
1472- if (exprSaved ) {
1473- newNode .addStatement (new BreakStatement ());
1474- }
1475- }
1385+ SwitchExpression switchExpression =
1386+ new SwitchExpression ()
1387+ .setExpression (convertWithoutParens (node .getExpression (), path ))
1388+ .setTypeMirror (getTypeMirror (path ));
14761389
1477- if (!newNode .hasDefaultCase ()) {
1478- addUnreachableDirective (newNode );
1390+ for (CaseTree caseTree : node .getCases ()) {
1391+ TreePath switchCasePath = getTreePath (parent , caseTree );
1392+ SwitchCase switchCase = convertSwitchCaseLabel (caseTree , switchCasePath );
1393+ switchExpression .addStatement (switchCase );
1394+ convertSwitchCaseBody (caseTree , switchCasePath , switchExpression , switchCase );
14791395 }
14801396
1481- return newNode ;
1482- }
1483-
1484- // If a switch expression doesn't have a default case, add a function call to tell clang that all
1485- // cases are handled. This can be asserted because switch expressions are checked to be
1486- // exhaustive.
1487- private void addUnreachableDirective (SwitchExpression switchExpression ) {
1488- // If the switch expression has no default case, add one
1489- SwitchCase defaultCase = new SwitchCase ().setIsDefault (true );
1490- switchExpression .addStatement (defaultCase );
1491-
1492- TypeMirror voidType = newUnit .getEnv ().typeUtil ().getVoid ();
1493- FunctionElement element = new FunctionElement ("__builtin_unreachable" , voidType , null );
1494- FunctionInvocation unreachableInvocation = new FunctionInvocation (element , voidType );
1495-
1496- if (switchExpression .getStatements ().isEmpty ()
1497- || ((SwitchCase ) switchExpression .getStatements ().get (0 )).getBody () != null ) {
1498- // If the switch expression has rules, add it as a rule.
1499- defaultCase .setBody (new ExpressionStatement (unreachableInvocation ));
1500- } else {
1501- // Otherwise, add it as a statement.
1502- switchExpression .addStatement (new ExpressionStatement (unreachableInvocation ));
1397+ if (!switchExpression .hasDefaultCase ()) {
1398+ // Switch expressions are exhaustive, so if there is no default case, add one to signal the
1399+ // compiler that the default case is unreachable.
1400+ addUnreachableDirective (switchExpression );
15031401 }
1402+
1403+ return switchExpression ;
15041404 }
15051405
1506- private SwitchCase convertCaseRule (CaseTree caseTree , TreePath parent ) {
1406+ private SwitchCase convertSwitchCaseLabel (CaseTree caseTree , TreePath parent ) {
1407+ TreePath switchCasePath = getTreePath (parent , caseTree );
15071408 SwitchCase switchCase = new SwitchCase ();
15081409 List <? extends CaseLabelTree > caseExpressionsList = caseTree .getLabels ();
15091410 for (CaseLabelTree caseLabelTree : caseExpressionsList ) {
15101411 switch (caseLabelTree .getKind ()) {
15111412 case CONSTANT_CASE_LABEL -> {
15121413 ConstantCaseLabelTree constantCaseLabelTree = (ConstantCaseLabelTree ) caseLabelTree ;
15131414 switchCase .addExpression (
1514- (Expression ) convert (constantCaseLabelTree .getConstantExpression (), parent ));
1415+ (Expression ) convert (constantCaseLabelTree .getConstantExpression (), switchCasePath ));
15151416 }
15161417 case PATTERN_CASE_LABEL -> {
15171418 PatternCaseLabelTree patternCaseLabelTree = (PatternCaseLabelTree ) caseLabelTree ;
15181419 if (patternCaseLabelTree .getPattern () instanceof BindingPatternTree bindingPatternTree ) {
15191420 VariableTree varTree = (VariableTree ) bindingPatternTree .getVariable ();
15201421 VariableElement var =
1521- ((VariableDeclaration ) convertVariableDeclaration (varTree , parent ))
1422+ ((VariableDeclaration ) convertVariableDeclaration (varTree , switchCasePath ))
15221423 .getVariableElement ();
15231424 Pattern .BindingPattern pattern = new Pattern .BindingPattern (var );
15241425 switchCase .setPattern (pattern );
@@ -1528,25 +1429,72 @@ private SwitchCase convertCaseRule(CaseTree caseTree, TreePath parent) {
15281429 default -> throw new AssertionError ("unknown case label type: " + caseLabelTree .getKind ());
15291430 }
15301431 }
1531- Tree javacBody = caseTree .getBody ();
1532- if (javacBody != null ) {
1533- TreeNode body = convert (javacBody , parent );
1534- if (body instanceof Expression ) {
1535- body = new YieldStatement ((Expression ) body );
1432+ switchCase .setGuard ((Expression ) convert (caseTree .getGuard (), switchCasePath ));
1433+
1434+ return switchCase ;
1435+ }
1436+
1437+ private void convertSwitchCaseBody (
1438+ CaseTree caseTree , TreePath parent , SwitchConstruct switchConstruct , SwitchCase switchCase ) {
1439+ TreePath switchCasePath = getTreePath (parent , caseTree );
1440+ if (caseTree .getCaseKind () == CaseKind .RULE ) {
1441+ if (caseTree .getBody () != null ) {
1442+ boolean isImplicitYield =
1443+ switchConstruct instanceof SwitchExpression switchExpression
1444+ && switchExpression .getTypeMirror ().getKind () != TypeKind .VOID ;
1445+
1446+ TreeNode body = convert (caseTree .getBody (), parent );
1447+ if (body instanceof Expression expression ) {
1448+ if (isImplicitYield ) {
1449+ // Switch expression that are not void have an implicit yield.
1450+ body = new YieldStatement (expression );
1451+ } else {
1452+ body = new ExpressionStatement (expression );
1453+ }
1454+ }
1455+ if (!isImplicitYield ) {
1456+ // Add a break statement to the switch statement with rules to avoid fallback.
1457+ Block block = body instanceof Block b ? b : new Block ().addStatement ((Statement ) body );
1458+ block .addStatement (new BreakStatement ());
1459+ body = block ;
1460+ }
1461+ switchCase .setBody ((Statement ) body );
1462+
1463+ } else {
1464+ Block body = new Block ();
1465+ for (StatementTree s : caseTree .getStatements ()) {
1466+ body .addStatement ((Statement ) convert (s , switchCasePath ));
1467+ }
1468+ switchCase .setBody (body );
15361469 }
1537- switchCase .setBody ((Statement ) body );
15381470 } else {
1539- Block body = new Block ();
1540- List <? extends StatementTree > statementTrees = caseTree .getStatements ();
1541- for (StatementTree statementTree : statementTrees ) {
1542- body .addStatement ((Statement ) convert (statementTree , parent ));
1471+ checkArgument (caseTree .getBody () == null );
1472+ for (StatementTree s : caseTree .getStatements ()) {
1473+ switchConstruct .addStatement ((Statement ) convert (s , switchCasePath ));
15431474 }
1544- switchCase .setBody (body );
15451475 }
1476+ }
1477+
1478+ // If a switch expression doesn't have a default case, add a function call to tell clang that all
1479+ // cases are handled. This can be asserted because switch expressions are checked to be
1480+ // exhaustive.
1481+ private void addUnreachableDirective (SwitchExpression switchExpression ) {
1482+ // If the switch expression has no default case, add one
1483+ SwitchCase defaultCase = new SwitchCase ().setIsDefault (true );
1484+ switchExpression .addStatement (defaultCase );
15461485
1547- switchCase .setGuard ((Expression ) convert (caseTree .getGuard (), parent ));
1486+ TypeMirror voidType = newUnit .getEnv ().typeUtil ().getVoid ();
1487+ FunctionElement element = new FunctionElement ("__builtin_unreachable" , voidType , null );
1488+ FunctionInvocation unreachableInvocation = new FunctionInvocation (element , voidType );
15481489
1549- return switchCase ;
1490+ if (switchExpression .getStatements ().isEmpty ()
1491+ || ((SwitchCase ) switchExpression .getStatements ().get (0 )).getBody () != null ) {
1492+ // If the switch expression has rules, add it as a rule.
1493+ defaultCase .setBody (new ExpressionStatement (unreachableInvocation ));
1494+ } else {
1495+ // Otherwise, add it as a statement.
1496+ switchExpression .addStatement (new ExpressionStatement (unreachableInvocation ));
1497+ }
15501498 }
15511499
15521500 private TreeNode convertSynchronized (SynchronizedTree node , TreePath parent ) {
0 commit comments