Skip to content

Erratic compiler behavior for inlined function definitions #15893

Closed
@Bersier

Description

@Bersier

See this discussion thread.

Compiler version

3.1.3

Minimized code

sealed trait NatT
case class Zero() extends NatT
case class Succ[+N <: NatT](n: N) extends NatT

type Mod2[N <: NatT] <: NatT = N match
  case Zero => Zero
  case Succ[Zero] => Succ[Zero]
  case Succ[Succ[predPredN]] => Mod2[predPredN]

def mod2(n: NatT):  NatT = n match
  case Zero() => Zero()
  case Succ(Zero()) => Succ(Zero())
  case Succ(Succ(predPredN)) => mod2(predPredN)

inline def inlineMod2(inline n: NatT):  NatT = inline n match
  case Zero() => Zero()
  case Succ(Zero()) => Succ(Zero())
  case Succ(Succ(predPredN)) => inlineMod2(predPredN)

transparent inline def transparentInlineMod2(inline n: NatT):  NatT = inline n match
  case Zero() => Zero()
  case Succ(Zero()) => Succ(Zero())
  case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN)

def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected
  case Zero(): Zero => Zero()
  case Succ(Zero()): Succ[Zero] => Succ(Zero())
  case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN)

inline def inlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match
  case Zero(): Zero => Zero()
  case Succ(Zero()): Succ[Zero] => Succ(Zero())
  case Succ(Succ(predPredN)): Succ[Succ[_]] => inlineDependentlyTypedMod2(predPredN)

transparent inline def transparentInlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match
  case Zero(): Zero => Zero()
  case Succ(Zero()): Succ[Zero] => Succ(Zero())
  case Succ(Succ(predPredN)): Succ[Succ[_]] => transparentInlineDependentlyTypedMod2(predPredN)

def foo(n: NatT): NatT = mod2(n) match
  case Succ(Zero()) => Zero()
  case _ => n

inline def inlineFoo(inline n: NatT): NatT = inline inlineMod2(n) match
  case Succ(Zero()) => Zero()
  case _ => n

inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInlineMod2(n) match
  case Succ(Zero()) => Zero()
  case _ => n

@main def main(): Unit =
  println(mod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
  println(foo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected
  println(inlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
  println(inlineFoo(Succ(Succ(Succ(Zero()))))) // prints Succ(Succ(Succ(Zero()))); unexpected
  println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
  println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected
  println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected
//  println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected
//  println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected

Output

Succ(Zero())
Zero()
Succ(Zero())
Succ(Succ(Succ(Zero())))
Succ(Zero())
Zero()
[error] java.lang.ClassCastException: Succ cannot be cast to Zero
[error]         at main$package$.dependentlyTypedMod2(main.scala:27)
[error]         at main$package$.main(main.scala:59)
[error]         at main.main(main.scala:52)
[error]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error]         at java.lang.reflect.Method.invoke(Method.java:498) 

Expectation

  1. inlineFoo(Succ(Succ(Succ(Zero())))) should not return Succ(Succ(Succ(Zero()))); inline should not change function output.
  2. Compilation should not be negatively affected by more refined types.
  3. No runtime exception

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions