Skip to content

Regression: match-error in JVM backend for val a: C.type = C after export I.* #26983

Description

@ahjohannessen

Compiler version

3.9.0, and still present in 3.10.0-RC1.

Bisected against the release artifacts:

version result
3.8.4 ok
3.9.0-RC1 … RC4 ok
3.9.0-RC5 crash
3.9.0-RC6, 3.9.0 crash
3.10.0-RC1 crash

Minimized code

object O:
  object I:
    object C
  export I.*
  val a: C.type = C

Output

Error while emitting O$
( (of class java.lang.Character)
1 error found

(No stack trace is printed, so the MatchError scrutinee — the char ( — is all the
diagnostic there is.)

Expectation

It should compile, as it does on 3.8.4.

Notes

The crash needs two ingredients: an export, and a val whose declared type is the
singleton type of the exported alias. It goes away if either one is removed:

object O:
  object I:
    object C
  export I.*
  val a: I.C.type = C     // ok — type written through the original, not the alias
object O:
  object I:
    object C
  export I.*
  val a = C               // ok — inferred type
object O:
  object I:
    object C
  export I.*
  def a: C.type = C       // ok — def instead of val

Variations that all still crash, so the trigger is narrow but not exotic:

  • case class C(x: Int) or an enum case instead of object C
  • a plain term: object I { val x: Int = 1 }; export I.*; val a: x.type = x
  • targeted export (export I.C) as well as wildcard
  • top level rather than inside an object (crashes as Error while emitting G$package$)
  • the exporting object being top-level rather than nested

-Xno-generic-signatures makes it compile, which places the failure in generic-signature
handling rather than in descriptor emission.

A likely mechanism: export I.* emits a forwarder method C() (confirmed with javap
on a build using -Xno-generic-signatures):

public final O$E$C$ C();
public O$E$C$ a();

so C.type in val a: C.type is the singleton type of a method reference, and the
generic signature computed for the field appears to come out as (or to contain) a method
descriptor — hence a ( reaching something that expects a reference type signature.

This looks like the same failure family as #26786 ("Match-error in JVM backend when emitting
HK-type with type lambda containing primitive"), which reported
Z (of class java.lang.Character) from GenericSignatureVisitor.referenceTypeSignature.
The version bisect above matches that issue's backport window (it landed in 3.9.0-RC5), so
this is plausibly the same #26632 lineage with a different malformed input. #26787 is in
3.9.0 and does not cover this case.

Found on real code during a 3.8.4 -> 3.9.0 upgrade; the shapes above are reduced from it.


Provenance: the minimization, the version bisect and the flag experiments above were done by
Claude (Claude Code), prompted by a developer who hit the crash while upgrading a real project.
Take it for what it is — but note what is and is not verified. Every row of the version table and
every ok/crash claim is an actual run of the published release artifact against the snippet as
written, reproducible in a few seconds each. The proposed mechanism, and the suggested link to
#26786 and #26632, are inference from black-box behaviour: no one read the 3.9.0 backend source,
and the compiler prints no stack trace for this crash. Discount those two paragraphs accordingly;
the reproducer and the bisect stand on their own.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    itype:bugstat:needs triageEvery issue needs to have an "area" and "itype" label

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions