-
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 by-name types first-class value types #14225
Conversation
@smarter To make by-name types context functions, we need to overcome a tricky interaction between type variable avoidance and implicit search divergence checking. The problem is that We can now generate new type variables for every level of by-name implicit search. This violates the basic assumption underlying implicit divergence checking that the set of type variables is fixed. Consequently, we get infinite recursion on neg/byname-implicits-11 and neg/by-name-implicits-16. Previously, this problem did not manifest itself, since we did not generate synthesized terms with nested scopes. But now we do. (I guess even before this could have arisen if we I see two possible routes for a solution
Right now I am blocked by this, so if you have some advice that would be great! |
Yes we use the same trick than depTypeVar and can map back using representedParamRef |
d2dabb8
to
a9dfc91
Compare
I tried to extend specialization to all context functions, not just ones of 0 arity. But that runs into problems for dependent context functions, since the necessary casts get complicated. Since context functions over primitive types are an anti-pattern anyway I don't think we need to optimize this case, after all.
The regular changeOwner can cause cycles in transformations. Transformations like ByNameLambda should use the less demanding changeOwnerAfter.
There was an omission before, but as long as byname parameters were ExprTypes it did not lead to problems.
test performance please |
performance test scheduled: 1 job(s) in queue, 1 running. |
@@ -726,6 +726,14 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => | |||
case _ => tree | |||
} | |||
|
|||
/** An anonyous function and a closure node referring to it in a block, without any wrappigs */ |
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.
typo:
anonyous -> anonymous
wrappigs -> wrappings
* that they are cbv, and we have to correct this assumption if the actual resolved | ||
* method is cbn. If the call is non-overloaded, we do the right thing from the start. | ||
* Inserting a byName call just makes clear that the argumnent is cbn. There is no | ||
* special treatemnt in the compiler associated with that method; it is just the |
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.
typo: treatemnt -> treatment
@smarter I solved the problem by delaying the closure generation. In typer a call-by-name argument is represented as |
Performance test finished successfully: Visit https://dotty-bench.epfl.ch/14225/ to see the changes. Benchmarks is based on merging with master (7939ddb) |
Instead, drop <by-name> applications when pickling and reconstitute them based on formal parameter types when unpickling.
@odersky I added some patches in dotty-staging@36f4598. You can cherry-pick that commit. It fixes most of the macro related issues. |
ByNameLambda needs to run in a group after ElimRepeated since ElimRepeated works on ByName arguments but not converted closures, and it sees the arguments after transformations by subsequent miniphases in the same group.
ByName nodes in arguments are not pickled, which means that we can use the same Tasty version as before.
I made the following changes
This means that no new Tasty version is needed. The pickled Tasty stays the same. |
@sjrd Should it be considered a Tasty incompatibility that def foo(arg: => Int) = { arg.apply(); arg.apply() } i.e. we now have |
Overall, here are the major representation changes, from phase
|
Writing things differently is not issue per se, as long as reading the old stuff still works (backward compat) and that an old compiler can still read the new stuff (forward compat). So, if the new compiler sees only If the old compiler sees the |
- Handle combinations of NamedArg and ByName - Accept and evaluate any remainign ByName applications
Well, errors from Scaladoc don't look scary. There are just slight differences in signatures with call-by-name params. Scaladoc now presents |
That's the desugared form, but the form users should write and we should use for pretty-printing is still |
@pikinier20 No, by-name parameter types should still be rendered as |
Okey, good that we explained this. I'm going to fix it tomorrow if it's ok. |
@nicolasstucki I integrated your changes. Now there's 5 tests that are still failing.
|
The Tasty interop problems look daunting, unfortunately.
So this means we need a mode where we still generate the old Maybe we should put this work on hold a bit and decide afterwards whether it's worth continuing. /cc @nicolasstucki @pikinier20. |
Is this because of the extra
This is made much easier by #14156 which added support for multi-compiler tests to our test infrastructure, from http://dotty.epfl.ch/docs/contributing/testing.html:
|
It's not only that. Also, that when the expected type is a |
We should be able to prevent avoidance from kicking in by not increasing the nesting level when typing a def with no parameters, the relevant code is here: |
In the TreePickler can we replace class Foo {
def foo(arg: => Any): String = arg.toString
}
class Bar[F <: Foo { def foo(arg: => String): String }] {
def bar(foo: F, arg: String): String = foo.foo(arg)
} otherwise when by-name parameter is in tree position we still have |
Withdrawn in favor of #14295. The latter is more conservative, and less principled, but much simpler to implement, and it causes not inter-version headaches. |
A big revision so that types indicating call-by-name passing are regular value types. In fact, the
by-name type
=> T
now behaves like a nullary context function type() ?=> T
. Nullary context function types still cannot be written explicitly but the type mechanics for cbn types and nullary context function types are the same.Benefits:
These generalizations should be considered for future PRs.
Things to do:
scala3-bootstrapped/test
task.