Skip to content

Opaque types incompatible with inline as representation leaks from opaque type's enclosing scope #6662

Closed
@j-mie6

Description

@j-mie6

The following code worked fine as of 0.15.0-RC1:

opaque type Opt[A >: Null] = A

inline def (x: Opt[A]) nonEmpty[A >: Null]: Boolean = x.get != null
inline def (x: Opt[A]) isEmpty[A >: Null]: Boolean = x.get == null
inline def (x: Opt[A]) isDefined[A >: Null]: Boolean = x.nonEmpty
inline def (x: Opt[A]) get[A >: Null]: A = Opt.unOpt(x)

object Opt
{
    inline def unOpt[A >: Null](x: Opt[A]): A = x
    inline def apply[A >: Null](x: A): Opt[A] = x
    inline def some[A >: Null](x: A): Opt[A] = x
    inline def none[A >: Null]: Opt[A] = null
    inline def fromOption[A >: Null](x: Option[A]) = x.orNull
}

As of 0.16.0-RC3 however, things have changed with opaque types as I understand it. I managed to get everything working again by wrapping in an Object. This code compiles fine:

object opt
{
    opaque type Opt[A >: Null] = A
    object Opt
    {
        inline def unOpt[A >: Null](x: Opt[A]): A = x
        inline def apply[A >: Null](x: A): Opt[A] = x
        /*inline*/ def some[A >: Null](x: A): Opt[A] = x
        /*inline*/ def none[A >: Null]: Opt[A] = null
        inline def fromOption[A >: Null](x: Option[A]) = x.orNull
    }
}

import opt.Opt
inline def (x: Opt[A]) nonEmpty[A >: Null]: Boolean = x.get != null
inline def (x: Opt[A]) isEmpty[A >: Null]: Boolean = x.get == null
inline def (x: Opt[A]) isDefined[A >: Null]: Boolean = x.nonEmpty
inline def (x: Opt[A]) get[A >: Null]: A = Opt.unOpt(x)

However, suppose we uncomment the inline of some or none, then when using either of them we get this compiler error:

-- [E007] Type Mismatch Error: /*snip*/DominatorTree.scala:163:47 
[error] 163 |                    ancestor(cs.get.idx) = some(s)
[error]     |                                           ^^^^^^^
[error]     |Found:    datastructures.opt.Opt[DominatorTree.this.DomNode]
[error]     |Required: datastructures.opt.Opt[DominatorTree.this.DomNode]

This error is pretty obscure, but I imagine it is something to do with the new rules surrounding how opaque types are treated inside and outside of the defining scope (and inline probably makes that leak!).

Additionally, moving any of the extension methods into the opt object makes them give "Member not found errors" when used outside of the opt object's scope if inline is not removed. Placing them inside a delegate for {} and adding inline makes a "An extension method was tried, but could not be fully constructed:" be given in addition. I'm fairly sure this is the same leakage problem, where they are not agreeing with the representation found outside the opt object (and so are method not found) because they see the erased type.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions