-
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
Decouple transparent and erased properties #4860
Conversation
I am still not sure about this. It seems we need two kinds of transparent methods: Compile-time only, i.e.
Compile-time + Run-time, i.e.
Examples of the first kind of method are all the typelevel functions discussed in #4616. An example of the second kind of method is a transparent In this PR, the first kind of method is labelled
Any other ideas? Note: Why do the second kind of methods inline in their bodies before they are expanded? Because these calls might go to erased transparent functions, which will not be available at runtime. An alternative, (which is what's implemented in 1299850) is to not inline in the body, but then erased transparency becomes transitive. You cannot call an erased transparent function from a normal transparent function, since the call does not go away. This seems weird. After all, you can call an erased transparent function from a normal function, since the call simply gets inlined there. |
I discovered a tweak which, even though it is very small, might change the balance of power. The tweak is:
That means we essentially have two modes:
This means that now we don't have to choose: Let's redo the characterisation with this change: transparent erased methods
transparent methods
|
@nicolasstucki I believe the whole |
d655ee3
to
648b8a4
Compare
To allow the use of |
|
@@ -160,7 +160,7 @@ object Config { | |||
final val showCompletions = false | |||
|
|||
/** If set, enables tracing */ | |||
final val tracingEnabled = false | |||
final val tracingEnabled = true |
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.
Was this enabled on purpose?
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.
No, I mistakenly added two files to the commit that should not be there.
@@ -548,8 +548,8 @@ object Flags { | |||
/** Assumed to be pure */ | |||
final val StableOrErased = Stable | Erased | |||
|
|||
/** Labeled `private`, `final`, or `transparent` */ | |||
final val EffectivelyFinal = Private | Final | Transparent | |||
/** Labeled `private`, `final`, `transparent`, or `erasedd` */ |
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 erasedd
-> erased
.
@@ -761,7 +761,7 @@ class ClassfileParser( | |||
assert(cond, | |||
s"Unpickling ${classRoot.symbol.showLocated} from ${classRoot.symbol.associatedFile} is not allowed with -Yscala2-unpickler $allowed") | |||
|
|||
if (allowed != "always") { | |||
if (false && allowed != "always") { |
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.
Should be removed
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.
Ah, I needed that because my local setup does not run otherwise. I have to inquire why.
} else if (member.is(Erased) && member.allOverriddenSymbols.forall(_.is(Deferred))) { // (1.9) | ||
overrideError("is an erased method, may not override only deferred methods") | ||
} else if (member.is(Erased)) { // (1.9) | ||
overrideError("is an erased method, may not override anything") | ||
} else if (member.is(Macro, butNot = Scala2x)) { // (1.9) | ||
overrideError("is a macro, may not override anything") |
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.
Unreachable code. This case is covered be the previous one.
@@ -143,7 +143,7 @@ object RefChecks { | |||
* 1.8.1 M's type is a subtype of O's type, or | |||
* 1.8.2 M is of type []S, O is of type ()T and S <: T, or | |||
* 1.8.3 M is of type ()S, O is of type []T and S <: T, or | |||
* 1.9 M must not be a typelevel def or a Dotty macro def | |||
* 1.9 M must not be an erased definition | |||
* 1.10. If M is a 2.x macro def, O cannot be deferred unless there's a concrete method overriding O. | |||
* 1.11. If M is not a macro def, O cannot be a macro def. |
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 1.11
rule should probably be for erased
in general.
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.
In fact, 1.11 is not needed at all, since macros are erased, hence effectively final.
But macros are always implicitly erased. And I don't think there is a problem for normal transparent functions. |
The latest commit brings back the rule that erased methods may override other methods, as long as at least one of the overridden methods is concrete. Scala 2 has the same rule for macros. I believe it's a useful rule to have, since otherwise we can never re-implement an inherited method with macro-generated code. We can think about tightening the rule later. |
The issue arises with the combination of both transparent def foo(transparent i: Int): Int = bar(i)
erased transparent def bar(transparent i: Int): Int = ~baz(i)
def baz(i: Int): Int = i In this case, for the body of transparent def foo(transparent i: Int): Int = ~baz(i) but now we cannot evaluate |
It is at times useful to have `transparent` methods that are not `erased`. For instance, a transparent `unapply` might enable inline reductions, but we want to also keep it at runtime, because the same type might be matched at compile-time and at run-time. This commit decouples `transparent` and `erased` again. `transparent` does not imply `erased`.
In bodies of transparent unerased method we inline only transparent erased methods.
- Mark a method with a toplevel splice as a macro already when we register the inline info. This is needed so that such methods are recognized as erased the first time someone wants to inline them. - Improve error message for splice outside quote.
We need the `erased` on foo because otherwise we get the error -------------------------------- 6 | transparent def foo(): Unit = foo2() | ^^^^^^ |Could not find macro class Foo$ in classpath. The most common reason for that is that you cannot use transparent macro implementations in the same compilation run that defines them
95d06bc
to
370125e
Compare
Test Then there is a second problem, in the body of transparent def power2(x: Double) = power(x, 2)
erased transparent def power(x: Double, transparent n: Long) = ~PowerMacro.powerCode('(x), n) We will probably have the same issues for type level matches in |
Deciding if a The complete rules would be
|
Closed for now, in favor of #4927. |
It is at times useful to have
transparent
methods that are noterased
. For instance, a transparentunapply
might enable inlinereductions, but we want to also keep it at runtime, because the same
type might be matched at compile-time and at run-time.
This commit decouples
transparent
anderased
again.transparent
does not imply
erased
.