diff options
135 files changed, 2272 insertions, 5312 deletions
diff --git a/.gitignore b/.gitignore index 0fff976e90..d9710a65cc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ # # -# JARs aren't checked in, they are fetched by Ant / pull_binary_libs.sh +# JARs aren't checked in, they are fetched by sbt # # We could be more concise with /lib/**/*.jar but that assumes # a late-model git. @@ -22,23 +22,20 @@ /test/files/speclib/instrumented.jar /tools/*.jar -# Developer specific Ant properties +# Developer specific properties /build.properties /buildcharacter.properties # might get generated when testing Jenkins scripts locally /jenkins.properties -# target directories for ant build -/build +# target directory for build /build/ -/dists/ # other /out/ /bin/ /sandbox/ -/.ant-targets-build.xml # eclipse, intellij /.classpath @@ -59,3 +56,4 @@ /project/project/target/ /project/project/project/target/ /build-sbt/ +local.sbt @@ -43,8 +43,6 @@ P.S.: If you have some spare time to help out around here, we would be delighted ``` scala/ +--build.sbt The main sbt build script -+--build.xml The deprecated Ant build script -+--pull-binary-libs.sh Pulls binary artifacts from remote repository, used by build scripts +--lib/ Pre-compiled libraries for the build +--src/ All sources +---/library Scala Standard Library @@ -65,13 +63,11 @@ scala/ ## Requirements You need the following tools: - - A Java SDK. The baseline version is 6 for 2.11.x, 8 for 2.12.x. It's possible - to use a later SDK for local development, but the CI will verify against the baseline - version. - - sbt, we recommend the [sbt-extras](https://github.com/paulp/sbt-extras) runner - script. It provides sensible default jvm options (stack and heap size). - - curl (for `./pull-binary-libs.sh`, used by the sbt / ant build). - - Apache Ant (version 1.9.3 or above) if you need to use the (deprecated) ant build. + - Java SDK. The baseline version is 8 for 2.12.x. It may be possible to use a + later SDK for local development, but the CI will verify against the baseline + version. + - sbt. We recommend the [sbt-extras](https://github.com/paulp/sbt-extras) runner + script. It provides sensible default jvm options (stack and heap size). Mac OS X and Linux work. Windows may work if you use Cygwin. Community help with keeping the build working on Windows is appreciated. @@ -80,32 +76,25 @@ the build working on Windows is appreciated. ### Basics -Scala is built in layers, where each layer is a complete Scala compiler and library. -Here is a short description of the layers, from bottom to top: - - - `starr`: the stable reference Scala release. We use an official release of - Scala (specified by `starr.version` in [versions.properties](versions.properties)), - downloaded from the Central Repository. - - `locker` (deprecated, only in ant): an intermediate layer that existed in the - ant build to perform a bootstrap. - - `quick`: the development layer which is incrementally built when working on - changes in the compiler or library. - - `strap` (deprecated, only in ant) : a test layer used to check stability of - the build. - -The sbt build uses `starr` to build `quick`. This is sufficient for most development -scenarios: changes to the library or the compiler can be tested by running the `quick` -Scala (see below for how to do that). - -However, a full build of Scala (a *bootstrap*, as performed by our CI) requires two -layers. This guarantees that every Scala version can build itself. If you change the -code generation part of the Scala compiler, your changes will only reflect in the -bytecode of the library and compiler after a bootstrap. See below for how to create -a bootstrap build locally. +During ordinary development, a new Scala build is built by the +previously released version. For short we call the previous release +"starr": the stable reference Scala release. Building with starr is +sufficient for most kinds of changes. + +However, a full build of Scala (a *bootstrap*, as performed by our CI) +requires two layers. This guarantees that every Scala version can +build itself. If you change the code generation part of the Scala +compiler, your changes will only show up in the bytecode of the +library and compiler after a bootstrap. See below for how to do a +bootstrap build locally. + +For history on how the current scheme was arrived at, see +https://groups.google.com/d/topic/scala-internals/gp5JsM1E0Fo/discussion. ### Using the Sbt Build Core commands: + - `compile` compiles all sub-projects (library, reflect, compiler, scaladoc, etc) - `scala` / `scalac` run the REPL / compiler directly from sbt (accept options / arguments) @@ -115,7 +104,7 @@ Core commands: - `partest` runs partest tests (accepts options, try `partest --help`) - `publishLocal` publishes a distribution locally (can be used as `scalaVersion` in other sbt projects) - - Optionally `set VersionUtil.baseVersionSuffix in Global := "abcd123-SNAPSHOT"` + - Optionally `set baseVersionSuffix := "abcd123-SNAPSHOT"` where `abcd123` is the git hash of the revision being published. You can also use something custom like `"mypatch"`. This changes the version number from `2.12.0-SNAPSHOT` to something more stable (`2.12.0-abcd123-SNAPSHOT`). @@ -133,13 +122,13 @@ Note that sbt's incremental compilation is often too coarse for the Scala compil codebase and re-compiles too many files, resulting in long build times (check [sbt#1104](https://github.com/sbt/sbt/issues/1104) for progress on that front). In the meantime you can: - - Enable "ant mode" in which sbt only re-compiles source files that were modified. + - Enable "Ant mode" in which sbt only re-compiles source files that were modified. Create a file `local.sbt` containing the line `antStyle := true`. Add an entry `local.sbt` to your `~/.gitignore`. - Use IntelliJ IDEA for incremental compiles (see [IDE Setup](#ide-setup) below) - its incremental compiler is a bit less conservative, but usually correct. -#### Local Bootstrap Build +#### Bootstrapping Locally To perform a bootstrap using sbt - first a build is published either locally or on a temporary repository, @@ -176,7 +165,7 @@ In order to use IntelliJ's incremental compiler: Now you can edit and build in IntelliJ and use the scripts (compiler, REPL) to directly test your changes. You can also run the `scala`, `scalac` and `partest` -commands in sbt. Enable "ant mode" (explained above) to prevent sbt's incremental +commands in sbt. Enable "Ant mode" (explained above) to prevent sbt's incremental compiler from re-compiling (too many) files before each `partest` invocation. # Coding Guidelines diff --git a/build-ant-macros.xml b/build-ant-macros.xml deleted file mode 100644 index e077cfbb4c..0000000000 --- a/build-ant-macros.xml +++ /dev/null @@ -1,832 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project name="build-support" xmlns:artifact="urn:maven-artifact-ant"> - <description> Macros for Scala's ant build </description> - - <macrodef name="optimized"> - <attribute name="name"/> - <sequential> - <antcall target="@{name}"> - <param name="scalac.args.optimise" value="-opt:l:classpath"/> - </antcall> - </sequential> - </macrodef> - - <macrodef name="copy-deps" description="Copy a file set based on maven dependency resolution to a directory. Currently used by the IntelliJ config files."> - <attribute name="project"/> - <attribute name="refid" default="@{project}.fileset"/> - <sequential> - <delete dir="${build-deps.dir}/@{project}" includes="*.jar"/> - <copy todir="${build-deps.dir}/@{project}"> - <resources refid="@{refid}"/> - <mapper type="flatten"/> - </copy> - </sequential> - </macrodef> - - <!-- Set a property @{name}.cross to the actual cross suffix that should be - used when resolving the module "@{name}". If the (user-supplied) - @{name}.cross.suffix property exists then use that value, otherwise use - "_${scala.binary.version}". --> - <macrodef name="prepareCross"> - <attribute name="name" /> - <sequential> - <if> - <isset property="@{name}.cross.suffix" /> - <then> - <property name="@{name}.cross" value="${@{name}.cross.suffix}" /> - </then> - <else> - <property name="@{name}.cross" value="_${scala.binary.version}" /> - </else> - </if> - </sequential> - </macrodef> - - <!-- Set property named @{name} to the jar resolved as @{jar}_${scala.binary.version}:jar. - @{jar}_${scala.binary.version} must be a maven dependency. --> - <macrodef name="propertyForCrossedArtifact"> - <attribute name="name"/> - <attribute name="jar"/> - <attribute name="suffix" default="${@{name}.cross}"/> - <sequential> - <readProperty name="@{name}" property="@{jar}@{suffix}:jar"/> - <readProperty name="@{name}-sources" property="@{jar}@{suffix}:java-source:sources"/> - <readProperty name="@{name}-javadoc" property="@{jar}@{suffix}:java-source:javadoc"/> - </sequential> - </macrodef> - - <!-- Set property named @{name} to the value of the property named @{property}. - Helper for performing nested property expansion without using the ant props lib --> - <macrodef name="readProperty"> - <attribute name="name"/> - <attribute name="property"/> - <sequential> - <property name="@{name}" value="${@{property}}"/> - </sequential> - </macrodef> - - <macrodef name="init-project-prop"> - <attribute name="project"/> - <attribute name="name"/> - <attribute name="default"/> - <sequential> - <local name="@{name}"/> - <if> - <not> - <isset property="@{project}.@{name}"/> - </not> - <then> - <property name="@{project}.@{name}" value="@{default}"/> - </then> - </if> - </sequential> - </macrodef> - - <macrodef name="clean"> - <attribute name="build"/> - <sequential> - <delete dir="${build-@{build}.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/> - </sequential> - </macrodef> - - <macrodef name="simple-javac"> - <attribute name="project"/> - <attribute name="args" default=""/> - <attribute name="jar" default="yes"/> - <sequential> - <uptodate property="@{project}.available" targetfile="${build-libs.dir}/@{project}.complete"> - <srcfiles dir="${src.dir}/@{project}"/> - </uptodate> - <if> - <not> - <isset property="@{project}.available"/> - </not> - <then> - <stopwatch name="@{project}.timer"/> - <mkdir dir="${@{project}-classes}"/> - <javac debug="true" srcdir="${src.dir}/@{project}" destdir="${@{project}-classes}" classpath="${@{project}-classes}" includes="**/*.java" target="1.8" source="1.8" compiler="javac1.8"> - <compilerarg line="${javac.args} @{args}"/> - </javac> - <if> - <equals arg1="@{jar}" arg2="yes"/> - <then> - <jar whenmanifestonly="fail" destfile="${build-libs.dir}/@{project}.jar" basedir="${@{project}-classes}"/> - </then> - </if> - <stopwatch name="@{project}.timer" action="total"/> - <mkdir dir="${build-libs.dir}"/> - <touch file="${build-libs.dir}/@{project}.complete" verbose="no"/> - </then> - </if> - </sequential> - </macrodef> - - <macrodef name="staged-javac"> - <attribute name="stage"/> - <!-- current stage (locker, quick, strap) --> - <attribute name="project"/> - <!-- project: library/reflect/compiler --> - <attribute name="destproject" default="@{project}"/> - <!-- overrides the output directory; used when building multiple projects into the same directory--> - <attribute name="args" default=""/> - <attribute name="excludes" default=""/> - <sequential> - <javac debug="true" srcdir="${src.dir}/@{project}" destdir="${build-@{stage}.dir}/classes/@{destproject}" includes="**/*.java" excludes="@{excludes}" target="1.8" source="1.8"> - <compilerarg line="${javac.args} @{args}"/> - <classpath refid="@{stage}.@{destproject}.build.path"/> - </javac> - </sequential> - </macrodef> - - <!-- Zinc assumes a one-to-one correspondence of output folder to set of source files. - When compiling different sets of source files in multiple compilations to the same output directory, - Zinc thinks source files that appeared in an earlier compilation but are absent in the current one, - were deleted and thus deletes the corresponding output files. - - Note that zinc also requires each arg to scalac to be prefixed by -S. - --> - <macrodef name="zinc"> - <attribute name="compilerpathref"/> - <attribute name="destdir"/> - <attribute name="srcdir"/> - <attribute name="srcpath" default="NOT SET"/> - <!-- needed to compile the library, "NOT SET" is just a convention to denote an optional attribute --> - <attribute name="buildpathref"/> - <attribute name="params" default=""/> - <attribute name="java-excludes" default=""/> - <sequential> - <local name="sources"/> - <pathconvert pathsep=" " property="sources"> - <fileset dir="@{srcdir}"> - <include name="**/*.java"/> - <include name="**/*.scala"/> - <exclude name="@{java-excludes}"/> - </fileset> - </pathconvert> - <local name="args"/> - <local name="sargs"/> - <if> - <not> - <equals arg1="@{srcpath}" arg2="NOT SET"/> - </not> - <then> - <property name="args" value="@{params} -sourcepath @{srcpath}"/> - </then> - </if> - <property name="args" value="@{params}"/> - <!-- default --> - <!-- HACK: prefix scalac args by -S --> - <script language="javascript"> - project.setProperty("sargs", project.getProperty("args").trim().replaceAll(" ", " -S")); - </script> - <exec osfamily="unix" executable="tools/zinc" failifexecutionfails="true" failonerror="true"> - <arg line="-nailed -compile-order JavaThenScala -scala-path ${ant.refid:@{compilerpathref}} -d @{destdir} -classpath ${toString:@{buildpathref}} ${sargs} ${sources}"/> - </exec> - </sequential> - </macrodef> - - <!-- STAGED COMPILATION MACROS --> - <macrodef name="staged-scalac"> - <attribute name="with"/> - <!-- will use path `@{with}.compiler.path` to locate scalac --> - <attribute name="stage"/> - <!-- current stage (locker, quick, strap) --> - <attribute name="project"/> - <!-- project: library/reflect/compiler --> - <attribute name="srcpath" default="NOT SET"/> - <!-- needed to compile the library --> - <attribute name="args" default=""/> - <!-- additional args --> - <attribute name="destproject" default="@{project}"/> - <!-- overrides the output directory; used when building multiple projects into the same directory--> - <attribute name="srcdir" default="@{project}"/> - <attribute name="java-excludes" default=""/> - <attribute name="mixed" default="NOPE"/> - <sequential> - <local name="mixed.true"/><condition property="mixed.true"><equals arg1="@{mixed}" arg2="true"/></condition> - <!-- TODO: detect zinc anywhere on PATH - use zinc for the quick stage if it's available; - would use it for locker but something is iffy in sbt: get a class cast error on global phase --> - <if> - <and> - <available file="tools/zinc"/> - <equals arg1="@{stage}" arg2="quick"/> - </and> - <then> - <zinc taskname="Z.@{stage}.@{project}" compilerpathref="@{with}.compiler.path" destdir="${build-@{stage}.dir}/classes/@{destproject}" srcdir="${src.dir}/@{srcdir}" srcpath="@{srcpath}" buildpathref="@{stage}.@{project}.build.path" params="${scalac.args.@{stage}} @{args}" java-excludes="@{java-excludes}"/> - </then> - <else> - <if> - <equals arg1="@{srcpath}" arg2="NOT SET"/> - <then> - <scalacfork taskname="@{stage}.@{project}" jvmargs="${scalacfork.jvmargs}" compilerpathref="@{with}.compiler.path" destdir="${build-@{stage}.dir}/classes/@{destproject}" srcdir="${src.dir}/@{srcdir}" params="${scalac.args.@{stage}} @{args}"> - <include name="**/*.scala"/> - <include name="**/*.java" if="mixed.true"/> - <compilationpath refid="@{stage}.@{project}.build.path"/> - </scalacfork> - </then> - <else> - <scalacfork taskname="@{stage}.@{project}" jvmargs="${scalacfork.jvmargs}" compilerpathref="@{with}.compiler.path" destdir="${build-@{stage}.dir}/classes/@{destproject}" srcdir="${src.dir}/@{srcdir}" srcpath="@{srcpath}" params="${scalac.args.@{stage}} @{args}"> - <include name="**/*.scala"/> - <include name="**/*.java" if="mixed.true"/> - <compilationpath refid="@{stage}.@{project}.build.path"/> - </scalacfork> - </else> - </if> - </else> - </if> - </sequential> - </macrodef> - - <macrodef name="staged-uptodate"> - <attribute name="stage"/> - <attribute name="project"/> - <element name="check"/> - <element name="do"/> - <sequential> - <uptodate property="@{stage}.@{project}.available" targetfile="${build-@{stage}.dir}/@{project}.complete"> - <check/> - </uptodate> - <if> - <not> - <isset property="@{stage}.@{project}.available"/> - </not> - <then> - <do/> - <touch file="${build-@{stage}.dir}/@{project}.complete" verbose="no"/> - </then> - </if> - </sequential> - </macrodef> - - <macrodef name="staged-build"> - <attribute name="with"/> - <!-- will use path `@{with}.compiler.path` to locate scalac --> - <attribute name="stage"/> - <!-- current stage (locker, quick, strap) --> - <attribute name="project"/> - <!-- project: library/reflect/compiler --> - <attribute name="srcpath" default="NOT SET"/> - <!-- needed to compile the library --> - <attribute name="args" default=""/> - <!-- additional args --> - <attribute name="includes" default="comp.includes"/> - <attribute name="java-excludes" default=""/> - <attribute name="version" default=""/> - <attribute name="mixed" default="NOPE"/> - <!-- non-empty for scaladoc: use @{version}.version.number in property file--> - <sequential> - <staged-uptodate stage="@{stage}" project="@{project}"> - <check> - <srcfiles dir="${src.dir}/@{project}"/> - </check> - <do> - <stopwatch name="@{stage}.@{project}.timer"/> - <mkdir dir="${build-@{stage}.dir}/classes/@{project}"/> - <if> - <equals arg1="@{mixed}" arg2="true"/> - <then> - <staged-scalac with="@{with}" stage="@{stage}" project="@{project}" srcpath="@{srcpath}" args="@{args}" java-excludes="@{java-excludes}" mixed="@{mixed}"/> - <staged-javac stage="@{stage}" project="@{project}" excludes="@{java-excludes}"/> - </then> - <else> - <staged-javac stage="@{stage}" project="@{project}" excludes="@{java-excludes}"/> - <!-- always compile with javac for simplicity and regularity; it's cheap --> - <staged-scalac with="@{with}" stage="@{stage}" project="@{project}" srcpath="@{srcpath}" args="@{args}" java-excludes="@{java-excludes}"/> - </else> - </if> - <if> - <equals arg1="@{version}" arg2=""/> - <then> - <propertyfile file="${build-@{stage}.dir}/classes/@{project}/@{project}.properties"> - <entry key="version.number" value="${version.number}"/> - <entry key="maven.version.number" value="${maven.version.number}"/> - <entry key="osgi.version.number" value="${osgi.version.number}"/> - <entry key="copyright.string" value="${copyright.string}"/> - </propertyfile> - </then> - <else> - <propertyfile file="${build-@{stage}.dir}/classes/@{project}/@{project}.properties"> - <entry key="version.number" value="${@{version}.version.number}"/> - <entry key="copyright.string" value="${copyright.string}"/> - </propertyfile> - </else> - </if> - <copy todir="${build-@{stage}.dir}/classes/@{project}"> - <fileset dir="${src.dir}/@{project}"> - <patternset refid="@{includes}"/> - </fileset> - </copy> - <stopwatch name="@{stage}.@{project}.timer" action="total"/> - </do> - </staged-uptodate> - </sequential> - </macrodef> - - <macrodef name="staged-bin"> - <attribute name="stage"/> - <attribute name="classpathref" default="NOT SET"/> - <sequential> - <staged-uptodate stage="@{stage}" project="bin"> - <check> - <srcfiles dir="${src.dir}"> - <include name="compiler/scala/tools/ant/templates/**"/> - </srcfiles> - </check> - <do> - <taskdef name="mk-bin" classname="scala.tools.ant.ScalaTool" classpathref="@{stage}.bin.tool.path"/> - <mkdir dir="${build-@{stage}.dir}/bin"/> - <if> - <equals arg1="@{classpathref}" arg2="NOT SET"/> - <then> - <mk-bin file="${build-@{stage}.dir}/bin/scala" class="scala.tools.nsc.MainGenericRunner" javaFlags="${java.flags}"/> - <mk-bin file="${build-@{stage}.dir}/bin/scalac" class="scala.tools.nsc.Main" javaFlags="${java.flags}"/> - <mk-bin file="${build-@{stage}.dir}/bin/scaladoc" class="scala.tools.nsc.ScalaDoc" javaFlags="${java.flags}"/> - <mk-bin file="${build-@{stage}.dir}/bin/fsc" class="scala.tools.nsc.CompileClient" javaFlags="${java.flags}"/> - <mk-bin file="${build-@{stage}.dir}/bin/scalap" class="scala.tools.scalap.Main" javaFlags="${java.flags}"/> - </then> - <else> - <mk-bin file="${build-@{stage}.dir}/bin/scala" class="scala.tools.nsc.MainGenericRunner" javaFlags="${java.flags}" classpathref="@{classpathref}"/> - <mk-bin file="${build-@{stage}.dir}/bin/scalac" class="scala.tools.nsc.Main" javaFlags="${java.flags}" classpathref="@{classpathref}"/> - <mk-bin file="${build-@{stage}.dir}/bin/scaladoc" class="scala.tools.nsc.ScalaDoc" javaFlags="${java.flags}" classpathref="@{classpathref}"/> - <mk-bin file="${build-@{stage}.dir}/bin/fsc" class="scala.tools.nsc.CompileClient" javaFlags="${java.flags}" classpathref="@{classpathref}"/> - <mk-bin file="${build-@{stage}.dir}/bin/scalap" class="scala.tools.scalap.Main" javaFlags="${java.flags}" classpathref="@{classpathref}"/> - </else> - </if> - <chmod perm="ugo+rx" file="${build-@{stage}.dir}/bin/scala"/> - <chmod perm="ugo+rx" file="${build-@{stage}.dir}/bin/scalac"/> - <chmod perm="ugo+rx" file="${build-@{stage}.dir}/bin/scaladoc"/> - <chmod perm="ugo+rx" file="${build-@{stage}.dir}/bin/fsc"/> - <chmod perm="ugo+rx" file="${build-@{stage}.dir}/bin/scalap"/> - </do> - </staged-uptodate> - </sequential> - </macrodef> - - <macrodef name="staged-pack"> - <attribute name="project"/> - <attribute name="manifest" default=""/> - <element name="pre" optional="true"/> - <element name="jar-opts" optional="true"/> - <sequential> - <local name="destfile"/> - <property name="destfile" value="${build-pack.dir}/${@{project}.targetdir}/${@{project}.targetjar}"/> - <uptodate property="pack.@{project}.available" targetfile="${destfile}"> - <srcresources> - <resources refid="pack.@{project}.files"/> - <!-- <path><pathelement location="${build-quick.dir}/@{project}.complete"/></path> --> - </srcresources> - </uptodate> - <if> - <not> - <isset property="pack.@{project}.available"/> - </not> - <then> - <mkdir dir="${build-pack.dir}/${@{project}.targetdir}"/> - <pre/> - <!-- can't check if a fileset is defined, so we have an additional property --> - <if><not><isset property="pack.@{project}.include-jars.defined"/></not><then> - <fileset id="pack.@{project}.include-jars" dir="." excludes="**" /> - </then></if> - <if> - <not> - <equals arg1="@{manifest}" arg2=""/> - </not> - <then> - <jar whenmanifestonly="fail" destfile="${destfile}" manifest="@{manifest}"> - <!-- update="true" makes no difference on my machine, so starting from scratch--> - <jar-opts/> - <path refid="pack.@{project}.files"/> - <zipgroupfileset refid="pack.@{project}.include-jars"/> - </jar> - </then> - <else> - <jar whenmanifestonly="fail" destfile="${destfile}"> - <jar-opts/> - <path refid="pack.@{project}.files"/> - </jar> - </else> - </if> - </then> - </if> - </sequential> - </macrodef> - - <macrodef name="staged-docs"> - <attribute name="project"/> - <element name="includes" implicit="true"/> - <sequential> - <staged-uptodate stage="docs" project="@{project}"> - <check> - <srcfiles dir="${src.dir}/${@{project}.srcdir}"/> - </check> - <do> - <stopwatch name="docs.@{project}.timer"/> - <mkdir dir="${build-docs.dir}/@{project}"/> - <if> - <equals arg1="${@{project}.docroot}" arg2="NOT SET"/> - <then> - <scaladoc - destdir="${build-docs.dir}/@{project}" - doctitle="${@{project}.description}" - docfooter="epfl" - docversion="${version.number}" - sourcepath="${src.dir}" - classpathref="docs.@{project}.build.path" - srcdir="${src.dir}/${@{project}.srcdir}" - addparams="${scalac.args.all}" - docsourceurl="${scaladoc.url}€{FILE_PATH}.scala#L1" - implicits="on" - diagrams="on" - groups="on" - rawOutput="${scaladoc.raw.output}" - noPrefixes="${scaladoc.no.prefixes}" - docUncompilable="${src.dir}/library-aux" - skipPackages="${@{project}.skipPackages}"> - <includes/> - </scaladoc> - </then> - <else> - <scaladoc docRootContent="${src.dir}/@{project}/${@{project}.docroot}" - destdir="${build-docs.dir}/@{project}" - doctitle="${@{project}.description}" - docfooter="epfl" - docversion="${version.number}" - sourcepath="${src.dir}" - classpathref="docs.@{project}.build.path" - srcdir="${src.dir}/${@{project}.srcdir}" - addparams="${scalac.args.all}" - docsourceurl="${scaladoc.url}€{FILE_PATH}.scala#L1" - implicits="on" - diagrams="on" - groups="on" - rawOutput="${scaladoc.raw.output}" - noPrefixes="${scaladoc.no.prefixes}" - docUncompilable="${src.dir}/library-aux" - skipPackages="${@{project}.skipPackages}"> - <includes/> - </scaladoc> - </else> - </if> - <stopwatch name="docs.@{project}.timer" action="total"/> - </do> - </staged-uptodate> - </sequential> - </macrodef> - - <macrodef name="make-bundle"> - <attribute name="project"/> - <element name="srcs" description="Sources for this bundle" optional="true" implicit="true"/> - <sequential> - <copy file="${src.dir}/build/bnd/${@{project}.name}.bnd" tofile="${build-osgi.dir}/${@{project}.name}.bnd" overwrite="true"> - <filterset> - <filter token="VERSION" value="${osgi.version.number}"/> - <filter token="SCALA_BINARY_VERSION" value="${scala.binary.version}"/> - <filter token="SCALA_FULL_VERSION" value="${scala.full.version}"/> - <filter token="SCALA_COMPILER_DOC_VERSION" value="${scala-compiler-doc.version.number}"/> - <filter token="SCALA_COMPILER_INTERACTIVE_VERSION" value="${scala-compiler-interactive.version.number}"/> - <filter token="XML_VERSION" value="${scala-xml.version.number}" /> - <filter token="PARSER_COMBINATORS_VERSION" value="${scala-parser-combinators.version.number}" /> - <filter token="SCALA_SWING_VERSION" value="${scala-swing.version.osgi}" /> - <filter token="SOURCE_JARNAME" value="${@{project}.targetjar}"/> - </filterset> - </copy> - <bnd classpath="${@{project}.jar}" eclipse="false" failok="false" exceptions="true" files="${build-osgi.dir}/${@{project}.name}.bnd" output="${build-osgi.dir}"/> - <if> - <equals arg1="${@{project}.src}" arg2="true"/> - <then> - <!-- - A jar-like task that creates an OSGi source bundle. It adds the required MANIFEST.MF headers that allow - Eclipse to match sources with the corresponding binaries. - --> - <jar whenmanifestonly="fail" destfile="${build-osgi.dir}/${@{project}.name}-src.jar"> - <srcs/> - <manifest> - <attribute name="Manifest-Version" value="1.0"/> - <attribute name="Bundle-Name" value="${@{project}.description} Sources"/> - <attribute name="Bundle-SymbolicName" value="org.scala-lang.${@{project}.package}${@{project}.name}${@{project}.namesuffix}.source"/> - <attribute name="Bundle-Version" value="${@{project}.version}"/> - <attribute name="Eclipse-SourceBundle" value="org.scala-lang.${@{project}.package}${@{project}.name}${@{project}.namesuffix};version="${@{project}.version}";roots:=".""/> - </manifest> - </jar> - </then> - </if> - </sequential> - </macrodef> - - <macrodef name="copy-bundle"> - <attribute name="project"/> - <sequential> - <copy tofile="${dist.dir}/${@{project}.targetdir}/${@{project}.name}.jar" file="${build-osgi.dir}/org.scala-lang.${@{project}.package}${@{project}.name}.jar" overwrite="true"/> - <copy tofile="${dist.dir}/src/${@{project}.name}-src.jar" file="${@{project}.srcjar}" overwrite="true"/> - </sequential> - </macrodef> - - <macrodef name="mvn-package"> - <attribute name="project"/> - <sequential> - <local name="artifact-base"/> - <property name="artifact-base" value="${dist.maven}/${@{project}.dir}${@{project}.name}/${@{project}.name}"/> - <mkdir dir="${dist.maven}/${@{project}.dir}${@{project}.name}"/> - <copy tofile="${artifact-base}.jar" file="${build-osgi.dir}/org.scala-lang.${@{project}.package}${@{project}.name}${@{project}.namesuffix}.jar" overwrite="true"/> - <copy tofile="${artifact-base}-src.jar" file="${build-osgi.dir}/${@{project}.name}-src.jar" overwrite="true"/> - <copy tofile="${artifact-base}-pom.xml" file="${src.dir}/build/maven/${@{project}.dir}/${@{project}.name}-pom.xml" overwrite="true"/> - <if> - <not> - <isset property="docs.skip"/> - </not> - <then> - <jar destfile="${artifact-base}-docs.jar" basedir="${build-docs.dir}/@{project}" whenmanifestonly="fail"> - <include name="**/*"/> - </jar> - </then> - </if> - </sequential> - </macrodef> - - <macrodef name="deploy-remote"> - <attribute name="jar" default=""/> - <attribute name="pom"/> - <element name="artifacts" implicit="true" optional="true"/> - <sequential> - <if><equals arg1="@{jar}" arg2="true"/><then> - <artifact:deploy settingsFile="${settings.file}"> - <artifact:remoteRepository url="${remote.repository}" id="${repository.credentials.id}" /> - <artifact:pom refid="@{pom}" /> - <artifacts/> - </artifact:deploy> - </then><else> - <artifact:deploy file="@{jar}" settingsFile="${settings.file}"> - <artifact:remoteRepository url="${remote.repository}" id="${repository.credentials.id}" /> - <artifact:pom refid="@{pom}" /> - <artifacts/> - </artifact:deploy> - </else></if> - </sequential> - </macrodef> - - <macrodef name="deploy-local"> - <attribute name="jar" default=""/> - <attribute name="pom"/> - <element name="artifacts" implicit="true" optional="true"/> - <sequential> - <if><equals arg1="@{jar}" arg2="true"/><then> - <artifact:install> - <artifact:localRepository path="${local.repository}" id="${repository.credentials.id}" /> - <artifact:pom refid="@{pom}" /> - <artifacts/> - </artifact:install> - </then><else> - <artifact:install file="@{jar}"> - <artifact:localRepository path="${local.repository}" id="${repository.credentials.id}" /> - <artifact:pom refid="@{pom}" /> - <artifacts/> - </artifact:install> - </else></if> - </sequential> - </macrodef> - - <macrodef name="deploy-to"> - <attribute name="jar" default=""/> - <attribute name="pom"/> - <attribute name="local"/> - <element name="artifacts" implicit="true" optional="true"/> - <sequential> - <if><equals arg1="@{local}" arg2="true"/><then> - <deploy-local jar="@{jar}" pom="@{pom}"> <artifacts/> </deploy-local> - </then><else> - <deploy-remote jar="@{jar}" pom="@{pom}"> <artifacts/> </deploy-remote> - </else></if> - </sequential> - </macrodef> - - <macrodef name="filter-pom"> - <attribute name="path" /> - <attribute name="name" /> - - <sequential> - <copy file="${path}-pom.xml" tofile="${path}-pom-filtered.xml" overwrite="true"> - <filterset> - <filter token="VERSION" value="${maven.version.number}" /> - <filter token="SCALA_BINARY_VERSION" value="${scala.binary.version}" /> - <filter token="SCALA_FULL_VERSION" value="${scala.full.version}" /> - <filter token="XML_VERSION" value="${scala-xml.version.number}" /> - <filter token="PARSER_COMBINATORS_VERSION" value="${scala-parser-combinators.version.number}" /> - <filter token="SCALA_SWING_VERSION" value="${scala-swing.version.number}" /> - <filter token="RELEASE_REPOSITORY" value="${remote.release.repository}" /> - <filter token="SNAPSHOT_REPOSITORY" value="${remote.snapshot.repository}" /> - <filter token="JLINE_VERSION" value="${jline.version}" /> - - <!-- TODO modularize compiler. - <filter token="SCALA_COMPILER_DOC_VERSION" value="${scala-compiler-doc.version.number}" /> - <filter token="SCALA_COMPILER_INTERACTIVE_VERSION" value="${scala-compiler-interactive.version.number}" /> - --> - </filterset> - </copy> - <artifact:pom id="@{name}.pom" file="${path}-pom-filtered.xml" /> - </sequential> - </macrodef> - - <macrodef name="deploy-one"> - <attribute name="name" /> - <attribute name="local" default="false"/> - <attribute name="signed" default="false"/> - - <sequential> - <local name="path"/> <property name="path" value="${dist.maven}/@{name}/@{name}"/> - - <echo>Deploying ${path}-[pom.xml|src.jar|docs.jar].</echo> - - <filter-pom name="@{name}" path="@{path}"/> - - <if><equals arg1="@{signed}" arg2="false"/><then> - <if><isset property="docs.skip"/><then> - <deploy-to local="@{local}" jar="${path}.jar" pom="@{name}.pom"> - <artifact:attach type="jar" file="${path}-src.jar" classifier="sources" /> - </deploy-to> - </then><else> - <deploy-to local="@{local}" jar="${path}.jar" pom="@{name}.pom"> - <artifact:attach type="jar" file="${path}-src.jar" classifier="sources" /> - <artifact:attach type="jar" file="${path}-docs.jar" classifier="javadoc" /> - </deploy-to> - </else></if> - </then><else> - <local name="repo"/> - <if><equals arg1="@{local}" arg2="false"/><then> - <property name="repo" value="${remote.repository}"/> - </then><else> - <property name="repo" value="${local.repository}"/> - </else></if> - <artifact:mvn failonerror="true"> - <arg value="org.apache.maven.plugins:maven-gpg-plugin:1.3:sign-and-deploy-file" /> - <arg value="-Durl=${repo}" /> - <arg value="-DrepositoryId=${repository.credentials.id}" /> - <arg value="-DpomFile=${path}-pom-filtered.xml" /> - <arg value= "-Dfile=${path}.jar" /> - <arg value="-Dsources=${path}-src.jar" /> - <arg value="-Djavadoc=${path}-docs.jar" /> - <arg value="-Pgpg" /> - <arg value="-Dgpg.useagent=true" /> - </artifact:mvn> - </else></if> - </sequential> - </macrodef> - - <macrodef name="deploy-jar"> - <attribute name="name" /> - <attribute name="local" default="false"/> - <attribute name="signed" default="false"/> - - <sequential> - <local name="path"/> <property name="path" value="${dist.maven}/@{name}/@{name}"/> - - <echo>Deploying ${path}.jar with ${path}-pom.xml.</echo> - - <filter-pom name="@{name}" path="@{path}"/> - - <if><equals arg1="@{signed}" arg2="false"/><then> - <deploy-to local="@{local}" jar="${path}.jar" pom="@{name}.pom"/> - </then><else> - <local name="repo"/> - <if><equals arg1="@{local}" arg2="false"/><then> - <property name="repo" value="${remote.repository}"/> - </then><else> - <property name="repo" value="${local.repository}"/> - </else></if> - <artifact:mvn failonerror="true"> - <arg value="org.apache.maven.plugins:maven-gpg-plugin:1.3:sign-and-deploy-file" /> - <arg value="-Durl=${repo}" /> - <arg value="-DrepositoryId=${repository.credentials.id}" /> - <arg value="-DpomFile=${path}-pom-filtered.xml" /> - <arg value= "-Dfile=${path}.jar" /> - <arg value="-Pgpg" /> - <arg value="-Dgpg.useagent=true" /> - </artifact:mvn> - </else></if> - </sequential> - </macrodef> - - <macrodef name="deploy-pom"> - <attribute name="name" /> - <attribute name="local" default="false"/> - <attribute name="signed" default="false"/> - - <sequential> - <local name="path"/> <property name="path" value="${dist.maven}/@{name}/@{name}"/> - - <echo>Deploying ${path}-pom.xml.</echo> - - <filter-pom name="@{name}" path="@{path}"/> - - <if><equals arg1="@{signed}" arg2="false"/><then> - <deploy-to local="@{local}" pom="@{name}.pom"/> - </then><else> - <local name="repo"/> - <if><equals arg1="@{local}" arg2="false"/><then> - <property name="repo" value="${remote.repository}"/> - </then><else> - <property name="repo" value="${local.repository}"/> - </else></if> - <artifact:mvn failonerror="true"> - <arg value="org.apache.maven.plugins:maven-gpg-plugin:1.3:sign-and-deploy-file" /> - <arg value="-Durl=${repo}" /> - <arg value="-DrepositoryId=${repository.credentials.id}" /> - <arg value="-DpomFile=${path}-pom-filtered.xml" /> - <arg value= "-Dfile=${path}-pom-filtered.xml" /> - <arg value="-Pgpg" /> - <arg value="-Dgpg.useagent=true" /> - </artifact:mvn> - </else></if> - </sequential> - </macrodef> - - <macrodef name="deploy"> - <attribute name="local" default="false"/> - <attribute name="signed" default="false"/> - - <sequential> - <deploy-one name="scala-library" local="@{local}" signed="@{signed}"/> - <deploy-one name="scala-reflect" local="@{local}" signed="@{signed}"/> - <deploy-one name="scala-compiler" local="@{local}" signed="@{signed}"/> - - <!-- TODO modularize compiler. - <deploy-one name="scala-compiler-doc" local="@{local}" signed="@{signed}"/> - <deploy-one name="scala-compiler-interactive" local="@{local}" signed="@{signed}"/> - --> - - <deploy-one name="scalap" local="@{local}" signed="@{signed}"/> - </sequential> - </macrodef> - - <macrodef name="testSuite"> - <attribute name="dir" default="${partest.dir}"/> - <attribute name="srcdir" default="files"/> <!-- TODO: make targets for `pending` and other subdirs --> - <attribute name="colors" default="${partest.colors}"/> - <attribute name="scalacOpts" default="${partest.scalac_opts} ${scalac.args.optimise}"/> - <attribute name="javaOpts" default="${env.ANT_OPTS}"/> - <attribute name="pcp" default="${toString:partest.compilation.path}"/> - <attribute name="kinds"/> - <sequential> - <property name="partest.dir" value="@{dir}" /> - <partest srcdir="@{srcdir}" - kinds="@{kinds}" - colors="@{colors}" - scalacOpts="@{scalacOpts}" - javaOpts="@{javaOpts}" - compilationpath="@{pcp}"/> - </sequential> - </macrodef> - - <macrodef name="bc.run-mima"> - <attribute name="jar-name"/> - <attribute name="prev"/> - <attribute name="curr"/> - <attribute name="direction"/> - <sequential> - <echo message="Checking @{direction} binary compatibility for @{jar-name} (against ${bc-reference-version})"/> - <java taskname="mima" fork="true" failonerror="true" classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--prev"/> - <arg value="@{prev}"/> - <arg value="--curr"/> - <arg value="@{curr}"/> - <arg value="--filters"/> - <arg value="${basedir}/bincompat-@{direction}.whitelist.conf"/> - <arg value="--generate-filters"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> - </sequential> - </macrodef> - - <macrodef name="bc.check"> - <attribute name="project"/> - <sequential> - <bc.run-mima jar-name="scala-@{project}" prev="${org.scala-lang:scala-@{project}:jar}" curr="${@{project}.jar}" direction="backward"/> - <bc.run-mima jar-name="scala-@{project}" prev="${@{project}.jar}" curr="${org.scala-lang:scala-@{project}:jar}" direction="forward"/> - </sequential> - </macrodef> - - <macrodef name="tarz"> - <attribute name="name" description="The tar file name (without extension)."/> - <element name="file-sets" description="A sequence of fileset elements to be included in the tar balls." optional="false" implicit="true"/> - <sequential> - <tar destfile="@{name}.tar" compression="none" longfile="gnu"> - <file-sets/> - </tar> - <gzip src="@{name}.tar" destfile="@{name}.tgz"/> - <if> - <not> - <equals arg1="${archives.skipxz}" arg2="true"/> - </not> - <then> - <exec executable="xz" failifexecutionfails="false"> - <arg line="-k -9e -S .xz @{name}.tar"/> - </exec> - <move file="@{name}.tar.xz" tofile="@{name}.txz" failonerror="false"/> - </then> - </if> - <delete file="@{name}.tar"/> - </sequential> - </macrodef> -</project> diff --git a/build.number b/build.number deleted file mode 100644 index 7c7e26fd09..0000000000 --- a/build.number +++ /dev/null @@ -1,13 +0,0 @@ -# The version number in this file should be the next un-released minor version, -# e.g., 2.11.7, 2.12.0, 2.12.1. It's used to determine version numbers for -# SNAPSHOT / nightly builds and local builds of source checkouts. - -version.major=2 -version.minor=12 -version.patch=0 - -# This is the -N part of a version (2.9.1-1). If it's 0, it's dropped from maven versions. It should not be used again. -version.bnum=0 - -# To build a release, see scripts/jobs/scala-release-2.11.x-build -# (normally run by the eponymous job on scala-ci.typesafe.com). @@ -17,40 +17,19 @@ * This nicely leads me to explaining goal and non-goals of this build definition. Goals are: * * - to be easy to tweak it in case a bug or small inconsistency is found - * - to mimic Ant's behavior as closely as possible * - to be super explicit about any departure from standard sbt settings - * - to achieve functional parity with Ant build as quickly as possible * - to be readable and not necessarily succinct * - to provide the nicest development experience for people hacking on Scala + * - originally, to mimic Ant's behavior as closely as possible, so the + * sbt and Ant builds could be maintained in parallel. the Ant build + * has now been removed, so we are now free to depart from that history. * * Non-goals are: * - * - to have the shortest sbt build definition possible; we'll beat Ant definition - * easily and that will thrill us already + * - to have the shortest sbt build definition possible * - to remove irregularities from our build process right away + * (but let's keep making gradual progress on this) * - to modularize the Scala compiler or library further - * - * It boils down to simple rules: - * - * - project layout is set in stone for now - * - if you need to work on convincing sbt to follow non-standard layout then - * explain everything you did in comments - * - constantly check where Ant build produces class files, artifacts, what kind of other - * files generates and port all of that to here - * - * Note on bootstrapping: - * - * Let's start with reminder what bootstrapping means in our context. It's an answer - * to this question: which version of Scala are using to compile Scala? The fact that - * the question sounds circular suggests trickiness. Indeed, bootstrapping Scala - * compiler is a tricky process. - * - * Ant build used to have involved system of bootstrapping Scala. It would consist of - * three layers: starr, locker and quick. The sbt build for Scala ditches layering - * and strives to be as standard sbt project as possible. This means that we are simply - * building Scala with latest stable release of Scala. - * See this discussion for more details behind this decision: - * https://groups.google.com/d/topic/scala-internals/gp5JsM1E0Fo/discussion */ import VersionUtil._ @@ -69,8 +48,8 @@ val asmDep = "org.scala-lang.modules" % "scala-asm" % versionPr val jlineDep = "jline" % "jline" % versionProps("jline.version") val antDep = "org.apache.ant" % "ant" % "1.9.4" -/** Publish to ./dists/maven-sbt, similar to the ANT build which publishes to ./dists/maven. This - * can be used to compare the output of the sbt and ANT builds during the transition period. Any +/** Publish to ./dists/maven-sbt, similar to the Ant build which publishes to ./dists/maven. This + * can be used to compare the output of the sbt and Ant builds during the transition period. Any * real publishing should be done with sbt's standard `publish` task. */ lazy val publishDists = taskKey[Unit]("Publish to ./dists/maven-sbt.") @@ -133,11 +112,8 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ publishSettings + // we always assume that Java classes are standalone and do not have any dependency // on Scala classes compileOrder := CompileOrder.JavaThenScala, - javacOptions in Compile ++= Seq("-g", "-source", "1.8", "-target", "1.8"), - // we don't want any unmanaged jars; as a reminder: unmanaged jar is a jar stored - // directly on the file system and it's not resolved through Ivy - // Ant's build stored unmanaged jars in `lib/` directory - unmanagedJars in Compile := Seq.empty, + javacOptions in Compile ++= Seq("-g", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked"), + unmanagedJars in Compile := Seq.empty, // no JARs in version control! sourceDirectory in Compile := baseDirectory.value, unmanagedSourceDirectories in Compile := List(baseDirectory.value), unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / thisProject.value.id, @@ -217,7 +193,7 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ publishSettings + ) /** Extra post-processing for the published POM files. These are needed to create POMs that - * are equivalent to the ones from the ANT build. In the long term this should be removed and + * are equivalent to the ones from the Ant build. In the long term this should be removed and * POMs, scaladocs, OSGi manifests, etc. should all use the same metadata. */ def fixPom(extra: (String, scala.xml.Node)*): Setting[_] = { /** Find elements in an XML document by a simple XPath and replace them */ @@ -314,8 +290,6 @@ def filterDocSources(ff: FileFilter): Seq[Setting[_]] = Seq( // always required because otherwise the compiler cannot even initialize Definitions without // binaries of the library on the classpath. Specifically, we get this error: // (library/compile:doc) scala.reflect.internal.FatalError: package class scala does not have a member Int - // Ant build does the same thing always: it puts binaries for documented classes on the classpath - // sbt never does this by default (which seems like a good default) dependencyClasspath in (Compile, doc) += (classDirectory in Compile).value, doc in Compile <<= doc in Compile dependsOn (compile in Compile) ) @@ -398,8 +372,8 @@ lazy val compiler = configureAsSubproject(project) // These are only needed for the POM: libraryDependencies ++= Seq(scalaXmlDep, jlineDep % "optional"), // this a way to make sure that classes from interactive and scaladoc projects - // end up in compiler jar (that's what Ant build does) - // we need to use LocalProject references (with strings) to deal with mutual recursion + // end up in compiler jar. note that we need to use LocalProject references + // (with strings) to deal with mutual recursion products in Compile in packageBin := (products in Compile in packageBin).value ++ Seq((dependencyClasspath in Compile).value.find(_.get(moduleID.key) == Some(asmDep)).get.data) ++ @@ -430,7 +404,7 @@ lazy val compiler = configureAsSubproject(project) "*"), "Class-Path" -> "scala-reflect.jar scala-library.jar" ), - // Generate the ScriptEngineFactory service definition. The ant build does this when building + // Generate the ScriptEngineFactory service definition. The Ant build does this when building // the JAR but sbt has no support for it and it is easier to do as a resource generator: generateServiceProviderResources("javax.script.ScriptEngineFactory" -> "scala.tools.nsc.interpreter.IMain$Factory"), managedResourceDirectories in Compile := Seq((resourceManaged in Compile).value), @@ -482,7 +456,7 @@ lazy val replJlineEmbedded = Project("repl-jline-embedded", file(".") / "target" // There is nothing to compile for this project. Instead we use the compile task to create // shaded versions of repl-jline and jline.jar. dist/mkBin puts all of quick/repl, // quick/repl-jline and quick/repl-jline-shaded on the classpath for quick/bin scripts. - // This is different from the ant build where all parts are combined into quick/repl, but + // This is different from the Ant build where all parts are combined into quick/repl, but // it is cleaner because it avoids circular dependencies. compile in Compile <<= (compile in Compile).dependsOn(Def.task { import java.util.jar._ @@ -741,7 +715,7 @@ lazy val scalaDist = Project("scala-dist", file(".") / "target" / "scala-dist-di (manOut ** "*.1" pair rebase(manOut, fixedManOut)).foreach { case (in, out) => // Generated manpages should always use LF only. There doesn't seem to be a good reason // for generating them with the platform EOL first and then converting them but that's - // what the ant build does. + // what the Ant build does. IO.write(out, IO.readBytes(in).filterNot(_ == '\r')) } (htmlOut ** "*.html").get ++ (fixedManOut ** "*.1").get diff --git a/build.xml b/build.xml deleted file mode 100644 index 7c6f525c1c..0000000000 --- a/build.xml +++ /dev/null @@ -1,1786 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<project name="sabbus" default="build" - xmlns:artifact="urn:maven-artifact-ant" - xmlns:rsel="antlib:org.apache.tools.ant.types.resources.selectors"> - <include file="build-ant-macros.xml" as="macros"/> - - <description> -SuperSabbus for Scala core, builds the scala library and compiler. It can also package it as a simple distribution, tests it for stable bootstrapping and against the Scala test suite. - </description> - -<!-- HINTS - - for faster builds, have a build.properties in the same directory as build.xml that says: - locker.skip=1 ---> - -<!-- USAGE FROM JENKINS SCRIPTS IS (CURRENTLY) AS FOLLOWS: -ant $antArgs $scalacArgs $targets - -antArgs tend to be: - -Darchives.skipxz=true - -Dscalac.args.optimise=-opt:l:classpath - -scalacArgs examples: - "-Dscalac.args=\"-Yrangepos\" -Dpartest.scalac_opts=\"-Yrangepos\"" - -supported/exercised targets - to publish: nightly publish-opt-nodocs - to build: build build-opt locker.done - to run tests: test.suite test.scaladoc - -DO NOT RELY ON ANY OTHER TARGETS (ok, you're probably ok assuming the ones defined in the first 100 lines of this file) - -To build your own Scala distribution, do, e.g.: - - ant publish-local-opt -Dmaven.version.suffix="-foo" - cd ~/git - hub clone scala/scala-dist - cd scala-dist - sbt 'set version := "2.11.0-foo"' 'set resolvers += Resolver.mavenLocal' universal:package-bin - -NOTE: `ant build` builds the essence of a Scala distribution under build/pack - (The only thing missing are the docs; see `pack.doc` and `docs.done`.) - ---> - -<!-- To use Zinc with the ant build: - - install zinc and symlink the installed zinc script to ${basedir}/tools/zinc (${basedir} is where build.xml and the rest of your checkout resides) - - make sure to set ZINC_OPTS to match ANT_OPTS! ---> - -<!-- -TODO: - - detect zinc anywhere on PATH - - automatically set ZINC_OPTS - - skip locker (and test.stability) by default to speed up PR validation, still do full build & testing during nightly - - (rework the build to pack locker and build using that when using zinc) ---> - - -<!-- =========================================================================== - END-USER TARGETS -============================================================================ --> - <target name="build" depends="pack.done" description="Builds the Scala compiler and library. Executables are in 'build/pack/bin'."/> - <target name="test" depends="test.done" description="Runs test suite and bootstrapping test on Scala compiler and library."/> - <target name="docs" depends="docs.done" description="Builds documentation for the Scala library. Scaladoc is in 'build/scaladoc/library'."/> - <target name="docscomp" depends="docs.comp" description="Builds documentation for the Scala compiler and library. Scaladoc is in 'build/scaladoc'."/> - - <target name="build-opt" description="Optimized version of build."> <optimized name="build"/></target> - <target name="test-opt" description="Optimized version of test."> <optimized name="test"/></target> - <target name="test-core-opt" description="Optimized version of test.core."> <optimized name="test.core"/></target> - <target name="test-stab-opt" description="Optimized version of test.stability."> <optimized name="test.stability"/></target> - - <target name="all.done" depends="test.done, pack-maven.done"/> - <target name="nightly"><optimized name="all.done"/></target> - <target name="nightly.checkall"> <antcall target="all.done"> <param name="partest.scalac_opts" value="-Ycheck:all"/></antcall></target> - - <!-- The IDE build requires swing, so need to publish them during PR validation until they are modules --> - <target name="publish-opt-nodocs" description="Publishes Scala (optimized) without generating docs/testing (library/reflect/compiler/swing)."> - <antcall target="publish"> - <param name="docs.skip" value="1"/> - <param name="scalac.args.optimise" value="-opt:l:classpath"/> - </antcall> - </target> - <target name="publish-core-opt-nodocs" description="Builds an untested, undocumented optimised core (library/reflect/compiler) and publishes to maven."> - <antcall target="publish-core"> - <param name="docs.skip" value="1"/> - <param name="scalac.args.optimise" value="-opt:l:classpath"/> - </antcall> - </target> - <target name="publish-core-local-nodocs" description="Builds an untested, undocumented core (library/reflect/compiler) and locally publishes to maven"> - <antcall target="publish-core-local"> - <param name="docs.skip" value="1"/> - </antcall> - </target> - - <!-- prefer the sbt names, but the dotted names are used in jenkins; - rename there first before dropping the dotted ones --> - <target name="publish-local" depends="publish.local"/> - <target name="publish-local-opt"><optimized name="publish-local"/></target> - <target name="publish-signed" depends="publish.signed"/> - - - - - - - - - -<!-- DEPRECATED --> - <target name="dist" depends="all.clean, all.done" description="Cleans all and builds and tests a new distribution."/> - <target name="partialdist" depends="pack.done" description="Makes a new distribution without testing it or removing partially build elements."/> - <target name="fastdist" depends="pack.done, pack.doc" description="Makes a new distribution without testing it or removing partially build elements."/> - <target name="dist-opt" description="Optimized version of dist."> <optimized name="dist"/></target> - <target name="partialdist-opt" description="Optimized version of partialdist."> <optimized name="partialdist"/></target> - <target name="fastdist-opt" description="Optimized version of fastdist."> <optimized name="fastdist"/></target> - - <!-- packaging --> - <target name="distpack" depends="pack-maven.done"/> - <target name="distpack-maven" depends="pack-maven.done"/> - <target name="distpack-opt" description="Builds an optimised distribution."> <optimized name="distpack"/></target> - <target name="distpack-maven-opt" description="Builds an optimised maven distribution."><optimized name="distpack-maven"/></target> - <target name="distclean" depends="dist.clean" description="Removes all distributions. Binaries and documentation are untouched."/> - - <target name="nightly-nopt" depends="all.done"/> - - <target name="clean" depends="quick.clean" description="Removes binaries of compiler and library. Locker and distributions are untouched."/> - <target name="docsclean" depends="docs.clean" description="Removes generated documentation. Distributions are untouched."/> - - - -<!-- =========================================================================== - PROPERTIES -============================================================================ --> - - <property environment="env"/> - <!-- Prevents system classpath from being used --> - <property name="build.sysclasspath" value="ignore"/> - - <!-- Defines the repository layout --> - <property name="doc.dir" value="${basedir}/doc"/> - <property name="lib.dir" value="${basedir}/lib"/> - <property name="src.dir" value="${basedir}/src"/> - <property name="partest.dir" value="${basedir}/test"/> - - <property name="lib-ant.dir" value="${lib.dir}/ant"/> - <!-- For developers: any jars placed in this dir will be added to the classpath - of all targets and copied into quick/pack/etc builds. --> - <property name="lib-extra.dir" value="${lib.dir}/extra"/> - - <!-- Loads custom properties definitions --> - <property file="${basedir}/build.properties"/> - - <!-- Generating version number --> - <property file="${basedir}/build.number"/> - - <!-- read versions.properties --> - <property file="${basedir}/versions.properties"/> - - <!-- Sets location of pre-compiled libraries --> - <property name="ant.jar" value="${ant.home}/lib/ant.jar"/> - - <!-- Sets location of build folders --> - <property name="build.dir" value="${basedir}/build"/> - <property name="build-deps.dir" value="${build.dir}/deps"/> - <property name="build-libs.dir" value="${build.dir}/libs"/> - <property name="build-locker.dir" value="${build.dir}/locker"/> - <property name="build-quick.dir" value="${build.dir}/quick"/> - <property name="build-pack.dir" value="${build.dir}/pack"/> - <property name="build-manual.dir" value="${build.dir}/manual"/> - <property name="build-osgi.dir" value="${build.dir}/osgi"/> - <property name="build-junit.dir" value="${build.dir}/junit"/> - <property name="build-strap.dir" value="${build.dir}/strap"/> - <property name="build-docs.dir" value="${build.dir}/scaladoc"/> - <property name="build-sbt.dir" value="${build.dir}/sbt-interface"/> - - <property name="test.osgi.src" value="${partest.dir}/osgi/src"/> - <property name="test.osgi.classes" value="${build-osgi.dir}/classes"/> - - <property name="test.junit.src" value="${partest.dir}/junit"/> - <property name="test.junit.classes" value="${build-junit.dir}/classes"/> - - <property name="dists.dir" value="${basedir}/dists"/> - - <property name="copyright.string" value="Copyright 2002-2016, LAMP/EPFL"/> - - <!-- These are NOT the flags used to run SuperSabbus, but the ones written - into the script runners created with scala.tools.ant.ScalaTool --> - <property name="java.flags" value="-Xmx256M -Xms32M"/> - <property name="jvm.opts" value=""/> - - <!-- if ANT_OPTS is already set by the environment, it will be unaltered, - but if it is unset it will take this default value. --> - <property name="env.ANT_OPTS" value="-Xms1536M -Xmx1536M -Xss1M -XX:+UseParallelGC" /> - - <property name="scalacfork.jvmargs" value="${env.ANT_OPTS} ${jvm.opts}"/> - -<!-- =========================================================================== - INITIALIZATION -============================================================================ --> - <target name="desired.jars.uptodate"> - <patternset id="desired.jars"> - <include name="lib/**/*.desired.sha1"/> - <include name="test/files/**/*.desired.sha1"/> - <include name="tools/**/*.desired.sha1"/> - </patternset> - - <uptodate property="lib.jars.uptodate"> - <srcfiles dir="${basedir}"><patternset refid="desired.jars"/></srcfiles> - <mapper type="glob" from="*.desired.sha1" to="*"/> - </uptodate> - </target> - - <target name="boot" depends="desired.jars.uptodate" unless="lib.jars.uptodate"> - <echo level="warn" message="Updating bootstrap libs. (To do this by hand, run ./pull-binary-libs.sh)"/> - <exec osfamily="unix" vmlauncher="false" executable="./pull-binary-libs.sh" failifexecutionfails="true" /> - <exec osfamily="windows" vmlauncher="false" executable="pull-binary-libs.sh" failifexecutionfails="true" /> - <!-- uptodate task needs to know these are what's in the sha. --> - <touch> - <fileset dir="${basedir}"><patternset refid="desired.jars"/></fileset> - <mapper type="glob" from="*.desired.sha1" to="*"/> - </touch> - </target> - - <target name="init.git" depends="boot"> - <!-- replacestarr needs git.commit.sha, but doesn't want to run the init target (it computes maven.version.number) --> - <exec osfamily="unix" executable="tools/get-scala-commit-sha" outputproperty="git.commit.sha" failifexecutionfails="false" /> - <exec osfamily="windows" executable="cmd.exe" outputproperty="git.commit.sha" failifexecutionfails="false"> - <arg value="/c"/> - <arg value="tools\get-scala-commit-sha.bat"/> - <arg value="-p"/> - </exec> - <exec osfamily="unix" executable="tools/get-scala-commit-date" outputproperty="git.commit.date" failifexecutionfails="false" /> - <exec osfamily="windows" executable="cmd.exe" outputproperty="git.commit.date" failifexecutionfails="false"> - <arg value="/c"/> - <arg value="tools\get-scala-commit-date.bat"/> - <arg value="-p"/> - </exec> - - <!-- some default in case something went wrong getting the revision --> - <property name="git.commit.sha" value="unknown"/> - <property name="git.commit.date" value="unknown"/> - </target> - - <target name="init" depends="init.git"> - <!-- Set up Ant contrib tasks so we can use <if><then><else> instead of the clunky `unless` attribute --> - <taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib-ant.dir}/ant-contrib.jar"/> - - <property name="scala.ant.min.version" value="1.8.2"/> - <if><not><antversion atleast="${scala.ant.min.version}"/></not> - <then><fail message="Ant version ${scala.ant.min.version} is required. You are running ${ant.version}"/></then> - </if> - - <!-- Add our maven ant tasks --> - <path id="maven-ant-tasks.classpath" path="${lib-ant.dir}/maven-ant-tasks-2.1.1.jar" /> - <typedef resource="org/apache/maven/artifact/ant/antlib.xml" uri="urn:maven-artifact-ant" classpathref="maven-ant-tasks.classpath" /> - - <!-- Resolve maven dependencies --> - - <!-- work around http://jira.codehaus.org/browse/MANTTASKS-203: - java.lang.ClassCastException: org.codehaus.plexus.DefaultPlexusContainer cannot be cast to org.codehaus.plexus.PlexusContainer - on repeated use of artifact:dependencies - --> - <if><not><isset property="maven-deps-done"></isset></not><then> - <mkdir dir="${user.home}/.m2/repository"/> - <!-- This task has an issue where if the user directory does not exist, so we create it above. UGH. --> - <artifact:dependencies pathId="extra.tasks.classpath" filesetId="extra.tasks.fileset"> - <dependency groupId="biz.aQute.bnd" artifactId="biz.aQute.bnd" version="2.4.1"/> - </artifact:dependencies> - - <artifact:dependencies pathId="jarjar.classpath"> - <dependency groupId="org.pantsbuild" artifactId="jarjar" version="1.6.0"/> - </artifact:dependencies> - - <artifact:dependencies pathId="jarlister.classpath"> - <dependency groupId="com.github.rjolly" artifactId="jarlister_2.11" version="1.0"/> - </artifact:dependencies> - - <!-- JUnit --> - <property name="junit.version" value="4.12"/> - <artifact:dependencies pathId="junit.classpath" filesetId="junit.fileset"> - <dependency groupId="junit" artifactId="junit" version="${junit.version}"/> - <dependency groupId="org.openjdk.jol" artifactId="jol-core" version="0.5"/> - </artifact:dependencies> - <copy-deps project="junit"/> - - <!-- Pax runner --> - <property name="pax.exam.version" value="4.5.0"/> - <property name="osgi.felix.version" value="5.0.1"/> - <property name="osgi.equinox.version" value="3.10.100.v20150521-1310"/> - <artifact:dependencies pathId="pax.exam.classpath" filesetId="pax.exam.fileset"> - <dependency groupId="org.ops4j.pax.exam" artifactId="pax-exam-container-native" version="${pax.exam.version}"/> - <dependency groupId="org.ops4j.pax.exam" artifactId="pax-exam-junit4" version="${pax.exam.version}"/> - <dependency groupId="org.ops4j.pax.exam" artifactId="pax-exam-link-assembly" version="${pax.exam.version}"/> - <dependency groupId="org.ops4j.pax.url" artifactId="pax-url-aether" version="2.4.1"/> - <dependency groupId="org.ops4j.pax.swissbox" artifactId="pax-swissbox-tracker" version="1.8.1"/> - <dependency groupId="ch.qos.logback" artifactId="logback-core" version="1.1.3"/> - <dependency groupId="ch.qos.logback" artifactId="logback-classic" version="1.1.3"/> - <dependency groupId="junit" artifactId="junit" version="${junit.version}"/> - <dependency groupId="org.slf4j" artifactId="slf4j-api" version="1.7.12"/> - </artifact:dependencies> - <copy-deps project="pax.exam"/> - - <artifact:dependencies pathId="osgi.framework.felix"> - <dependency groupId="org.apache.felix" artifactId="org.apache.felix.framework" version="${osgi.felix.version}"/> - </artifact:dependencies> - - <artifact:dependencies pathId="osgi.framework.equinox"> - <dependency groupId="org.eclipse.tycho" artifactId="org.eclipse.osgi" version="${osgi.equinox.version}"/> - </artifact:dependencies> - - <artifact:remoteRepository id="sonatype-release" url="https://oss.sonatype.org/content/repositories/releases"/> - <artifact:remoteRepository id="extra-repo" url="${extra.repo.url}"/> - - <!-- prepare, for each of the names below, the property "@{name}.cross", set to the - necessary cross suffix (usually something like "_2.11.0-M6". --> - <prepareCross name="scala-xml" /> - <prepareCross name="scala-parser-combinators" /> - <prepareCross name="scala-swing"/> - <prepareCross name="partest"/> - - <artifact:dependencies pathId="asm.classpath" filesetId="asm.fileset"> - <dependency groupId="org.scala-lang.modules" artifactId="scala-asm" version="${scala-asm.version}"/> - </artifact:dependencies> - <copy-deps project="asm"/> - - <!-- TODO: delay until absolutely necessary to allow minimal build, also move out partest dependency from scaladoc --> - <artifact:dependencies pathId="partest.classpath" filesetId="partest.fileset" versionsId="partest.versions"> - <!-- uncomment the following if you're deploying your own partest locally --> - <!-- <localRepository path="${user.home}/.m2/repository"/> --> - <!-- so we don't have to wait for artifacts to synch to maven central - (we don't distribute partest with Scala, so the risk of sonatype and maven being out of synch is irrelevant): - --> - <artifact:remoteRepository refid="sonatype-release"/> - <artifact:remoteRepository refid="extra-repo"/> - <dependency groupId="org.scala-lang.modules" artifactId="scala-partest${partest.cross}" version="${partest.version.number}" /> - </artifact:dependencies> - <copy-deps project="partest"/> - - <artifact:dependencies pathId="repl.deps.classpath" filesetId="repl.fileset" versionsId="repl.deps.versions"> - <dependency groupId="jline" artifactId="jline" version="${jline.version}"/> - </artifact:dependencies> - <copy-deps project="repl"/> - - <!-- used by the test.osgi target to create osgi bundles for the xml, parser-combinator jars - must specify sourcesFilesetId, javadocFilesetId to download these types of artifacts --> - <artifact:dependencies pathId="external-modules.deps.classpath" sourcesFilesetId="external-modules.sources.fileset" javadocFilesetId="external-modules.javadoc.fileset"> - <artifact:remoteRepository refid="extra-repo"/> - <dependency groupId="org.scala-lang.modules" artifactId="scala-xml${scala-xml.cross}" version="${scala-xml.version.number}"/> - <dependency groupId="org.scala-lang.modules" artifactId="scala-parser-combinators${scala-parser-combinators.cross}" version="${scala-parser-combinators.version.number}"/> - <dependency groupId="org.scala-lang.modules" artifactId="scala-swing${scala-swing.cross}" version="${scala-swing.version.number}"/> - </artifact:dependencies> - - <!-- External modules, excluding the core --> - <path id="external-modules-nocore"> - <restrict> - <path refid="external-modules.deps.classpath"/> - <rsel:not><rsel:or> - <rsel:name name="scala-library*.jar"/> - <rsel:name name="scala-reflect*.jar"/> - <rsel:name name="scala-compiler*.jar"/> - </rsel:or></rsel:not> - </restrict> - </path> - <copy-deps refid="external-modules-nocore" project="scaladoc"/> - - <propertyForCrossedArtifact name="scala-parser-combinators" jar="org.scala-lang.modules:scala-parser-combinators"/> - <propertyForCrossedArtifact name="scala-xml" jar="org.scala-lang.modules:scala-xml"/> - <propertyForCrossedArtifact name="scala-swing" jar="org.scala-lang.modules:scala-swing"/> - - <!-- BND support --> - <typedef resource="aQute/bnd/ant/taskdef.properties" classpathref="extra.tasks.classpath" /> - - <echo message="Using Scala ${starr.version} for STARR."/> - <artifact:dependencies pathId="starr.compiler.path" filesetId="starr.fileset"> - <artifact:remoteRepository refid="extra-repo"/> - <dependency groupId="org.scala-lang" artifactId="scala-library" version="${starr.version}"/> - <dependency groupId="org.scala-lang" artifactId="scala-reflect" version="${starr.version}"/> - <dependency groupId="org.scala-lang" artifactId="scala-compiler" version="${starr.version}"/> - </artifact:dependencies> - <copy-deps project="starr"/> - - <property name="maven-deps-done" value="yep!"/> - </then></if> - - - <!-- NOTE: ant properties are write-once: second writes are silently discarded; the logic below relies on this --> - - <!-- Compute defaults (i.e., if not specified on command-line) for OSGi/maven version suffixes. - Try to establish the invariant (verified below): - `version.suffix == maven.version.suffix == osgi.version.suffix`, - except for: - - snapshot builds, where: - - `maven.suffix == "-SNAPSHOT"` - - `version.suffix == osgi.version.suffix == ""` - - final builds, where: - - `osgi.suffix == "-VFINAL"` - - `version.suffix == maven.version.suffix == ""` - --> - <if><not><equals arg1="${version.bnum}" arg2="0"/></not><then> - <property name="version.suffix" value="-${version.bnum}"/> - </then></if> - - <if><or><not><isset property="version.suffix"/></not><equals arg1="${version.suffix}" arg2=""/></or><then> - <if><isset property="build.release"/><then> - <property name="maven.version.suffix" value=""/> - <property name="version.suffix" value="${maven.version.suffix}"/> - <if><equals arg1="${maven.version.suffix}" arg2=""/><then> - <property name="osgi.version.suffix" value="-VFINAL"/></then> - <else> - <property name="osgi.version.suffix" value="${maven.version.suffix}"/></else></if></then></if></then> - <else> <!-- version.suffix set and not empty --> - <property name="maven.version.suffix" value="${version.suffix}"/> - <property name="osgi.version.suffix" value="${version.suffix}"/></else></if> - - <!-- if a maven version suffix was set (or inferred), assume we're building a release --> - <if><isset property="maven.version.suffix"/><then> - <property name="build.release" value="1"/></then></if> - - <!-- not building a release and no version.suffix specified --> - <property name="maven.version.suffix" value="-SNAPSHOT"/> - - <if><equals arg1="${maven.version.suffix}" arg2="-SNAPSHOT"/><then> - <property name="osgi.version.suffix" value=""/> - <property name="version.suffix" value=""/></then> - <else> - <property name="osgi.version.suffix" value="${maven.version.suffix}"/> - <property name="version.suffix" value="${maven.version.suffix}"/></else></if> - - <!-- We use the git describe to determine the OSGi modifier for our build. --> - <property name="maven.version.number" - value="${version.major}.${version.minor}.${version.patch}${maven.version.suffix}"/> - <property name="osgi.version.number" - value="${version.major}.${version.minor}.${version.patch}.v${git.commit.date}${osgi.version.suffix}-${git.commit.sha}"/> - - <if><isset property="build.release"/><then> - <property name="version.number" value="${maven.version.number}"/> - </then><else> - <property name="version.number" value="${version.major}.${version.minor}.${version.patch}${version.suffix}-${git.commit.date}-${git.commit.sha}"/> - </else></if> - - <!-- some default in case something went wrong getting the revision --> - <property name="version.number" value="-unknown-"/> - - <condition property="has.java8"> - <equals arg1="${ant.java.version}" arg2="1.8"/> - </condition> - <condition property="has.unsupported.jdk"> - <not><or> - <isset property="has.java8" /> - </or></not> - </condition> - - <fail if="has.unsupported.jdk" message="JDK ${ant.java.version} is not supported by this build!"/> - <fail message="Ant 1.9+ required"> - <condition> - <not><antversion atleast="1.9" /></not> - </condition> - </fail> - - <!-- Allow this to be overridden simply --> - <property name="sbt.latest.version" value="0.13.11"/> - - <property name="sbt.src.dir" value="${build-sbt.dir}/${sbt.latest.version}/src"/> - <property name="sbt.lib.dir" value="${build-sbt.dir}/${sbt.latest.version}/lib"/> - - <property name="sbt.interface.jar" value="${sbt.lib.dir}/interface.jar"/> - <property name="sbt.interface.url" value="http://dl.bintray.com/typesafe/ivy-releases/org.scala-sbt/interface/${sbt.latest.version}/jars/interface.jar"/> - <property name="sbt.interface.src.jar" value="${sbt.src.dir}/compiler-interface-src.jar"/> - <property name="sbt.interface.src.url" value="http://dl.bintray.com/typesafe/ivy-releases/org.scala-sbt/compiler-interface/${sbt.latest.version}/srcs/compiler-interface-sources.jar"/> - - - <!-- Additional command line arguments for scalac. They are added to all build targets --> - <property name="scalac.args" value=""/> - <property name="javac.args" value=""/> - - <property name="scalac.args.always" value="-feature" /> - <property name="scalac.args.optimise" value=""/> <!-- scalac.args.optimise is selectively overridden in certain antcall tasks. --> - <property name="scalac.args.all" value="${scalac.args.always} ${scalac.args} ${scalac.args.optimise}"/> - <property name="scalac.args.locker" value="${scalac.args.all}"/> - <property name="scalac.args.quick" value="${scalac.args.all}"/> - <property name="scalac.args.strap" value="${scalac.args.quick}"/> - - <property name="partest.scalac_opts" value=""/> <!-- set default value, otherwise the property name will be passed to partest if undefined --> - - <!-- This is the start time for the distribution --> - <tstamp prefix="time"> - <format property="human" pattern="d MMMM yyyy, HH:mm:ss" locale="en,US"/> - <format property="short" pattern="yyyyMMddHHmmss"/> - </tstamp> - - <!-- Local libs (developer use.) --> - <mkdir dir="${lib-extra.dir}"/> - - <!-- Auxiliary libs placed on every classpath. --> - <path id="aux.libs"> - <pathelement location="${ant.jar}"/> - <!-- needs ant 1.7.1 --> - <!-- <fileset dir="${lib-extra.dir}" erroronmissingdir="false"> --> - <fileset dir="${lib-extra.dir}"> - <include name="**/*.jar"/> - </fileset> - </path> - - <!-- And print-out what we are building --> - <echo message=" build time: ${time.human}" /> - <echo message=" java version: ${java.vm.name} ${java.version} (${ant.java.version})" /> - <echo message=" java args: ${env.ANT_OPTS} ${jvm.opts}" /> - <echo message=" javac args: ${javac.args}" /> - <echo message=" scalac args: ${scalac.args.all}" /> - <echo message="scalac quick args: ${scalac.args.quick}" /> - <echo message=" git date: ${git.commit.date}" /> - <echo message=" git hash: ${git.commit.sha}" /> - <echo message=" maven version: ${maven.version.number}"/> - <echo message=" OSGi version: ${osgi.version.number}" /> - <echo message="canonical version: ${version.number}" /> - - <echoproperties destfile="buildcharacter.properties"> - <propertyset> - <propertyref regex="time.*" /> - <propertyref regex="git.*" /> - <propertyref name="java.vm.name" /> - <propertyref regex=".*version.*" /> - <propertyref regex="scalac.args.*" /> - <propertyref name="scalacfork.jvmargs" /> - </propertyset> - </echoproperties> - - <!-- validate version suffixes --> - <if><equals arg1="${maven.version.suffix}" arg2="-SNAPSHOT"/><then> - <condition property="version.suffixes.consistent"><and> - <equals arg1="${osgi.version.suffix}" arg2=""/> - <equals arg1="${version.suffix}" arg2=""/> - </and></condition></then> - <else> - <if><equals arg1="${osgi.version.suffix}" arg2="-VFINAL"/><then> - <condition property="version.suffixes.consistent"><and> - <equals arg1="${maven.version.suffix}" arg2=""/> - <equals arg1="${version.suffix}" arg2=""/> - </and></condition></then> - <else> - <condition property="version.suffixes.consistent"><and> - <equals arg1="${osgi.version.suffix}" arg2="${maven.version.suffix}"/> - <equals arg1="${version.suffix}" arg2="${maven.version.suffix}"/> - </and></condition></else></if></else></if> - - <!-- <echo message=" maven suffix: ${maven.version.suffix}"/> - <echo message=" OSGi suffix: ${osgi.version.suffix}" /> - <echo message="canonical suffix: ${version.suffix}" /> --> - <fail unless="version.suffixes.consistent" message="Version suffixes inconsistent!"/> - - - <!-- used during releases to bump versions in versions.properties --> - <if><isset property="update.versions"/><then> - <echo message="Updating `versions.properties`:"/> - <echo message="starr.version = ${starr.version}"/> - <echo message="scala.binary.version = ${scala.binary.version}"/> - <echo message="scala-xml.version.number = ${scala-xml.version.number}"/> - <echo message="scala-parser-combinators.version.number = ${scala-parser-combinators.version.number}"/> - <echo message="scala-swing.version.number = ${scala-swing.version.number}"/> - <echo message="jline.version = ${jline.version}"/> - <echo message="partest.version.number = ${partest.version.number}"/> - - <propertyfile file="versions.properties"> - <entry key="starr.version" value="${starr.version}"/> - <entry key="scala.binary.version" value="${scala.binary.version}"/> - <entry key="scala-xml.version.number" value="${scala-xml.version.number}"/> - <entry key="scala-parser-combinators.version.number" value="${scala-parser-combinators.version.number}"/> - <entry key="scala-swing.version.number" value="${scala-swing.version.number}"/> - <entry key="jline.version" value="${jline.version}"/> - <entry key="partest.version.number" value="${partest.version.number}"/> - </propertyfile> - </then></if> - - <!-- the following properties fully define staged-docs, staged-pack, make-bundle, copy-bundle and mvn-package for each of the projects --> - <property name="library.description" value="Scala Standard Library"/> - <property name="library.docroot" value="rootdoc.txt"/> - <property name="library.skipPackages" value="scala.concurrent.impl"/> - - <property name="reflect.description" value="Scala Reflection Library"/> - <property name="reflect.skipPackages" value="scala.reflect.macros.internal:scala.reflect.internal:scala.reflect.io"/> - - <property name="compiler.description" value="Scala Compiler"/> - <property name="compiler.docroot" value="rootdoc.txt"/> - - <!-- these are not used used, preparation for the 'TODO modularize compiler' task --> - <property name="interactive.description" value="Scala Interactive Compiler" /> - <property name="interactive.package" value="modules." /> - <property name="interactive.name" value="scala-compiler-interactive"/> - <property name="interactive.namesuffix" value="_${scala.binary.version}"/> - <property name="interactive.version" value="${scala-compiler-interactive.version.number}"/> - <property name="interactive.targetjar" value="scala-compiler-interactive_${scala.binary.version}-${scala-compiler-interactive.version.number}.jar"/> - - <property name="scaladoc.description" value="Scala Documentation Generator"/> - <property name="scaladoc.package" value="modules." /> - <property name="scaladoc.name" value="scala-compiler-doc" /> - <property name="scaladoc.namesuffix" value="_${scala.binary.version}"/> - <property name="scaladoc.version" value="${scala-compiler-doc.version.number}"/> - <property name="scaladoc.targetjar" value="scala-compiler-doc_${scala.binary.version}-${scala-compiler-doc.version.number}.jar"/> - - <property name="swing.description" value="Scala Swing Library"/> - <property name="swing.package" value="modules."/> - <property name="swing.targetjar" value="scala-swing${scala-swing.cross}-${scala-swing.version.number}.jar"/> - <property name="swing.jar" value="${scala-swing}"/> - <property name="swing.src" value="false"/> - <property name="swing.srcjar" value="${scala-swing-sources}"/> - - <property name="parser-combinators.description" value="Scala Parser Combinators Library"/> - <property name="parser-combinators.package" value="modules."/> - <property name="parser-combinators.targetjar" value="scala-parser-combinators${scala-parser-combinators.cross}-${scala-parser-combinators.version.number}.jar"/> - <property name="parser-combinators.jar" value="${scala-parser-combinators}"/> - <property name="parser-combinators.src" value="false"/> - <property name="parser-combinators.srcjar" value="${scala-parser-combinators-sources}"/> - - <property name="xml.description" value="Scala XML Library"/> - <property name="xml.package" value="modules."/> - <property name="xml.targetjar" value="scala-xml${scala-xml.cross}-${scala-xml.version.number}.jar"/> - <property name="xml.jar" value="${scala-xml}"/> - <property name="xml.src" value="false"/> - <property name="xml.srcjar" value="${scala-xml-sources}"/> - - <property name="scalap.description" value="Scala Bytecode Parser"/> - <property name="scalap.targetjar" value="scalap.jar"/> - - <property name="partest.description" value="Scala Compiler Testing Tool"/> - <property name="partest-extras.description" value="Scala Compiler Testing Tool (compiler-specific extras)"/> - <property name="partest-javaagent.description" value="Scala Compiler Testing Tool (compiler-specific java agent)"/> - - <!-- projects without project-specific options: manual, bin, repl --> - <for list="compiler,interactive,scaladoc,library,parser-combinators,partest,partest-extras,partest-javaagent,reflect,scalap,swing,xml,repl-jline" param="project"> - <sequential> - <!-- description is mandatory --> - <init-project-prop project="@{project}" name="package" default=""/> <!-- used by mvn-package, copy-bundle, make-bundle --> - <init-project-prop project="@{project}" name="dir" default=""/> <!-- used by mvn-package --> - <init-project-prop project="@{project}" name="name" default="scala-@{project}"/> <!-- used for defaults in this block and by mvn-package, copy-bundle, make-bundle --> - <init-project-prop project="@{project}" name="namesuffix" default=""/> - <init-project-prop project="@{project}" name="version" default="${osgi.version.number}"/> - <init-project-prop project="@{project}" name="targetdir" default="lib"/> - <init-project-prop project="@{project}" name="targetjar" default="${@{project}.name}.jar"/> - <init-project-prop project="@{project}" name="jar" default="${build-pack.dir}/${@{project}.targetdir}/${@{project}.targetjar}" /> - <init-project-prop project="@{project}" name="docroot" default="NOT SET"/> - <init-project-prop project="@{project}" name="skipPackages" default=""/> - <init-project-prop project="@{project}" name="srcdir" default="@{project}"/> - <init-project-prop project="@{project}" name="src" default="true"/> - <init-project-prop project="@{project}" name="srcjar" default="${build-osgi.dir}/${@{project}.name}-src.jar"/> - </sequential> - </for> - - - <!-- Compilers to use for the various stages. - There must be a variable of the shape @{stage}.compiler.path for all @{stage} in starr, locker, quick, strap. - --> - - <!-- starr is resolved (to starr.compiler.path) in the block protected by maven-deps-done - the maven task must not be executed twice, or you get a java.lang.ClassCastException: - org.apache.maven.artifact.ant.RemoteRepository cannot be cast to org.apache.maven.artifact.ant.Repository - --> - - <!-- To skip locker, use -Dlocker.skip=1 --> - <if><isset property="locker.skip"/><then> - <echo message="Using STARR to build the quick stage (skipping locker)."/> - <path id="locker.compiler.path" refid="starr.compiler.path"/> - <!-- this is cheating (we don't know the classpath used to build starr) - but should be close enough: --> - <path id="locker.compiler.build.path" refid="starr.compiler.path"/> - <property name="locker.locked" value="locker skipped"/></then> - <else> - <path id="locker.compiler.path"><path refid="locker.compiler.build.path"/></path></else></if> - - <!-- compilerpathref for compiling with quick --> - <path id="quick.compiler.path"> <path refid="quick.compiler.build.path"/></path> - - - <!-- What to have on the compilation path when compiling during certain phases. - - There must be a variable of the shape @{stage}.@{project}.build.path - for all @{stage} in locker, quick, strap - and all @{project} in library, reflect, compiler - when stage is quick, @{project} also includes: repl, scalap - - NOTE: interactive, scaladoc, are only used upto quick; they are still packed into the compiler jar - --> - - <!-- LOCKER --> - <path id="locker.library.build.path"> - <pathelement location="${build-locker.dir}/classes/library"/> - <path refid="aux.libs"/> - </path> - - <path id="locker.reflect.build.path"> - <path refid="locker.library.build.path"/> - <pathelement location="${build-locker.dir}/classes/reflect"/> - </path> - - <if><not><isset property="locker.skip"/></not><then> - <path id="locker.compiler.build.path"> - <path refid="locker.reflect.build.path"/> - <pathelement location="${build-locker.dir}/classes/compiler"/> - <path refid="asm.classpath"/> - </path> - </then></if> - <!-- else, locker.compiler.build.path is set above --> - - <!-- QUICK --> - <path id="quick.library.build.path"> - <pathelement location="${build-quick.dir}/classes/library"/> - <path refid="aux.libs"/> - </path> - - <path id="quick.reflect.build.path"> - <path refid="quick.library.build.path"/> - <pathelement location="${build-quick.dir}/classes/reflect"/> - </path> - - <path id="quick.compiler.build.path"> - <path refid="quick.reflect.build.path"/> - <pathelement location="${build-quick.dir}/classes/compiler"/> - <path refid="asm.classpath"/> - </path> - - <path id="quick.repl.build.path"> - <path refid="quick.compiler.build.path"/> - <path refid="quick.interactive.build.path"/> - <pathelement location="${build-quick.dir}/classes/repl"/> - </path> - - <path id="quick.repl-jline.build.path"> - <path refid="quick.repl.build.path"/> - <pathelement location="${build-quick.dir}/classes/repl-jline"/> - <path refid="repl.deps.classpath"/> - </path> - - <path id="quick.scalap.build.path"> - <path refid="quick.compiler.build.path"/> - <pathelement location="${build-quick.dir}/classes/scalap"/> - </path> - - <path id="quick.partest-extras.build.path"> - <path refid="asm.classpath"/> - <restrict> - <path refid="partest.classpath"/> - <rsel:not><rsel:or> - <rsel:name name="scala-library*.jar"/> - </rsel:or></rsel:not> - </restrict> - - <path refid="quick.compiler.build.path"/> - <pathelement location="${build-quick.dir}/classes/repl"/> - <!-- for the java dependency: Profiler.java --> - <pathelement location="${build-quick.dir}/classes/partest-extras"/> - </path> - - <path id="quick.partest-javaagent.build.path"> - <path refid="asm.classpath"/> - </path> - - <path id="quick.scaladoc.build.path"> - <path refid="quick.compiler.build.path"/> - <path refid="partest.classpath"/> - <path refid="external-modules-nocore"/> - <pathelement location="${build-quick.dir}/classes/scaladoc"/> - </path> - - <path id="quick.interactive.build.path"> - <path refid="quick.compiler.build.path"/> - <pathelement location="${build-quick.dir}/classes/interactive"/> - </path> - - <path id="quick.bin.tool.path"> - <path refid="quick.repl-jline.build.path"/> - <pathelement location="${build-quick.dir}/classes/scalap"/> - <pathelement location="${build-quick.dir}/classes/scaladoc"/> - <path refid="external-modules-nocore"/> - </path> - - <!-- PACK --> - <path id="pack.compiler.path"> - <pathelement location="${library.jar}"/> - <pathelement location="${reflect.jar}"/> - <pathelement location="${compiler.jar}"/> - <pathelement location="${ant.jar}"/> - <path refid="aux.libs"/> - </path> - - <path id="pack.lib.path"> - <pathelement location="${library.jar}"/> - <path refid="jarlister.classpath"/> - </path> - - <path id="pack.bin.tool.path"> - <pathelement location="${library.jar}"/> - <pathelement location="${xml.jar}"/> - <pathelement location="${reflect.jar}"/> - <pathelement location="${compiler.jar}"/> - <!-- TODO modularize compiler: <pathelement location="${scaladoc.jar}"/> --> - <pathelement location="${scalap.jar}"/> - <path refid="repl.deps.classpath"/> - <path refid="aux.libs"/> - </path> - - <path id="pack.library.files"> - <fileset dir="${build-quick.dir}/classes/library"/> - </path> - - <path id="pack.repl-jline.files"> - <fileset dir="${build-quick.dir}/classes/repl-jline"/> - </path> - - <path id="pack.compiler.files"> - <fileset dir="${build-quick.dir}/classes/compiler"/> - - <!-- TODO modularize compiler. Remove the other class dirs as soon as they become modules --> - <fileset dir="${build-quick.dir}/classes/scaladoc"/> - <fileset dir="${build-quick.dir}/classes/interactive"/> - <fileset dir="${build-quick.dir}/classes/repl"/> - </path> - <fileset id="pack.compiler.include-jars" refid="asm.fileset"/> - <property name="pack.compiler.include-jars.defined" value="yeah"/> - - <!-- TODO modularize compiler. - <path id="pack.scaladoc.files"> <fileset dir="${build-quick.dir}/classes/scaladoc"/> </path> - <path id="pack.interactive.files"><fileset dir="${build-quick.dir}/classes/interactive"/> </path> - --> - - <path id="pack.reflect.files"> <fileset dir="${build-quick.dir}/classes/reflect"/> </path> - <path id="pack.scalap.files"> <fileset dir="${build-quick.dir}/classes/scalap"/> </path> - - <path id="pack.partest-extras.files"> <fileset dir="${build-quick.dir}/classes/partest-extras"/> </path> - <path id="pack.partest-javaagent.files"> <fileset dir="${build-quick.dir}/classes/partest-javaagent"/> </path> - - <!-- STRAP --> - <path id="strap.library.build.path"> - <pathelement location="${build-strap.dir}/classes/library"/> - <path refid="aux.libs"/> - </path> - - <path id="strap.reflect.build.path"> - <path refid="strap.library.build.path"/> - <pathelement location="${build-strap.dir}/classes/reflect"/> - </path> - - <path id="strap.compiler.build.path"> - <path refid="strap.reflect.build.path"/> - <pathelement location="${build-strap.dir}/classes/compiler"/> - <path refid="asm.classpath"/> - </path> - - <!-- DOCS --> - <path id="docs.library.build.path"> <path refid="quick.library.build.path"/> </path> - <path id="docs.reflect.build.path"> <path refid="quick.reflect.build.path"/> </path> - <path id="docs.compiler.build.path"> <path refid="quick.compiler.build.path"/> </path> - <path id="docs.scaladoc.build.path"> <path refid="quick.scaladoc.build.path"/> </path> - <path id="docs.interactive.build.path"> <path refid="quick.interactive.build.path"/> </path> - <path id="docs.scalap.build.path"> <path refid="quick.scalap.build.path"/> </path> - - <!-- run-time classpath for scaladoc TODO: resolve through maven --> - <path id="scaladoc.classpath"> - <path refid="external-modules-nocore"/> - <pathelement location="${library.jar}"/> - <pathelement location="${reflect.jar}"/> - <pathelement location="${compiler.jar}"/> - - <!-- TODO modularize compiler - <pathelement location="${interactive.jar}"/> - <pathelement location="${scaladoc.jar}"/> - --> - - <pathelement location="${ant.jar}"/> - <path refid="aux.libs"/> - </path> - - <path id="manual.build.path"> - <path refid="external-modules-nocore"/> <!-- xml --> - <pathelement location="${library.jar}"/> - <pathelement location="${build.dir}/manmaker/classes"/> - <path refid="aux.libs"/> <!-- for ant --> - </path> - - <!-- MISC --> - <path id="sbt.compile.build.path"> - <path refid="scaladoc.classpath"/> - <!-- TODO modularize compiler: bring back when repl leaves compiler jar - <pathelement location="${build-quick.dir}/classes/repl"/> - --> - <pathelement location="${sbt.interface.jar}"/> - </path> - - - <!-- - This is the classpath used to run partest, which is what it uses to run the compiler and find other required jars. - "What's on the compiler's compilation path when compiling partest tests," you ask? - Why, the compiler we're testing, of course, and partest with all its dependencies. - --> - <path id="partest.compilation.path"> - <path refid="partest.compilation.path.core"/> - <path refid="partest.compilation.path.noncore"/> - </path> - <path id="partest.compilation.path.core"> - <pathelement location="${library.jar}"/> - <pathelement location="${reflect.jar}"/> - <pathelement location="${compiler.jar}"/> - </path> - <path id="partest.compilation.path.noncore"> - - <!-- TODO modularize compiler - <pathelement location="${scaladoc.jar}"/> - <pathelement location="${interactive.jar}"/> - --> - - <!-- TODO: move scalap out of repo --> - <pathelement location="${scalap.jar}"/> - - <!-- partest's dependencies, which marks most of its dependencies as provided, - (but not scala-library, so we filter that one out...) - so we provide them: scala-[library/reflect/compiler], scalap built here, - scala-xml via external-modules-nocore, as part of `partest.classpath` --> - <restrict> - <path refid="partest.classpath"/> - <rsel:not><rsel:or> - <rsel:name name="scala-library*.jar"/> - </rsel:or></rsel:not> - </restrict> - <pathelement location="${scala-xml}"/> - <!-- <pathelement location="${scala-swing}"/> --> - - <!-- partest classes specific to the core compiler build --> - <pathelement location="${partest-extras.jar}"/> - <pathelement location="${partest-javaagent.jar}"/> - - <!-- sneaky extras used in tests --> - <fileset dir="${partest.dir}/files/lib" includes="*.jar" /> - </path> - - <path id="test.junit.compiler.build.path"> - <pathelement location="${test.junit.classes}"/> - <path refid="quick.compiler.build.path"/> - <path refid="quick.repl.build.path"/> - <path refid="quick.scaladoc.build.path"/> - <path refid="quick.partest-extras.build.path"/> - <path refid="junit.classpath"/> - </path> - - <path id="test.osgi.compiler.build.path"> - <pathelement location="${test.osgi.classes}"/> - <pathelement location="${build-osgi.dir}/org.scala-lang.scala-library.jar"/> - <pathelement location="${build-osgi.dir}/org.scala-lang.scala-reflect.jar"/> - <pathelement location="${build-osgi.dir}/org.scala-lang.scala-compiler.jar"/> - <path refid="pax.exam.classpath"/> - </path> - - <path id="test.osgi.compiler.build.path.felix"> - <path refid="test.osgi.compiler.build.path"/> - <path refid="osgi.framework.felix"/> - </path> - - <path id="test.osgi.compiler.build.path.equinox"> - <path refid="test.osgi.compiler.build.path"/> - <path refid="osgi.framework.equinox"/> - </path> - - <path id="test.positions.sub.build.path" path="${build-quick.dir}/classes/library"/> - - <!-- TODO: consolidate *.includes --> - <patternset id="lib.includes"> - <include name="**/*.tmpl"/> - <include name="**/*.xml"/> - <include name="**/*.js"/> - <include name="**/*.css"/> - </patternset> - - <patternset id="lib.rootdoc.includes"> - <include name="**/*.tmpl"/> - <include name="**/*.xml"/> - <include name="**/*.js"/> - <include name="**/*.css"/> - <include name="rootdoc.txt"/> - </patternset> - - <patternset id="comp.includes"> - <include name="**/*.tmpl"/> - <include name="**/*.xml"/> - <include name="**/*.js"/> - <include name="**/*.css"/> - <include name="**/*.html"/> - <include name="**/*.properties"/> - <include name="**/*.swf"/> - <include name="**/*.png"/> - <include name="**/*.gif"/> - <include name="**/*.txt"/> - <include name="**/*.eot"/> - <include name="**/*.ttf"/> - <include name="**/*.woff"/> - <include name="**/*.svg"/> - </patternset> - - <taskdef resource="scala/tools/ant/sabbus/antlib.xml" classpathref="starr.compiler.path"/> - <taskdef name="jarjar" classname="org.pantsbuild.jarjar.JarJarTask" classpathref="jarjar.classpath" /> - </target> - -<!-- =========================================================================== - CLEANLINESS -=============================================================================--> - <target name="libs.clean"> <clean build="libs"/> </target> - <target name="quick.clean" depends="libs.clean"> <clean build="quick"/> <clean build="pack"/> <clean build="strap"/> </target> - <target name="locker.clean" depends="quick.clean"> <clean build="locker"/> </target> - - <target name="docs.clean"> <clean build="docs"/> <delete dir="${build.dir}/manmaker" includeemptydirs="yes" quiet="yes" failonerror="no"/> </target> - <target name="dist.clean"> <delete dir="${dists.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/> </target> - - <target name="junit.clean"> <clean build="junit"/> </target> - - <target name="all.clean" depends="locker.clean, docs.clean, junit.clean"> - <clean build="sbt"/> <clean build="osgi"/> - </target> - - <!-- Used by the scala-installer script --> - <target name="allallclean" depends="all.clean, dist.clean"/> - -<!-- =========================================================================== - LOCAL DEPENDENCIES -============================================================================ --> - - <!-- For local development only. We only allow released versions of Scala for STARR. - This builds quick (core only) and publishes it with a generated version number, - saving it as starr.version in build.properties, so this compiler will be used as STARR in your next build - NOTES: - - to speed things up, you can also pass -Dlocker.skip=1 - --> - <target name="replacestarr" depends="init.git" description="Produces a new STARR from current sources. Publishes core locally with a generated version number, - stored in build.properties as starr.version (overriding the one in versions.properties)."> - <antcall target="publish-core-local"> - <param name="maven.version.suffix" value="-STARR-${git.commit.sha}-SNAPSHOT"/> - <param name="docs.skip" value="1"/> - <param name="scalac.args.optimise" value="-opt:l:classpath"/> - <param name="update.starr.version" value="alright then"/> - </antcall> - </target> - - -<!-- =========================================================================== - LOCAL REFERENCE BUILD (LOCKER) -============================================================================ --> - <target name="locker.start" depends="init"> - <condition property="locker.locked"><available file="${build-locker.dir}/locker.locked"/></condition></target> - - <target name="locker.lib" depends="locker.start" unless="locker.locked"> - <!-- "mixed" needed for JFunction classes in scala.runtime.java8 --> - <staged-build with="starr" stage="locker" project="library" srcpath="${src.dir}/library" includes="lib.includes" mixed="true"/></target> - - <target name="locker.reflect" depends="locker.lib" unless="locker.locked"> - <staged-build with="starr" stage="locker" project="reflect"/></target> - - <target name="locker.comp" depends="locker.reflect" unless="locker.locked"> - <staged-build with="starr" stage="locker" project="compiler"/></target> - - <target name="locker.done" depends="locker.comp"> - <mkdir dir="${build-locker.dir}"/> - <touch file="${build-locker.dir}/locker.locked" verbose="no"/> - </target> - <target name="locker.unlock"> <delete file="${build-locker.dir}/locker.locked"/> - <delete file="${build-locker.dir}/*.complete"/></target> - -<!-- =========================================================================== - QUICK BUILD (QUICK) -============================================================================ --> - <target name="quick.start" depends="locker.done"/> - - <target name="quick.lib" depends="quick.start"> - <!-- "mixed" needed for JFunction classes in scala.runtime.java8 --> - <staged-build with="locker" stage="quick" project="library" srcpath="${src.dir}/library" includes="lib.rootdoc.includes" mixed="true"/></target> - - <target name="quick.reflect" depends="quick.lib"> - <staged-build with="locker" stage="quick" project="reflect"/> </target> - - <target name="quick.comp" depends="quick.reflect"> - <staged-build with="locker" stage="quick" project="compiler"/> </target> - - <target name="quick.repl" depends="quick.comp, quick.interactive"> - <staged-build with="locker" stage="quick" project="repl"/> - <staged-build with="locker" stage="quick" project="repl-jline"/> - - <staged-pack project="repl-jline"/> - - <!-- make jline_embedded jar with classes of repl-jline and jline, then shade--> - <jarjar jarfile="${build-pack.dir}/${repl-jline.targetdir}/scala-repl-jline-embedded.jar" whenmanifestonly="fail"> - <zipfileset src="${jline:jline:jar}"/> - <zipfileset src="${build-pack.dir}/${repl-jline.targetdir}/${repl-jline.targetjar}"/> - - <rule pattern="org.fusesource.**" result="scala.tools.fusesource_embedded.@1"/> - <rule pattern="jline.**" result="scala.tools.jline_embedded.@1"/> - <rule pattern="scala.tools.nsc.interpreter.jline.**" result="scala.tools.nsc.interpreter.jline_embedded.@1"/> - <keep pattern="scala.tools.**"/> - </jarjar> - - <!-- unzip jar to repl's class dir to obtain - - standard repl-jline - - a shaded repl-jline (scala/tools/nsc/interpreter/jline_embedded) & jline (scala.tools.jline_embedded) - --> - <copy todir="${build-quick.dir}/classes/repl"> - <zipfileset src="${build-pack.dir}/${repl-jline.targetdir}/${repl-jline.targetjar}"/> - <zipfileset src="${build-pack.dir}/${repl-jline.targetdir}/scala-repl-jline-embedded.jar"/> - </copy> - </target> - - <target name="quick.scaladoc" depends="quick.comp"> - <staged-build with="locker" stage="quick" project="scaladoc"/> </target> - - <target name="quick.interactive" depends="quick.comp, quick.scaladoc"> - <staged-build with="locker" stage="quick" project="interactive"/> </target> - - <target name="quick.scalap" depends="quick.repl"> - <staged-build with="locker" stage="quick" project="scalap"/> </target> - - - - <target name="quick.modules" depends="quick.repl, quick.scaladoc, quick.interactive, quick.scalap"/> - - <target name="quick.bin" depends="quick.lib, quick.reflect, quick.comp, quick.modules"> - <staged-bin stage="quick" classpathref="quick.bin.tool.path"/> - </target> - - <target name="quick.done" depends="quick.bin"/> - <target name="quick-opt" description="Optimized version of quick.done."> <optimized name="quick.done"/></target> - - -<!-- =========================================================================== - PACKED QUICK BUILD (PACK) -============================================================================ --> - <target name="pack.lib" depends="quick.lib"> <staged-pack project="library"/> - <taskdef resource="scala/tools/ant/antlib.xml" classpathref="pack.lib.path"/> - <jarlister file="${library.jar}"/> - </target> - - <target name="pack.reflect" depends="quick.reflect"> <staged-pack project="reflect"/> </target> - - <!-- TODO modularize compiler. Remove other quick targets when they become modules. --> - <target name="pack.comp" depends="quick.comp, quick.scaladoc, quick.interactive, quick.repl"> - <staged-pack project="compiler" manifest="${build-pack.dir}/META-INF/MANIFEST.MF"> - <pre> <!-- TODO the files copied here do not influence actuality of this target (nor does the manifest) --> - <copy todir="${build-pack.dir}/lib"> - <resources refid="repl.fileset"/> - <mapper classpathref="maven-ant-tasks.classpath" classname="org.apache.maven.artifact.ant.VersionMapper" from="${repl.deps.versions}" to="flatten"/> - </copy> - <copy todir="${build-pack.dir}/lib"> - <fileset dir="${lib-extra.dir}"> - <include name="**/*.jar"/> - </fileset> - </copy> - <mkdir dir="${build-pack.dir}/META-INF"/> - <copy file="${basedir}/META-INF/MANIFEST.MF" toDir="${build-pack.dir}/META-INF"/> - <manifest file="${build-pack.dir}/META-INF/MANIFEST.MF" mode="update"> - <attribute name="Bundle-Version" value="${version.number}"/> - <attribute name="Class-Path" value="scala-reflect.jar scala-library.jar"/> - </manifest> - </pre> - <!-- JSR-223 support introduced in 2.11 --> - <jar-opts> - <service type="javax.script.ScriptEngineFactory" provider="scala.tools.nsc.interpreter.Scripted$Factory"/> - </jar-opts> - </staged-pack> - </target> - - <!-- TODO modularize compiler. These targets are currently not used. - <target name="pack.scaladoc" depends="quick.scaladoc"> <staged-pack project="scaladoc"/> </target> - <target name="pack.interactive" depends="quick.interactive"> <staged-pack project="interactive"/> </target> - --> - - <target name="pack.scalap" depends="quick.scalap"> <staged-pack project="scalap"/> </target> - - <target name="pack.core" depends="pack.reflect, pack.comp, pack.lib"/> - - <!-- TODO modularize compiler: pack.scaladoc, pack.interactive, --> - <target name="pack.modules" depends="pack.scalap"> - <copy todir="${build-pack.dir}/lib"> - <path refid="external-modules-nocore" /> - <mapper type="flatten" /> - </copy> - </target> - - <!-- depends on pack.core for scaladoc --> - <target name="scaladoc.task" depends="pack.core, pack.modules" unless="docs.skip"> - <taskdef resource="scala/tools/ant/antlib.xml" classpathref="scaladoc.classpath"/> - </target> - - <target name="pack.partest-extras" depends="quick.comp"> - <!-- compile compiler-specific parts of partest --> - <staged-build with="quick" stage="quick" project="partest-extras" /> - <staged-build with="quick" stage="quick" project="partest-javaagent" /> - - <staged-pack project="partest-extras"/> - <staged-pack project="partest-javaagent" - manifest="${src.dir}/partest-javaagent/scala/tools/partest/javaagent/MANIFEST.MF"/> - </target> - - <target name="pack.bin" depends="pack.core, pack.modules, pack.partest-extras"> - <staged-bin stage="pack"/> - </target> - - <!-- depend on quick.done so quick.bin is run when pack.done is --> - <target name="pack.done" depends="quick.done, pack.bin"/> - - -<!-- =========================================================================== - BOOTSTRAPPING BUILD (STRAP) -============================================================================ --> - <target name="strap.done" depends="pack.done"> - <!-- "mixed" needed for JFunction classes in scala.runtime.java8 --> - <staged-build with="pack" stage="strap" project="library" srcpath="${src.dir}/library" includes="lib.rootdoc.includes" mixed="true"/> - <staged-build with="pack" stage="strap" project="reflect"/> - <staged-build with="pack" stage="strap" project="compiler"/> - </target> - - <target name="strap-opt" description="Optimized version of strap.done."> <optimized name="strap.done"/></target> - - -<!-- =========================================================================== - OSGi Artifacts -============================================================================ --> - <!-- This task takes the output of the pack stage and OSGi-fies the jars based on the bnd files in src/build/bnd - This means adding manifests and enforcing the Exports clauses (removing non-exported classes!) - These jars are then copied to the distribution and published to maven. - --> - - - <target name="osgi.core" depends="pack.core"> - <mkdir dir="${build-osgi.dir}"/> - - <uptodate property="osgi.bundles.available" targetfile="${build-osgi.dir}/bundles.core.complete"> - <srcresources> - <fileset dir="${basedir}"> - <include name="build.xml"/> - <include name="build-ant-macros.xml"/> - <include name="src/build/bnd/*.bnd"/> - </fileset> - <filelist> - <file name="${library.jar}"/> - <file name="${reflect.jar}"/> - <file name="${compiler.jar}"/> - </filelist> - </srcresources> - </uptodate> - - <if><not><isset property="osgi.bundles.available"/></not><then> - <stopwatch name="osgi.core.timer"/> - <make-bundle project="library"> - <fileset dir="${src.dir}/library"/> - </make-bundle> - - <make-bundle project="reflect"> - <fileset dir="${src.dir}/reflect"/> - </make-bundle> - - <!-- TODO modularize compiler. Remove the other class dirs as soon as they become modules --> - <make-bundle project="compiler"> - <fileset dir="${src.dir}/compiler"/> - <fileset dir="${src.dir}/scaladoc"/> - <fileset dir="${src.dir}/interactive"/> - <fileset dir="${src.dir}/repl"/> - </make-bundle> - - <touch file="${build-osgi.dir}/bundles.core.complete" verbose="no"/> - <stopwatch name="osgi.core.timer" action="total"/> - </then></if> - </target> - - <target name="osgi.done" depends="pack.done, osgi.core"> - <uptodate property="osgi.all.bundles.available" targetfile="${build-osgi.dir}/bundles.all.complete"> - <srcresources> - <fileset dir="${basedir}"> - <include name="build.xml"/> - <include name="build-ant-macros.xml"/> - <include name="src/build/bnd/*.bnd"/> - </fileset> - <filelist> - <!-- TODO modularize compiler - <include name="${interactive.jar}"/> - <include name="${scaladoc.jar}"/> - --> - <file name="${xml.jar}"/> - <file name="${swing.jar}"/> - </filelist> - </srcresources> - </uptodate> - - <if><not><isset property="osgi.all.bundles.available"/></not><then> - <stopwatch name="osgi.all.timer"/> - - <!-- TODO modularize compiler - TODO: refactor so that we can restrict exported packages to scala.tools.nsc.doc.* - move ant task, partest stuff to other jars, - and move scala.tools.nsc.ScalaDoc main class to scala.tools.nsc.doc - <make-bundle project="scaladoc"> - <fileset dir="${src.dir}/scaladoc"/> - </make-bundle> - - TODO: refactor so that we can restrict exported packages to scala.tools.nsc.interactive.* - <make-bundle project="interactive"> - <fileset dir="${src.dir}/interactive"/> - </make-bundle> - --> - - <make-bundle project="parser-combinators"/> - <make-bundle project="xml"/> - <make-bundle project="swing"/> - - <touch file="${build-osgi.dir}/bundles.all.complete" verbose="no"/> - <stopwatch name="osgi.all.timer" action="total"/> - </then></if> - </target> - - -<!-- =========================================================================== - TEST SUITE -============================================================================ --> - <!-- bootstrapping stability: compare {quick,strap}/(lib|reflect|comp) --> - <target name="test.stability" depends="strap.done"> - <exec osfamily="unix" vmlauncher="false" executable="${basedir}/tools/stability-test.sh" failonerror="true" /> - <!-- I think doing it this way means it will auto-pass on windows... that's the idea. If not, something like this. --> - <!-- <exec osfamily="windows" executable="foo" failonerror="false" failifexecutionfails="false" /> --> - </target> - - <target name="test.stability-opt" description="Optimized version of test.stability."> <optimized name="test.stability"/></target> - - <target name="test.osgi.init" depends="osgi.done"> - <uptodate property="test.osgi.available" targetfile="${build-osgi.dir}/test-compile.complete"> - <srcfiles dir="${test.osgi.src}"> - <include name="**/*.scala"/> - </srcfiles> - </uptodate> - </target> - - <target name="test.osgi.comp" depends="test.osgi.init, quick.done" unless="test.osgi.available"> - <stopwatch name="test.osgi.compiler.timer"/> - <mkdir dir="${test.osgi.classes}"/> - <scalacfork - destdir="${test.osgi.classes}" - compilerpathref="quick.compiler.path" - params="${scalac.args.quick}" - srcdir="${test.osgi.src}" - jvmargs="${scalacfork.jvmargs}"> - <include name="**/*.scala"/> - <compilationpath refid="test.osgi.compiler.build.path.felix"/> - </scalacfork> - <touch file="${build-osgi.dir}/test-compile.complete" verbose="no"/> - <stopwatch name="test.osgi.compiler.timer" action="total"/> - </target> - - <target name="test.osgi" depends="test.osgi.comp"> - <if><isset property="test.osgi.skip"/><then> - <echo message="Skipping OSGi JUnit tests"/> - </then><else> - <echo message="Running OSGi JUnit tests. Output in ${build-osgi.dir}"/> - <stopwatch name="test.osgi.timer"/> - <mkdir dir="${test.osgi.classes}"/> - - <echo message="Test pass 1 of 2 using Apache Felix ${osgi.felix.version}"/> - <junit fork="yes" haltonfailure="yes"> - <classpath refid="test.osgi.compiler.build.path.felix"/> - <jvmarg value="-Duser.home=${user.home}"/> - <batchtest fork="yes" todir="${build-osgi.dir}"> - <fileset dir="${test.osgi.classes}"> - <include name="**/*Test.class"/> - </fileset> - </batchtest> - <formatter type="xml" /> - </junit> - - <echo message="Test pass 2 of 2 using Eclipse Equinox ${osgi.equinox.version}"/> - <junit fork="yes" haltonfailure="yes"> - <classpath refid="test.osgi.compiler.build.path.equinox"/> - <jvmarg value="-Duser.home=${user.home}"/> - <batchtest fork="yes" todir="${build-osgi.dir}"> - <fileset dir="${test.osgi.classes}"> - <include name="**/*Test.class"/> - </fileset> - </batchtest> - <formatter type="xml" /> <!-- silenced by having it use a file; I tried for an hour to use other formatters but classpath issues drove me to this usefile="false" --> - </junit> - <stopwatch name="test.osgi.timer" action="total"/> - </else></if> - </target> - - -<!-- =========================================================================== - SBT Compiler Interface -============================================================================ --> - <target name="test.sbt" depends="quick.done"> - <if><not><and> - <available file="${sbt.interface.jar}"/> - <available file="${sbt.interface.src.jar}"/></and></not> - <then> - <!-- Ensure directories exist --> - <mkdir dir="${sbt.src.dir}"/> - <mkdir dir="${sbt.lib.dir}"/> - - <get src="${sbt.interface.url}" dest="${sbt.interface.jar}"/> - <get src="${sbt.interface.src.url}" dest="${sbt.interface.src.jar}"/> - - <!-- Explode sources --> - <unzip src="${sbt.interface.src.jar}" dest="${sbt.src.dir}"/> - </then></if> - - <stopwatch name="quick.sbt-interface.timer"/> - <mkdir dir="${build-sbt.dir}/classes"/> - <scalacfork - destdir="${build-sbt.dir}/classes" - compilerpathref="quick.compiler.path" - params="${scalac.args.quick}" - srcdir="${sbt.src.dir}" - jvmargs="${scalacfork.jvmargs}"> - <include name="**/*.scala"/> - <compilationpath refid="sbt.compile.build.path"/> - </scalacfork> - <touch file="${build-sbt.dir}/sbt-interface.complete" verbose="no"/> - <stopwatch name="quick.sbt-interface.timer" action="total"/> - </target> - - <target name="test.junit.comp" depends="pack.done"> - <stopwatch name="test.junit.compiler.timer"/> - <mkdir dir="${test.junit.classes}"/> - <javac - debug="true" - srcdir="${test.junit.src}" - destdir="${test.junit.classes}" - classpathref="test.junit.compiler.build.path" - target="1.8" - source="1.8" - compiler="javac1.8" - includes="**/*.java"/> - <scalacfork - destdir="${test.junit.classes}" - compilerpathref="quick.compiler.path" - params="${scalac.args.quick}" - srcdir="${test.junit.src}" - jvmargs="${scalacfork.jvmargs}"> - <include name="**/*.scala"/> - <compilationpath refid="test.junit.compiler.build.path"/> - </scalacfork> - <touch file="${build-junit.dir}/test-compile.complete" verbose="no"/> - <stopwatch name="test.junit.compiler.timer" action="total"/> - </target> - - <target name="test.junit" depends="test.junit.comp"> - <stopwatch name="test.junit.timer"/> - <mkdir dir="${test.junit.classes}"/> - <echo message="Note: details of failed tests will be output to ${build-junit.dir}"/> - - <propertyfile file = "${test.junit.classes}/classpath.properties"> - <entry key = "test.junit.compiler.build.path" value="${toString:test.junit.compiler.build.path}"/> - </propertyfile> - - <if><isset property="test.method" /><then><property name="test.methods" value="${test.method}" /></then></if> - <junit fork="yes" haltonfailure="yes" printsummary="on"> - <classpath refid="test.junit.compiler.build.path"/> - <test fork="yes" todir="${build-junit.dir}" if="test.class" unless="test.methods" name="${test.class}" /> - <test fork="yes" todir="${build-junit.dir}" if="test.methods" name="${test.class}" methods="${test.methods}" /> - <batchtest fork="yes" todir="${build-junit.dir}" unless="test.class"> - <fileset dir="${test.junit.classes}"> - <include name="**/*Test.class"/> - </fileset> - </batchtest> - <formatter type="plain"/> - </junit> - <stopwatch name="test.junit.timer" action="total"/> - </target> - - <!-- See test/build-partest.xml for the macro(s) being used here. --> - <target name="partest.task" depends="pack.done"> - <!-- note the classpathref! this is the classpath used to run partest, - so it must have the new compiler.... --> - <taskdef - classpathref="partest.compilation.path" - resource="scala/tools/partest/antlib.xml"/> - </target> - - <target name="test.suite.init" depends="partest.task"> - <!-- read by test/partest to determine classpath used to run partest --> - <propertyfile file = "build/pack/partest.properties"> - <!-- TODO: change "partest.classpath" to "partest.runtime.classpath" or something --> - <entry key = "partest.classpath" value="${toString:partest.compilation.path}"/> - </propertyfile> - </target> - - <target name="test.suite" depends="test.suite.init"> - <testSuite kinds="pos neg run jvm res scalap scalacheck specialized instrumented"/> - </target> - - <target name="test.suite.color" depends="test.suite.init"> - <testSuite colors="8" kinds="pos neg run jvm res scalap scalacheck specialized instrumented"/> - </target> - - <target name="test.suite.quick" depends="init, quick.done"> - <path id="test.suite.path"> - <path refid="quick.bin.tool.path"/> - <path refid="quick.interactive.build.path"/> - <path refid="partest.compilation.path.noncore"/> - </path> - <property name="pcp" value="${toString:test.suite.path}"/> - <taskdef classpathref="test.suite.path" resource="scala/tools/partest/antlib.xml"/> - <testSuite colors="8" kinds="pos neg run jvm res scalap scalacheck specialized instrumented" pcp="${pcp}"/> - </target> - - <target name="test.run" depends="test.suite.init"> - <testSuite kinds="run jvm"/> - </target> - - <target name="test.scaladoc" depends="test.suite.init"> - <testSuite kinds="run scalacheck" srcdir="scaladoc"/> - </target> - - <target name="test.interactive" depends="test.suite.init"> - <testSuite kinds="presentation"/> - </target> - - <!-- for use in PR validation, where stability is rarely broken, so we're going to use starr for locker, - and skip test.stability (which requires locker == quick) --> - <target name="test.core" depends="test.osgi, test.sbt, test.bc, test.junit, test.interactive, test.scaladoc, test.suite"/> - <target name="test.done" depends="test.core, test.stability"/> - -<!-- =========================================================================== - BINARY COMPATIBILITY TESTING -============================================================================ --> - <target name="bc.init" depends="init" if="test.bc.skip"> - <!-- if test.bc.skip is set, make sure that pc.prepare is not executed either --> - <property name="maven-deps-done-mima" value="true"/> - </target> - - <target name="bc.prepare" depends="bc.init" unless="maven-deps-done-mima"> - <property name="bc-reference-version" value="2.11.0"/> - - <property name="bc-build.dir" value="${build.dir}/bc"/> - <!-- Obtain mima --> - <mkdir dir="${bc-build.dir}"/> - <!-- Pull down MIMA --> - <artifact:dependencies pathId="mima.classpath"> - <dependency groupId="com.typesafe" artifactId="mima-reporter_2.10" version="0.1.8"/> - </artifact:dependencies> - <artifact:dependencies pathId="old.bc.classpath"> - <dependency groupId="org.scala-lang" artifactId="scala-library" version="${bc-reference-version}"/> - <dependency groupId="org.scala-lang" artifactId="scala-reflect" version="${bc-reference-version}"/> - </artifact:dependencies> - <property name="maven-deps-done-mima" value="true"/> - </target> - - <target name="test.bc-opt" description="Optimized version of test.bc."> <optimized name="test.bc"/></target> - <target name="test.bc" depends="bc.prepare, pack.lib, pack.reflect" unless="test.bc.skip"> - <echo message="binary compatibility testing disabled in the 2.12.x branch"/> - <!-- <bc.check project="library"/> --> - <!-- <bc.check project="reflect"/> --> - </target> - -<!-- =========================================================================== - DOCUMENTATION -============================================================================ --> - <target name="docs.start" depends="scaladoc.task" unless="docs.skip"> - <!-- Set the github commit scaladoc sources point to --> - <!-- For releases, look for the tag with the same name as the maven version --> - <condition property="scaladoc.git.commit" value="v${maven.version.number}"> - <isset property="build.release"/> - </condition> - <!-- For snapshots, if we know the commit, point scaladoc to that particular commit instead of master --> - <condition property="scaladoc.git.commit" value="${git.commit.sha}"> - <not><equals arg1="${git.commit.sha}" arg2="unknown"/></not> - </condition> - <!-- Fallback: point scaladoc to master --> - <property name="scaladoc.git.commit" value="master"/> - <!-- Compute the URL and show it --> - <property name="scaladoc.url" value="https://github.com/scala/scala/tree/${scaladoc.git.commit}/src"/> - <echo message="Scaladoc will point to ${scaladoc.url} for source files."/> - - <!-- Unless set with -Dscaladoc.<...>, these won't be activated --> - <property name="scaladoc.raw.output" value="no"/> - <property name="scaladoc.no.prefixes" value="no"/> - </target> - - <target name="docs.lib" depends="docs.start" unless="docs.skip"> - <staged-docs project="library"> - <include name="**/*.scala"/> - <exclude name="**/runtime/*$.scala"/> - <exclude name="**/runtime/ScalaRunTime.scala"/> - <exclude name="**/runtime/StringAdd.scala"/> - </staged-docs> - </target> - - <target name="docs.reflect" depends="docs.start" unless="docs.skip"> - <staged-docs project="reflect"> - <include name="**/*.scala"/> - </staged-docs> - </target> - - <target name="docs.comp" depends="docs.start" unless="docs.skip"> - <staged-docs project="compiler"> - <include name="**/*.scala"/> - </staged-docs> - </target> - - <!-- TODO modularize compiler. These targets are currently not used. - <target name="docs.scaladoc" depends="docs.start" unless="docs.skip"> - <staged-docs project="scaladoc"> - <include name="**/*.scala"/> - </staged-docs> - </target> - - <target name="docs.interactive" depends="docs.start" unless="docs.skip"> - <staged-docs project="interactive"> - <include name="**/*.scala"/> - </staged-docs> - </target> - --> - - <target name="docs.scalap" depends="docs.start" unless="docs.skip"> - <staged-docs project="scalap"> - <include name="**/*.scala"/> - </staged-docs> - </target> - - <target name="docs.core" depends="docs.lib, docs.reflect, docs.comp" unless="docs.skip"/> - <!-- TODO modularize compiler: docs.scaladoc, docs.interactive, --> - <target name="docs.done" depends="docs.core, docs.scalap" unless="docs.skip"/> - - <!-- doc/ and man/ --> - <target name="pack.doc" depends="scaladoc.task" unless="docs.skip"> <!-- depends on scaladoc.task for scalac taskdef --> - <mkdir dir="${build-pack.dir}/doc"/> - <copy toDir="${build-pack.dir}/doc" overwrite="true"> - <fileset dir="${doc.dir}"/> - </copy> - - <mkdir dir="${build-pack.dir}/doc/tools"/> - <mkdir dir="${build-pack.dir}/man/man1"/> - <staged-uptodate stage="manual" project="manual"> - <check><srcfiles dir="${src.dir}/manual"/></check> - <do> - <mkdir dir="${build.dir}/manmaker/classes"/> - <scalac - destdir="${build.dir}/manmaker/classes" - classpathref="manual.build.path" - srcdir="${src.dir}/manual" - includes="**/*.scala" - addparams="${scalac.args.all} -language:implicitConversions"/> - <mkdir dir="${build-manual.dir}/genman/man1"/> - <taskdef name="genman" - classname="scala.tools.docutil.ManMaker" - classpathref="manual.build.path"/> - <genman command="fsc, scala, scalac, scaladoc, scalap" - htmlout="${build-pack.dir}/doc/tools" - manout="${build-manual.dir}/genman"/> - </do> - </staged-uptodate> - - <!-- On Windows source and target files can't be the same ! --> - <fixcrlf - srcdir="${build-manual.dir}/genman" - destdir="${build-pack.dir}/man" - eol="unix" includes="**/*.1"/> - <copy todir="${build-pack.dir}/doc/tools" overwrite="true"> - <fileset dir="${src.dir}/manual/scala/tools/docutil/resources"> - <include name="**/*.html"/> - <include name="**/*.css"/> - <include name="**/*.gif"/> - <include name="**/*.png"/> - <include name="**/*.eot"/> - <include name="**/*.ttf"/> - <include name="**/*.woff"/> - <include name="**/*.svg"/> - </fileset> - </copy> - </target> - -<!-- =========================================================================== -MAIN DISTRIBUTION PACKAGING -============================================================================ --> - <target name="pack-maven.core" depends="osgi.core, docs.core"> - <property name="dist.maven" value="${dists.dir}/maven/${version.number}"/> - <mkdir dir="${dist.maven}"/> - - <mvn-package project="library"/> - <mvn-package project="reflect"/> - <mvn-package project="compiler"/> - - <copy tofile="${dist.maven}/scala-library-all/scala-library-all-pom.xml" - file="${src.dir}/build/maven/scala-library-all-pom.xml" overwrite="true"/> - - <!-- for replacestarr --> - <if><isset property="update.starr.version"/><then> - <echo message="From now on, ${maven.version.number} will be used as STARR (`build.properties`'s `starr.version` was modified)."/> - <propertyfile file = "build.properties"> - <entry key = "starr.version" value="${maven.version.number}"/> - </propertyfile> - </then></if> - </target> - - <target name="pack-maven.done" depends="pack-maven.core, osgi.done, docs.done, pack.bin, pack.doc"> - <!-- TODO modularize compiler - <mvn-package project="interactive"/> - <mvn-package project="scaladoc"/> - --> - - <!-- don't bother fitting scalap into the mould: it will move out soon --> - <copy tofile="${dist.maven}/scalap/scalap-pom.xml" file="${src.dir}/build/maven/scalap-pom.xml" overwrite="true"/> - <copy tofile="${dist.maven}/scalap/scalap.jar" file="${scalap.jar}" overwrite="true"/> - <jar destfile="${dist.maven}/scalap/scalap-src.jar" basedir="${src.dir}/scalap" whenmanifestonly="fail"/> - <if><not><isset property="docs.skip"/></not><then> - <jar destfile="${dist.maven}/scalap/scalap-docs.jar" basedir="${build-docs.dir}/scalap"/> - </then></if> - - <copy tofile="${dist.maven}/scala-dist/scala-dist-pom.xml" file="${src.dir}/build/maven/scala-dist-pom.xml" overwrite="true"/> - <jar whenmanifestonly="fail" destfile="${dist.maven}/scala-dist/scala-dist.jar" basedir="${build-pack.dir}"> - <include name="bin/" /> - <include name="doc/" /> - <include name="man/" /> - </jar> - </target> - -<!-- =========================================================================== - MAVEN PUBLISHING -============================================================================ --> - <target name="init.maven" depends="init"> - <property name="remote.snapshot.repository" value="https://oss.sonatype.org/content/repositories/snapshots" /> - <property name="remote.release.repository" value="https://oss.sonatype.org/service/local/staging/deploy/maven2" /> - - <property name="local.snapshot.repository" value="${user.home}/.m2/repository" /> - <property name="local.release.repository" value="${user.home}/.m2/repository" /> - - <property name="repository.credentials.id" value="sonatype-nexus" /> - <property name="settings.file" value="${user.home}/.m2/settings.xml" /> - - <if><contains string="${maven.version.number}" substring="-SNAPSHOT"/><then> - <property name="remote.repository" value="${remote.snapshot.repository}"/> - <property name="local.repository" value="${local.snapshot.repository}"/> - </then><else> - <property name="remote.repository" value="${remote.release.repository}"/> - <property name="local.repository" value="${local.release.repository}"/> - </else></if> - </target> - - <target name="publish" depends="pack-maven.done, init.maven" description="Publishes unsigned artifacts to the maven repo."> - <deploy /> - <deploy-pom name="scala-library-all"/> - <deploy-jar name="scala-dist"/> - </target> - - <target name="publish.local" depends="pack-maven.done, init.maven" description="Publishes unsigned artifacts to the local maven repo."> - <deploy local="true"/> - <deploy-pom name="scala-library-all" local="true"/> - <deploy-jar name="scala-dist" local="true"/> - </target> - - <target name="publish.signed" depends="pack-maven.done, init.maven" description="Publishes signed artifacts to the remote maven repo."> - <deploy signed="true"/> - <deploy-pom name="scala-library-all" signed="true"/> - <deploy-jar name="scala-dist" signed="true"/> - </target> - - <target name="publish-core" depends="pack-maven.core, init.maven"> - <deploy-one name="scala-compiler" /> - <deploy-one name="scala-library" /> - <deploy-one name="scala-reflect" /> - </target> - - <target name="publish-core-local" depends="pack-maven.core, init.maven"> - <deploy-one name="scala-compiler" local="true"/> - <deploy-one name="scala-library" local="true"/> - <deploy-one name="scala-reflect" local="true"/> - </target> - - <target name="publish-core-opt" description="Builds an untested optimised core (library/reflect/compiler) and publishes to maven."> - <optimized name="publish-core"/> - </target> - -<!-- =========================================================================== - VISUALIZATION -============================================================================ --> - - <target name="graph.init"> - <taskdef name="vizant" classname="vizant.Vizant" classpath="${lib-ant.dir}/vizant.jar"/> - </target> - - <target name="graph.all" depends="graph.init"> - <vizant antfile="${ant.file}" outfile="${ant.project.name}.dot" from="all.done"/> - </target> - - <target name="graph.sabbus" depends="graph.init"> - <vizant antfile="${ant.file}" outfile="${ant.project.name}.dot"/> - </target> -</project> diff --git a/compare-build-dirs-ignore-patterns b/compare-build-dirs-ignore-patterns deleted file mode 100644 index 8c8160ba15..0000000000 --- a/compare-build-dirs-ignore-patterns +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -*.complete -locker -deps -scala-continuations-*.jar -scala-parser-combinators*.jar -scala-swing*.jar -scala-xml*.jar diff --git a/compare-build-dirs.sh b/compare-build-dirs.sh deleted file mode 100755 index f6806dd422..0000000000 --- a/compare-build-dirs.sh +++ /dev/null @@ -1,5 +0,0 @@ -# Compares build directories generated by Ant and sbt build definitions -# This let's us to see how far are we from achieving perfect parity -# between the builds - -diff -X compare-build-dirs-ignore-patterns -qr build/ build-sbt/ diff --git a/project/MiMa.scala b/project/MiMa.scala index 66442fc725..6c6f5efd51 100644 --- a/project/MiMa.scala +++ b/project/MiMa.scala @@ -4,7 +4,7 @@ // both forwards and backwards incompatibilities (possibly fixed as of // https://github.com/typesafehub/migration-manager/commit/2844ffa48b6d2255aa64bd687703aec21dadd55e) // * ability to pass a filter file (https://github.com/typesafehub/migration-manager/issues/102) -// So we invoke the MiMa CLI directly; it's also what the Ant build did. +// So we invoke the MiMa CLI directly. import sbt._ import sbt.Keys._ @@ -29,8 +29,7 @@ object MiMa { prev = if (isForward) curr else prev, curr = if (isForward) prev else curr, // TODO: it would be nicer if each subproject had its own whitelist, but for now - // for compatibility with how Ant did things, there's just one at the root. - // once Ant is gone we'd be free to split it up. + // there's just one at the root. with the Ant build gone, we would be free now to split it. filter = (baseDirectory in ThisBuild).value / s"bincompat-$direction.whitelist.conf", log) } diff --git a/project/Osgi.scala b/project/Osgi.scala index 4177251aa4..8a62c9128a 100644 --- a/project/Osgi.scala +++ b/project/Osgi.scala @@ -9,9 +9,9 @@ import VersionUtil.versionProperties /** OSGi packaging for the Scala build, distilled from sbt-osgi. We do not use sbt-osgi because it * depends on a newer version of BND which gives slightly different output (probably OK to upgrade - * in the future but for now it would make comparing the sbt and ant build output harder) and does - * not allow a crucial bit of configuration that we need: Setting the classpath for BND. In sbt-osgi - * this is always `fullClasspath in Compile` whereas we want `products in Compile in packageBin`. */ + * in the future, now that the Ant build has been removed) and does not allow a crucial bit of + * configuration that we need: Setting the classpath for BND. In sbt-osgi this is always + * `fullClasspath in Compile` whereas we want `products in Compile in packageBin`. */ object Osgi { val bundle = TaskKey[File]("osgiBundle", "Create an OSGi bundle.") val bundleName = SettingKey[String]("osgiBundleName", "The Bundle-Name for the manifest.") diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 77c9d765e9..af82f8fce5 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -94,7 +94,7 @@ object ScalaOptionParser { private def stringSettingNames = List("-Xgenerate-phase-graph", "-Xmain-class", "-Xpluginsdir", "-Xshow-class", "-Xshow-object", "-Xsource-reader", "-Ydump-classes", "-Ygen-asmp", "-Ypresentation-log", "-Ypresentation-replay", "-Yrepl-outdir", "-d", "-dependencyfile", "-encoding", "-Xscript") private def pathSettingNames = List("-bootclasspath", "-classpath", "-extdirs", "-javabootclasspath", "-javaextdirs", "-sourcepath", "-toolcp") - private val phases = List("all", "parser", "namer", "packageobjects", "typer", "patmat", "superaccessors", "extmethods", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "posterasure", "lazyvals", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "delambdafy", "icode", "jvm", "terminal") + private val phases = List("all", "parser", "namer", "packageobjects", "typer", "patmat", "superaccessors", "extmethods", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "posterasure", "fields", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "delambdafy", "icode", "jvm", "terminal") private val phaseSettings = List("-Xprint-icode", "-Ystop-after", "-Yskip", "-Yshow", "-Ystop-before", "-Ybrowse", "-Ylog", "-Ycheck", "-Xprint") private def multiStringSettingNames = List("-Xmacro-settings", "-Xplugin", "-Xplugin-disable", "-Xplugin-require") private def intSettingNames = List("-Xmax-classfile-name", "-Xelide-below", "-Ypatmat-exhaust-depth", "-Ypresentation-delay", "-Yrecursion") diff --git a/project/ScalaTool.scala b/project/ScalaTool.scala index 5e3f20b1ba..98e18235c4 100644 --- a/project/ScalaTool.scala +++ b/project/ScalaTool.scala @@ -4,8 +4,6 @@ import org.apache.commons.lang3.StringUtils.replaceEach /** * A class that generates a shell or batch script to execute a Scala program. - * - * This is a simplified copy of Ant task (see scala.tools.ant.ScalaTool). */ case class ScalaTool(mainClass: String, classpath: List[String], diff --git a/project/build.sbt b/project/build.sbt index b19238f577..a604896ded 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,2 +1,2 @@ -// Add genprod to the build; It should be moved from `src/build` to `project` once the ANT build is gone +// Add genprod to the build; It should be moved from `src/build` to `project` now that the Ant build is gone sources in Compile += ((baseDirectory).value.getParentFile / "src" / "build" / "genprod.scala") diff --git a/pull-binary-libs.sh b/pull-binary-libs.sh deleted file mode 100755 index 6c94e39fe7..0000000000 --- a/pull-binary-libs.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# -# Script to pull binary artifacts for scala from the remote repository. - -# Avoid corrupting the jar cache in ~/.sbt and the ugly crash when curl is not installed -# This affects Linux systems mostly, because wget is the default download tool and curl -# is not installed at all. -curl --version &> /dev/null -if [ $? -ne 0 ] -then - echo "" - echo "Please install curl to download the jar files necessary for building Scala." - echo "" - exit 1 -fi - -. $(dirname $0)/tools/binary-repo-lib.sh - -# TODO - argument parsing... -pullJarFiles $(pwd) diff --git a/push-binary-libs.sh b/push-binary-libs.sh deleted file mode 100755 index 0a1c62a1db..0000000000 --- a/push-binary-libs.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# -# Script to push binary artifacts for scala from the remote repository. - -. $(dirname $0)/tools/binary-repo-lib.sh - -if test $# -lt 2; then - echo "Usage: $0 <username> <password>" - exit 1 -fi - -# TODO - Argument parsing for username/password. -pushJarFiles $(pwd) $1 $2 diff --git a/src/build/bnd/scala-compiler-doc.bnd b/src/build/bnd/scala-compiler-doc.bnd deleted file mode 100644 index 5b662e8cef..0000000000 --- a/src/build/bnd/scala-compiler-doc.bnd +++ /dev/null @@ -1,8 +0,0 @@ -Bundle-Name: Scala Documentation Generator -Bundle-SymbolicName: org.scala-lang.modules.scala-compiler-doc_@SCALA_BINARY_VERSION@ -ver: @SCALA_COMPILER_DOC_VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/bnd/scala-compiler-interactive.bnd b/src/build/bnd/scala-compiler-interactive.bnd deleted file mode 100644 index fbfff60801..0000000000 --- a/src/build/bnd/scala-compiler-interactive.bnd +++ /dev/null @@ -1,8 +0,0 @@ -Bundle-Name: Scala Interactive Compiler -Bundle-SymbolicName: org.scala-lang.modules.scala-compiler-interactive_@SCALA_BINARY_VERSION@ -ver: @SCALA_COMPILER_INTERACTIVE_VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/bnd/scala-compiler.bnd b/src/build/bnd/scala-compiler.bnd deleted file mode 100644 index c12c84c3f9..0000000000 --- a/src/build/bnd/scala-compiler.bnd +++ /dev/null @@ -1,12 +0,0 @@ -Bundle-Name: Scala Compiler -Bundle-SymbolicName: org.scala-lang.scala-compiler -ver: @VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: jline.*;resolution:=optional, \ - org.apache.tools.ant.*;resolution:=optional, \ - scala.xml.*;version="${range;[====,====];@XML_VERSION@}";resolution:=optional, \ - scala.*;version="${range;[==,=+);${ver}}", \ - * -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/bnd/scala-library.bnd b/src/build/bnd/scala-library.bnd deleted file mode 100644 index e211c5d1ad..0000000000 --- a/src/build/bnd/scala-library.bnd +++ /dev/null @@ -1,8 +0,0 @@ -Bundle-Name: Scala Standard Library -Bundle-SymbolicName: org.scala-lang.scala-library -ver: @VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: sun.misc;resolution:=optional, * -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/bnd/scala-parser-combinators.bnd b/src/build/bnd/scala-parser-combinators.bnd deleted file mode 100644 index 515084f4a8..0000000000 --- a/src/build/bnd/scala-parser-combinators.bnd +++ /dev/null @@ -1,8 +0,0 @@ -Bundle-Name: Scala Parser Combinators Library -Bundle-SymbolicName: org.scala-lang.modules.scala-parser-combinators -ver: @PARSER_COMBINATORS_VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/bnd/scala-reflect.bnd b/src/build/bnd/scala-reflect.bnd deleted file mode 100644 index 59db311f8d..0000000000 --- a/src/build/bnd/scala-reflect.bnd +++ /dev/null @@ -1,10 +0,0 @@ -Bundle-Name: Scala Reflect -Bundle-SymbolicName: org.scala-lang.scala-reflect -ver: @VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: scala.*;version="${range;[==,=+);${ver}}", \ - scala.tools.nsc;resolution:=optional;version="${range;[==,=+);${ver}}", \ - * -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/bnd/scala-swing.bnd b/src/build/bnd/scala-swing.bnd deleted file mode 100644 index 24cd9f6f90..0000000000 --- a/src/build/bnd/scala-swing.bnd +++ /dev/null @@ -1,8 +0,0 @@ -Bundle-Name: Scala Swing -Bundle-SymbolicName: org.scala-lang.modules.scala-swing -ver: @SCALA_SWING_VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/bnd/scala-xml.bnd b/src/build/bnd/scala-xml.bnd deleted file mode 100644 index b7b19824e8..0000000000 --- a/src/build/bnd/scala-xml.bnd +++ /dev/null @@ -1,8 +0,0 @@ -Bundle-Name: Scala XML Library -Bundle-SymbolicName: org.scala-lang.modules.scala-xml -ver: @XML_VERSION@ -Bundle-Version: ${ver} -Export-Package: *;version=${ver} -Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* -Bundle-RequiredExecutionEnvironment: JavaSE-1.8 -Include-Resource: @@SOURCE_JARNAME@ diff --git a/src/build/maven/scala-compiler-doc-pom.xml b/src/build/maven/scala-compiler-doc-pom.xml deleted file mode 100644 index 86ca3f865b..0000000000 --- a/src/build/maven/scala-compiler-doc-pom.xml +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang.modules</groupId> - <artifactId>scala-compiler-doc_@SCALA_BINARY_VERSION@</artifactId> - <packaging>jar</packaging> - <version>@SCALA_COMPILER_DOC_VERSION@</version> - <name>Scala Documentation Generator</name> - <description>Documentation generator for the Scala Programming Language</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <dependencies> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-compiler</artifactId> - <version>@VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang.modules</groupId> - <artifactId>scala-xml_@SCALA_BINARY_VERSION@</artifactId> - <version>@XML_VERSION@</version> - </dependency> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/build/maven/scala-compiler-interactive-pom.xml b/src/build/maven/scala-compiler-interactive-pom.xml deleted file mode 100644 index d3e5e0b834..0000000000 --- a/src/build/maven/scala-compiler-interactive-pom.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang.modules</groupId> - <artifactId>scala-compiler-interactive_@SCALA_BINARY_VERSION@</artifactId> - <packaging>jar</packaging> - <version>@SCALA_COMPILER_INTERACTIVE_VERSION@</version> - <name>Scala Interactive Compiler</name> - <description>Interactive Compiler for the Scala Programming Language</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <dependencies> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-compiler</artifactId> - <version>@VERSION@</version> - </dependency> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/build/maven/scala-compiler-pom.xml b/src/build/maven/scala-compiler-pom.xml deleted file mode 100644 index 9c157d17d9..0000000000 --- a/src/build/maven/scala-compiler-pom.xml +++ /dev/null @@ -1,65 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang</groupId> - <artifactId>scala-compiler</artifactId> - <packaging>jar</packaging> - <version>@VERSION@</version> - <name>Scala Compiler</name> - <description>Compiler for the Scala Programming Language</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <dependencies> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-library</artifactId> - <version>@VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-reflect</artifactId> - <version>@VERSION@</version> - </dependency> - <!-- TODO modularize compiler: these dependencies will disappear when the compiler is modularized --> - <dependency> <!-- for scala-compiler-doc --> - <groupId>org.scala-lang.modules</groupId> - <artifactId>scala-xml_@SCALA_BINARY_VERSION@</artifactId> - <version>@XML_VERSION@</version> - </dependency> - <dependency> <!-- for scala-compiler-repl; once it moves there, make it required --> - <groupId>jline</groupId> - <artifactId>jline</artifactId> - <version>@JLINE_VERSION@</version> - <optional>true</optional> - </dependency> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/build/maven/scala-dist-pom.xml b/src/build/maven/scala-dist-pom.xml deleted file mode 100644 index ce511661b0..0000000000 --- a/src/build/maven/scala-dist-pom.xml +++ /dev/null @@ -1,69 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang</groupId> - <artifactId>scala-dist</artifactId> - <packaging>jar</packaging> - <version>@VERSION@</version> - <name>Scala Distribution Artifacts</name> - <description>The Artifacts Distributed with Scala</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <dependencies> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-library-all</artifactId> - <version>@VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-compiler</artifactId> - <version>@VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scalap</artifactId> - <version>@VERSION@</version> - </dependency> - <!-- duplicated from scala-compiler, where it's optional, - so that resolving scala-dist's transitive dependencies does not include jline, - even though we need to include it in the dist, but macros depending on the compiler - shouldn't have to require jline... - another reason to modularize and move the dependency to scala-compiler-repl - TODO: remove duplication once we have the scala-compiler-repl module --> - <dependency> - <groupId>jline</groupId> - <artifactId>jline</artifactId> - <version>@JLINE_VERSION@</version> - </dependency> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/build/maven/scala-library-all-pom.xml b/src/build/maven/scala-library-all-pom.xml deleted file mode 100644 index 4620c620dc..0000000000 --- a/src/build/maven/scala-library-all-pom.xml +++ /dev/null @@ -1,68 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang</groupId> - <artifactId>scala-library-all</artifactId> - <packaging>pom</packaging> - <version>@VERSION@</version> - <name>Scala Library Powerpack</name> - <description>The Scala Standard Library and Official Modules</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <dependencies> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-library</artifactId> - <version>@VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-reflect</artifactId> - <version>@VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang.modules</groupId> - <artifactId>scala-xml_@SCALA_BINARY_VERSION@</artifactId> - <version>@XML_VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang.modules</groupId> - <artifactId>scala-parser-combinators_@SCALA_BINARY_VERSION@</artifactId> - <version>@PARSER_COMBINATORS_VERSION@</version> - </dependency> - <dependency> - <groupId>org.scala-lang.modules</groupId> - <artifactId>scala-swing_@SCALA_BINARY_VERSION@</artifactId> - <version>@SCALA_SWING_VERSION@</version> - </dependency> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/build/maven/scala-library-pom.xml b/src/build/maven/scala-library-pom.xml deleted file mode 100644 index e27f8fb12f..0000000000 --- a/src/build/maven/scala-library-pom.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang</groupId> - <artifactId>scala-library</artifactId> - <packaging>jar</packaging> - <version>@VERSION@</version> - <name>Scala Library</name> - <description>Standard library for the Scala Programming Language</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <properties> - <info.apiURL>http://www.scala-lang.org/api/@VERSION@/</info.apiURL> - </properties> - <dependencies> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/build/maven/scala-reflect-pom.xml b/src/build/maven/scala-reflect-pom.xml deleted file mode 100644 index f7f3c8bc08..0000000000 --- a/src/build/maven/scala-reflect-pom.xml +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang</groupId> - <artifactId>scala-reflect</artifactId> - <packaging>jar</packaging> - <version>@VERSION@</version> - <name>Scala Compiler</name> - <description>Compiler for the Scala Programming Language</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <properties> - <info.apiURL>http://www.scala-lang.org/api/@VERSION@/</info.apiURL> - </properties> - <dependencies> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-library</artifactId> - <version>@VERSION@</version> - </dependency> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/build/maven/scalap-pom.xml b/src/build/maven/scalap-pom.xml deleted file mode 100644 index acdd44f19b..0000000000 --- a/src/build/maven/scalap-pom.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0"?> -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <groupId>org.scala-lang</groupId> - <artifactId>scalap</artifactId> - <packaging>jar</packaging> - <version>@VERSION@</version> - <name>Scalap</name> - <description>bytecode analysis tool</description> - <url>http://www.scala-lang.org/</url> - <inceptionYear>2002</inceptionYear> - <organization> - <name>LAMP/EPFL</name> - <url>http://lamp.epfl.ch/</url> - </organization> - <licenses> - <license> - <name>BSD 3-Clause</name> - <url>http://www.scala-lang.org/license.html</url> - <distribution>repo</distribution> - </license> - </licenses> - <scm> - <connection>scm:git:git://github.com/scala/scala.git</connection> - <url>https://github.com/scala/scala.git</url> - </scm> - <issueManagement> - <system>JIRA</system> - <url>https://issues.scala-lang.org/</url> - </issueManagement> - <dependencies> - <dependency> - <groupId>org.scala-lang</groupId> - <artifactId>scala-compiler</artifactId> - <version>@VERSION@</version> - </dependency> - </dependencies> - <developers> - <developer> - <id>lamp</id> - <name>EPFL LAMP</name> - </developer> - <developer> - <id>Lightbend</id> - <name>Lightbend, Inc.</name> - </developer> - </developers> -</project> diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index 091d42bb6d..581ce8256a 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -49,13 +49,13 @@ trait Reshape { if (discard) hk else ta case classDef @ ClassDef(mods, name, params, impl) => val Template(parents, self, body) = impl - var body1 = trimAccessors(classDef, reshapeLazyVals(body)) + var body1 = trimAccessors(classDef, body) body1 = trimSyntheticCaseClassMembers(classDef, body1) val impl1 = Template(parents, self, body1).copyAttrs(impl) ClassDef(mods, name, params, impl1).copyAttrs(classDef) case moduledef @ ModuleDef(mods, name, impl) => val Template(parents, self, body) = impl - var body1 = trimAccessors(moduledef, reshapeLazyVals(body)) + var body1 = trimAccessors(moduledef, body) body1 = trimSyntheticCaseClassMembers(moduledef, body1) val impl1 = Template(parents, self, body1).copyAttrs(impl) ModuleDef(mods, name, impl1).copyAttrs(moduledef) @@ -63,10 +63,10 @@ trait Reshape { val discardedParents = parents collect { case tt: TypeTree => tt } filter isDiscarded if (reifyDebug && discardedParents.length > 0) println("discarding parents in Template: " + discardedParents.mkString(", ")) val parents1 = parents diff discardedParents - val body1 = reshapeLazyVals(trimSyntheticCaseClassCompanions(body)) + val body1 = trimSyntheticCaseClassCompanions(body) Template(parents1, self, body1).copyAttrs(template) case block @ Block(stats, expr) => - val stats1 = reshapeLazyVals(trimSyntheticCaseClassCompanions(stats)) + val stats1 = trimSyntheticCaseClassCompanions(stats) Block(stats1, expr).copyAttrs(block) case unapply @ UnApply(Unapplied(Select(fun, nme.unapply | nme.unapplySeq)), args) => if (reifyDebug) println("unapplying unapply: " + tree) @@ -306,34 +306,6 @@ trait Reshape { stats1 } - private def reshapeLazyVals(stats: List[Tree]): List[Tree] = { - val lazyvaldefs:Map[Symbol, DefDef] = stats.collect({ case ddef: DefDef if ddef.mods.isLazy => ddef }). - map((ddef: DefDef) => ddef.symbol -> ddef).toMap - // lazy valdef and defdef are in the same block. - // only that valdef needs to have its rhs rebuilt from defdef - stats flatMap (stat => stat match { - case vdef: ValDef if vdef.symbol.isLazy => - if (reifyDebug) println(s"reconstructing original lazy value for $vdef") - val ddefSym = vdef.symbol.lazyAccessor - val vdef1 = lazyvaldefs.get(ddefSym) match { - case Some(ddef) => - toPreTyperLazyVal(ddef) - case None => - if (reifyDebug) println("couldn't find corresponding lazy val accessor") - vdef - } - if (reifyDebug) println(s"reconstructed lazy val is $vdef1") - vdef1::Nil - case ddef: DefDef if ddef.symbol.isLazy => - if (isUnitType(ddef.symbol.info)) { - // since lazy values of type Unit don't have val's - // we need to create them from scratch - toPreTyperLazyVal(ddef) :: Nil - } else Nil - case _ => stat::Nil - }) - } - private def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]): List[Tree] = stats filterNot (memberDef => memberDef.isDef && { val isSynthetic = memberDef.symbol.isSynthetic diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index e9d1dfe4d2..511572f6f3 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -88,7 +88,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { object CompilerPhase extends PermissibleValue { val values = List("namer", "typer", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", - "erasure", "lazyvals", "lambdalift", "constructors", + "erasure", "fields", "lambdalift", "constructors", "flatten", "mixin", "delambdafy", "cleanup", "jvm", "terminal") } diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 9b8e9fa330..3879d7b425 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -107,7 +107,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { else { val sb = new StringBuilder allSettings foreach { - case s: MultiChoiceSetting[_] if s.isHelping => sb append s.help + case s if s.isHelping => sb append s.help case _ => } sb.toString diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index af866e1a6f..32c446e16a 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -516,17 +516,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = Some("erasure") } with PostErasure - // phaseName = "lazyvals" - object lazyVals extends { - val global: Global.this.type = Global.this - val runsAfter = List("erasure") - val runsRightAfter = None - } with LazyVals // phaseName = "lambdalift" object lambdaLift extends { val global: Global.this.type = Global.this - val runsAfter = List("lazyvals") + val runsAfter = List("erasure") val runsRightAfter = None } with LambdaLift @@ -620,13 +614,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) pickler -> "serialize symbol tables", refChecks -> "reference/override checking, translate nested objects", uncurry -> "uncurry, translate function values to anonymous classes", - fields -> "synthesize accessors and fields", + fields -> "synthesize accessors and fields, including bitmaps for lazy vals", tailCalls -> "replace tail calls by jumps", specializeTypes -> "@specialized-driven class and method specialization", explicitOuter -> "this refs to outer pointers", erasure -> "erase types, add interfaces for traits", postErasure -> "clean up erased inline classes", - lazyVals -> "allocate bitmaps, translate lazy vals into lazified defs", lambdaLift -> "move nested functions to top level", constructors -> "move field definitions into constructors", mixer -> "mixin composition", @@ -1258,7 +1251,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val explicitouterPhase = phaseNamed("explicitouter") val erasurePhase = phaseNamed("erasure") val posterasurePhase = phaseNamed("posterasure") - // val lazyvalsPhase = phaseNamed("lazyvals") val lambdaliftPhase = phaseNamed("lambdalift") // val constructorsPhase = phaseNamed("constructors") val flattenPhase = phaseNamed("flatten") diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 5dddf30c96..762456c9c9 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -91,7 +91,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { ) /** Make a synchronized block on 'monitor'. */ - def mkSynchronized(monitor: Tree, body: Tree): Tree = + def mkSynchronized(monitor: Tree)(body: Tree): Tree = Apply(Select(monitor, Object_synchronized), List(body)) def mkAppliedTypeForCase(clazz: Symbol): Tree = { @@ -233,26 +233,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { else Block(prefix, containing) setPos (prefix.head.pos union containing.pos) } - /** Return the synchronized part of the double-checked locking idiom around the syncBody tree. It guards with `cond` and - * synchronizes on `clazz.this`. Additional statements can be included after initialization, - * (outside the synchronized block). - * - * The idiom works only if the condition is using a volatile field. - * - * @see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html - */ - def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = - mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats) - - def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = { - def blockOrStat(stats: List[Tree]): Tree = stats match { - case head :: Nil => head - case _ => Block(stats : _*) - } - val sync = mkSynchronized(attrThis, If(cond, blockOrStat(syncBody), EmptyTree)) - blockOrStat(sync :: stats) - } - /** Creates a tree representing new Object { stats }. * To make sure an anonymous subclass of Object is created, * if there are no stats, a () is added. diff --git a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala index 630b2b6c7f..1982c7f643 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala @@ -5,11 +5,15 @@ package scala.tools.nsc.backend.jvm -import scala.tools.asm.tree.{InsnList, AbstractInsnNode, ClassNode, MethodNode} -import java.io.{StringWriter, PrintWriter} -import scala.tools.asm.util.{CheckClassAdapter, TraceClassVisitor, TraceMethodVisitor, Textifier} -import scala.tools.asm.{ClassReader, ClassWriter, Attribute} +import scala.tools.asm.tree.{AbstractInsnNode, ClassNode, FieldNode, InsnList, MethodNode} +import java.io.{PrintWriter, StringWriter} +import java.util + +import scala.tools.asm.util.{CheckClassAdapter, Textifier, TraceClassVisitor, TraceMethodVisitor} +import scala.tools.asm.{Attribute, ClassReader, ClassWriter} import scala.collection.JavaConverters._ +import scala.concurrent.duration.Duration +import scala.concurrent.{Await, Future} import scala.tools.nsc.backend.jvm.analysis.InitialProducer import scala.tools.nsc.backend.jvm.opt.InlineInfoAttributePrototype @@ -64,21 +68,37 @@ object AsmUtils { bytes } - def textifyClassStably(bytes: Array[Byte]): Unit = { + def classFromBytes(bytes: Array[Byte]): ClassNode = { val node = new ClassNode() new ClassReader(bytes).accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES) - node.fields = node.fields.asScala.sortBy(_.name).asJava - node.methods = node.methods.asScala.sortBy(_.name).asJava - node.visibleAnnotations = null - node.attrs = null - node.invisibleAnnotations = null + node + } + +// def main(args: Array[String]): Unit = println(textify(sortedClassRead(classBytes(args.head)))) + + def sortClassMembers(node: ClassNode): node.type = { + node.fields.sort(_.name compareTo _.name) + node.methods.sort(_.name compareTo _.name) + node + } + + // drop ScalaSig annotation and class attributes + def zapScalaClassAttrs(node: ClassNode): node.type = { + if (node.visibleAnnotations != null) + node.visibleAnnotations = node.visibleAnnotations.asScala.filterNot(a => a == null || a.desc.contains("Lscala/reflect/ScalaSignature")).asJava - println(textify(node)) + node.attrs = null + node } - def main(args: Array[String]): Unit = { - textifyClassStably(classBytes(args.head)) + def main(args: Array[String]): Unit = args.par.foreach { classFileName => + val node = zapScalaClassAttrs(sortClassMembers(classFromBytes(classBytes(classFileName)))) + + val pw = new PrintWriter(classFileName + ".asm") + val trace = new TraceClassVisitor(pw) + node.accept(trace) + pw.close() } /** diff --git a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala index 8386722b63..9d643825f6 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala @@ -30,8 +30,8 @@ trait AbsScalaSettings { type OutputSetting <: Setting def BooleanSetting(name: String, descr: String): BooleanSetting - def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): ChoiceSetting - def ChoiceSettingForcedDefault(name: String, helpArg: String, descr: String, choices: List[String], default: String): ChoiceSetting + def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String, choicesHelp: List[String] = Nil): ChoiceSetting + def ChoiceSettingForcedDefault(name: String, helpArg: String, descr: String, choices: List[String], default: String, choicesHelp: List[String] = Nil): ChoiceSetting def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]): IntSetting def MultiStringSetting(name: String, helpArg: String, descr: String): MultiStringSetting def MultiChoiceSetting[E <: MultiChoiceEnumeration](name: String, helpArg: String, descr: String, domain: E, default: Option[List[String]]): MultiChoiceSetting[E] diff --git a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala index 060a24d8d4..08fa56d8e9 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala @@ -88,6 +88,12 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings { /** Issue error and return */ def errorAndValue[T](msg: String, x: T): T = { errorFn(msg) ; x } + /** If this method returns true, print the [[help]] message and exit. */ + def isHelping: Boolean = false + + /** The help message to be printed if [[isHelping]]. */ + def help: String = "" + /** After correct Setting has been selected, tryToSet is called with the * remainder of the command line. It consumes any applicable arguments and * returns the unconsumed ones. diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 9cc8faf8c2..7b4c55c2af 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -219,10 +219,10 @@ class MutableSettings(val errorFn: String => Unit) } def BooleanSetting(name: String, descr: String) = add(new BooleanSetting(name, descr)) - def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String) = - add(new ChoiceSetting(name, helpArg, descr, choices, default)) - def ChoiceSettingForcedDefault(name: String, helpArg: String, descr: String, choices: List[String], default: String) = - ChoiceSetting(name, helpArg, descr, choices, default).withPostSetHook(sett => + def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String, choicesHelp: List[String]) = + add(new ChoiceSetting(name, helpArg, descr, choices, default, choicesHelp)) + def ChoiceSettingForcedDefault(name: String, helpArg: String, descr: String, choices: List[String], default: String, choicesHelp: List[String]) = + ChoiceSetting(name, helpArg, descr, choices, default, choicesHelp).withPostSetHook(sett => if (sett.value != default) { sett.withDeprecationMessage(s"${name}:${sett.value} is deprecated, forcing use of $default") sett.value = default @@ -627,7 +627,7 @@ class MutableSettings(val errorFn: String => Unit) descr: String, val domain: E, val default: Option[List[String]] - ) extends Setting(name, s"$descr: `_' for all, `$name:help' to list") with Clearable { + ) extends Setting(name, s"$descr: `_' for all, `$name:help' to list choices.") with Clearable { withHelpSyntax(s"$name:<_,$helpArg,-$helpArg>") @@ -748,9 +748,9 @@ class MutableSettings(val errorFn: String => Unit) def contains(choice: domain.Value): Boolean = value contains choice - def isHelping: Boolean = sawHelp + override def isHelping: Boolean = sawHelp - def help: String = { + override def help: String = { val choiceLength = choices.map(_.length).max + 1 val formatStr = s" %-${choiceLength}s %s" choices.zipAll(descriptions, "", "").map { @@ -808,18 +808,33 @@ class MutableSettings(val errorFn: String => Unit) helpArg: String, descr: String, override val choices: List[String], - val default: String) - extends Setting(name, descr + choices.mkString(" (", ",", ") default:" + default)) { + val default: String, + val choicesHelp: List[String]) + extends Setting(name, + if (choicesHelp.isEmpty) s"$descr Choices: ${choices.mkString("(", ",", ")")}, default: $default." + else s"$descr Default: `$default', `help' to list choices.") { type T = String protected var v: T = default def indexOfChoice: Int = choices indexOf value - private def usageErrorMessage = f"Usage: $name:<$helpArg>%n where <$helpArg> choices are ${choices mkString ", "} (default: $default)%n" + private def choicesHelpMessage = if (choicesHelp.isEmpty) "" else { + val choiceLength = choices.map(_.length).max + 1 + val formatStr = s" %-${choiceLength}s %s%n" + choices.zipAll(choicesHelp, "", "").map({ + case (choice, desc) => formatStr.format(choice, desc) + }).mkString("") + } + private def usageErrorMessage = f"Usage: $name:<$helpArg> where <$helpArg> choices are ${choices mkString ", "} (default: $default).%n$choicesHelpMessage" + + private var sawHelp = false + override def isHelping = sawHelp + override def help = usageErrorMessage def tryToSet(args: List[String]) = errorAndValue(usageErrorMessage, None) override def tryToSetColon(args: List[String]) = args match { case Nil => errorAndValue(usageErrorMessage, None) + case List("help") => sawHelp = true; Some(Nil) case List(x) if choices contains x => value = x ; Some(Nil) case List(x) => errorAndValue("'" + x + "' is not a valid choice for '" + name + "'", None) case xs => errorAndValue("'" + name + "' does not accept multiple arguments.", None) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index dae8539c66..e10fa3a114 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -38,8 +38,8 @@ trait ScalaSettings extends AbsScalaSettings /** If any of these settings is enabled, the compiler should print a message and exit. */ def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph) - /** Any -multichoice:help? Nicer if any option could report that it had help to offer. */ - private def multihelp = allSettings exists { case s: MultiChoiceSetting[_] => s.isHelping case _ => false } + /** Any -option:help? */ + private def multihelp = allSettings exists { case s => s.isHelping case _ => false } /** Is an info setting set? */ def isInfo = (infoSettings exists (_.isSetByUser)) || multihelp @@ -133,7 +133,22 @@ trait ScalaSettings extends AbsScalaSettings val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") - val XgenMixinForwarders = BooleanSetting("-Xgen-mixin-forwarders", "Generate forwarder methods in classes inhering concrete methods from traits.") + + val XmixinForceForwarders = ChoiceSetting( + name = "-Xmixin-force-forwarders", + helpArg = "mode", + descr = "Generate forwarder methods in classes inhering concrete methods from traits.", + choices = List("true", "junit", "false"), + default = "junit", + choicesHelp = List( + "Always generate mixin forwarders.", + "Generate mixin forwarders for JUnit-annotated methods (JUnit 4 does not support default methods).", + "Only generate mixin forwarders required for program correctness.")) + + object mixinForwarderChoices { + def isTruthy = XmixinForceForwarders.value == "true" + def isJunit = isTruthy || XmixinForceForwarders.value == "junit" + } // XML parsing options object XxmlSettings extends MultiChoiceEnumeration { @@ -143,7 +158,7 @@ trait ScalaSettings extends AbsScalaSettings val Xxml = MultiChoiceSetting( name = "-Xxml", helpArg = "property", - descr = "Configure XML parsing", + descr = "Configure XML parsing.", domain = XxmlSettings ) @@ -169,7 +184,7 @@ trait ScalaSettings extends AbsScalaSettings val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees.") val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL.") val debug = BooleanSetting ("-Ydebug", "Increase the quantity of debugging output.") - val termConflict = ChoiceSetting ("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") + val termConflict = ChoiceSetting ("-Yresolve-term-conflict", "strategy", "Resolve term conflicts.", List("package", "object", "error"), "error") val log = PhasesSetting ("-Ylog", "Log operations during") val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.") val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.") @@ -193,7 +208,7 @@ trait ScalaSettings extends AbsScalaSettings val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.") val Ymemberpos = StringSetting ("-Yshow-member-pos", "output style", "Show start and end positions of members", "") withPostSetHook (_ => Yrangepos.value = true) val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") - val Ymacroexpand = ChoiceSetting ("-Ymacro-expand", "policy", "Control expansion of macros, useful for scaladoc and presentation compiler", List(MacroExpand.Normal, MacroExpand.None, MacroExpand.Discard), MacroExpand.Normal) + val Ymacroexpand = ChoiceSetting ("-Ymacro-expand", "policy", "Control expansion of macros, useful for scaladoc and presentation compiler.", List(MacroExpand.Normal, MacroExpand.None, MacroExpand.Discard), MacroExpand.Normal) val Ymacronoexpand = BooleanSetting ("-Ymacro-no-expand", "Don't expand macros. Might be useful for scaladoc and presentation compiler, but will crash anything which uses macros and gets past typer.") withDeprecationMessage(s"Use ${Ymacroexpand.name}:${MacroExpand.None}") withPostSetHook(_ => Ymacroexpand.value = MacroExpand.None) val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") val Yreplclassbased = BooleanSetting ("-Yrepl-class-based", "Use classes to wrap REPL snippets instead of objects") diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 7ef606b6ef..839e734abc 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -25,8 +25,6 @@ trait Warnings { // currently considered too noisy for general use val warnUnusedImport = BooleanSetting("-Ywarn-unused-import", "Warn when imports are unused.") - val nowarnDefaultJunitMethods = BooleanSetting("-Ynowarn-default-junit-methods", "Don't warn when a JUnit @Test method is generated as a default method (not supported in JUnit 4).") - // Experimental lint warnings that are turned off, but which could be turned on programmatically. // They are not activated by -Xlint and can't be enabled on the command line because they are not // created using the standard factory methods. diff --git a/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala new file mode 100644 index 0000000000..120ee5c26e --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/AccessorSynthesis.scala @@ -0,0 +1,465 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL and Lightbend, Inc + */ + +package scala.tools.nsc +package transform + +import symtab._ +import Flags._ +import scala.collection.mutable + +trait AccessorSynthesis extends Transform with ast.TreeDSL { + import global._ + import definitions._ + import CODE._ + + val EmptyThicket = EmptyTree + def Thicket(trees: List[Tree]) = if (trees.isEmpty) EmptyTree else Block(trees, EmptyTree) + def mustExplodeThicket(tree: Tree): Boolean = + tree match { + case EmptyTree => true + case Block(_, EmptyTree) => true + case _ => false + } + def explodeThicket(tree: Tree): List[Tree] = tree match { + case EmptyTree => Nil + case Block(thicket, EmptyTree) => thicket + case stat => stat :: Nil + } + + + trait AccessorTreeSynthesis { + protected def typedPos(pos: Position)(tree: Tree): Tree + + // used while we still need to synthesize some accessors in mixins: paramaccessors and presupers + class UncheckedAccessorSynth(protected val clazz: Symbol){ + protected val _newDefs = mutable.ListBuffer[Tree]() + + def newDefs = _newDefs.toList + + /** Add tree at given position as new definition */ + protected def addDef(tree: ValOrDefDef): Unit = _newDefs += typedPos(position(tree.symbol))(tree) + + /** The position of given symbol, or, if this is undefined, + * the position of the current class. + */ + private def position(sym: Symbol) = if (sym.pos == NoPosition) clazz.pos else sym.pos + + /** Add new method definition. + * + * @param sym The method symbol. + * @param rhs The method body. + */ + def addDefDef(sym: Symbol, rhs: Tree = EmptyTree) = addDef(DefDef(sym, rhs)) + def addValDef(sym: Symbol, rhs: Tree = EmptyTree) = addDef(ValDef(sym, rhs)) + + /** Complete `stats` with init checks and bitmaps, + * removing any abstract method definitions in `stats` that are + * matched by some symbol defined by a tree previously passed to `addDef`. + */ + def implementWithNewDefs(stats: List[Tree]): List[Tree] = { + val newDefs = _newDefs.toList + val newSyms = newDefs map (_.symbol) + def isNotDuplicate(tree: Tree) = tree match { + case DefDef(_, _, _, _, _, _) => + val sym = tree.symbol + !(sym.isDeferred && + (newSyms exists (nsym => nsym.name == sym.name && (nsym.tpe matches sym.tpe)))) + case _ => true + } + if (newDefs.isEmpty) stats + else newDefs ::: (stats filter isNotDuplicate) + } + + def accessorBody(sym: Symbol) = + if (sym.isSetter) setterBody(sym, sym.getterIn(clazz)) else getterBody(sym) + + protected def getterBody(getter: Symbol): Tree = { + assert(getter.isGetter) + assert(getter.hasFlag(PARAMACCESSOR)) + + fieldAccess(getter) + } + + protected def setterBody(setter: Symbol, getter: Symbol): Tree = { + assert(getter.hasFlag(PARAMACCESSOR), s"missing implementation for non-paramaccessor $setter in $clazz") + + Assign(fieldAccess(setter), Ident(setter.firstParam)) + } + + private def fieldAccess(accessor: Symbol) = + Select(This(clazz), accessor.accessed) + + } + } + + case class BitmapInfo(symbol: Symbol, mask: Literal) { + def storageClass: ClassSymbol = symbol.info.typeSymbol.asClass + } + + + // TODO: better way to communicate from info transform to tree transfor? + private[this] val _bitmapInfo = perRunCaches.newMap[Symbol, BitmapInfo] + private[this] val _slowPathFor = perRunCaches.newMap[Symbol, Symbol]() + + def checkedAccessorSymbolSynth(clz: Symbol) = + if (settings.checkInit) new CheckInitAccessorSymbolSynth { val clazz = clz } + else new CheckedAccessorSymbolSynth { val clazz = clz } + + // base trait, with enough functionality for lazy vals -- CheckInitAccessorSymbolSynth adds logic for -Xcheckinit + trait CheckedAccessorSymbolSynth { + protected val clazz: Symbol + + protected def defaultPos = clazz.pos.focus + protected def isTrait = clazz.isTrait + protected def hasTransientAnnot(field: Symbol) = field.accessedOrSelf hasAnnotation TransientAttr + + def needsBitmap(sym: Symbol): Boolean = !(isTrait || sym.isDeferred) && sym.isMethod && sym.isLazy && !sym.isSpecialized + + + /** Examines the symbol and returns a name indicating what brand of + * bitmap it requires. The possibilities are the BITMAP_* vals + * defined in StdNames. If it needs no bitmap, nme.NO_NAME. + * + * bitmaps for checkinit fields are not inherited + */ + protected def bitmapCategory(sym: Symbol): Name = { + // ensure that nested objects are transformed TODO: still needed? + sym.initialize + + import nme._ + + if (needsBitmap(sym) && sym.isLazy) + if (hasTransientAnnot(sym)) BITMAP_TRANSIENT else BITMAP_NORMAL + else NO_NAME + } + + + def bitmapFor(sym: Symbol): BitmapInfo = _bitmapInfo(sym) + protected def hasBitmap(sym: Symbol): Boolean = _bitmapInfo isDefinedAt sym + + + /** Fill the map from fields to bitmap infos. + * + * Instead of field symbols, the map keeps their getter symbols. This makes code generation easier later. + */ + def computeBitmapInfos(decls: List[Symbol]): List[Symbol] = { + def doCategory(fields: List[Symbol], category: Name) = { + val nbFields = fields.length // we know it's > 0 + val (bitmapClass, bitmapCapacity) = + if (nbFields == 1) (BooleanClass, 1) + else if (nbFields <= 8) (ByteClass, 8) + else if (nbFields <= 32) (IntClass, 32) + else (LongClass, 64) + + // 0-based index of highest bit, divided by bits per bitmap + // note that this is only ever > 0 when bitmapClass == LongClass + val maxBitmapNumber = (nbFields - 1) / bitmapCapacity + + // transient fields get their own category + val isTransientCategory = fields.head hasAnnotation TransientAttr + + val bitmapSyms = + (0 to maxBitmapNumber).toArray map { bitmapNumber => + val bitmapSym = ( + clazz.newVariable(nme.newBitmapName(category, bitmapNumber).toTermName, defaultPos) + setInfo bitmapClass.tpe + setFlag PrivateLocal | NEEDS_TREES + ) + + bitmapSym addAnnotation VolatileAttr + + if (isTransientCategory) bitmapSym addAnnotation TransientAttr + + bitmapSym + } + + fields.zipWithIndex foreach { case (f, idx) => + val bitmapIdx = idx / bitmapCapacity + val offsetInBitmap = idx % bitmapCapacity + val mask = + if (bitmapClass == LongClass) Constant(1L << offsetInBitmap) + else Constant(1 << offsetInBitmap) + + _bitmapInfo(f) = BitmapInfo(bitmapSyms(bitmapIdx), Literal(mask)) + } + + bitmapSyms + } + + decls groupBy bitmapCategory flatMap { + case (category, fields) if category != nme.NO_NAME && fields.nonEmpty => doCategory(fields, category) + case _ => Nil + } toList + } + + def slowPathFor(lzyVal: Symbol): Symbol = _slowPathFor(lzyVal) + + def newSlowPathSymbol(lzyVal: Symbol): Symbol = { + val pos = if (lzyVal.pos != NoPosition) lzyVal.pos else defaultPos // TODO: is the else branch ever taken? + val sym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), pos, PRIVATE) setInfo MethodType(Nil, lzyVal.tpe.resultType) + _slowPathFor(lzyVal) = sym + sym + } + + } + + trait CheckInitAccessorSymbolSynth extends CheckedAccessorSymbolSynth { + /** Does this field require an initialized bit? + * Note: fields of classes inheriting DelayedInit are not checked. + * This is because they are neither initialized in the constructor + * nor do they have a setter (not if they are vals anyway). The usual + * logic for setting bitmaps does therefore not work for such fields. + * That's why they are excluded. + * Note: The `checkinit` option does not check if transient fields are initialized. + */ + protected def needsInitFlag(sym: Symbol): Boolean = + sym.isGetter && + !( sym.isInitializedToDefault + || isConstantType(sym.info.finalResultType) // SI-4742 + || sym.hasFlag(PARAMACCESSOR | SPECIALIZED | LAZY) + || sym.accessed.hasFlag(PRESUPER) + || sym.isOuterAccessor + || (sym.owner isSubClass DelayedInitClass) + || (sym.accessed hasAnnotation TransientAttr)) + + /** Examines the symbol and returns a name indicating what brand of + * bitmap it requires. The possibilities are the BITMAP_* vals + * defined in StdNames. If it needs no bitmap, nme.NO_NAME. + * + * bitmaps for checkinit fields are not inherited + */ + override protected def bitmapCategory(sym: Symbol): Name = { + import nme._ + + super.bitmapCategory(sym) match { + case NO_NAME if needsInitFlag(sym) && !sym.isDeferred => + if (hasTransientAnnot(sym)) BITMAP_CHECKINIT_TRANSIENT else BITMAP_CHECKINIT + case category => category + } + } + + override def needsBitmap(sym: Symbol): Boolean = super.needsBitmap(sym) || !(isTrait || sym.isDeferred) && needsInitFlag(sym) + } + + + // synthesize trees based on info gathered during info transform + // (which are known to have been run because the tree transform runs afterOwnPhase) + // since we can't easily share all info via symbols and flags, we have two maps above + // (they are persisted even between phases because the -Xcheckinit logic runs during constructors) + // TODO: can we use attachments instead of _bitmapInfo and _slowPathFor? + trait CheckedAccessorTreeSynthesis extends AccessorTreeSynthesis { + + // note: we deal in getters here, not field symbols + trait SynthCheckedAccessorsTreesInClass extends CheckedAccessorSymbolSynth { + def isUnitGetter(sym: Symbol) = sym.tpe.resultType.typeSymbol == UnitClass + def thisRef = gen.mkAttributedThis(clazz) + + /** Return an (untyped) tree of the form 'clazz.this.bitmapSym & mask (==|!=) 0', the + * precise comparison operator depending on the value of 'equalToZero'. + */ + def mkTest(field: Symbol, equalToZero: Boolean = true): Tree = { + val bitmap = bitmapFor(field) + val bitmapTree = thisRef DOT bitmap.symbol + + if (bitmap.storageClass == BooleanClass) { + if (equalToZero) NOT(bitmapTree) else bitmapTree + } else { + val lhs = bitmapTree GEN_&(bitmap.mask, bitmap.storageClass) + if (equalToZero) lhs GEN_==(ZERO, bitmap.storageClass) + else lhs GEN_!=(ZERO, bitmap.storageClass) + } + } + + /** Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */ + def mkSetFlag(valSym: Symbol): Tree = { + val bitmap = bitmapFor(valSym) + def x = thisRef DOT bitmap.symbol + + Assign(x, + if (bitmap.storageClass == BooleanClass) TRUE + else { + val or = Apply(Select(x, getMember(bitmap.storageClass, nme.OR)), List(bitmap.mask)) + // NOTE: bitwise or (`|`) on two bytes yields and Int (TODO: why was this not a problem when this ran during mixins?) + // TODO: need this to make it type check -- is there another way?? + if (bitmap.storageClass != LongClass) Apply(Select(or, newTermName("to" + bitmap.storageClass.name)), Nil) + else or + } + ) + } + } + + class SynthLazyAccessorsIn(protected val clazz: Symbol) extends SynthCheckedAccessorsTreesInClass { + /** + * The compute method (slow path) looks like: + * + * ``` + * def l$compute() = { + * synchronized(this) { + * if ((bitmap$n & MASK) == 0) { + * init // l$ = <rhs> + * bitmap$n = bimap$n | MASK + * } + * } + * ... + * this.f1 = null + * ... + * this.fn = null + * l$ + * } + * ``` + * + * `bitmap$n` is a byte, int or long value acting as a bitmap of initialized values. + * The kind of the bitmap determines how many bit indicators for lazy vals are stored in it. + * For Int bitmap it is 32 and then 'n' in the above code is: (offset / 32), + * the MASK is (1 << (offset % 32)). + * + * If the class contains only a single lazy val then the bitmap is + * represented as a Boolean and the condition checking is a simple bool test. + * + * Private fields used only in this initializer are subsequently set to null. + * + * For performance reasons the double-checked locking is split into two parts, + * the first (fast) path checks the bitmap without synchronizing, and if that + * fails it initializes the lazy val within the synchronization block (slow path). + * + * This way the inliner should optimize the fast path because the method body is small enough. + */ + def expandLazyClassMember(lazyVar: Symbol, lazyAccessor: Symbol, transformedRhs: Tree, nullables: Map[Symbol, List[Symbol]]): Tree = { + // use cast so that specialization can turn null.asInstanceOf[T] into null.asInstanceOf[Long] + def nullify(sym: Symbol) = + Select(thisRef, sym.accessedOrSelf) === gen.mkAsInstanceOf(NULL, sym.info.resultType) + + val nulls = nullables.getOrElse(lazyAccessor, Nil) map nullify + + if (nulls.nonEmpty) + log("nulling fields inside " + lazyAccessor + ": " + nulls) + + val slowPathSym = slowPathFor(lazyAccessor) + val rhsAtSlowDef = transformedRhs.changeOwner(lazyAccessor -> slowPathSym) + + val isUnit = isUnitGetter(lazyAccessor) + val selectVar = if (isUnit) UNIT else Select(thisRef, lazyVar) + val storeRes = if (isUnit) rhsAtSlowDef else Assign(selectVar, rhsAtSlowDef) + + def needsInit = mkTest(lazyAccessor) + val doInit = Block(List(storeRes), mkSetFlag(lazyAccessor)) + // the slow part of double-checked locking (TODO: is this the most efficient pattern? https://github.come/scala/scala-dev/issues/204) + val slowPathRhs = Block(gen.mkSynchronized(thisRef)(If(needsInit, doInit, EmptyTree)) :: nulls, selectVar) + + // The lazy accessor delegates to the compute method if needed, otherwise just accesses the var (it was initialized previously) + // `if ((bitmap&n & MASK) == 0) this.l$compute() else l$` + val accessorRhs = If(needsInit, Apply(Select(thisRef, slowPathSym), Nil), selectVar) + + afterOwnPhase { // so that we can assign to vals + Thicket(List((DefDef(slowPathSym, slowPathRhs)), DefDef(lazyAccessor, accessorRhs)) map typedPos(lazyAccessor.pos.focus)) + } + } + } + + /** Map lazy values to the fields they should null after initialization. */ + // TODO: fix + def lazyValNullables(clazz: Symbol, templStats: List[Tree]): Map[Symbol, List[Symbol]] = { + // if there are no lazy fields, take the fast path and save a traversal of the whole AST + if (!clazz.info.decls.exists(_.isLazy)) Map() + else { + // A map of single-use fields to the lazy value that uses them during initialization. + // Each field has to be private and defined in the enclosing class, and there must + // be exactly one lazy value using it. + // + // Such fields will be nulled after the initializer has memoized the lazy value. + val singleUseFields: Map[Symbol, List[Symbol]] = { + val usedIn = mutable.HashMap[Symbol, List[Symbol]]() withDefaultValue Nil + + object SingleUseTraverser extends Traverser { + override def traverse(tree: Tree) { + tree match { + // assignment targets don't count as a dereference -- only check the rhs + case Assign(_, rhs) => traverse(rhs) + case tree: RefTree if tree.symbol != NoSymbol => + val sym = tree.symbol + // println(s"$sym in ${sym.owner} from $currentOwner ($tree)") + if ((sym.hasAccessorFlag || (sym.isTerm && !sym.isMethod)) && sym.isPrivate && !sym.isLazy // non-lazy private field or its accessor + && !definitions.isPrimitiveValueClass(sym.tpe.resultType.typeSymbol) // primitives don't hang on to significant amounts of heap + && sym.owner == currentOwner.enclClass && !(currentOwner.isGetter && currentOwner.accessed == sym)) { + + // println("added use in: " + currentOwner + " -- " + tree) + usedIn(sym) ::= currentOwner + } + super.traverse(tree) + case _ => super.traverse(tree) + } + } + } + templStats foreach SingleUseTraverser.apply + // println("usedIn: " + usedIn) + + // only consider usages from non-transient lazy vals (SI-9365) + val singlyUsedIn = usedIn filter { case (_, member :: Nil) => member.isLazy && !member.accessed.hasAnnotation(TransientAttr) case _ => false } toMap + + // println("singlyUsedIn: " + singlyUsedIn) + singlyUsedIn + } + + val map = mutable.Map[Symbol, Set[Symbol]]() withDefaultValue Set() + // invert the map to see which fields can be nulled for each non-transient lazy val + for ((field, users) <- singleUseFields; lazyFld <- users) map(lazyFld) += field + + map.mapValues(_.toList sortBy (_.id)).toMap + } + } + + + class SynthInitCheckedAccessorsIn(protected val clazz: Symbol) extends SynthCheckedAccessorsTreesInClass with CheckInitAccessorSymbolSynth { + private object addInitBitsTransformer extends Transformer { + private def checkedGetter(lhs: Tree)(pos: Position) = { + val getter = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter) + if (hasBitmap(getter) && needsInitFlag(getter)) { + debuglog("adding checked getter for: " + getter + " " + lhs.symbol.flagString) + List(typedPos(pos)(mkSetFlag(getter))) + } + else Nil + } + override def transformStats(stats: List[Tree], exprOwner: Symbol) = { + // !!! Ident(self) is never referenced, is it supposed to be confirming + // that self is anything in particular? + super.transformStats( + stats flatMap { + case stat@Assign(lhs@Select(This(_), _), rhs) => stat :: checkedGetter(lhs)(stat.pos.focus) + // remove initialization for default values -- TODO is this case ever hit? constructors does not generate Assigns with EmptyTree for the rhs AFAICT + case Apply(lhs@Select(Ident(self), _), EmptyTree.asList) if lhs.symbol.isSetter => Nil + case stat => List(stat) + }, + exprOwner + ) + } + } + + /** Make getters check the initialized bit, and the class constructor & setters are changed to set the initialized bits. */ + def wrapRhsWithInitChecks(sym: Symbol)(rhs: Tree): Tree = { + // Add statements to the body of a constructor to set the 'init' bit for each field initialized in the constructor + if (sym.isConstructor) addInitBitsTransformer transform rhs + else if (isTrait || rhs == EmptyTree) rhs + else if (needsInitFlag(sym)) // getter + mkCheckedAccessorRhs(if (isUnitGetter(sym)) UNIT else rhs, rhs.pos, sym) + else if (sym.isSetter) { + val getter = sym.getterIn(clazz) + if (needsInitFlag(getter)) Block(List(rhs, typedPos(rhs.pos.focus)(mkSetFlag(getter))), UNIT) + else rhs + } + else rhs + } + + private def mkCheckedAccessorRhs(retVal: Tree, pos: Position, getter: Symbol): Tree = { + val msg = s"Uninitialized field: ${clazz.sourceFile}: ${pos.line}" + val result = + IF(mkTest(getter, equalToZero = false)). + THEN(retVal). + ELSE(Throw(NewFromConstructor(UninitializedFieldConstructor, LIT(msg)))) + + typedPos(pos)(BLOCK(result, retVal)) + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 0fb6213d36..81df28bc87 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -456,6 +456,11 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { super.transform(treeCopy.ApplyDynamic(tree, atPos(fn.pos)(Ident(SymbolLiteral_dummy).setType(SymbolLiteral_dummy.info)), LIT(SymbolLiteral_bootstrap) :: arg :: Nil)) + // Drop the TypeApply, which was used in Erasure to make `synchronized { ... } ` erase like `...` + // (and to avoid boxing the argument to the polymorphic `synchronized` method). + case app@Apply(TypeApply(fun, _), args) if fun.symbol == Object_synchronized => + super.transform(treeCopy.Apply(app, fun, args)) + // Replaces `Array(Predef.wrapArray(ArrayValue(...).$asInstanceOf[...]), <tag>)` // with just `ArrayValue(...).$asInstanceOf[...]` // diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 0a87e358b4..8d362f13dd 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -450,7 +450,9 @@ abstract class Constructors extends Statics with Transform with TypingTransforme with DelayedInitHelper with OmittablesHelper with GuardianOfCtorStmts - { + with fields.CheckedAccessorTreeSynthesis + { + protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) val clazz = impl.symbol.owner // the transformed class @@ -770,11 +772,20 @@ abstract class Constructors extends Statics with Transform with TypingTransforme // We never eliminate delayed hooks or the constructors, so, only filter `defs`. val prunedStats = (defs filterNot omittableStat) ::: delayedHookDefs ::: constructors + val statsWithInitChecks = + if (settings.checkInit) { + val addChecks = new SynthInitCheckedAccessorsIn(currentOwner) + prunedStats mapConserve { + case dd: DefDef => deriveDefDef(dd)(addChecks.wrapRhsWithInitChecks(dd.symbol)) + case stat => stat + } + } else prunedStats + // Add the static initializers - if (classInitStats.isEmpty) deriveTemplate(impl)(_ => prunedStats) + if (classInitStats.isEmpty) deriveTemplate(impl)(_ => statsWithInitChecks) else { - val staticCtor = staticConstructor(prunedStats, localTyper, impl.pos)(classInitStats) - deriveTemplate(impl)(_ => staticCtor :: prunedStats) + val staticCtor = staticConstructor(statsWithInitChecks, localTyper, impl.pos)(classInitStats) + deriveTemplate(impl)(_ => staticCtor :: statsWithInitChecks) } } } // TemplateTransformer diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index eecd52546c..69240b07a1 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1117,7 +1117,8 @@ abstract class Erasure extends InfoTransform case TypeApply(fun, args) if (fun.symbol.owner != AnyClass && fun.symbol != Object_asInstanceOf && - fun.symbol != Object_isInstanceOf) => + fun.symbol != Object_isInstanceOf && + fun.symbol != Object_synchronized) => // leave all other type tests/type casts, remove all other type applications preErase(fun) @@ -1194,7 +1195,7 @@ abstract class Erasure extends InfoTransform else { val tree1 = preErase(tree) tree1 match { - case TypeApply(fun, targs @ List(targ)) if fun.symbol == Any_asInstanceOf && targ.tpe == UnitTpe => + case TypeApply(fun, targs @ List(targ)) if (fun.symbol == Any_asInstanceOf || fun.symbol == Object_synchronized) && targ.tpe == UnitTpe => // SI-9066 prevent transforming `o.asInstanceOf[Unit]` to `o.asInstanceOf[BoxedUnit]`. // adaptMember will then replace the call by a reference to BoxedUnit.UNIT. treeCopy.TypeApply(tree1, transform(fun), targs).clearType() diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index 26e517743a..a383b65192 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -10,26 +10,38 @@ import scala.annotation.tailrec import symtab.Flags._ -/** Synthesize accessors and field for each (strict) val owned by a trait. +/** Synthesize accessors, fields (and bitmaps) for (lazy) vals and modules. * - * For traits: + * During Namers, a `ValDef` that is `lazy`, deferred and/or defined in a trait carries its getter's symbol. + * The underlying field symbol does not exist until this phase. * - * - Namers translates a definition `val x = rhs` into a getter `def x = rhs` -- no underlying field is created. - * - This phase synthesizes accessors and fields for any vals mixed into a non-trait class. - * - Constructors will move the rhs to an assignment in the template body. - * Those statements then move to the template into the constructor, - * which means it will initialize the fields defined in this template (and execute the corresponding side effects). - * We need to maintain the connection between getter and rhs until after specialization so that it can duplicate vals. - * - A ModuleDef is desugared to a ClassDef, an accessor (which reuses the module's term symbol) - * and a module var (unless the module is static and does not implement a member of a supertype, or we're in a trait). - * For subclasses of traits that define modules, a module var is mixed in, as well as the required module accessors. + * For `val`s defined in classes, we still emit a field immediately. + * TODO: uniformly assign getter symbol to all `ValDef`s, stop using `accessed`. * - * Runs after uncurry to deal with classes that implement SAM traits with ValDefs. - * Runs before erasure (to get bridges), and thus before lambdalift/flatten, so that nested functions/definitions must be considered. + * This phase synthesizes accessors, fields and bitmaps (for lazy or init-checked vals under -Xcheckinit) + * in the first (closest in the subclassing lattice) subclass (not a trait) of a trait. * - * We run after uncurry because it can introduce subclasses of traits with fields (SAMs with vals). - * Lambdalift also introduces new fields (paramaccessors for captured vals), but runs too late in the pipeline - * (mixins still synthesizes implementations for accessors that need to be mixed into subclasses of local traits that capture). + * For lazy vals and modules, we emit accessors that using double-checked locking (DCL) to balance thread safety + * and performance. A lazy val gets a compute method for the DCL's slow path, for a module it's all done in the accessor. + * + * Local lazy vals do not receive bitmaps, but use a Lazy*Holder that has the volatile init bit and the computed value. + * See `mkLazyLocalDef`. + * + * Constructors will move the rhs to an assignment in the template body. + * Those statements then move to the template into the constructor, + * which means it will initialize the fields defined in this template (and execute the corresponding side effects). + * We need to maintain the connection between getter and rhs until after specialization so that it can duplicate vals. + * + * A ModuleDef is desugared to a ClassDef, an accessor (which reuses the module's term symbol) + * and a module var (unless the module is static and does not implement a member of a supertype, or we're in a trait). + * + * For subclasses of traits that define modules, a module var is mixed in, as well as the required module accessors. + * + * Phase ordering: + * - Runs after uncurry to deal with classes that implement SAM traits with ValDefs. + * - Runs before erasure (to get bridges), and thus before lambdalift/flatten, so that nested functions/definitions must be considered. + * - Lambdalift introduces new paramaccessors for captured vals, but runs too late in the pipeline, so + * mixins still synthesizes implementations for these accessors when a local trait that captures is subclassed. * * * In the future, would like to get closer to dotty, which lifts a val's RHS (a similar thing is done for template-level statements) @@ -54,10 +66,12 @@ import symtab.Flags._ * The only change due to overriding is that its value is never written to the field * (the overridden val's value is, of course, stored in the field in addition to its side-effect being performed). * - * TODO: check init support (or drop the -Xcheck-init flag??) + * TODO: Java 9 support for vals defined in traits. They are currently emitted as final, + * but the write (putfield) to the val does not occur syntactically within the <init> method + * (it's done by the trait setter, which is called from the trait's mixin constructor, + * which is called from the subclass's constructor...) */ -abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransformers { - +abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransformers with AccessorSynthesis { import global._ import definitions._ @@ -69,8 +83,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor if (sym.isJavaDefined || sym.isPackageClass || !sym.isClass) tp else synthFieldsAndAccessors(tp) - // we leave lazy vars/accessors and early-init vals alone for now - private def excludedAccessorOrFieldByFlags(statSym: Symbol): Boolean = statSym hasFlag LAZY | PRESUPER + // TODO: drop PRESUPER support when we implement trait parameters in 2.13 + private def excludedAccessorOrFieldByFlags(statSym: Symbol): Boolean = statSym hasFlag PRESUPER // used for internal communication between info and tree transform of this phase -- not pickled, not in initialflags // TODO: reuse MIXEDIN for NEEDS_TREES? @@ -139,13 +153,12 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor class FieldMemoization(accessorOrField: Symbol, site: Symbol) { val tp = fieldTypeOfAccessorIn(accessorOrField, site.thisType) - // not stored, no side-effect - val pureConstant = tp.isInstanceOf[ConstantType] - - // if !stored, may still have a side-effect - // (currently not distinguished -- used to think we could drop unit-typed vals, - // but the memory model cares about writes to unit-typed fields) - val stored = !pureConstant // || isUnitType(tp)) + // We can only omit strict vals of ConstantType. Lazy vals do not receive constant types (anymore). + // (See note at widenIfNecessary -- for example, the REPL breaks when we omit constant lazy vals) + // Note that a strict unit-typed val does receive a field, because we cannot omit the write to the field + // (well, we could emit it for non-@volatile ones, if I understand the memory model correctly, + // but that seems pretty edge-casey) + val constantTyped = tp.isInstanceOf[ConstantType] } private def fieldTypeForGetterIn(getter: Symbol, pre: Type): Type = getter.info.finalResultType.asSeenFrom(pre, getter.owner) @@ -174,22 +187,71 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // can't use the referenced field since it already tracks the module's moduleClass - private[this] val moduleVarOf = perRunCaches.newMap[Symbol, Symbol] + private[this] val moduleOrLazyVarOf = perRunCaches.newMap[Symbol, Symbol] + + // TODO: can we drop FINAL? In any case, since these variables are MUTABLE, they cannot and will + // not be emitted as ACC_FINAL. They are FINAL in the Scala sense, though: cannot be overridden. + private final val ModuleOrLazyFieldFlags = FINAL | PrivateLocal | SYNTHETIC | NEEDS_TREES - private def newModuleVarSymbol(site: Symbol, module: Symbol, tp: Type, extraFlags: Long): TermSymbol = { + private def newModuleVarSymbol(owner: Symbol, module: Symbol, tp: Type): TermSymbol = { // println(s"new module var in $site for $module of type $tp") - val moduleVar = site.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, MODULEVAR | extraFlags) setInfo tp addAnnotation VolatileAttr - moduleVarOf(module) = moduleVar + val flags = MODULEVAR | (if (owner.isClass) ModuleOrLazyFieldFlags else 0) + + val moduleVar = + (owner.newVariable(nme.moduleVarName(module.name.toTermName), module.pos.focus, flags) + setInfo tp + addAnnotation VolatileAttr) + + moduleOrLazyVarOf(module) = moduleVar moduleVar } private def moduleInit(module: Symbol) = { // println(s"moduleInit for $module in ${module.ownerChain} --> ${moduleVarOf.get(module)}") - val moduleVar = moduleVarOf(module) - gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.info)) + val moduleVar = moduleOrLazyVarOf(module) + def moduleVarRef = gen.mkAttributedRef(moduleVar) + + // for local modules, we synchronize on the owner of the method that owns the module + val monitorHolder = This(moduleVar.owner.enclClass) + def needsInit = Apply(Select(moduleVarRef, Object_eq), List(CODE.NULL)) + val init = Assign(moduleVarRef, gen.newModule(module, moduleVar.info)) + + /** double-checked locking following https://shipilev.net/blog/2014/safe-public-construction/#_safe_publication + * + * public class SafeDCLFactory { + * private volatile Singleton instance; + * + * public Singleton get() { + * if (instance == null) { // check 1 + * synchronized(this) { + * if (instance == null) { // check 2 + * instance = new Singleton(); + * } + * } + * } + * return instance; + * } + * } + * + * TODO: optimize using local variable? + */ + Block(If(needsInit, gen.mkSynchronized(monitorHolder)(If(needsInit, init, EmptyTree)), EmptyTree) :: Nil, moduleVarRef) } + // NoSymbol for lazy accessor sym with unit result type + def lazyVarOf(sym: Symbol) = moduleOrLazyVarOf.getOrElse(sym, NoSymbol) + + private def newLazyVarMember(clazz: Symbol, member: Symbol, tp: Type): TermSymbol = { + val flags = LAZY | (member.flags & FieldFlags) | ModuleOrLazyFieldFlags + val name = member.name.toTermName.append(reflect.NameTransformer.LOCAL_SUFFIX_STRING) + + // Set the MUTABLE flag because the field cannot be ACC_FINAL since we write to it outside of a constructor. + val sym = clazz.newVariable(name, member.pos.focus, flags) setInfo tp + + moduleOrLazyVarOf(member) = sym + sym + } private object synthFieldsAndAccessors extends TypeMap { private def newTraitSetter(getter: Symbol, clazz: Symbol) = { @@ -210,7 +272,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor private def newModuleAccessor(module: Symbol, site: Symbol, moduleVar: Symbol) = { val accessor = site.newMethod(module.name.toTermName, site.pos, STABLE | MODULE | NEEDS_TREES) - moduleVarOf(accessor) = moduleVar + moduleOrLazyVarOf(accessor) = moduleVar // we're in the same prefix as module, so no need for site.thisType.memberType(module) accessor setInfo MethodType(Nil, moduleVar.info) @@ -221,7 +283,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor accessor } - // needed for the following scenario (T could be trait or class) // trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O. // marking it as an ACCESSOR so that it will get to `getterBody` when synthesizing trees below @@ -233,6 +294,15 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor } + private def newSuperLazy(lazyCallingSuper: Symbol, site: Type, lazyVar: Symbol) = { + lazyCallingSuper.asTerm.referenced = lazyVar + + val tp = site.memberInfo(lazyCallingSuper) + + lazyVar setInfo tp.resultType + lazyCallingSuper setInfo tp + } + def apply(tp0: Type): Type = tp0 match { // TODO: make less destructive (name changes, decl additions, flag setting -- // none of this is actually undone when travelling back in time using atPhase) @@ -246,11 +316,16 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor if (member hasFlag ACCESSOR) { val fieldMemoization = fieldMemoizationIn(member, clazz) // check flags before calling makeNotPrivate - val accessorUnderConsideration = !(member hasFlag (DEFERRED | LAZY)) + val accessorUnderConsideration = !(member hasFlag DEFERRED) // destructively mangle accessor's name (which may cause rehashing of decls), also sets flags // this accessor has to be implemented in a subclass -- can't be private - if ((member hasFlag PRIVATE) && fieldMemoization.stored) member makeNotPrivate clazz + if ((member hasFlag PRIVATE) && !fieldMemoization.constantTyped) member makeNotPrivate clazz + // Since we need to refer to `member` using a super call in a subclass, we must ensure that access is allowed. + // If `member` has an access boundary, make sure the `PROTECTED` flag is set, + // to widen from `private[foo]` to `protected[foo]` + // (note that `member.hasAccessBoundary` implies `!member.hasFlag(PRIVATE)`, so we don't have to `resetFlag PRIVATE`) + else if (member.isLazy && member.hasAccessBoundary) member setFlag PROTECTED // This must remain in synch with publicizeTraitMethod in Mixins, so that the // synthesized member in a subclass and the trait member remain in synch regarding access. @@ -262,10 +337,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // (not sure why this only problem only arose when we started setting the notPROTECTED flag) // derive trait setter after calling makeNotPrivate (so that names are mangled consistently) - if (accessorUnderConsideration && fieldMemoization.stored) { + if (accessorUnderConsideration && !fieldMemoization.constantTyped) { synthesizeImplInSubclasses(member) - if (member hasFlag STABLE) // TODO: check isGetter? + if ((member hasFlag STABLE) && !(member hasFlag LAZY)) newDecls += newTraitSetter(member, clazz) } } else if (member hasFlag MODULE) { @@ -296,29 +371,37 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor (existingGetter ne NoSymbol) && (tp matches (site memberInfo existingGetter).resultType) // !existingGetter.isDeferred && -- see (3) } - def newModuleVar(member: Symbol): TermSymbol = - newModuleVarSymbol(clazz, member, site.memberType(member).resultType, PrivateLocal | SYNTHETIC | NEEDS_TREES) + def newModuleVarMember(member: Symbol): TermSymbol = + newModuleVarSymbol(clazz, member, site.memberType(member).resultType) + + def newLazyVarMember(member: Symbol): TermSymbol = + Fields.this.newLazyVarMember(clazz, member, site.memberType(member).resultType) // a module does not need treatment here if it's static, unless it has a matching member in a superclass // a non-static method needs a module var - val modulesNeedingExpansion = - oldDecls.toList.filter(m => m.isModule && (!m.isStatic || m.isOverridingSymbol)) + val modulesAndLazyValsNeedingExpansion = + oldDecls.toList.filter(m => (m.isModule && (!m.isStatic || m.isOverridingSymbol)) || m.isLazy) + + val accessorSymbolSynth = checkedAccessorSymbolSynth(tp.typeSymbol) // expand module def in class/object (if they need it -- see modulesNeedingExpansion above) - val expandedModules = - modulesNeedingExpansion map { module => + val expandedModulesAndLazyVals = ( + modulesAndLazyValsNeedingExpansion flatMap { member => + if (member.isLazy) { + List(newLazyVarMember(member), accessorSymbolSynth.newSlowPathSymbol(member)) + } // expanding module def (top-level or nested in static module) - if (module.isStatic) { // implies m.isOverridingSymbol as per above filter + else List(if (member.isStatic) { // implies m.isOverridingSymbol as per above filter // Need a module accessor, to implement/override a matching member in a superclass. // Never a need for a module var if the module is static. - newMatchingModuleAccessor(clazz, module) + newMatchingModuleAccessor(clazz, member) } else { - nonStaticModuleToMethod(module) + nonStaticModuleToMethod(member) // must reuse symbol instead of creating an accessor - module setFlag NEEDS_TREES - newModuleVar(module) - } - } + member setFlag NEEDS_TREES + newModuleVarMember(member) + }) + }) // println(s"expanded modules for $clazz: $expandedModules") @@ -344,16 +427,12 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor clonedAccessor } - if (member hasFlag MODULE) { - val moduleVar = newModuleVar(member) - List(moduleVar, newModuleAccessor(member, clazz, moduleVar)) - } // when considering whether to mix in the trait setter, forget about conflicts -- they are reported for the getter // a trait setter for an overridden val will receive a unit body in the tree transform - else if (nme.isTraitSetterName(member.name)) { + if (nme.isTraitSetterName(member.name)) { val getter = member.getterIn(member.owner) val clone = cloneAccessor() - + setClonedTraitSetterFlags(clazz, getter, clone) // println(s"mixed in trait setter ${clone.defString}") @@ -362,7 +441,17 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // don't cause conflicts, skip overridden accessors contributed by supertraits (only act on the last overriding one) // see pos/trait_fields_dependent_conflict.scala and neg/t1960.scala else if (accessorConflictsExistingVal(member) || isOverriddenAccessor(member, clazz)) Nil - else if (member.isGetter && fieldMemoizationIn(member, clazz).stored) { + else if (member hasFlag MODULE) { + val moduleVar = newModuleVarMember(member) + List(moduleVar, newModuleAccessor(member, clazz, moduleVar)) + } + else if (member hasFlag LAZY) { + val mixedinLazy = cloneAccessor() + val lazyVar = newLazyVarMember(mixedinLazy) + // println(s"mixing in lazy var: $lazyVar for $member") + List(lazyVar, accessorSymbolSynth.newSlowPathSymbol(mixedinLazy), newSuperLazy(mixedinLazy, site, lazyVar)) + } + else if (member.isGetter && !fieldMemoizationIn(member, clazz).constantTyped) { // add field if needed val field = clazz.newValue(member.localName, member.pos) setInfo fieldTypeForGetterIn(member, clazz.thisType) @@ -376,27 +465,34 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor } else List(cloneAccessor()) // no field needed (constant-typed getter has constant as its RHS) } -// println(s"mixedInAccessorAndFields for $clazz: $mixedInAccessorAndFields") + // println(s"mixedInAccessorAndFields for $clazz: $mixedInAccessorAndFields") // omit fields that are not memoized, retain all other members - def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && !fieldMemoizationIn(sym, clazz).stored + def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && fieldMemoizationIn(sym, clazz).constantTyped val newDecls = - if (expandedModules.isEmpty && mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField) + // under -Xcheckinit we generate all kinds of bitmaps, even when there are no lazy vals + if (expandedModulesAndLazyVals.isEmpty && mixedInAccessorAndFields.isEmpty && !settings.checkInit) + oldDecls.filterNot(omittableField) else { // must not alter `decls` directly val newDecls = newScope val enter = newDecls enter (_: Symbol) val enterAll = (_: List[Symbol]) foreach enter + expandedModulesAndLazyVals foreach enter oldDecls foreach { d => if (!omittableField(d)) enter(d) } - expandedModules foreach enter mixedInAccessorAndFields foreach enterAll + // both oldDecls and mixedInAccessorAndFields (a list of lists) contribute + val bitmapSyms = accessorSymbolSynth.computeBitmapInfos(newDecls.toList) + + bitmapSyms foreach enter + newDecls } -// println(s"new decls for $clazz: $expandedModules ++ $mixedInAccessorAndFields") + // println(s"new decls for $clazz: $expandedModules ++ $mixedInAccessorAndFields") if (newDecls eq oldDecls) tp else ClassInfoType(parents, newDecls, clazz) @@ -408,53 +504,123 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // done by uncurry's info transformer // instead of forcing every member's info to run said transformer, duplicate the flag update logic... - def nonStaticModuleToMethod(module: Symbol): Unit = { + def nonStaticModuleToMethod(module: Symbol): Unit = if (!module.isStatic) module setFlag METHOD | STABLE - } - class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { - def mkTypedUnit(pos: Position) = localTyper.typedPos(pos)(CODE.UNIT) - def deriveUnitDef(stat: Tree) = deriveDefDef(stat)(_ => mkTypedUnit(stat.pos)) - - def mkAccessor(accessor: Symbol)(body: Tree) = localTyper.typedPos(accessor.pos)(DefDef(accessor, body)).asInstanceOf[DefDef] + class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) with CheckedAccessorTreeSynthesis { + protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) - def mkField(sym: Symbol) = localTyper.typedPos(sym.pos)(ValDef(sym)).asInstanceOf[ValDef] + def mkTypedUnit(pos: Position) = typedPos(pos)(CODE.UNIT) + // TODO: clean up. this method is not used + def deriveUnitDef(stat: Tree) = deriveDefDef(stat)(_ => mkTypedUnit(stat.pos)) + def mkAccessor(accessor: Symbol)(body: Tree) = typedPos(accessor.pos)(DefDef(accessor, body)).asInstanceOf[DefDef] + + // this makes trees for mixed in fields, as well as for bitmap fields (their RHS will be EmptyTree because they are initialized implicitly) + // if we decide to explicitly initialize, use this RHS: if (symbol.info.typeSymbol.asClass == BooleanClass) FALSE else ZERO) + // could detect it's a bitmap field with something like `sym.name.startsWith(nme.BITMAP_PREFIX)` (or perhaps something more robust...) + def mkTypedValDef(sym: Symbol, rhs: Tree = EmptyTree) = typedPos(sym.pos)(ValDef(sym, rhs)).asInstanceOf[ValDef] + + /** + * Desugar a local `lazy val x: Int = rhs` into + * ``` + * val x$lzy = new scala.runtime.LazyInt() + * def x$lzycompute(): Int = + * x$lzy.synchronized { + * if (x$lzy.initialized()) x$lzy.value() + * else x$lzy.initialize(rhs) // for a Unit-typed lazy val, this becomes `{ rhs ; x$lzy.initialize() }` to avoid passing around BoxedUnit + * } + * def x(): Int = if (x$lzy.initialized()) x$lzy.value() else x$lzycompute() + * ``` + */ + private def mkLazyLocalDef(lazyVal: Symbol, rhs: Tree): Tree = { + import CODE._ + import scala.reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING + val owner = lazyVal.owner + + val lazyValType = lazyVal.tpe.resultType + val refClass = lazyHolders.getOrElse(lazyValType.typeSymbol, LazyRefClass) + val isUnit = refClass == LazyUnitClass + val refTpe = if (refClass != LazyRefClass) refClass.tpe else appliedType(refClass.typeConstructor, List(lazyValType)) + + val lazyName = lazyVal.name.toTermName + val pos = lazyVal.pos.focus + + // used twice: once in the same owner as the lazy val, another time inside the compute method + val localLazyName = lazyName append LAZY_LOCAL_SUFFIX_STRING + + // The lazy holder val need not be mutable, as we write to its field. + // In fact, it MUST not be mutable to avoid capturing it as an ObjectRef in lambdalift + // Must be marked LAZY to allow forward references, as in `def test2 { println(s.length) ; lazy val s = "abc" } + val holderSym = owner.newValue(localLazyName, pos, LAZY | ARTIFACT) setInfo refTpe + + val initializedSym = refTpe.member(nme.initialized) + val initializeSym = refTpe.member(nme.initialize) + + // LazyUnit does not have a `value` member + val valueSym = if (isUnit) NoSymbol else refTpe.member(nme.value) + + def initialized = Select(Ident(holderSym), initializedSym) + def initialize = Select(Ident(holderSym), initializeSym) + def getValue = if (isUnit) UNIT else Apply(Select(Ident(holderSym), valueSym), Nil) + + val computerSym = + owner.newMethod(lazyName append nme.LAZY_SLOW_SUFFIX, pos, ARTIFACT | PRIVATE) setInfo MethodType(Nil, lazyValType) + + val rhsAtComputer = rhs.changeOwner(lazyVal -> computerSym) + + val computer = mkAccessor(computerSym)(gen.mkSynchronized(Ident(holderSym))( + If(initialized, getValue, + if (isUnit) Block(rhsAtComputer :: Nil, Apply(initialize, Nil)) + else Apply(initialize, rhsAtComputer :: Nil)))) + + val accessor = mkAccessor(lazyVal)( + If(initialized, getValue, + Apply(Ident(computerSym), Nil))) + + // do last! + // remove STABLE: prevent replacing accessor call of type Unit by BoxedUnit.UNIT in erasure + // remove ACCESSOR: prevent constructors from eliminating the method body if the lazy val is + // lifted into a trait (TODO: not sure about the details here) + lazyVal.resetFlag(STABLE | ACCESSOR) + + Thicket(mkTypedValDef(holderSym, New(refTpe)) :: computer :: accessor :: Nil) + } // synth trees for accessors/fields and trait setters when they are mixed into a class def fieldsAndAccessors(clazz: Symbol): List[ValOrDefDef] = { - def fieldAccess(accessor: Symbol): Option[Tree] = { + def fieldAccess(accessor: Symbol): List[Tree] = { val fieldName = accessor.localName val field = clazz.info.decl(fieldName) // The `None` result denotes an error, but it's refchecks' job to report it (this fallback is for robustness). // This is the result of overriding a val with a def, so that no field is found in the subclass. - if (field.exists) Some(Select(This(clazz), field)) - else None + if (field.exists) List(Select(This(clazz), field)) + else Nil } - def getterBody(getter: Symbol): Option[Tree] = { + def getterBody(getter: Symbol): List[Tree] = { // accessor created by newMatchingModuleAccessor for a static module that does need an accessor // (because there's a matching member in a super class) if (getter.asTerm.referenced.isModule) { - Some(gen.mkAttributedRef(clazz.thisType, getter.asTerm.referenced)) + List(gen.mkAttributedRef(clazz.thisType, getter.asTerm.referenced)) } else { val fieldMemoization = fieldMemoizationIn(getter, clazz) - if (fieldMemoization.pureConstant) Some(gen.mkAttributedQualifier(fieldMemoization.tp)) // TODO: drop when we no longer care about producing identical bytecode + if (fieldMemoization.constantTyped) List(gen.mkAttributedQualifier(fieldMemoization.tp)) // TODO: drop when we no longer care about producing identical bytecode else fieldAccess(getter) } } // println(s"accessorsAndFieldsNeedingTrees for $templateSym: $accessorsAndFieldsNeedingTrees") - def setterBody(setter: Symbol): Option[Tree] = { + def setterBody(setter: Symbol): List[Tree] = { // trait setter in trait - if (clazz.isTrait) Some(EmptyTree) + if (clazz.isTrait) List(EmptyTree) // trait setter for overridden val in class - else if (checkAndClearOverriddenTraitSetter(setter)) Some(mkTypedUnit(setter.pos)) + else if (checkAndClearOverriddenTraitSetter(setter)) List(mkTypedUnit(setter.pos)) // trait val/var setter mixed into class else fieldAccess(setter) map (fieldSel => Assign(fieldSel, Ident(setter.firstParam))) } - def moduleAccessorBody(module: Symbol): Some[Tree] = Some( + def moduleAccessorBody(module: Symbol): List[Tree] = List( // added during synthFieldsAndAccessors using newModuleAccessor // a module defined in a trait by definition can't be static (it's a member of the trait and thus gets a new instance for every outer instance) if (clazz.isTrait) EmptyTree @@ -462,11 +628,20 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor else moduleInit(module) ) + val synthAccessorInClass = new SynthLazyAccessorsIn(clazz) + def superLazy(getter: Symbol): List[ValOrDefDef] = { + assert(!clazz.isTrait) + // this contortion was the only way I can get the super select to be type checked correctly.. TODO: why does SelectSuper not work? + val rhs = Apply(Select(Super(This(clazz), tpnme.EMPTY), getter.name), Nil) + explodeThicket(synthAccessorInClass.expandLazyClassMember(lazyVarOf(getter), getter, rhs, Map.empty)).asInstanceOf[List[ValOrDefDef]] + } + clazz.info.decls.toList.filter(checkAndClearNeedsTrees) flatMap { case module if module hasAllFlags (MODULE | METHOD) => moduleAccessorBody(module) map mkAccessor(module) + case getter if getter hasAllFlags (LAZY | METHOD) => superLazy(getter) case setter if setter.isSetter => setterBody(setter) map mkAccessor(setter) case getter if getter.hasFlag(ACCESSOR) => getterBody(getter) map mkAccessor(getter) - case field if !(field hasFlag METHOD) => Some(mkField(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES) + case field if !(field hasFlag METHOD) => Some(mkTypedValDef(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES) case _ => None } } @@ -474,10 +649,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor def rhsAtOwner(stat: ValOrDefDef, newOwner: Symbol): Tree = atOwner(newOwner)(super.transform(stat.rhs.changeOwner(stat.symbol -> newOwner))) - - private def Thicket(trees: List[Tree]) = Block(trees, EmptyTree) override def transform(stat: Tree): Tree = { - val clazz = currentOwner + val currOwner = currentOwner // often a class, but not necessarily val statSym = stat.symbol /* @@ -500,22 +673,37 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // also remove ACCESSOR flag since there won't be an underlying field to access? case DefDef(_, _, _, _, _, rhs) if (statSym hasFlag ACCESSOR) && (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym) - && !clazz.isTrait // we've already done this for traits.. the asymmetry will be solved by the above todo - && fieldMemoizationIn(statSym, clazz).pureConstant => - deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe)) // TODO: recurse? + && !currOwner.isTrait // we've already done this for traits.. the asymmetry will be solved by the above todo + && fieldMemoizationIn(statSym, currOwner).constantTyped => + deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe)) + + // deferred val, trait val, lazy val (local or in class) + case vd@ValDef(mods, name, tpt, rhs) if vd.symbol.hasFlag(ACCESSOR) && treeInfo.noFieldFor(vd, currOwner) => + val transformedRhs = atOwner(statSym)(transform(rhs)) + + if (rhs == EmptyTree) mkAccessor(statSym)(EmptyTree) + else if (currOwner.isTrait) mkAccessor(statSym)(transformedRhs) + else if (!currOwner.isClass) mkLazyLocalDef(vd.symbol, transformedRhs) + else { + // TODO: make `synthAccessorInClass` a field and update it in atOwner? + // note that `LazyAccessorTreeSynth` is pretty lightweight + // (it's just a bunch of methods that all take a `clazz` parameter, which is thus stored as a field) + val synthAccessorInClass = new SynthLazyAccessorsIn(currOwner) + synthAccessorInClass.expandLazyClassMember(lazyVarOf(statSym), statSym, transformedRhs, nullables.getOrElse(currOwner, Map.empty)) + } // drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields case ValDef(mods, _, _, rhs) if (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym) - && fieldMemoizationIn(statSym, clazz).pureConstant => - EmptyTree + && fieldMemoizationIn(statSym, currOwner).constantTyped => + EmptyThicket case ModuleDef(_, _, impl) => // ??? The typer doesn't take kindly to seeing this ClassDef; we have to set NoType so it will be ignored. val cd = super.transform(ClassDef(statSym.moduleClass, impl) setType NoType) - if (clazz.isClass) cd + if (currOwner.isClass) cd else { // local module -- symbols cannot be generated by info transformer, so do it all here - val moduleVar = newModuleVarSymbol(currentOwner, statSym, statSym.info.resultType, 0) - Thicket(cd :: mkField(moduleVar) :: mkAccessor(statSym)(moduleInit(statSym)) :: Nil) + val moduleVar = newModuleVarSymbol(currOwner, statSym, statSym.info.resultType) + Thicket(cd :: mkTypedValDef(moduleVar) :: mkAccessor(statSym)(moduleInit(statSym)) :: Nil) } case tree => @@ -524,26 +712,29 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor } } + def transformTermsAtExprOwner(exprOwner: Symbol)(stat: Tree) = if (stat.isTerm) atOwner(exprOwner)(transform(stat)) else transform(stat) + private val nullables = perRunCaches.newMap[Symbol, Map[Symbol, List[Symbol]]] + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { val addedStats = - if (!currentOwner.isClass) Nil + if (!currentOwner.isClass) Nil // TODO: || currentOwner.isPackageClass else afterOwnPhase { fieldsAndAccessors(currentOwner) } + val inRealClass = currentOwner.isClass && !(currentOwner.isPackageClass || currentOwner.isTrait) + if (inRealClass) + nullables(currentOwner) = lazyValNullables(currentOwner, stats) + val newStats = stats mapConserve (if (exprOwner != currentOwner) transformTermsAtExprOwner(exprOwner) else transform) addedStats ::: (if (newStats eq stats) stats else { // check whether we need to flatten thickets and drop empty ones - if (newStats exists { case EmptyTree => true case Block(_, EmptyTree) => true case _ => false }) - newStats flatMap { - case EmptyTree => Nil - case Block(thicket, EmptyTree) => thicket - case stat => stat :: Nil - } + if (newStats exists mustExplodeThicket) + newStats flatMap explodeThicket else newStats }) } diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala deleted file mode 100644 index fc7999bf3b..0000000000 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ /dev/null @@ -1,318 +0,0 @@ -package scala.tools.nsc -package transform - -import scala.collection.mutable - -abstract class LazyVals extends Transform with TypingTransformers with ast.TreeDSL { - // inherits abstract value `global` and class `Phase` from Transform - - import global._ // the global environment - import definitions._ // standard classes and methods - import typer.typed // methods to type trees - import CODE._ - - val phaseName: String = "lazyvals" - private val FLAGS_PER_BYTE: Int = 8 // Byte - private def bitmapKind = ByteClass - - def newTransformer(unit: CompilationUnit): Transformer = - new LazyValues(unit) - - private def lazyUnit(sym: Symbol) = sym.tpe.resultType.typeSymbol == UnitClass - - object LocalLazyValFinder extends Traverser { - var result: Boolean = _ - - def find(t: Tree) = {result = false; traverse(t); result} - def find(ts: List[Tree]) = {result = false; traverseTrees(ts); result} - - override def traverse(t: Tree) { - if (!result) - t match { - case v@ValDef(_, _, _, _) if v.symbol.isLazy => - result = true - - case d@DefDef(_, _, _, _, _, _) if d.symbol.isLazy && lazyUnit(d.symbol) => - d.symbol.resetFlag(symtab.Flags.LAZY) - result = true - - case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) => - - // Avoid adding bitmaps when they are fully overshadowed by those that are added inside loops - case LabelDef(name, _, _) if nme.isLoopHeaderLabel(name) => - - case _ => - super.traverse(t) - } - } - } - - /** - * Transform local lazy accessors to check for the initialized bit. - */ - class LazyValues(unit: CompilationUnit) extends TypingTransformer(unit) { - /** map from method symbols to the number of lazy values it defines. */ - private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0 - - import symtab.Flags._ - private def flattenThickets(stats: List[Tree]): List[Tree] = stats.flatMap(_ match { - case b @ Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if b.tpe == null && n1.endsWith(nme.LAZY_SLOW_SUFFIX) => - List(d1, d2) - case stat => - List(stat) - }) - - /** Perform the following transformations: - * - for a lazy accessor inside a method, make it check the initialization bitmap - * - implement double checked locking of member modules for non-trait owners (trait just have the abstract accessor) - * ``` - * // typer - * class C { object x } - * // fields - * class C { var x$module; def x() = { x$module = new x; x$module } - * // lazyvals - * class C { - * var x$module // module var - * def x() = { if (x$module == null) x$lzycompute() else x$module // fast path - * def x$lzycompute() = { synchronized { if (x$module == null) x$module = new x }; x$module } // slow path - * } - * ``` - * - for all methods, add enough int vars to allow one flag per lazy local value - * - blocks in template bodies behave almost like methods. A single bitmaps section is - * added in the first block, for all lazy values defined in such blocks. - * - remove ACCESSOR flags: accessors in traits are not statically implemented, - * but moved to the host class. local lazy values should be statically implemented. - */ - override def transform(tree: Tree): Tree = { - val sym = tree.symbol - curTree = tree - - tree match { - - case Block(_, _) => - val block1 = super.transform(tree) - val Block(stats, expr) = block1 - treeCopy.Block(block1, flattenThickets(stats), expr) - - case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) { - val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) { - val enclosingClassOrDummyOrMethod = { - val enclMethod = sym.enclMethod - - if (enclMethod != NoSymbol) { - val enclClass = sym.enclClass - if (enclClass != NoSymbol && enclMethod == enclClass.enclMethod) - enclClass - else - enclMethod - } else - sym.owner - } - debuglog(s"determined enclosing class/dummy/method for lazy val as $enclosingClassOrDummyOrMethod given symbol $sym") - val idx = lazyVals(enclosingClassOrDummyOrMethod) - lazyVals(enclosingClassOrDummyOrMethod) = idx + 1 - val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym) - sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR) - (rhs1, sDef) - } else if (sym.hasAllFlags(MODULE | METHOD) && !sym.owner.isTrait) { - rhs match { - case b @ Block((assign @ Assign(moduleRef, _)) :: Nil, expr) => - def cond = Apply(Select(moduleRef, Object_eq), List(Literal(Constant(null)))) - val (fastPath, slowPath) = mkFastPathBody(sym.owner.enclClass, moduleRef.symbol, cond, transform(assign) :: Nil, Nil, transform(expr)) - (localTyper.typedPos(tree.pos)(fastPath), localTyper.typedPos(tree.pos)(slowPath)) - case rhs => - global.reporter.error(tree.pos, "Unexpected tree on the RHS of a module accessor: " + rhs) - (rhs, EmptyTree) - } - } else { - (transform(rhs), EmptyTree) - } - - val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) - if (slowPathDef != EmptyTree) { - // The contents of this block are flattened into the enclosing statement sequence, see flattenThickets - // This is a poor man's version of dotty's Thicket: https://github.com/lampepfl/dotty/blob/d5280358d1/src/dotty/tools/dotc/ast/Trees.scala#L707 - Block(slowPathDef, ddef1) - } else ddef1 - } - - case Template(_, _, body) => atOwner(currentOwner) { - // TODO: shady business... can this logic be encapsulated in LocalLazyValFinder? - var added = false - val stats = super.transformTrees(body) mapConserve { - case stat: ValDef => typed(deriveValDef(stat)(addBitmapDefs(stat.symbol, _))) - case stat: TermTree if !added && (LocalLazyValFinder find stat) => - added = true - typed(addBitmapDefs(sym, stat)) - case stat => stat - } - - val innerClassBitmaps = if (!added && currentOwner.isClass && bitmaps.contains(currentOwner)) { - // add bitmap to inner class if necessary - val toAdd0 = bitmaps(currentOwner).map(s => typed(ValDef(s, ZERO))) - toAdd0.foreach(t => { - if (currentOwner.info.decl(t.symbol.name) == NoSymbol) { - t.symbol.setFlag(PROTECTED) - currentOwner.info.decls.enter(t.symbol) - } - }) - toAdd0 - } else List() - deriveTemplate(tree)(_ => innerClassBitmaps ++ flattenThickets(stats)) - } - - case ValDef(_, _, _, _) if !sym.owner.isModule && !sym.owner.isClass => - deriveValDef(tree) { rhs0 => - val rhs = transform(rhs0) - if (LocalLazyValFinder.find(rhs)) typed(addBitmapDefs(sym, rhs)) else rhs - } - - case l@LabelDef(name0, params0, ifp0@If(_, _, _)) if name0.startsWith(nme.WHILE_PREFIX) => - val ifp1 = super.transform(ifp0) - val If(cond0, thenp0, elsep0) = ifp1 - - if (LocalLazyValFinder.find(thenp0)) - deriveLabelDef(l)(_ => treeCopy.If(ifp1, cond0, typed(addBitmapDefs(sym.owner, thenp0)), elsep0)) - else - l - - case l@LabelDef(name0, params0, block@Block(stats0, expr)) - if name0.startsWith(nme.WHILE_PREFIX) || name0.startsWith(nme.DO_WHILE_PREFIX) => - val stats1 = super.transformTrees(stats0) - if (LocalLazyValFinder.find(stats1)) - deriveLabelDef(l)(_ => treeCopy.Block(block, typed(addBitmapDefs(sym.owner, stats1.head))::stats1.tail, expr)) - else - l - - case _ => super.transform(tree) - } - } - - /** Add the bitmap definitions to the rhs of a method definition. - * If the rhs has been tail-call transformed, insert the bitmap - * definitions inside the top-level label definition, so that each - * iteration has the lazy values uninitialized. Otherwise add them - * at the very beginning of the method. - */ - private def addBitmapDefs(methSym: Symbol, rhs: Tree): Tree = { - def prependStats(stats: List[Tree], tree: Tree): Block = tree match { - case Block(stats1, res) => Block(stats ::: stats1, res) - case _ => Block(stats, tree) - } - - val bmps = bitmaps(methSym) map (ValDef(_, ZERO)) - - def isMatch(params: List[Ident]) = (params.tail corresponds methSym.tpe.params)(_.tpe == _.tpe) - - if (bmps.isEmpty) rhs else rhs match { - case Block(assign, l @ LabelDef(name, params, _)) - if (name string_== "_" + methSym.name) && isMatch(params) => - Block(assign, deriveLabelDef(l)(rhs => typed(prependStats(bmps, rhs)))) - - case _ => prependStats(bmps, rhs) - } - } - - def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], - stats: List[Tree], retVal: Tree): Tree = { - val owner = lzyVal.owner - val defSym = owner.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE) - defSym setInfo MethodType(List(), lzyVal.tpe.resultType) - if (owner.isClass) owner.info.decls.enter(defSym) - debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal") - // this is a hack i don't understand for lazy vals nested in a lazy val, introduced in 3769f4d, - // tested in pos/t3670 (add9be64). class A { val n = { lazy val b = { lazy val dd = 3; dd }; b } } - // bitmaps has an entry bMethodSym -> List(bitmap$0), where bitmap$0.owner == bMethodSym. - // now we set bitmap$0.owner = b$lzycomputeMethodSym. - for (bitmap <- bitmaps(lzyVal)) bitmap.owner = defSym - val rhs: Tree = gen.mkSynchronizedCheck(clazz, cond, syncBody, stats).changeOwner(currentOwner -> defSym) - - DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) - } - - - def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree], - stats: List[Tree], retVal: Tree): (Tree, Tree) = { - val slowPathDef: Tree = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal) - (If(cond, Apply(Ident(slowPathDef.symbol), Nil), retVal), slowPathDef) - } - - /** return a 'lazified' version of rhs. Rhs should conform to the - * following schema: - * { - * l$ = <rhs> - * l$ - * } or - * <rhs> when the lazy value has type Unit (for which there is no field - * to cache its value. - * - * Similarly as for normal lazy val members (see Mixin), the result will be a tree of the form - * { if ((bitmap&n & MASK) == 0) this.l$compute() - * else l$ - * - * def l$compute() = { synchronized(enclosing_class_or_dummy) { - * if ((bitmap$n & MASK) == 0) { - * l$ = <rhs> - * bitmap$n = bimap$n | MASK - * }} - * l$ - * } - * } - * where bitmap$n is a byte value acting as a bitmap of initialized values. It is - * the 'n' is (offset / 8), the MASK is (1 << (offset % 8)). If the value has type - * unit, no field is used to cache the value, so the l$compute will now look as following: - * { - * def l$compute() = { synchronized(enclosing_class_or_dummy) { - * if ((bitmap$n & MASK) == 0) { - * <rhs>; - * bitmap$n = bimap$n | MASK - * }} - * () - * } - * } - */ - private def mkLazyDef(methOrClass: Symbol, tree: Tree, offset: Int, lazyVal: Symbol): (Tree, Tree) = { - val bitmapSym = getBitmapFor(methOrClass, offset) - val mask = LIT(1 << (offset % FLAGS_PER_BYTE)) - val bitmapRef = if (methOrClass.isClass) Select(This(methOrClass), bitmapSym) else Ident(bitmapSym) - - def mkBlock(stmt: Tree) = BLOCK(stmt, mkSetFlag(bitmapSym, mask, bitmapRef), UNIT) - - debuglog(s"create complete lazy def in $methOrClass for $lazyVal") - val (block, res) = tree match { - case Block(List(assignment), res) if !lazyUnit(lazyVal) => - (mkBlock(assignment), res) - case rhs => - (mkBlock(rhs), UNIT) - } - - def cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind) - val lazyDefs = mkFastPathBody(methOrClass.enclClass, lazyVal, cond, List(block), Nil, res) - (atPos(tree.pos)(localTyper.typed {lazyDefs._1 }), atPos(tree.pos)(localTyper.typed {lazyDefs._2 })) - } - - private def mkSetFlag(bmp: Symbol, mask: Tree, bmpRef: Tree): Tree = - bmpRef === (bmpRef GEN_| (mask, bitmapKind)) - - val bitmaps = mutable.Map[Symbol, List[Symbol]]() withDefaultValue Nil - - /** Return the symbol corresponding of the right bitmap int inside meth, - * given offset. - */ - private def getBitmapFor(meth: Symbol, offset: Int): Symbol = { - val n = offset / FLAGS_PER_BYTE - val bmps = bitmaps(meth) - if (bmps.length > n) - bmps(n) - else { - val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(ByteTpe) - enteringTyper { - sym addAnnotation VolatileAttr - } - - bitmaps(meth) = (sym :: bmps).reverse - sym - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index f781426f1a..582c51b90d 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -1,5 +1,6 @@ /* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL + * Copyright 2005-2016 LAMP/EPFL and Lightbend, Inc + * * @author Martin Odersky */ @@ -9,13 +10,14 @@ package transform import symtab._ import Flags._ import scala.annotation.tailrec -import scala.collection.mutable -abstract class Mixin extends InfoTransform with ast.TreeDSL { + +abstract class Mixin extends InfoTransform with ast.TreeDSL with AccessorSynthesis { import global._ import definitions._ import CODE._ + /** The name of the phase: */ val phaseName: String = "mixin" @@ -57,8 +59,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ private val treatedClassInfos = perRunCaches.newMap[Symbol, Type]() withDefaultValue NoType - /** Map a lazy, mixedin field accessor to its trait member accessor */ - private val initializer = perRunCaches.newMap[Symbol, Symbol]() // --------- helper functions ----------------------------------------------- @@ -71,47 +71,22 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * (private modules, on the other hand, are implemented statically, but their * module variable is not. all such private modules are lifted, because * non-lifted private modules have been eliminated in ExplicitOuter) - * - field accessors and superaccessors, except for lazy value accessors which become initializer - * methods in the impl class (because they can have arbitrary initializers) + * - field accessors and superaccessors */ private def isImplementedStatically(sym: Symbol) = ( (sym.isMethod || ((sym hasFlag MODULE) && !sym.isStatic)) + // TODO: ^^^ non-static modules should have been turned into methods by fields by now, no? maybe the info transformer hasn't run??? && notDeferred(sym) && sym.owner.isTrait && (!sym.isModule || sym.hasFlag(PRIVATE | LIFTED)) - && (!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isLazy) + && (!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || (sym hasFlag LAZY)) && !sym.isPrivate && !sym.hasAllFlags(LIFTED | MODULE | METHOD) && !sym.isConstructor && (!sym.hasFlag(notPRIVATE | LIFTED) || sym.hasFlag(ACCESSOR | SUPERACCESSOR | MODULE)) ) - private def isFieldWithBitmap(field: Symbol) = { - field.info // ensure that nested objects are transformed - // For checkinit consider normal value getters - // but for lazy values only take into account lazy getters - field.isLazy && field.isMethod && !field.isDeferred - } - /** Does this field require an initialized bit? - * Note: fields of classes inheriting DelayedInit are not checked. - * This is because they are neither initialized in the constructor - * nor do they have a setter (not if they are vals anyway). The usual - * logic for setting bitmaps does therefore not work for such fields. - * That's why they are excluded. - * Note: The `checkinit` option does not check if transient fields are initialized. - */ - private def needsInitFlag(sym: Symbol) = ( - settings.checkInit - && sym.isGetter - && !sym.isInitializedToDefault - && !isConstantType(sym.info.finalResultType) // SI-4742 - && !sym.hasFlag(PARAMACCESSOR | SPECIALIZED | LAZY) - && !sym.accessed.hasFlag(PRESUPER) - && !sym.isOuterAccessor - && !(sym.owner isSubClass DelayedInitClass) - && !(sym.accessed hasAnnotation TransientAttr) - ) /** Returns the symbol that is accessed by a super-accessor in a mixin composition. * @@ -156,7 +131,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { ) } - private def isUnitGetter(sym: Symbol) = sym.tpe.resultType.typeSymbol == UnitClass /** Add given member to given class, and mark member as mixed-in. */ @@ -207,9 +181,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else { assert(member.isTerm && !member.isDeferred, member) // disable assert to support compiling against code compiled by an older compiler (until we re-starr) - // assert(member hasFlag LAZY | PRESUPER, s"unexpected $member in $clazz ${member.debugFlagString}") - // lazy vals still leave field symbols lying around in traits -- TODO: never emit them to begin with - // ditto for early init vals + // assert(member hasFlag PRESUPER, s"unexpected $member in $clazz ${member.debugFlagString}") clazz.info.decls.unlink(member) } @@ -243,51 +215,59 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case NoSymbol => val isMemberOfClazz = clazz.info.findMember(member.name, 0, 0L, stableOnly = false).alternatives.contains(member) if (isMemberOfClazz) { - def genForwarder(): Unit = { - cloneAndAddMixinMember(mixinClass, member).asInstanceOf[TermSymbol] setAlias member + def genForwarder(required: Boolean): Unit = { + val owner = member.owner + if (owner.isJavaDefined && owner.isInterface && !clazz.parentSymbols.contains(owner)) { + val text = s"Unable to implement a mixin forwarder for $member in $clazz unless interface ${owner.name} is directly extended by $clazz." + if (required) reporter.error(clazz.pos, text) + else warning(clazz.pos, text) + } else + cloneAndAddMixinMember(mixinClass, member).asInstanceOf[TermSymbol] setAlias member + } + + // `member` is a concrete method defined in `mixinClass`, which is a base class of + // `clazz`, and the method is not overridden in `clazz`. A forwarder is needed if: + // + // - A non-trait base class of `clazz` defines a matching method. Example: + // class C {def f: Int}; trait T extends C {def f = 1}; class D extends T + // Even if C.f is abstract, the forwarder in D is needed, otherwise the JVM would + // resolve `D.f` to `C.f`, see jvms-6.5.invokevirtual. + // + // - There exists another concrete, matching method in a parent interface `p` of + // `clazz`, and the `mixinClass` does not itself extend `p`. In this case the + // forwarder is needed to disambiguate. Example: + // trait T1 {def f = 1}; trait T2 extends T1 {override def f = 2}; class C extends T2 + // In C we don't need a forwarder for f because T2 extends T1, so the JVM resolves + // C.f to T2.f non-ambiguously. See jvms-5.4.3.3, "maximally-specific method". + // trait U1 {def f = 1}; trait U2 {self:U1 => override def f = 2}; class D extends U2 + // In D the forwarder is needed, the interfaces U1 and U2 are unrelated at the JVM + // level. + + @tailrec + def existsCompetingMethod(baseClasses: List[Symbol]): Boolean = baseClasses match { + case baseClass :: rest => + if (baseClass ne mixinClass) { + val m = member.overriddenSymbol(baseClass) + val isCompeting = m.exists && { + !m.owner.isTraitOrInterface || + (!m.isDeferred && !mixinClass.isNonBottomSubClass(m.owner)) + } + isCompeting || existsCompetingMethod(rest) + } else existsCompetingMethod(rest) + + case _ => false } - if (settings.XgenMixinForwarders) genForwarder() - else { - - // `member` is a concrete method defined in `mixinClass`, which is a base class of - // `clazz`, and the method is not overridden in `clazz`. A forwarder is needed if: - // - // - A non-trait base class of `clazz` defines a matching method. Example: - // class C {def f: Int}; trait T extends C {def f = 1}; class D extends T - // Even if C.f is abstract, the forwarder in D is needed, otherwise the JVM would - // resolve `D.f` to `C.f`, see jvms-6.5.invokevirtual. - // - // - There exists another concrete, matching method in a parent interface `p` of - // `clazz`, and the `mixinClass` does not itself extend `p`. In this case the - // forwarder is needed to disambiguate. Example: - // trait T1 {def f = 1}; trait T2 extends T1 {override def f = 2}; class C extends T2 - // In C we don't need a forwarder for f because T2 extends T1, so the JVM resolves - // C.f to T2.f non-ambiguously. See jvms-5.4.3.3, "maximally-specific method". - // trait U1 {def f = 1}; trait U2 {self:U1 => override def f = 2}; class D extends U2 - // In D the forwarder is needed, the interfaces U1 and U2 are unrelated at the JVM - // level. - - @tailrec - def existsCompetingMethod(baseClasses: List[Symbol]): Boolean = baseClasses match { - case baseClass :: rest => - if (baseClass ne mixinClass) { - val m = member.overriddenSymbol(baseClass) - val isCompeting = m.exists && { - !m.owner.isTraitOrInterface || - (!m.isDeferred && !mixinClass.isNonBottomSubClass(m.owner)) - } - isCompeting || existsCompetingMethod(rest) - } else existsCompetingMethod(rest) - - case _ => false - } - - if (existsCompetingMethod(clazz.baseClasses)) - genForwarder() - else if (!settings.nowarnDefaultJunitMethods && JUnitTestClass.exists && member.hasAnnotation(JUnitTestClass)) - warning(member.pos, "JUnit tests in traits that are compiled as default methods are not executed by JUnit 4. JUnit 5 will fix this issue.") + def generateJUnitForwarder: Boolean = { + settings.mixinForwarderChoices.isJunit && + member.annotations.nonEmpty && + JUnitAnnotations.exists(annot => annot.exists && member.hasAnnotation(annot)) } + + if (existsCompetingMethod(clazz.baseClasses) || generateJUnitForwarder) + genForwarder(required = true) + else if (settings.mixinForwarderChoices.isTruthy) + genForwarder(required = false) } case _ => @@ -295,9 +275,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } } - /* Mix in members of trait mixinClass into class clazz. Also, - * for each lazy field in mixinClass, add a link from its mixed in member to its - * initializer method inside the implclass. + /* Mix in members of trait mixinClass into class clazz. */ def mixinTraitMembers(mixinClass: Symbol) { // For all members of a trait's interface do: @@ -319,28 +297,15 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } } else if (mixinMember.hasFlag(ACCESSOR) && notDeferred(mixinMember) - && (mixinMember hasFlag (LAZY | PARAMACCESSOR)) + && (mixinMember hasFlag PARAMACCESSOR) && !isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) { - // pick up where `fields` left off -- it already mixed in fields and accessors for regular vals. - // but has ignored lazy vals and constructor parameter accessors - // TODO: captures added by lambdalift for local traits? - // - // mixin accessor for lazy val or constructor parameter + // mixin accessor for constructor parameter // (note that a paramaccessor cannot have a constant type as it must have a user-defined type) - val mixedInAccessor = cloneAndAddMixinMember(mixinClass, mixinMember) + cloneAndAddMixinMember(mixinClass, mixinMember) + val name = mixinMember.name - if (mixinMember.isLazy) - initializer(mixedInAccessor) = - (mixinClass.info.decl(name) orElse abort(s"Could not find initializer for lazy val $name!")) - - // Add field while we're mixing in the getter (unless it's a Unit-typed lazy val) - // - // lazy val of type Unit doesn't need a field -- the bitmap is enough. - // TODO: constant-typed lazy vals... it's an extreme corner case, but we could also suppress the field in: - // `trait T { final lazy val a = "a" }; class C extends T`, but who writes code like that!? :) - // we'd also have to change the lazyvals logic if we do this - if (!nme.isSetterName(name) && !(mixinMember.isLazy && isUnitGetter(mixinMember))) { + if (!nme.isSetterName(name)) { // enteringPhase: the private field is moved to the implementation class by erasure, // so it can no longer be found in the mixinMember's owner (the trait) val accessed = enteringPickler(mixinMember.accessed) @@ -354,7 +319,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val newFlags = ( (PrivateLocal) - | (mixinMember getFlag MUTABLE | LAZY) + | (mixinMember getFlag MUTABLE) | (if (mixinMember.hasStableFlag) 0 else MUTABLE) ) @@ -384,82 +349,20 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { override def transformInfo(sym: Symbol, tp: Type): Type = tp - /** Return a map of single-use fields to the lazy value that uses them during initialization. - * Each field has to be private and defined in the enclosing class, and there must - * be exactly one lazy value using it. - * - * Such fields will be nulled after the initializer has memoized the lazy value. - */ - def singleUseFields(templ: Template): scala.collection.Map[Symbol, List[Symbol]] = { - val usedIn = mutable.HashMap[Symbol, List[Symbol]]() withDefaultValue Nil - - object SingleUseTraverser extends Traverser { - override def traverse(tree: Tree) { - tree match { - case Assign(lhs, rhs) => traverse(rhs) // assignments don't count - case _ => - if (tree.hasSymbolField && tree.symbol != NoSymbol) { - val sym = tree.symbol - if ((sym.hasAccessorFlag || (sym.isTerm && !sym.isMethod)) - && sym.isPrivate - && !(currentOwner.isGetter && currentOwner.accessed == sym) // getter - && !definitions.isPrimitiveValueClass(sym.tpe.resultType.typeSymbol) - && sym.owner == templ.symbol.owner - && !sym.isLazy - && !tree.isDef) { - debuglog("added use in: " + currentOwner + " -- " + tree) - usedIn(sym) ::= currentOwner - - } - } - super.traverse(tree) - } - } - } - SingleUseTraverser(templ) - debuglog("usedIn: " + usedIn) - usedIn filter { - case (_, member :: Nil) => member.isValue && member.isLazy - case _ => false - } - } - // --------- term transformation ----------------------------------------------- protected def newTransformer(unit: CompilationUnit): Transformer = new MixinTransformer(unit) - class MixinTransformer(unit : CompilationUnit) extends Transformer { + class MixinTransformer(unit : CompilationUnit) extends Transformer with AccessorTreeSynthesis { + /** The typer */ + private var localTyper: erasure.Typer = _ + protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) /** The rootContext used for typing */ private val rootContext = erasure.NoContext.make(EmptyTree, rootMirror.RootClass, newScope) - /** The typer */ - private var localTyper: erasure.Typer = _ - private def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) - - /** Map lazy values to the fields they should null after initialization. */ - private var lazyValNullables: Map[Symbol, Set[Symbol]] = _ - - /** Map a field symbol to a unique integer denoting its position in the class layout. - * For each class, fields defined by the class come after inherited fields. Mixed-in - * fields count as fields defined by the class itself. - */ - private val fieldOffset = perRunCaches.newMap[Symbol, Int]() - - private val bitmapKindForCategory = perRunCaches.newMap[Name, ClassSymbol]() - - // ByteClass, IntClass, LongClass - private def bitmapKind(field: Symbol): ClassSymbol = bitmapKindForCategory(bitmapCategory(field)) - - private def flagsPerBitmap(field: Symbol): Int = bitmapKind(field) match { - case BooleanClass => 1 - case ByteClass => 8 - case IntClass => 32 - case LongClass => 64 - } - /** The first transform; called in a pre-order traversal at phase mixin * (that is, every node is processed before its children). @@ -484,416 +387,24 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } } - def needsInitAndHasOffset(sym: Symbol) = - needsInitFlag(sym) && (fieldOffset contains sym) - - /** Examines the symbol and returns a name indicating what brand of - * bitmap it requires. The possibilities are the BITMAP_* vals - * defined in StdNames. If it needs no bitmap, nme.NO_NAME. - */ - def bitmapCategory(field: Symbol): Name = { - import nme._ - val isNormal = ( - if (isFieldWithBitmap(field)) true - // bitmaps for checkinit fields are not inherited - else if (needsInitFlag(field) && !field.isDeferred) false - else return NO_NAME - ) - if (field.accessed hasAnnotation TransientAttr) { - if (isNormal) BITMAP_TRANSIENT - else BITMAP_CHECKINIT_TRANSIENT - } else { - if (isNormal) BITMAP_NORMAL - else BITMAP_CHECKINIT - } - } /** Add all new definitions to a non-trait class - * These fall into the following categories: - * - for a trait interface: - * - abstract accessors for all fields in the implementation class - * - for a non-trait class: - * - A field for every in a mixin class - * - Setters and getters for such fields - * - getters for mixed in lazy fields are completed - * - module variables and module creators for every module in a mixin class - * (except if module is lifted -- in this case the module variable - * is local to some function, and the creator method is static.) - * - A super accessor for every super accessor in a mixin class - * - Forwarders for all methods that are implemented statically - * All superaccessors are completed with right-hand sides (@see completeSuperAccessor) - * - * @param clazz The class to which definitions are added - */ + * + * These fall into the following categories: + * - for a trait interface: + * - abstract accessors for all paramaccessor or early initialized fields + * - for a non-trait class: + * - field and accessor implementations for each inherited paramaccessor or early initialized field + * - A super accessor for every super accessor in a mixin class + * - Forwarders for all methods that are implemented statically + * + * All superaccessors are completed with right-hand sides (@see completeSuperAccessor) + * + * @param clazz The class to which definitions are added + */ private def addNewDefs(clazz: Symbol, stats: List[Tree]): List[Tree] = { - val newDefs = mutable.ListBuffer[Tree]() - - /* Attribute given tree and anchor at given position */ - def attributedDef(pos: Position, tree: Tree): Tree = { - debuglog("add new def to " + clazz + ": " + tree) - typedPos(pos)(tree) - } - - /* The position of given symbol, or, if this is undefined, - * the position of the current class. - */ - def position(sym: Symbol) = - if (sym.pos == NoPosition) clazz.pos else sym.pos - - /* Add tree at given position as new definition */ - def addDef(pos: Position, tree: Tree) { - newDefs += attributedDef(pos, tree) - } - - /* Add new method definition. - * - * @param sym The method symbol. - * @param rhs The method body. - */ - def addDefDef(sym: Symbol, rhs: Tree = EmptyTree) = addDef(position(sym), DefDef(sym, rhs)) - def addValDef(sym: Symbol, rhs: Tree = EmptyTree) = addDef(position(sym), ValDef(sym, rhs)) - - /* Add `newdefs` to `stats`, removing any abstract method definitions - * in `stats` that are matched by some symbol defined in - * `newDefs`. - */ - def add(stats: List[Tree], newDefs: List[Tree]) = { - val newSyms = newDefs map (_.symbol) - def isNotDuplicate(tree: Tree) = tree match { - case DefDef(_, _, _, _, _, _) => - val sym = tree.symbol - !(sym.isDeferred && - (newSyms exists (nsym => nsym.name == sym.name && (nsym.tpe matches sym.tpe)))) - case _ => - true - } - if (newDefs.isEmpty) stats - else newDefs ::: (stats filter isNotDuplicate) - } - - /* If `stat` is a superaccessor, complete it by adding a right-hand side. - * Note: superaccessors are always abstract until this point. - * The method to call in a superaccessor is stored in the accessor symbol's alias field. - * The rhs is: - * super.A(xs) where A is the super accessor's alias and xs are its formal parameters. - * This rhs is typed and then mixin transformed. - */ - def completeSuperAccessor(stat: Tree) = stat match { - case DefDef(_, _, _, vparams :: Nil, _, EmptyTree) if stat.symbol.isSuperAccessor => - val body = atPos(stat.pos)(Apply(SuperSelect(clazz, stat.symbol.alias), vparams map (v => Ident(v.symbol)))) - val pt = stat.symbol.tpe.resultType - - copyDefDef(stat)(rhs = enteringMixin(transform(localTyper.typed(body, pt)))) - case _ => - stat - } - - /* - * Return the bitmap field for 'offset'. Depending on the hierarchy it is possible to reuse - * the bitmap of its parents. If that does not exist yet we create one. - */ - def bitmapFor(clazz0: Symbol, offset: Int, field: Symbol): Symbol = { - val category = bitmapCategory(field) - val bitmapName = nme.newBitmapName(category, offset / flagsPerBitmap(field)).toTermName - val sym = clazz0.info.decl(bitmapName) - - assert(!sym.isOverloaded, sym) - - def createBitmap: Symbol = { - val bitmapKind = bitmapKindForCategory(category) - val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo bitmapKind.tpe - enteringTyper(sym addAnnotation VolatileAttr) - - category match { - case nme.BITMAP_TRANSIENT | nme.BITMAP_CHECKINIT_TRANSIENT => sym addAnnotation TransientAttr - case _ => - } - val init = bitmapKind match { - case BooleanClass => ValDef(sym, FALSE) - case _ => ValDef(sym, ZERO) - } - - sym setFlag PrivateLocal - clazz0.info.decls.enter(sym) - addDef(clazz0.pos, init) - sym - } - - sym orElse createBitmap - } - - def maskForOffset(offset: Int, sym: Symbol, kind: ClassSymbol): Tree = { - def realOffset = offset % flagsPerBitmap(sym) - if (kind == LongClass ) LIT(1L << realOffset) else LIT(1 << realOffset) - } - - /* Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */ - def mkSetFlag(clazz: Symbol, offset: Int, valSym: Symbol, kind: ClassSymbol): Tree = { - val bmp = bitmapFor(clazz, offset, valSym) - def mask = maskForOffset(offset, valSym, kind) - def x = This(clazz) DOT bmp - def newValue = if (kind == BooleanClass) TRUE else (x GEN_| (mask, kind)) - - x === newValue - } - - /* Return an (untyped) tree of the form 'clazz.this.bitmapSym & mask (==|!=) 0', the - * precise comparison operator depending on the value of 'equalToZero'. - */ - def mkTest(clazz: Symbol, mask: Tree, bitmapSym: Symbol, equalToZero: Boolean, kind: ClassSymbol): Tree = { - val bitmapTree = (This(clazz) DOT bitmapSym) - def lhs = bitmapTree GEN_& (mask, kind) - kind match { - case BooleanClass => - if (equalToZero) NOT(bitmapTree) - else bitmapTree - case _ => - if (equalToZero) lhs GEN_== (ZERO, kind) - else lhs GEN_!= (ZERO, kind) - } - } - - def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], - stats: List[Tree], retVal: Tree, attrThis: Tree, args: List[Tree]): Symbol = { - val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, PRIVATE) - val params = defSym newSyntheticValueParams args.map(_.symbol.tpe) - defSym setInfoAndEnter MethodType(params, lzyVal.tpe.resultType) - val rhs: Tree = gen.mkSynchronizedCheck(attrThis, cond, syncBody, stats).changeOwner(currentOwner -> defSym) - val strictSubst = new TreeSymSubstituterWithCopying(args.map(_.symbol), params) - addDef(position(defSym), DefDef(defSym, strictSubst(BLOCK(rhs, retVal)))) - defSym - } - - def mkFastPathLazyBody(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree], - stats: List[Tree], retVal: Tree): Tree = { - mkFastPathBody(clazz, lzyVal, cond, syncBody, stats, retVal, gen.mkAttributedThis(clazz), List()) - } - - def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree], - stats: List[Tree], retVal: Tree, attrThis: Tree, args: List[Tree]): Tree = { - val slowPathSym: Symbol = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal, attrThis, args) - If(cond, fn (This(clazz), slowPathSym, args.map(arg => Ident(arg.symbol)): _*), retVal) - } - - - /* Always copy the tree if we are going to perform sym substitution, - * otherwise we will side-effect on the tree that is used in the fast path - */ - class TreeSymSubstituterWithCopying(from: List[Symbol], to: List[Symbol]) extends TreeSymSubstituter(from, to) { - override def transform(tree: Tree): Tree = - if (tree.hasSymbolField && from.contains(tree.symbol)) - super.transform(tree.duplicate) - else super.transform(tree.duplicate) - - override def apply[T <: Tree](tree: T): T = if (from.isEmpty) tree else super.apply(tree) - } - - /* return a 'lazified' version of rhs. It uses double-checked locking to ensure - * initialization is performed at most once. For performance reasons the double-checked - * locking is split into two parts, the first (fast) path checks the bitmap without - * synchronizing, and if that fails it initializes the lazy val within the - * synchronization block (slow path). This way the inliner should optimize - * the fast path because the method body is small enough. - * Private fields used only in this initializer are subsequently set to null. - * - * @param clazz The class symbol - * @param lzyVal The symbol of this lazy field - * @param init The tree which initializes the field ( f = <rhs> ) - * @param offset The offset of this field in the flags bitmap - * - * The result will be a tree of the form - * { if ((bitmap&n & MASK) == 0) this.l$compute() - * else l$ - * - * ... - * def l$compute() = { synchronized(this) { - * if ((bitmap$n & MASK) == 0) { - * init // l$ = <rhs> - * bitmap$n = bimap$n | MASK - * }} - * l$ - * } - * - * ... - * this.f1 = null - * ... this.fn = null - * } - * where bitmap$n is a byte, int or long value acting as a bitmap of initialized values. - * The kind of the bitmap determines how many bit indicators for lazy vals are stored in it. - * For Int bitmap it is 32 and then 'n' in the above code is: (offset / 32), - * the MASK is (1 << (offset % 32)). - * If the class contains only a single lazy val then the bitmap is represented - * as a Boolean and the condition checking is a simple bool test. - */ - def mkLazyDef(clazz: Symbol, lzyVal: Symbol, init: List[Tree], retVal: Tree, offset: Int): Tree = { - def nullify(sym: Symbol) = Select(This(clazz), sym.accessedOrSelf) === LIT(null) - - val bitmapSym = bitmapFor(clazz, offset, lzyVal) - val kind = bitmapKind(lzyVal) - val mask = maskForOffset(offset, lzyVal, kind) - def cond = mkTest(clazz, mask, bitmapSym, equalToZero = true, kind) - val nulls = lazyValNullables(lzyVal).toList sortBy (_.id) map nullify - def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal, kind), UNIT) - - if (nulls.nonEmpty) - log("nulling fields inside " + lzyVal + ": " + nulls) - - typedPos(init.head.pos)(mkFastPathLazyBody(clazz, lzyVal, cond, syncBody, nulls, retVal)) - } - - def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = { - val sym = fieldSym.getterIn(fieldSym.owner) - val bitmapSym = bitmapFor(clazz, offset, sym) - val kind = bitmapKind(sym) - val mask = maskForOffset(offset, sym, kind) - val msg = s"Uninitialized field: ${unit.source}: ${pos.line}" - val result = - IF (mkTest(clazz, mask, bitmapSym, equalToZero = false, kind)) . - THEN (retVal) . - ELSE (Throw(NewFromConstructor(UninitializedFieldConstructor, LIT(msg)))) - - typedPos(pos)(BLOCK(result, retVal)) - } - - /* Complete lazy field accessors. Applies only to classes, - * for its own (non inherited) lazy fields. If 'checkinit' - * is enabled, getters that check for the initialized bit are - * generated, and the class constructor is changed to set the - * initialized bits. - */ - def addCheckedGetters(clazz: Symbol, stats: List[Tree]): List[Tree] = { - def dd(stat: DefDef) = { - val sym = stat.symbol - def isEmpty = stat.rhs == EmptyTree - - if (!clazz.isTrait && sym.isLazy && !isEmpty) { - assert(fieldOffset contains sym, sym) - deriveDefDef(stat) { - case t if isUnitGetter(sym) => mkLazyDef(clazz, sym, List(t), UNIT, fieldOffset(sym)) - - case Block(stats, res) => - mkLazyDef(clazz, sym, stats, Select(This(clazz), res.symbol), fieldOffset(sym)) - - case t => t // pass specialized lazy vals through - } - } - else if (needsInitFlag(sym) && !isEmpty && !clazz.hasFlag(TRAIT)) { - assert(fieldOffset contains sym, sym) - deriveDefDef(stat)(rhs => - (mkCheckedAccessor(clazz, _: Tree, fieldOffset(sym), stat.pos, sym))( - if (isUnitGetter(sym)) UNIT else rhs - ) - ) - } - else if (sym.isConstructor) { - deriveDefDef(stat)(addInitBits(clazz, _)) - } - else if (settings.checkInit && !clazz.isTrait && sym.isSetter) { - val getter = sym.getterIn(clazz) - if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter)) - deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))), UNIT)) - else stat - } - else stat - } - stats map { - case defn: DefDef => dd(defn) - case stat => stat - } - } - - class AddInitBitsTransformer(clazz: Symbol) extends Transformer { - private def checkedGetter(lhs: Tree) = { - val sym = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter) - if (needsInitAndHasOffset(sym)) { - debuglog("adding checked getter for: " + sym + " " + lhs.symbol.flagString) - List(localTyper typed mkSetFlag(clazz, fieldOffset(sym), sym, bitmapKind(sym))) - } - else Nil - } - override def transformStats(stats: List[Tree], exprOwner: Symbol) = { - // !!! Ident(self) is never referenced, is it supposed to be confirming - // that self is anything in particular? - super.transformStats( - stats flatMap { - case stat @ Assign(lhs @ Select(This(_), _), rhs) => stat :: checkedGetter(lhs) - // remove initialization for default values - case Apply(lhs @ Select(Ident(self), _), EmptyTree.asList) if lhs.symbol.isSetter => Nil - case stat => List(stat) - }, - exprOwner - ) - } - } - - /* Adds statements to set the 'init' bit for each field initialized - * in the body of a constructor. - */ - def addInitBits(clazz: Symbol, rhs: Tree): Tree = - new AddInitBitsTransformer(clazz) transform rhs - - // begin addNewDefs - - /* Fill the map from fields to offset numbers. - * Instead of field symbols, the map keeps their getter symbols. This makes - * code generation easier later. - */ - def buildBitmapOffsets() { - def fold(fields: List[Symbol], category: Name) = { - var idx = 0 - fields foreach { f => - fieldOffset(f) = idx - idx += 1 - } - - if (idx == 0) () - else if (idx == 1) bitmapKindForCategory(category) = BooleanClass - else if (idx < 9) bitmapKindForCategory(category) = ByteClass - else if (idx < 33) bitmapKindForCategory(category) = IntClass - else bitmapKindForCategory(category) = LongClass - } - clazz.info.decls.toList groupBy bitmapCategory foreach { - case (nme.NO_NAME, _) => () - case (category, fields) => fold(fields, category) - } - } - buildBitmapOffsets() - var stats1 = addCheckedGetters(clazz, stats) - - def getterBody(getter: Symbol) = { - assert(getter.isGetter) - val readValue = - if (getter.isLazy) { - getter.tpe.resultType match { - case ConstantType(c) => Literal(c) - case _ => - val initCall = Apply(SuperSelect(clazz, initializer(getter)), Nil) - val offset = fieldOffset(getter) - if (isUnitGetter(getter)) mkLazyDef(clazz, getter, List(initCall), UNIT, offset) - else mkLazyDef(clazz, getter, List(atPos(getter.pos)(Assign(fieldAccess(getter), initCall))), fieldAccess(getter), offset) - } - } else { - assert(getter.hasFlag(PARAMACCESSOR)) - fieldAccess(getter) - } - - if (!needsInitFlag(getter)) readValue - else mkCheckedAccessor(clazz, readValue, fieldOffset(getter), getter.pos, getter) - } - - def setterBody(setter: Symbol) = { - val getter = setter.getterIn(clazz) - assert(getter.hasFlag(PARAMACCESSOR), s"missing implementation for non-paramaccessor $setter in $clazz") - - val setInitFlag = - if (!needsInitFlag(getter)) Nil - else List(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter))) - - Block(Assign(fieldAccess(setter), Ident(setter.firstParam)) :: setInitFlag : _*) - } - - def fieldAccess(accessor: Symbol) = Select(This(clazz), accessor.accessed) - + val accessorSynth = new UncheckedAccessorSynth(clazz) + import accessorSynth._ // for all symbols `sym` in the class definition, which are mixed in by mixinTraitMembers for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) { @@ -902,10 +413,10 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { if (clazz.isTrait || sym.isSuperAccessor) addDefDef(sym) // implement methods mixed in from a supertrait (the symbols were created by mixinTraitMembers) else if (sym.hasFlag(ACCESSOR) && !sym.hasFlag(DEFERRED)) { - assert(sym hasFlag (LAZY | PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?") + assert(sym hasFlag (PARAMACCESSOR), s"mixed in $sym from $clazz is not param?!?") // add accessor definitions - addDefDef(sym, if (sym.isSetter) setterBody(sym) else getterBody(sym)) + addDefDef(sym, accessorBody(sym)) } else if (!sym.isMethod) addValDef(sym) // field else if (!sym.isMacro) { // forwarder @@ -915,32 +426,33 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } } - stats1 = add(stats1, newDefs.toList) + val implementedAccessors = implementWithNewDefs(stats) - if (clazz.isTrait) stats1 = stats1.filter { - case vd: ValDef => - assert(vd.symbol.hasFlag(PRESUPER | PARAMACCESSOR | LAZY), s"unexpected valdef $vd in trait $clazz") - false + if (clazz.isTrait) + implementedAccessors filter { + case vd: ValDef => assert(vd.symbol.hasFlag(PRESUPER | PARAMACCESSOR), s"unexpected valdef $vd in trait $clazz"); false case _ => true } + else { + /* If `stat` is a superaccessor, complete it by adding a right-hand side. + * Note: superaccessors are always abstract until this point. + * The method to call in a superaccessor is stored in the accessor symbol's alias field. + * The rhs is: + * super.A(xs) where A is the super accessor's alias and xs are its formal parameters. + * This rhs is typed and then mixin transformed. + */ + def completeSuperAccessor(stat: Tree) = stat match { + case DefDef(_, _, _, vparams :: Nil, _, EmptyTree) if stat.symbol.isSuperAccessor => + val body = atPos(stat.pos)(Apply(SuperSelect(clazz, stat.symbol.alias), vparams map (v => Ident(v.symbol)))) + val pt = stat.symbol.tpe.resultType + + copyDefDef(stat)(rhs = enteringMixin(transform(localTyper.typed(body, pt)))) + case _ => + stat + } - if (!clazz.isTrait) stats1 = stats1 map completeSuperAccessor - - stats1 - } - - private def nullableFields(templ: Template): Map[Symbol, Set[Symbol]] = { - val scope = templ.symbol.owner.info.decls - // if there are no lazy fields, take the fast path and save a traversal of the whole AST - if (scope exists (_.isLazy)) { - val map = mutable.Map[Symbol, Set[Symbol]]() withDefaultValue Set() - // check what fields can be nulled for - for ((field, users) <- singleUseFields(templ); lazyFld <- users if !lazyFld.accessed.hasAnnotation(TransientAttr)) - map(lazyFld) += field - - map.toMap + implementedAccessors map completeSuperAccessor } - else Map() } /** The transform that gets applied to a tree after it has been completely @@ -961,8 +473,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case templ @ Template(parents, self, body) => // change parents of templates to conform to parents in the symbol info val parents1 = currentOwner.info.parents map (t => TypeTree(t) setPos tree.pos) - // mark fields which can be nulled afterward - lazyValNullables = nullableFields(templ) withDefaultValue Set() + // add all new definitions to current class or interface val statsWithNewDefs = addNewDefs(currentOwner, body) statsWithNewDefs foreach { diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 87c14eb3a1..c171050bbd 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -723,7 +723,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else if (!sClass.isTrait && m.isMethod && m.hasFlag(LAZY)) { forwardToOverload(m) - } else if (m.isValue && !m.isMethod && !m.hasFlag(LAZY)) { // concrete value definition + } else if (m.isValue && !m.isMethod) { // concrete value definition def mkAccessor(field: Symbol, name: Name) = { val newFlags = (SPECIALIZED | m.getterIn(clazz).flags) & ~(LOCAL | CASEACCESSOR | PARAMACCESSOR) // we rely on the super class to initialize param accessors @@ -744,7 +744,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { enterMember(specVal) // create accessors - if (nme.isLocalName(m.name)) { + if (m.isLazy) { + // no getters needed (we'll specialize the compute method and accessor separately), can stay private + // m.setFlag(PRIVATE) -- TODO: figure out how to leave the non-specialized lazy var private + // (the implementation needs it to be visible while duplicating and retypechecking, + // but it really could be private in bytecode) + specVal.setFlag(PRIVATE) + } + else if (nme.isLocalName(m.name)) { val specGetter = mkAccessor(specVal, specVal.getterName) setInfo MethodType(Nil, specVal.info) val origGetter = overrideIn(sClass, m.getterIn(clazz)) info(origGetter) = Forward(specGetter) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index fa7c503213..744b9c8a8e 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -274,10 +274,8 @@ abstract class TailCalls extends Transform { import runDefinitions.{Boolean_or, Boolean_and} tree match { - case ValDef(_, _, _, _) => - if (tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass)) - reporter.error(tree.pos, "lazy vals are not tailcall transformed") - + case dd: DefDef if tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass) => + reporter.error(tree.pos, "lazy vals are not tailcall transformed") super.transform(tree) case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if isEligible(dd) => diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 6ade45c41c..f6c667353f 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -439,9 +439,9 @@ abstract class UnCurry extends InfoTransform super.transform(treeCopy.DefDef(dd, mods, name, tparams, vparamssNoRhs, tpt, rhs)) } } - case ValDef(_, _, _, rhs) => + case ValDef(mods, _, _, rhs) => if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit) - if (!sym.owner.isSourceMethod) + if (!sym.owner.isSourceMethod || mods.isLazy) withNeedLift(needLift = true) { super.transform(tree) } else super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 78e72cf771..df014b5161 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -151,8 +151,12 @@ abstract class Duplicators extends Analyzer { ldef.symbol = newsym debuglog("newsym: " + newsym + " info: " + newsym.info) - case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) => - debuglog("ValDef " + name + " sym.info: " + vdef.symbol.info) + // don't retypecheck val members or local lazy vals -- you'll end up with duplicate symbols because + // entering a valdef results in synthesizing getters etc + // TODO: why retype check any valdefs?? I checked and the rhs is specialized just fine this way + // (and there are no args/type params/... to warrant full type checking?) + case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) || owner.isClass => + debuglog(s"ValDef $name in $owner sym.info: ${vdef.symbol.info}") invalidSyms(vdef.symbol) = vdef val newowner = owner orElse context.owner val newsym = vdef.symbol.cloneSymbol(newowner) diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index e0b64a7600..d11417192d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -5,6 +5,7 @@ package scala.tools.nsc package typechecker +import scala.reflect.NameTransformer import symtab.Flags._ import scala.reflect.internal.util.StringOps.ojoin import scala.reflect.internal.util.ListOfNil @@ -116,38 +117,53 @@ trait MethodSynthesis { import NamerErrorGen._ - def enterImplicitWrapper(tree: ClassDef): Unit = { - enterSyntheticSym(ImplicitClassWrapper(tree).derivedTree) - } - // trees are later created by addDerivedTrees (common logic is encapsulated in field/standardAccessors/beanAccessors) + import treeInfo.noFieldFor + + // populate synthetics for this unit with trees that will later be added by the typer + // we get here when entering the symbol for the valdef, so its rhs has not yet been type checked def enterGetterSetter(tree: ValDef): Unit = { + val fieldSym = + if (noFieldFor(tree, owner)) NoSymbol + else owner.newValue(tree.name append NameTransformer.LOCAL_SUFFIX_STRING, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) + val getter = Getter(tree) val getterSym = getter.createSym - val setterSym = if (getter.needsSetter) Setter(tree).createSym else NoSymbol - - // a lazy field is linked to its lazy accessor (TODO: can we do the same for field -> getter -> setter) - val fieldSym = if (Field.noFieldFor(tree)) NoSymbol else Field(tree).createSym(getterSym) // only one symbol can have `tree.pos`, the others must focus their position // normally the field gets the range position, but if there is none, give it to the getter tree.symbol = fieldSym orElse (getterSym setPos tree.pos) + val namer = namerOf(tree.symbol) + + // the valdef gets the accessor symbol for a lazy val (too much going on in its RHS) + // the fields phase creates the field symbol + if (!tree.mods.isLazy) { + // if there's a field symbol, the getter is considered a synthetic that must be added later + // if there's no field symbol, the ValDef tree receives the getter symbol and thus is not a synthetic + if (fieldSym != NoSymbol) { + context.unit.synthetics(getterSym) = getter.derivedTree(getterSym) + getterSym setInfo namer.accessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = false) + } else getterSym setInfo namer.valTypeCompleter(tree) + + enterInScope(getterSym) + + if (getter.needsSetter) { + val setter = Setter(tree) + val setterSym = setter.createSym + context.unit.synthetics(setterSym) = setter.derivedTree(setterSym) + setterSym setInfo namer.accessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = true) + enterInScope(setterSym) + } - val namer = if (fieldSym != NoSymbol) namerOf(fieldSym) else namerOf(getterSym) - - // There's no reliable way to detect all kinds of setters from flags or name!!! - // A BeanSetter's name does not end in `_=` -- it does begin with "set", but so could the getter - // for a regular Scala field... TODO: can we add a flag to distinguish getter/setter accessors? - val getterCompleter = namer.accessorTypeCompleter(tree, isSetter = false) - val setterCompleter = namer.accessorTypeCompleter(tree, isSetter = true) - - getterSym setInfo getterCompleter - setterSym andAlso (_ setInfo setterCompleter) - fieldSym andAlso (_ setInfo namer.valTypeCompleter(tree)) - - enterInScope(getterSym) - setterSym andAlso (enterInScope(_)) - fieldSym andAlso (enterInScope(_)) + // TODO: delay emitting the field to the fields phase (except for private[this] vals, which only get a field and no accessors) + if (fieldSym != NoSymbol) { + fieldSym setInfo namer.valTypeCompleter(tree) + enterInScope(fieldSym) + } + } else { + getterSym setInfo namer.valTypeCompleter(tree) + enterInScope(getterSym) + } deriveBeanAccessors(tree, namer) } @@ -188,242 +204,82 @@ trait MethodSynthesis { sym } - val getterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = false) + val getterCompleter = namer.accessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = false) enterInScope(deriveBeanAccessor(if (hasBeanProperty) "get" else "is") setInfo getterCompleter) if (tree.mods.isMutable) { - val setterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = true) + val setterCompleter = namer.accessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = true) enterInScope(deriveBeanAccessor("set") setInfo setterCompleter) } } } - import AnnotationInfo.{mkFilter => annotationFilter} - def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { - case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar && !vd.symbol.isJava => - stat.symbol.initialize // needed! - - val getter = Getter(vd) - getter.validate() - val accessors = getter :: (if (getter.needsSetter) Setter(vd) :: Nil else Nil) - (Field(vd) :: accessors).map(_.derivedTree).filter(_ ne EmptyTree) - - case cd @ ClassDef(mods, _, _, _) if mods.isImplicit => - val annotations = stat.symbol.initialize.annotations - // TODO: need to shuffle annotations between wrapper and class. - val wrapper = ImplicitClassWrapper(cd) - val meth = wrapper.derivedSym - context.unit.synthetics get meth match { - case Some(mdef) => - context.unit.synthetics -= meth - meth setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false)) - cd.symbol setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true)) - List(cd, mdef) - case _ => - // Shouldn't happen, but let's give ourselves a reasonable error when it does - context.error(cd.pos, s"Internal error: Symbol for synthetic factory method not found among ${context.unit.synthetics.keys.mkString(", ")}") - // Soldier on for the sake of the presentation compiler - List(cd) - } - case _ => - stat :: Nil - } - - - sealed trait Derived { - /** The derived symbol. It is assumed that this symbol already exists and has been - * entered in the parent scope when derivedSym is called - */ - def derivedSym: Symbol - - /** The definition tree of the derived symbol. */ - def derivedTree: Tree + def enterImplicitWrapper(classDef: ClassDef): Unit = { + val methDef = factoryMeth(classDef.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC, classDef.name.toTermName, classDef) + val methSym = assignAndEnterSymbol(methDef) + context.unit.synthetics(methSym) = methDef + methSym setInfo implicitFactoryMethodCompleter(methDef, classDef.symbol, completerOf(methDef).asInstanceOf[LockingTypeCompleter]) } - /** A synthetic method which performs the implicit conversion implied by - * the declaration of an implicit class. - */ - case class ImplicitClassWrapper(tree: ClassDef) extends Derived { - def derivedSym = { - val enclClass = tree.symbol.owner.enclClass - // Only methods will do! Don't want to pick up any stray - // companion objects of the same name. - val result = enclClass.info decl derivedName filter (x => x.isMethod && x.isSynthetic) - if (result == NoSymbol || result.isOverloaded) - context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $derivedName in $enclClass / ${enclClass.info.decls}") - result - } - - def derivedTree = factoryMeth(derivedMods, derivedName, tree) - - def derivedName = tree.name.toTermName - def derivedMods = tree.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC - } - - trait DerivedAccessor extends Derived { + trait DerivedAccessor { def tree: ValDef def derivedName: TermName def derivedFlags: Long + def derivedTree(sym: Symbol): Tree def derivedPos = tree.pos.focus def createSym = createMethod(tree, derivedName, derivedPos, derivedFlags) } case class Getter(tree: ValDef) extends DerivedAccessor { - def derivedName = tree.name - - def derivedSym = - if (tree.mods.isLazy) tree.symbol.lazyAccessor - else if (Field.noFieldFor(tree)) tree.symbol - else tree.symbol.getterIn(tree.symbol.enclClass) - + def derivedName = tree.name def derivedFlags = tree.mods.flags & GetterFlags | ACCESSOR.toLong | ( if (needsSetter) 0 else STABLE ) + def needsSetter = tree.mods.isMutable // implies !lazy - def needsSetter = tree.mods.isMutable // implies !lazy - - override def derivedTree = - if (tree.mods.isLazy) deriveLazyAccessor - else newDefDef(derivedSym, if (Field.noFieldFor(tree)) tree.rhs else Select(This(tree.symbol.enclClass), tree.symbol))(tpt = derivedTpt) - - /** Implements lazy value accessors: - * - for lazy values of type Unit and all lazy fields inside traits, - * the rhs is the initializer itself, because we'll just "compute" the result on every access - * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap) - * - for all other lazy values z the accessor is a block of this form: - * { z = <rhs>; z } where z can be an identifier or a field. - */ - private def deriveLazyAccessor: DefDef = { - val ValDef(_, _, tpt0, rhs0) = tree - val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0) - val body = - if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor - else gen.mkAssignAndReturn(tree.symbol, rhs1) - - derivedSym setPos tree.pos // TODO: can we propagate `tree.pos` to `derivedSym` when the symbol is created? - val ddefRes = DefDef(derivedSym, new ChangeOwnerTraverser(tree.symbol, derivedSym)(body)) - // ValDef will have its position focused whereas DefDef will have original correct rangepos - // ideally positions would be correct at the creation time but lazy vals are really a special case - // here so for the sake of keeping api clean we fix positions manually in LazyValGetter - ddefRes.tpt.setPos(tpt0.pos) - tpt0.setPos(tpt0.pos.focus) - ddefRes - } + override def derivedTree(derivedSym: Symbol) = { + val missingTpt = tree.tpt.isEmpty + val tpt = if (missingTpt) TypeTree() else tree.tpt.duplicate - // TODO: more principled approach -- this is a bit bizarre - private def derivedTpt = { - // For existentials, don't specify a type for the getter, even one derived - // from the symbol! This leads to incompatible existentials for the field and - // the getter. Let the typer do all the work. You might think "why only for - // existentials, why not always," and you would be right, except: a single test - // fails, but it looked like some work to deal with it. Test neg/t0606.scala - // starts compiling (instead of failing like it's supposed to) because the typer - // expects to be able to identify escaping locals in typedDefDef, and fails to - // spot that brand of them. In other words it's an artifact of the implementation. - // - // JZ: ... or we could go back to uniformly using explicit result types in all cases - // if we fix `dropExistential`. More details https://github.com/scala/scala-dev/issues/165 - val getterTp = derivedSym.tpe_*.finalResultType - // Range position errors ensue if we don't duplicate this in some - // circumstances (at least: concrete vals with existential types.) - def inferredTpt = TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus) - val tpt = getterTp match { - case _: ExistentialType => inferredTpt - case _ => getterTp.widen match { - case _: ExistentialType => inferredTpt - case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field - case _ => TypeTree(getterTp) - } - } - tpt setPos tree.tpt.pos.focus - } + val rhs = + if (noFieldFor(tree, owner)) tree.rhs // context.unit.transformed.getOrElse(tree.rhs, tree.rhs) + else Select(This(tree.symbol.enclClass), tree.symbol) - def validate() = { - assert(derivedSym != NoSymbol, tree) - if (derivedSym.isOverloaded) - GetterDefinedTwiceError(derivedSym) + newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = Nil, tpt = tpt) } +// derivedSym setPos tree.pos +// // ValDef will have its position focused whereas DefDef will have original correct rangepos +// // ideally positions would be correct at the creation time but lazy vals are really a special case +// // here so for the sake of keeping api clean we fix positions manually in LazyValGetter +// tpt.setPos(tree.tpt.pos) +// tree.tpt.setPos(tree.tpt.pos.focus) + } case class Setter(tree: ValDef) extends DerivedAccessor { def derivedName = tree.setterName - def derivedSym = tree.symbol.setterIn(tree.symbol.enclClass) def derivedFlags = tree.mods.flags & SetterFlags | ACCESSOR - def derivedTree = - derivedSym.paramss match { - case (setterParam :: Nil) :: _ => - // assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for ${tree.symbol} in ${tree.symbol.enclClass}") - val rhs = - if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree - else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam)) - - DefDef(derivedSym, rhs) - case _ => EmptyTree - } - } - - object Field { - // No field for these vals (either never emitted or eliminated later on): - // - abstract vals have no value we could store (until they become concrete, potentially) - // - lazy vals of type Unit - // - concrete vals in traits don't yield a field here either (their getter's RHS has the initial value) - // Constructors will move the assignment to the constructor, abstracting over the field using the field setter, - // and Fields will add a field to the class that mixes in the trait, implementing the accessors in terms of it - // - [Emitted, later removed during Constructors] a concrete val with a statically known value (ConstantType) - // performs its side effect according to lazy/strict semantics, but doesn't need to store its value - // each access will "evaluate" the RHS (a literal) again - // We would like to avoid emitting unnecessary fields, but the required knowledge isn't available until after typer. - // The only way to avoid emitting & suppressing, is to not emit at all until we are sure to need the field, as dotty does. - // NOTE: do not look at `vd.symbol` when called from `enterGetterSetter` (luckily, that call-site implies `!mods.isLazy`), - // similarly, the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite) - // as the symbol info is in the process of being created then. - // TODO: harmonize tree & symbol creation - // the middle `&& !owner.isTrait` is needed after `isLazy` because non-unit-typed lazy vals in traits still get a field -- see neg/t5455.scala - def noFieldFor(vd: ValDef) = (vd.mods.isDeferred - || (vd.mods.isLazy && !owner.isTrait && isUnitType(vd.symbol.info)) - || (owner.isTrait && !traitFieldFor(vd))) - - // TODO: never emit any fields in traits -- only use getter for lazy/presuper ones as well - private def traitFieldFor(vd: ValDef): Boolean = vd.mods.hasFlag(PRESUPER | LAZY) - } + def derivedTree(derivedSym: Symbol) = { + val setterParam = nme.syntheticParamName(1) - case class Field(tree: ValDef) extends Derived { - private val isLazy = tree.mods.isLazy - - // If the owner is not a class, this is a lazy val from a method, - // with no associated field. It has an accessor with $lzy appended to its name and - // its flags are set differently. The implicit flag is reset because otherwise - // a local implicit "lazy val x" will create an ambiguity with itself - // via "x$lzy" as can be seen in test #3927. - private val localLazyVal = isLazy && !owner.isClass - private val nameSuffix = - if (!localLazyVal) reflect.NameTransformer.LOCAL_SUFFIX_STRING - else reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING - - def derivedName = tree.name.append(nameSuffix) - - def createSym(getter: MethodSymbol) = { - val sym = owner.newValue(derivedName, tree.pos, derivedMods.flags) - if (isLazy) sym setLazyAccessor getter - sym - } + // note: tree.tpt may be EmptyTree, which will be a problem when use as the tpt of a parameter + // the completer will patch this up (we can't do this now without completing the field) + val missingTpt = tree.tpt.isEmpty + val tptToPatch = if (missingTpt) TypeTree() else tree.tpt.duplicate - def derivedSym = tree.symbol + val vparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), setterParam, tptToPatch, EmptyTree)) - def derivedMods = - if (!localLazyVal) tree.mods & FieldFlags | PrivateLocal | (if (isLazy) MUTABLE else 0) - else (tree.mods | ARTIFACT | MUTABLE) & ~IMPLICIT + val tpt = TypeTree(UnitTpe) - // TODO: why is this different from the symbol!? - private def derivedModsForTree = tree.mods | PrivateLocal + val rhs = + if (noFieldFor(tree, owner)) EmptyTree + else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam)) - def derivedTree = - if (Field.noFieldFor(tree)) EmptyTree - else if (isLazy) copyValDef(tree)(mods = derivedModsForTree, name = derivedName, rhs = EmptyTree).setPos(tree.pos.focus) - else copyValDef(tree)(mods = derivedModsForTree, name = derivedName) + newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = List(vparams), tpt = tpt) + } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 6b11dec967..0cd547c1eb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -129,6 +129,7 @@ trait Namers extends MethodSynthesis { !(vd.name startsWith nme.OUTER) && // outer accessors are added later, in explicitouter !isEnumConstant(vd) // enums can only occur in classes, so only check here + /** Determines whether this field holds an enum constant. * To qualify, the following conditions must be met: * - The field's class has the ENUM flag set @@ -685,6 +686,8 @@ trait Namers extends MethodSynthesis { if (name == nme.copy && sym.isSynthetic) enterCopyMethod(tree) + else if (name == nme.apply && sym.hasAllFlags(SYNTHETIC | CASE)) + sym setInfo caseApplyMethodCompleter(tree, completerOf(tree).asInstanceOf[LockingTypeCompleter]) else sym setInfo completerOf(tree) } @@ -803,86 +806,98 @@ trait Namers extends MethodSynthesis { import AnnotationInfo.{mkFilter => annotationFilter} - def valTypeCompleter(tree: ValDef) = mkTypeCompleter(tree) { sym => - val annots = - if (tree.mods.annotations.isEmpty) Nil - else annotSig(tree.mods.annotations) filter annotationFilter(FieldTargetClass, !tree.mods.isParamAccessor) + def implicitFactoryMethodCompleter(tree: DefDef, classSym: Symbol, sigCompleter: LockingTypeCompleter) = mkTypeCompleter(tree) { methSym => + sigCompleter.completeImpl(methSym) - sym setInfo typeSig(tree, annots) + val annotations = classSym.initialize.annotations - validate(sym) + methSym setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false)) + classSym setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true)) } - /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ - def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym => - // println(s"triaging for ${sym.debugFlagString} $sym from $valAnnots to $annots") - - // typeSig calls valDefSig (because tree: ValDef) - // sym is an accessor, while tree is the field (which may have the same symbol as the getter, or maybe it's the field) - // TODO: can we make this work? typeSig is called on same tree (valdef) to complete info for field and all its accessors - // reuse work done in valTypeCompleter if we already computed the type signature of the val - // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait) -// val valSig = -// if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info -// else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + def caseApplyMethodCompleter(tree: DefDef, sigCompleter: LockingTypeCompleter) = mkTypeCompleter(tree) { methSym => + sigCompleter.completeImpl(methSym) - val valSig = typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + // don't propagate e.g. @volatile annot to apply's argument + def retainOnlyParamAnnots(param: Symbol) = + param setAnnotations (param.annotations filter AnnotationInfo.mkFilter(ParamTargetClass, defaultRetention = false)) - val sig = accessorSigFromFieldTp(sym, isSetter, valSig) + methSym.info.paramss.foreach(_.foreach(retainOnlyParamAnnots)) + } + // complete the type of a value definition (may have a method symbol, for those valdefs that never receive a field, + // as specified by Field.noFieldFor) + def valTypeCompleter(tree: ValDef) = mkTypeCompleter(tree) { fieldOrGetterSym => val mods = tree.mods - if (mods.annotations.nonEmpty) { - val annotSigs = annotSig(mods.annotations) - - // neg/t3403: check that we didn't get a sneaky type alias/renamed import that we couldn't detect because we only look at names during synthesis - // (TODO: can we look at symbols earlier?) - if (!((mods hasAnnotationNamed tpnme.BeanPropertyAnnot) || (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot)) - && annotSigs.exists(ann => (ann.matches(BeanPropertyAttr)) || ann.matches(BooleanBeanPropertyAttr))) - BeanPropertyAnnotationLimitationError(tree) + val isGetter = fieldOrGetterSym.isMethod + val annots = + if (mods.annotations.isEmpty) Nil + else { + val annotSigs = annotSig(mods.annotations) + if (isGetter) filterAccessorAnnots(annotSigs, tree) // if this is really a getter, retain annots targeting either field/getter + else annotSigs filter annotationFilter(FieldTargetClass, !mods.isParamAccessor) + } - sym setAnnotations (annotSigs filter filterAccessorAnnotations(isSetter)) - } + // must use typeSig, not memberSig (TODO: when do we need to switch namers?) + val sig = typeSig(tree, annots) - sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym) + fieldOrGetterSym setInfo (if (isGetter) NullaryMethodType(sig) else sig) - validate(sym) + validate(fieldOrGetterSym) } - /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ - def beanAccessorTypeCompleter(tree: ValDef, missingTpt: Boolean, isSetter: Boolean) = mkTypeCompleter(tree) { sym => - context.unit.synthetics get sym match { + // knowing `isBean`, we could derive `isSetter` from `valDef.name` + def accessorTypeCompleter(valDef: ValDef, missingTpt: Boolean, isBean: Boolean, isSetter: Boolean) = mkTypeCompleter(valDef) { accessorSym => + context.unit.synthetics get accessorSym match { case Some(ddef: DefDef) => - // sym is an accessor, while tree is the field (for traits it's actually the getter, and we're completing the setter) + // `accessorSym` is the accessor for which we're completing the info (tree == ddef), + // while `valDef` is the field definition that spawned the accessor + // NOTE: `valTypeCompleter` handles abstract vals, trait vals and lazy vals, where the ValDef carries the getter's symbol + // reuse work done in valTypeCompleter if we already computed the type signature of the val // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait) val valSig = - if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info - else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + if ((accessorSym ne valDef.symbol) && valDef.symbol.isInitialized) valDef.symbol.info + else typeSig(valDef, Nil) // don't set annotations for the valdef -- we just want to compute the type sig (TODO: dig deeper and see if we can use memberSig) // patch up the accessor's tree if the valdef's tpt was not known back when the tree was synthesized - if (missingTpt) { // can't look at tree.tpt here because it may have been completed by now + // can't look at `valDef.tpt` here because it may have been completed by now (this is why we pass in `missingTpt`) + // HACK: a param accessor `ddef.tpt.tpe` somehow gets out of whack with `accessorSym.info`, so always patch it back... + // (the tpt is typed in the wrong namer, using the class as owner instead of the outer context, which is where param accessors should be typed) + if (missingTpt || accessorSym.isParamAccessor) { if (!isSetter) ddef.tpt setType valSig else if (ddef.vparamss.nonEmpty && ddef.vparamss.head.nonEmpty) ddef.vparamss.head.head.tpt setType valSig - else throw new TypeError(tree.pos, s"Internal error: could not complete parameter/return type for $ddef from $sym") + else throw new TypeError(valDef.pos, s"Internal error: could not complete parameter/return type for $ddef from $accessorSym") } + val mods = valDef.mods val annots = - if (tree.mods.annotations.isEmpty) Nil - else annotSig(tree.mods.annotations) filter filterBeanAccessorAnnotations(isSetter) + if (mods.annotations.isEmpty) Nil + else filterAccessorAnnots(annotSig(mods.annotations), valDef, isSetter, isBean) + + // for a setter, call memberSig to attribute the parameter (for a bean, we always use the regular method sig completer since they receive method types) + // for a regular getter, make sure it gets a NullaryMethodType (also, no need to recompute it: we already have the valSig) + val sig = + if (isSetter || isBean) typeSig(ddef, annots) + else { + if (annots.nonEmpty) annotate(accessorSym, annots) + + NullaryMethodType(valSig) + } - val sig = typeSig(ddef, annots) + accessorSym setInfo pluginsTypeSigAccessor(sig, typer, valDef, accessorSym) - sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym) + if (!isBean && accessorSym.isOverloaded) + if (isSetter) ddef.rhs.setType(ErrorType) + else GetterDefinedTwiceError(accessorSym) - validate(sym) + validate(accessorSym) case _ => - throw new TypeError(tree.pos, s"Internal error: no synthetic tree found for bean accessor $sym") + throw new TypeError(valDef.pos, s"Internal error: no synthetic tree found for bean accessor $accessorSym") } - } - // see scala.annotation.meta's package class for more info // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param. // The defaults are: @@ -893,24 +908,33 @@ trait Namers extends MethodSynthesis { // // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit? // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter) - private def filterAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean = - if (isSetter || !owner.isTrait) - annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false) - else (ann => - annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || - annotationFilter(GetterTargetClass, defaultRetention = true)(ann)) + private def filterAccessorAnnots(annotSigs: List[global.AnnotationInfo], tree: global.ValDef, isSetter: Boolean = false, isBean: Boolean = false): List[AnnotationInfo] = { + val mods = tree.mods + if (!isBean) { + // neg/t3403: check that we didn't get a sneaky type alias/renamed import that we couldn't detect because we only look at names during synthesis + // (TODO: can we look at symbols earlier?) + if (!((mods hasAnnotationNamed tpnme.BeanPropertyAnnot) || (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot)) + && annotSigs.exists(ann => (ann.matches(BeanPropertyAttr)) || ann.matches(BooleanBeanPropertyAttr))) + BeanPropertyAnnotationLimitationError(tree) + } + + def filterAccessorAnnotations: AnnotationInfo => Boolean = + if (isSetter || !owner.isTrait) + annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(GetterTargetClass, defaultRetention = true)(ann)) - private def filterBeanAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean = - if (isSetter || !owner.isTrait) - annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false) - else (ann => - annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || - annotationFilter(BeanGetterTargetClass, defaultRetention = true)(ann)) + def filterBeanAccessorAnnotations: AnnotationInfo => Boolean = + if (isSetter || !owner.isTrait) + annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(BeanGetterTargetClass, defaultRetention = true)(ann)) + annotSigs filter (if (isBean) filterBeanAccessorAnnotations else filterAccessorAnnotations) + } - private def accessorSigFromFieldTp(sym: Symbol, isSetter: Boolean, tp: Type): Type = - if (isSetter) MethodType(List(sym.newSyntheticValueParam(tp)), UnitTpe) - else NullaryMethodType(tp) def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => val selftpe = typer.typedType(tree).tpe @@ -959,7 +983,7 @@ trait Namers extends MethodSynthesis { ) dropIllegalStarTypes( if (shouldWiden) tpe.widen - else if (sym.isFinal) tpe // "final val" allowed to retain constant type + else if (sym.isFinal && !sym.isLazy) tpe // "final val" allowed to retain constant type else tpe.deconst ) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 8449260fe6..8034d056d7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -450,9 +450,9 @@ abstract class RefChecks extends Transform { } else if (other.isStable && !member.isStable) { // (1.4) overrideError("needs to be a stable, immutable value") } else if (member.isValue && member.isLazy && - other.isValue && !other.isSourceMethod && !other.isDeferred && !other.isLazy) { + other.isValue && other.hasFlag(STABLE) && !(other.isDeferred || other.isLazy)) { overrideError("cannot override a concrete non-lazy value") - } else if (other.isValue && other.isLazy && !other.isSourceMethod && !other.isDeferred && // !(other.hasFlag(MODULE) && other.hasFlag(PACKAGE | JAVA)) && other.hasFlag(LAZY) && (!other.isMethod || other.hasFlag(STABLE)) && !other.hasFlag(DEFERRED) + } else if (other.isValue && other.isLazy && member.isValue && !member.isLazy) { overrideError("must be declared lazy to override a concrete lazy value") } else if (other.isDeferred && member.isTermMacro && member.extendedOverriddenSymbols.forall(_.isDeferred)) { // (1.9) @@ -609,7 +609,7 @@ abstract class RefChecks extends Transform { val (missing, rest) = memberList partition (m => m.isDeferred && !ignoreDeferred(m)) // Group missing members by the name of the underlying symbol, // to consolidate getters and setters. - val grouped = missing groupBy (sym => analyzer.underlyingSymbol(sym).name) + val grouped = missing groupBy (_.name.getterName) val missingMethods = grouped.toList flatMap { case (name, syms) => if (syms exists (_.isSetter)) syms filterNot (_.isGetter) @@ -651,15 +651,16 @@ abstract class RefChecks extends Transform { // Give a specific error message for abstract vars based on why it fails: // It could be unimplemented, have only one accessor, or be uninitialized. - if (underlying.isVariable) { - val isMultiple = grouped.getOrElse(underlying.name, Nil).size > 1 + val groupedAccessors = grouped.getOrElse(member.name.getterName, Nil) + val isMultiple = groupedAccessors.size > 1 + if (groupedAccessors.exists(_.isSetter) || (member.isGetter && !isMultiple && member.setterIn(member.owner).exists)) { // If both getter and setter are missing, squelch the setter error. if (member.isSetter && isMultiple) () else undefined( if (member.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)" else if (member.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)" - else analyzer.abstractVarMessage(member) + else "\n(Note that variables need to be initialized to be defined)" ) } else if (underlying.isMethod) { @@ -919,17 +920,11 @@ abstract class RefChecks extends Transform { var index = -1 for (stat <- stats) { index = index + 1 - def enterSym(sym: Symbol) = if (sym.isLocalToBlock) { - currentLevel.scope.enter(sym) - symIndex(sym) = index - } stat match { - case DefDef(_, _, _, _, _, _) if stat.symbol.isLazy => - enterSym(stat.symbol) - case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => - //assert(stat.symbol != NoSymbol, stat);//debug - enterSym(stat.symbol.lazyAccessorOrSelf) + case _ : MemberDef if stat.symbol.isLocalToBlock => + currentLevel.scope.enter(stat.symbol) + symIndex(stat.symbol) = index case _ => } } @@ -1180,10 +1175,10 @@ abstract class RefChecks extends Transform { val tree1 = transform(tree) // important to do before forward reference check if (tree1.symbol.isLazy) tree1 :: Nil else { - val lazySym = tree.symbol.lazyAccessorOrSelf - if (lazySym.isLocalToBlock && index <= currentLevel.maxindex) { + val sym = tree.symbol + if (sym.isLocalToBlock && index <= currentLevel.maxindex) { debuglog("refsym = " + currentLevel.refsym) - reporter.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym) + reporter.error(currentLevel.refpos, "forward reference extends over definition of " + sym) } tree1 :: Nil } @@ -1451,9 +1446,9 @@ abstract class RefChecks extends Transform { ) } - sym.isSourceMethod && + sym.name == nme.apply && + !(sym hasFlag STABLE) && // ??? sym.isCase && - sym.name == nme.apply && isClassTypeAccessible(tree) && !tree.tpe.finalResultType.typeSymbol.primaryConstructor.isLessAccessibleThan(tree.symbol) } diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 49d892e04f..963a9dea02 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -146,7 +146,28 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val intermediateClasses = clazz.info.baseClasses.tail.takeWhile(_ != sym.owner) intermediateClasses.map(sym.overridingSymbol).find(s => s.isDeferred && !s.isAbstractOverride && !s.owner.isTrait).foreach { absSym => - reporter.error(sel.pos, s"${sym.fullLocationString} cannot be directly accessed from ${clazz} because ${absSym.owner} redeclares it as abstract") + reporter.error(sel.pos, s"${sym.fullLocationString} cannot be directly accessed from $clazz because ${absSym.owner} redeclares it as abstract") + } + } else if (mix != tpnme.EMPTY) { + // SD-143: a call super[T].m that resolves to A.m cannot be translated to correct bytecode if + // - A is a class (not a trait / interface), but not the direct superclass. Invokespecial + // would select an overriding method in the direct superclass, rather than A.m. + // We allow this if there are statically no intervening overrides. + // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial + // - A is a java-defined interface and not listed as direct parent of the class. In this + // case, `invokespecial A.m` would be invalid. + def hasClassOverride(member: Symbol, subclass: Symbol): Boolean = { + if (subclass == ObjectClass || subclass == member.owner) false + else if (member.overridingSymbol(subclass) != NoSymbol) true + else hasClassOverride(member, subclass.superClass) + } + val owner = sym.owner + if (!owner.isTrait && owner != clazz.superClass && hasClassOverride(sym, clazz.superClass)) { + reporter.error(sel.pos, + s"cannot emit super call: the selected $sym is declared in $owner, which is not the direct superclass of $clazz.\n" + + s"An unqualified super call (super.${sym.name}) would be allowed.") + } else if (owner.isInterface && owner.isJavaDefined && !clazz.parentSymbols.contains(owner)) { + reporter.error(sel.pos, s"unable to emit super call unless interface ${owner.name} (which declares $sym) is directly extended by $clazz.") } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index bee327c760..b66dbf21c0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -97,7 +97,7 @@ trait TypeDiagnostics { /** An explanatory note to be added to error messages * when there's a problem with abstract var defs */ def abstractVarMessage(sym: Symbol): String = - if (underlyingSymbol(sym).isVariable) + if (sym.isSetter || sym.isGetter && sym.setterIn(sym.owner).exists) "\n(Note that variables need to be initialized to be defined)" else "" @@ -140,7 +140,7 @@ trait TypeDiagnostics { * TODO: is it wise to create new symbols simply to generate error message? is this safe in interactive/resident mode? */ def underlyingSymbol(member: Symbol): Symbol = - if (!member.hasAccessorFlag || member.owner.isTrait) member + if (!member.hasAccessorFlag || member.accessed == NoSymbol) member else if (!member.isDeferred) member.accessed else { val getter = if (member.isSetter) member.getterIn(member.owner) else member diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a95ecd360c..d8183ea8df 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -128,6 +128,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def canTranslateEmptyListToNil = true def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree + // used to exempt synthetic accessors (i.e. those that are synthesized by the compiler to access a field) + // from skolemization because there's a weird bug that causes spurious type mismatches + // (it seems to have something to do with existential abstraction over values + // 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 assignment to allow for + // requiring both the ACCESSOR and the SYNTHETIC bits to trigger the exemption + private def isSyntheticAccessor(sym: Symbol) = sym.isAccessor && (!sym.isLazy || isPastTyper) + // when type checking during erasure, generate erased types in spots that aren't transformed by erasure // (it erases in TypeTrees, but not in, e.g., the type a Function node) def phasedAppliedType(sym: Symbol, args: List[Type]) = { @@ -1159,7 +1168,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper adapt(tree setType restpe, mode, pt, original) case TypeRef(_, ByNameParamClass, arg :: Nil) if mode.inExprMode => // (2) adapt(tree setType arg, mode, pt, original) - case tp if mode.typingExprNotLhs && isExistentialType(tp) => + case tp if mode.typingExprNotLhs && isExistentialType(tp) && !isSyntheticAccessor(context.owner) => adapt(tree setType tp.dealias.skolemizeExistential(context.owner, tree), mode, pt, original) case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode) && !context.inTypeConstructorAllowed => // (3) // assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function, @@ -1373,13 +1382,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.") else if (stat.symbol != null && stat.symbol.isParamAccessor) notAllowed("additional parameter") - // concrete accessor (getter) in trait corresponds to a field definition (neg/anytrait.scala) - // TODO: only reject accessors that actually give rise to field (e.g., a constant-type val is fine) - else if (!isValueClass && stat.symbol.isAccessor && !stat.symbol.isDeferred) - notAllowed("field definition") checkEphemeralDeep.traverse(rhs) - // for value class or "exotic" vals in traits - // (traits don't receive ValDefs for regular vals until fields phase -- well, except for early initialized/lazy vals) case _: ValDef => notAllowed("field definition") case _: ModuleDef => @@ -1956,11 +1959,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) - val body2 = { - val body2 = - if (isPastTyper || reporter.hasErrors) body1 - else body1 flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) - val primaryCtor = treeInfo.firstConstructor(body2) + val bodyWithPrimaryCtor = { + val primaryCtor = treeInfo.firstConstructor(body1) val primaryCtor1 = primaryCtor match { case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) => val argss = superArgs(parents1.head) getOrElse Nil @@ -1969,10 +1969,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos case _ => primaryCtor } - body2 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat } + body1 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat } } - val body3 = typedStats(body2, templ.symbol) + val body3 = typedStats(bodyWithPrimaryCtor, templ.symbol) if (clazz.info.firstParent.typeSymbol == AnyValClass) validateDerivedValueClass(clazz, body3) @@ -2040,7 +2040,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper override def matches(sym: Symbol, sym1: Symbol) = if (sym.isSkolem) matches(sym.deSkolemize, sym1) else if (sym1.isSkolem) matches(sym, sym1.deSkolemize) - else super[SubstTypeMap].matches(sym, sym1) + else super.matches(sym, sym1) } // allow defaults on by-name parameters if (sym hasFlag BYNAMEPARAM) @@ -2436,13 +2436,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ => } } - val stats1 = if (isPastTyper) block.stats else - block.stats.flatMap { - case vd@ValDef(_, _, _, _) if vd.symbol.isLazy => - namer.addDerivedTrees(Typer.this, vd) - case stat => stat::Nil - } - val stats2 = typedStats(stats1, context.owner, warnPure = false) + val statsTyped = typedStats(block.stats, context.owner, warnPure = false) val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode), pt) // sanity check block for unintended expr placement @@ -2456,18 +2450,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def checkPure(t: Tree, supple: Boolean): Unit = if (treeInfo.isPureExprForWarningPurposes(t)) { val msg = "a pure expression does nothing in statement position" - val parens = if (stats2.length + count > 1) "multiline expressions might require enclosing parentheses" else "" + val parens = if (statsTyped.length + count > 1) "multiline expressions might require enclosing parentheses" else "" val discard = if (adapted) "; a value can be silently discarded when Unit is expected" else "" val text = if (supple) s"${parens}${discard}" else if (!parens.isEmpty) s"${msg}; ${parens}" else msg context.warning(t.pos, text) } - stats2.foreach(checkPure(_, supple = false)) + statsTyped.foreach(checkPure(_, supple = false)) if (result0.nonEmpty) checkPure(result0, supple = true) } - treeCopy.Block(block, stats2, expr1) + treeCopy.Block(block, statsTyped, expr1) .setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst) } finally { // enable escaping privates checking from the outside and recycle @@ -3171,6 +3165,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case (ClassDef(cmods, cname, _, _), DefDef(dmods, dname, _, _, _, _)) => cmods.isImplicit && dmods.isImplicit && cname.toTermName == dname + // ValDef and Accessor + case (ValDef(_, cname, _, _), DefDef(_, dname, _, _, _, _)) => + cname.getterName == dname.getterName + case _ => false } @@ -4455,8 +4453,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def narrowRhs(tp: Type) = { val sym = context.tree.symbol context.tree match { case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !mods.isMutable && sym != null && sym != NoSymbol => - val sym1 = if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner) - else sym.lazyAccessorOrSelf + val sym1 = + if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner) + else sym val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix intersectionType(List(tp, singleType(pre, sym1))) case _ => tp diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 22fb0728e6..f2e9b260b0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -128,6 +128,7 @@ trait Unapplies extends ast.TreeDSL { */ def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef): DefDef = { val tparams = constrTparamsInvariant(cdef) + val cparamss = constrParamss(cdef) def classtpe = classType(cdef, tparams) atPos(cdef.pos.focus)( diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 64535a749f..715ba0d4f3 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -72,8 +72,6 @@ trait InteractiveAnalyzer extends Analyzer { override def enterExistingSym(sym: Symbol, tree: Tree): Context = { if (sym != null && sym.owner.isTerm) { enterIfNotThere(sym) - if (sym.isLazy) - sym.lazyAccessor andAlso enterIfNotThere for (defAtt <- sym.attachments.get[DefaultsOfLocalMethodAttachment]) defAtt.defaultGetters foreach enterIfNotThere diff --git a/src/library/scala/runtime/LazyRef.scala b/src/library/scala/runtime/LazyRef.scala new file mode 100644 index 0000000000..5a0bd5442c --- /dev/null +++ b/src/library/scala/runtime/LazyRef.scala @@ -0,0 +1,157 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2016, LAMP/EPFL and Lightbend, Inc ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.runtime + +/** Classes used as holders for lazy vals defined in methods. */ + +class LazyRef[T] { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: T = _ + def value: T = _value + def initialize(value: T): T = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyRef ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyBoolean { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Boolean = _ + def value: Boolean = _value + def initialize(value: Boolean): Boolean = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyBoolean ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyByte { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Byte = _ + + def value: Byte = _value + + def initialize(value: Byte): Byte = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyByte ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyChar { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Char = _ + def value: Char = _value + def initialize(value: Char): Char = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyChar ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyShort { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Short = _ + def value: Short = _value + def initialize(value: Short): Short = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyShort ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyInt { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Int = _ + def value: Int = _value + def initialize(value: Int): Int = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyInt ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyLong { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Long = _ + def value: Long = _value + def initialize(value: Long): Long = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyLong ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyFloat { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Float = _ + def value: Float = _value + def initialize(value: Float): Float = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyFloat ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyDouble { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + private[this] var _value: Double = _ + def value: Double = _value + def initialize(value: Double): Double = { + _value = value + _initialized = true + value + } + + override def toString = s"LazyDouble ${if (_initialized) s"of: ${_value}" else "thunk"}" +} + +class LazyUnit { + @volatile private[this] var _initialized: Boolean = _ + def initialized = _initialized + + def initialize(): Unit = _initialized = true + + override def toString = s"LazyUnit${if (_initialized) "" else " thunk"}" +} diff --git a/src/manual/scala/man1/scalac.scala b/src/manual/scala/man1/scalac.scala index 6ffcccea25..79c175e0f0 100644 --- a/src/manual/scala/man1/scalac.scala +++ b/src/manual/scala/man1/scalac.scala @@ -379,8 +379,8 @@ object scalac extends Command { MItalic("posterasure"), "clean up erased inline classes"), Definition( - MItalic("lazyvals"), - "allocate bitmaps, translate lazy vals into lazified defs"), + MItalic("fields"), + "synthesize accessors and fields, including bitmaps for lazy vals"), Definition( MItalic("lambdalift"), "move nested functions to top level"), diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index d0539dfd42..0f7cf07f08 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -81,7 +81,7 @@ trait Definitions extends api.StandardDefinitions { } } - private[Definitions] def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f) + private[Definitions] def classesMap[T](f: Name => T): Map[Symbol, T] = symbolsMap(ScalaValueClassesNoUnit, f) private def symbolsMap[T](syms: List[Symbol], f: Name => T): Map[Symbol, T] = mapFrom(syms)(x => f(x.name)) private def symbolsMapFilt[T](syms: List[Symbol], p: Name => Boolean, f: Name => T) = symbolsMap(syms filter (x => p(x.name)), f) @@ -93,6 +93,9 @@ trait Definitions extends api.StandardDefinitions { lazy val boxedClass = classesMap(x => getClassByName(boxedName(x))) lazy val refClass = classesMap(x => getRequiredClass("scala.runtime." + x + "Ref")) lazy val volatileRefClass = classesMap(x => getRequiredClass("scala.runtime.Volatile" + x + "Ref")) + lazy val lazyHolders = symbolsMap(ScalaValueClasses, x => getClassIfDefined("scala.runtime.Lazy" + x)) + lazy val LazyRefClass = getClassIfDefined("scala.runtime.LazyRef") + lazy val LazyUnitClass = getClassIfDefined("scala.runtime.LazyUnit") lazy val allRefClasses: Set[Symbol] = { refClass.values.toSet ++ volatileRefClass.values.toSet ++ Set(VolatileObjectRefClass, ObjectRefClass) @@ -1151,7 +1154,6 @@ trait Definitions extends api.StandardDefinitions { lazy val ElidableMethodClass = requiredClass[scala.annotation.elidable] lazy val ImplicitNotFoundClass = requiredClass[scala.annotation.implicitNotFound] lazy val ImplicitAmbiguousClass = getClassIfDefined("scala.annotation.implicitAmbiguous") - lazy val JUnitTestClass = getClassIfDefined("org.junit.Test") lazy val MigrationAnnotationClass = requiredClass[scala.annotation.migration] lazy val ScalaStrictFPAttr = requiredClass[scala.annotation.strictfp] lazy val SwitchClass = requiredClass[scala.annotation.switch] @@ -1193,6 +1195,8 @@ trait Definitions extends api.StandardDefinitions { lazy val MethodTargetClass = requiredClass[meta.companionMethod] // TODO: module, moduleClass? package, packageObject? lazy val LanguageFeatureAnnot = requiredClass[meta.languageFeature] + lazy val JUnitAnnotations = List("Test", "Ignore", "Before", "After", "BeforeClass", "AfterClass").map(n => getClassIfDefined("org.junit." + n)) + // Language features lazy val languageFeatureModule = getRequiredModule("scala.languageFeature") diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 925018d3a6..1a6c84b19e 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -704,6 +704,8 @@ trait StdNames { val immutable: NameType = "immutable" val implicitly: NameType = "implicitly" val in: NameType = "in" + val initialize : NameType = "initialize" + val initialized : NameType = "initialized" val internal: NameType = "internal" val inlinedEquals: NameType = "inlinedEquals" val isArray: NameType = "isArray" diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 487aadf5e5..ac025e50ae 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -655,7 +655,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => isClass && isFinal && loop(typeParams) } - final def isLazyAccessor = isLazy && lazyAccessor != NoSymbol final def isOverridableMember = !(isClass || isEffectivelyFinal) && safeOwner.isClass /** Does this symbol denote a wrapper created by the repl? */ @@ -1683,7 +1682,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * * - packageobjects (follows namer) * - superaccessors (follows typer) - * - lazyvals (follows erasure) + * - lambdaLift (follows erasure) * - null */ private def unsafeTypeParamPhase = { @@ -2075,11 +2074,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def alias: Symbol = NoSymbol - /** For a lazy value, its lazy accessor. NoSymbol for all others. */ + @deprecated("No longer applicable, as lazy vals are not desugared until the fields phase", "2.12.0") // used by scala-refactoring def lazyAccessor: Symbol = NoSymbol - /** If this is a lazy value, the lazy accessor; otherwise this symbol. */ - def lazyAccessorOrSelf: Symbol = if (isLazy) lazyAccessor else this + @deprecated("No longer applicable, as lazy vals are not desugared until the fields phase", "2.12.0") + def lazyAccessorOrSelf: Symbol = NoSymbol /** `accessed`, if this is an accessor that should have an underlying field. Otherwise, `this`. * Note that a "regular" accessor in a trait does not have a field, as an interface cannot define a field. @@ -2088,7 +2087,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * as they are an implementation detail that's irrelevant to type checking. */ def accessedOrSelf: Symbol = - if (hasAccessorFlag && (!owner.isTrait || hasFlag(PRESUPER | LAZY))) accessed + if (hasAccessorFlag && (!owner.isTrait || hasFlag(PRESUPER))) accessed else this /** For an outer accessor: The class from which the outer originates. @@ -2834,17 +2833,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => this } - def setLazyAccessor(sym: Symbol): TermSymbol = { - assert(isLazy && (referenced == NoSymbol || referenced == sym), (this, debugFlagString, referenced, sym)) - referenced = sym - this - } - - override def lazyAccessor: Symbol = { - assert(isLazy, this) - referenced - } - /** change name by appending $$<fully-qualified-name-of-class `base`> * Do the same for any accessed symbols or setters/getters */ diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index b9f3e987ee..61937958dd 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -293,6 +293,26 @@ abstract class TreeInfo { } } + + // No field for these vals, which means the ValDef carries the symbol of the getter (and not the field symbol) + // - abstract vals have no value we could store (until they become concrete, potentially) + // - lazy vals: the ValDef carries the symbol of the lazy accessor. + // The sausage factory will spew out the inner workings during the fields phase (actual bitmaps won't follow + // until lazyvals & mixins, though we should move this stuff from mixins to lazyvals now that fields takes care of mixing in lazy vals) + // - concrete vals in traits don't yield a field here either (their getter's RHS has the initial value) + // Constructors will move the assignment to the constructor, abstracting over the field using the field setter, + // and Fields will add a field to the class that mixes in the trait, implementing the accessors in terms of it + // + // The following case does receive a field symbol (until it's eliminated during the fields phase): + // - a concrete val with a statically known value (ConstantType) + // performs its side effect according to lazy/strict semantics, but doesn't need to store its value + // each access will "evaluate" the RHS (a literal) again + // + // We would like to avoid emitting unnecessary fields, but the required knowledge isn't available until after typer. + // The only way to avoid emitting & suppressing, is to not emit at all until we are sure to need the field, as dotty does. + def noFieldFor(vd: ValDef, owner: Symbol) = vd.mods.isDeferred || vd.mods.isLazy || (owner.isTrait && !vd.mods.hasFlag(PRESUPER)) + + def isDefaultGetter(tree: Tree) = { tree.symbol != null && tree.symbol.isDefaultGetter } diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 4bc804445c..c6cb0d0223 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -227,9 +227,7 @@ abstract class UnPickler { return NoSymbol if (tag == EXTMODCLASSref) { - val moduleVar = owner.info.decl(nme.moduleVarName(name.toTermName)) - if (moduleVar.isLazyAccessor) - return moduleVar.lazyAccessor.lazyAccessor + owner.info.decl(nme.moduleVarName(name.toTermName)) } NoSymbol } diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 62ca50d035..e2f1e74740 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -147,21 +147,20 @@ trait Erasure { case AnnotatedType(_, atp) => apply(atp) case ClassInfoType(parents, decls, clazz) => - ClassInfoType( - if (clazz == ObjectClass || isPrimitiveValueClass(clazz) || parents.isEmpty) Nil + val newParents = + if (parents.isEmpty || clazz == ObjectClass || isPrimitiveValueClass(clazz)) Nil else if (clazz == ArrayClass) ObjectTpe :: Nil else { - val erasedParents = parents map this + val erasedParents = parents mapConserve this // drop first parent for traits -- it has been normalized to a class by now, // but we should drop that in bytecode - val firstParent = - if (clazz.hasFlag(Flags.TRAIT) && !clazz.hasFlag(Flags.JAVA)) ObjectTpe - else erasedParents.head - - firstParent :: erasedParents.tail.filter(_.typeSymbol != ObjectClass) - }, - decls, clazz) + if (clazz.hasFlag(Flags.TRAIT) && !clazz.hasFlag(Flags.JAVA)) + ObjectTpe :: erasedParents.tail.filter(_.typeSymbol != ObjectClass) + else erasedParents + } + if (newParents eq parents) tp + else ClassInfoType(newParents, decls, clazz) case _ => mapOver(tp) } @@ -343,23 +342,30 @@ trait Erasure { } } - /** The symbol's erased info. This is the type's erasure, except for the following symbols: - * - * - For $asInstanceOf : [T]T - * - For $isInstanceOf : [T]scala#Boolean - * - For class Array : [T]C where C is the erased classinfo of the Array class. - * - For Array[T].<init> : {scala#Int)Array[T] - * - For a type parameter : A type bounds type consisting of the erasures of its bounds. - */ + /** The symbol's erased info. This is the type's erasure, except for the following primitive symbols: + * + * - $asInstanceOf --> [T]T + * - $isInstanceOf --> [T]scala#Boolean + * - synchronized --> [T](x: T)T + * - class Array --> [T]C where C is the erased classinfo of the Array class. + * - Array[T].<init> --> {scala#Int)Array[T] + * + * An abstract type's info erases to a TypeBounds type consisting of the erasures of the abstract type's bounds. + */ def transformInfo(sym: Symbol, tp: Type): Type = { - if (sym == Object_asInstanceOf) + // Do not erase the primitive `synchronized` method's info or the info of its parameter. + // We do erase the info of its type param so that subtyping can relate its bounds after erasure. + def synchronizedPrimitive(sym: Symbol) = + sym == Object_synchronized || (sym.owner == Object_synchronized && sym.isTerm) + + if (sym == Object_asInstanceOf || synchronizedPrimitive(sym)) sym.info else if (sym == Object_isInstanceOf || sym == ArrayClass) PolyType(sym.info.typeParams, specialErasure(sym)(sym.info.resultType)) else if (sym.isAbstractType) - TypeBounds(WildcardType, WildcardType) + TypeBounds(WildcardType, WildcardType) // TODO why not use the erasure of the type's bounds, as stated in the doc? else if (sym.isTerm && sym.owner == ArrayClass) { - if (sym.isClassConstructor) + if (sym.isClassConstructor) // TODO: switch on name for all branches -- this one is sym.name == nme.CONSTRUCTOR tp match { case MethodType(params, TypeRef(pre, sym1, args)) => MethodType(cloneSymbolsAndModify(params, specialErasure(sym)), @@ -376,12 +382,14 @@ trait Erasure { } else if ( sym.owner != NoSymbol && sym.owner.owner == ArrayClass && - sym == Array_update.paramss.head(1)) { + sym == Array_update.paramss.head(1)) { // TODO: can we simplify the guard, perhaps cache the symbol to compare to? // special case for Array.update: the non-erased type remains, i.e. (Int,A)Unit // since the erasure type map gets applied to every symbol, we have to catch the // symbol here tp } else { + // TODO OPT: altogether, there are 9 symbols that we special-case. + // Could we get to the common case more quickly by looking them up in a set? specialErasure(sym)(tp) } } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index f55b33959a..b74ccb9177 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -379,7 +379,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.ElidableMethodClass definitions.ImplicitNotFoundClass definitions.ImplicitAmbiguousClass - definitions.JUnitTestClass definitions.MigrationAnnotationClass definitions.ScalaStrictFPAttr definitions.SwitchClass @@ -417,6 +416,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.ClassTargetClass definitions.MethodTargetClass definitions.LanguageFeatureAnnot + definitions.JUnitAnnotations definitions.languageFeatureModule definitions.metaAnnotations definitions.AnnotationDefaultAttr @@ -432,6 +432,9 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.boxedClass definitions.refClass definitions.volatileRefClass + definitions.lazyHolders + definitions.LazyRefClass + definitions.LazyUnitClass definitions.allRefClasses definitions.UnitClass definitions.ByteClass diff --git a/src/repl/scala/tools/nsc/interpreter/Phased.scala b/src/repl/scala/tools/nsc/interpreter/Phased.scala index d1d422ce3e..dd327a13d4 100644 --- a/src/repl/scala/tools/nsc/interpreter/Phased.scala +++ b/src/repl/scala/tools/nsc/interpreter/Phased.scala @@ -88,7 +88,7 @@ trait Phased { lazy val all = List( Parser, Namer, Packageobjects, Typer, Superaccessors, Pickler, Refchecks, - Uncurry, Tailcalls, Specialize, Explicitouter, Erasure, Lazyvals, Lambdalift, + Uncurry, Tailcalls, Specialize, Explicitouter, Erasure, Fields, Lambdalift, Constructors, Flatten, Mixin, Cleanup, Delambdafy, Jvm, Terminal ) lazy val nameMap = all.map(x => x.name -> x).toMap withDefaultValue NoPhaseName @@ -114,12 +114,12 @@ trait Phased { case object Pickler extends PhaseName case object Refchecks extends PhaseName case object Uncurry extends PhaseName + case object Fields extends PhaseName case object Tailcalls extends PhaseName case object Specialize extends PhaseName case object Explicitouter extends PhaseName case object Erasure extends PhaseName case object PostErasure extends PhaseName - case object Lazyvals extends PhaseName case object Lambdalift extends PhaseName case object Constructors extends PhaseName case object Flatten extends PhaseName diff --git a/src/scaladoc/scala/tools/nsc/doc/Settings.scala b/src/scaladoc/scala/tools/nsc/doc/Settings.scala index 063a949323..fbb2dd9f87 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Settings.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Settings.scala @@ -22,7 +22,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) val docformat = ChoiceSetting ( "-doc-format", "format", - "Selects in which format documentation is rendered", + "Selects in which format documentation is rendered.", List("html"), "html" ) diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index 928cb34d30..fb9a5ce7eb 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -106,10 +106,12 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // in the doc comment of MyClass def linkTarget: DocTemplateImpl = inTpl - lazy val comment = { - val documented = if (sym.hasAccessorFlag) sym.accessed else sym - thisFactory.comment(documented, linkTarget, inTpl) - } + // if there is a field symbol, the ValDef will use it, which means docs attached to it will be under the field symbol, not the getter's + protected[this] def commentCarryingSymbol(sym: Symbol) = + if (sym.hasAccessorFlag && sym.accessed.exists) sym.accessed else sym + + lazy val comment = thisFactory.comment(commentCarryingSymbol(sym), linkTarget, inTpl) + def group = comment flatMap (_.group) getOrElse defaultGroup override def inTemplate = inTpl override def toRoot: List[MemberImpl] = this :: inTpl.toRoot @@ -476,17 +478,18 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { override lazy val comment = { def nonRootTemplate(sym: Symbol): Option[DocTemplateImpl] = if (sym eq RootPackage) None else findTemplateMaybe(sym) + /* Variable precedence order for implicitly added members: Take the variable definitions from ... * 1. the target of the implicit conversion * 2. the definition template (owner) * 3. the current template */ - val inRealTpl = conversion.flatMap { conv => - nonRootTemplate(conv.toType.typeSymbol) - } orElse nonRootTemplate(sym.owner) orElse Option(inTpl) - inRealTpl flatMap { tpl => - thisFactory.comment(sym, tpl, tpl) - } + val inRealTpl = ( + conversion.flatMap(conv => nonRootTemplate(conv.toType.typeSymbol)) + orElse nonRootTemplate(sym.owner) + orElse Option(inTpl)) + + inRealTpl flatMap (tpl => thisFactory.comment(commentCarryingSymbol(sym), tpl, tpl)) } override def inDefinitionTemplates = useCaseOf.fold(super.inDefinitionTemplates)(_.inDefinitionTemplates) diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala index 5c666a615f..288c6ee30f 100644 --- a/test/files/jvm/innerClassAttribute/Test.scala +++ b/test/files/jvm/innerClassAttribute/Test.scala @@ -298,7 +298,7 @@ object Test extends BytecodeTest { assertEnclosingMethod ("SI_9105$B$5" , "SI_9105", "m$1", "()Ljava/lang/Object;") assertEnclosingMethod ("SI_9105$C$1" , "SI_9105", null , null) assertEnclosingMethod ("SI_9105$D$1" , "SI_9105", "met", "()Lscala/Function1;") - assertEnclosingMethod ("SI_9105$E$1" , "SI_9105", "m$3", "()Ljava/lang/Object;") + assertEnclosingMethod ("SI_9105$E$1" , "SI_9105", "m$2", "()Ljava/lang/Object;") assertEnclosingMethod ("SI_9105$F$1" , "SI_9105", "met", "()Lscala/Function1;") assertNoEnclosingMethod("SI_9105") @@ -311,7 +311,7 @@ object Test extends BytecodeTest { // by-name assertEnclosingMethod("SI_9105$G$1", "SI_9105", null , null) - assertEnclosingMethod("SI_9105$H$1", "SI_9105", "m$2", "()Ljava/lang/Object;") + assertEnclosingMethod("SI_9105$H$1", "SI_9105", "m$3", "()Ljava/lang/Object;") assertEnclosingMethod("SI_9105$I$1", "SI_9105", null , null) assertEnclosingMethod("SI_9105$J$1", "SI_9105", "bnM", "()I") assertEnclosingMethod("SI_9105$K$2", "SI_9105", "m$4", "()Ljava/lang/Object;") @@ -323,11 +323,11 @@ object Test extends BytecodeTest { def testSI_9124() { val classes: Map[String, String] = { List("SI_9124$$anon$10", - "SI_9124$$anon$11", "SI_9124$$anon$12", + "SI_9124$$anon$13", "SI_9124$$anon$8", "SI_9124$$anon$9", - "SI_9124$O$$anon$13").map({ name => + "SI_9124$O$$anon$11").map({ name => val node = loadClassNode(name) val fMethod = node.methods.asScala.find(_.name.startsWith("f")).get.name (fMethod, node.name) @@ -380,8 +380,8 @@ object Test extends BytecodeTest { val b3 = assertLocal(_ : InnerClassNode, "ImplClassesAreTopLevel$B3$1", "B3$1", flags = publicAbstractInterface) val b4 = assertLocal(_ : InnerClassNode, "ImplClassesAreTopLevel$B4$1", "B4$1", flags = publicAbstractInterface) - testInner("ImplClassesAreTopLevel$$anon$14", an14, b3) - testInner("ImplClassesAreTopLevel$$anon$15", an15, b2) + testInner("ImplClassesAreTopLevel$$anon$14", an14, b2) + testInner("ImplClassesAreTopLevel$$anon$15", an15, b3) testInner("ImplClassesAreTopLevel$$anon$16", an16, b4) testInner("ImplClassesAreTopLevel$B1", b1) diff --git a/test/files/jvm/javaReflection.check b/test/files/jvm/javaReflection.check index 9e9fe36d14..f3924940e9 100644 --- a/test/files/jvm/javaReflection.check +++ b/test/files/jvm/javaReflection.check @@ -2,13 +2,13 @@ A / A (canon) / A (simple) - declared cls: List(class A$B, interface A$C, class A$D$) - enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) - properties : false (local) / false (member) -A$$anon$1 / null (canon) / $anon$1 (simple) +A$$anon$2 / null (canon) / $anon$2 (simple) - declared cls: List() - enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) - properties : true (local) / false (member) A$$anon$3 / null (canon) / $anon$3 (simple) - declared cls: List() -- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) - properties : true (local) / false (member) A$$anon$4 / null (canon) / $anon$4 (simple) - declared cls: List() @@ -16,7 +16,7 @@ A$$anon$4 / null (canon) / $anon$4 (simple) - properties : true (local) / false (member) A$$anon$5 / null (canon) / $anon$5 (simple) - declared cls: List() -- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) - properties : true (local) / false (member) A$$anon$6 / null (canon) / $anon$6 (simple) - declared cls: List() @@ -38,7 +38,7 @@ A$D$ / A.D$ (canon) / D$ (simple) - declared cls: List(class A$D$B, interface A$D$C, class A$D$D$) - enclosing : class A (declaring cls) / class A (cls) / null (constr) / null (meth) - properties : false (local) / true (member) -A$D$$anon$2 / null (canon) / anon$2 (simple) +A$D$$anon$1 / null (canon) / anon$1 (simple) - declared cls: List() - enclosing : null (declaring cls) / class A$D$ (cls) / null (constr) / null (meth) - properties : true (local) / false (member) diff --git a/test/files/neg/anytrait.check b/test/files/neg/anytrait.check index fabe74d379..6d9d681d60 100644 --- a/test/files/neg/anytrait.check +++ b/test/files/neg/anytrait.check @@ -4,4 +4,7 @@ anytrait.scala:3: error: field definition is not allowed in universal trait exte anytrait.scala:5: error: this statement is not allowed in universal trait extending from class Any { x += 1 } ^ -two errors found +anytrait.scala:9: error: field definition is not allowed in universal trait extending from class Any + val y: T + ^ +three errors found diff --git a/test/files/neg/choices.check b/test/files/neg/choices.check index df4f23461f..2449cadcd6 100644 --- a/test/files/neg/choices.check +++ b/test/files/neg/choices.check @@ -1,5 +1,4 @@ -error: Usage: -Yresolve-term-conflict:<strategy> - where <strategy> choices are package, object, error (default: error) +error: Usage: -Yresolve-term-conflict:<strategy> where <strategy> choices are package, object, error (default: error). error: bad option: '-Yresolve-term-conflict' error: bad options: -Yresolve-term-conflict error: flags file may only contain compiler options, found: -Yresolve-term-conflict diff --git a/test/files/neg/names-defaults-neg.check b/test/files/neg/names-defaults-neg.check index 0a7b1a7157..af164d90ea 100644 --- a/test/files/neg/names-defaults-neg.check +++ b/test/files/neg/names-defaults-neg.check @@ -1,7 +1,3 @@ -names-defaults-neg.scala:65: error: not enough arguments for method apply: (a: Int, b: String)(c: Int*)Fact in object Fact. -Unspecified value parameter b. - val fac = Fact(1)(2, 3) - ^ names-defaults-neg.scala:5: error: type mismatch; found : String("#") required: Int @@ -81,6 +77,10 @@ and method f in object t8 of type (a: Int, b: Object)String match argument types (a: Int,b: String) and expected result type Any println(t8.f(a = 0, b = "1")) // ambiguous reference ^ +names-defaults-neg.scala:65: error: not enough arguments for method apply: (a: Int, b: String)(c: Int*)Fact in object Fact. +Unspecified value parameter b. + val fac = Fact(1)(2, 3) + ^ names-defaults-neg.scala:69: error: wrong number of arguments for pattern A1(x: Int,y: String) A1() match { case A1(_) => () } ^ diff --git a/test/files/neg/nowarnDefaultJunitMethods.check b/test/files/neg/nowarnDefaultJunitMethods.check deleted file mode 100644 index 7efdcc299a..0000000000 --- a/test/files/neg/nowarnDefaultJunitMethods.check +++ /dev/null @@ -1,6 +0,0 @@ -C_1.scala:2: warning: JUnit tests in traits that are compiled as default methods are not executed by JUnit 4. JUnit 5 will fix this issue. - @org.junit.Test def foo = 0 - ^ -error: No warnings can be incurred under -Xfatal-warnings. -one warning found -one error found diff --git a/test/files/neg/nowarnDefaultJunitMethods.flags b/test/files/neg/nowarnDefaultJunitMethods.flags deleted file mode 100644 index 85d8eb2ba2..0000000000 --- a/test/files/neg/nowarnDefaultJunitMethods.flags +++ /dev/null @@ -1 +0,0 @@ --Xfatal-warnings diff --git a/test/files/neg/nowarnDefaultJunitMethods/C_1.scala b/test/files/neg/nowarnDefaultJunitMethods/C_1.scala deleted file mode 100644 index e2565a48bc..0000000000 --- a/test/files/neg/nowarnDefaultJunitMethods/C_1.scala +++ /dev/null @@ -1,5 +0,0 @@ -trait T { - @org.junit.Test def foo = 0 -} - -class C extends T diff --git a/test/files/neg/nowarnDefaultJunitMethods/Test.java b/test/files/neg/nowarnDefaultJunitMethods/Test.java deleted file mode 100644 index e8d64c2cc8..0000000000 --- a/test/files/neg/nowarnDefaultJunitMethods/Test.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.junit; - -public @interface Test { } diff --git a/test/files/neg/protected-constructors.check b/test/files/neg/protected-constructors.check index 0279f5815d..4f076ec993 100644 --- a/test/files/neg/protected-constructors.check +++ b/test/files/neg/protected-constructors.check @@ -1,3 +1,12 @@ +protected-constructors.scala:15: error: class Foo3 in object Ding cannot be accessed in object dingus.Ding + Access to protected class Foo3 not permitted because + enclosing object P in package hungus is not a subclass of + object Ding in package dingus where target is defined + class Bar3 extends Ding.Foo3("abc") + ^ +protected-constructors.scala:15: error: no arguments allowed for nullary constructor Object: ()Object + class Bar3 extends Ding.Foo3("abc") + ^ protected-constructors.scala:17: error: no arguments allowed for nullary constructor Foo1: ()dingus.Foo1 val foo1 = new Foo1("abc") ^ @@ -13,13 +22,4 @@ protected-constructors.scala:19: error: class Foo3 in object Ding cannot be acce object Ding in package dingus where target is defined val foo3 = new Ding.Foo3("abc") ^ -protected-constructors.scala:15: error: class Foo3 in object Ding cannot be accessed in object dingus.Ding - Access to protected class Foo3 not permitted because - enclosing object P in package hungus is not a subclass of - object Ding in package dingus where target is defined - class Bar3 extends Ding.Foo3("abc") - ^ -protected-constructors.scala:15: error: no arguments allowed for nullary constructor Object: ()Object - class Bar3 extends Ding.Foo3("abc") - ^ 5 errors found diff --git a/test/files/neg/sabin2.check b/test/files/neg/sabin2.check index 8a09361069..aa0e8f734c 100644 --- a/test/files/neg/sabin2.check +++ b/test/files/neg/sabin2.check @@ -1,6 +1,6 @@ sabin2.scala:22: error: type mismatch; found : Test.Base#T - required: _7.T where val _7: Test.Base + required: _5.T where val _5: Test.Base a.set(b.get()) // Error ^ one error found diff --git a/test/files/neg/t1838.check b/test/files/neg/t1838.check index a476158c7b..af811a3810 100644 --- a/test/files/neg/t1838.check +++ b/test/files/neg/t1838.check @@ -1,7 +1,7 @@ -t1838.scala:6: error: `sealed' modifier can be used only for classes - sealed val v = 0 - ^ t1838.scala:5: error: `sealed' modifier can be used only for classes sealed def f = 0 ^ +t1838.scala:6: error: `sealed' modifier can be used only for classes + sealed val v = 0 + ^ two errors found diff --git a/test/files/neg/t4158.check b/test/files/neg/t4158.check index af281c52cd..7bac6558f7 100644 --- a/test/files/neg/t4158.check +++ b/test/files/neg/t4158.check @@ -1,7 +1,7 @@ -t4158.scala:3: error: an expression of type Null is ineligible for implicit conversion - var y = null: Int - ^ t4158.scala:2: error: an expression of type Null is ineligible for implicit conversion var x: Int = null ^ +t4158.scala:3: error: an expression of type Null is ineligible for implicit conversion + var y = null: Int + ^ two errors found diff --git a/test/files/neg/t6446-additional.check b/test/files/neg/t6446-additional.check index 45db63317c..23df978cd9 100644 --- a/test/files/neg/t6446-additional.check +++ b/test/files/neg/t6446-additional.check @@ -10,19 +10,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - fields 11 synthesize accessors and fields + fields 11 synthesize accessors and fields, including bitmaps for la... tailcalls 12 replace tail calls by jumps specialize 13 @specialized-driven class and method specialization explicitouter 14 this refs to outer pointers erasure 15 erase types, add interfaces for traits posterasure 16 clean up erased inline classes - lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 18 move nested functions to top level - constructors 19 move field definitions into constructors - flatten 20 eliminate inner classes - mixin 21 mixin composition - cleanup 22 platform-specific cleanups, generate reflective calls - delambdafy 23 remove lambdas - jvm 24 generate JVM bytecode - ploogin 25 A sample phase that does so many things it's kind of hard... - terminal 26 the last phase during a compilation run + lambdalift 17 move nested functions to top level + constructors 18 move field definitions into constructors + flatten 19 eliminate inner classes + mixin 20 mixin composition + cleanup 21 platform-specific cleanups, generate reflective calls + delambdafy 22 remove lambdas + jvm 23 generate JVM bytecode + ploogin 24 A sample phase that does so many things it's kind of hard... + terminal 25 the last phase during a compilation run diff --git a/test/files/neg/t6446-missing.check b/test/files/neg/t6446-missing.check index 04523d18e6..c0a8fea140 100644 --- a/test/files/neg/t6446-missing.check +++ b/test/files/neg/t6446-missing.check @@ -11,18 +11,17 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - fields 11 synthesize accessors and fields + fields 11 synthesize accessors and fields, including bitmaps for la... tailcalls 12 replace tail calls by jumps specialize 13 @specialized-driven class and method specialization explicitouter 14 this refs to outer pointers erasure 15 erase types, add interfaces for traits posterasure 16 clean up erased inline classes - lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 18 move nested functions to top level - constructors 19 move field definitions into constructors - flatten 20 eliminate inner classes - mixin 21 mixin composition - cleanup 22 platform-specific cleanups, generate reflective calls - delambdafy 23 remove lambdas - jvm 24 generate JVM bytecode - terminal 25 the last phase during a compilation run + lambdalift 17 move nested functions to top level + constructors 18 move field definitions into constructors + flatten 19 eliminate inner classes + mixin 20 mixin composition + cleanup 21 platform-specific cleanups, generate reflective calls + delambdafy 22 remove lambdas + jvm 23 generate JVM bytecode + terminal 24 the last phase during a compilation run diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check index 03f8273c17..cf8595db5d 100644 --- a/test/files/neg/t6446-show-phases.check +++ b/test/files/neg/t6446-show-phases.check @@ -10,18 +10,17 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - fields 11 synthesize accessors and fields + fields 11 synthesize accessors and fields, including bitmaps for la... tailcalls 12 replace tail calls by jumps specialize 13 @specialized-driven class and method specialization explicitouter 14 this refs to outer pointers erasure 15 erase types, add interfaces for traits posterasure 16 clean up erased inline classes - lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 18 move nested functions to top level - constructors 19 move field definitions into constructors - flatten 20 eliminate inner classes - mixin 21 mixin composition - cleanup 22 platform-specific cleanups, generate reflective calls - delambdafy 23 remove lambdas - jvm 24 generate JVM bytecode - terminal 25 the last phase during a compilation run + lambdalift 17 move nested functions to top level + constructors 18 move field definitions into constructors + flatten 19 eliminate inner classes + mixin 20 mixin composition + cleanup 21 platform-specific cleanups, generate reflective calls + delambdafy 22 remove lambdas + jvm 23 generate JVM bytecode + terminal 24 the last phase during a compilation run diff --git a/test/files/neg/t6666.check b/test/files/neg/t6666.check index 090ef72770..bae948fe56 100644 --- a/test/files/neg/t6666.check +++ b/test/files/neg/t6666.check @@ -1,7 +1,7 @@ t6666.scala:23: error: Implementation restriction: access of method x$2 in object O1 from <$anon: Function0>, would require illegal premature access to object O1 F.byname(x) ^ -t6666.scala:30: error: Implementation restriction: access of method x$3 in object O2 from <$anon: Function0>, would require illegal premature access to object O2 +t6666.scala:30: error: Implementation restriction: access of lazy value x$3 in object O2 from <$anon: Function0>, would require illegal premature access to object O2 F.byname(x) ^ t6666.scala:37: error: Implementation restriction: access of method x$4 in object O3 from <$anon: Function0>, would require illegal premature access to object O3 @@ -10,7 +10,7 @@ t6666.scala:37: error: Implementation restriction: access of method x$4 in objec t6666.scala:50: error: Implementation restriction: access of method x$6 in class C1 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C1 F.byname(x) ^ -t6666.scala:54: error: Implementation restriction: access of method x$7 in class C2 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C2 +t6666.scala:54: error: Implementation restriction: access of lazy value x$7 in class C2 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C2 F.byname(x) ^ t6666.scala:58: error: Implementation restriction: access of method x$8 in class C3 from <$anon: Function0>, would require illegal premature access to the unconstructed `this` of class C3 diff --git a/test/files/neg/t6829.check b/test/files/neg/t6829.check index 914a1c9260..274094f791 100644 --- a/test/files/neg/t6829.check +++ b/test/files/neg/t6829.check @@ -17,32 +17,32 @@ t6829.scala:49: error: not found: value nextState ^ t6829.scala:50: error: type mismatch; found : s.type (with underlying type Any) - required: _53.State where val _53: G + required: _30.State where val _30: G val r = rewards(agent).r(s,a,s2) ^ t6829.scala:50: error: type mismatch; found : a.type (with underlying type Any) - required: _53.Action where val _53: G + required: _30.Action where val _30: G val r = rewards(agent).r(s,a,s2) ^ t6829.scala:50: error: type mismatch; found : s2.type (with underlying type Any) - required: _53.State where val _53: G + required: _30.State where val _30: G val r = rewards(agent).r(s,a,s2) ^ t6829.scala:51: error: type mismatch; found : s.type (with underlying type Any) - required: _50.State + required: _25.State agent.learn(s,a,s2,r): G#Agent ^ t6829.scala:51: error: type mismatch; found : a.type (with underlying type Any) - required: _50.Action + required: _25.Action agent.learn(s,a,s2,r): G#Agent ^ t6829.scala:51: error: type mismatch; found : s2.type (with underlying type Any) - required: _50.State + required: _25.State agent.learn(s,a,s2,r): G#Agent ^ t6829.scala:53: error: not found: value nextState diff --git a/test/files/neg/t712.check b/test/files/neg/t712.check index 831e943063..3f02b4b294 100644 --- a/test/files/neg/t712.check +++ b/test/files/neg/t712.check @@ -1,5 +1,4 @@ -t712.scala:10: error: value self is not a member of B.this.ParentImpl - Note: implicit method coerce is not applicable here because it comes after the application point and it lacks an explicit result type +t712.scala:10: error: overloaded method coerce needs result type implicit def coerce(p : ParentImpl) = p.self; ^ one error found diff --git a/test/files/neg/t7494-no-options.check b/test/files/neg/t7494-no-options.check index bb143e8644..138d2fe9a3 100644 --- a/test/files/neg/t7494-no-options.check +++ b/test/files/neg/t7494-no-options.check @@ -11,19 +11,18 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - fields 11 synthesize accessors and fields + fields 11 synthesize accessors and fields, including bitmaps for la... tailcalls 12 replace tail calls by jumps specialize 13 @specialized-driven class and method specialization explicitouter 14 this refs to outer pointers erasure 15 erase types, add interfaces for traits posterasure 16 clean up erased inline classes - lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 18 move nested functions to top level - constructors 19 move field definitions into constructors - flatten 20 eliminate inner classes - mixin 21 mixin composition - cleanup 22 platform-specific cleanups, generate reflective calls - delambdafy 23 remove lambdas - jvm 24 generate JVM bytecode - ploogin 25 A sample phase that does so many things it's kind of hard... - terminal 26 the last phase during a compilation run + lambdalift 17 move nested functions to top level + constructors 18 move field definitions into constructors + flatten 19 eliminate inner classes + mixin 20 mixin composition + cleanup 21 platform-specific cleanups, generate reflective calls + delambdafy 22 remove lambdas + jvm 23 generate JVM bytecode + ploogin 24 A sample phase that does so many things it's kind of hard... + terminal 25 the last phase during a compilation run diff --git a/test/files/neg/t8217-local-alias-requires-rhs.check b/test/files/neg/t8217-local-alias-requires-rhs.check index 0d4f0864ba..d970400ff6 100644 --- a/test/files/neg/t8217-local-alias-requires-rhs.check +++ b/test/files/neg/t8217-local-alias-requires-rhs.check @@ -1,9 +1,9 @@ -t8217-local-alias-requires-rhs.scala:6: error: only classes can have declared but undefined members - type B - ^ t8217-local-alias-requires-rhs.scala:3: error: only classes can have declared but undefined members type A ^ +t8217-local-alias-requires-rhs.scala:6: error: only classes can have declared but undefined members + type B + ^ t8217-local-alias-requires-rhs.scala:14: error: only classes can have declared but undefined members def this(a: Any) = { this(); type C } ^ diff --git a/test/files/neg/t963.check b/test/files/neg/t963.check index 483e53c77d..85b64b0bb5 100644 --- a/test/files/neg/t963.check +++ b/test/files/neg/t963.check @@ -1,12 +1,12 @@ +t963.scala:10: error: type mismatch; + found : AnyRef{def x: Integer} + required: AnyRef{val x: Integer} + val y2 : { val x : java.lang.Integer } = new { def x = new java.lang.Integer(r.nextInt) } + ^ t963.scala:14: error: stable identifier required, but y3.x.type found. val w3 : y3.x.type = y3.x ^ t963.scala:17: error: stable identifier required, but y4.x.type found. val w4 : y4.x.type = y4.x ^ -t963.scala:10: error: type mismatch; - found : AnyRef{def x: Integer} - required: AnyRef{val x: Integer} - val y2 : { val x : java.lang.Integer } = new { def x = new java.lang.Integer(r.nextInt) } - ^ three errors found diff --git a/test/files/pos/t5240.scala b/test/files/pos/t5240.scala index 065d175f2f..ae52c6d69a 100644 --- a/test/files/pos/t5240.scala +++ b/test/files/pos/t5240.scala @@ -1,11 +1,3 @@ - - - - - - package object foo { - var labels: Array[_ <: String] = null - } diff --git a/test/files/pos/t8873.scala b/test/files/pos/t8873.scala new file mode 100644 index 0000000000..e2f0a5fad2 --- /dev/null +++ b/test/files/pos/t8873.scala @@ -0,0 +1 @@ +case class X(@volatile var x:Int) diff --git a/test/files/pos/trait_lazy_accessboundary.scala b/test/files/pos/trait_lazy_accessboundary.scala new file mode 100644 index 0000000000..6529816ffb --- /dev/null +++ b/test/files/pos/trait_lazy_accessboundary.scala @@ -0,0 +1,2 @@ +package foo { trait HasLazy { private[foo] lazy val myLazy = "my lady" } } +package bar { class MixInSuperLazy extends foo.HasLazy } diff --git a/test/files/presentation/t5708.check b/test/files/presentation/t5708.check index 4b33893e98..0f24d9626b 100644 --- a/test/files/presentation/t5708.check +++ b/test/files/presentation/t5708.check @@ -35,7 +35,7 @@ final def wait(): Unit final def wait(x$1: Long): Unit final def wait(x$1: Long,x$2: Int): Unit final private[this] val CONST_STRING: String("constant") -lazy private[this] var foo: Int +lazy val foo: Int private[package test] def pkgPrivateM: String private[this] val pkgPrivateV: String ================================================================================ diff --git a/test/files/presentation/t8459.check b/test/files/presentation/t8459.check index 336c147141..4c105d2a00 100644 --- a/test/files/presentation/t8459.check +++ b/test/files/presentation/t8459.check @@ -9,6 +9,7 @@ scala.AnyRef { () }; private[this] val bar: F = new F(); + <stable> <accessor> def bar: F = Foo.this.bar; Foo.this.bar.<selectDynamic: error>("<error>") } ================================================================================ diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index ca0005ea4d..64b68db242 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -1,4 +1,5 @@ adaptBoundsToAnnots(List( <: Int), List(type T), List(Int @testAnn)) [2] +annotationsConform(Boolean @testAnn, Boolean @testAnn) [2] annotationsConform(Boolean @testAnn, Boolean) [1] annotationsConform(Boolean(false), Boolean @testAnn) [1] annotationsConform(Int @testAnn, ?A) [1] @@ -13,7 +14,7 @@ canAdaptAnnotations(Trees$Select, ?) [1] canAdaptAnnotations(Trees$Select, Boolean @testAnn) [1] canAdaptAnnotations(Trees$Select, Boolean) [1] canAdaptAnnotations(Trees$Select, String @testAnn) [1] -canAdaptAnnotations(Trees$TypeTree, ?) [10] +canAdaptAnnotations(Trees$TypeTree, ?) [8] canAdaptAnnotations(Trees$Typed, ?) [3] canAdaptAnnotations(Trees$Typed, Any) [1] canAdaptAnnotations(Trees$Typed, Int) [1] @@ -24,21 +25,21 @@ pluginsPt(?, Trees$ApplyImplicitView) [2] pluginsPt(?, Trees$Block) [4] pluginsPt(?, Trees$ClassDef) [2] pluginsPt(?, Trees$DefDef) [14] -pluginsPt(?, Trees$Ident) [50] +pluginsPt(?, Trees$Ident) [51] pluginsPt(?, Trees$If) [2] pluginsPt(?, Trees$Literal) [16] -pluginsPt(?, Trees$New) [5] +pluginsPt(?, Trees$New) [6] pluginsPt(?, Trees$PackageDef) [1] pluginsPt(?, Trees$Return) [1] -pluginsPt(?, Trees$Select) [43] +pluginsPt(?, Trees$Select) [45] pluginsPt(?, Trees$Super) [2] pluginsPt(?, Trees$This) [13] pluginsPt(?, Trees$TypeApply) [3] pluginsPt(?, Trees$TypeBoundsTree) [2] pluginsPt(?, Trees$TypeDef) [1] -pluginsPt(?, Trees$TypeTree) [38] +pluginsPt(?, Trees$TypeTree) [32] pluginsPt(?, Trees$Typed) [1] -pluginsPt(?, Trees$ValDef) [21] +pluginsPt(?, Trees$ValDef) [13] pluginsPt(Any, Trees$Literal) [2] pluginsPt(Any, Trees$Typed) [1] pluginsPt(Array[Any], Trees$ArrayValue) [1] @@ -60,7 +61,7 @@ pluginsPt(String, Trees$Ident) [3] pluginsPt(String, Trees$Literal) [1] pluginsPt(String, Trees$Select) [1] pluginsPt(Unit, Trees$Assign) [1] -pluginsPt(testAnn, Trees$Apply) [5] +pluginsPt(testAnn, Trees$Apply) [6] pluginsTypeSig(<none>, Trees$Template) [2] pluginsTypeSig(class A, Trees$ClassDef) [1] pluginsTypeSig(class testAnn, Trees$ClassDef) [1] @@ -70,16 +71,18 @@ pluginsTypeSig(method foo, Trees$DefDef) [1] pluginsTypeSig(method method, Trees$DefDef) [1] pluginsTypeSig(method nested, Trees$DefDef) [1] pluginsTypeSig(type T, Trees$TypeDef) [2] -pluginsTypeSig(value annotField, Trees$ValDef) [2] +pluginsTypeSig(value annotField, Trees$ValDef) [1] +pluginsTypeSig(value count_=, Trees$ValDef) [1] pluginsTypeSig(value f, Trees$ValDef) [1] -pluginsTypeSig(value inferField, Trees$ValDef) [2] -pluginsTypeSig(value lub1, Trees$ValDef) [2] -pluginsTypeSig(value lub2, Trees$ValDef) [2] +pluginsTypeSig(value inferField, Trees$ValDef) [1] +pluginsTypeSig(value lub1, Trees$ValDef) [1] +pluginsTypeSig(value lub2, Trees$ValDef) [1] pluginsTypeSig(value param, Trees$ValDef) [2] pluginsTypeSig(value str, Trees$ValDef) [1] -pluginsTypeSig(value x, Trees$ValDef) [4] -pluginsTypeSig(value y, Trees$ValDef) [4] -pluginsTypeSig(variable count, Trees$ValDef) [3] +pluginsTypeSig(value x, Trees$ValDef) [3] +pluginsTypeSig(value y, Trees$ValDef) [3] +pluginsTypeSig(variable count, Trees$DefDef) [1] +pluginsTypeSig(variable count, Trees$ValDef) [1] pluginsTypeSigAccessor(value annotField) [1] pluginsTypeSigAccessor(value inferField) [1] pluginsTypeSigAccessor(value lub1) [1] @@ -93,7 +96,7 @@ pluginsTyped(()Object, Trees$Select) [1] pluginsTyped(()String, Trees$Ident) [1] pluginsTyped(()String, Trees$TypeApply) [1] pluginsTyped(()scala.annotation.Annotation, Trees$Select) [1] -pluginsTyped(()testAnn, Trees$Select) [10] +pluginsTyped(()testAnn, Trees$Select) [12] pluginsTyped((str: String)A <and> (param: Double)A, Trees$Select) [1] pluginsTyped((x$1: Any)Boolean <and> (x: Double)Boolean <and> (x: Float)Boolean <and> (x: Long)Boolean <and> (x: Int)Boolean <and> (x: Char)Boolean <and> (x: Short)Boolean <and> (x: Byte)Boolean, Trees$Select) [1] pluginsTyped((x$1: Int)Unit, Trees$Select) [1] @@ -106,7 +109,7 @@ pluginsTyped(<notype>, Trees$ClassDef) [2] pluginsTyped(<notype>, Trees$DefDef) [14] pluginsTyped(<notype>, Trees$PackageDef) [1] pluginsTyped(<notype>, Trees$TypeDef) [1] -pluginsTyped(<notype>, Trees$ValDef) [21] +pluginsTyped(<notype>, Trees$ValDef) [13] pluginsTyped(=> Boolean @testAnn, Trees$Select) [1] pluginsTyped(=> Double, Trees$Select) [1] pluginsTyped(=> Int, Trees$Select) [5] @@ -122,7 +125,7 @@ pluginsTyped(Any, Trees$TypeTree) [1] pluginsTyped(AnyRef, Trees$Select) [4] pluginsTyped(Array[Any], Trees$ArrayValue) [1] pluginsTyped(Boolean @testAnn, Trees$Select) [1] -pluginsTyped(Boolean @testAnn, Trees$TypeTree) [4] +pluginsTyped(Boolean @testAnn, Trees$TypeTree) [3] pluginsTyped(Boolean(false), Trees$Literal) [1] pluginsTyped(Boolean, Trees$Apply) [1] pluginsTyped(Boolean, Trees$Select) [3] @@ -139,15 +142,15 @@ pluginsTyped(Int, Trees$Apply) [1] pluginsTyped(Int, Trees$Ident) [1] pluginsTyped(Int, Trees$If) [1] pluginsTyped(Int, Trees$Select) [12] -pluginsTyped(Int, Trees$TypeTree) [13] +pluginsTyped(Int, Trees$TypeTree) [10] pluginsTyped(List[Any], Trees$Apply) [1] pluginsTyped(List[Any], Trees$Select) [1] -pluginsTyped(List[Any], Trees$TypeTree) [3] +pluginsTyped(List[Any], Trees$TypeTree) [2] pluginsTyped(Nothing, Trees$Return) [1] pluginsTyped(Object, Trees$Apply) [1] pluginsTyped(String @testAnn, Trees$Ident) [1] pluginsTyped(String @testAnn, Trees$Select) [1] -pluginsTyped(String @testAnn, Trees$TypeTree) [4] +pluginsTyped(String @testAnn, Trees$TypeTree) [3] pluginsTyped(String(""), Trees$Literal) [2] pluginsTyped(String("huhu"), Trees$Literal) [1] pluginsTyped(String("str") @testAnn, Trees$Typed) [1] @@ -156,13 +159,13 @@ pluginsTyped(String("two"), Trees$Literal) [2] pluginsTyped(String, Trees$Apply) [2] pluginsTyped(String, Trees$Block) [2] pluginsTyped(String, Trees$Select) [7] -pluginsTyped(String, Trees$TypeTree) [7] +pluginsTyped(String, Trees$TypeTree) [6] pluginsTyped(Unit, Trees$Apply) [2] pluginsTyped(Unit, Trees$Assign) [1] pluginsTyped(Unit, Trees$Block) [4] pluginsTyped(Unit, Trees$If) [1] pluginsTyped(Unit, Trees$Literal) [5] -pluginsTyped(Unit, Trees$TypeTree) [1] +pluginsTyped(Unit, Trees$TypeTree) [2] pluginsTyped([A](xs: A*)List[A], Trees$Select) [1] pluginsTyped([T <: Int]=> Int, Trees$Select) [1] pluginsTyped([T0]()T0, Trees$Select) [1] @@ -176,9 +179,9 @@ pluginsTyped(scala.collection.immutable.List.type, Trees$Select) [2] pluginsTyped(scala.collection.immutable.StringOps, Trees$ApplyImplicitView) [2] pluginsTyped(scala.collection.mutable.WrappedArray[Any], Trees$Apply) [1] pluginsTyped(str.type, Trees$Ident) [3] -pluginsTyped(testAnn, Trees$Apply) [5] -pluginsTyped(testAnn, Trees$Ident) [5] -pluginsTyped(testAnn, Trees$New) [5] +pluginsTyped(testAnn, Trees$Apply) [6] +pluginsTyped(testAnn, Trees$Ident) [6] +pluginsTyped(testAnn, Trees$New) [6] pluginsTyped(testAnn, Trees$This) [1] pluginsTyped(testAnn, Trees$TypeTree) [2] pluginsTyped(testAnn.super.type, Trees$Super) [1] diff --git a/test/files/run/compiler-asSeenFrom.check b/test/files/run/compiler-asSeenFrom.check index 7305504115..46ea4d3685 100644 --- a/test/files/run/compiler-asSeenFrom.check +++ b/test/files/run/compiler-asSeenFrom.check @@ -332,11 +332,6 @@ value dZ { // after parser val cD: ll.C[List[T3]] } -value dZ { // after parser - private[this] val cD: ll.C[List[T3]] - val cD: ll.C[List[T3]] -} - value dZ { // after uncurry private[this] val cD: ll.C[List[T3]] val cD(): ll.C[List[T3]] @@ -347,11 +342,9 @@ value dZ { // after erasure val cD(): ll.C } -value jZ { // after parser - def thisI(): I.this.type - def thisC(): C.this.type - def t2(): T2 - def t1(): T1 +value dZ { // after parser + private[this] val cD: ll.C[List[T3]] + val cD: ll.C[List[T3]] } value jZ { // after parser @@ -393,6 +386,13 @@ value jZ { // after flatten def t1(): Object } +value jZ { // after parser + def thisI(): I.this.type + def thisC(): C.this.type + def t2(): T2 + def t1(): T1 +} + method kz { // after parser def thisI(): I.this.type def thisC(): C.this.type diff --git a/test/files/run/delambdafy_t6028.check b/test/files/run/delambdafy_t6028.check index 8b0ae7e9b9..eaba70ee1a 100644 --- a/test/files/run/delambdafy_t6028.check +++ b/test/files/run/delambdafy_t6028.check @@ -42,18 +42,12 @@ package <empty> { <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer; <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer }; - final <stable> private[this] def MethodLocalObject$lzycompute$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = { - T.this.synchronized({ - if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) - MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1); - scala.runtime.BoxedUnit.UNIT - }); + final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = { + if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) + T.this.synchronized[Unit](if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) + MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1)); MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]() }; - final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) - T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1) - else - MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type](); final <artifact> private[this] def $anonfun$tryy$1(tryyParam$1: String, tryyLocal$1: runtime.ObjectRef): Unit = try { tryyLocal$1.elem = tryyParam$1 } finally () diff --git a/test/files/run/existential-rangepos.check b/test/files/run/existential-rangepos.check index 1212b60bae..984baeaaf8 100644 --- a/test/files/run/existential-rangepos.check +++ b/test/files/run/existential-rangepos.check @@ -7,7 +7,7 @@ }; [24:51]private[this] val foo: [28]Set[_ <: T] = [47:51]null; [28]<stable> <accessor> def foo: [28]Set[_ <: T] = [28][28]A.this.foo; - [54:74]<stable> <accessor> def bar: [58]Set[_ <: T] + [54:74]<stable> <accessor> val bar: [58]Set[_ <: T] } } diff --git a/test/files/run/idempotency-lazy-vals.check b/test/files/run/idempotency-lazy-vals.check index 15afa5303c..3a6f1a7ef0 100644 --- a/test/files/run/idempotency-lazy-vals.check +++ b/test/files/run/idempotency-lazy-vals.check @@ -5,19 +5,11 @@ C.super.<init>(); () }; - lazy private[this] val x: Int = _; - <stable> <accessor> lazy def x: Int = { - C.this.x = 2; - C.this.x - }; - lazy private[this] val y: Int = _; - implicit <stable> <accessor> lazy def y: Int = { - C.this.y = 3; - C.this.y - } + <stable> <accessor> lazy val x: Int = 2; + implicit <stable> <accessor> lazy val y: Int = 3 }; val c: C = new C(); import c._; c.x.*(Predef.implicitly[Int](c.y)) } -error! +6 diff --git a/test/files/run/junitForwarders/C_1.scala b/test/files/run/junitForwarders/C_1.scala new file mode 100644 index 0000000000..2af2026a61 --- /dev/null +++ b/test/files/run/junitForwarders/C_1.scala @@ -0,0 +1,15 @@ +trait T { + @org.junit.Test def foo = 0 +} + +class C extends T + +object Test extends App { + def check(c: Class[_], e: String) = { + val s = c.getDeclaredMethods.sortBy(_.getName).map(m => s"${m.getName} - ${m.getDeclaredAnnotations.mkString(", ")}").mkString(";") + assert(s == e, s"found: $s\nexpected: $e") + } + check(classOf[C], "foo - @org.junit.Test()") + // TODO scala-dev#213: should `foo$` really carry the @Test annotation? + check(classOf[T], "$init$ - ;foo - @org.junit.Test();foo$ - @org.junit.Test()") +} diff --git a/test/files/run/junitForwarders/Test.java b/test/files/run/junitForwarders/Test.java new file mode 100644 index 0000000000..57c4d5b544 --- /dev/null +++ b/test/files/run/junitForwarders/Test.java @@ -0,0 +1,10 @@ +package org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface Test { } diff --git a/test/files/run/lazy-locals-2.scala b/test/files/run/lazy-locals-2.scala new file mode 100644 index 0000000000..d6c33cffcb --- /dev/null +++ b/test/files/run/lazy-locals-2.scala @@ -0,0 +1,322 @@ +object Logs { + val logBuf = new collection.mutable.StringBuilder() + def log(m: Any): Unit = { if (logBuf.nonEmpty) logBuf.append(":"); logBuf.append(m) } + def checkLog(expected: String): Unit = { + val res = logBuf.toString + assert(res == expected, s"expected:\n$expected\nfound:\n$res") + logBuf.clear() + } +} + +import Logs._ + +class C { + def getInt : Int = { log("getInt"); 1 } + def getString: String = { log("getString"); "s" } + def getUnit : Unit = { log("getUnit") } + + lazy val t1 = getInt + lazy val t2 = getString + lazy val t3 = getUnit + checkLog("") + + def m1 = { + lazy val t1 = getInt + t1 + t1 + } + def m2 = { + lazy val t1 = getString + t1 + t1 + } + def m3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("") + + + val vl1 = { + lazy val t1 = getInt + t1 + t1 + } + val vl2 = { + lazy val t1 = getString + t1 + t1 + } + val vl3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("getInt:getString:getUnit:():()") + + + var vr1 = { + lazy val t1 = getInt + t1 + t1 + } + var vr2 = { + lazy val t1 = getString + t1 + t1 + } + var vr3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("getInt:getString:getUnit:():()") + + + lazy val lvl1 = { + lazy val t1 = getInt + t1 + t1 + } + lazy val lvl2 = { + lazy val t1 = getString + t1 + t1 + } + lazy val lvl3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("") + + + { + lazy val t1 = getInt + lazy val t2 = getString + lazy val t3 = getUnit + + log(t1 + t1) + log(t2 + t2) + log(t3); log(t3) + } + checkLog("getInt:2:getString:ss:getUnit:():()") + + + def run(): Unit = { + log(t1); log(t1); + log(t2); log(t2); + log(t3); log(t3); + checkLog("getInt:1:1:getString:s:s:getUnit:():()") + + log(m1); log(m1) + log(m2); log(m2) + log(m3); log(m3) + checkLog("getInt:2:getInt:2:getString:ss:getString:ss:getUnit:():():():getUnit:():():()") + + log(vl1); log(vl1) + log(vl2); log(vl2) + log(vl3); log(vl3) + checkLog("2:2:ss:ss:():()") + + log(vr1); log(vr1); vr1 = 393; log(vr1) + log(vr2); log(vr2); vr2 = "h"; log(vr2) + log(vr3); log(vr3); vr3 = () ; log(vr3) + checkLog("2:2:393:ss:ss:h:():():()") + + log(lvl1); log(lvl1) + log(lvl2); log(lvl2) + log(lvl3); log(lvl3) + checkLog("getInt:2:2:getString:ss:ss:getUnit:():():():()") + } +} + +trait T { + def getInt : Int = { log("getInt"); 1 } + def getString: String = { log("getString"); "s" } + def getUnit : Unit = { log("getUnit") } + + lazy val t1 = getInt + lazy val t2 = getString + lazy val t3 = getUnit + checkLog("") + + def m1 = { + lazy val t1 = getInt + t1 + t1 + } + def m2 = { + lazy val t1 = getString + t1 + t1 + } + def m3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("") + + + val vl1 = { + lazy val t1 = getInt + t1 + t1 + } + val vl2 = { + lazy val t1 = getString + t1 + t1 + } + val vl3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("getInt:getString:getUnit:():()") + + + var vr1 = { + lazy val t1 = getInt + t1 + t1 + } + var vr2 = { + lazy val t1 = getString + t1 + t1 + } + var vr3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("getInt:getString:getUnit:():()") + + + lazy val lvl1 = { + lazy val t1 = getInt + t1 + t1 + } + lazy val lvl2 = { + lazy val t1 = getString + t1 + t1 + } + lazy val lvl3 = { + lazy val t1 = getUnit + log(t1); log(t1) + } + checkLog("") + + + { + lazy val t1 = getInt + lazy val t2 = getString + lazy val t3 = getUnit + + log(t1 + t1) + log(t2 + t2) + log(t3); log(t3) + } + checkLog("getInt:2:getString:ss:getUnit:():()") + + + def run(): Unit = { + log(t1); log(t1); + log(t2); log(t2); + log(t3); log(t3); + checkLog("getInt:1:1:getString:s:s:getUnit:():()") + + log(m1); log(m1) + log(m2); log(m2) + log(m3); log(m3) + checkLog("getInt:2:getInt:2:getString:ss:getString:ss:getUnit:():():():getUnit:():():()") + + log(vl1); log(vl1) + log(vl2); log(vl2) + log(vl3); log(vl3) + checkLog("2:2:ss:ss:():()") + + log(vr1); log(vr1); vr1 = 393; log(vr1) + log(vr2); log(vr2); vr2 = "h"; log(vr2) + log(vr3); log(vr3); vr3 = () ; log(vr3) + checkLog("2:2:393:ss:ss:h:():():()") + + log(lvl1); log(lvl1) + log(lvl2); log(lvl2) + log(lvl3); log(lvl3) + checkLog("getInt:2:2:getString:ss:ss:getUnit:():():():()") + } +} + +class D extends T + +class D1 extends T { + override lazy val t1 = { log("o-t1"); -1 } + checkLog("") + + override def m1 = { log("o-m1"); -2 } + override val m2 = { log("o-m2"); "n" } + override lazy val m3 = { log("o-m3") } + checkLog("o-m2") + + override val vl1 = { log("o-vl1"); -3 } + checkLog("o-vl1") + + override lazy val lvl1 = { log("o-lvl1"); -4 } + checkLog("") + + override def run(): Unit = { + log(t1); log(t1) + checkLog("o-t1:-1:-1") + + log(m1); log(m1) + log(m2); log(m2) + log(m3); log(m3) + checkLog("o-m1:-2:o-m1:-2:n:n:o-m3:():()") + + log(vl1); log(vl1) + checkLog("-3:-3") + + log(lvl1); log(lvl1) + checkLog("o-lvl1:-4:-4") + } +} + +class E { + object T { log("init T"); override def toString = "T" } + def m = { object T { log("init T"); val x = 1 }; T.x } + checkLog("") +} + +object Test { + def main(args: Array[String]): Unit = { + val c = new C + c.run() + + val lzyComputeMethods = c.getClass.getDeclaredMethods.filter(_.getName contains "lzycompute").map(_.getName).toList.sorted + val expComputeMethods = List("lvl1$lzycompute", "lvl2$lzycompute", "lvl3$lzycompute", "t1$lzycompute", "t1$lzycompute$1", "t1$lzycompute$10", "t1$lzycompute$11", "t1$lzycompute$12", "t1$lzycompute$13", "t1$lzycompute$2", "t1$lzycompute$3", "t1$lzycompute$4", "t1$lzycompute$5", "t1$lzycompute$6", "t1$lzycompute$7", "t1$lzycompute$8", "t1$lzycompute$9", "t2$lzycompute", "t2$lzycompute$1", "t3$lzycompute", "t3$lzycompute$1") + assert( + lzyComputeMethods == expComputeMethods, + s"wrong lzycompute methods. expected:\n$expComputeMethods\nfound:\n$lzyComputeMethods") + + val fields = c.getClass.getDeclaredFields.toList.sortBy(_.getName).map(_.toString) + val expFields = List( + "private volatile byte C.bitmap$0", + "private int C.lvl1", + "private java.lang.String C.lvl2", + "private scala.runtime.BoxedUnit C.lvl3", + "private int C.t1", + "private java.lang.String C.t2", + "private scala.runtime.BoxedUnit C.t3", + "private final int C.vl1", + "private final java.lang.String C.vl2", + "private final scala.runtime.BoxedUnit C.vl3", + "private int C.vr1", + "private java.lang.String C.vr2", + "private scala.runtime.BoxedUnit C.vr3") + assert( + fields == expFields, + s"wrong fields. expected:\n$expFields\nfound:\n$fields") + + + val d = new D + d.run() + + val dFields = d.getClass.getDeclaredFields.toList.sortBy(_.getName).map(_.toString) + assert( + dFields == expFields.map(_.replaceAll(" C.", " D.")), + s"wrong fields. expected:\n$expFields\nfound:\n$fields") + + + val d1 = new D1 + d1.run() + + val e = new E + log(e.T); log(e.T) + checkLog("init T:T:T") + log(e.m); log(e.m) + checkLog("init T:1:init T:1") + } +} diff --git a/test/files/run/lazy-locals.check b/test/files/run/lazy-locals.check index 4565326bea..0a3a85ead6 100644 --- a/test/files/run/lazy-locals.check +++ b/test/files/run/lazy-locals.check @@ -1,9 +1,6 @@ lazy-locals.scala:153: warning: a pure expression does nothing in statement position; multiline expressions may require enclosing parentheses { ^ -lazy-locals.scala:159: warning: a pure expression does nothing in statement position; multiline expressions may require enclosing parentheses - { - ^ forced lazy val q q = 10 forced lazy val t diff --git a/test/files/run/lazy_local_labels.check b/test/files/run/lazy_local_labels.check new file mode 100644 index 0000000000..e42c8fb8ce --- /dev/null +++ b/test/files/run/lazy_local_labels.check @@ -0,0 +1,9 @@ +HI +HI +HI +HI +HI +HI +HI +HI +HI diff --git a/test/files/run/lazy_local_labels.scala b/test/files/run/lazy_local_labels.scala new file mode 100644 index 0000000000..f4a1cdf689 --- /dev/null +++ b/test/files/run/lazy_local_labels.scala @@ -0,0 +1,28 @@ +// should print HI nine times to indicate the lazy val has been re-initialized on every iteration +object Test extends App { + def fooDo: Unit = { + var i = 3 + do { + lazy val x = { println("HI"); 1 } + i -= x + } while(i > 0) + } + + def fooWhile: Unit = { + var i = 3 + while(i > 0) { + lazy val x = { println("HI"); 1 } + i -= x + } + } + + @annotation.tailrec def fooTail(i: Int): Unit = { + lazy val x = { println("HI"); 1 } + if (i > 0) fooTail(i - x) + } + + + fooWhile + fooDo + fooTail(3) +} diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index 03f8273c17..cf8595db5d 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -10,18 +10,17 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - fields 11 synthesize accessors and fields + fields 11 synthesize accessors and fields, including bitmaps for la... tailcalls 12 replace tail calls by jumps specialize 13 @specialized-driven class and method specialization explicitouter 14 this refs to outer pointers erasure 15 erase types, add interfaces for traits posterasure 16 clean up erased inline classes - lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 18 move nested functions to top level - constructors 19 move field definitions into constructors - flatten 20 eliminate inner classes - mixin 21 mixin composition - cleanup 22 platform-specific cleanups, generate reflective calls - delambdafy 23 remove lambdas - jvm 24 generate JVM bytecode - terminal 25 the last phase during a compilation run + lambdalift 17 move nested functions to top level + constructors 18 move field definitions into constructors + flatten 19 eliminate inner classes + mixin 20 mixin composition + cleanup 21 platform-specific cleanups, generate reflective calls + delambdafy 22 remove lambdas + jvm 23 generate JVM bytecode + terminal 24 the last phase during a compilation run diff --git a/test/files/run/showraw_mods.check b/test/files/run/showraw_mods.check index ff77d22adf..5afd7a438f 100644 --- a/test/files/run/showraw_mods.check +++ b/test/files/run/showraw_mods.check @@ -1 +1 @@ -Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), TypeName("C"), List(), Template(List(Ident(TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), TermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), DefDef(Modifiers(PRIVATE | METHOD | LOCAL | STABLE | ACCESSOR), TermName("x"), List(), List(), TypeTree(), Literal(Constant(2))), DefDef(Modifiers(METHOD | ACCESSOR), TermName("y"), List(), List(), TypeTree(), Select(This(TypeName("C")), TermName("x"))), DefDef(Modifiers(METHOD | ACCESSOR), TermName("y_$eq"), List(), List(List(ValDef(Modifiers(PARAM | SYNTHETIC), TermName("x$1"), TypeTree(), EmptyTree))), TypeTree(), EmptyTree), ValDef(Modifiers(LAZY), TermName("z"), TypeTree(), Select(This(TypeName("C")), TermName("y"))))))), Literal(Constant(()))) +Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), TypeName("C"), List(), Template(List(Ident(TypeName("AnyRef"))), noSelfType, List(DefDef(Modifiers(), TermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), ValDef(Modifiers(PRIVATE | LOCAL), TermName("x"), TypeTree(), Literal(Constant(2))), ValDef(Modifiers(MUTABLE), TermName("y"), TypeTree(), Select(This(TypeName("C")), TermName("x"))), DefDef(Modifiers(METHOD | ACCESSOR), TermName("y_$eq"), List(), List(List(ValDef(Modifiers(PARAM | SYNTHETIC), TermName("x$1"), TypeTree(), EmptyTree))), TypeTree(), EmptyTree), ValDef(Modifiers(LAZY), TermName("z"), TypeTree(), Select(This(TypeName("C")), TermName("y"))))))), Literal(Constant(()))) diff --git a/test/files/run/t3569.check b/test/files/run/t3569.check index a9fb5ff32e..e0e1d6c405 100644 --- a/test/files/run/t3569.check +++ b/test/files/run/t3569.check @@ -2,6 +2,8 @@ private final int Test$X.val1 private final int Test$X.val2 private final int Test$X.val3 +private int Test$X.const1 +private int Test$X.const2 private int Test$X.lval1 private int Test$X.lval2 private int Test$X.lval3 diff --git a/test/files/run/t3569.scala b/test/files/run/t3569.scala index eb3b424439..7da4de9e95 100644 --- a/test/files/run/t3569.scala +++ b/test/files/run/t3569.scala @@ -4,7 +4,13 @@ object Test { lazy val lv = scala.util.Random.nextInt() - class X(final var x: Int) { + trait T { final lazy val const1 = 1 } // no fields + + class X(final var x: Int) extends T { + // a lazy val does not receive a constant type, for backwards compat (e.g. for the repl) + // besides, since you explicitly wanted something lazy, we'll give you something lazy! (a field and a bitmap) + final lazy val const2 = 2 + final var var1: Int = 0 final private var var2: Int = 0 final private[this] var var3: Int = 0 diff --git a/test/files/run/t5552.check b/test/files/run/t5552.check index a19a60840e..73ad9cf824 100644 --- a/test/files/run/t5552.check +++ b/test/files/run/t5552.check @@ -1,2 +1,6 @@ +lazy: 3 (3,3) +(3,3) +lazy: 3.0 +(3.0,3.0) (3.0,3.0) diff --git a/test/files/run/t5552.scala b/test/files/run/t5552.scala index afb8a1f0be..5b717f9e13 100644 --- a/test/files/run/t5552.scala +++ b/test/files/run/t5552.scala @@ -1,10 +1,14 @@ class C[@specialized(Int) A](a:A) { - lazy val b = (a, a) + lazy val b = {println(s"lazy: $a"); (a, a)} // there should only be two instances of "lazy" in the output def c = b } object Test { def main(args:Array[String]) { - println(new C(3).c) - println(new C(3.0).c) + val cInt = new C(3) + println(cInt.c) + println(cInt.c) + val cFloat = new C(3.0) + println(cFloat.c) + println(cFloat.c) } } diff --git a/test/files/run/t6023.check b/test/files/run/t6023.check index ee93565234..dd6d8f1f1c 100644 --- a/test/files/run/t6023.check +++ b/test/files/run/t6023.check @@ -1,12 +1,12 @@ { abstract trait Foo extends AnyRef { - <stable> <accessor> def a: Int + val a: Int }; () } { abstract trait Foo extends AnyRef { - <stable> <accessor> def a: Int + <stable> <accessor> val a: Int }; () } diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check index 532d177300..d6cc452bbf 100644 --- a/test/files/run/t6028.check +++ b/test/files/run/t6028.check @@ -54,18 +54,12 @@ package <empty> { <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer; <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer }; - final <stable> private[this] def MethodLocalObject$lzycompute$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = { - T.this.synchronized({ - if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) - MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1); - scala.runtime.BoxedUnit.UNIT - }); + final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = { + if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) + T.this.synchronized[Unit](if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) + MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1)); MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]() }; - final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null)) - T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1) - else - MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type](); @SerialVersionUID(value = 0) final <synthetic> class $anonfun$tryy$1 extends scala.runtime.AbstractFunction0$mcV$sp with Serializable { def <init>($outer: T, tryyParam$1: Int, tryyLocal$1: runtime.IntRef): <$anon: Function0> = { $anonfun$tryy$1.super.<init>(); diff --git a/test/files/run/t6733.check b/test/files/run/t6733.check index 7062301c56..811a7d8f70 100644 --- a/test/files/run/t6733.check +++ b/test/files/run/t6733.check @@ -4,7 +4,6 @@ method pri2a: isPrivateThis = true, isProtectedThis = false variable pri3a: isPrivateThis = true, isProtectedThis = false variable pri3a: isPrivateThis = true, isProtectedThis = false lazy value pri4a: isPrivateThis = true, isProtectedThis = false -lazy value pri4a: isPrivateThis = true, isProtectedThis = false type Pri5a: isPrivateThis = true, isProtectedThis = false class Pri6: isPrivateThis = true, isProtectedThis = false trait Pri7: isPrivateThis = true, isProtectedThis = false @@ -18,7 +17,6 @@ variable pro3a: isPrivateThis = false, isProtectedThis = true variable pro3b: isPrivateThis = false, isProtectedThis = true variable pro3b: isPrivateThis = false, isProtectedThis = true lazy value pro4a: isPrivateThis = false, isProtectedThis = true -lazy value pro4a: isPrivateThis = true, isProtectedThis = false type Pro5a: isPrivateThis = false, isProtectedThis = true type Pro5b: isPrivateThis = false, isProtectedThis = true class Pro6: isPrivateThis = false, isProtectedThis = true diff --git a/test/files/run/trait-fields-override-lazy.check b/test/files/run/trait-fields-override-lazy.check new file mode 100644 index 0000000000..9e4a9fe6c2 --- /dev/null +++ b/test/files/run/trait-fields-override-lazy.check @@ -0,0 +1,2 @@ +warning: there was one feature warning; re-run with -feature for details +ok diff --git a/test/files/run/trait-fields-override-lazy.scala b/test/files/run/trait-fields-override-lazy.scala new file mode 100644 index 0000000000..2c1cf0e3b0 --- /dev/null +++ b/test/files/run/trait-fields-override-lazy.scala @@ -0,0 +1,13 @@ +trait T { + protected lazy val lv: Boolean = ??? +} + +object Test extends App { + val overrideLazy = new T { + override lazy val lv = true + def foo = lv + } + + assert(overrideLazy.foo) + println("ok") +} diff --git a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala index fe07893a36..07bff40f13 100644 --- a/test/files/scalacheck/quasiquotes/TypecheckedProps.scala +++ b/test/files/scalacheck/quasiquotes/TypecheckedProps.scala @@ -103,7 +103,7 @@ object TypecheckedProps extends QuasiquoteProperties("typechecked") val lazyName = TermName("x") val lazyRhsVal = 42 val lazyRhs = Literal(Constant(lazyRhsVal)) - val q"{ $_ ; $mods val $pname: $_ = { $_ = $rhs ; $_ } }" = typecheck(q"{lazy val $lazyName = $lazyRhsVal}") + val q"{ $mods val $pname: $_ = $rhs }" = typecheck(q"{lazy val $lazyName = $lazyRhsVal}") assert(pname == lazyName) assert(rhs ≈ lazyRhs) diff --git a/test/junit/scala/lang/traits/BytecodeTest.scala b/test/junit/scala/lang/traits/BytecodeTest.scala index e6c74b86ab..cf658288c4 100644 --- a/test/junit/scala/lang/traits/BytecodeTest.scala +++ b/test/junit/scala/lang/traits/BytecodeTest.scala @@ -9,7 +9,6 @@ import scala.collection.JavaConverters._ import scala.tools.asm.Opcodes import scala.tools.asm.Opcodes._ import scala.tools.asm.tree.ClassNode -import scala.tools.nsc.backend.jvm.opt.BytecodeUtils import scala.tools.partest.ASMConverters._ import scala.tools.testing.BytecodeTesting import scala.tools.testing.BytecodeTesting._ @@ -237,7 +236,7 @@ class BytecodeTest extends BytecodeTesting { |class C extends T """.stripMargin val List(c1, _) = compileClasses(code) - val List(c2, _) = newCompiler(extraArgs = "-Xgen-mixin-forwarders").compileClasses(code) + val List(c2, _) = newCompiler(extraArgs = "-Xmixin-force-forwarders:true").compileClasses(code) assert(getMethods(c1, "f").isEmpty) assertSameCode(getMethod(c2, "f"), List(VarOp(ALOAD, 0), Invoke(INVOKESTATIC, "T", "f$", "(LT;)I", true), Op(IRETURN))) @@ -245,7 +244,6 @@ class BytecodeTest extends BytecodeTesting { @Test def sd143(): Unit = { - // this tests the status quo, which is wrong. val code = """class A { def m = 1 } |class B extends A { override def m = 2 } @@ -254,10 +252,119 @@ class BytecodeTest extends BytecodeTesting { | override def m = super[T].m // should invoke A.m |} """.stripMargin + + val err = + """cannot emit super call: the selected method m is declared in class A, which is not the direct superclass of class C. + |An unqualified super call (super.m) would be allowed.""".stripMargin + val cls = compileClasses(code, allowMessage = _.msg contains err) + assert(cls.isEmpty, cls.map(_.name)) + } + + @Test + def sd143b(): Unit = { + val jCode = List("interface A { default int m() { return 1; } }" -> "A.java") + val code = + """class B extends A { override def m = 2 } + |trait T extends A + |class C extends B with T { + | override def m = super[T].m + |} + """.stripMargin + + val err = "unable to emit super call unless interface A (which declares method m) is directly extended by class C" + val cls = compileClasses(code, jCode, allowMessage = _.msg contains err) + assert(cls.isEmpty, cls.map(_.name)) + } + + @Test + def sd143c(): Unit = { + // Allow super calls to class methods of indirect super classes + val code = + """class A { def f = 1 } + |class B extends A + |trait T extends A { override def f = 2 } + |class C extends B with T { + | def t1 = super[B].f + | def t2 = super.f + | def t3 = super[T].f + |} + """.stripMargin val List(_, _, c, _) = compileClasses(code) - // even though the bytecode refers to A.m, invokespecial will resolve to B.m - assertSameCode(getMethod(c, "m"), - List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "A", "m", "()I", false), Op(IRETURN))) + val t1 = getInstructions(c, "t1") + assert(t1 contains Invoke(INVOKESPECIAL, "A", "f", "()I", false), t1.stringLines) + val t2 = getInstructions(c, "t2") + val invStat = Invoke(INVOKESTATIC, "T", "f$", "(LT;)I", true) + assert(t2 contains invStat, t2.stringLines) + val t3 = getInstructions(c, "t3") + assert(t3 contains invStat, t3.stringLines) + } + + @Test + def sd210(): Unit = { + val forwardersCompiler = newCompiler(extraArgs = "-Xmixin-force-forwarders:true") + val jCode = List("interface A { default int m() { return 1; } }" -> "A.java") + + + // used to crash in the backend (SD-210) under `-Xmixin-force-forwarders:true` + val code1 = + """trait B1 extends A // called "B1" not "B" due to scala-dev#214 + |class C extends B1 + """.stripMargin + + val List(_, c1a) = compileClasses(code1, jCode) + assert(getAsmMethods(c1a, "m").isEmpty) // ok, no forwarder + + // here we test a warning. without `-Xmixin-force-forwarders:true`, the forwarder would not be + // generated, it is not necessary for correctness. + val warn = "Unable to implement a mixin forwarder for method m in class C unless interface A is directly extended by class C" + val List(_, c1b) = forwardersCompiler.compileClasses(code1, jCode, allowMessage = _.msg.contains(warn)) + assert(getAsmMethods(c1a, "m").isEmpty) // no forwarder + + + val code2 = + """abstract class B { def m(): Int } + |trait T extends B with A + |class C extends T + """.stripMargin + + // here we test a compilation error. the forwarder is required for correctness, but it cannot be generated. + val err = "Unable to implement a mixin forwarder for method m in class C unless interface A is directly extended by class C" + val cs = compileClasses(code2, jCode, allowMessage = _.msg contains err) + assert(cs.isEmpty, cs.map(_.name)) + + + val code3 = + """abstract class B { def m: Int } + |class C extends B with A + """.stripMargin + + val List(_, c3) = compileClasses(code3, jCode) + // invokespecial to A.m is correct here: A is an interface, so resolution starts at A. + // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial + val ins3 = getMethod(c3, "m").instructions + assert(ins3 contains Invoke(INVOKESPECIAL, "A", "m", "()I", true), ins3.stringLines) + + + val code4 = + """trait B { self: A => override def m = 2 } + |class C extends A with B // forwarder, invokestatic B.m$ + """.stripMargin + + val List(_, c4) = compileClasses(code4, jCode) + val ins4 = getMethod(c4, "m").instructions + assert(ins4 contains Invoke(INVOKESTATIC, "B", "m$", "(LB;)I", true), ins4.stringLines) + + + // scala-only example + val code5 = + """trait AS { def m = 1 } + |abstract class B { def m: Int } + |class C extends B with AS // forwarder, invokestatic AS.m$ + """.stripMargin + + val List(_, _, c5) = compileClasses(code5) + val ins5 = getMethod(c5, "m").instructions + assert(ins5 contains Invoke(INVOKESTATIC, "AS", "m$", "(LAS;)I", true), ins5.stringLines) } } diff --git a/test/junit/scala/reflect/internal/PrintersTest.scala b/test/junit/scala/reflect/internal/PrintersTest.scala index 234f22e9fb..722062ba21 100644 --- a/test/junit/scala/reflect/internal/PrintersTest.scala +++ b/test/junit/scala/reflect/internal/PrintersTest.scala @@ -151,7 +151,7 @@ class BasePrintTest { |else | ((a.toString): String)""", typedCode=sm""" - |val a: Int = 1; + |val a = 1; |if (PrintersContext.this.a.>(1)) | ((PrintersContext.this.a): scala.Int) |else @@ -864,7 +864,7 @@ class TraitPrintTest { @Test def testTraitWithSelf2 = assertPrintedCode(sm""" |trait X { self: scala.Cloneable with scala.Serializable => - | val x: Int = 1 + | val x: scala.Int = 1 |}""") @Test def testTraitTypeParams = assertPrintedCode("trait X[A, B]") @@ -903,7 +903,7 @@ class TraitPrintTest { | type Foo; | type XString = scala.Predef.String |} with scala.Serializable { - | val z: Int = 7 + | val z: scala.Int = 7 |}""") @Test def testTraitWithSingletonTypeTree = assertPrintedCode(sm""" @@ -1008,27 +1008,16 @@ class ValAndDefPrintTest { @Test def testDef9 = assertPrintedCode("def a(x: scala.Int)(implicit z: scala.Double, y: scala.Float): scala.Unit = ()") - @Test def testDefWithLazyVal1 = assertResultCode( - code = "def a = { lazy val test: Int = 42 }")( - parsedCode = sm""" + @Test def testDefWithLazyVal1 = assertPrintedCode(sm""" |def a = { - | lazy val test: Int = 42; + | lazy val test: scala.Int = 42; | () |} - """, - typedCode = sm""" - |def a = { - | lazy val test$$lzy: scala.Int = _; - | lazy val test: Int = { - | test$$lzy = 42; - | test$$lzy - | }; - | () - |}""") + """) @Test def testDefWithLazyVal2 = assertPrintedCode(sm""" |def a = { - | lazy val test: Unit = { + | lazy val test: scala.Unit = { | scala.Predef.println(); | scala.Predef.println() | }; diff --git a/test/junit/scala/runtime/LambdaDeserializerTest.java b/test/junit/scala/runtime/LambdaDeserializerTest.java index 3ed1ae1365..4e9c5c8954 100644 --- a/test/junit/scala/runtime/LambdaDeserializerTest.java +++ b/test/junit/scala/runtime/LambdaDeserializerTest.java @@ -136,6 +136,7 @@ public final class LambdaDeserializerTest { } } + @SuppressWarnings("unchecked") private <A> A deserizalizeLambdaCreatingAllowedMap(A f1, HashMap<String, MethodHandle> cache, MethodHandles.Lookup lookup) { SerializedLambda serialized = writeReplace(f1); HashMap<String, MethodHandle> allowed = createAllowedMap(lookup, serialized); diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala index 5bd2ce68f1..85b44d9fa0 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala @@ -57,7 +57,7 @@ class InlineWarningTest extends BytecodeTesting { assert(c == 1, c) } - @Test +// @Test -- TODO def mixedWarnings(): Unit = { val javaCode = """public class A { diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala index 29a23df784..9999cdb376 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -726,7 +726,7 @@ class InlinerTest extends BytecodeTesting { """sealed trait T { | lazy val a = 0 | val b = 1 - | final lazy val c = 2 + | final lazy val c: Int = 2 // make sure it doesn't get a constant type | final val d = 3 | final val d1: Int = 3 | @@ -740,7 +740,7 @@ class InlinerTest extends BytecodeTesting { |trait U { // not sealed | lazy val a = 0 | val b = 1 - | final lazy val c = 2 + | final lazy val c: Int = 2 // make sure it doesn't get a constant type | final val d = 3 | final val d1: Int = 3 | @@ -766,7 +766,7 @@ class InlinerTest extends BytecodeTesting { val m1 = getMethod(c, "m1") assertInvoke(m1, "T", "a") assertInvoke(m1, "T", "b") - assertInvoke(m1, "T", "c") +// assertInvoke(m1, "T", "c") -- this lazy val is implemented purely in the trait, as it's constant, so it *can* be inlined assertNoInvoke(getMethod(c, "m2")) @@ -779,7 +779,7 @@ class InlinerTest extends BytecodeTesting { val m4 = getMethod(c, "m4") assertInvoke(m4, "U", "a") assertInvoke(m4, "U", "b") - assertInvoke(m4, "U", "c") +// assertInvoke(m4, "U", "c") -- this lazy val is implemented purely in the trait, as it's constant, so it *can* be inlined assertNoInvoke(getMethod(c, "m5")) diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala index 56da0e2493..eae5385147 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala @@ -105,7 +105,6 @@ class ScalaInlineInfoTest extends BytecodeTesting { ("x4$(LT;)I", MethodInlineInfo(true ,false,false)), ("x5()I", MethodInlineInfo(true, false,false)), ("x5$(LT;)I", MethodInlineInfo(true ,false,false)), - ("L$lzycompute$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)), ("L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)), ("nest$1()I", MethodInlineInfo(true, false,false)), ("$init$(LT;)V", MethodInlineInfo(true,false,false))), @@ -118,7 +117,6 @@ class ScalaInlineInfoTest extends BytecodeTesting { val infoC = inlineInfo(c) val expectC = InlineInfo(false, None, Map( "O()LT$O$;" -> MethodInlineInfo(true ,false,false), - "O$lzycompute()LT$O$;" -> MethodInlineInfo(true, false,false), "f6()I" -> MethodInlineInfo(false,false,false), "x1()I" -> MethodInlineInfo(false,false,false), "T$_setter_$x1_$eq(I)V" -> MethodInlineInfo(false,false,false), @@ -181,7 +179,6 @@ class ScalaInlineInfoTest extends BytecodeTesting { val infoC = inlineInfo(c) val expected = Map( "<init>()V" -> MethodInlineInfo(false,false,false), - "O$lzycompute()LC$O$;" -> MethodInlineInfo(true,false,false), "O()LC$O$;" -> MethodInlineInfo(true,false,false)) assert(infoC.methodInfos == expected, mapDiff(infoC.methodInfos, expected)) assertSameMethods(c, expected.keySet) diff --git a/test/junit/scala/tools/testing/ClearAfterClass.java b/test/junit/scala/tools/testing/ClearAfterClass.java index 95e170ec13..7f87f9a4d7 100644 --- a/test/junit/scala/tools/testing/ClearAfterClass.java +++ b/test/junit/scala/tools/testing/ClearAfterClass.java @@ -45,6 +45,7 @@ public class ClearAfterClass { } } + @SuppressWarnings("unchecked") public <T> T cached(String key, scala.Function0<T> t) { Map<String, Object> perClassCache = cache.get(getClass()); return (T) perClassCache.computeIfAbsent(key, s -> t.apply()); diff --git a/test/scaladoc/run/t7767.scala b/test/scaladoc/run/t7767.scala index 6c9ceb511d..433fc5c0c4 100644 --- a/test/scaladoc/run/t7767.scala +++ b/test/scaladoc/run/t7767.scala @@ -4,15 +4,49 @@ import scala.tools.partest.ScaladocModelTest object Test extends ScaladocModelTest { override def code = """ - class Docable extends { /**Doc*/ val foo = 0 } with AnyRef - """ + class CEarly extends { /**CEarly_Doc_foo*/ val foo = 0 } with AnyRef + trait TEarly extends { /**TEarly_Doc_foo*/ val foo = 0 } with AnyRef + class C { + /**C_Doc_sigInferred*/ val sigInferred = 0 + /**C_Doc_const*/ final val const = 0 + /**C_Doc_varr*/ var varr: Any = null + /**C_Doc_abs*/ val abs: Int + /**C_Doc_absVar*/ var absVar: Any + /**C_Doc_lazyValInferred*/ lazy val lazyValInferred = 0 + /**C_Doc_lazyValConst*/ final lazy val lazyValConst = 0 + /**C_Doc_lazyValUnit*/ lazy val lazyValUnit: Unit = println() + /**C_Doc_lazyVal*/ lazy val lazyVal: Int = 0 + } + trait T { + /**T_Doc_sigInferred*/ val sigInferred = 0 + /**T_Doc_const*/ final val const = 0 + /**T_Doc_varr*/ var varr: Any = null + /**T_Doc_abs*/ val abs: Int + /**T_Doc_absVar*/ var absVar: Any + /**T_Doc_lazyValInferred*/ lazy val lazyValInferred = 0 + /**T_Doc_lazyValConst*/ final lazy val lazyValConst = 0 + /**T_Doc_lazyValUnit*/ lazy val lazyValUnit: Unit = println() + /**T_Doc_lazyVal*/ lazy val lazyVal: Int = 0 + }""" // no need for special settings def scaladocSettings = "" + def assertDoc(classEntity: DocTemplateEntity, valName: String) = { + import access._ + val comment = classEntity._value(valName).comment.map(_.body.toString.trim).getOrElse("") + val className = classEntity.name + val marker = s"${className}_Doc_${valName}" + assert(comment.contains(marker), s"Expected $marker in comment for $valName in $className, found: $comment.") + } + def testModel(rootPackage: Package) = { import access._ - val comment = rootPackage._class("Docable")._value("foo").comment.map(_.body.toString.trim).getOrElse("") - assert(comment.contains("Doc"), comment) + assertDoc(rootPackage._class("CEarly"), "foo") + assertDoc(rootPackage._trait("TEarly"), "foo") + + val valNames = List("sigInferred", "const", "varr", "abs", "absVar", "lazyValInferred", "lazyValConst", "lazyValUnit", "lazyVal") + val entities = List(rootPackage._class("C"), rootPackage._trait("T")) + for (e <- entities; vn <- valNames) assertDoc(e, vn) } } diff --git a/tools/binary-repo-lib.sh b/tools/binary-repo-lib.sh deleted file mode 100755 index 278804e30e..0000000000 --- a/tools/binary-repo-lib.sh +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env bash -# -# Library to push and pull binary artifacts from a remote repository using CURL. - -remote_urlget="https://dl.bintray.com/typesafe/scala-sha-bootstrap/org/scala-lang/bootstrap" -remote_urlpush="https://dl.bintray.com/typesafe/scala-sha-bootstrap/org/scala-lang/bootstrap" -libraryJar="$(pwd)/lib/scala-library.jar" -desired_ext=".desired.sha1" -push_jar="$(pwd)/tools/push.jar" - -if [[ "$OSTYPE" == *Cygwin* || "$OSTYPE" == *cygwin* ]]; then push_jar="$(cygpath -m "$push_jar")"; fi -# Cache dir has .sbt in it to line up with SBT build. -SCALA_BUILD_REPOS_HOME=${SCALA_BUILD_REPOS_HOME:=$HOME} -cache_dir="${SCALA_BUILD_REPOS_HOME}/.sbt/cache/scala" - -# Checks whether or not curl is installed and issues a warning on failure. -checkCurl() { - if ! which curl >/dev/null; then - cat <<EOM -No means of downloading or uploading binary artifacts found. - -Please install curl for your OS. e.g. -* sudo apt-get install curl -* brew install curl -EOM - fi -} - -# Executes the `curl` command to publish artifacts into a maven/ivy repository. -# Arugment 1 - The location to publish the file. -# Argument 2 - The file to publish. -# Argument 3 - The user to publish as. -# Argument 4 - The password for the user. -curlUpload() { - checkCurl - local remote_location=$1 - local data=$2 - local user=$3 - local password=$4 - local url="${remote_urlpush}/${remote_location}" - java -jar $push_jar "$data" "$url" "$user" "$password" - if (( $? != 0 )); then - echo "Error uploading $data to $url" - echo "$url" - exit 1 - fi -} - -# Executes the `curl` command to download a file. -# Argument 1 - The location to store the file. -# Argument 2 - The URL to download. -curlDownload() { - checkCurl - local jar=$1 - local url=$2 - if [[ "$OSTYPE" == *Cygwin* || "$OSTYPE" == *cygwin* ]]; then - jar=$(cygpath -m $1) - fi - http_code=$(curl --write-out '%{http_code}' --silent --fail -L --output "$jar" "$url") - if (( $? != 0 )); then - echo "Error downloading $jar: response code: $http_code" - echo "$url" - exit 1 - fi -} - -# Pushes a local JAR file to the remote repository and updates the .desired.sha1 file. -# Argument 1 - The JAR file to update. -# Argument 2 - The root directory of the project. -# Argument 3 - The user to use when publishing artifacts. -# Argument 4 - The password to use when publishing artifacts. -pushJarFile() { - local jar=$1 - local basedir=$2 - local user=$3 - local pw=$4 - local jar_dir=$(dirname $jar) - local jar_name=${jar#$jar_dir/} - pushd $jar_dir >/dev/null - local version=$(makeJarSha $jar_name) - local remote_uri=${version}${jar#$basedir} - echo " Pushing to ${remote_urlpush}/${remote_uri} ..." - echo " $curl" - curlUpload $remote_uri $jar_name $user $pw - echo " Making new sha1 file ...." - echo "$version ?$jar_name" > "${jar_name}${desired_ext}" - popd >/dev/null - # TODO - Git remove jar and git add jar.desired.sha1 - # rm $jar -} - -makeJarSha() { - local jar=$1 - if which sha1sum 2>/dev/null >/dev/null; then - shastring=$(sha1sum "$jar") - echo "$shastring" | sed 's/ .*//' - elif which shasum 2>/dev/null >/dev/null; then - shastring=$(shasum "$jar") - echo "$shastring" | sed 's/ .*//' - else - shastring=$(openssl sha1 "$jar") - echo "$shastring" | sed 's/^.*= //' - fi -} - -getJarSha() { - local jar=$1 - if [[ ! -f "$jar" ]]; then - echo "" - else - echo $(makeJarSha $jar) - fi -} - -# Tests whether or not the .desired.sha1 hash matches a given file. -# Arugment 1 - The jar file to test validity. -# Returns: Empty string on failure, "OK" on success. -isJarFileValid() { - local jar=$1 - if [[ ! -f "$jar" ]]; then - echo "" - else - local jar_dir=$(dirname $jar) - local jar_name=${jar#$jar_dir/} - pushd $jar_dir >/dev/null - local valid=$(shasum -p --check ${jar_name}${desired_ext} 2>/dev/null) - echo "${valid#$jar_name: }" - popd >/dev/null - fi -} - -# Pushes any jar file in the local repository for which the corresponding SHA1 hash is invalid or nonexistent. -# Argument 1 - The base directory of the project. -# Argument 2 - The user to use when pushing artifacts. -# Argument 3 - The password to use when pushing artifacts. -pushJarFiles() { - local basedir=$1 - local user=$2 - local password=$3 - # TODO - ignore target/ and build/ - local jarFiles="$(find ${basedir}/lib -name "*.jar") $(find ${basedir}/test/files -name "*.jar") $(find ${basedir}/tools -name "*.jar")" - local changed="no" - for jar in $jarFiles; do - local valid=$(isJarFileValid $jar) - if [[ "$valid" != "OK" ]]; then - echo "$jar has changed, pushing changes...." - changed="yes" - pushJarFile $jar $basedir $user $password - fi - done - if test "$changed" == "no"; then - echo "No jars have been changed." - else - echo "Binary changes have been pushed. You may now submit the new *${desired_ext} files to git." - fi -} - - -checkJarSha() { - local jar=$1 - local sha=$2 - local testsha=$(getJarSha "$jar") - if test "$sha" == "$testsha"; then - echo "OK" - fi -} - -makeCacheLocation() { - local uri=$1 - local sha=$2 - local cache_loc="$cache_dir/$uri" - local cdir=$(dirname $cache_loc) - if [[ ! -d "$cdir" ]]; then - mkdir -p "$cdir" - fi - echo "$cache_loc" -} - -# Pulls a single binary artifact from a remote repository. -# Argument 1 - The uri to the file that should be downloaded. -# Argument 2 - SHA of the file... -# Returns: Cache location. -pullJarFileToCache() { - local uri=$1 - local sha=$2 - local cache_loc="$(makeCacheLocation $uri)" - # TODO - Check SHA of local cache is accurate. - if test -f "$cache_loc" && test "$(checkJarSha "$cache_loc" "$sha")" != "OK"; then - echo "Found bad cached file: $cache_loc" - rm -f "$cache_loc" - fi - if [[ ! -f "$cache_loc" ]]; then - # Note: After we follow up with JFrog, we should check the more stable raw file server first - # before hitting the more flaky artifactory. - curlDownload $cache_loc ${remote_urlget}/${uri} - if test "$(checkJarSha "$cache_loc" "$sha")" != "OK"; then - echo "Trouble downloading $uri. Please try pull-binary-libs again when your internet connection is stable." - exit 2 - fi - fi -} - -# Pulls a single binary artifact from a remote repository. -# Argument 1 - The jar file that needs to be downloaded. -# Argument 2 - The root directory of the project. -pullJarFile() { - local jar=$1 - local basedir=$2 - local sha1=$(cat ${jar}${desired_ext}) - local jar_dir=$(dirname $jar) - local jar_name=${jar#$jar_dir/} - local version=${sha1%% *} - local remote_uri=${version}/${jar#$basedir/} - echo "Resolving [${remote_uri}]" - pullJarFileToCache $remote_uri $version - local cached_file=$(makeCacheLocation $remote_uri) - cp $cached_file $jar -} - -# Pulls binary artifacts from the remote repository. -# Argument 1 - The directory to search for *.desired.sha1 files that need to be retrieved. -pullJarFiles() { - local basedir=$1 - local desiredFiles="$(find ${basedir}/lib -name *${desired_ext}) $(find ${basedir}/test/files -name *${desired_ext}) $(find ${basedir}/tools -name *${desired_ext})" - for sha in $desiredFiles; do - jar=${sha%$desired_ext} - local valid=$(isJarFileValid $jar) - if [[ "$valid" != "OK" ]]; then - pullJarFile $jar $basedir - fi - done -} - - diff --git a/tools/deploy-local-maven-snapshot b/tools/deploy-local-maven-snapshot deleted file mode 100755 index 30f78cb110..0000000000 --- a/tools/deploy-local-maven-snapshot +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# -# Install the -SNAPSHOT artifacts in the local maven cache. - -set -e - -cd $(dirname $0)/.. - -ant fastdist distpack -cd dists/maven/latest -ant deploy.snapshot.local diff --git a/tools/new-starr b/tools/new-starr deleted file mode 100755 index 5f00cc758e..0000000000 --- a/tools/new-starr +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -ant -Dscalac.args.optimise="-optimise" locker.done -cp -R src/starr/* src/ -ant build-opt -ant starr.done diff --git a/tools/push.jar.desired.sha1 b/tools/push.jar.desired.sha1 deleted file mode 100644 index 63e6a47372..0000000000 --- a/tools/push.jar.desired.sha1 +++ /dev/null @@ -1 +0,0 @@ -a1883f4304d5aa65e1f6ee6aad5900c62dd81079 ?push.jar |