summaryrefslogtreecommitdiff
path: root/src/reflect
Commit message (Collapse)AuthorAgeFilesLines
* Merge pull request #5550 from retronym/ticket/3772Lukas Rytz2016-12-121-0/+28
|\ | | | | SI-3772 Fix detection of term-owned companions
| * Refactor companion lookup methods after code reviewJason Zaugg2016-11-291-6/+5
| |
| * SI-3772 Fix detection of term-owned companionsJason Zaugg2016-11-291-0/+29
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Companion detection consults the scopes of enclosing Contexts during typechecking to avoid the cycles that would ensue if we had to look at into the info of enclosing class symbols. For example, this used to typecheck: object CC { val outer = 42 } if ("".isEmpty) { case class CC(c: Int) CC.outer } This logic was not suitably hardened to find companions in exactly the same nesting level. After fixing this problem, a similar problem in `Namer::inCurrentScope` could be solved to be more selective about synthesizing a companion object. In particular, if a manually defined companion trails after the case class, don't create an addiotional synthetic comanpanion object.
* | Compiler support for JEP-193 VarHandle polymorphic signaturesJason Zaugg2016-12-122-4/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | VarHandles bring a host of new "polymorphic signature" methods to the Java 9 standard library. This commit updates the way we detect such methods to look at the absense/presense of the `PolymorphicSignature` annotation, so as to include these (and any future additions.) When we see applications of such methods, we disable adaptation of argument and return types. Tested manually with JDK9-ea: ``` Welcome to Scala 2.12.2-20161208-165912-3de1c0c (Java HotSpot(TM) 64-Bit Server VM, Java 9-ea). Type in expressions for evaluation. Or try :help. scala> import java.lang.invoke._, scala.runtime.IntRef import java.lang.invoke._ import scala.runtime.IntRef scala> val varHandle = MethodHandles.lookup().in(classOf[IntRef]).findVarHandle(classOf[IntRef], "elem", classOf[Int]); varHandle: java.lang.invoke.VarHandle = java.lang.invoke.VarHandleInts$FieldInstanceReadWrite@7112ce6 scala> varHandle.getAndSet(ref, 1): Int res5: Int = 0 scala> varHandle.getAndSet(ref, 1): Int res6: Int = 1 ``` Inspecting bytecode shows the absense of box/unboxing: ``` object Test { import java.lang.invoke._, scala.runtime.IntRef val varHandle = MethodHandles.lookup().in(classOf[IntRef]).findVarHandle(classOf[IntRef], "elem", classOf[Int]); def main(args: Array[String]): Unit = { val i : Int = varHandle.getAndSet(IntRef.zero, 1) } } ``` ``` public void main(java.lang.String[]); Code: 0: aload_0 1: invokevirtual #28 // Method varHandle:()Ljava/lang/invoke/VarHandle; 4: invokestatic #34 // Method scala/runtime/IntRef.zero:()Lscala/runtime/IntRef; 7: iconst_1 8: invokevirtual #40 // Method java/lang/invoke/VarHandle.getAndSet:(Lscala/runtime/IntRef;I)I 11: istore_2 12: return ```
* | Merge pull request #5573 from adriaanm/revert-part-5376Adriaan Moors2016-12-021-39/+34
|\ \ | | | | | | Remove existential infer hack enabled by #5376
| * | Revert existential infer part of #5376Adriaan Moors2016-12-011-39/+34
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | It wasn't a good idea after all. Also removed some tracing code that I cannot imagine was ever used in a production compiler. It's still just a recompile away. Fixes scala/scala-dev#280
* | | Support Java 9 modular runtime imagesJason Zaugg2016-12-021-1/+79
|/ / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | http://openjdk.java.net/jeps/220 changes the layout of the JDK to encapsulate the provided libraries with the new module system. This commit modifies the compiler's classpath implementation to scan the new location of these, the `jrt://` virtual filesystem. This might need to be adjusted once we provide a means for users to specify the subset of modules that they want to depend on, but for now reclaims the ground we lost. ``` ⚡ (java_use 9-ea; qscala) Welcome to Scala 2.12.0-20160908-223617-7e4ebda (Java HotSpot(TM) 64-Bit Server VM, Java 9-ea). Type in expressions for evaluation. Or try :help. scala> import StackWalker._, java.util.stream._, scala.collection.JavaConverters._ import StackWalker._ import java.util.stream._ import scala.collection.JavaConverters._ scala> (() => StackWalker.getInstance(java.util.EnumSet.of(Option.RETAIN_CLASS_REFERENCE)).walk[Seq[String]]((s: java.util.stream.Stream[StackFrame]) => s.iterator.asScala.take(3).map(_.toString).toList)).apply().mkString("\n") res0: String = .$anonfun$res0$1(<console>:21) .<init>(<console>:21) .<clinit>(<console>) scala> ``` I've marked the new class, `NioFile` as `private[scala]` to justify the forward compatibility whitelist entry. In principle we could use NioFile more widely rather than `PlainFile` I tried this out in https://github.com/retronym/scala/commit/b2d0a17a which passed CI. But to be conservative, I'm not submitting that change at this point.
* | Merge pull request #5284 from milessabin/topic/si-7046Adriaan Moors2016-11-304-1/+28
|\ \ | | | | | | | | | | | | | | | | | | | | | | | | | | | SI-7046 reflection doesn't see all knownDirectSubclasses This appears to do the right thing in the most typical scenarios in which `knownDirectSubclasses` would be used. The missing 5% is that subclasses defined in local scopes might not be seen by `knownDirectSubclasses` (see `Local` and `Riddle` in the test below). In mitigation, though, it is almost certain that a local subclass would represent an error in any scenario where `knownDirectSubclasses` might be used. Errors for such situations are reported by recording (via a symbol attachment) that `knownDirectSubclasses` has been called and reporting an error if any additional children are added subsequently. Despite these limitations and caveats, I believe that this represents a huge improvement over the status quo, and would eliminate 100% of the failures that I've seen in practice with people using shapeless for type class derivation.
| * | Partial fix for SI-7046Miles Sabin2016-11-284-1/+28
| | |
* | | Merge pull request #5376 from milessabin/topic/clean-experimentalAdriaan Moors2016-11-303-17/+10
|\ \ \ | | | | | | | | Clean up of code guarded by bare -Xexperimental
| * | | Typevar experimentals now default; t5729 pos -> neg.Miles Sabin2016-11-281-17/+8
| | | |
| * | | Pattern matching virtualization now guarded by -Yvirtpatmat.Miles Sabin2016-11-282-0/+2
| |/ /
* | | Merge pull request #5562 from retronym/ticket/SD-275Jason Zaugg2016-11-302-32/+14
|\ \ \ | | | | | | | | SD-275 Further harden against refs to absentee classes
| * | | SD-275 Remove obsolete code from the unpicklerJason Zaugg2016-11-291-27/+12
| | | | | | | | | | | | | | | | | | | | | | | | AFAICT, this was only needed to support pickle compatibility after the fix for SI-1591. We don't need to maintain the compatibility after incrementing our major version.
| * | | SD-275 Further harden against refs to absentee classesJason Zaugg2016-11-291-3/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - Limit the strategy of unpickling an external reference to a module class to a lookup of the module var to non-stub owners in order to enable fall through to stub symbol creation. Fixes scala/scala-dev#275
| * | | SI-8502 Rework handling of stub symbols in unpicklerJason Zaugg2016-11-292-6/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | - Rework previous fixes for SI-8502 to move the creation of a term or type stub symbol during unpickling to the initial point of stub creation, based on the tag. - Just set the PACKAGE flag on class stub symbols created during unpickling `ThisType`, rather than bothering with a different subclass of `StubSymbol` for (assumed) packages.
* | | | Merge pull request #5554 from retronym/ticket/10009Adriaan Moors2016-11-291-1/+2
|\ \ \ \ | | | | | | | | | | SI-10009 Fields survive untypecheck/retypecheck
| * | | | SI-10009 Fields survive untypecheck/retypecheckJason Zaugg2016-11-281-1/+2
| | |_|/ | |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Some places in the compiler, and many places in macros, use `untypecheck` (aka `resetAttrs`) to strip types and local symbols from a tree before retypechecking it under some different context. The refactoring of the desugaring of vals and vars in Scala 2.12.0 broke an assumption in this facility. When a ValDef must be split into multiple members (e.g. a field and a getter, or a perhaps also a setter), the ValDef that was parsed assumes the role of the `field`, and the trees for other members are stached by `Namer` to the `synthetics` map of the compilation unit, in order to spliced into the right statement list by typechecking. See `enterGetterSetter` for more details. However, the parsed ValDef is now used verbatim, carrying the meaning (ie, the symbol) of the `private[this]` field. This tree now had an inconsistency between the flags in `tree.mods.flags` and `tree.symbol.flags`. `tree.name` also differed from `tree.symbol.name` (the latter was renamed to be a local name, ie one with a trailing space.) When `ResetAttrs` stripped off the symbol and we retypechecked, we'd end up with two symbols in scope with the same name. In the first from the `run` test: ``` ================================================================================ { class a extends scala.AnyRef { def <init>(): a = { a.super.<init>(); () }; private[this] val x: Int = 42; <stable> <accessor> def x: Int = a.this.x }; new a() } { class a extends scala.AnyRef { def <init>() = { super.<init>(); () }; val x = 42; // oops, the name is "x" rather than "x " and we've missing `private[this]`! <stable> <accessor> def x: Int = a.this.x }; new a() } scala.tools.reflect.ToolBoxError: reflective typecheck has failed: x is already defined as value x ``` This commit uses the flags and name of the symbol in `typedValDef`. I've also had to modify the internals of `CodePrinter` to use the implicit, override, and deferred flags from the modifiers of an accessor when recovering pre-typer tree for a ValDef.
* | | | Merge pull request #5506 from retronym/topic/existential-idsAdriaan Moors2016-11-294-3/+17
|\ \ \ \ | |_|/ / |/| | | Avoid name table pollution with fresh existentials
| * | | Avoid name table pollution with fresh existentialsJason Zaugg2016-11-084-3/+17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | During large compilations runs, the large numbers of globally unique fresh names for existentials captured from prefixes of `asSeenFrom`. is a) somewhat wasteful (all these names are interned in the name table) , and, b) form a pathological case for the current implementation of `Names#hashValue`, which leads to overfull hash-buckets in the name table. `hashValue` should probably be improved, but my attempts to do so have shown a small performance degradation in some benchmarks. So this commit starts by being more frugal with these names, only uniquely naming within an `asSeenFrom` operation. References scala/scala-dev#246
* | | | SI-8779 Enable inlining of code within a REPL sessionJason Zaugg2016-11-281-13/+15
| |_|/ |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The REPL has a long running instance of Global which outputs classfiles by default to a VirtualDirectory. The inliner did not find any of these class files when compiling calls to methods defined in previous runs (ie, previous lines of input.) This commit: - Adds a hook to augment the classpath that the optimizer searches, and uses this in the REPL to add the output directory - Fixes the implementation of `findClassFile` in VirtualDirectory, which doesn't seem to have been used in anger before. I've factored out some common code into a new method on `AbstractFile`. - Fixes a similar problem getSubDir reported by Li Haoyi - Adds missing unit test coverage. This also fixes a bug in REPL autocompletion for types defined in packages >= 2 level deep (with the `:paste -raw` command). I've added a test for this case.
* | | Simplify creation of varargs forwarder symbolJason Zaugg2016-11-251-42/+26
| | | | | | | | | | | | | | | | | | Cloning the original symbol in its entirety, rather than cloning its type/value parameters individually. `cloneSymbol` takes care of all the tricky substitutions for us!
* | | Don’t run the uncurry info transformer on Java symbols.Iulian Dragos2016-11-251-3/+4
| | |
* | | SI-10071 Separate compilation for varargs methodsIulian Dragos2016-11-253-2/+85
| |/ |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Make sure that methods annotated with varargs are properly mixed-in. This commit splits the transformation into an info transformer (that works on all symbols, whether they come from source or binary) and a tree transformer. The gist of this is that the symbol-creation part of the code was moved to the UnCurry info transformer, while tree operations remained in the tree transformer. The newly created symbol is attached to the original method so that the tree transformer can still retrieve the symbol. A few fall outs: - I removed a local map that was identical to TypeParamsVarargsAttachment - moved the said attachment to StdAttachments so it’s visible between reflect.internal and nsc.transform - a couple more comments in UnCurry to honour the boy-scout rule
* | Merge pull request #5330 from som-snytt/issue/9885Jason Zaugg2016-11-181-9/+14
|\ \ | | | | | | SI-9885 Don't return offset past EOF
| * | SI-9885 Don't return offset past EOFSom Snytt2016-08-121-9/+14
| | | | | | | | | | | | | | | On bad line number, `lineToOffset` should not return an offset past EOF (which was sentinel, internally).
* | | Typo and spelling correctionsJanek Bogucki2016-11-118-9/+9
| | |
* | | Merge pull request #5303 from xuwei-k/isNaN-boxingJason Zaugg2016-11-111-2/+2
|\ \ \ | | | | | | | | avoid boxing
| * | | avoid boxingxuwei-k2016-10-281-2/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | scala.runtime.Rich{Double, Float} has `isNaN` and these are value class. Also java.lang.{Double, Float} has `isNaN`. - https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#isNaN-- - https://docs.oracle.com/javase/8/docs/api/java/lang/Float.html#isNaN-- We can't call `RichDouble#isNaN` because `implicit def double2Double(x: Double): java.lang.Double` is higher priority than `implicit def doubleWrapper(x: Double): RichDouble` ``` $ scala -version Scala code runner version 2.11.8 -- Copyright 2002-2016, LAMP/EPFL $ scala -Xprint:jvm -e "1.0.isNaN" [[syntax trees at end of jvm]] // scalacmd616162202928036892.scala package <empty> { object Main extends Object { def main(args: Array[String]): Unit = { new <$anon: Object>(); () }; def <init>(): Main.type = { Main.super.<init>(); () } }; final class anon$1 extends Object { def <init>(): <$anon: Object> = { anon$1.super.<init>(); scala.this.Predef.double2Double(1.0).isNaN(); () } } } ```
* | | | Manually inlined all other instances of Platform.arraycopy to System.arraycopyRex Kerr2016-11-092-3/+3
| |_|/ |/| | | | | | | | | | | to avoid the same kind of slowdowns that Vector was experiencing due to the less aggressive inlining by scalac.
* | | Address review commentsLukas Rytz2016-10-283-6/+7
| | | | | | | | | | | | | | | | | | | | | | | | Tighten some types (Symbol -> ClassSymbol / ModuleSymbol), use NonFatal instead of catching Throwable. Also don't run the classfile parser enteringPhase(phaseBeforeRefchecks) anymore. This was added in 0ccdb15 but seems no longer required.
* | | Minor style cleanups, no changes in logicLukas Rytz2016-10-273-31/+7
| | |
* | | Robustly identify unpickling the current module classLukas Rytz2016-10-271-11/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When unpickling a class, if the name and owner matches the current `classRoot` of the unpickling Scan, that `classRoot` symbol is used instead of creating a new symbol. If, in addition, the class being unpickled has the MODULE flag, the unpickler should use the `moduleRoot.moduleClass` symbol (instead of creating a new one). To identify the module class, the current implementation compares the name and owner to the `classRoot`. This fails in case the `classRoot` is `NoSymbol`, which can happen in corner cases (when a type alias shadows a class symbol, scala-dev#248). In this patch we identify the module class by comparing the name and owner to the `moduleRoot` symbol directly (using a `toTypeName`).
* | | Classfile parser and unpickler require class and module symbol argumentsLukas Rytz2016-10-271-2/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In SymbolLoaders, when seeing a classfile `Foo.class`, we always (unconditionally) create 3 symbols: a class, a module and a module class. Some symbols get invalidated later (`.exists`). Until now, the classfile parser (and unpickler) received the "root" symbol as argument, which is the symbol whose type is being completed. This is either the class symbol or the module symbol. The classfile parser would then try to lookup the other symbol through `root.companionClass` or `root.companionModule`. Howver, this lookup can fail. One example is scala-dev#248: when a type alias (in a package object) shadows a class symbol, `companionClass` will fail. The implementations of the classfile parser / unpickler assume that both the `clazz` and the `staticModule` symbols are available. This change makes sure that they are always passed in explicitly. Before this patch, in the example of scala-dev#248, the `classRoot` of the unpickler was NoSymbol. This caused a bug when unpickling the module class symbol, causing a second module class symbol to be created mistakingly. The next commit cleans up this logic, more details there. This second symbol would then cause the crash in the backend because it doesn't have an `associatedFile`, therefore `isCoDefinedWith` would spuriously return `true`.
* | | Don't follow type aliases in getClassByName and friendsLukas Rytz2016-10-261-1/+0
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This makes getClassByName fail / getClassIfDefined return NoSymbol when querying an alias. The current behavior can confuse the classfile parser: when parsing a class, a cross-check verifies that `pool.getClassSymbol(nameIdx)` returns the symbol of the class currently being parsed. If there's a type alias that shadows the linked class, following the alias would return an unrelated class. (The cross-check didn't fail because there are some other guards around it) The logic to follow aliases was was added in ff98878, without a clear explanation. Note that `requiredClass[C]` works if `C` is an alias, it is expanded by the compiler.
* | | Ensure companionClass returns a class, not a type aliasLukas Rytz2016-10-261-3/+1
|/ / | | | | | | | | | | | | | | | | | | | | | | This fixes scala/scala-dev#248, where a type alias reached the backend through this method. This is very similar to the fix for SI-5031, which changed it only in ModuleSymbol, but not in Symbol. The override in ModuleSymbol is actually unnecessary (it's identical), so it's removed in this commit. It was added for unclear reasons in 296b706.
* | Merge pull request #5442 from adriaanm/t9943Adriaan Moors2016-10-111-2/+2
|\ \ | | | | | | SI-9943 sealed class does not yield SAM type
| * | SI-9943 final/sealed class does not yield SAM typeAdriaan Moors2016-10-041-2/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* | | SI-9946 don't null field in lazy accessors that turn out to be liveJason Zaugg2016-10-071-1/+1
|/ / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If a non-transient lazy val is the only user of a private field in a class, the field is nulled out at the end of the lazy initializer. This is tested in the existing test `run/lazy-leaks.scala`. The analysis of which fields could be nulled out was recently moved from `mixin` to the new `fields` phase. This introduced a regression as a reference from an inner- or companion-classes had not yet been processed by `explicitouter` to publicise private fields. This commit delays the analysis to mixin (after explicit outer has done its work.) Navigating from `foo$lzycompute()` to `foo()` to `foo` is a little dirty now. I'm not sure whether there is a more robust way to structure things.
* | Merge pull request #5430 from adriaanm/dev235Adriaan Moors2016-09-291-9/+10
|\ \ | | | | | | Emit local module like lazy val
| * | Make some name suffixes constantsAdriaan Moors2016-09-281-9/+10
| | | | | | | | | | | | | | | | | | There's still a lot of duplication, as well as plenty of opportunities for constant folding / simplification.
* | | SI-9697 / SD-229 Fix DelayedInit subclass capturing local valueLukas Rytz2016-09-281-1/+0
|/ / | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* | More elegant holders for local lazy vals.Adriaan Moors2016-09-021-0/+1
| |
* | Merge pull request #5369 from lrytz/sd210Lukas Rytz2016-09-022-2/+3
|\ \ | | | | | | Fixes to mixin forwarders
| * | Emit mixin forwarders for JUnit-annotated trait methods by defaultLukas Rytz2016-09-012-2/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
* | | Merge pull request #5294 from adriaanm/fields-laziesAdriaan Moors2016-09-017-43/+64
|\ \ \ | |/ / |/| | Fields: expand lazy vals during fields, like modules
| * | Specialize erasure of `synchronized` primitive methodAdriaan Moors2016-08-301-12/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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`.
| * | Fields does bitmaps & synch for lazy vals & modulesAdriaan Moors2016-08-294-2/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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
| * | optimize/simplify erasure of class info typeAdriaan Moors2016-08-291-10/+9
| | |
| * | Fields phase expands lazy vals like modulesAdriaan Moors2016-08-293-19/+25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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