-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Make polymorphic functions more efficient and expressive #17548
Conversation
7535ede
to
0022e5b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise LGTM
@@ -151,7 +151,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { | |||
case class CapturesAndResult(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree | |||
|
|||
/** Short-lived usage in typer, does not need copy/transform/fold infrastructure */ | |||
case class DependentTypeTree(tp: List[Symbol] => Type)(implicit @constructorOnly src: SourceFile) extends Tree | |||
case class DependentTypeTree(tp: (List[TypeSymbol], List[TermSymbol]) => Type)(implicit @constructorOnly src: SourceFile) extends Tree |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we simplify this so that a DependentTypeTree just takes a (tp: List[Symbol] => Type)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a List[Symbol] means that we need to spend extra time concatenating and partitioning the parameters, and we need to document this behavior, so I'm not sure it's really more simple, but I'm happy to do the change if you prefer it that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, in the end it does not seem to be a simplification. So OK to keep as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I started my comment stream I thought it would come out simpler than it did 😄
val tpe = tpFun(termParamss.head) | ||
// A lambda has at most one type parameter list followed by exactly one term parameter list. | ||
val tpe = (paramss: @unchecked) match | ||
case TypeSymbols(tparams) :: TermSymbols(vparams) :: Nil => tpFun(tparams, vparams) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we simplify DependentTypeTree
s, these would become:
case TypeSymbols(tparams) :: TermSymbols(vparams) :: Nil => tpFun(tparams ++ vparams)
case TermSymbols(vparams) :: Nil => tpFun(vparams)
val resultTpt = pt.dealias match | ||
case RefinedType(parent, nme.apply, poly @ PolyType(_, mt: MethodType)) if parent.classSymbol eq defn.PolyFunctionClass => | ||
untpd.DependentTypeTree((tsyms, vsyms) => | ||
mt.resultType.substParams(mt, vsyms.map(_.termRef)).substParams(poly, tsyms.map(_.typeRef))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we simplify DependentTypeTree
s, this would become:
untpd.DependentTypeTree(syms =>
val (tsyms, vsyms) = syms.partition(_.isType)
mt.resultType.substParams(mt, vsyms.map(_.termRef)).substParams(poly, tsyms.map(_.typeRef)))
So I am OK to merge as is. |
Previously, the expression val a: (x: Int) => Bar[x.type] = ??? : ((x: Int) => Foo[x.type]) lead to the following error: Found: (x: Int) => Foo[x.type] Required: (x: Int) => Bar[x².type] where: x is a reference to a value parameter x² is a reference to a value parameter There's two problems here: 1. In the second lambda, x is supposed to be renamed to x², but the binder wasn't renamed. 2. Since the binders introducing x are part of the printed types, there isn't any ambiguity and no renaming is actually needed. (1.) is a straight-up bug in the printing logic that we fix. (2.) is maybe a matter of opinion, but for clarity we choose to address it by using the same superscript for every parameter reference with the same name whose binder has been printed. This does meant that `(x: Int) => (x: Int) => x.type` won't be disambiguated, but something like that would be better addressed by printing a warning about name shadowing anyway.
This was dropped from the syntax, so this isn't needed anymore.
Reuse the existing `DependentTypeTree` mechanism already in place for monomorphic lambdas to compute the result type of polymorphic lambdas based on their expected type.
Previously, we desugared them manually into anonymous class instances, but by using a Closure node instead, we ensure that they get translated into indy lambdas on the JVM. Also cleaned up and added a TODO in the desugaring of polymorphic function types into refinement types since I realized that purity wasn't taken into account.
…uilds This avoids finding compilation errors late in the development cycle. Having explicit nulls on is especially important because typed and untyped trees are now only distinguished using `Null`, so on the non-bootstrapped build we don't get compiler errors when passing an untyped Tree where a typed Tree is expected.
Just like for monomorphic closures this isn't currently supported.
The check used to disallow any reference of the form `X.this.type` even though `X` is never the refinement class. This change mirrors the check done with `ThisType` a couple of line below.
This PR enhances polymorphic function types in two ways:
Additionally, we fix the logic for renaming bound variables when pretty-printing lambdas and fix the handling of
this
in refinements.