| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Cannot subclass such a class. (Well, we could subclass a sealed
class in the same compilation unit. We ignore this for simplicity.)
This is a bit of a sneaky fix for this bug, but our hand
is pretty much forced by other constraints, in this intersection
of overload resolution involving built-in function types and SAMs,
and type inference for higher-order function literals (#5307).
Luckily, in this particular issue, the overloading clash seems
accidental. The `sealed` `<:<` class is not a SAM type as it
cannot be subclassed outside of `Predef`. For simplicity,
we don't consider where the SAM conversion occurs and exclude
all sealed classes from yielding SAM types.
Thanks to Miles for pointing out that `final` was missing in my
first iteration of this fix.
|
|\
| |
| | |
Emit local module like lazy val
|
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The motivation is to use the new fine-grained lock scoping that
local lazies have since #5294.
Fixes scala/scala-dev#235
Co-Authored-By: Jason Zaugg <jzaugg@gmail.com>
|
|\ \
| | |
| | |
| | |
| | | |
Fixes for DelayedInit classes capturing values
Fix scala/scala-dev#229
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
Constructors rewrites references to parameter accessor methods in the
constructor to references to parameters. It avoids doing so for
subclasses of DelayedInit.
This commit makes sure the rewrite does not happen for the $outer
paramter, a case that was simply forgotten.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
When a class captures an outer value, a field for that value is created
in the class. The class also gets a constructor parameter for the
captured value, the constructor will assign the field.
LambdaLift re-writes accesses to the local value (Ident trees) to the
field. However, if the statement accessing the local value will end up
inside the constructor, the access is re-written to the constructor
parameter instead. This is the case for constructor statements:
class C {
{
println(capturedLocal)
}
}
If C extends DelayedInit, the statement does not end up in C's
constructor, but into a new synthetic method. The access to
`capturedLocal` needs to be re-written to the field instead of the
constructor parameter.
LambdaLift takes the decision (field or constructor parameter) based on
the owner chain of `currentOwner`. For the constructor statement block,
the owner is a local dummy, for which `logicallyEnclosingMember` returns
the constructor symbol.
This commit introduces a special case in LambdaLift for local dummies
of DelayedInit subclasses: instead of the constructor, we use a
temporary symbol representing the synthetic method holding the
initializer statements.
|
|\ \ \
| |_|/
|/| | |
SI-9920 Avoid linkage errors with captured local objects + self types
|
| |/
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
An outer parameter of a nested class is typed with the self type
of the enclosing class:
```
class C; trait T { _: C => def x = 42; class D { x } }
```
leads to:
```
class D extends Object {
def <init>($outer: C): T.this.D = {
D.super.<init>();
()
};
D.this.$outer().$asInstanceOf[T]().x();
```
Note that a cast is inserted before the call to `x`.
If we modify that a little, to instead capture a local module:
```
class C; trait T { _: C => def y { object O; class D { O } } }
```
Scala 2.11 used to generate (after lambdalift):
```
class D$1 extends Object {
def <init>($outer: C, O$module$1: runtime.VolatileObjectRef): C#D$1 = {
D$1.super.<init>();
()
};
D$1.this.$outer().O$1(O$module$1);
```
That isn't type correct, `D$1.this.$outer() : C` does not
have a member `O$1`.
However, the old trait encoding would rewrite this in mixin to:
```
T$class.O$1($outer, O$module$1);
```
Trait implementation methods also used to accept the self type:
```
trait T$class {
final <stable> def O$1($this: C, O$module$1: runtime.VolatileObjectRef): T$O$2.type
}
```
So the problem was hidden.
This commit changes replaces manual typecheckin of the selection in LambdaLift with
a use of the local (erasure) typer, which will add casts as needed.
For `run/t9220.scala`, this changes the post LambdaLift AST as follows:
```
class C1$1 extends Object {
def <init>($outer: C0, Local$module$1: runtime.VolatileObjectRef): T#C1$1 = {
C1$1.super.<init>();
()
};
- C1$1.this.$outer.Local$1(Local$module$1);
+ C1$1.this.$outer.$asInstanceOf[T]().Local$1(Local$module$1);
<synthetic> <paramaccessor> <artifact> private[this] val $outer: C0 = _;
<synthetic> <stable> <artifact> def $outer(): C0 = C1$1.this.$outer
}
```
|
|\ \
| | |
| | | |
Avoid mismatched symbols in fields phase
|
| | |
| | |
| | |
| | | |
Also narrow scope of afterOwnPhase.
|
| |/
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The info of the var that stores a trait's lazy val's computed value
is expressed in terms of symbols that exist before the fields phase.
When we're implementing the lazy val in a subclass of that trait,
we now see symbols created by the fields phase, which results
in mismatches between the types of the lhs and rhs in the assignment
of `lazyVar = super.lazyImpl`.
So, type check the super-call to the trait's lazy accessor before our
own phase. If the lazy var's info depends on a val that is now
implemented by an accessor synthesize by our info transformer,
we'll get a mismatch when assigning `rhs` to `lazyVarOf(getter)`,
unless we also run before our own phase (like when we were
creating the info for the lazy var).
This was revealed by Hanns Holger Rutz's efforts in compiling
scala-refactoring's test suite (reported on scala-internals).
Fixes scala/scala-dev#219
|
|/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
GenBCode, the new backend in Scala 2.12, subtly changed
the way that synchronized blocks are emitted.
It used `java/lang/Throwable` as an explicitly named exception
type, rather than implying the same by omitting this in bytecode.
This appears to confuse HotSpot JIT, which reports a error
parsing the bytecode into its IR which leaves the enclosing method
stuck in interpreted mode.
This commit passes a `null` descriptor to restore the old pattern
(the same one used by javac.) I've checked that the JIT warnings
are gone and that the method can be compiled again.
|
|\
| |
| | |
Avoid omitting constant typed vals in constructors
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
Fix for regression in 2.12.0-RC1 compiling shapeless tests.
They were given the same treatment as vals that are
members of classes on the definition side without the
requisite transformation of references to the val to
fold the constant into references.
This commit limits the transform to members of classes.
Co-Authored-By: Miles Sabin <miles@milessabin.com>
|
|\ \
| | |
| | | |
SD-225 Use a "lzycompute" method for module initialization
|
| |/
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
The monitors and module instantation were inliuned into the module accessor
method in b2e0911. However, this seems to have had a detrimental impact on
performance. This might be because the module accessors are now above the
"always inline" HotSpot threshold of 35 bytes, or perhaps because they
contain monitor-entry/exit and exception handlers.
This commit returns to the the 2.11.8 appraoch of factoring
the the second check of the doublecheck locking into a method.
I've done this by declaring a nested method within the accessor;
this will be lifted out to the class level by lambdalift.
This represents a slight deviation from the implementation strategy used
for lazy accessors, which create a symbol for the slowpath method in
the info transform and generate the corresponding DefDef as a class
member. I don't believe this deviation is particular worrisome, though.
I have bootstrapped the compiler through this commit and found that
the drastic regression in compiling the shapeless test suite is solved.
|
|/ |
|
|\
| |
| |
| |
| | |
SD-208 Restore 2.11 names for arrayOps implicits
Fix scala/scala-dev#208
|
| | |
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| | |
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).
|
|\ \
| | |
| | | |
Fixes to mixin forwarders
|
| | | |
|
| | | |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|
| | | |
|
|\ \ \
| |/ /
|/| | |
Fields: expand lazy vals during fields, like modules
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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`.
|
| | | |
|
| | |
| | |
| | |
| | | |
Inline `mkSynchronizedCheck`, whose abstraction obscured rather than clarified.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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`.)
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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
|
| | |
| | |
| | |
| | | |
Final implementation based on feedback by Jason
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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
|
| |/
|/| |
|
|\ \
| | |
| | | |
Ensure trait var accessor type is widened
|
| |/
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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-8079 Only expand local aliases during variance checks
|
| | |
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| | |
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.
|
|\ \
| | |
| | | |
SI-5294 SI-6161 Hard graft in asSeenFrom, refinements, and existentials [ci: last-only]
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
- 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.
|
| | | |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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`.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | | |
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.
|