forked from scala/scala
-
Notifications
You must be signed in to change notification settings - Fork 1
SI-6187 Make partial functions re-typable #2
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
Closed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
B1 setInfo TypeBounds.lower(matchResTp) // patch info | ||
|
||
val defaultCase = mkDefaultCase(gen.mkCast(REF(default) APPLY (REF(x)), intersectionType(List(B1.tpe, matchResTp)))) |
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.
Paging @adriaanm; I can't find a clean way out of this straightjacket. Let's discuss this one tomorrow...
- `New(tpe)` doesn't survive a `resetAttrs` / typecheck; use a name instead. - Abandon the tree attachment that passed the default case from `typer` to `patmat`; this tree eluded the attribute reset performed in the macro. Instead, add it to the match. Apart from making the tree re-typable, it also exposes the true code structure to macros, which is important if they need to perform other code transformations. - Install original trees on the declared types of the parameters of the `applyOrElse` method to ensure that references to them within the method pick up the correct type parameter skolems upon retypechecking. - Propagate `TypeTree#original` through `copyAttrs`, which is called during tree duplication / `TreeCopiers`. Without this, the original trees that we installed were not visible anymore during `ResetAttrs`. We are not able to reify partial functions yet -- the particular sticking point is reification of the parentage which is only available in the `ClassInfoType`.
retronym
added a commit
that referenced
this pull request
Jun 26, 2013
Swathes of important logic are duplicated between `findMember` and `findMembers` after this run of optimization. d905558 Variation #10 to optimze findMember fcb0c01 Attempt #9 to opimize findMember. 71d2ceb Attempt #8 to opimize findMember. 77e5692 Attempty #7 to optimize findMember 275115e Fixing problem that caused fingerprints to fail in reflection. Also fixed test case that failed when moving to findMember e94252e Attemmpt #6 to optimize findMember 73e61b8 Attempt #5 to optimize findMember. 04f0b65 Attempt #4 to optimize findMember 0e3c70f Attempt #3 to optimize findMember 41f4497 Attempt #2 to optimize findMember 1a73aa0 Attempt #1 to optimize findMember This commit updates `findMembers` with the bug fixes that `findMember` has received, and adds flashing warning signs for future maintainers. A followup commit will address the duplication at the root.
retronym
pushed a commit
that referenced
this pull request
Sep 22, 2013
At this commit ant test-opt has two test failures: test/files/pos/javaReadsSigs [FAILED] test/files/run/t4238 [FAILED] Fix for wrong bytecode in forwarders. This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; Before/after this commit: < signature search (Ljava/lang/String;)LC<Ljava/lang/Object;>; --- > signature search (Ljava/lang/Object;)LC<Ljava/lang/Object;>; Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; Closes SI-3452.
retronym
pushed a commit
that referenced
this pull request
Oct 24, 2013
This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; This commit creates a bridge method to match the erased interface signature exactly. This forwards to a method with a more refined signature. Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; Closes SI-3452. fail
retronym
pushed a commit
that referenced
this pull request
Nov 14, 2013
When an application of a blackbox macro still has undetermined type parameters after Scala’s type inference algorithm has finished working, these type parameters are inferred forcedly, in exactly the same manner as type inference happens for normal methods. This makes it impossible for blackbox macros to influence type inference, prohibiting fundep materialization.
retronym
added a commit
that referenced
this pull request
Dec 11, 2013
In the enclosed test, we ended up relating the following types: tp1 = AnyRef{type Mem <: AnyRef{def x: Int}; type Memory#3 = this.Mem}" // #1 tp2 = AnyRef{type Mem <: AnyRef{def x: Int @ann}; type Memory#4 = this.Mem}" // #2 The first was the result of `Use.field`, in which the `asSeenFrom` had stripped the annotation. (`AsSeenFromMap` extends `KeepOnlyTypeConstraints`). The second type was the declared return type of the accessor method. Usually, `=:=` and `<:<` are annotation agnostic, and unless a plugin says otherwise, just compare the underlying types. But, when refinements are thrown into the mix, one has to wade through `specializesSym`. On the way, we get to: tp1 = Use.<refinement#1>.type tp2 = Use.<refinement#2>.type sym1 = type Memory#3 sym2 = type Memory#4 tp2.memberType(sym2).substThis(tp2.typeSymbol, tp1) =:= tp1.memberType(sym1) And finally down to business in `equalSymsAndPrefixes`. Here, we allow loose matching, based on symbol names, if the prefixes are =:= and represent a refined type (or a singleton type or type variable over a refined type.) But none of these cases covered what we encounter here: a this type over a refinement class. This commit adds that case to `isEligibleForPrefixUnification`.
xeno-by
referenced
this pull request
Dec 19, 2013
- Replace NoPosition with the focus of the macro application - Focus all range positions, for example, those of spliced arguments
retronym
pushed a commit
that referenced
this pull request
Dec 19, 2013
While fixing the problem with the order of typechecks for whitebox expansions, I realized that we’re doing redundant work when expanding blackbox macros. Concretely, typechecking blackbox expansions looked as follows: val expanded1 = atPos(enclosingMacroPosition.focus)(Typed(expanded0, TypeTree(innerPt))) val expanded2 = typecheck("blackbox typecheck #1", expanded1, innerPt) typecheck("blackbox typecheck #2", expanded1, outerPt) Or, if we reformulate it using quasiquotes (temporarily not taking positions into account, since they aren’t important here): val expanded2 = typed(q”$expanded: $innerPt”, innerPt) typed(expanded2, outerPt) In this formulation, it becomes apparent that the first typecheck is redundant. If something is ascribed with some type, then typechecking the ascription against that type does nothing useful. This is also highlights one of the reasons why it would be really nice to have quasiquotes used in the compiler. With them, it’s easy to notice things that would otherwise remain buried behind swaths of boilerplate.
retronym
added a commit
that referenced
this pull request
Jan 29, 2014
Exclude them from superclasses in `findMember` and in `OverridingPairs`. The odd logic in `findMember` that considered whether the selector class was owned by the owner of the candidate private symbol dates back to 2007 (bff4268), but does not appear to have any relationship to the spec. Refinement types are still able to inherit private members from all direct parents, as was needed in pos/t2399.scala. More tests are included for this scenario. In short, the logic now: - includes direct parents of refinements, - otherwise, excludes privates after the first class in the base class sequence TODO: Swathes of important logic are duplicated between `findMember` and `findMembers` after this run of optimization. d905558 Variation #10 to optimze findMember fcb0c01 Attempt #9 to opimize findMember. 71d2ceb Attempt #8 to opimize findMember. 77e5692 Attempty #7 to optimize findMember 275115e Fixing problem that caused fingerprints to fail in reflection. Als e94252e Attemmpt #6 to optimize findMember 73e61b8 Attempt #5 to optimize findMember. 04f0b65 Attempt #4 to optimize findMember 0e3c70f Attempt #3 to optimize findMember 41f4497 Attempt #2 to optimize findMember 1a73aa0 Attempt #1 to optimize findMember
retronym
added a commit
that referenced
this pull request
Jan 31, 2014
Swathes of important logic are duplicated between `findMember` and `findMembers` after they separated on grounds of irreconcilable differences about how fast they should run: d905558 Variation #10 to optimze findMember fcb0c01 Attempt #9 to opimize findMember. 71d2ceb Attempt #8 to opimize findMember. 77e5692 Attempty #7 to optimize findMember 275115e Fixing problem that caused fingerprints to fail in e94252e Attemmpt #6 to optimize findMember 73e61b8 Attempt #5 to optimize findMember. 04f0b65 Attempt #4 to optimize findMember 0e3c70f Attempt #3 to optimize findMember 41f4497 Attempt #2 to optimize findMember 1a73aa0 Attempt #1 to optimize findMember This didn't actually bear fruit, and the intervening years have seen the implementations drift. Now is the time to reunite them under the banner of `FindMemberBase`. Each has a separate subclass to customise the behaviour. This is primarily used by `findMember` to cache member types and to assemble the resulting list of symbols in an low-allocation manner. While there I have introduced some polymorphic calls, the call sites are only bi-morphic, and our typical pattern of compilation involves far more `findMember` calls, so I expect that JIT will keep the virtual call cost to an absolute minimum. Test results have been updated now that `findMembers` correctly excludes constructors and doesn't inherit privates. Coming up next: we can actually fix SI-7475!
retronym
added a commit
that referenced
this pull request
Jan 31, 2014
Swathes of important logic are duplicated between `findMember` and `findMembers` after they separated on grounds of irreconcilable differences about how fast they should run: d905558 Variation #10 to optimze findMember fcb0c01 Attempt #9 to opimize findMember. 71d2ceb Attempt #8 to opimize findMember. 77e5692 Attempty #7 to optimize findMember 275115e Fixing problem that caused fingerprints to fail in e94252e Attemmpt #6 to optimize findMember 73e61b8 Attempt #5 to optimize findMember. 04f0b65 Attempt #4 to optimize findMember 0e3c70f Attempt #3 to optimize findMember 41f4497 Attempt #2 to optimize findMember 1a73aa0 Attempt #1 to optimize findMember This didn't actually bear fruit, and the intervening years have seen the implementations drift. Now is the time to reunite them under the banner of `FindMemberBase`. Each has a separate subclass to customise the behaviour. This is primarily used by `findMember` to cache member types and to assemble the resulting list of symbols in an low-allocation manner. While there I have introduced some polymorphic calls, the call sites are only bi-morphic, and our typical pattern of compilation involves far more `findMember` calls, so I expect that JIT will keep the virtual call cost to an absolute minimum. Test results have been updated now that `findMembers` correctly excludes constructors and doesn't inherit privates. Coming up next: we can actually fix SI-7475!
retronym
added a commit
that referenced
this pull request
Jan 31, 2014
Swathes of important logic are duplicated between `findMember` and `findMembers` after they separated on grounds of irreconcilable differences about how fast they should run: d905558 Variation #10 to optimze findMember fcb0c01 Attempt #9 to opimize findMember. 71d2ceb Attempt #8 to opimize findMember. 77e5692 Attempty #7 to optimize findMember 275115e Fixing problem that caused fingerprints to fail in e94252e Attemmpt #6 to optimize findMember 73e61b8 Attempt #5 to optimize findMember. 04f0b65 Attempt #4 to optimize findMember 0e3c70f Attempt #3 to optimize findMember 41f4497 Attempt #2 to optimize findMember 1a73aa0 Attempt #1 to optimize findMember This didn't actually bear fruit, and the intervening years have seen the implementations drift. Now is the time to reunite them under the banner of `FindMemberBase`. Each has a separate subclass to customise the behaviour. This is primarily used by `findMember` to cache member types and to assemble the resulting list of symbols in an low-allocation manner. While there I have introduced some polymorphic calls, the call sites are only bi-morphic, and our typical pattern of compilation involves far more `findMember` calls, so I expect that JIT will keep the virtual call cost to an absolute minimum. Test results have been updated now that `findMembers` correctly excludes constructors and doesn't inherit privates. Coming up next: we can actually fix SI-7475!
retronym
added a commit
that referenced
this pull request
Feb 6, 2014
Swathes of important logic are duplicated between `findMember` and `findMembers` after they separated on grounds of irreconcilable differences about how fast they should run: d905558 Variation #10 to optimze findMember fcb0c01 Attempt #9 to opimize findMember. 71d2ceb Attempt #8 to opimize findMember. 77e5692 Attempty #7 to optimize findMember 275115e Fixing problem that caused fingerprints to fail in e94252e Attemmpt #6 to optimize findMember 73e61b8 Attempt #5 to optimize findMember. 04f0b65 Attempt #4 to optimize findMember 0e3c70f Attempt #3 to optimize findMember 41f4497 Attempt #2 to optimize findMember 1a73aa0 Attempt #1 to optimize findMember This didn't actually bear fruit, and the intervening years have seen the implementations drift. Now is the time to reunite them under the banner of `FindMemberBase`. Each has a separate subclass to customise the behaviour. This is primarily used by `findMember` to cache member types and to assemble the resulting list of symbols in an low-allocation manner. While there I have introduced some polymorphic calls, the call sites are only bi-morphic, and our typical pattern of compilation involves far more `findMember` calls, so I expect that JIT will keep the virtual call cost to an absolute minimum. Test results have been updated now that `findMembers` correctly excludes constructors and doesn't inherit privates. Coming up next: we can actually fix SI-7475!
retronym
pushed a commit
that referenced
this pull request
Feb 9, 2014
This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; This commit creates a bridge method to match the erased interface signature exactly. This forwards to a method with a more refined signature. Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; Closes SI-3452. fail
retronym
pushed a commit
that referenced
this pull request
Feb 9, 2014
This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; The principled thing to do here would be to create a pair of methods in the host class: a mixin forwarder with the erased signature `(String)C[Int]`, and a bridge method with the same erased signature as the trait interface facet. But, this turns out to be pretty hard to retrofit onto the current setup of Mixin and Erasure, mostly due to the fact that mixin happens after erasure which has already taken care of bridging. For a future, release, we should try to move all bridging after mixin, and pursue this approach. But for now, what can we do about `LinkageError`s for Java clients? This commit simply checks if the pre-erasure method signature that we generate for the trait forward erases identically to that of the interface method. If so, we can be precise. If not, we emit the erased signature as the generic signature. Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; Closes SI-3452. fail
retronym
pushed a commit
that referenced
this pull request
Feb 9, 2014
This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; The principled thing to do here would be to create a pair of methods in the host class: a mixin forwarder with the erased signature `(String)C[Int]`, and a bridge method with the same erased signature as the trait interface facet. But, this turns out to be pretty hard to retrofit onto the current setup of Mixin and Erasure, mostly due to the fact that mixin happens after erasure which has already taken care of bridging. For a future, release, we should try to move all bridging after mixin, and pursue this approach. But for now, what can we do about `LinkageError`s for Java clients? This commit simply checks if the pre-erasure method signature that we generate for the trait forward erases identically to that of the interface method. If so, we can be precise. If not, we emit the erased signature as the generic signature. Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; This takes the warning count for compiling collections under `-Ycheck:jvm` from 1521 to 26.
retronym
added a commit
that referenced
this pull request
Feb 9, 2014
[Parts of this patch and some of the commentary are from @paulp] This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; The principled thing to do here would be to create a pair of methods in the host class: a mixin forwarder with the erased signature `(String)C[Int]`, and a bridge method with the same erased signature as the trait interface facet. But, this turns out to be pretty hard to retrofit onto the current setup of Mixin and Erasure, mostly due to the fact that mixin happens after erasure which has already taken care of bridging. For a future, release, we should try to move all bridging after mixin, and pursue this approach. But for now, what can we do about `LinkageError`s for Java clients? This commit simply checks if the pre-erasure method signature that we generate for the trait forward erases identically to that of the interface method. If so, we can be precise. If not, we emit the erased signature as the generic signature. Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; This takes the warning count for compiling collections under `-Ycheck:jvm` from 1521 to 26.
retronym
added a commit
that referenced
this pull request
Feb 9, 2014
[Parts of this patch and some of the commentary are from @paulp] This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; The principled thing to do here would be to create a pair of methods in the host class: a mixin forwarder with the erased signature `(String)C[Int]`, and a bridge method with the same erased signature as the trait interface facet. But, this turns out to be pretty hard to retrofit onto the current setup of Mixin and Erasure, mostly due to the fact that mixin happens after erasure which has already taken care of bridging. For a future, release, we should try to move all bridging after mixin, and pursue this approach. But for now, what can we do about `LinkageError`s for Java clients? This commit simply checks if the pre-erasure method signature that we generate for the trait forward erases identically to that of the interface method. If so, we can be precise. If not, we emit the erased signature as the generic signature. Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; This takes the warning count for compiling collections under `-Ycheck:jvm` from 1521 to 26.
retronym
added a commit
that referenced
this pull request
Feb 9, 2014
[Parts of this patch and some of the commentary are from @paulp] This took me so long to figure out I can't even tell you. Partly because there were two different bugs, one which only arose for trait forwarders and one for mirror class forwarders, and every time I'd make one set of tests work another set would start failing. The runtime failures associated with these bugs were fairly well hidden because you usually have to go through java to encounter them: scala doesn't pay that much attention to generic signatures, so they can be wrong and scala might still generate correct code. But java is not so lucky. Bug #1) During mixin composition, classes which extend traits receive forwarders to the implementations. An attempt was made to give these the correct info (in method "cloneBeforeErasure") but it was prone to giving the wrong answer, because: the key attribute which the forwarder must capture is what the underlying method will erase to *where the implementation is*, not how it appears to the class which contains it. That means the signature of the forwarder must be no more precise than the signature of the inherited implementation unless additional measures will be taken. This subtle difference will put on an unsubtle show for you in test run/t3452.scala. trait C[T] trait Search[M] { def search(input: M): C[Int] = null } object StringSearch extends Search[String] { } StringSearch.search("test"); // java // java.lang.NoSuchMethodError: StringSearch.search(Ljava/lang/String;)LC; The principled thing to do here would be to create a pair of methods in the host class: a mixin forwarder with the erased signature `(String)C[Int]`, and a bridge method with the same erased signature as the trait interface facet. But, this turns out to be pretty hard to retrofit onto the current setup of Mixin and Erasure, mostly due to the fact that mixin happens after erasure which has already taken care of bridging. For a future, release, we should try to move all bridging after mixin, and pursue this approach. But for now, what can we do about `LinkageError`s for Java clients? This commit simply checks if the pre-erasure method signature that we generate for the trait forward erases identically to that of the interface method. If so, we can be precise. If not, we emit the erased signature as the generic signature. Bug #2) The same principle is at work, at a different location. During genjvm, objects without declared companion classes are given static forwarders in the corresponding class, e.g. object Foo { def bar = 5 } which creates these classes (taking minor liberties): class Foo$ { static val MODULE$ = new Foo$ ; def bar = 5 } class Foo { static def bar = Foo$.MODULE$.bar } In generating these, genjvm circumvented the usual process whereby one creates a symbol and gives it an info, preferring to target the bytecode directly. However generic signatures are calculated from symbol info (in this case reusing the info from the module class.) Lacking even the attempt which was being made in mixin to "clone before erasure", we would have runtime failures of this kind: abstract class Foo { type T def f(x: T): List[T] = List() } object Bar extends Foo { type T = String } Bar.f(""); // java // java.lang.NoSuchMethodError: Bar.f(Ljava/lang/String;)Lscala/collection/immutable/List; Before/after this commit: < signature f (Ljava/lang/String;)Lscala/collection/immutable/List<Ljava/lang/String;>; --- > signature f (Ljava/lang/Object;)Lscala/collection/immutable/List<Ljava/lang/Object;>; This takes the warning count for compiling collections under `-Ycheck:jvm` from 1521 to 26.
retronym
added a commit
that referenced
this pull request
Mar 1, 2015
Under `-Ydelambdafy:method`, a public, static accessor method is created to expose the private method containing the body of the lambda. Currently this accessor method has its parameters in the same order structure as those of the lambda body method. What is this order? There are three categories of parameters: 1. lambda parameters 2. captured parameters (added by lambdalift) 3. self parameters (added to lambda bodies that end up in trait impl classes by mixin, and added unconditionally to the static accessor method.) These are currently emitted in order #3, #1, #2. Here are examples of the current behaviour: BEFORE (trait): ``` % cat sandbox/test.scala trait Test { def member: A def foo { val local = new B (arg: C) => "" + arg + member + local } } % qscalac -Ydelambdafy:method sandbox/test.scala && echo ':javap -private Test$class' | qscala scala> :javap -private Test$class Compiled from "test.scala" public abstract class Test$class { public static void foo(Test); private static final java.lang.String $anonfun$1(Test, C, B); public static void $init$(Test); public static final java.lang.String accessor$1(Test, C, B); } ``` BEFORE (class): ``` % cat sandbox/test.scala abstract class Test { def member: A def foo { val local = new B (arg: C) => "" + arg + member + local } } % qscalac -Ydelambdafy:method sandbox/test.scala && echo ':javap -private Test' | qscala scala> :javap -private Test Compiled from "test.scala" public abstract class Test { public abstract A member(); public void foo(); private final java.lang.String $anonfun$1(C, B); public Test(); public static final java.lang.String accessor$1(Test, C, B); } ``` Contrasting the class case with Java: ``` % cat sandbox/Test.java public abstract class Test { public static class A {}; public static class B {}; public static class C {}; public static interface I { public abstract Object c(C arg); } public abstract A member(); public void test() { B local = new B(); I i1 = (C arg) -> "" + member() + local; } } % javac -d . sandbox/Test.java && javap -private Test Compiled from "Test.java" public abstract class Test { public Test(); public abstract Test$A member(); public void test(); private java.lang.Object lambda$test$0(Test$B, Test$C); } ``` We can see that in Java 8 lambda parameters come first. If we want to use Java's LambdaMetafactory to spin up our anoymous FunctionN subclasses on the fly, a signature must change. I can see three options for change: 1. Adjust `Mixin` to always append the `$this` parameter, rather than prepending it. 2. More conservatively, do this just for lambda bodies 3. Adjust the parameters of the accessor method only. The body of this method can permute params before calling the lambda body method. This commit implements option #3. This is the lowest risk / impact to pave the way for experimentation with indy lambdas. But it isn't ideal as a long term solution, as indy lambdas actually don't need the accessor method at all, private methods can be used directly by LambdaMetaFactory, saving a little indirection. Option #1 might be worth a shot on the 2.12.x branch. Option #2 might even be feasible on 2.11.x. I've included a test that shows this in all in action. However, that is currently disabled, as we don't have a partest category for tests that require Java 8.
retronym
added a commit
that referenced
this pull request
Mar 1, 2015
Under `-Ydelambdafy:method`, a public, static accessor method is created to expose the private method containing the body of the lambda. Currently this accessor method has its parameters in the same order structure as those of the lambda body method. What is this order? There are three categories of parameters: 1. lambda parameters 2. captured parameters (added by lambdalift) 3. self parameters (added to lambda bodies that end up in trait impl classes by mixin, and added unconditionally to the static accessor method.) These are currently emitted in order #3, #1, #2. Here are examples of the current behaviour: BEFORE (trait): ``` % cat sandbox/test.scala trait Test { def member: A def foo { val local = new B (arg: C) => "" + arg + member + local } } % qscalac -Ydelambdafy:method sandbox/test.scala && echo ':javap -private Test$class' | qscala scala> :javap -private Test$class Compiled from "test.scala" public abstract class Test$class { public static void foo(Test); private static final java.lang.String $anonfun$1(Test, C, B); public static void $init$(Test); public static final java.lang.String accessor$1(Test, C, B); } ``` BEFORE (class): ``` % cat sandbox/test.scala abstract class Test { def member: A def foo { val local = new B (arg: C) => "" + arg + member + local } } % qscalac -Ydelambdafy:method sandbox/test.scala && echo ':javap -private Test' | qscala scala> :javap -private Test Compiled from "test.scala" public abstract class Test { public abstract A member(); public void foo(); private final java.lang.String $anonfun$1(C, B); public Test(); public static final java.lang.String accessor$1(Test, C, B); } ``` Contrasting the class case with Java: ``` % cat sandbox/Test.java public abstract class Test { public static class A {}; public static class B {}; public static class C {}; public static interface I { public abstract Object c(C arg); } public abstract A member(); public void test() { B local = new B(); I i1 = (C arg) -> "" + member() + local; } } % javac -d . sandbox/Test.java && javap -private Test Compiled from "Test.java" public abstract class Test { public Test(); public abstract Test$A member(); public void test(); private java.lang.Object lambda$test$0(Test$B, Test$C); } ``` We can see that in Java 8 lambda parameters come first. If we want to use Java's LambdaMetafactory to spin up our anoymous FunctionN subclasses on the fly, a signature must change. I can see three options for change: 1. Adjust `Mixin` to always append the `$this` parameter, rather than prepending it. 2. More conservatively, do this just for lambda bodies 3. Adjust the parameters of the accessor method only. The body of this method can permute params before calling the lambda body method. This commit implements option #3. This is the lowest risk / impact to pave the way for experimentation with indy lambdas. But it isn't ideal as a long term solution, as indy lambdas actually don't need the accessor method at all, private methods can be used directly by LambdaMetaFactory, saving a little indirection. Option #1 might be worth a shot on the 2.12.x branch. Option #2 might even be feasible on 2.11.x. I've included a test that shows this in all in action. However, that is currently disabled, as we don't have a partest category for tests that require Java 8.
retronym
added a commit
that referenced
this pull request
Mar 4, 2015
Under `-Ydelambdafy:method`, a public, static accessor method is created to expose the private method containing the body of the lambda. Currently this accessor method has its parameters in the same order structure as those of the lambda body method. What is this order? There are three categories of parameters: 1. lambda parameters 2. captured parameters (added by lambdalift) 3. self parameters (added to lambda bodies that end up in trait impl classes by mixin, and added unconditionally to the static accessor method.) These are currently emitted in order #3, #1, #2. Here are examples of the current behaviour: BEFORE (trait): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . 'Test$class' trait Member; class Capture; trait LambdaParam trait Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test$class { public static void foo(Test); private static final java.lang.String $anonfun$1(Test, LambdaParam, Capture); public static void $init$(Test); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` BEFORE (class): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . Test trait Member; class Capture; trait LambdaParam abstract class Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test { public abstract Member member(); public void foo(); private final java.lang.String $anonfun$1(LambdaParam, Capture); public Test(); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` Contrasting the class case with Java: ``` % cat sandbox/Test.java && javac -d . sandbox/Test.java && javap -private -classpath . Test public abstract class Test { public static class Member {}; public static class Capture {}; public static class LambaParam {}; public static interface I { public abstract Object c(LambaParam arg); } public abstract Member member(); public void test() { Capture local = new Capture(); I i1 = (LambaParam arg) -> "" + member() + local; } } Compiled from "Test.java" public abstract class Test { public Test(); public abstract Test$Member member(); public void test(); private java.lang.Object lambda$test$0(Test$Capture, Test$LambaParam); } ``` We can see that in Java 8 lambda parameters come after captures. If we want to use Java's LambdaMetafactory to spin up our anoymous FunctionN subclasses on the fly, our ordering must change. I can see three options for change: 1. Adjust `Mixin` to always append the `$this` parameter, rather than prepending it. 2. More conservatively, do this just for lambda bodies 3. Adjust the parameters of the accessor method only. The body of this method can permute params before calling the lambda body method. This commit implements option #3. This is the lowest risk / impact to pave the way for experimentation with indy lambdas. But it isn't ideal as a long term solution, as indy lambdas actually don't need the accessor method at all, private methods can be used directly by LambdaMetaFactory, saving a little indirection. Option #1 might be worth a shot on the 2.12.x branch. Option #2 might even be feasible on 2.11.x. I've included a test that shows this in all in action. However, that is currently disabled, as we don't have a partest category for tests that require Java 8.
retronym
added a commit
that referenced
this pull request
Mar 4, 2015
Under `-Ydelambdafy:method`, a public, static accessor method is created to expose the private method containing the body of the lambda. Currently this accessor method has its parameters in the same order structure as those of the lambda body method. What is this order? There are three categories of parameters: 1. lambda parameters 2. captured parameters (added by lambdalift) 3. self parameters (added to lambda bodies that end up in trait impl classes by mixin, and added unconditionally to the static accessor method.) These are currently emitted in order #3, #1, #2. Here are examples of the current behaviour: BEFORE (trait): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . 'Test$class' trait Member; class Capture; trait LambdaParam trait Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test$class { public static void foo(Test); private static final java.lang.String $anonfun$1(Test, LambdaParam, Capture); public static void $init$(Test); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` BEFORE (class): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . Test trait Member; class Capture; trait LambdaParam abstract class Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test { public abstract Member member(); public void foo(); private final java.lang.String $anonfun$1(LambdaParam, Capture); public Test(); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` Contrasting the class case with Java: ``` % cat sandbox/Test.java && javac -d . sandbox/Test.java && javap -private -classpath . Test public abstract class Test { public static class Member {}; public static class Capture {}; public static class LambaParam {}; public static interface I { public abstract Object c(LambaParam arg); } public abstract Member member(); public void test() { Capture local = new Capture(); I i1 = (LambaParam arg) -> "" + member() + local; } } Compiled from "Test.java" public abstract class Test { public Test(); public abstract Test$Member member(); public void test(); private java.lang.Object lambda$test$0(Test$Capture, Test$LambaParam); } ``` We can see that in Java 8 lambda parameters come after captures. If we want to use Java's LambdaMetafactory to spin up our anoymous FunctionN subclasses on the fly, our ordering must change. I can see three options for change: 1. Adjust `LambdaLift` to always prepend captured parameters, rather than appending them. I think we could leave `Mixin` as it is, it already prepends the self parameter. This would result a parameter ordering, in terms of the list above: #3, #2, #1. 2. More conservatively, do this just for methods known to hold lambda bodies. This might avoid needlessly breaking code that has come to depend on our binary encoding. 3. Adjust the parameters of the accessor method only. The body of this method can permute params before calling the lambda body method. This commit implements option #3. This is the lowest risk / impact to pave the way for experimentation with indy lambdas. But it isn't ideal as a long term solution, as indy lambdas actually don't need the accessor method at all, private methods can be used directly by LambdaMetaFactory, saving a little indirection. Option #1 might be worth a shot on the 2.12.x branch. Option #2 might even be feasible on 2.11.x. I've included a test that shows this in all in action. However, that is currently disabled, as we don't have a partest category for tests that require Java 8.
retronym
added a commit
that referenced
this pull request
Mar 4, 2015
Under `-Ydelambdafy:method`, a public, static accessor method is created to expose the private method containing the body of the lambda. Currently this accessor method has its parameters in the same order structure as those of the lambda body method. What is this order? There are three categories of parameters: 1. lambda parameters 2. captured parameters (added by lambdalift) 3. self parameters (added to lambda bodies that end up in trait impl classes by mixin, and added unconditionally to the static accessor method.) These are currently emitted in order #3, #1, #2. Here are examples of the current behaviour: BEFORE (trait): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . 'Test$class' trait Member; class Capture; trait LambdaParam trait Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test$class { public static void foo(Test); private static final java.lang.String $anonfun$1(Test, LambdaParam, Capture); public static void $init$(Test); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` BEFORE (class): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . Test trait Member; class Capture; trait LambdaParam abstract class Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test { public abstract Member member(); public void foo(); private final java.lang.String $anonfun$1(LambdaParam, Capture); public Test(); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` Contrasting the class case with Java: ``` % cat sandbox/Test.java && javac -d . sandbox/Test.java && javap -private -classpath . Test public abstract class Test { public static class Member {}; public static class Capture {}; public static class LambaParam {}; public static interface I { public abstract Object c(LambaParam arg); } public abstract Member member(); public void test() { Capture local = new Capture(); I i1 = (LambaParam arg) -> "" + member() + local; } } Compiled from "Test.java" public abstract class Test { public Test(); public abstract Test$Member member(); public void test(); private java.lang.Object lambda$test$0(Test$Capture, Test$LambaParam); } ``` We can see that in Java 8 lambda parameters come after captures. If we want to use Java's LambdaMetafactory to spin up our anoymous FunctionN subclasses on the fly, our ordering must change. I can see three options for change: 1. Adjust `LambdaLift` to always prepend captured parameters, rather than appending them. I think we could leave `Mixin` as it is, it already prepends the self parameter. This would result a parameter ordering, in terms of the list above: #3, #2, #1. 2. More conservatively, do this just for methods known to hold lambda bodies. This might avoid needlessly breaking code that has come to depend on our binary encoding. 3. Adjust the parameters of the accessor method only. The body of this method can permute params before calling the lambda body method. This commit implements option #1. I've included a test that shows this in all in action. However, that is currently disabled, as we don't have a partest category for tests that require Java 8.
retronym
added a commit
that referenced
this pull request
Mar 6, 2015
Under `-Ydelambdafy:method`, a public, static accessor method is created to expose the private method containing the body of the lambda. Currently this accessor method has its parameters in the same order structure as those of the lambda body method. What is this order? There are three categories of parameters: 1. lambda parameters 2. captured parameters (added by lambdalift) 3. self parameters (added to lambda bodies that end up in trait impl classes by mixin, and added unconditionally to the static accessor method.) These are currently emitted in order #3, #1, #2. Here are examples of the current behaviour: BEFORE (trait): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . 'Test$class' trait Member; class Capture; trait LambdaParam trait Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test$class { public static void foo(Test); private static final java.lang.String $anonfun$1(Test, LambdaParam, Capture); public static void $init$(Test); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` BEFORE (class): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . Test trait Member; class Capture; trait LambdaParam abstract class Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test { public abstract Member member(); public void foo(); private final java.lang.String $anonfun$1(LambdaParam, Capture); public Test(); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` Contrasting the class case with Java: ``` % cat sandbox/Test.java && javac -d . sandbox/Test.java && javap -private -classpath . Test public abstract class Test { public static class Member {}; public static class Capture {}; public static class LambaParam {}; public static interface I { public abstract Object c(LambaParam arg); } public abstract Member member(); public void test() { Capture local = new Capture(); I i1 = (LambaParam arg) -> "" + member() + local; } } Compiled from "Test.java" public abstract class Test { public Test(); public abstract Test$Member member(); public void test(); private java.lang.Object lambda$test$0(Test$Capture, Test$LambaParam); } ``` We can see that in Java 8 lambda parameters come after captures. If we want to use Java's LambdaMetafactory to spin up our anoymous FunctionN subclasses on the fly, our ordering must change. I can see three options for change: 1. Adjust `LambdaLift` to always prepend captured parameters, rather than appending them. I think we could leave `Mixin` as it is, it already prepends the self parameter. This would result a parameter ordering, in terms of the list above: #3, #2, #1. 2. More conservatively, do this just for methods known to hold lambda bodies. This might avoid needlessly breaking code that has come to depend on our binary encoding. 3. Adjust the parameters of the accessor method only. The body of this method can permute params before calling the lambda body method. This commit implements option #1. We limit this change to non-constructors, to sidestep the need to make corresponding changes elsewhere in the compiler to avoid the crasher shown in the enclosed test case, which was minimized from a bootstrap failure from an earlier a version of this patch. I've included a test that shows this in all in action. However, that is currently disabled, as we don't have a partest category for tests that require Java 8.
retronym
added a commit
that referenced
this pull request
Mar 24, 2015
Under `-Ydelambdafy:method`, a public, static accessor method is created to expose the private method containing the body of the lambda. Currently this accessor method has its parameters in the same order structure as those of the lambda body method. What is this order? There are three categories of parameters: 1. lambda parameters 2. captured parameters (added by lambdalift) 3. self parameters (added to lambda bodies that end up in trait impl classes by mixin, and added unconditionally to the static accessor method.) These are currently emitted in order #3, #1, #2. Here are examples of the current behaviour: BEFORE (trait): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . 'Test$class' trait Member; class Capture; trait LambdaParam trait Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test$class { public static void foo(Test); private static final java.lang.String $anonfun$1(Test, LambdaParam, Capture); public static void $init$(Test); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` BEFORE (class): ``` % cat sandbox/test.scala && scalac-hash v2.11.5 -Ydelambdafy:method sandbox/test.scala && javap -private -classpath . Test trait Member; class Capture; trait LambdaParam abstract class Test { def member: Member def foo { val local = new Capture (arg: LambdaParam) => "" + arg + member + local } } Compiled from "test.scala" public abstract class Test { public abstract Member member(); public void foo(); private final java.lang.String $anonfun$1(LambdaParam, Capture); public Test(); public static final java.lang.String accessor$1(Test, LambdaParam, Capture); } ``` Contrasting the class case with Java: ``` % cat sandbox/Test.java && javac -d . sandbox/Test.java && javap -private -classpath . Test public abstract class Test { public static class Member {}; public static class Capture {}; public static class LambaParam {}; public static interface I { public abstract Object c(LambaParam arg); } public abstract Member member(); public void test() { Capture local = new Capture(); I i1 = (LambaParam arg) -> "" + member() + local; } } Compiled from "Test.java" public abstract class Test { public Test(); public abstract Test$Member member(); public void test(); private java.lang.Object lambda$test$0(Test$Capture, Test$LambaParam); } ``` We can see that in Java 8 lambda parameters come after captures. If we want to use Java's LambdaMetafactory to spin up our anoymous FunctionN subclasses on the fly, our ordering must change. I can see three options for change: 1. Adjust `LambdaLift` to always prepend captured parameters, rather than appending them. I think we could leave `Mixin` as it is, it already prepends the self parameter. This would result a parameter ordering, in terms of the list above: #3, #2, #1. 2. More conservatively, do this just for methods known to hold lambda bodies. This might avoid needlessly breaking code that has come to depend on our binary encoding. 3. Adjust the parameters of the accessor method only. The body of this method can permute params before calling the lambda body method. This commit implements option #2. In also prototyped #1, and found it worked so long as I limited it to non-constructors, to sidestep the need to make corresponding changes elsewhere in the compiler to avoid the crasher shown in the enclosed test case, which was minimized from a bootstrap failure from an earlier a version of this patch. We would need to defer option #1 to 2.12 in any case, as some of these lifted methods are publicied by the optimizer, and we must leave the signatures alone to comply with MiMa. I've included a test that shows this in all in action. However, that is currently disabled, as we don't have a partest category for tests that require Java 8.
retronym
added a commit
that referenced
this pull request
Aug 6, 2015
The log messages intented to chronicle implicit search were always being filtered out by virtue of the fact that the the tree passed to `printTyping` was already typed, (e.g. with an implicit MethodType.) This commit enabled printing in this case, although it still filters out trees that are deemed unfit for typer tracing, such as `()`. In the context of implicit search, this happens to filter out the noise of: ``` | | | [search #2] start `()`, searching for adaptation to pt=Unit => Foo[Int,Int] (silent: value <local Test> in Test) implicits disabled | | | [search #3] start `()`, searching for adaptation to pt=(=> Unit) => Foo[Int,Int] (silent: value <local Test> in Test) implicits disabled | | | \-> <error> ``` ... which I think is desirable. The motivation for this fix was to better display the interaction between implicit search and type inference. For instance: ``` class Foo[A, B] class Test { implicit val f: Foo[Int, String] = ??? def t[A, B](a: A)(implicit f: Foo[A, B]) = ??? t(1) } ``` ```` % scalac -Ytyper-debug sandbox/instantiate.scala ... | |-- t(1) BYVALmode-EXPRmode (site: value <local Test> in Test) | | |-- t BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Test> in Test) | | | [adapt] [A, B](a: A)(implicit f: Foo[A,B])Nothing adapted to [A, B](a: A)(implicit f: Foo[A,B])Nothing | | | \-> (a: A)(implicit f: Foo[A,B])Nothing | | |-- 1 BYVALmode-EXPRmode-POLYmode (site: value <local Test> in Test) | | | \-> Int(1) | | solving for (A: ?A, B: ?B) | | solving for (B: ?B) | | [search #1] start `[A, B](a: A)(implicit f: Foo[A,B])Nothing` inferring type B, searching for adaptation to pt=Foo[Int,B] (silent: value <local Test> in Test) implicits disabled | | [search #1] considering f | | [adapt] f adapted to => Foo[Int,String] based on pt Foo[Int,B] | | [search #1] solve tvars=?B, tvars.constr= >: String <: String | | solving for (B: ?B) | | [search #1] success inferred value of type Foo[Int,=?String] is SearchResult(Test.this.f, TreeTypeSubstituter(List(type B),List(String))) | | |-- [A, B](a: A)(implicit f: Foo[A,B])Nothing BYVALmode-EXPRmode (site: value <local Test> in Test) | | | \-> Nothing | | [adapt] [A, B](a: A)(implicit f: Foo[A,B])Nothing adapted to [A, B](a: A)(implicit f: Foo[A,B])Nothing | | \-> Nothing ```
retronym
added a commit
that referenced
this pull request
May 4, 2016
Motivation: - Avoid introducing public virtual methods. (javac uses private methods, but we prefer to make the public to support important AOT inlining use cases) - No more need for unsightly expanded names in lambda stack traces! - CHA in on HotSpot is great at devirtualizing, but that doesn't mean we *should* emit non-virtual methods as virtual so pervasively. ``` // Entering paste mode (ctrl-D to finish) package com.acme.wizzle.wozzle; class C { def foo = () => ??? } // Exiting paste mode, now interpreting. scala> new com.acme.wizzle.wozzle.C().foo res0: () => Nothing = com.acme.wizzle.wozzle.C$$Lambda$1986/43856716@100f9bbe scala> new com.acme.wizzle.wozzle.C().foo.apply() scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(Predef.scala:284) at com.acme.wizzle.wozzle.C.$anonfun$1(<pastie>:1) ... 30 elided scala> :paste -raw // Entering paste mode (ctrl-D to finish) package p1; class StaticAllTheThings { def foo = () => ""; def bar = () => foo; def baz = () => this } // Exiting paste mode, now interpreting. scala> :javap -private -c p1.StaticAllTheThings Compiled from "<pastie>" public class p1.StaticAllTheThings { public scala.Function0<java.lang.String> foo(); Code: 0: invokedynamic #38, 0 // InvokeDynamic #0:apply:()Lscala/Function0; 5: areturn public scala.Function0<scala.Function0<java.lang.String>> bar(); Code: 0: aload_0 1: invokedynamic #49, 0 // InvokeDynamic #1:apply:(Lp1/StaticAllTheThings;)Lscala/Function0; 6: areturn public scala.Function0<p1.StaticAllTheThings> baz(); Code: 0: aload_0 1: invokedynamic #58, 0 // InvokeDynamic #2:apply:(Lp1/StaticAllTheThings;)Lscala/Function0; 6: areturn public static final java.lang.String $anonfun$1(); Code: 0: ldc #60 // String 2: areturn public static final scala.Function0 $anonfun$2(p1.StaticAllTheThings); Code: 0: aload_0 1: invokevirtual #63 // Method foo:()Lscala/Function0; 4: areturn public static final p1.StaticAllTheThings $anonfun$3(p1.StaticAllTheThings); Code: 0: aload_0 1: areturn public p1.StaticAllTheThings(); Code: 0: aload_0 1: invokespecial #67 // Method java/lang/Object."<init>":()V 4: return private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda); Code: 0: aload_0 1: invokedynamic #79, 0 // InvokeDynamic #3:lambdaDeserialize:(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; 6: areturn } ```
retronym
added a commit
that referenced
this pull request
May 5, 2016
Motivation: - Avoid introducing public virtual methods. (javac uses private methods, but we prefer to make the public to support important AOT inlining use cases) - No more need for unsightly expanded names in lambda stack traces! - CHA in on HotSpot is great at devirtualizing, but that doesn't mean we *should* emit non-virtual methods as virtual so pervasively. ``` // Entering paste mode (ctrl-D to finish) package com.acme.wizzle.wozzle; class C { def foo = () => ??? } // Exiting paste mode, now interpreting. scala> new com.acme.wizzle.wozzle.C().foo res0: () => Nothing = com.acme.wizzle.wozzle.C$$Lambda$1986/43856716@100f9bbe scala> new com.acme.wizzle.wozzle.C().foo.apply() scala.NotImplementedError: an implementation is missing at scala.Predef$.$qmark$qmark$qmark(Predef.scala:284) at com.acme.wizzle.wozzle.C.$anonfun$1(<pastie>:1) ... 30 elided scala> :paste -raw // Entering paste mode (ctrl-D to finish) package p1; class StaticAllTheThings { def foo = () => ""; def bar = () => foo; def baz = () => this } // Exiting paste mode, now interpreting. scala> :javap -private -c p1.StaticAllTheThings Compiled from "<pastie>" public class p1.StaticAllTheThings { public scala.Function0<java.lang.String> foo(); Code: 0: invokedynamic #38, 0 // InvokeDynamic #0:apply:()Lscala/Function0; 5: areturn public scala.Function0<scala.Function0<java.lang.String>> bar(); Code: 0: aload_0 1: invokedynamic #49, 0 // InvokeDynamic #1:apply:(Lp1/StaticAllTheThings;)Lscala/Function0; 6: areturn public scala.Function0<p1.StaticAllTheThings> baz(); Code: 0: aload_0 1: invokedynamic #58, 0 // InvokeDynamic #2:apply:(Lp1/StaticAllTheThings;)Lscala/Function0; 6: areturn public static final java.lang.String $anonfun$1(); Code: 0: ldc #60 // String 2: areturn public static final scala.Function0 $anonfun$2(p1.StaticAllTheThings); Code: 0: aload_0 1: invokevirtual #63 // Method foo:()Lscala/Function0; 4: areturn public static final p1.StaticAllTheThings $anonfun$3(p1.StaticAllTheThings); Code: 0: aload_0 1: areturn public p1.StaticAllTheThings(); Code: 0: aload_0 1: invokespecial #67 // Method java/lang/Object."<init>":()V 4: return private static java.lang.Object $deserializeLambda$(java.lang.invoke.SerializedLambda); Code: 0: aload_0 1: invokedynamic #79, 0 // InvokeDynamic #3:lambdaDeserialize:(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object; 6: areturn } ```
retronym
added a commit
that referenced
this pull request
Aug 4, 2016
Dmitry learned that we've been relying on a bug in the verifier that will be fixed in JDK 9 under the new classfile format. Assignment to a static final must occur lexically within the <clinit>. We were performing this assignment from the constructor of the module class. This commit moves the assignment to <clinit>. Before: ``` public final class O$ { public static final O$ MODULE$; public static {}; Code: 0: new #2 // class O$ 3: invokespecial #12 // Method "<init>":()V 6: return private O$(); Code: 0: aload_0 1: invokespecial #13 // Method java/lang/Object."<init>":()V 4: aload_0 5: putstatic #15 // Field MODULE$:LO$; 8: return } ``` After: ``` public final class O$ { public static final O$ MODULE$; public static {}; Code: 0: new #2 // class O$ 3: dup 4: invokespecial #12 // Method "<init>":()V 7: putstatic #14 // Field MODULE$:LO$; 10: return private O$(); Code: 0: aload_0 1: invokespecial #15 // Method java/lang/Object."<init>":()V 4: return } ``` The difference is observable is the constructor is called a second time with reflection/setAccessible. That used to overwrite the `MODULE$` field. I think few would argue that the new semantics are a clear improvement.
retronym
added a commit
that referenced
this pull request
Aug 18, 2016
Top level modules in Scala currently desugar as: ``` class C; object O extends C { toString } ``` ``` public final class O$ extends C { public static final O$ MODULE$; public static {}; Code: 0: new #2 // class O$ 3: invokespecial #12 // Method "<init>":()V 6: return private O$(); Code: 0: aload_0 1: invokespecial #13 // Method C."<init>":()V 4: aload_0 5: putstatic #15 // Field MODULE$:LO$; 8: aload_0 9: invokevirtual #21 // Method java/lang/Object.toString:()Ljava/lang/String; 12: pop 13: return } ``` The static initalizer `<clinit>` calls the constructor `<init>`, which invokes superclass constructor, assigns `MODULE$= this`, and then runs the remainder of the object's constructor (`toString` in the example above.) It turns out that this relies on a bug in the JVM's verifier: assignment to a static final must occur lexically within the <clinit>, not from within `<init>` (even if the latter is happens to be called by the former). I'd like to move the assignment to <clinit> but that would change behaviour of "benign" cyclic references between modules. Example: ``` package p1; class CC { def foo = O.bar}; object O {new CC().foo; def bar = println(1)}; // Exiting paste mode, now interpreting. scala> p1.O 1 ``` This relies on the way that we assign MODULE$ field after the super class constructors are finished, but before the rest of the module constructor is called. Instead, this commit removes the ACC_FINAL bit from the field. It actually wasn't behaving as final at all, precisely the issue that the stricter verifier now alerts us to. ``` scala> :paste -raw // Entering paste mode (ctrl-D to finish) package p1; object O // Exiting paste mode, now interpreting. scala> val O1 = p1.O O1: p1.O.type = p1.O$@ee7d9f1 scala> scala.reflect.ensureAccessible(p1.O.getClass.getDeclaredConstructor()).newInstance() res0: p1.O.type = p1.O$@64cee07 scala> O1 eq p1.O res1: Boolean = false ``` We will still achieve safe publication of the assignment to other threads by virtue of the fact that `<clinit>` is executed within the scope of an initlization lock, as specified by: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5 Fixes scala/scala-dev#SD-194
retronym
added a commit
that referenced
this pull request
Aug 18, 2016
Top level modules in Scala currently desugar as: ``` class C; object O extends C { toString } ``` ``` public final class O$ extends C { public static final O$ MODULE$; public static {}; Code: 0: new #2 // class O$ 3: invokespecial #12 // Method "<init>":()V 6: return private O$(); Code: 0: aload_0 1: invokespecial #13 // Method C."<init>":()V 4: aload_0 5: putstatic #15 // Field MODULE$:LO$; 8: aload_0 9: invokevirtual #21 // Method java/lang/Object.toString:()Ljava/lang/String; 12: pop 13: return } ``` The static initalizer `<clinit>` calls the constructor `<init>`, which invokes superclass constructor, assigns `MODULE$= this`, and then runs the remainder of the object's constructor (`toString` in the example above.) It turns out that this relies on a bug in the JVM's verifier: assignment to a static final must occur lexically within the <clinit>, not from within `<init>` (even if the latter is happens to be called by the former). I'd like to move the assignment to <clinit> but that would change behaviour of "benign" cyclic references between modules. Example: ``` package p1; class CC { def foo = O.bar}; object O {new CC().foo; def bar = println(1)}; // Exiting paste mode, now interpreting. scala> p1.O 1 ``` This relies on the way that we assign MODULE$ field after the super class constructors are finished, but before the rest of the module constructor is called. Instead, this commit removes the ACC_FINAL bit from the field. It actually wasn't behaving as final at all, precisely the issue that the stricter verifier now alerts us to. ``` scala> :paste -raw // Entering paste mode (ctrl-D to finish) package p1; object O // Exiting paste mode, now interpreting. scala> val O1 = p1.O O1: p1.O.type = p1.O$@ee7d9f1 scala> scala.reflect.ensureAccessible(p1.O.getClass.getDeclaredConstructor()).newInstance() res0: p1.O.type = p1.O$@64cee07 scala> O1 eq p1.O res1: Boolean = false ``` We will still achieve safe publication of the assignment to other threads by virtue of the fact that `<clinit>` is executed within the scope of an initlization lock, as specified by: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5 Fixes scala/scala-dev#SD-194
retronym
added a commit
that referenced
this pull request
Oct 6, 2016
Manually tested with: ``` % cat sandbox/test.scala package p { object X { def f(i: Int) = ??? ; def f(s: String) = ??? } object Main { val res = X.f(3.14) } } % qscalac -Ytyper-debug sandbox/test.scala |-- p EXPRmode-POLYmode-QUALmode (site: package <root>) | \-> p.type |-- object X BYVALmode-EXPRmode (site: package p) | |-- super EXPRmode-POLYmode-QUALmode (silent: <init> in X) | | |-- this EXPRmode (silent: <init> in X) | | | \-> p.X.type | | \-> p.X.type | |-- def f BYVALmode-EXPRmode (site: object X) | | |-- $qmark$qmark$qmark EXPRmode (site: method f in X) | | | \-> Nothing | | |-- Int TYPEmode (site: value i in X) | | | \-> Int | | |-- Int TYPEmode (site: value i in X) | | | \-> Int | | \-> [def f] (i: Int)Nothing | |-- def f BYVALmode-EXPRmode (site: object X) | | |-- $qmark$qmark$qmark EXPRmode (site: method f in X) | | | \-> Nothing | | |-- String TYPEmode (site: value s in X) | | | [adapt] String is now a TypeTree(String) | | | \-> String | | |-- String TYPEmode (site: value s in X) | | | [adapt] String is now a TypeTree(String) | | | \-> String | | \-> [def f] (s: String)Nothing | \-> [object X] p.X.type |-- object Main BYVALmode-EXPRmode (site: package p) | |-- X.f(3.14) EXPRmode (site: value res in Main) | | |-- X.f BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value res in Main) | | | |-- X EXPRmode-POLYmode-QUALmode (silent: value res in Main) | | | | \-> p.X.type | | | \-> (s: String)Nothing <and> (i: Int)Nothing | | |-- 3.14 BYVALmode-EXPRmode (silent: value res in Main) | | | \-> Double(3.14) | | [search #1] start `<?>`, searching for adaptation to pt=Double => String (silent: value res in Main) implicits disabled | | [search #2] start `<?>`, searching for adaptation to pt=(=> Double) => String (silent: value res in Main) implicits disabled | | [search #3] start `<?>`, searching for adaptation to pt=Double => Int (silent: value res in Main) implicits disabled | | 1 implicits in companion scope | | [search #4] start `<?>`, searching for adaptation to pt=(=> Double) => Int (silent: value res in Main) implicits disabled | | 1 implicits in companion scope | | second try: <error> and 3.14 | | [search #5] start `p.X.type`, searching for adaptation to pt=p.X.type => ?{def f(x$1: ? >: Double(3.14)): ?} (silent: value res in Main) implicits disabled | | [search #6] start `p.X.type`, searching for adaptation to pt=(=> p.X.type) => ?{def f(x$1: ? >: Double(3.14)): ?} (silent: value res in Main) implicits disabled sandbox/test.scala:4: error: overloaded method value f with alternatives: (s: String)Nothing <and> (i: Int)Nothing cannot be applied to (Double) val res = X.f(3.14) ^ ```
retronym
added a commit
that referenced
this pull request
Oct 9, 2016
…lining ``` ⚡ qscalac -opt:l:classpath -d /tmp sandbox/test.scala && javap -v -cp /tmp Test | cat -b | grep -i --color '$anonfun$test$1' 26 #17 = Utf8 $anonfun$test$1 27 #18 = NameAndType #17:#13 // $anonfun$test$1:()V 28 #19 = InterfaceMethodref #2.#18 // Test.$anonfun$test$1:()V 59 #50 = MethodHandle #6:#19 // invokestatic Test.$anonfun$test$1:()V 96 0: invokestatic #19 // InterfaceMethod $anonfun$test$1:()V 140 public static void $anonfun$test$1(); 169 #50 invokestatic Test.$anonfun$test$1:()V ```
retronym
pushed a commit
that referenced
this pull request
Mar 29, 2017
The following commit message is a squash of several commit messages. - This is the 1st commit message: Add position to stub error messages Stub errors happen when we've started the initialization of a symbol but key information of this symbol is missing (the information cannot be found in any entry of the classpath not sources). When this error happens, we better have a good error message with a position to the place where the stub error came from. This commit goes into this direction by adding a `pos` value to `StubSymbol` and filling it in in all the use sites (especifically `UnPickler`). This commit also changes some tests that test stub errors-related issues. Concretely, `t6440` is using special Partest infrastructure and doens't pretty print the position, while `t5148` which uses the conventional infrastructure does. Hence the difference in the changes for both tests. - This is the commit message #2: Add partest infrastructure to test stub errors `StubErrorMessageTest` is the friend I introduce in this commit to help state stub errors. The strategy to test them is easy and builds upon previous concepts: we reuse `StoreReporterDirectTest` and add some methods that will compile the code and simulate a missing classpath entry by removing the class files from the class directory (the folder where Scalac compiles to). This first iteration allow us to programmatically check that stub errors are emitted under certain conditions. - This is the commit message #3: Improve contents of stub error message This commit does three things: * Keep track of completing symbol while unpickling First, it removes the previous `symbolOnCompletion` definition to be more restrictive/clear and use only positions, since only positions are used to report the error (the rest of the information comes from the context of the `UnPickler`). Second, it adds a new variable called `lazyCompletingSymbol` that is responsible for keeping a reference to the symbol that produces the stub error. This symbol will usually (always?) come from the classpath entries and therefore we don't have its position (that's why we keep track of `symbolOnCompletion` as well). This is the one that we have to explicitly use in the stub error message, the culprit so to speak. Aside from these two changes, this commit modifies the existing tests that are affected by the change in the error message, which is more precise now, and adds new tests for stub errors that happen in complex inner cases and in return type of `MethodType`. * Check that order of initialization is correct With the changes introduced previously to keep track of position of symbols coming from source files, we may ask ourselves: is this going to work always? What happens if two symbols the initialization of two symbols is intermingled and the stub error message gets the wrong position? This commit adds a test case and modifications to the test infrastructure to double check empirically that this does not happen. Usually, this interaction in symbol initialization won't happen because the `UnPickler` will lazily load all the buckets necessary for a symbol to be truly initialized, with the pertinent addresses from which this information has to be deserialized. This ensures that this operation is atomic and no other symbol initialization can happen in the meantime. Even though the previous paragraph is the feeling I got from reading the sources, this commit creates a test to double-check it. My attempt to be better safe than sorry. * Improve contents of the stub error message This commit modifies the format of the previous stub error message by being more precise in its formulation. It follows the structured format: ``` s"""|Symbol '${name.nameKind} ${owner.fullName}.$name' is missing from the classpath. |This symbol is required by '${lazyCompletingSymbol.kindString} ${lazyCompletingSymbol.fullName}'. ``` This format has the advantage that is more readable and explicit on what's happening. First, we report what is missing. Then, why it was required. Hopefully, people working on direct dependencies will find the new message friendlier. Having a good test suite to check the previously added code is important. This commit checks that stub errors happen in presence of well-known and widely used Scala features. These include: * Higher kinded types. * Type definitions. * Inheritance and subclasses. * Typeclasses and implicits. - This is the commit message #4: Use `lastTreeToTyper` to get better positions The previous strategy to get the last user-defined position for knowing what was the root cause (the trigger) of stub errors relied on instrumenting `def info`. This instrumentation, while easy to implement, is inefficient since we register the positions for symbols that are already completed. However, we cannot do it only for uncompleted symbols (!hasCompleteInfo) because the positions won't be correct anymore -- definitions using stub symbols (val b = new B) are for the compiler completed, but their use throws stub errors. This means that if we initialize symbols between a definition and its use, we'll use their positions instead of the position of `b`. To work around this we use `lastTreeToTyper`. We assume that stub errors will be thrown by Typer at soonest. The benefit of this approach is better error messages. The positions used in them are now as concrete as possible since they point to the exact tree that **uses** a symbol, instead of the one that **defines** it. Have a look at `StubErrorComplexInnerClass` for an example. This commit removes the previous infrastructure and replaces it by the new one. It also removes the fields positions from the subclasses of `StubSymbol`s. - This is the commit message #5: Keep track of completing symbols Make sure that cycles don't happen by keeping track of all the symbols that are being completed by `completeInternal`. Stub errors only need the last completing symbols, but the whole stack of symbols may be useful to reporting other error like cyclic initialization issues. I've added this per Jason's suggestion. I've implemented with a list because `remove` in an array buffer is linear. Array was not an option because I would need to resize it myself. I think that even though list is not as efficient memory-wise, it probably doesn't matter since the stack will usually be small. - This is the commit message #6: Remove `isPackage` from `newStubSymbol` Remove `isPackage` since in 2.12.x its value is not used.
retronym
pushed a commit
that referenced
this pull request
Apr 18, 2017
The following commit message is a squash of several commit messages. - This is the 1st commit message: Add position to stub error messages Stub errors happen when we've started the initialization of a symbol but key information of this symbol is missing (the information cannot be found in any entry of the classpath not sources). When this error happens, we better have a good error message with a position to the place where the stub error came from. This commit goes into this direction by adding a `pos` value to `StubSymbol` and filling it in in all the use sites (especifically `UnPickler`). This commit also changes some tests that test stub errors-related issues. Concretely, `t6440` is using special Partest infrastructure and doens't pretty print the position, while `t5148` which uses the conventional infrastructure does. Hence the difference in the changes for both tests. - This is the commit message #2: Add partest infrastructure to test stub errors `StubErrorMessageTest` is the friend I introduce in this commit to help state stub errors. The strategy to test them is easy and builds upon previous concepts: we reuse `StoreReporterDirectTest` and add some methods that will compile the code and simulate a missing classpath entry by removing the class files from the class directory (the folder where Scalac compiles to). This first iteration allow us to programmatically check that stub errors are emitted under certain conditions. - This is the commit message #3: Improve contents of stub error message This commit does three things: * Keep track of completing symbol while unpickling First, it removes the previous `symbolOnCompletion` definition to be more restrictive/clear and use only positions, since only positions are used to report the error (the rest of the information comes from the context of the `UnPickler`). Second, it adds a new variable called `lazyCompletingSymbol` that is responsible for keeping a reference to the symbol that produces the stub error. This symbol will usually (always?) come from the classpath entries and therefore we don't have its position (that's why we keep track of `symbolOnCompletion` as well). This is the one that we have to explicitly use in the stub error message, the culprit so to speak. Aside from these two changes, this commit modifies the existing tests that are affected by the change in the error message, which is more precise now, and adds new tests for stub errors that happen in complex inner cases and in return type of `MethodType`. * Check that order of initialization is correct With the changes introduced previously to keep track of position of symbols coming from source files, we may ask ourselves: is this going to work always? What happens if two symbols the initialization of two symbols is intermingled and the stub error message gets the wrong position? This commit adds a test case and modifications to the test infrastructure to double check empirically that this does not happen. Usually, this interaction in symbol initialization won't happen because the `UnPickler` will lazily load all the buckets necessary for a symbol to be truly initialized, with the pertinent addresses from which this information has to be deserialized. This ensures that this operation is atomic and no other symbol initialization can happen in the meantime. Even though the previous paragraph is the feeling I got from reading the sources, this commit creates a test to double-check it. My attempt to be better safe than sorry. * Improve contents of the stub error message This commit modifies the format of the previous stub error message by being more precise in its formulation. It follows the structured format: ``` s"""|Symbol '${name.nameKind} ${owner.fullName}.$name' is missing from the classpath. |This symbol is required by '${lazyCompletingSymbol.kindString} ${lazyCompletingSymbol.fullName}'. ``` This format has the advantage that is more readable and explicit on what's happening. First, we report what is missing. Then, why it was required. Hopefully, people working on direct dependencies will find the new message friendlier. Having a good test suite to check the previously added code is important. This commit checks that stub errors happen in presence of well-known and widely used Scala features. These include: * Higher kinded types. * Type definitions. * Inheritance and subclasses. * Typeclasses and implicits. - This is the commit message #4: Use `lastTreeToTyper` to get better positions The previous strategy to get the last user-defined position for knowing what was the root cause (the trigger) of stub errors relied on instrumenting `def info`. This instrumentation, while easy to implement, is inefficient since we register the positions for symbols that are already completed. However, we cannot do it only for uncompleted symbols (!hasCompleteInfo) because the positions won't be correct anymore -- definitions using stub symbols (val b = new B) are for the compiler completed, but their use throws stub errors. This means that if we initialize symbols between a definition and its use, we'll use their positions instead of the position of `b`. To work around this we use `lastTreeToTyper`. We assume that stub errors will be thrown by Typer at soonest. The benefit of this approach is better error messages. The positions used in them are now as concrete as possible since they point to the exact tree that **uses** a symbol, instead of the one that **defines** it. Have a look at `StubErrorComplexInnerClass` for an example. This commit removes the previous infrastructure and replaces it by the new one. It also removes the fields positions from the subclasses of `StubSymbol`s. - This is the commit message #5: Keep track of completing symbols Make sure that cycles don't happen by keeping track of all the symbols that are being completed by `completeInternal`. Stub errors only need the last completing symbols, but the whole stack of symbols may be useful to reporting other error like cyclic initialization issues. I've added this per Jason's suggestion. I've implemented with a list because `remove` in an array buffer is linear. Array was not an option because I would need to resize it myself. I think that even though list is not as efficient memory-wise, it probably doesn't matter since the stack will usually be small. - This is the commit message #6: Remove `isPackage` from `newStubSymbol` Remove `isPackage` since in 2.12.x its value is not used.
retronym
added a commit
that referenced
this pull request
Sep 21, 2018
``` /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/bin/java -Xmx1G -Xss1M "-javaagent:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/183.2407.10/IntelliJ IDEA 2018.3 EAP.app/Contents/lib/idea_rt.jar=60195:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/183.2407.10/IntelliJ IDEA 2018.3 EAP.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/tools.jar:/Users/jz/code/scala/build/quick/classes/compiler:/Users/jz/code/scala/build/quick/classes/library:/Users/jz/code/scala/build/quick/classes/reflect:/Users/jz/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar:/Users/jz/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar:/Users/jz/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-6.2.0-scala-2.jar:/Users/jz/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12/bundles/scala-xml_2.12-1.0.6.jar:/Users/jz/.ivy2/cache/jline/jline/jars/jline-2.14.6.jar scala.tools.nsc.PipelineMain /code/scala-2.12.x/target/compiler/compile.args /code/scala-2.12.x/target/interactive/compile.args /code/scala-2.12.x/target/library/compile.args /code/scala-2.12.x/target/partest-extras/compile.args /code/scala-2.12.x/target/reflect/compile.args /code/scala-2.12.x/target/repl-jline/compile.args /code/scala-2.12.x/target/repl/compile.args /code/scala-2.12.x/target/scaladoc/compile.args /code/scala-2.12.x/target/scalap/compile.args Round #1: /code/scala-2.12.x/target/library/compile.args warning: there were 37 deprecation warnings (since 2.10.0) warning: there were 24 deprecation warnings (since 2.11.0) warning: there were 46 deprecation warnings (since 2.12.0) warning: there were 107 deprecation warnings in total; re-run with -deprecation for details four warnings found Round #2: /code/scala-2.12.x/target/reflect/compile.args warning: there was one deprecation warning warning: there were three deprecation warnings (since 2.10.0) warning: there were four deprecation warnings (since 2.10.1) warning: there were 17 deprecation warnings (since 2.11.0) warning: there were 15 deprecation warnings (since 2.12.0) warning: there were 40 deprecation warnings in total; re-run with -deprecation for details warning: there were four unchecked warnings; re-run with -unchecked for details 7 warnings found Round #3: /code/scala-2.12.x/target/compiler/compile.args warning: there were two deprecation warnings warning: there were 12 deprecation warnings (since 2.10.0) warning: there were 55 deprecation warnings (since 2.11.0) warning: there were three deprecation warnings (since 2.11.2) warning: there were 26 deprecation warnings (since 2.12.0) warning: there was one deprecation warning (since 2.12.4) warning: there was one deprecation warning (since 2.12.5) warning: there were three deprecation warnings (since 2.12.7) warning: there were 103 deprecation warnings in total; re-run with -deprecation for details warning: there were 31 unchecked warnings; re-run with -unchecked for details warning: there were 6 feature warnings; re-run with -feature for details Round #4: /code/scala-2.12.x/target/interactive/compile.args 11 warnings found warning: there were four deprecation warnings (since 2.11.0) Round #4: /code/scala-2.12.x/target/scaladoc/compile.args warning: there was one deprecation warning (since 2.12.0) warning: there were 5 deprecation warnings in total; re-run with -deprecation for details warning: there were two unchecked warnings; re-run with -unchecked for details four warnings found Round #4: /code/scala-2.12.x/target/scalap/compile.args warning: there were two deprecation warnings (since 2.12.0); re-run with -deprecation for details warning: there were two unchecked warnings; re-run with -unchecked for details two warnings found warning: there was one deprecation warning (since 2.12.0); re-run with -deprecation for details Round #5: /code/scala-2.12.x/target/repl/compile.args one warning found warning: there were 10 deprecation warnings (since 2.11.0) Round #6: /code/scala-2.12.x/target/repl-jline/compile.args warning: there were three deprecation warnings (since 2.12.0) warning: there was one deprecation warning (since 2.9.0) warning: there were 14 deprecation warnings in total; re-run with -deprecation for details four warnings found Round #7: /code/scala-2.12.x/target/partest-extras/compile.args ```
retronym
added a commit
that referenced
this pull request
Sep 21, 2018
``` /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/bin/java -Xmx1G -Xss1M "-javaagent:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/183.2407.10/IntelliJ IDEA 2018.3 EAP.app/Contents/lib/idea_rt.jar=60195:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/183.2407.10/IntelliJ IDEA 2018.3 EAP.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/lib/tools.jar:/Users/jz/code/scala/build/quick/classes/compiler:/Users/jz/code/scala/build/quick/classes/library:/Users/jz/code/scala/build/quick/classes/reflect:/Users/jz/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.4.jar:/Users/jz/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.4.jar:/Users/jz/.ivy2/cache/org.scala-lang.modules/scala-asm/bundles/scala-asm-6.2.0-scala-2.jar:/Users/jz/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12/bundles/scala-xml_2.12-1.0.6.jar:/Users/jz/.ivy2/cache/jline/jline/jars/jline-2.14.6.jar scala.tools.nsc.PipelineMain /code/scala-2.12.x/target/compiler/compile.args /code/scala-2.12.x/target/interactive/compile.args /code/scala-2.12.x/target/library/compile.args /code/scala-2.12.x/target/partest-extras/compile.args /code/scala-2.12.x/target/reflect/compile.args /code/scala-2.12.x/target/repl-jline/compile.args /code/scala-2.12.x/target/repl/compile.args /code/scala-2.12.x/target/scaladoc/compile.args /code/scala-2.12.x/target/scalap/compile.args Round #1: /code/scala-2.12.x/target/library/compile.args warning: there were 37 deprecation warnings (since 2.10.0) warning: there were 24 deprecation warnings (since 2.11.0) warning: there were 46 deprecation warnings (since 2.12.0) warning: there were 107 deprecation warnings in total; re-run with -deprecation for details four warnings found Round #2: /code/scala-2.12.x/target/reflect/compile.args warning: there was one deprecation warning warning: there were three deprecation warnings (since 2.10.0) warning: there were four deprecation warnings (since 2.10.1) warning: there were 17 deprecation warnings (since 2.11.0) warning: there were 15 deprecation warnings (since 2.12.0) warning: there were 40 deprecation warnings in total; re-run with -deprecation for details warning: there were four unchecked warnings; re-run with -unchecked for details 7 warnings found Round #3: /code/scala-2.12.x/target/compiler/compile.args warning: there were two deprecation warnings warning: there were 12 deprecation warnings (since 2.10.0) warning: there were 55 deprecation warnings (since 2.11.0) warning: there were three deprecation warnings (since 2.11.2) warning: there were 26 deprecation warnings (since 2.12.0) warning: there was one deprecation warning (since 2.12.4) warning: there was one deprecation warning (since 2.12.5) warning: there were three deprecation warnings (since 2.12.7) warning: there were 103 deprecation warnings in total; re-run with -deprecation for details warning: there were 31 unchecked warnings; re-run with -unchecked for details warning: there were 6 feature warnings; re-run with -feature for details Round #4: /code/scala-2.12.x/target/interactive/compile.args 11 warnings found warning: there were four deprecation warnings (since 2.11.0) Round #4: /code/scala-2.12.x/target/scaladoc/compile.args warning: there was one deprecation warning (since 2.12.0) warning: there were 5 deprecation warnings in total; re-run with -deprecation for details warning: there were two unchecked warnings; re-run with -unchecked for details four warnings found Round #4: /code/scala-2.12.x/target/scalap/compile.args warning: there were two deprecation warnings (since 2.12.0); re-run with -deprecation for details warning: there were two unchecked warnings; re-run with -unchecked for details two warnings found warning: there was one deprecation warning (since 2.12.0); re-run with -deprecation for details Round #5: /code/scala-2.12.x/target/repl/compile.args one warning found warning: there were 10 deprecation warnings (since 2.11.0) Round #6: /code/scala-2.12.x/target/repl-jline/compile.args warning: there were three deprecation warnings (since 2.12.0) warning: there was one deprecation warning (since 2.9.0) warning: there were 14 deprecation warnings in total; re-run with -deprecation for details four warnings found Round #7: /code/scala-2.12.x/target/partest-extras/compile.args ```
retronym
pushed a commit
that referenced
this pull request
Jan 14, 2019
# This is the 1st commit message: Optimize BitSet#min and max for case of Ordering.Int # This is the commit message #2: Fix buggy bitset min and max implementations
retronym
pushed a commit
that referenced
this pull request
Jun 24, 2019
* origin/2.10.x: SI-6089 pt2: _ is tailpos in `_ || _` and `_ && _` Linked the PR policy in the README file. Scaladoc: Removing forgotten debugging info address "this would catch all throwables" warnings SI-1832 consistent symbols in casedef's pattern&body SIP-14 - Fix critical Java compatibility issue in scala.concurrent.Await Removes redundant outers Use `findMember` to lookup the static field in the host class. update docs for (partial) fun synth in uncurry SI-5999 a real fix to the packageless problem evicts calls to reify from our codebase an improvement based on Adriaan's comment SI-6031 customizable budget for patmat analyses SI-5739 address @retronym's feedback, more docs SI-5999 staticXXX is now friendly to packageless SI-5949 updates documentation of staticClass SI-5999 removes the macro context mirror more meaningful name for a missing hook method SI-5999 deploys a new starr SI-5999 removes Context.reify improves docs of scala.reflect.api.Mirrors SI-5984 improves error reporting in JavaMirrors SI-4897 derive expected value from single type Switch to 1.6 target for all javac invocations. Clean ups in impl.Future Critical bugfixes/leak fixes/API corrections + ScalaDoc for SIP-14 Print the stack trace. Deprecate all JVM 1.5 targets and make 1.6 default. Shield from InterruptedException in partest. Scaladoc: Adressed @hubertp's comment on scala#925 SI-5784 Scaladoc: Type templates Scaladoc: Groups Better debugging output in GenASM. Updated list of targets allowed in Ant's scalac. WIP add private/lazy checks and a few tests. Removes Float.Epsilon and Double.Epsilon SI-5939 resident compilation of sources in empty package Scaladoc: Typers change SI-6104 support This pattern Make field strict and private. Implement @static annotation on singleton object fields. Fixed SI-6092. Fixed leaky annotations, and relaxed the conditions under which a try-catch is lifted out to an inner method. Fix SI-5937. SI-6089 better tail position analysis for matches SI-5695 removes Context.enclosingApplication SI-5892 allow implicit views in annotation args SI-5739 store sub-patterns in local vals changes error message generated by compiler SI-5856 enables use of $this in string interpolation SI-5895 fixes FieldMirrors SI-5784 Scaladoc: {Abstract,Alias} type templates test case closes SI-6047 Fix for SI-5385. SI-6086 magic symbols strike back Scaladoc: Refactoring the entities SI-5533 Skip scaladoc packages from documentation Scaladoc: updated type class descriptions Scaladoc: Reducing the memory footprint 2 Scaladoc: Reducing the memory footprint SI-3695 SI-4224 SI-4497 SI-5079 scaladoc links SI-4887 Link existentials in scaladoc Scaladoc minor fix: Typos in diagrams SI-4360 Adds prefixes to scaladoc Scaladoc: workaround for untypical Map usecases SI-4324 Scaladoc case class argument currying SI-5558 Package object members indexing SI-5965 Scaladoc crash Scaladoc: Inherited templates in diagrams SI-3314 SI-4888 Scaladoc: Relative type prefixes SI-5235 Correct usecase variable expansion Variation #10 to optimze findMember Attempt #9 to opimize findMember. Attempt #8 to opimize findMember. Attempty #7 to optimize findMember Fixing problem that caused fingerprints to fail in reflection. Also fixed test case that failed when moving to findMembers. Avoids similar problems in the future by renaming nme.ANYNAME Attemmpt #6 to optimize findMember Attempt #5 to optimize findMember. Attempt #4 to optimize findMember Attempt #3 to optimize findMember Attempt #2 to optimize findMember Attempt #1 to optimize findMember Conflicts: test/files/run/existentials-in-compiler.scala
retronym
pushed a commit
that referenced
this pull request
Jun 24, 2019
First of all, GIL should only apply to runtime reflection, because noone is going to run toolboxes in multiple threads: a) that's impossible, b/c the compiler isn't thread safe, b) ToolBox api prevents that. Secondly, the only things in symbols which require synchronization are: 1) info/validTo (completers aren't thread-safe), 2) rawInfo and its dependencies (it shares a mutable field with info) 3) non-trivial caches like in typeAsMemberOfLock If you think about it, other things like sourceModule or associatedFile don't need synchronization, because they are either set up when a symbol is created or cloned or when it's completed. We can say that symbols can be in four possible states: 1) being created, 2) created, but not yet initialized, 3) initializing, 4) initialized. single thread. #2 and #4 don't need synchronization either, because the only mutation symbols in runtime reflection can undergo is init. #3 is dangerous and needs protection.
retronym
added a commit
that referenced
this pull request
Jun 24, 2019
Yin and yang would be pleased: A fix in two parts. 1. Use the name of the imported symbol, rather than the alias, in the generated Select(qual, name) tree. 2. Do the opposite in isQualifyingImplicit, which performs one part of the shadowing check. But there is still work to do. The second part of the shadowing check, nonImplicitSynonymInScope, fails to notice this case (irrespective of aliased imports). // Expecting shadowing #2. Alas, none is cast! object Test1 { object A { implicit val x: Int = 1 } import A.x def x: Int = 0 implicitly[Int] } I'm hitching the residual problem to SI-4270's wagon.
retronym
pushed a commit
that referenced
this pull request
Jun 24, 2019
First of all, GIL should only apply to runtime reflection, because noone is going to run toolboxes in multiple threads: a) that's impossible, b/c the compiler isn't thread safe, b) ToolBox api prevents that. Secondly, the only things in symbols which require synchronization are: 1) info/validTo (completers aren't thread-safe), 2) rawInfo and its dependencies (it shares a mutable field with info) 3) non-trivial caches like in typeAsMemberOfLock If you think about it, other things like sourceModule or associatedFile don't need synchronization, because they are either set up when a symbol is created or cloned or when it's completed. We can say that symbols can be in four possible states: 1) being created, 2) created, but not yet initialized, 3) initializing, 4) initialized. single thread. #2 and #4 don't need synchronization either, because the only mutation symbols in runtime reflection can undergo is init. #3 is dangerous and needs protection.
retronym
pushed a commit
that referenced
this pull request
Jun 24, 2019
retronym
pushed a commit
that referenced
this pull request
Jun 24, 2019
Modifications to local variables declarations
retronym
pushed a commit
that referenced
this pull request
Jun 24, 2019
When an application of a blackbox macro still has undetermined type parameters after Scala’s type inference algorithm has finished working, these type parameters are inferred forcedly, in exactly the same manner as type inference happens for normal methods. This makes it impossible for blackbox macros to influence type inference, prohibiting fundep materialization.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
a name instead.
case from
typer
topatmat
; this tree eluded theattribute reset performed in the macro. Instead, add it
to the match. Apart from making the tree re-typable, it
also exposes the true code structure to macros, which
is important if they need to perform other code
transformations.
TODO I could only get this through typechecking with
a cast in the default case.