Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use functional interface as a SAM target type #11361

Closed
retronym opened this issue Jul 7, 2016 · 1 comment
Closed

Unable to use functional interface as a SAM target type #11361

retronym opened this issue Jul 7, 2016 · 1 comment
Assignees
Milestone

Comments

@retronym
Copy link
Member

retronym commented Jul 7, 2016

% cat sandbox/Test.java && javac -d . sandbox/Test.java && dbg qscala -Ydebug -Ytyper-debug -nc -Xexperimental -e 'val x: p1.Test[AnyRef, AnyRef] = {(a: Object, b: Object) => new Object}; x.invokeWithHeaders("x", "y")'
package p1;

@FunctionalInterface
public interface Test<Request, Response> {
    Object invokeWithHeaders(Object var1, Object var2);

    default Object invoke(Object var1) {
        throw new UnsupportedOperationException("ServerServiceCalls should be invoked by using the invokeWithHeaders method.");
    }

    default <Request, Response> Object of(Object var0) {
        return var0;
    }
}
Listening for transport dt_socket at address: 5006
[running phase parser on scalacmd4317652137244637493.scala]
[running phase namer on scalacmd4317652137244637493.scala]
[running phase packageobjects on scalacmd4317652137244637493.scala]
[running phase typer on scalacmd4317652137244637493.scala]
...
|    |    |    |    |    |-- p1.this.Test[scala.this.AnyRef,scala.this.AnyRef] BYVALmode-EXPRmode (site: anonymous class $anon, a ClassSymbol with flags final)
|    |    |    |    |    |    |-- p1.this.Test[Request,Response] TYPEmode (site: value x  in $anon, a TermSymbol with flags private[this])
|    |    |    |    |    |    |    |-- AnyRef TYPEmode (site: value x  in $anon, a TermSymbol with flags private[this])
|    |    |    |    |    |    |    |    [adapt] scala.this.AnyRef is now a TypeTree(scala.this.AnyRef)
|    |    |    |    |    |    |    |    \-> scala.this.AnyRef
|    |    |    |    |    |    |    |-- AnyRef TYPEmode (site: value x  in $anon, a TermSymbol with flags private[this])
|    |    |    |    |    |    |    |    [adapt] scala.this.AnyRef is now a TypeTree(scala.this.AnyRef)
|    |    |    |    |    |    |    |    \-> scala.this.AnyRef
|    |    |    |    |    |    |    \-> p1.this.Test[scala.this.AnyRef,scala.this.AnyRef]
|    |    |    |    |    |    |-- ((a: Object, b: Object) => new Object.<init>()) : pt=p1.this.Test[scala.this.AnyRef,scala.this.AnyRef] BYVALmode-EXPRmode (site: value x  in $anon, a TermSymbol with flags private[this])
|    |    |    |    |    |    |    |-- Object TYPEmode (site: value a in $anon, a TermSymbol with flags <param> <locked>)
|    |    |    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    |    |    |-- Object TYPEmode (site: value a in $anon, a TermSymbol with flags <param>)
|    |    |    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    |    |    |-- Object TYPEmode (site: value b in $anon, a TermSymbol with flags <param> <locked>)
|    |    |    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    |    |    |-- Object TYPEmode (site: value b in $anon, a TermSymbol with flags <param>)
|    |    |    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    |    |    |-- new Object.<init>() : pt=lang.this.Object EXPRmode (site: value $anonfun in $anon, a TermSymbol with flags <synthetic>)
|    |    |    |    |    |    |    |    |-- new Object.<init> BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value $anonfun in $anon, a TermSymbol with flags <synthetic>)
|    |    |    |    |    |    |    |    |    |-- new Object EXPRmode-POLYmode-QUALmode (silent: value $anonfun in $anon, a TermSymbol with flags <synthetic>)
|    |    |    |    |    |    |    |    |    |    |-- Object FUNmode-TYPEmode (silent: value $anonfun in $anon, a TermSymbol with flags <synthetic>)
|    |    |    |    |    |    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    |    |    |    |    \-> ()lang.this.Object
|    |    |    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    |    |    solving for (Request: ?Request, Response: ?Response)
|    |    |    |    |    |    |    solving for (Request: ?Request, Response: ?Response)
|    |    |    |    |    |    |    solving for (Request: ?Request, Response: ?Response)
|    |    |    |    |    |    |    solving for (Request: ?Request, Response: ?Response)
      def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && {
        val samMethType = pt memberInfo sam
        fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType)
//      |                 |
//      |                 `--scala.this.Function2[scala.this.Any,scala.this.Any,lang.this.Object]
//      |
//      `---- scala.this.Function2[lang.this.Object,lang.this.Object,lang.this.Object]
      }

We miss out on the loose matching between Object and Any that usually occurs with Java method types:

  /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */
  protected[internal] def matchingParams(syms1: List[Symbol], syms2: List[Symbol], syms1isJava: Boolean, syms2isJava: Boolean): Boolean = syms1 match {
    case Nil =>
      syms2.isEmpty
    case sym1 :: rest1 =>
      syms2 match {
        case Nil =>
          false
        case sym2 :: rest2 =>
          val tp1 = sym1.tpe
          val tp2 = sym2.tpe
          (tp1 =:= tp2 ||
           syms1isJava && tp2.typeSymbol == ObjectClass && tp1.typeSymbol == AnyClass ||
           syms2isJava && tp1.typeSymbol == ObjectClass && tp2.typeSymbol == AnyClass) &&
          matchingParams(rest1, rest2, syms1isJava, syms2isJava)
      }
  }

Which leads to:

|    |    |    |    |    |    |    [search #1] start `<notype>`, searching for adaptation to pt=scala.this.Function1[scala.this.Function2[lang.this.Object,lang.this.Object,lang.this.Object],p1.this.Test[scala.this.AnyRef,scala.this.AnyRef]] (silent: value x  in $anon, a TermSymbol with flags private[this]) implicits disabled
|    |    |    |    |    |    |    [search #2] start `<notype>`, searching for adaptation to pt=scala.this.Function1[scala.this.<byname>[scala.this.Function2[lang.this.Object,lang.this.Object,lang.this.Object]],p1.this.Test[scala.this.AnyRef,scala.this.AnyRef]] (silent: value x  in $anon, a TermSymbol with flags private[this]) implicits disabled
/var/folders/b7/xcc2k0ln6ldcv247ffpy2d1w0000gp/T/scalacmd4317652137244637493.scala:1: error: type mismatch;
 found   : scala.this.Function2[lang.this.Object,lang.this.Object,lang.this.Object]
 required: p1.this.Test[scala.this.AnyRef,scala.this.AnyRef]
val x: p1.Test[AnyRef, AnyRef] = {(a: Object, b: Object) => new Object}; x.invokeWithHeaders("x", "y")
                                                         ^
|    |    |    |    |    |    |    \-> <error>
|    |    |    |    |    |    \-> [val x ] p1.this.Test[scala.this.AnyRef,scala.this.AnyRef]
|    |    |    |    |    |-- def x BYVALmode-EXPRmode (site: anonymous class $anon, a ClassSymbol with flags final)
|    |    |    |    |    |    |-- p1.this.Test[scala.this.AnyRef,scala.this.AnyRef] : pt=p1.this.Test[scala.this.AnyRef,scala.this.AnyRef] EXPRmode (site: getter x in $anon, a MethodSymbol with flags <method> <stable> <accessor> <trans_flag> private)
|    |    |    |    |    |    |    |-- scala.this.AnyRef{} EXPRmode-POLYmode-QUALmode (site: getter x in $anon, a MethodSymbol with flags <method> <stable> <accessor> <trans_flag> private)
|    |    |    |    |    |    |    |    \-> Main.$anon.type (with underlying type scala.this.AnyRef{})
|    |    |    |    |    |    |    \-> p1.this.Test[scala.this.AnyRef,scala.this.AnyRef]
|    |    |    |    |    |    \-> [def x] => p1.this.Test[scala.this.AnyRef,scala.this.AnyRef]
|    |    |    |    |    |-- x.invokeWithHeaders("x", "y") BYVALmode-EXPRmode (site: value <local $anon> in $anon, a TermSymbol)
|    |    |    |    |    |    |-- x.invokeWithHeaders BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local $anon> in $anon, a TermSymbol)
|    |    |    |    |    |    |    |-- x EXPRmode-POLYmode-QUALmode (silent: value <local $anon> in $anon, a TermSymbol)
|    |    |    |    |    |    |    |    \-> $anon.this.x.type (with underlying type p1.this.Test[scala.this.AnyRef,scala.this.AnyRef])
|    |    |    |    |    |    |    \-> (<param> <synthetic> x$1: scala.this.Any, <param> <synthetic> x$2: scala.this.Any)lang.this.Object
|    |    |    |    |    |    |-- "x" : pt=scala.this.Any BYVALmode-EXPRmode (silent: value <local $anon> in $anon, a TermSymbol)
|    |    |    |    |    |    |    \-> lang.this.String("x")
|    |    |    |    |    |    |-- "y" : pt=scala.this.Any BYVALmode-EXPRmode (silent: value <local $anon> in $anon, a TermSymbol)
|    |    |    |    |    |    |    \-> lang.this.String("y")
|    |    |    |    |    |    \-> lang.this.Object
|    |    |    |    |    \-> [class $anon] scala.this.AnyRef{}
|    |    |    |    |-- new $anon.<init>() : pt=scala.this.Unit EXPRmode (site: method main in Main, a MethodSymbol with flags <method>)
|    |    |    |    |    |-- new $anon.<init> BYVALmode-EXPRmode-FUNmode-POLYmode (silent: method main in Main, a MethodSymbol with flags <method>)
|    |    |    |    |    |    |-- new $anon EXPRmode-POLYmode-QUALmode (silent: method main in Main, a MethodSymbol with flags <method>)
|    |    |    |    |    |    |    |-- $anon FUNmode-TYPEmode (silent: method main in Main, a MethodSymbol with flags <method>)
|    |    |    |    |    |    |    |    \-> scala.this.AnyRef{}
|    |    |    |    |    |    |    \-> scala.this.AnyRef{}
|    |    |    |    |    |    \-> ()scala.this.AnyRef{}
|    |    |    |    |    |-- { new $anon.<init>(); () } : pt=scala.this.Unit EXPRmode (site: method main in Main, a MethodSymbol with flags <method>)
|    |    |    |    |    |    |-- () : pt=scala.this.Unit EXPRmode (site: method main in Main, a MethodSymbol with flags <method>)
|    |    |    |    |    |    |    \-> scala.this.Unit
|    |    |    |    |    |    \-> scala.this.Unit
|    |    |    |    |    [adapt] ()scala.this.AnyRef{} adapted to { new $anon.<init>(); () } based on pt scala.this.Unit
|    |    |    |    |    \-> scala.this.Unit
|    |    |    |    \-> scala.this.Unit
|    |    |    \-> [def main] (<param> args: scala.this.Array[String])scala.this.Unit
|    |    \-> [object Main] <empty>.this.Main.type
|    \-> [package <empty>] type
@retronym retronym changed the title Unable to use functional interface as a SAM Unable to use functional interface as a SAM target type Jul 7, 2016
@adriaanm adriaanm transferred this issue from scala/scala-dev Jan 16, 2019
@adriaanm adriaanm added this to the 2.13.1 milestone Jan 16, 2019
@adriaanm adriaanm modified the milestones: 2.13.1, 2.13.0-RC2 May 15, 2019
@adriaanm
Copy link
Contributor

fixed by scala/scala#7966

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants