Bug 565844 - [14] Compiler rejects code with conditional expression in a switch expression's case
Summary: [14] Compiler rejects code with conditional expression in a switch expression...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.14   Edit
Hardware: PC Mac OS X
: P3 normal (vote)
Target Milestone: 4.17 M3   Edit
Assignee: Manoj N Palat CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-08-06 00:24 EDT by Jay Arthanareeswaran CLA
Modified: 2023-03-21 16:21 EDT (History)
4 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jay Arthanareeswaran CLA 2020-08-06 00:24:35 EDT
The following code produces a compiler error:

public class Cls  {
    public final static int j = 1;
    public static void main(String argv[]) {
    	boolean b = 
    			switch (j) {
    				case j != 1 ? 2 : 3 ->  false;
    				default -> true;
    			}; 
    }
}

Reported error is: Syntax error on token "3", BeginCaseExpr expected after this token

My debugging so far shows that we don't return TokenNameBeginCaseExpr in disambiguatedToken() because of prematurely setting isCase to false by mistaking the ':' in the conditional expression as the end of case. Working on a fix.
Comment 1 Eclipse Genie CLA 2020-08-06 02:51:53 EDT
New Gerrit change created: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/167330
Comment 3 Jay Arthanareeswaran CLA 2020-08-06 06:20:42 EDT
Manoj, when you can spare a min, please go through the patch.
Comment 4 Jay Arthanareeswaran CLA 2020-08-06 06:56:01 EDT
Sorry, this patch is naive. Needs a better fix. The following case as pointed out by Manoj doesn't work:

case j != 1 ? ( j != 1 ? 2: 3 ) : 3 -> false;

Reopening.
Comment 5 Eclipse Genie CLA 2020-08-07 00:35:05 EDT
New Gerrit change created: https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/167373
Comment 6 Manoj N Palat CLA 2020-08-08 03:42:34 EDT
(In reply to Eclipse Genie from comment #5)
> New Gerrit change created:
> https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/167373

Dev Notes:
This patch attempts to resolve the conflict of lambda and case at arrow by using the Vanguard parser to parse the "relevant" code snippet for an equivalent case of colon - i.e, by pushing a fake colon token into a duplicated source code and then from the "relevant start", vanguard parser is called in action for a SwitchLabelCaseLhs - since there is no conflict for the colon case, there is a clear success or failure without any synthetic token. Now, to figure out the "relevant" code, the scanner keeps the source start rather than the existing boolean of "inCase". However, this is not enough for nested case statements. This is solved by having a "case" stack in the parser that is decremented in consumeCaseLabel. 

Further: 
(1) the parser setting the start would work for the "Parser", but when diagnostic parser kicks in, the scanner will have to set the start position (see updateCase()) since the case source start setting code may not be touched in.
(2) There indeed is some duplication involving nested case, since we are reparsing once for every nesting case level - until we can come up with a more optimized solution, this may stay.
(3)case having a constant switch expression as a label is allowed, but the constant analysis does not return this as a constant - a follow up bug 565907 tracks this issue.
Comment 7 Manoj N Palat CLA 2020-08-09 05:12:16 EDT
(In reply to Manoj Palat from comment #6)
Well, the above one hit upon an infinite loop in recovery -Looks like this is not a great solution.
Consider :
public class C {

	void foo() {
		Object value2 = switch(1) {
		case AAABBB -> 1;
		(Runnable)()->f();
		default -> throw new RuntimeException("unsupported");
		};
	}
}
the above (reduced from ForRegTests.testBug543818a) gets into an infinite loop. Need to find an alternate solution..
(a) a parser that copies the state and check for a reduction with colon?
(b) revamp of grammar? [the coflict is wrt NestedLambda vs SimpleName)
Comment 8 Stephan Herrmann CLA 2020-08-09 07:13:04 EDT
Seeing that lambda parsing has a finger in the pie, I would actually propose to first bring bug 561934 to conclusion, see in particular bug 561934 comment 10. It may or may not have an influence on the issue here, but knowing that somewhere at the bottom of our house of cards of parser tweaks there is one shaky card that could be replaced with a solid brick wall, I would advise against further sleights of hand at the top of the house.
Comment 9 Stephan Herrmann CLA 2020-08-09 07:32:21 EDT
(In reply to Manoj Palat from comment #7)
> (In reply to Manoj Palat from comment #6)
> Well, the above one hit upon an infinite loop in recovery

It would be interesting to compare this to existing bugs with the same symptom: bug 530556, bug 548779. Could you describe the hang more closely?

Indirectly related: bug 561878 and https://wiki.eclipse.org/JDT_Core_Programmer_Guide/Completion
Comment 10 Manoj N Palat CLA 2020-08-10 11:04:32 EDT
(In reply to Stephan Herrmann from comment #9)
>  Could you describe the hang more closely?
Given the following snippet:
public class X {
	void foo() {
		Object value2 = switch(1) {
			case AAABBB -> 1;
		//	(I)()->();
			default : yield 0;
		};
	}
}
interface I {
	void apply();
}
}

Parser.moveRecoveryCheckpoint() does not move beyond the -> in case AAABBB -> 1. BeginCaseExpr is the token at the checkpoint, and nextToken will be TokenNameARROW; on a restart, this scenario repeats and the scanner does not move forward, resulting in a hang.
Comment 12 Manoj N Palat CLA 2020-08-12 03:55:03 EDT
(In reply to Eclipse Genie from comment #11)
> Gerrit change https://git.eclipse.org/r/c/jdt/eclipse.jdt.core/+/167373 was
> merged to [master].
> Commit:
> http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/
> ?id=73e843b3ad1076d2dbe78fac7e9422c3e823cd2e

The infinite loop was resolved by checking whether we move forward in Parser.moveRecoveryCheckpoint() by comparing the lastCheckpoint and the scanner's currentPosition. The patch above returns false if they are same indicating that we don't move forward. 
@Stephan: yes, the stack traces look similar for  bug 530556 and bug 548779. I checked the test cases there but it doesn't hit P.mRC() pointing towards another issue. And from comment 8, that issue doesn't look related but will try a hand once I can get at bug 561934 post Java 15 support.
Comment 13 Jay Arthanareeswaran CLA 2020-08-19 03:22:29 EDT
Verified for 4.17 M3 using build I20200818-1800
Comment 14 Simeon Andreev CLA 2023-03-21 16:21:35 EDT
The change here introduced a regression: https://github.com/eclipse-jdt/eclipse.jdt.core/issues/193

The following snippet now shows unexpected compile errors:

import java.nio.file.StandardCopyOption;

@SuppressWarnings("nls")
public class EclipseTest {
    
    private void a(StandardCopyOption option) throws Exception {
	switch (option) {
	case ATOMIC_MOVE:
	    break;
	}
    }

    public void b() throws Exception {
	Runnable r = () -> {
	};

    }

    private fi  <==== Empty content assist box 
    
}


----------
1. ERROR in .../EclipseTest.java (at line 11)
        }
        ^
Syntax error, insert "}" to complete ClassBody
----------
2. ERROR in .../EclipseTest.java (at line 14)
        Runnable r = () -> {
                        ^^
Syntax error on token "BeginCaseExpr", delete this token
----------
3. ERROR in .../EclipseTest.java (at line 17)
        }
        ^
Syntax error on token "}", delete this token


From what I can tell Scanner.disambiguateArrowWithCaseExpr() detects an arrow switch case where there is none.