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

inlining fails under separate compilation in empty package #8580

Closed
scabug opened this issue May 12, 2014 · 11 comments
Closed

inlining fails under separate compilation in empty package #8580

scabug opened this issue May 12, 2014 · 11 comments
Milestone

Comments

@scabug
Copy link

scabug commented May 12, 2014

~/code/scala tail sandbox/{a,b}.scala
==> sandbox/a.scala <==
  def isEmpty = value == null
  // can't seem to coerce the inliner into inlining calls to getOrElse
  // would be great if you know a way of achieving that!
  @inline final def getOrElse[B >: A](alt: => B): B = if (isEmpty) alt else value
  override def toString = if (isEmpty) "<empty>" else s"$value"
}

object Joint {
  def foo = new Optional("").getOrElse("!!!")
}

==> sandbox/b.scala <==
object Separate {
  def foo = new Optional("").getOrElse("!!!")
}
2.11.x ~/code/scala (set -x; qbin/scalac -optimize -Yinline-warnings sandbox/a.scala && qbin/scalac -optimize -Yinline-warnings sandbox/b.scala)
+ qbin/scalac -optimize -Yinline-warnings sandbox/a.scala
+ qbin/scalac -optimize -Yinline-warnings sandbox/b.scala
warning: Could not inline required method getOrElse$extension because bytecode unavailable.
warning: Could not inline required method getOrElse$extension because bytecode unavailable.
warning: At the end of the day, could not inline @inline-marked method getOrElse$extension
three warnings found
@scabug
Copy link
Author

scabug commented May 12, 2014

Imported From: https://issues.scala-lang.org/browse/SI-8580?orig=1
Reporter: @retronym
Affected Versions: 2.10.4, 2.11.0
See #6723
Attachments:

@scabug
Copy link
Author

scabug commented May 12, 2014

@retronym said:
See also: #6723 #7393 #7685

@scabug
Copy link
Author

scabug commented May 12, 2014

@retronym said:
This works if the class is not in the empty package.

@scabug
Copy link
Author

scabug commented May 12, 2014

@xeno-by said:
Not sure it works for me:

package foo

final class Optional[A >: Null](val value: A) extends AnyVal {
  def get: A = value
  def isEmpty = value == null
  @inline final def getOrElse[B >: A](alt: => B): B = if (isEmpty) alt else value
}
package foo

class C

object Test {
  def foo: Unit = {
    val x = new Optional(null)
    println(x.getOrElse(new C))
  }
}
18:03 ~/Projects/211x/sandbox (2.11.x)$ scalac Macros.scala && scalac Test.scala && javap -v foo.Test$
...
  public void foo();
    flags: ACC_PUBLIC
    Code:
      stack=5, locals=2, args_size=1
         0: aconst_null
         1: astore_1
         2: getstatic     #18                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
         5: getstatic     #23                 // Field foo/Optional$.MODULE$:Lfoo/Optional$;
         8: aload_1
         9: pop
        10: aconst_null
        11: new           #25                 // class foo/Test$$anonfun$foo$1
        14: dup
        15: invokespecial #26                 // Method foo/Test$$anonfun$foo$1."<init>":()V
        18: invokevirtual #30                 // Method foo/Optional$.getOrElse$extension:(Ljava/lang/Object;Lscala/Function0;)Ljava/lang/Object;
        21: invokevirtual #34                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
        24: return
...

@scabug
Copy link
Author

scabug commented May 12, 2014

@xeno-by said:
Oh I'm an idiot. After specifying the -optimize flag, everything works just fine:

  public void foo();
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: getstatic     #18                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
         3: getstatic     #23                 // Field foo/Optional$.MODULE$:Lfoo/Optional$;
         6: aconst_null
         7: invokevirtual #27                 // Method foo/Optional$.isEmpty$extension:(Ljava/lang/Object;)Z
        10: ifne          17
        13: aconst_null
        14: goto          24
        17: new           #29                 // class foo/C
        20: dup
        21: invokespecial #30                 // Method foo/C."<init>":()V
        24: astore_2
        25: astore_1
        26: getstatic     #35                 // Field scala/Console$.MODULE$:Lscala/Console$;
        29: aload_2
        30: invokevirtual #39                 // Method scala/Console$.println:(Ljava/lang/Object;)V
        33: return

@scabug
Copy link
Author

scabug commented May 12, 2014

@retronym said:
Update: this isn't related to value classes, remove extends AnyVal for:

+ qbin/scalac -optimize -Yinline-warnings sandbox/a.scala
+ qbin/scalac -optimize -Yinline-warnings sandbox/b.scala
sandbox/b.scala:4: warning: Could not inline required method getOrElse because bytecode unavailable.
  def foo = new Optional("").getOrElse("!!!")
                                      ^
sandbox/b.scala:4: warning: At the end of the day, could not inline @inline-marked method getOrElse
  def foo = new Optional("").getOrElse("!!!")
                                      ^

@scabug
Copy link
Author

scabug commented Nov 25, 2014

Jim Kleckner (jkleckner) said:
I think I may be hitting this but it is hard to know. The error I see has no file/line numbers or function names to give a hint at where the failure might be located (see below). Further, I have determined by very careful steps that under certain circumstances, inlining can fail when a full compile is performed but succeed when a large block of code is added to a function and compile then just touches that compilation unit.

This non-stationary behavior makes bisecting infeasible and I burned a lot of time until I realized that the compiler wasn't invariant. Coupled with the inability to point to a file/line number, it is impractical to isolate a bit of code to demonstrate the problem. I notice the OP's error message likewise has no file/line info but the names are suggestive. Can we improve the error message at least?

Suggestions?

From sbt:

> clean
[success] Total time: 0 s, completed Nov 25, 2014 5:41:56 AM
> compile
[info] Updating {file:/Users/jim/dev/tesla/}hawking...
[info] Updating {file:/Users/jim/dev/tesla/}rosen...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Updating {file:/Users/jim/dev/tesla/}main...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[warn] At the end of the day, could not inline @inline-marked method ->$extension
[warn] one warning found
[success] Total time: 19 s, completed Nov 25, 2014 5:42:19 AM
> 
> sbtVersion
[info] rosen/*:sbtVersion
[info]  0.13.1
[info] hawking/*:sbtVersion
[info]  0.13.1
[info] main/*:sbtVersion
[info]  0.13.1
> scalaVersion
[info] rosen/*:scalaVersion
[info]  2.10.4
[info] hawking/*:scalaVersion
[info]  2.10.4
[info] main/*:scalaVersion
[info]  2.10.4
> 

@scabug
Copy link
Author

scabug commented Nov 27, 2014

@retronym said:
I'm not sure why SBT is failing to show the full warnings. You can export a corresponding command line for the batch compiler in SBT with export compile to help to troubleshoot this problem without SBT. I would also recommend updating to a more recent SBT version, perhaps the output is improved.

That warning is almost certainly about the -> extension method used for tuple creation. See SI-6723 and the proposed fix, scala/scala#4041. The workaround is to use (a, b) instead of a -> b in affected call sites.

More generally, inline warnings aren't really something that you can always seek to reduce to zero. They are just a tool to help you understand why inlining isn't happening in certain places.

@scabug
Copy link
Author

scabug commented Nov 27, 2014

Jim Kleckner (jkleckner) said:
Thanks for the "export compile" trick. It gave the attached file output which I don't expect to be very useful. Bisecting with it is a bit like whack-a-mole with deleting the code just makes the problem move to another area. The file listed in the compiler had no "->" operators in it.

Your conjecture that #6723 may be the root cause is plausible since exceeding inline counts could explain why a "global compile" fails when an "incremental compile" succeeds.

FYI, I tried using sbt 13.6 but that didn't put any line numbers into the warning output, not surprisingly.

@SethTisue
Copy link
Member

is still this relevant in 2.12, with the new inliner?

@lrytz
Copy link
Member

lrytz commented Mar 7, 2018

works in 2.12

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

No branches or pull requests

3 participants