scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

Default class file version is 52 not 64 #12817

Closed som-snytt closed 10 months ago

som-snytt commented 1 year ago

Reproduction steps

Scala version: 2.13.12

➜  beaners javap -private -verbose /tmp/Bean.class
Classfile /tmp/Bean.class
  Last modified Jun 30, 2023; size 1540 bytes
  SHA-256 checksum b5d8938ffe0dadfce61939c65fc49196d14cf0b8f28c40a53c15d205322b086a
  Compiled from "bean.scala"
public class Bean
  minor version: 0
  major version: 52

Problem

Will you still need me? Will you still feed me, when I'm 64?

➜  beaners skalac -release:20 -cp /tmp -d /tmp -Werror -Xlint bean.scala
➜  beaners javap -private -verbose /tmp/Bean.class
Classfile /tmp/Bean.class
  Last modified Jun 30, 2023; size 1540 bytes
  SHA-256 checksum 771bfe2b1135d50bae1d44f34860599026a5ed6dfa4e881c2896b62ec218af92
  Compiled from "bean.scala"
public class Bean
  minor version: 0
  major version: 64

Since the default value for -release is 20 under JDK 20, I would expect the class file version to reflect that.

Noticed while comparing javac output.

scalac -target defaults to 8 but follows -release except for the default case. Note that 2.12 is fixed to target 8, but I don't recall any discussion about 2.13 target defaulting to default release value (which depends on the JVM in use).

SethTisue commented 10 months ago

@som-snytt is this one you were thinking of tackling yourself...? I agree the current behavior is peculiar.

lrytz commented 10 months ago

So far scalac always emitted a fixed classfile version, no matter what JDK was used at compile-time (unless -target is set, or -release which in turn sets -target). Scala 3 does the same, for the record.

The failure mode is that compiled code can refer to JDK elements that don't exist on the JDK corresponding to the classfile version. The question is, does it make sense to turn that into a hard error now?

SethTisue commented 10 months ago

Yeah, although I described the current behavior as “peculiar”, that might not actually be sufficient justification for changing it now. One reason not to might be possible fragility around -release. For example, someone might not have JDK 8 on their system at all.

som-snytt commented 10 months ago

I'm not sure what a JDK element is.

Are -release and -target orthogonal after all? What API I can call has nothing to do with class file features which may constrain how my Scala code is compiled. Well, the reason for target to follow release is not functional but practical, as it ensures that the class is loaded by the desired JVM version.

Normally, I don't care much. Why does javac do the natural thing? Only to support Java features. But Scala does not have feature dependencies; functionally, setting the release/target is a kind of niche optimization.

OTOH, arguably, defaulting to version 52 gives the misleading impression that the code will run on JDK 8, although it was compiled against later API. Oh, that's what lrytz meant by element. It seems to me that in fact release and target should correspond, and the question is whether release should default to ambient or 8.

(A moment ago, I was prepared to close the issue, but are we to believe that no one has ever accidentally published a jar without setting release, used new API, and some unsuspecting user saw an error only later in production?) (I can believe that because release is of relatively recent vintage, and entities stuck on JDK 8 are not rebuilding code, so they would never see the bad jar.)

lrytz commented 10 months ago

I'm not sure what a JDK element is.

Sorry, I just meant API from the JDK. Lost my jargon during vacation.

the reason for target to follow release is not functional but practical

I agree

Why does javac do the natural thing?

Maybe because the compiler version is tied to the JDK? What happens if you run the Java 17 compiler on JDK 20?

question is whether release should default to ambient or 8

Yeah... trying to change this is probably not worth the risk. Say someone has been publishing a project using JDK 17 for a while, upgrades to 2.13.12, and then suddenly the artifacts no longer work for users on JDK 11.

som-snytt commented 10 months ago

Closing as a FAQ.

SethTisue commented 10 months ago

Oh... I was confused before. Yes, I'm okay with the status quo here.