This Bugzilla instance is deprecated, and most Eclipse projects now use GitHub or Eclipse GitLab. Please see the deprecation plan for details.
Bug 390889 - [1.8][compiler] Evaluate options to support 1.7- projects against 1.8 JRE.
Summary: [1.8][compiler] Evaluate options to support 1.7- projects against 1.8 JRE.
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.8   Edit
Hardware: All All
: P2 normal with 2 votes (vote)
Target Milestone: 4.4 M7   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 426265 426789 430793 432087 432205 (view as bug list)
Depends on:
Blocks: 380501
  Show dependency tree
 
Reported: 2012-10-02 02:42 EDT by Srikanth Sankaran CLA
Modified: 2016-06-17 10:51 EDT (History)
22 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Srikanth Sankaran CLA 2012-10-02 02:42:53 EDT
BETA_JAVA8:

If a 1.7- project that builds fine against a 1.7 JRE is compiled against
1.8 JRE, the code may not compile anymore. This can happen for instance
when the 1.8 JRE introduces a brand new method in an existing interface
and provides a default implementation for that.

We need to consider how best to support (or not) this scenario, the cost
benefit analysis etc.

Stephan, could you identify the problem scenarios and provide small
snippets for them based on how you tweaked our test suites to address
the problems encountered there.

See that javac7 will refuse JRE8 class files. javac8 when invoked with
-source 1.7 option skips the interface method bodies and treats them
as plain abstract methods.
Comment 1 Stephan Herrmann CLA 2012-10-11 17:20:47 EDT
I'm using the following aliases for various compiler invocations:

javac7: oracle javac 1.7 with default options
javac8: lambda enabled javac with default options
javac8-7: lambda enabled javac with -source 1.7 -target 1.8

ecj7: ecj as of Juno GA with option -1.7
ecjb8: ecj from BETA_JAVA8 with option -1.8
ecjb7: ecj from BETA_JAVA8 with option -1.7

-------------------------------------------------------------------------
1. Experiment:

Compile with javac8 or ecjb8 (equal bytes):

  package pack;
  public interface I {
    void foo() default {}
  }

Then consuming the above from its .class file compile this:

  package pack;
  import pack.I;
  public class CI implements I {}

Results:

javac7:   silent
javac8:   silent
javac8-7: 1 warning:
  warning: [options] bootstrap class path not set in conjunction with -source 1.7
  
ecj7:   silent
ecjb8:  silent
ecjb7:  1 error:
  The type CI must implement the inherited abstract method I.foo()

================================
Discussion:
javac7 being silent is UNEXPECTED, does it see that foo is non-abstract?
javac8-7 being silent is even more UNEXPECTED.
ecj7 results are basically expected since we changed only recently how 
non-abstract interface methods are interpreted.
================================

-------------------------------------------------------------------------
2. Experiment:

Compile this class:

package pack;
import java.util.Collection;
import java.util.Iterator;

public class CCollection implements Collection<String> {
        public int size() { return 0; }
        public boolean isEmpty() { return false; }
        public boolean contains(Object o) { return false; }
        public Iterator<String> iterator() { return null; }
        public Object[] toArray() { return null; }
        public <T> T[] toArray(T[] a) { return null; }
        public boolean add(String e) { return false; }
        public boolean remove(Object o) { return false; }
        public boolean containsAll(Collection<?> c) { return false; }
        public boolean addAll(Collection<? extends String> c) { return false; }
        public boolean removeAll(Collection<?> c) { return false; }
        public boolean retainAll(Collection<?> c) { return false; }
        public void clear() {}
}

Results:
javac7:   silent
javac8:   silent
javac8-7:   1 error, 1 warning:
  warning: [options] bootstrap class path not set in conjunction with -source 1.7
  CCollection is not abstract and does not override abstract method addAll(Iterable<? extends String>) in Fillable

Using -bootclasspath pointing to the lambda enabled JRE:
javac7:   1 error:
  CCollection is not abstract and does not override abstract method addAll(Iterable<? extends String>) in Fillable
  
  
ecj: all variants are silent when run on JVM 7

Running ecj on the lambda enabled JVM:
ecj7:  silent
ecjb8: silent
ecjb7: 26 errors, all like
  The type CCollection must implement the inherited abstract method Collection<String>.addAll(Iterable<? extends String>)

================================
Discussion:
javac results are expected, since fillAll(Iterable) is a default method.
The surprise is in the difference to experiment 1.

ecj on JVM 7 is expected, the default methods are not present here.
ecj on lambda enabled JVM 8 results are basically expected since we
changed only recently how non-abstract interface methods are interpreted.
================================

-------------------------------------------------------------------------
3. Experiment:

Let our compiler produce class file with version 52 (javac still produces 51).

Consume the 52 version class file:

all variants of javac: 1 warning
  major version 52 is newer than 51, the highest major version supported by this compiler.
  It is recommended that the compiler be upgraded.

all variants of ecj: silent

-------------------------------------------------------------------------
Results:

I have no explanation why javac7 and javac8-7 are silent in Experiment 1.
The only difference between the interfaces I and Collection that comes to
mind is: one is part of the JRE while the other one is my own. Having a deep look into the byte code I could not find a significant difference. No default method is marked with a special modifier or such.

As long as javac doesn't make up its mind how to interpret default methods
below 1.8 I don't believe we can successfully mimic its behavior.

Shouldn't ecj start emitting warnings regarding unsupported class file versions?
Comment 2 Stephan Herrmann CLA 2012-10-11 17:30:19 EDT
(In reply to comment #0)
> Stephan, could you identify the problem scenarios and provide small
> snippets for them based on how you tweaked our test suites to address
> the problems encountered there.

The tweaks I applied to the test suite basically go like this:

Check the system property "java.specification.version" of the running vm for "1.8".

If found, add stub implementation for all relevant new interface methods (with default body) to all implementing test classes.

The bulk of http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?h=BETA_JAVA8&id=dd3bff4d99a5193497eb7e3c0e1bc46a32b7c36a is only devoted to this tweak (incl. string based substitution of type parameters).

This way our tests work fine, but in modes below the host VM version we're basically operating in the very situation that is considered as "unsupported".
Comment 3 Stephan Herrmann CLA 2012-10-11 17:34:05 EDT
(In reply to comment #0)
> See that javac7 will refuse JRE8 class files.

It *will* eventually emit a _warning_. Currently, there are no class files recognizable as Java8.

> javac8 when invoked with -source 1.7 option skips the interface method 
> bodies and treats them as plain abstract methods.

Sometimes, but not in Experiment 1 above.
Comment 4 Stephan Herrmann CLA 2012-10-11 17:37:13 EDT
I see these possible action items:

A. wait until class file version 52 is in use by all tools

B. ask oracle about the difference between Experiments 1 & 2

C. add a warning re incompatible class file version to ecj.

D. wait until the access bit for default methods shows up.

E. work with releng to provide multiple JREs into the test suite
   and always compile against the one matching the compliance level.
Comment 5 Stephan Herrmann CLA 2013-01-26 14:16:27 EST
(In reply to comment #4)
> A. wait until class file version 52 is in use by all tools

OK, lambda enabled jdk b74 uses class file version 52. Re-testing with this jdk yields:

In Experiment 1 javac8-7 remains silent (except for the general warning re bootstrap class path).
This is EVEN MORE UNEXPECTED  (I used a version 52 pack/I.class) and javac8-7 should report that we're facing something in I which we can't support at -source 1.7.

To add insult to injury: javac8-7 (i.e., compiling with -source 1.7) creates a class file with version 52, too! So even when compiling for 1.7 running on a 1.7 VM yields:
java.lang.UnsupportedClassVersionError: pack/CI : Unsupported major.minor version 52.0

Conclusion: javac is not yet in a state where we should copy any behavior in these regards.
Comment 6 Stephan Herrmann CLA 2013-03-31 19:41:22 EDT
Let's give an entirely new twist to this story:

Recall: in October I spoke to Oracle people how to handle this compatibility issue, and they made me feel the question itself is wrong: no help should ever be given to users for compiling in 1.7 mode against a 1.8 JRE (my words, not exactly theirs).

As a result we implemented (in bug 388954) that the body of default methods found in class files should simply be ignored and AccAbstract silently added instead.
This required significant rework across the entire test suite (bug 388800).

From doing more experiments I learned that even in 1.7 mode javac8 uses some sophistication for gracefully dealing with default methods:
- clients of the interface can see the default method as a normal interface method, ready to be called.
- implementors of the interface *do not* see the default method at all, as demonstrated by two facts:
  - implementors do *not* have to implement any default methods from the super interface
  - implementors can *not* resolve the inherited method in any self calls!

This behavior was actually already present at least in b47, just nobody explained their strategy.

Here's the snippet that reveals the strategy:

  // compile separately first:
  public interface I {
     default void foo() {}
  }
  // then compile this consuming the above from .class:
  public class CI implements I {
    void test(I i) {
      this.foo();
      i.foo();
    }
  }

javac8 -source 1.7 -target 1.7 answers a single error:

pack/CI.java:5: error: cannot find symbol
      this.foo();
          ^
  symbol: method foo()


So, "this" (instanceof CI which implements I) knows no method "foo", whereas "i" of type "I" does.

Do we want to mimic this behavior? Looks kind of crude to me, but, OTOH, it might indeed avoid some of the issues I was raising in the first place.


Let me reveal one more mystery: why did javac8-7 produce in experiment 2 the following message (older versions only):
  CCollection is not abstract and does not override abstract method addAll(Iterable<? extends String>) in Fillable
?

Because:
- in early versions (e.g., b50) Collection extended an interface Fillable with a *regular* (i.e., abstract) interface method addAll().
- Collection had (and still has) a default method of the same signature
- for javac8 the default method implemented the inherited abstract method
- when analyzing the inheritance structure, javac8-7 *only* saw the abstract method in Fillable, but *not* the implementation in Collection.

Ergo, using a default method to implement an inherited abstract method is an anti pattern, should this occur in any library, cross-compilation will break.

Implementationwise, in order to mimic this behavior we should probably mark default methods, which we find while in 1.7 mode as (AccAbstract|AccDefaultMethod).
Only so, can we
- treat them as normal abstract methods in call scenarii
- simply drop them when analyzing inheritance
Which also means: we can *not* rely on isDefaultMethod() indicating that we are compiling for Java8, which we are currently doing in several locations.
Comment 7 Stephan Herrmann CLA 2013-04-06 18:40:58 EDT
From answers on the EG list I conclude:

- the observed behaviour of javac8 is more a result of its implementation
  than a carefully designed solution.
  The fact that inherited default methods cannot be called via the class
  simply results from javac's strategy to never search in interfaces
  when a method is invoked an a non-abstract class.
  By contrast ecj avoids to report such errors, which we consider to be
  uninteresting secondary errors (the primary error being an unimplemented
  method).
  What ever happens to be the straight forward behaviour in javac, may
  not even be easy to achieve in ecj.

- that behaviour of javac may well change in the future

- it's too early to make a call now, javac developers have more important
  tasks on their plates now.

This bug is in state IDLE now.
Comment 8 Stephan Herrmann CLA 2013-11-28 11:13:46 EST
News from the EG list:
http://mail.openjdk.java.net/pipermail/lambda-spec-experts/2013-November/000437.html

The essence:
"javac does a lot of work to ensure that, under -source 7, default methods are "visible" at appropriate times, but don't get in the way:"

It seems we should make ecj -1.7 aware of default methods to some extent, too.
Comment 9 Srikanth Sankaran CLA 2014-01-31 07:50:00 EST
*** Bug 426789 has been marked as a duplicate of this bug. ***
Comment 10 Srikanth Sankaran CLA 2014-01-31 07:51:54 EST
reclassifying this to be CR rather than ER - I think users will find this to
be a bug rather than an enhancement.

JDT/Debug could be a verification target when this is completed. See
https://bugs.eclipse.org/bugs/show_bug.cgi?id=426789
Comment 11 Stephan Herrmann CLA 2014-01-31 08:57:30 EST
(In reply to Srikanth Sankaran from comment #10)
> reclassifying this to be CR rather than ER - I think users will find this to
> be a bug rather than an enhancement.

Point taken. From a user's p.o.v. I totally agree.
A bug as in "a deviation from the specified behaviour" I must object:
this behaviour is explicitly unspecified :)

> JDT/Debug could be a verification target when this is completed. See
> https://bugs.eclipse.org/bugs/show_bug.cgi?id=426789

Good point. I'll try to attend to this soon.

I guess the most straight-forward approach will be to let the compiler at compliance 1.7 exactly *understand* what a default method in a class file is,
and perform (a slight variant of?) 1.8 validation actually.
Comment 12 Srikanth Sankaran CLA 2014-02-06 22:26:58 EST
Dani and Markus,

Unless I hear strong objections, I would like to move this task out of Java 8 GA
to 4.4 (or perhaps 4.5). This is completely out of the realms of specification
and involves risky changes. Parts of the compiler should get default methods 
exposed to them and other not. We don't have a precise picture of the contours
of the solution. While in the long run, "best case" efforts should be made
to support this scenario, compatibility here is against javac which is an opaque/ 
black box for us.

FWIW, see that the equivalent scenario of inter-operability of 1.4 programs'with 1.5+ projects/libraries was not solved till 2012 Dec many years after 1.5 support
shipped.

https://bugs.eclipse.org/bugs/buglist.cgi?classification=Eclipse&component=Core&list_id=8172697&product=JDT&query_format=advanced&short_desc=1.4%2F1.5&short_desc_type=allwordssubstr

Please share your opinion either way.
Comment 13 Srikanth Sankaran CLA 2014-02-06 22:28:16 EST
(In reply to Srikanth Sankaran from comment #12)

> FWIW, see that the equivalent scenario of inter-operability of 1.4
> programs'with 1.5+ projects/libraries was not solved till 2012 Dec many
> years after 1.5 support
> shipped.

I meant till Dec 2010.
Comment 14 Markus Keller CLA 2014-02-07 11:40:58 EST
I agree with taking this out of the Java 8 GA bucket, but I think we should aim to address frequently-encountered problems for 4.4.

I.e. the goal should be to allow basic development of 1.6/1.7 projects in situations where only a Java 8 JRE is available.
Good test case: Compile Eclipse SDK projects from source with only a Java 8 JRE.

I'm not sure about the 1.4/1.5 bugs you cited. Was the interoperability really unusable before those fixes? Or was this just the last batch of fixes for corner cases?
Comment 15 Markus Keller CLA 2014-02-07 11:53:58 EST
E.g. these classes should compile:

import java.util.Comparator;
public class MyComp implements Comparator {
	@Override
	public int compare(Object o1, Object o2) {
		return 0;
	}
}
class MyStringComp implements Comparator<String> {
	@Override
	public int compare(String o1, String o2) {
		return 0;
	}
}

In BETA_JAVA8 you get 7 compile errors "Type must implement the inherited abstract method Comparator.reversed()", etc.
Comment 16 Srikanth Sankaran CLA 2014-02-07 13:57:22 EST
(In reply to Markus Keller from comment #14)

Thanks for weighing in.

> I'm not sure about the 1.4/1.5 bugs you cited. Was the interoperability
> really unusable before those fixes? Or was this just the last batch of fixes
> for corner cases?

I don't think they were corner cases. They were blockers in that code would
not compile and fail with name clash errors and iirc runtime failures due to
missing bridges and such.

I don't know that it was unusable before this cluster of bugs was fixed - what
we do know that as soon as some of the eclipse projects entered into this mode
(IIRC Equinox) they were hit with this and there were a series of 10-12 problems
that needed to be attended to.
Comment 17 Stephan Herrmann CLA 2014-02-07 14:08:13 EST
(In reply to Markus Keller from comment #15)
> E.g. these classes should compile:

So, ecj -1.7 must understand default methods, I don't see a way around that, because the same method must behave like an interface method (for clients) and like a concrete method (for implementors).

In particular for the implementors view the following options *don't* work:
- treat the method as a regular interface method: would force implementors to implement it.
- make the method invisible: the default method could be migrated from a regular interface method. Implementors must be able to call it in self calls.


I'll give it a quick shot and let you test it and decide into which bucket it should go :)
Comment 18 Michael Rennie CLA 2014-02-14 11:21:07 EST
*** Bug 426265 has been marked as a duplicate of this bug. ***
Comment 19 Andrew Clement CLA 2014-03-05 11:56:02 EST
Just chipping in that I've hit this problem in a project of mine this week. The codebase is primarily for use on 1.6 so is compiled with 1.6 compliance. A couple of classes (that only get loaded when we're on 1.8) are interacting with 1.8 classes (LambdaMetaFactory).  Because I can't compile with compliance 1.6 against a 1.8 JRE (I get all the problems with default methods being reported) I'm going to have to do some nasty rework to split my project into 1.6 and 1.8 pieces ... no fun.

Am I right in thinking there were versions of ECJ BETA_JAVA8 where this worked? So my alternative is to go back to one of those ... nasty.
Comment 20 Stephan Herrmann CLA 2014-03-05 13:28:14 EST
(In reply to Andrew Clement from comment #19)
> Just chipping in that I've hit this problem in a project of mine this week.
> The codebase is primarily for use on 1.6 so is compiled with 1.6 compliance.
> A couple of classes (that only get loaded when we're on 1.8) are interacting
> with 1.8 classes (LambdaMetaFactory).  Because I can't compile with
> compliance 1.6 against a 1.8 JRE (I get all the problems with default
> methods being reported) 

Just a quick idea: if it's a small number of classes from JRE 8 you are referencing: couldn't you compile against JRE 6 + selective jars from JRE 8, configured such that JRE 8 is used only as a fallback for classes missing in JRE 6?

> I'm going to have to do some nasty rework to split
> my project into 1.6 and 1.8 pieces ... no fun.

That would probably be the orthodox solution ;-P   A main module that compiles and runs against JRE 6 plus an optional fragment that adds more capabilities, if enabled.
You're probably aware of the dangers that any API calls could cause linking errors at runtime, when compiling against JRE 8 and running on JRE 6. These dangers would be reduced by the main + fragment approach :)

> Am I right in thinking there were versions of ECJ BETA_JAVA8 where this
> worked? So my alternative is to go back to one of those ... nasty.

I can't recall such version, sorry.
Comment 21 Andrew Clement CLA 2014-03-05 20:31:05 EST
The bug fix I was thinking of that was dealt with previously was: https://bugs.eclipse.org/409473 "JDT cannot compile against JRE 1.8"

I think I was considering that also covering default methods in some way, guess not.

Yes I can mess around with my project configuration but basically as it stands:
- my code builds fine with javac, my gradle build is perfectly happy, my build system is perfectly happy
- but I can't use eclipse to work on my codebase...

(my code is like Markus' comment #15)
Comment 22 Timo Kinnunen CLA 2014-03-06 07:50:42 EST
(In reply to comment #21)
> Yes I can mess around with my project configuration but basically as it stands:
> - my code builds fine with javac, my gradle build is perfectly happy, my build
> system is perfectly happy
> - but I can't use eclipse to work on my codebase...
> 
> (my code is like Markus' comment #15)

Could the patch from Attachment 240135 [details] on bug 428203 be useful here? Provided as a checkbox option somewhere, perhaps?
Comment 23 Markus Keller CLA 2014-03-20 10:23:27 EDT
*** Bug 430793 has been marked as a duplicate of this bug. ***
Comment 24 Max Rydahl Andersen CLA 2014-03-26 08:08:09 EDT
shouldn't this have a higher priority ?

one of the forces of Eclipse JDT have always been that it supports crosscompilation.
Comment 25 Markus Keller CLA 2014-03-26 12:24:07 EDT
Yes, we should try to improve this for Luna M7.

Although it's technically wrong to compile against a JRE that has a higher version than the source level, we know that many users are willing to accept some problems this can entail (but not the current complete blockage).

(In reply to Stephan Herrmann from comment #17)
> So, ecj -1.7 must understand default methods, I don't see a way around that,
> because the same method must behave like an interface method (for clients)
> and like a concrete method (for implementors).

Yes. Stephan, can you take this up for M7? If not, please assign to someone else.
Comment 26 Stephan Herrmann CLA 2014-03-27 07:08:27 EDT
> (In reply to Stephan Herrmann from comment #17)
> > So, ecj -1.7 must understand default methods, I don't see a way around that,
> > because the same method must behave like an interface method (for clients)
> > and like a concrete method (for implementors).
> 
> Yes. Stephan, can you take this up for M7? If not, please assign to someone
> else.

I'll try to find some time within 2 weeks from now.
Comment 27 Thomas Watson CLA 2014-03-27 08:55:55 EDT
(In reply to Srikanth Sankaran from comment #16)
> I don't know that it was unusable before this cluster of bugs was fixed -
> what
> we do know that as soon as some of the eclipse projects entered into this
> mode
> (IIRC Equinox) they were hit with this and there were a series of 10-12
> problems
> that needed to be attended to.

Srikanth is correct.  Equinox had issues when attempting to compile against Java 5 class libraries but using the jsr14 compile flag.  Our motivation for using that setup was so that we could use the generic APIs (e.g. Map<?,?>) from java SE 5 in our code while still being able to produce classes that could run on java 4.  I remember that it was a non-trivial task for ecj to continue to support that setup.  This is also a setup that I no longer use BTW.  This is because as of javac7 the jsr14 compiler option is no longer supported.  OSGi felt it as no longer reasonable to continue to ship jsr14 compiled class files.  As such Equionox no longer supports Java SE 4 based VMs (i.e. Java ME)

That in itself was a sort of corner case (although important to OSGi since we still wanted to support Java ME).  Now moving up to Java 8.  I'm not sure I see a great usecase for compiling against Java 8 class libraries while targeting < Java 8.
Comment 28 Markus Keller CLA 2014-03-27 15:19:27 EDT
(In reply to Thomas Watson from comment #27)
> (In reply to Srikanth Sankaran from comment #16)
> Srikanth is correct.  Equinox had issues when attempting to compile against
> Java 5 class libraries but using the jsr14 compile flag.

OK, I agree the unofficial jsr14 flag had problems, but that's not the equivalent of this bug.

I think we behaved reasonably well if you just used compliance 1.4 and an rt.jar from 1.5. That's the situation users run into when they check out a 1.5/1.6/1.7 project and just want to get something running.
Comment 29 Srikanth Sankaran CLA 2014-03-27 23:19:05 EDT
(In reply to Markus Keller from comment #28)

> I think we behaved reasonably well if you just used compliance 1.4 and an
> rt.jar from 1.5. That's the situation users run into when they check out a
> 1.5/1.6/1.7 project and just want to get something running.

The following has no bearing on the priority with which this bug should be
fixed, but it is worth pointing out that until the cluster of bugs fixed
in the context of jsr14 were fixed, the compiler was not even internalizing
the type parameters of a class from 1.5+ binaries/projects if project compliance 
was 1.4-. That would mean many things could not have behaved reasonably well
for a certain cross section of applications (basically those that subtype a
1.5+ generic class) while for applications that merely used the 1.5 classes
things would have worked fine since erasure guarantees that.
Comment 30 Antti Haapala CLA 2014-03-30 08:38:22 EDT
I was hit by this too, I was using java-8-oracle to compile for
Comment 31 Stephan Herrmann CLA 2014-03-30 08:48:18 EDT
(In reply to Missing name from comment #30)
> I was hit by this too, I was using java-8-oracle to compile for

I have it on my agenda for M7, but still I'm puzzled why so many people insist in using this broken/incorrect/unsupported configuration.
Comment 32 Chris Hubick CLA 2014-03-30 12:29:25 EDT
(In reply to Stephan Herrmann from comment #31)
> I'm puzzled why so many people insist in using this broken/incorrect/unsupported configuration.

Each of my projects needs to target the environment running on it's server until that gets upgraded, and having a bunch of JDK's installed on my workstation is a PITA.  And, due to the presence of options for 'source' and 'target' versions in the compiler, there is an expectation of being able to use a newer JDK to build code targeting an older environment, else, why have those options at all?
Comment 33 Stephan Herrmann CLA 2014-03-30 13:30:35 EDT
(In reply to Chris Hubick from comment #32)
> (In reply to Stephan Herrmann from comment #31)
> > I'm puzzled why so many people insist in using this broken/incorrect/unsupported configuration.
> 
> Each of my projects needs to target the environment running on it's server
> until that gets upgraded, and having a bunch of JDK's installed on my
> workstation is a PITA. 

I don't know what should be particularly painful about that. :)
If you are professionally developing projects targeting different JREs you *should* have each of these installed (JRE suffices, no need to have the full JDK). How else do you ensure you are not using API that doesn't even exist in your target environment?

> And, due to the presence of options for 'source' and
> 'target' versions in the compiler, there is an expectation of being able to
> use a newer JDK to build code targeting an older environment, else, why have
> those options at all?

So that's what raises the expectation? But wait: that dialog doesn't even allow you to enter any of the bogus combinations of source/target/compliance levels. Mh...
Comment 34 Chris Hubick CLA 2014-03-30 14:18:52 EDT
(In reply to Stephan Herrmann from comment #33)
> How else do you ensure you are not using API that doesn't even
> exist in your target environment?

The existing projects are normally just in maintenance mode with minimal such changes.  Or my brain remembers what is new API.  Or the Javadoc tells me.  Or the build server goes red :)  And if I'm going to make significant changes to a project, I'll likely then also upgrade it to current JDK at that time.


> > And, due to the presence of options for 'source' and
> > 'target' versions in the compiler, there is an expectation of being able to
> > use a newer JDK to build code targeting an older environment, else, why have
> > those options at all?
> 
> So that's what raises the expectation? But wait: that dialog doesn't even
> allow you to enter any of the bogus combinations of source/target/compliance
> levels. Mh...

Everything I do is Maven/m2e, so I'm not really twiddling any dialogs.  Most existing projects have source/target 1.7/1.7 in their pom.xml, untouched from before I upgraded to 1.8.

When I upgrade my workstation JDK to 1.8, and Eclipse to a 1.8 version, in an ideal world, I could just start up Eclipse and create a new 1.8 project (which I can), but all my old 1.7 projects would continue to build and display "green", just as they did before.  But, oh no, forget that, thanks to this bug, everything that used to be happy then exploded in a sea of red errors after I upgraded.

Previous upgrades resulted in little things like use of "enum" keywords, and maybe warnings for things like missing type parameters on collections.  You could easily fix those errors or ignore/remove those warnings.  But this, this is more of a show-stopper "it won't even build" scenario, with no easy workaround.

So, yeah, because of this bug, I had to go back and reinstall a 1.7 JDK on my system.  As a matter of good stewardship, when a new JDK is released, I like to at least grab it and immediately make whatever little fixes might be required on my projects to ensure they are at least buildable/runnable using that newer JDK, even if they are still primarily targeting the old one.  After having done that, it also just *feels* messy still needing the old one installed myself.
Comment 35 Stephan Herrmann CLA 2014-03-30 14:38:31 EDT
(In reply to Chris Hubick from comment #34)
> But, oh no, forget that, thanks
> to this bug, everything that used to be happy then exploded in a sea of red
> errors after I upgraded.

The reason for the errors you see is not in eclipse but because you removed the JRE 7 which *is* necessary to do 1.7 development in a controlled way.

None of what you are saying improves my motivation to invest my spare time for this bug.
Comment 36 Chris Hubick CLA 2014-03-30 15:00:01 EDT
(In reply to Stephan Herrmann from comment #35)
> The reason for the errors you see is not in eclipse but because you removed
> the JRE 7 which *is* necessary to do 1.7 development in a controlled way.

Then I'm back to not understanding why there are "source" and "target" options anywhere.

> None of what you are saying improves my motivation to invest my spare time
> for this bug.

I just CC'd to find out if I might be able to remove 1.7 before I'm done upgrading all my projects, and thought I might be able to respond to your query by communicating that some of us are used to being able to upgrade our JDK and have all our old projects still build without modifications.

Frankly, "suck it up" is a perfectly valid answer, as I don't think I'd invest *my* spare time in fixing this either ;)
Comment 37 Timo Kinnunen CLA 2014-03-31 06:10:13 EDT
This is such a pain when working with Eclipse plugins. Where am I going to find a CDC-1.1/Foundation-1.1 JRE?
Comment 38 David Williams CLA 2014-03-31 08:03:28 EDT
(In reply to Timo Kinnunen from comment #37)
> This is such a pain when working with Eclipse plugins. Where am I going to
> find a CDC-1.1/Foundation-1.1 JRE?

Does bug 386649 have what you need? 

Does installing "API Tools Execution Environment Descriptions" help?
Comment 39 Stephan Herrmann CLA 2014-03-31 10:00:02 EDT
(In reply to Timo Kinnunen from comment #37)
> This is such a pain when working with Eclipse plugins. Where am I going to
> find a CDC-1.1/Foundation-1.1 JRE?

Wait a minute: this bug is about mixing 1.7 with 1.8. Why would you need a  CDC-1.1/Foundation-1.1 JRE?
Comment 40 Markus Keller CLA 2014-03-31 10:36:37 EDT
(In reply to Stephan Herrmann from comment #39)
> Wait a minute: this bug is about mixing 1.7 with 1.8. Why would you need a 
> CDC-1.1/Foundation-1.1 JRE?

This bug is actually about compiling pre-1.8 projects against a 1.8 JRE.

E.g. org.eclipse.core.jobs is still at CDC-1.1/Foundation-1.1 and should also compile with a Java 8 JRE.
Comment 41 Stephan Herrmann CLA 2014-04-03 09:43:00 EDT
Given we are not bound by any specification in this issue, a solution is actually simple, as mentioned before: remove one check for sourceLevel >= JDK_1_8 and let the compiler at 1.7- handle binary default methods just like a 1.8 compiler would.

I only found one more error message which should be suppressed at 1.7- (duplicate default methods).

In comparison with javac I only see one difference: we are *not* copying the weird behavior discussed in comment 6 (which as I understand is an artifact of their implementation strategy for method lookup).

I could also compile all JDT/Debug sources against JRE8.

Released for 4.4 M7 via http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=8477130b01f048bfc1fd1f484a88902aeefb2f38


Please test whether this is what people are expecting.

In particular we might accept programs that can produce problems at runtime (unimplemented method, or method not found), but since we are explicitly disabling strict compatibility checks, we cannot perform some strict compatibility checks :)

IOW: when using this setup, users have to know exactly what they are doing! IOW: my recommendation to avoid this setup if humanly possible still holds.


Final question: do we want to add a warning when reading a class file with higher version than the current target? Might just be a question of where exactly to report (don't want to have myriads of duplicate warnings, but still want a hint at were it occurred).
Comment 42 Stephan Herrmann CLA 2014-04-03 09:51:03 EDT
See also bug 431911.
Comment 43 Timo Kinnunen CLA 2014-04-03 18:21:26 EDT
Looks promising so far, thanks! Although I'm hesitant to unravel my 1.2-1.5 JRE workarounds just yet at least a quick try on a CDC-1.1/Foundation-1.1 project has it building without errors. As long as it's possible to run it on a 1.8 JRE successfully that's good enough. Not that I plan on doing that, the priority has always been a Challenges view free of compile errors.
Comment 44 David Williams CLA 2014-04-05 00:37:03 EDT
> IOW: when using this setup, users have to know exactly what they are doing!
> IOW: my recommendation to avoid this setup if humanly possible still holds.

Yeah, I think that's always been our advice ... and you can see how well people follow it :) 

> Final question: do we want to add a warning when reading a class file with
> higher version than the current target? Might just be a question of where
> exactly to report (don't want to have myriads of duplicate warnings, but
> still want a hint at were it occurred).

I don't think we currently do, do we? But seems like a good idea ... maybe "once per project" (if "you" have knowledge of project, or else package?, and perhaps UI could filter further so "one warning per project"? [Keep in mind, I barely know what you are talking about :) ] But, seems worth opening "enhancement request" and could be discussed there in detail, and implemented later after getting some experience?

MY question is ... different topic ... this fix is currently in N-builds, right? and the first "versioned" one will be in next Tuesday's I-build (4/8). Would it be a good idea to move up to that version as the compiler we use in our production builds (before M7) just so it gets a little more exercise? Or, should I wait another week or so? 
[Our M7 stabilization week begins 4/27, so we have about 3 weeks left -- my main concern is I do think this should be used at least a week or two before M7, just to make sure we don't have any surprises in our delivered M7 compiler.] 

Thanks,
Comment 45 Dani Megert CLA 2014-04-07 03:39:31 EDT
(In reply to David Williams from comment #44)
> MY question is ... different topic ... this fix is currently in N-builds,
> right? and the first "versioned" one will be in next Tuesday's I-build
> (4/8). Would it be a good idea to move up to that version as the compiler we
> use in our production builds (before M7) just so it gets a little more
> exercise?

Yes.
Comment 46 Paul Elder CLA 2014-04-08 09:45:23 EDT
*** Bug 432205 has been marked as a duplicate of this bug. ***
Comment 47 David Williams CLA 2014-04-08 23:17:14 EDT
(In reply to Dani Megert from comment #45)
> (In reply to David Williams from comment #44)
> > MY question is ... different topic ... this fix is currently in N-builds,
> > right? and the first "versioned" one will be in next Tuesday's I-build
> > (4/8). Would it be a good idea to move up to that version as the compiler we
> > use in our production builds (before M7) just so it gets a little more
> > exercise?
> 
> Yes.

I'll document details in bug 432382. But will plan on updating for Wednesday night's N-build ... unless someone says otherwise (such as after seeing unit tests, or something).
Comment 48 Stephan Herrmann CLA 2014-04-12 08:12:40 EDT
*** Bug 432087 has been marked as a duplicate of this bug. ***
Comment 49 Jay Arthanareeswaran CLA 2014-04-17 02:44:59 EDT
Manju mentioned a somewhat similar issue when a static/default interface methods are accessed from a 1.7 project through eclipse project dependency. Has this already been discussed somewhere? In this case, the error messages seem to be either wrong or inconsistent.
Comment 50 Dani Megert CLA 2014-04-17 02:49:00 EDT
(In reply to Jayaprakash Arthanareeswaran from comment #49)
> Manju mentioned a somewhat similar issue when a static/default interface
> methods are accessed from a 1.7 project through eclipse project dependency.
> Has this already been discussed somewhere? In this case, the error messages
> seem to be either wrong or inconsistent.

Do we have a bug for that?
Comment 51 Stephan Herrmann CLA 2014-04-17 06:25:36 EDT
The change in this bug is not specific to the JRE, so I'd expect things to be consistent with project dependencies. Example?
Comment 52 Jay Arthanareeswaran CLA 2014-04-17 06:33:49 EDT
(In reply to Stephan Herrmann from comment #51)
> The change in this bug is not specific to the JRE, so I'd expect things to
> be consistent with project dependencies. Example?

A 1.8 project has this:

public interface I8 {

	static String staticM1(File f){
		return "static";
	}
	default String defaultM2(File f){
		return "static";
	}
}

And a 1.7 level project has the other project in it's build path and has this:

import p1.I8;
public class A7 {
	static String foo(I8 i, File f) {
		I8.staticM1(f); // Reported [1]
		i.defaultM2(f); // Not reported [2]
		return "instance";
	}
	public static void main(String[] args) {
		foo(new I8(){}, new File("c:\\temp\temp.txt"));
	}
}

[1] says static methods should be invoked in a static way. When this line is commented out and run, the default method invocation results in a run time error. (class version mismatch)
Comment 53 Stephan Herrmann CLA 2014-04-17 06:54:54 EDT
(In reply to Jayaprakash Arthanareeswaran from comment #52)
> [1] says static methods should be invoked in a static way.

While this is not the optimal error, I don't see how a static interface method could ever be called from a 1.7 project. I don't think we should invest in this case.

> When this line is
> commented out and run, the default method invocation results in a run time
> error. (class version mismatch)

Are you seeing this? :

"Exception in thread "main" java.lang.UnsupportedClassVersionError: p1/I8 : Unsupported major.minor version 52.0"


This is exactly what I expect when trying to run 52.0 files on a JVM7. On JVM8 the program executes flawlessly.

=> What behavior *are* you expecting?
Comment 54 Jay Arthanareeswaran CLA 2014-04-17 06:59:40 EDT
(In reply to Stephan Herrmann from comment #53)
> (In reply to Jayaprakash Arthanareeswaran from comment #52)
> > [1] says static methods should be invoked in a static way.
> 
> While this is not the optimal error, I don't see how a static interface
> method could ever be called from a 1.7 project. I don't think we should
> invest in this case.

This was my only concern. Although, I agree for such a wrong set up, it's not worth spending time.

Manju, what do you think?


> This is exactly what I expect when trying to run 52.0 files on a JVM7. On
> JVM8 the program executes flawlessly.
> 
> => What behavior *are* you expecting?

I am fine with this, actually.
Comment 55 Markus Keller CLA 2014-04-17 08:35:32 EDT
I agree with Stephan an Jay that we don't have to do anything special for the case where 1.7 code refers to 1.8 features.

The goal of this bug was to support compilation of 1.7 code against dependencies that declare 1.8 features, but not to support actual usage of those 1.8 features. In that case, the depending 1.7 project should be bumped to 1.8. If it can't do that, then it simply can't use the new APIs. That's totally OK, since there's no way to run a 1.8 class file on a 1.7 VM anyway.
Comment 56 Dani Megert CLA 2014-04-17 11:17:32 EDT
(In reply to Markus Keller from comment #55)
> I agree with Stephan an Jay that we don't have to do anything special for
> the case where 1.7 code refers to 1.8 features.
> 
> The goal of this bug was to support compilation of 1.7 code against
> dependencies that declare 1.8 features, but not to support actual usage of
> those 1.8 features. In that case, the depending 1.7 project should be bumped
> to 1.8. If it can't do that, then it simply can't use the new APIs. That's
> totally OK, since there's no way to run a 1.8 class file on a 1.7 VM anyway.

+1.
Comment 57 shankha banerjee CLA 2014-04-29 10:03:15 EDT
Tested out the examples mentioned in Comment 1 and Comment 15.
The examples compiled without any errors.

Verified for 4.4 M7 using I20140427-2030 build.
Comment 58 Philippe Marschall CLA 2014-07-17 05:56:47 EDT
(In reply to Stephan Herrmann from comment #53)
> (In reply to Jayaprakash Arthanareeswaran from comment #52)
> > [1] says static methods should be invoked in a static way.
> 
> While this is not the optimal error, I don't see how a static interface
> method could ever be called from a 1.7 project. I don't think we should
> invest in this case.

Streams API is an example. Several factory methods are on java.util.stream.Stream and the classes they instantiate are package protected.
Comment 59 Stephan Herrmann CLA 2014-07-17 06:01:41 EDT
(In reply to Philippe Marschall from comment #58)
> (In reply to Stephan Herrmann from comment #53)
> > (In reply to Jayaprakash Arthanareeswaran from comment #52)
> > > [1] says static methods should be invoked in a static way.
> > 
> > While this is not the optimal error, I don't see how a static interface
> > method could ever be called from a 1.7 project. I don't think we should
> > invest in this case.
> 
> Streams API is an example. Several factory methods are on
> java.util.stream.Stream and the classes they instantiate are package
> protected.

This doesn't justify that a 1.7 project should be allowed to access any of this Java 8 specific stuff. If you depend on API from JRE8, you need a 1.8 compiler.
Comment 60 Philippe Marschall CLA 2014-07-17 06:58:14 EDT
(In reply to Stephan Herrmann from comment #59)
> (In reply to Philippe Marschall from comment #58)
> > (In reply to Stephan Herrmann from comment #53)
> > Streams API is an example. Several factory methods are on
> > java.util.stream.Stream and the classes they instantiate are package
> > protected.
> 
> This doesn't justify that a 1.7 project should be allowed to access any of
> this Java 8 specific stuff. If you depend on API from JRE8, you need a 1.8
> compiler.

We use a JRE8 and a 1.8 compiler but we keep the source level at 1.7 because various tools are not ready yet.
Comment 61 Srikanth Sankaran CLA 2014-07-17 23:22:02 EDT
(In reply to Philippe Marschall from comment #60)

> We use a JRE8 and a 1.8 compiler but we keep the source level at 1.7 because
> various tools are not ready yet.

In comment#59, Stephan means a 1.8 source level project, not merely a compiler
that *can* compile 1.8