Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Operations on NamedTuples do not work with singleton types due to match type refusing to reduce #20453

Open
smarter opened this issue May 22, 2024 · 7 comments
Assignees
Labels
area:match-types area:named-tuples Issues tied to the named tuples feature. itype:bug

Comments

@smarter
Copy link
Member

smarter commented May 22, 2024

Compiler version

7d559ad

Minimized code

import scala.language.experimental.namedTuples

object Test:
  val f: (name: String, age: Int) = ???
  val x: NamedTupleDecomposition.Names[f.type] = ("name", "age")

Output

5 |  val x: NamedTupleDecomposition.Names[f.type] = ("name", "age")
  |                                                 ^^^^^^^^^^^^^^^
  |Found:    (String, String)
  |Required: NamedTupleDecomposition.Names[(Test.f : (name : String, age : Int))]
  |
  |Note: a match type could not be fully reduced:
  |
  |  trying to reduce  NamedTupleDecomposition.Names[(Test.f : (name : String, age : Int))]
  |  failed since selector (Test.f : (name : String, age : Int))
  |  does not match  case NamedTuple.NamedTuple[n, _] => n
  |  and cannot be shown to be disjoint from it either.

Expectation

I'd expect this to reduce, otherwise all NamedTuple operations that use NamedTupleDecomposition like NamedTuple.Map will fail to reduce on singleton types. NamedTupleDecomposition is defined as:

/** Separate from NamedTuple object so that we can match on the opaque type NamedTuple. */
@experimental
object NamedTupleDecomposition:
  import NamedTuple.*

  /** The names of a named tuple, represented as a tuple of literal string values. */
  type Names[X <: AnyNamedTuple] <: Tuple = X match
    case NamedTuple[n, _] => n

So NamedTuple is treated as an abstract type constructor and we end up in

case MatchTypeCasePattern.AbstractTypeConstructor(tycon, argPatterns) =>
scrut.dealias match
case scrutDealias @ AppliedType(scrutTycon, args) if scrutTycon =:= tycon =>
matchArgs(argPatterns, args, tycon.typeParams, scrutIsWidenedAbstract)
case _ =>
false

Which fails since the scrutinee isn't widened. @sjrd : do you anticipate a soundness issue if we added a .widenSingleton on the scrutinee here? Or is the whole design of NamedTupleDecomposition doomed?

@sjrd
Copy link
Member

sjrd commented May 31, 2024

Yes, widening singleton types of paths that can be substituted later creates established unsoundness issues. See #19746

@smarter
Copy link
Member Author

smarter commented Jun 2, 2024

Thanks for the reference. A possibly viable workaround is to use a wrapper type:

import scala.language.experimental.namedTuples

class Wrapper[T](x: T)
object Wrapper:
  type Extract[W <: Wrapper[?]] = W match
    case Wrapper[t] => t

object Test:
  val f: (name: String, age: Int) = ???
  val f2 = Wrapper(f)
  val x: NamedTupleDecomposition.Names[Wrapper.Extract[f2.type]] = ("name", "age")

Not sure if there's a better way or if that can help suggest some more systematic fix.

@soronpo
Copy link
Contributor

soronpo commented Jun 3, 2024

@smarter what if we explicitly create a Widen type operation? So Names will be implemented as Widen[X] match ...

@smarter
Copy link
Member Author

smarter commented Jun 3, 2024

That would reopen the soundness hole of #19746 if Widen[this.type] can reduce to different types depending on the context.

@soronpo
Copy link
Contributor

soronpo commented Jun 3, 2024

I'm not sure that will happen, because of the lazy nature of singleton ops operations.

@smarter
Copy link
Member Author

smarter commented Jun 3, 2024

Though note that the fix for #19746 (0a3497b) specifically target singleton types coming from parameters (since those can be substituted), so maybe something like a widenSingletonOfNonParamRef operation? It's not clear to me that this is enough if the underlying type of a non-param singleton is a GADT, in which case maybe the original hole isn't fixed.

@smarter
Copy link
Member Author

smarter commented Jun 3, 2024

in which case maybe the original hole isn't fixed.

Indeed: #20515.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:match-types area:named-tuples Issues tied to the named tuples feature. itype:bug
Projects
None yet
Development

No branches or pull requests

3 participants