summaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
* Merge pull request #5368 from retronym/ticket/SD-208Adriaan Moors2016-09-062-32/+10
|\ | | | | | | | | SD-208 Restore 2.11 names for arrayOps implicits Fix scala/scala-dev#208
| * SD-208 Restore 2.11 names for arrayOps implicitsJason Zaugg2016-08-312-32/+10
| |
* | SD-143 allow super calls to methods defined in indirect super classesJason Zaugg2016-09-051-1/+7
| | | | | | | | | | | | | | | | The restriction for super calls to methods defined in indirect super classes introduced in a980fde was over-restrictive. In particular, it ruled out a valid code pattern to select a method from a superclass when an unqualified `super` would resolve to an override defined in a trait (example in the commit as a test).
* | Merge pull request #5311 from szeiger/wip/raidAdriaan Moors2016-09-0516-518/+0
|\ \ | | | | | | Remove ant
| * | Remove the ant buildStefan Zeiger2016-09-0216-518/+0
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - Remove ant scripts. - Remove shell scripts that were specific to the ant build or the old `*.desired.sha1` binary artifact management. - Remove `build.number`. - Remove `src/build/maven` and `src/build/bnd`. The POM and Manifest metadata is generated in a different way by sbt.
* | | More elegant holders for local lazy vals.Adriaan Moors2016-09-023-44/+144
|/ /
* | Merge pull request #5369 from lrytz/sd210Lukas Rytz2016-09-0212-68/+126
|\ \ | | | | | | Fixes to mixin forwarders
| * | Add a -Xmixin-force-forwarders ChoiceSettingLukas Rytz2016-09-023-5/+18
| | |
| * | Allow per-choice help in ChoiceSettingLukas Rytz2016-09-024-14/+29
| | |
| * | Allow all settings to be helpingLukas Rytz2016-09-014-5/+11
| | |
| * | Emit mixin forwarders for JUnit-annotated trait methods by defaultLukas Rytz2016-09-014-6/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | JUnit 4 does not support default methods. For better user experience, this commit makes the compiler generate mixin forwarders for inherited trait methods that carry a JUnit annotation. The -Yjunit-trait-methods-no-forwarders flag disables this behavior. This supersedes the scala-js/scala-2.12-junit-mixin-plugin compiler plugin.
| * | SD-143 error for super calls that cannot be implemented correctlyLukas Rytz2016-09-012-2/+17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If a call super[T].m resolves to a method A.m where A is a class, but not the direct superclass of the current class, there is no way to emit an invocation of A.m: `invokespecial A.m` will resolve to B.m where B is the superclass of the current class. This commit adds an error message in this case. Note that this is similar to an existing error message for qualified super calls to a non-direct superclass: class A { def m = 1 } class B extends A { override def m = 2 } class C extends B { override def m = super[A].m } Gives "error: A does not name a parent class of class C". If the user means to call method m in the superclass, he can write an unqualified `super.m`. An similar error message is introduced if A is a Java-defined interface (and m is a default method), and A is not a direct parent of the current class. In this case `invokespecial A.m` is invalid bytecode. The solution is to add A as a direct parent of the current class.
| * | SD-210 don't generate invalid forwarders under -Xgen-mixin-forwardersLukas Rytz2016-09-011-42/+46
| |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | With -Xgen-mixin-forwarders the compiler eagerly creates mixin forwarders for methods inherited from traits, even if the JVM method resolution would pick the correct default method. When generating a such a forwarder for a Java-defined default method, the mixin forwarder invokes the default method directly using `invokespecial` (for Scala-defined trait methods, the forwarder uses the static `m$` method that is generated for every trait method). An `invokespecial` call is only legal if the named interface is a direct parent of the current class. If this is not the case, we don't generate the mixin forwarder and emit a warning. In the tests there's also an example where a mixin forwarder is required for correctness, but cannot be added because the corresponding `invokespecial` call would be invalid. In this case we emit an error. This is similar to what we already do for other super calls to Java- defined default methods. The difference is that the super call is not written by the user but generated by the mixin forwarder. The solution is the same: add the interface as a direct parent.
* | Merge pull request #5294 from adriaanm/fields-laziesAdriaan Moors2016-09-0134-1460/+1246
|\ \ | |/ |/| Fields: expand lazy vals during fields, like modules
| * Lazy val without local.Adriaan Moors2016-09-011-10/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Now `synchronized` is erased specially to avoid boxing, we can drop that work around. Note that this does add an extra cast and getter call on the slow path, but that likely doesn't matter. ``` class C { def foo = {lazy val x = {println("a"); "A" }; x } } ``` becomes ``` def foo(): String = { lazy <artifact> val x$lzy: scala.runtime.LazyRef[String] = new scala.runtime.LazyRef[String](); <artifact> private def x$lzycompute(): String = x$lzy.synchronized[String]{ if (x$lzy.initialized()) x$lzy.value() // NOTE: gets an `.asInstanceOf[String]` after erasure else { x$lzy.value_=({ scala.Predef.println("a"); "A" }); x$lzy.initialized_=(true); x$lzy.value() // NOTE: gets an `.asInstanceOf[String]` after erasure } } lazy def x(): String = if (x$lzy.initialized()) x$lzy.value() // NOTE: gets an `.asInstanceOf[String]` after erasure else x$lzycompute(); x() } ```
| * Cleanups after integrating lazyvals into fields.Adriaan Moors2016-09-012-67/+76
| | | | | | | | | | | | | | | | | | | | Mostly refactorings and catching up with doc updates. Some changes to flag handling, removing some redundancy, and making lazy fields and modules a bit more consistent in their flags. They now uniformly carry LAZY or MODULEVAR. Before, LAZY was dropped because mixin had some lazy val logic. No longer.
| * Specialize erasure of `synchronized` primitive methodAdriaan Moors2016-08-303-14/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The goal is to avoid emitting unneeded `BoxedUnit` values, which are the result of adapting a `Unit`-typed expression inside a `synchronized(...)` to the erased type of `synchronized`'s argument -- `Object`. The proposed solution gives `synchronized` a polymorphic type (the info of the type param is still erased so that bounds checking works in the erased type system), so that an application `synchronized(println("boo"))` erases to `synchronized[Unit])(println("boo"))`, and no boxing is performed on the `println("boo")` argument, whose expected type is now `Unit` instead of `Object`.
| * Local lazy vals: avoid boxing and mutable captureAdriaan Moors2016-08-291-13/+32
| |
| * Double-checked locking for local lazy vals.Adriaan Moors2016-08-291-27/+36
| |
| * Double-checked locking for modules.Adriaan Moors2016-08-293-36/+33
| | | | | | | | Inline `mkSynchronizedCheck`, whose abstraction obscured rather than clarified.
| * Ensure access from subclass to trait lazy valAdriaan Moors2016-08-291-0/+5
| | | | | | | | | | | | | | | | | | | | | | Since we need to refer to a trait lazy val's accessor using a super call in a subclass (when the field and bitmap are added), we must ensure that access is allowed. If the lazy val has an access boundary (e.g., `private[somePkg]`), make sure the `PROTECTED` flag is set, which widens access to `protected[somePkg]`. (As `member.hasAccessBoundary` implies `!member.hasFlag(PRIVATE)`, we don't have to `resetFlag PRIVATE`.)
| * Move AccessorSynthesis out to its own fileAdriaan Moors2016-08-292-455/+466
| |
| * Fields does bitmaps & synch for lazy vals & modulesAdriaan Moors2016-08-2915-725/+509
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Essentially, we fuse mixin and lazyvals into the fields phase. With fields mixing in trait members into subclasses, we have all info needed to compute bitmaps, and thus we can synthesize the synchronisation logic as well. By doing this before erasure we get better signatures, and before specialized means specialized lazy vals work now. Mixins is now almost reduced to its essence: implementing super accessors and forwarders. It still synthesizes accessors for param accessors and early init trait vals. Concretely, trait lazy vals are mixed into subclasses with the needed synchronization logic in place, as do lazy vals in classes and methods. Similarly, modules are initialized using double checked locking. Since the code to initialize a module is short, we do not emit compute methods for modules (anymore). For simplicity, local lazy vals do not get a compute method either. The strange corner case of constant-typed final lazy vals is resolved in favor of laziness, by no longer assigning a constant type to a lazy val (see widenIfNecessary in namers). If you explicitly ask for something lazy, you get laziness; with the constant-typedness implicit, it yields to the conflicting `lazy` modifier because it is explicit. Co-Authored-By: Lukas Rytz <lukas@lightbend.com> Fixes scala/scala-dev#133 Inspired by dotc, desugar a local `lazy val x = rhs` into ``` val x$lzy = new scala.runtime.LazyInt() def x(): Int = { x$lzy.synchronized { if (!x$lzy.initialized) { x$lzy.initialized = true x$lzy.value = rhs } x$lzy.value } } ``` Note that the 2.11 decoding (into a local variable and a bitmap) also creates boxes for local lazy vals, in fact two for each lazy val: ``` def f = { lazy val x = 0 x } ``` desugars to ``` public int f() { IntRef x$lzy = IntRef.zero(); VolatileByteRef bitmap$0 = VolatileByteRef.create((byte)0); return this.x$1(x$lzy, bitmap$0); } private final int x$lzycompute$1(IntRef x$lzy$1, VolatileByteRef bitmap$0$1) { C c = this; synchronized (c) { if ((byte)(bitmap$0$1.elem & 1) == 0) { x$lzy$1.elem = 0; bitmap$0$1.elem = (byte)(bitmap$0$1.elem | 1); } return x$lzy$1.elem; } } private final int x$1(IntRef x$lzy$1, VolatileByteRef bitmap$0$1) { return (byte)(bitmap$0$1.elem & 1) == 0 ? this.x$lzycompute$1(x$lzy$1, bitmap$0$1) : x$lzy$1.elem; } ``` An additional problem with the above encoding is that the `lzycompute` method synchronizes on `this`. In connection with the new lambda encoding that no longer generates anonymous classes, captured lazy vals no longer synchronize on the lambda object. The new encoding solves this problem (scala/scala-dev#133) by synchronizing on the lazy holder. Currently, we don't exploit the fact that the initialized field is `@volatile`, because it's not clear the performance is needed for local lazy vals (as they are not contended, and as soon as the VM warms up, biased locking should deal with that) Note, be very very careful when moving to double-checked locking, as this needs a different variation than the one we use for class-member lazy vals. A read of a volatile field of a class does not necessarily impart any knowledge about a "subsequent" read of another non-volatile field of the same object. A pair of volatile reads and write can be used to implement a lock, but it's not clear if the complexity is worth an unproven performance gain. (Once the performance gain is proven, let's change the encoding.) - don't explicitly init bitmap in bytecode - must apply method to () explicitly after uncurry
| * Precompute bitmap info for lazy/init-checked valsAdriaan Moors2016-08-291-255/+256
| | | | | | | | | | | | Instead of doing this lazily, rework the logic to make it suitable for operating first on symbols (during the info transform), then on trees (tree transform).
| * [refactor] strictly reorder definitionsAdriaan Moors2016-08-291-456/+448
| |
| * [refactor] corral init bits some moreAdriaan Moors2016-08-291-355/+410
| | | | | | | | More code motion.
| * [refactor] corral checkinit logicAdriaan Moors2016-08-291-57/+51
| | | | | | | | Just moving some code around to make it comprehensible.
| * [refactor] lazy val expansion in mixins/lazyvalsAdriaan Moors2016-08-293-103/+68
| | | | | | | | Towards expanding lazy vals and modules during fields phase.
| * optimize/simplify erasure of class info typeAdriaan Moors2016-08-291-10/+9
| |
| * SI-8873 don't propagate primary ctor arg to case class's applyAdriaan Moors2016-08-292-0/+12
| | | | | | | | Final implementation based on feedback by Jason
| * Fields phase expands lazy vals like modulesAdriaan Moors2016-08-2916-470/+375
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | They remain ValDefs until then. - remove lazy accessor logic now that we have a single ValDef for lazy vals, with the underlying machinery being hidden until the fields phase leave a `@deprecated def lazyAccessor` for scala-refactoring - don't skolemize in purely synthetic getters, but *do* skolemize in lazy accessor during typers Lazy accessors have arbitrary user code, so have to skolemize. We exempt the purely synthetic accessors (`isSyntheticAccessor`) for strict vals, and lazy accessors emitted by the fields phase to avoid spurious type mismatches due to issues with existentials (That bug is tracked as https://github.com/scala/scala-dev/issues/165) When we're past typer, lazy accessors are synthetic, but before they are user-defined to make this hack less hacky, we could rework our flag usage to allow for requiring both the ACCESSOR and the SYNTHETIC bits to identify synthetic accessors and trigger the exemption. see also https://github.com/scala/scala-dev/issues/165 ok 7 - pos/existentials-harmful.scala ok 8 - pos/t2435.scala ok 9 - pos/existentials.scala previous attempt: skolemize type of val inside the private[this] val because its type is only observed from inside the accessor methods (inside the method scope its existentials are skolemized) - bean accessors have regular method types, not nullary method types - must re-infer type for param accessor some weirdness with scoping of param accessor vals and defs? - tailcalls detect lazy vals, which are defdefs after fields - can inline constant lazy val from trait - don't mix in fields etc for an overridden lazy val - need try-lift in lazy vals: the assign is not seen in uncurry because fields does the transform (see run/t2333.scala) - ensure field members end up final in bytecode - implicit class companion method: annot filter in completer - update check: previous error message was tangled up with unrelated field definitions (`var s` and `val s_scope`), now it behaves consistently whether those are val/vars or defs - analyzer plugin check update seems benign, but no way to know... - error message gen: there is no underlying symbol for a deferred var look for missing getter/setter instead - avoid retypechecking valdefs while duplicating for specialize see pos/spec-private - Scaladoc uniformly looks to field/accessor symbol - test updates to innerClassAttribute by Lukas
| * asmutilsAdriaan Moors2016-08-291-13/+33
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | decompile classfiles in parallel to aid in diffing bytecode between quick & strap ``` mkdir class-repo cd class-repo git init . ( cd .. ; sbt publishLocal ) v="2.12.0-local-$(g rev-parse --short HEAD)" ( cd ~/git/scala-2 ; sbt -Dstarr.version=$v compile ) for i in compiler interactive junit library partest-extras partest-javaagent reflect repl repl-jline repl-jline-embedded scaladoc scalap do cp -a ~/git/scala/build/quick/classes/$i . ~/git/scala/build/quick/bin/scala scala.tools.nsc.backend.jvm.AsmUtils $(find $i -name "*.class" ) g add $(find $i -name "*.asm" ) done g commit -m"quick" for i in compiler interactive junit library partest-extras partest-javaagent reflect repl repl-jline repl-jline-embedded scaladoc scalap do cp -a ~/git/scala-2/build/quick/classes/$i/* $i/ ~/git/scala/build/quick/bin/scala scala.tools.nsc.backend.jvm.AsmUtils $(find $i -name "*.class" ) done git --no-pager diff | mate ```
* | Merge pull request #5364 from retronym/topic/instanceof-perf-2Adriaan Moors2016-08-303-10/+21
|\ \ | | | | | | SI-9823 Collections perf: favor virtual call over instanceof
| * | SI-9823 Collections perf: favor virtual call over instanceofJason Zaugg2016-08-303-10/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This avoids concurrent usages of collections in NUMA architectures from falling of a performance cliff due to an implementation detail of interface instanceof checks in HotSpot JVM. The VM contains a one element cache in the metadata of each class to record the most recent successful test for a interface. For example: classOf[Serializable].isAssignableFrom(classOf[Some[_]]) Would, in addition to returning `true`, set: classOf[Some[_]]._secondary_super_cache = classOf[Serializable] This is done to hide the fact that interface tests are O(N), where N is the number of inherited interfaces. This scheme is discussed in "Fast Subtype Checking for the HotSpot JVM" (Click, Rose) [1] However, if this cache repeatedly misses, not only are we exposed to the linear search of the secondary super type array, but we are also required to write back to the cache. If other cores are operating on the same cache line, this can lead to a significant slowdown ("cache thrashing"). This effect will by most (or only?) visible on multi socket machines. The pathological case is: scala> Iterator.continually(List(classOf[Product], classOf[Serializable])).flatten.take(100).map(intf => intf.isAssignableFrom(classOf[Some[_]])).count(x => x) res19: Int = 100 Which, if run on a multi-socket machine, should be much slower than: scala> (Iterator.continually(classOf[Product]).take(50) ++ Iterator.continually(classOf[Serializable]).take(50)).map(intf => intf.isAssignableFrom(classOf[Some[_]])).count(x => x) res20: Int = 100 This commit avoids a interface test in a hot path in the collections by instead using virtual dispatch to differentiate between IndexedSeqLike and other collections. HotSpot will still use some shared bookkeeping ("inline cache" [2]) at the callsites of this method, but these stabilize in the megamorphic usage and no longer force expensive cache line synchronization. [1] https://www.researchgate.net/publication/221552851_Fast_subtype_checking_in_the_HotSpot_JVM [2] https://wiki.openjdk.java.net/display/HotSpot/PerformanceTechniques
* | | Merge pull request #5367 from adriaanm/fields-widen-trait-varStefan Zeiger2016-08-291-1/+2
|\ \ \ | | | | | | | | Ensure trait var accessor type is widened
| * | | Ensure trait var accessor type is widenedAdriaan Moors2016-08-291-1/+2
| | |/ | |/| | | | | | | | | | | | | | | | | | | | | | | | | If we don't widen, we'll fail to find the setter when typing `x = 42`, because `x` is constant-folded to `0`, as its type is `=> Int(0)`. After widening, `x` is type checked to `x` and its symbol is the getter in the trait, which can then be rewritten to the setter. Regression spotted and test case by szeiger.
* | | SI-6967 Primitive ClassTag.unapply is removedSom Snytt2016-08-291-15/+0
| | | | | | | | | | | | | | | Follow-up to remove the overloads, which is source compatible, in favor of unapply(Any).
* | | Merge pull request #5280 from retronym/ticket/8079Adriaan Moors2016-08-291-8/+12
|\ \ \ | |/ / |/| | SI-8079 Only expand local aliases during variance checks
| * | Address review feedback: comments, renames and extra testJason Zaugg2016-08-291-5/+7
| | |
| * | SI-8079 Only expand local aliases during variance checksJason Zaugg2016-08-181-4/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | We've been flip-flopping on this one through the years, right now we issue an two errors for the enclosed test. After this commit, variance validation only expands aliases that are `{private,protected}[this]`. The rest need not be expanded, as we have already variance validated the RHS of the alias. It also removes a seemingly incorrect check in `isLocalOnly`. This also means that we can use `@uncheckedVariance` to create variant type aliases for Java interfaces. However, if such a type alias is declared private local, it *will* be expanded. That shouldn't be a problem, other than for the fact that we run through an as-seen-from that strips the `@uV` annotations in the type expansion. This has been recorded in a pending test.
* | | clean up genprod, get rid of warning (#5361)Seth Tisue2016-08-291-10/+8
| | | | | | | | | | | | | | | No more "Selecting value MAX_ARITY from object genprod, which extends scala.DelayedInit, is likely to yield an uninitialized value" at start of every build.
* | | Merge pull request #5263 from retronym/review/5041Jason Zaugg2016-08-297-141/+214
|\ \ \ | | | | | | | | SI-5294 SI-6161 Hard graft in asSeenFrom, refinements, and existentials [ci: last-only]
| * | | Improve RefinementTypeRef#normalizeJason Zaugg2016-08-231-1/+1
| | | |
| * | | Minor changes after reviewJason Zaugg2016-08-232-3/+3
| | | |
| * | | Address review commentsJason Zaugg2016-08-232-38/+43
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - clarify the intent of tests - Consolidate stripExistentialsAndTypeVars with similar logic in mergePrefixAndArgs - Refactor special cases in maybeRewrap The name isn't great, but I'm struggling to come up with a pithy way to describe the rogue band of types.
| * | | Tone abort down to a dev warningJason Zaugg2016-08-231-1/+1
| | | |
| * | | SI-5294 Use bounds of abstract prefix in asSeenFromJason Zaugg2016-08-231-5/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ASF was failing to recognize the correspondence between a prefix if it has an abstract type symbol, even if it is bounded by the currently considered class. Distilling the test cases, this led to incorrect typechecking of the RHS of `G` in: ``` trait T { type A trait HasH { type H[U] <: U } type F[N <: HasH] = N#H[T] type G[N <: HasH] = F[N]#A // RHS was incorrectly reduced to T.this.A } ``` In the fuller examples (included as test cases), this meant that type level functions written as members of `HList` could not be implemented in terms of each other, e.g. defining `Apply[N]` as `Drop[N]#Head` had the wrong semantics. This commit checks checks if the prefix has the candidate class as a base type, rather than checking if its type symbol has this as a base class. The latter formulation discarded information about the instantation of the abstract type. Using the example above: ``` scala> val F = typeOf[T].member(TypeName("F")).info F: $r.intp.global.Type = [N <: T.this.HasH]N#H[T] scala> F.resultType.typeSymbol.baseClasses // old approach res14: List[$r.intp.global.Symbol] = List(class Any) scala> F.resultType.baseClasses // new approach res13: List[$r.intp.global.Symbol] = List(trait T, class Object, class Any) ``` It is worth noting that dotty rejects some of these programs, as it introduces the rule that: > // A type T is a legal prefix in a type selection T#A if > // T is stable or T contains no abstract types except possibly A. > final def isLegalPrefixFor(selector: Name)(implicit ctx: Context) However, typechecking the program above in this comment in dotty yields: <trait> trait T() extends Object { type A <trait> trait HasH() extends Object { type H <: [HK$0] => <: HK$0 } type F = [HK$0] => HK$0#H{HK$0 = T}#Apply type G = [HK$0] => HK$0#H{HK$0 = T}#Apply#A } As the equivalent code [1] in dotc's `asSeenFrom` already looks for a base type of the prefix, rather than looking for a superclass of the prefix's type symbol. [1] https://github.com/lampepfl/dotty/blob/d2c96d02fccef3a82b88ee1ff31253b6ef17f900/src/dotty/tools/dotc/core/TypeOps.scala#L62
| * | | Improved refinement type and existential type handlingJason Zaugg2016-08-234-103/+128
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Lazy base type seq elements are encoded as a refined type with an empty scope and a list of type refs over some common type symbol that will be merged when `BaseTypeSeq#apply` is called. The first change in this commit is to mark the creation and consumption of such elements with calls to `[is]IntersectionTypeForBaseTypeSeq`. They are distinguished by using the actual type symbol rather than a refinement class symbol, which in turn simplifies the code in `BaseTypeSeq#typeSymbol`. I have also made `lub` aware of this encoding: it is now able to "see through" to the parents of such refined types and merge them with other base types of the same class symbol (even other refined types representing lazy BTS elements.) To make this fix work, I also had to fix a bug in LUBs of multiple with existential types. Because of the way the recursion was structured in `mergePrefixAndArgs`, the order of list of types being merged changed behaviour: quantified varialbles of existential types were being rewrapped around the resultting type, but only if we hadn't encountered the first regular `TypeRef`. This can be seen with the following before/after shot: ``` // 2.11.8 scala> val ts = typeOf[Set[Any]] :: typeOf[Set[X] forSome { type X <: Y; type Y <: Int}] :: Nil; def merge(ts: List[Type]) = mergePrefixAndArgs(ts, Variance.Contravariant, lubDepth(ts)); val merged1 = merge(ts); val merged2 = merge(ts.reverse); (ts.forall(_ <:< merged1), ts.forall(_ <:< merged2)) ts: List[$r.intp.global.Type] = List(Set[Any], Set[_ <: Int]) merge: (ts: List[$r.intp.global.Type])$r.intp.global.Type merged1: $r.intp.global.Type = scala.collection.immutable.Set[_ >: Int] merged2: $r.intp.global.Type = scala.collection.immutable.Set[_53] forSome { type X <: Int; type _53 >: X } res0: (Boolean, Boolean) = (false,true) // HEAD ... merged1: $r.intp.global.Type = scala.collection.immutable.Set[_10] forSome { type X <: Int; type _10 >: X } merged2: $r.intp.global.Type = scala.collection.immutable.Set[_11] forSome { type X <: Int; type _11 >: X } res0: (Boolean, Boolean) = (true,true) ``` Furthermore, I have fixed the computation of the base type sequences of existential types over refinement types, in order to maintain the invariant that each slot of the base type sequence of a existential has the same type symbol as that of its underlying type. Before, what I've now called a `RefinementTypeRef` was transformed into a `RefinedType` during rewrapping in the existential, which led to it being wrongly considered as a lazy element of the base type sequence. The first change above should also be sufficient to avoid the bug, but I felt it was worth cleaning up `maybeRewrap` as an extra line of defence. Finally, I have added another special case to `BaseTypeSeq#apply` to be able to lazily compute elements that have been wrapped in an existential. The unit test cases in `TypesTest` rely on these changes. A subsequent commit will build on this foundation to make a fix to `asSeenFrom`.
| * | | Type#contains should peer into RefinementTypeRef-sJason Zaugg2016-08-192-6/+16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Usually, `contains` should not look into class symbol infos. For instance, we expect that: ``` scala> trait C { def foo: Int }; typeOf[C].contains(IntClass) defined trait C res1: Boolean = false ``` We do, however, look at the decls of a `RefinedType` in contains: ``` scala> typeOf[{ def foo: Int }].contains(IntClass) res2: Boolean = true ``` Things get a little vague, however, when we consider a type ref to the refinement class symbol of a refined type. ``` scala> TypeRef(NoPrefix, typeOf[{ def foo: Int }].typeSymbol, Nil) res3: $r.intp.global.Type = AnyRef{def foo: Int} scala> .contains(IntClass) res4: Boolean = false ``` These show up in the first element of the base type seq of a refined type, e.g: ``` scala> typeOf[{ def foo: Int }].typeSymbol.tpe_* res5: $r.intp.global.Type = AnyRef{def foo: Int} scala> typeOf[{ def foo: Int }].baseTypeSeq(0).getClass res7: Class[_ <: $r.intp.global.Type] = class scala.reflect.internal.Types$RefinementTypeRef scala> typeOf[{ def foo: Int }].typeSymbol.tpe_*.getClass res6: Class[_ <: $r.intp.global.Type] = class scala.reflect.internal.Types$RefinementTypeRef ``` This commit takes the opinion that a `RefinementTypeRef` should be transparent with respect to `contains`. This paves the way for fixing the base type sequences of existential types over refinement types. The implementation of `ContainsCollector` was already calling `normalize`, which goes from `RefinementTypeRef` to `RefinedType`. This commit maps over the result, which looks in the parents and decls.
| * | | Determistically enter classes from directory into package scopeJason Zaugg2016-08-191-3/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | On Linux, the directory listing is not automatically sorted on Mac. This leads to non-determistic ids of Symbols of the classes in a directory, which in turn leads to instability of the ordering of parents within inferred refinement types. Notable, with this patch, we will stably infer: ``` scala> case class C(); case class D(); List(C(), D()).head defined class C defined class D res0: Product with Serializable = C() ``` rather than sometimes getting `Serializable with Product` on Linux. As such, I've removed the workarounds for this instability in two test cases.