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

Inline macros dealias function parameter types too eagerly #15304

Open
xerial opened this issue May 28, 2022 · 2 comments · May be fixed by #14586
Open

Inline macros dealias function parameter types too eagerly #15304

xerial opened this issue May 28, 2022 · 2 comments · May be fixed by #14586

Comments

@xerial
Copy link
Contributor

xerial commented May 28, 2022

Scala 3's inline macros resolve type aliases in function type parameters to the original types eagerly.
This issue didn't happen in Scala 2 macros.

For example, if an inline macro receives Function1[A, B] (type A = OriginalType), there is no way inside the macro to know the detailed information of type A as A is already resolved to the original type.

Compiler version

Scala 3.1.2

Minimized code

Macros.scala

import scala.quoted._

def typeNameOf[A: Type](using Quotes): Expr[String] = {
    val name = Type.show[A]
    Expr(name)
}

inline def typeNameOf[A]: String = ${
  typeNameOf[A]
}

inline def typeNameOfF1[A](f: A => Unit): String = ${
  typeNameOf[A]
}

main.sc

type MyString = String

println(typeNameOf[MyString])
// MyString (OK)

println(typeNameOfF1 { (x: MyString) =>  })
// java.lang.String <---------- NG: MyString is resolved to java.lang.String unexpectedly

println(typeNameOfF1[MyString] { (x: MyString) =>  })
// MyString <--- OK. If the type name is given as type param, the alias is preserved, although the semantics is the same with the above example.

println(typeNameOfF1 { (x: Seq[MyString]) => })
// scala.colleciton.immutable.Seq[MyString]

println(typeNameOfF1 { (x: (MyString, Int)) => })
// scala.Tuple2[main.MyString, scala.Int]

Output

$ scala-cli main.sc Macros.scala
main.MyString
java.lang.String     // MyString is resolved to java.lang.String eagerly
main.MyString
scala.collection.immutable.Seq[main.MyString]
scala.Tuple2[main.MyString, scala.Int]

Expectation

Type aliases (e.g., type MyStirng = String) in the function type parameters should be preserved inside the macros:

$ scala-cli main.sc Macros.scala
main.MyString
main.MyString
main.MyString
scala.collection.immutable.Seq[main.MyString]
scala.Tuple2[main.MyString, scala.Int]
@xerial xerial added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels May 28, 2022
@xerial xerial changed the title Inline macros dealiases types of function arguments too eagerly Inline macros dealiase function argument types too eagerly May 28, 2022
@xerial xerial changed the title Inline macros dealiase function argument types too eagerly Inline macros dealias function argument types too eagerly May 28, 2022
@xerial xerial changed the title Inline macros dealias function argument types too eagerly Inline macros dealias function parameter types too eagerly May 28, 2022
@smarter smarter linked a pull request May 29, 2022 that will close this issue
@szymon-rd szymon-rd added area:metaprogramming:quotes Issues related to quotes and splices and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels May 30, 2022
@nicolasstucki
Copy link
Contributor

This is a limitation in type inference.

It can be reproduced with the same inference with

inline def typeNameOf[A]: String = ???

inline def typeNameOfF1[A](f: A => Unit): String = ???

def test =
  type MyString = String

  println(typeNameOf[MyString])
  println(typeNameOfF1/*[String]*/ { (x: MyString) =>  })
  println(typeNameOfF1[MyString] { (x: MyString) =>  })
  println(typeNameOfF1/*[Seq[MyString]]*/ { (x: Seq[MyString]) => })
  println(typeNameOfF1/*[(MyString, Int)]*/ { (x: (MyString, Int)) => })

As far as I know, there are no guarantees that aliases are kept when inferring a type. Not sure if that is even possible in general.

@pweisenburger
Copy link
Contributor

pweisenburger commented May 30, 2022

As pointed out, this is a type checking/inference limitation. Usually whether a type alias is used or not to denote a type should be transparent. Expect when it is not. In particular, it is not when inspecting types in metaprogramming (as you did). The other problematic area is type unification.

Overall, I think it is a good idea to dealias types less eagerly. PR #14586 is my attempt to keep type aliases in more cases if possible. It also fixes this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants