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(???)
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
should compile
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
Most helpful comment
Sorry, I should specify, I meant in a scheduled meeting