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

Question: desugaring of right associative extenstion methods #22170

Open
Florian3k opened this issue Dec 9, 2024 · 0 comments
Open

Question: desugaring of right associative extenstion methods #22170

Florian3k opened this issue Dec 9, 2024 · 0 comments
Labels
area:desugar Desugaring happens after parsing but before typing, see desugar.scala area:extension-methods itype:question

Comments

@Florian3k
Copy link
Contributor

With the way right associative extension methods are desugared (https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html), some different methods end up with the same desugared form.
Both of those methods:

extension (x: X) def +:: [T](y: Y)
extension [T](x: X) def +:: (y: Y)

are desugared to:

<extension> def +:: [T](y: Y)(x: X)

It's important that the desugaring is reversible for some tools in the ecosystem, to display correct signature.
Scaladoc hacked solved this by sorting the parameters by their source positions, but the problem remains in ShortenedTypePrinter and RefinedPrinter:

if gsym.name.isRightAssocOperatorName then
val (leadingTyParamss, rest1) = paramss match
case fst :: tail if isTypeParamClause(fst) => (List(fst), tail)
case other => (List(), other)
val (leadingUsing, rest2) = rest1.span(isUsingClause)
val (rightTyParamss, rest3) = rest2.span(isTypeParamClause)
val (rightParamss, rest4) = rest3.splitAt(1)
val (leftParamss, rest5) = rest4.splitAt(1)
val (trailingUsing, rest6) = rest5.span(isUsingClause)
if leftParamss.nonEmpty then
leadingTyParamss ::: leadingUsing ::: leftParamss ::: rightTyParamss ::: rightParamss ::: trailingUsing ::: rest6
else paramss // it wasn't a binary operator, after all.

if tree.name.isRightAssocOperatorName then
// If you change the names of the clauses below, also change them in right-associative-extension-methods.md
// we have the following encoding of tree.paramss:
// (leftTyParams ++ leadingUsing
// ++ rightTyParams ++ rightParam
// ++ leftParam ++ trailingUsing ++ rest)
// e.g.
// extension [A](using B)(c: C)(using D)
// def %:[E](f: F)(g: G)(using H): Res = ???
// will have the following values:
// - leftTyParams = List(`[A]`)
// - leadingUsing = List(`(using B)`)
// - rightTyParams = List(`[E]`)
// - rightParam = List(`(f: F)`)
// - leftParam = List(`(c: C)`)
// - trailingUsing = List(`(using D)`)
// - rest = List(`(g: G)`, `(using H)`)
// we need to swap (rightTyParams ++ rightParam) with (leftParam ++ trailingUsing)
val (leftTyParams, rest1) = tree.paramss match
case fst :: tail if isTypeParamClause(fst) => (List(fst), tail)
case other => (List(), other)
val (leadingUsing, rest2) = rest1.span(isUsingClause)
val (rightTyParams, rest3) = rest2.span(isTypeParamClause)
val (rightParam, rest4) = rest3.splitAt(1)
val (leftParam, rest5) = rest4.splitAt(1)
val (trailingUsing, rest6) = rest5.span(isUsingClause)
if leftParam.nonEmpty then
leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6
else
tree.paramss // it wasn't a binary operator, after all.

This bug in ShortenedTypePrinter can be seen in Metals:
image

My question is, can we add some additional information in desugar so that the desugared form can be reversed easily to original signature?

This could be a flag added to type parameters that are next to extension keyword, maybe the third flag here (?):

/** An extension method, or a collective extension instance */
val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "<extension>")

Or this could be an annotation attached to such type params:
extension [T](x: X) def +:: (y: Y) -> <extension> def +:: [@extensionTypeParam T](y: Y)(x: X)
extension (x: X) def +:: [T](y: Y) -> <extension> def +:: [T](y: Y)(x: X)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:desugar Desugaring happens after parsing but before typing, see desugar.scala area:extension-methods itype:question
Projects
None yet
Development

No branches or pull requests

1 participant