Dotty: Inline member selection breaks with Nothing arguments

Created on 25 Mar 2020  路  9Comments  路  Source: lampepfl/dotty

minimized code

object Foo:
  def assert1(x: Boolean) = if !x then ???
  inline def assert2(x: Boolean) = if !x then ???
  inline def assert3(inline x: Boolean) = if !x then ???

  assert1(???)
  assert2(???)
  assert3(???)

Compilation output

scala -- Error: Foo.scala:8:9 -------------------------------------------------------- 8 | assert2(???) | ^^^^^^^^^^^^ |unary_! cannot be accessed as a member of (x : Nothing) from module class Foo$. | This location contains code that was inlined from Foo.scala:4 -- Error: Foo.scala:9:9 -------------------------------------------------------- 9 | assert3(???) | ^^^^^^^^^^^^ |unary_! cannot be accessed as a member of (x : Nothing) from module class Foo$. | This location contains code that was inlined from Foo.scala:5

expectation

should compile

inline bug

Most helpful comment

Sorry, I should specify, I meant in a scheduled meeting

All 9 comments

I don't think there's anything we can do here. Did you have some fix in mind?

Maybe when inlining a Nothing, we can ascribe it. Something where assert2(???) would become if !(??? : Boolean) then ???.

I am reluctant to make things more complicated. Are there real usecases where we need this?

I started writing some code with calling inline functions but placed the ??? as a placeholder while I wrote the other arguments and noticed the failure. It is mostly a potential usability issue with where things would fail with unhelpfully error messages.

From first principles---in this case, "If the invocation typechecks, then the expansion also typechecks"---, this should work.

Also: the same problem will arise with Null (when explicit-nulls are not enabled).

We had the same issue in the Scala.js IR at some point. Optimizations could reduce a term previously well-typed into null.method(), which would not typecheck. The fix in our case was to actually consider null.method() (and ???.method()) as type-correct (instead of inserting ascriptions).

Note that Nothing and Null are not the only things that may break when removing intermediate upcasts. Anywhere Scala's subtyping lacks transitivity will cause a problem. For instance:

type A
class B { def foo = 0 }
trait Ev { type T >: A <: B }

inline  // commenting this fixes the error
def test(ev: Ev)(x: ev.T): Int = x.foo

def trial(ev: Ev, a: A) = {
  test(ev)(a)  // foo cannot be accessed as a member of (a : A) from module class main$package$.
}

should this have further discussion?

@bishabosha what do you mean? The issue is not fixed.

Sorry, I should specify, I meant in a scheduled meeting

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ohze picture ohze  路  3Comments

odersky picture odersky  路  3Comments

dwijnand picture dwijnand  路  3Comments

fommil picture fommil  路  3Comments

NightMachinary picture NightMachinary  路  3Comments