-
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
Implement individual erased parameters #16507
Implement individual erased parameters #16507
Conversation
fb6d70f
to
d200a4b
Compare
5e50053
to
7fe8b14
Compare
ac13659
to
0cd5c46
Compare
Thanks for taking a look at my PR! Re: interaction with poly functions, afaik poly functions don’t handle erased parameters at all right now. I think having poly functions being a refined |
This seems promising, but since we are pickling the mods just as the tail of methodic trees, we can't easily do both. Perhaps if we just attach the erased parameters right next to the I will think a bit more about this. |
ce8a90b
to
2ca5b78
Compare
I implemented the above approach, still with the |
2f3159a
to
176b23c
Compare
3fc7774
to
0dba09b
Compare
compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala
Outdated
Show resolved
Hide resolved
compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala
Outdated
Show resolved
Hide resolved
ef19bca
to
5f3ffbb
Compare
1bfd88e
to
60c157d
Compare
|
60c157d
to
b810b00
Compare
2093b87
to
3e172e0
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.
The only missing change is to update Quotes.scala
to align to this new scheme.
@@ -2140,7 +2140,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler | |||
|
|||
given MethodTypeMethods: MethodTypeMethods with | |||
extension (self: MethodType) | |||
def isErased: Boolean = self.isErasedMethod | |||
def isErased: Boolean = self.hasErasedParams |
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.
This change is not right, it should probably return false as in the new scheme method types are not erased themself. isErased
should be deprecated in Quotes.scala
.
We have some changes in semantics of:
MethodTypeMethods.isErased
: Should be deprecated and replaced witherasedParams
and maybehasErasedParams
TermParamClauseMethods.isErased
: Should be deprecated and replaced witherasedArgs
and maybehasErasedArgs
TypeReprMethods.isErasedFunctionType
: we could make it work by detecting the refined function type. Though it might be cleaner to deprecate and add a more consistent API.defn.FunctionClass
: needs to be deprecated. Should not have had default parameters in the first palce.
We can only deprecate these methods in a minor release.
What we should include in this PR are the new replacement as @experimental
methods.
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 created #16847 to track this
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.
And started the work on defn.FunctionClass
in #16849
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.
def isErased: Boolean = self.hasErasedParams | |
def isErased: Boolean = false |
same for TermParamClauseMethods.isErased
.
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.
We could change the documentation of TypeReprMethods.isErasedFunctionType
in Quotes.scala
to be
/** Is this type a function type with erased parameters?
*
* @see `isFunctionType`
*/
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.
MethodTypeMethods.isErased
: Should be deprecated and replaced witherasedParams
and maybehasErasedParams
TermParamClauseMethods.isErased
: Should be deprecated and replaced witherasedArgs
and maybehasErasedArgs
TypeReprMethods.isErasedFunctionType
: we could make it work by detecting the refined function type.
This is what I did, alongside with crashing FunctionClass(isErased = true)
and adding ErasedFunctionClass
(that just returns the scala.compiletime.ErasedFunction
symbol).
02435f1
to
9b13653
Compare
* } | ||
* | ||
* ErasedFunctionN and ErasedContextFunctionN erase to Function0. | ||
* | ||
* ImpureXYZFunctionN follow this template: | ||
* | ||
* type ImpureXYZFunctionN[-T0,...,-T{N-1}, +R] = {*} XYZFunctionN[T0,...,T{N-1}, R] |
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.
Does this work with erased parameters?
The old encoding seemed to support ImpureErasedFunctionN
/ImpureErasedContextFunctionN
. With the new encoding we do not have an equivalent XYZFunctionN
.
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.
Unfortunately I don't think so. I've been trying to see how poly functions deal with it, but it seems like poly functions just make scalac crash with CC on (#16871)
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.
With the crash fixed I think it doesn't work with poly functions as well. I wonder if we can change the parser to recognize fat arrows and add a capturing wrapper for both poly functions and erased functions?
@@ -2140,7 +2140,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler | |||
|
|||
given MethodTypeMethods: MethodTypeMethods with | |||
extension (self: MethodType) | |||
def isErased: Boolean = self.isErasedMethod | |||
def isErased: Boolean = self.hasErasedParams |
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.
def isErased: Boolean = self.hasErasedParams | |
def isErased: Boolean = false |
same for TermParamClauseMethods.isErased
.
@@ -2140,7 +2140,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler | |||
|
|||
given MethodTypeMethods: MethodTypeMethods with | |||
extension (self: MethodType) | |||
def isErased: Boolean = self.isErasedMethod | |||
def isErased: Boolean = self.hasErasedParams |
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.
We could change the documentation of TypeReprMethods.isErasedFunctionType
in Quotes.scala
to be
/** Is this type a function type with erased parameters?
*
* @see `isFunctionType`
*/
faea51f
to
6d639ec
Compare
e1cc96c
to
41db4f5
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
tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala
Outdated
Show resolved
Hide resolved
3fa0423
to
ba78c70
Compare
@natsukagami It seems good. Could you squash the commits? |
Breaking change for erasedDefinitions: this effectively makes the current `erased` marker in parameter list apply to only the first parameter. def f(erased a: int, b: int) should now be written as def f(erased a: int, erased b: int) type Function1 = (x: Int, erased y: Int) => Int type Function2 = (Int, erased Int) => Int Use refined traits for erased functions - Function types with erased parameters are now always `ErasedFunction` refined with the correct `apply` definition, for example: scala.runtime.ErasedFunction { def apply(x1: Int, erased x2: Int): Int } where ErasedFunction is an @experimental empty trait. - Polymorphic functions cannot take erased parameters. - By-name parameters cannot be erased. - Internally, use the @ErasedParam annotation as a marker for an erased parameter. - Parameters that are erased classes are now marked `erased` at Typer phase (with an annotation), and in later phases, they are not taken into account when considering erasedness. - Erased parameters/functions quotes API are changed: - `isErased` => `erasedArgs`/`erasedParams` and `hasErasedArgs`/`hasErasedParams` - `FunctionClass` now fails when `isErased = true`. Add `ErasedFunctionClass`. - Added tests and test-fixes for `erasedDefinitions` feature - Updated specs and internal syntax - Aside, reject normal tuples with ValDefs in them. This comes up when trying to parse parameters out of tuples. In practice they don't show up. Co-authored-by: Nicolas Stucki <nicolas.stucki@gmail.com>
ba78c70
to
0f7c3ab
Compare
This seems to change the erasure for context functions with erased parameter: @Linyxus found this example object TestyFactorial:
import scala.annotation.tailrec
import language.experimental.erasedDefinitions
erased class Foo1
class Foo2
@tailrec
final def test1(n: Int, acc: Int): Foo1 ?=> Int =
if n <= 0 then acc
else test1(n - 1, acc * n)
@tailrec
final def test2(n: Int, acc: Int): Foo2 ?=> Int =
if n <= 0 then acc
else test2(n - 1, acc * n) before this change the erasure of test1 is |
…19654) Fixes #19641 How we got here: Originally, overloading resolution for types that were not applied was handled like this: ```scala case defn.FunctionOf(args, resultType, _) => narrowByTypes(alts, args, resultType) case pt => val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false)) if (compat.isEmpty) /* * the case should not be moved to the enclosing match * since SAM type must be considered only if there are no candidates * For example, the second f should be chosen for the following code: * def f(x: String): Unit = ??? * def f: java.io.OutputStream = ??? * new java.io.ObjectOutputStream(f) */ pt match { case SAMType(mtp, _) => narrowByTypes(alts, mtp.paramInfos, mtp.resultType) case _ => // pick any alternatives that are not methods since these might be convertible // to the expected type, or be used as extension method arguments. val convertible = alts.filterNot(alt => normalize(alt, IgnoredProto(pt)).widenSingleton.isInstanceOf[MethodType]) if convertible.length == 1 then convertible else compat } else compat ``` Note the warning comment that the case for SAM types should not be moved out, yet we do exactly the same thing for plain function types. I believe this was simply wrong, but it was not discovered in a test. Then in #16507 we changed the `defn.FunctionOf` extractor so that aliases of function types were matched by it. This triggered test failures since we now hit the wrong case with aliases of function types. In #18286, we moved the extractor test around, but that was not enough, as #19641 shows. Instead the test for `FunctionOf` should be aligned with the test for SAM case. But it turns out that's not even necessary since the preceding `val compat = ...` handles function prototypes correctly by simulating an eta expansion. So in the end we could simply delete the problematic case.
Description
Syntax
Erased parameters in a method / lambda comes with an
erased
modifier before its name:This is a breaking change, as previously erased methods / functions with multiple parameters now only have its first parameter erased.
Semantics
[Impure][Contextual]ErasedFunctionN
traits are no longer available. Instead, erased function values are denoted by refining thescala.runtime.ErasedFunction
trait:They are subsequently compiled (during Erasure) into
[Contextual]FunctionM
whereM
is the number of non-erased parameters.Erased Classes
Any parameter that is an instance of an erased class is automatically erased. This is different from before, where the parameters are erased only if all parameters are instances of erased classes.