Skip to content

Inner traits are sometimes thought to be SAM types when they aren't #492

Closed
@smarter

Description

@smarter

The following code:

class Outer {
  trait Inner {
    def apply(x: Int): Int
  }
  val f: Inner = x => x
}

Becomes after frontend:

package <empty> {
  class Outer() extends Object() { 
    <trait> trait Inner() extends Object { 
      def apply(x: Int): Int
    }
    val f: Outer.this.Inner = {
      def $anonfun(x: Int): Int = x
      closure($anonfun:Outer.this.Inner)
    }
  }

So far so good: Inner is a SAM type (also known as a Functional Interface) so it can be the result of a closure, now watch what happens after ExplicitOuter:

class Outer() extends Object() { 
  <trait> trait Inner() extends Object() { 
    def apply(x: Int): Int
    final def Outer$Inner$$$outer: Outer
  }
...

Oh no! Inner now has two abstract methods, it's no longer a SAM type, and that's terrible.

I'm not sure but I think that there are two things wrong here:

  • The SAMType extractor should not consider traits that may get an outer pointer as SAM types, not sure what the best way to ensure that is.
  • In this example, it seems like Inner should have the flag PureInterface set, that way it won't get an outer pointer. I investigated a bit: NormalizeFlags should set PureInterface if NoInits is set but that flag isn't set on Inner either. Namer#denotsNamed should set NoInits, but it doesn't because TreeInfo#isNoInitMember returns false when called on the apply DefDef. That's where I stopped.

@odersky : Could you take a look at this and advise on the best way to fix it?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions