Skip to content

Inlining in opaque type #14653

Closed
Closed
@Swoorup

Description

@Swoorup

Compiler version

3.1.1

Minimized code

https://scastie.scala-lang.org/Baodp0IPRQWdkjwE2SgUYg

type Amount = Amount.Type
object Amount:
  opaque type Type = BigDecimal
  inline def apply(inline dec: BigDecimal): Type = dec

  extension (self: Type)
    inline def value: BigDecimal = self
    inline def +(y: Type): Type = self + y

@main def r(): Unit = 
  val aa: Amount = Amount(1)
  val ab: Amount = Amount(2)
  val ac: Amount = Amount(2)
  val as1: Amount = aa + ab
  val as2: Amount = aa + ab + ac

  println(s"aa + ab = ${as1}")
  println(s"aa + ab = ${as2}")

Output

undefined: aa.+ # -1: TermRef(TermRef(NoPrefix,val aa),+) at inlining
9
Found:    (ac : Playground.Amount)
Required: BigDecimal

Expectation

Either successfully compile or warn about recursive definition.
I am not sure the correct way to write

    inline def +(y: Type): Type = self + y

is instead

    inline def +(y: Type): Type = self.value + y.value

Activity

changed the title [-]Odd compiler message using opaque type and inline recursive-like definition[/-] [+]undefined: aa.+ # -1: TermRef(TermRef(NoPrefix,val aa),+) at inlining[/+] on Mar 10, 2022
changed the title [-]undefined: aa.+ # -1: TermRef(TermRef(NoPrefix,val aa),+) at inlining[/-] [+]Inlining in opaque type[/+] on Mar 10, 2022
prolativ

prolativ commented on Mar 10, 2022

@prolativ
Contributor

I guess this is somehow related to the ordering of compilation phases. Some phases related to inlining are before ElimOpaque and some are after it.

nicolasstucki

nicolasstucki commented on Apr 29, 2022

@nicolasstucki
Contributor

Minimized

type Amount = Amount.Type
object Amount:
  opaque type Type = Int
  inline def twice(x: Type): Type = x + x

def double(a: Amount): Amount.Type = Amount.twice(a)

Workaround

- def double(a: Amount): Amount.Type = Amount.twice(a)
+ def double(a: Amount.Type): Amount.Type = Amount.twice(a)
nicolasstucki

nicolasstucki commented on Apr 29, 2022

@nicolasstucki
Contributor

It seems that by having the alias type we do not detect that we need to generate a refined proxy for x

    def double(a: Amount): Amount.Type = 
      {
        val $proxy1: Amount.type{Type = Int} = 
          Amount.$asInstanceOf[Amount.type{Type = Int}]
        val Amount$_this: ($proxy1 : Amount.type{Type = Int}) = $proxy1
        (a.+(a):Amount$_this.Type)
      }.$asInstanceOf[Amount.Type]
    def double(a: Amount.Type): Amount.Type = 
      {
        val $proxy1: Amount.type{Type = Int} = 
          Amount.$asInstanceOf[Amount.type{Type = Int}]
        val Amount$_this: ($proxy1 : Amount.type{Type = Int}) = $proxy1
        val x$proxy1: ((a : Amount.Type) & $proxy1.Type) = 
          a.$asInstanceOf[(a : Amount.Type) & $proxy1.Type]
        (x$proxy1.+(x$proxy1):Amount$_this.Type)
      }.$asInstanceOf[Amount.Type]
nicolasstucki

nicolasstucki commented on Apr 29, 2022

@nicolasstucki
Contributor
def a: Amount = ???
def double: Amount.Type = Amount.twice(a)

is inlined with an unrefined proxy

    def a: Amount = ???
    def double: Amount.Type = 
      {
        val $proxy1: Amount.type{Type = Int} = 
          Amount.$asInstanceOf[Amount.type{Type = Int}]
        val Amount$_this: ($proxy1 : Amount.type{Type = Int}) = $proxy1
        val x$proxy1: Amount = a
        (x$proxy1.+(x$proxy1):Amount$_this.Type)
      }.$asInstanceOf[Amount.Type]
added a commit that references this issue on Apr 29, 2022
88781e7
added a commit that references this issue on Oct 18, 2022
16faafc
added this to the 3.2.0 milestone on Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

    Development

    Participants

    @Swoorup@nicolasstucki@prolativ@Kordyjan

    Issue actions

      Inlining in opaque type · Issue #14653 · scala/scala3