Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 278562 - [1.5][compiler] Generated code results in VerifyError
Summary: [1.5][compiler] Generated code results in VerifyError
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.5   Edit
Hardware: All All
: P3 normal (vote)
Target Milestone: 3.5.1   Edit
Assignee: Olivier Thomann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-05-31 15:46 EDT by Robin Rosenberg CLA
Modified: 2009-08-28 03:09 EDT (History)
6 users (show)

See Also:
kent_johnson: review+


Attachments
proposed patch (1.19 KB, patch)
2009-05-31 19:43 EDT, Stephan Herrmann CLA
kent_johnson: iplog+
Details | Diff
Proposed patch and testcase (2.51 KB, patch)
2009-06-23 14:35 EDT, Kent Johnson CLA
no flags Details | Diff
Proposed fix + regression tests (12.45 KB, patch)
2009-06-23 18:07 EDT, Olivier Thomann CLA
no flags Details | Diff
Proposed fix + regression tests (12.11 KB, patch)
2009-06-23 21:45 EDT, Olivier Thomann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Robin Rosenberg CLA 2009-05-31 15:46:55 EDT
/*
Attempting to run the following program compiled by the eclipse compiler
results in this error:

Exception in thread "main" java.lang.VerifyError: (class: CompilerBugTest, method: i signature: (Ljava/lang/Enum;)V) Incompatible object argument for function call
Could not find the main class: CompilerBugTest. Program will exit.
*/
import java.util.HashMap;
import java.util.Map;

interface S {
}

enum A implements S {
	L;
}

public class CompilerBugTest {

	public static void main(String[] args) throws Exception {
		i(A.L);
	}

	static void i(Enum<? extends S> x) {
		Map m = new HashMap();
		for (Enum s : x.getDeclaringClass().getEnumConstants())
			m.put(s.name(), s);
	}

}
Comment 1 Stephan Herrmann CLA 2009-05-31 18:04:38 EDT
The compiler is confused about the type of
	x.getDeclaringClass().getEnumConstants();

Manual analysis yields
	x : Enum<? extends S>
	x.getDeclaringClass() : Class<Enum<? extends S>>
	x.getDeclaringClass().getEnumConstants() : Enum<? extends S>[]

However, the compiler seems to see a value of type S[].
Thus after invoking getEnumConstants() it adds a checkcast to S[]
which would mean that s is of type S - where S does not have
a method name().
javac correctly puts a checkcast to Enum[] instead.

The bug can be worked around by changing the loop to
	 for (Object s : x.getDeclaringClass().getEnumConstants()) {
             m.put(((Enum)s).name(), s);
         }
By typing s to Object we forget the wrong typing info and
the cast (Enum)s will produce the expected result.
Comment 2 Stephan Herrmann CLA 2009-05-31 19:43:23 EDT
Created attachment 137801 [details]
proposed patch

> The compiler is confused about the type of
>        x.getDeclaringClass().getEnumConstants();

Well, I was confused, too :-/. 
Should have looked at the declaration of class Enum<E extends Enum<E>>!

Types are better described as
        x : Enum<? extends S,Enum<?>>
        x.getDeclaringClass() : Class<? extends S,Enum<?>>
        x.getDeclaringClass().getEnumConstants() : <? extends S,Enum<?>>[]

The issue is, that the compiler finds two bounds for the leaf type of 
the overall expression: S and Enum<?>. From these two it incorrectly
decides to use S for the checkcast, whereas Enum<?> is used during
type checking.

Me thinks that when computing the conversion (including the cast) 
the expression cannot be analyzed in isolation but the expected type
is needed to decide which of two possible casts to insert.

The attached little patch does exactly this: feed an expected type
into computeConversion. I did run regression.GenericTypeTest and
ForeachStatementTest, but exhaustive testing still needs to be done.

Should also check if collections rather than arrays in the foreach
cause the same confusion.
Comment 3 Kent Johnson CLA 2009-06-01 14:52:30 EDT
Stephan - the patch looks good to me.

We need the expected return type to be passed to the conversion.


This code shows that either expected return type is valid :

import java.util.*;
interface S {}
enum A implements S {
 L;
}

class CompilerBugTest {
 public static void main(String[] args) throws Exception {
  i(A.L);
 }
 static void i(Enum<? extends S> x) {
  Class<? extends S> c = x.getDeclaringClass();
  S[] cs = c.getEnumConstants();
  // Enum[] ce = c.getEnumConstants(); // javac & eclipse expect S[]

  S[] cs2 = x.getDeclaringClass().getEnumConstants();
  Enum[] ce2 = x.getDeclaringClass().getEnumConstants(); // allowed by both

  Map m = new HashMap();
//  for (Enum s : cs2) // cannot convert from S to Enum
//   m.put(s.name(), s);

  for (Enum s : ce2)
   m.put(s.name(), s);

  for (Enum s : x.getDeclaringClass().getEnumConstants())
   m.put(s.name(), s);
 }
}
Comment 4 Olivier Thomann CLA 2009-06-22 08:34:43 EDT
+1 for 3.5.1
Comment 5 Kent Johnson CLA 2009-06-23 14:35:23 EDT
Created attachment 139899 [details]
Proposed patch and testcase

This patch runs fine on the 1.5 & 1.7 VM I have, but fails on 1.6 with :

[ERR]:java.lang.VerifyError: Bad type on operand stack in method X.i(Ljava/lang/Enum;)V at offset 36
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
	at java.lang.Class.getMethod0(Class.java:2670)
	at java.lang.Class.getMethod(Class.java:1603)
	at org.eclipse.jdt.core.tests.util.VerifyTests


Olivier, Stephan - please try the new EnumTest on a 1.6 VM to see if its reproduceable for you - thx
Comment 6 Olivier Thomann CLA 2009-06-23 15:57:58 EDT
I am working on it.
Comment 7 Olivier Thomann CLA 2009-06-23 17:19:31 EDT
Reproduced the failure with the 1.6 VM. I also get a failure with a JDK7 VM if I set the vm arguments to be -XX:-FailOverToOldVerifier -Xverify:all.

The type of the secret local for the array also needs to be updated. I'll update the patch and make sure that the test also fails with a JDK7 VM.
Comment 8 Olivier Thomann CLA 2009-06-23 18:07:25 EDT
Created attachment 139927 [details]
Proposed fix + regression tests

Fix the type of the local for the secret array.
Comment 9 Olivier Thomann CLA 2009-06-23 18:08:17 EDT
Kent, please review.
Comment 10 Olivier Thomann CLA 2009-06-23 19:04:25 EDT
Comment on attachment 139927 [details]
Proposed fix + regression tests

This was not good enough. Some problem with boxing/unboxing.
Comment 11 Olivier Thomann CLA 2009-06-23 21:45:14 EDT
Created attachment 139933 [details]
Proposed fix + regression tests

This patch is passing all existing tests + the new regression tests.
Comment 12 Olivier Thomann CLA 2009-06-24 11:18:04 EDT
Released for 3.5.1 and 3.6M1.
Added regression tests:
HEAD
org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test174
org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test175

3.5 maintenance
org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test173
org.eclipse.jdt.core.tests.compiler.regression.EnumTest#test174
Comment 13 Srikanth Sankaran CLA 2009-08-05 04:16:16 EDT
Verified for 3.6M1 using I20090803-1800
Comment 14 Jay Arthanareeswaran CLA 2009-08-28 03:01:55 EDT
Verified for 3.5.1RC2 using M20090826-1100