diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | build.xml | 2 | ||||
-rw-r--r-- | doc/LICENSE.md | 4 | ||||
-rw-r--r-- | doc/License.rtf | 4 | ||||
-rw-r--r-- | project/VersionUtil.scala | 37 | ||||
-rw-r--r-- | scripts/jobs/integrate/bootstrap | 135 | ||||
-rw-r--r-- | spec/05-classes-and-objects.md | 20 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaVersion.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 95 | ||||
-rw-r--r-- | src/library/scala/util/Properties.scala | 2 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/tpe/FindMembers.scala | 14 | ||||
-rw-r--r-- | src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala | 2 | ||||
-rw-r--r-- | src/scalap/decoder.properties | 2 | ||||
-rw-r--r-- | test/files/neg/userdefined_apply.check | 25 | ||||
-rw-r--r-- | test/files/neg/userdefined_apply.scala | 57 | ||||
-rw-r--r-- | test/files/pos/userdefined_apply.scala | 54 |
16 files changed, 353 insertions, 104 deletions
@@ -230,7 +230,7 @@ tested version during CI validation. The Scala CI builds nightly download releases (including all modules) and publishes them to the following locations: - [2.12.x](http://www.scala-lang.org/files/archive/nightly/2.12.x/?C=M;O=D) - - [2.11.x](http://www.scala-lang.org/files/archive/nightly/2.11.x/?C=M;O=A) + - [2.11.x](http://www.scala-lang.org/files/archive/nightly/2.11.x/?C=M;O=D) The CI also publishes nightly API docs: - [2.12.x](http://www.scala-lang.org/files/archive/nightly/2.12.x/api/?C=M;O=D) @@ -184,7 +184,7 @@ TODO: <property name="dists.dir" value="${basedir}/dists"/> - <property name="copyright.string" value="Copyright 2002-2016, LAMP/EPFL"/> + <property name="copyright.string" value="Copyright 2002-2017, 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 --> diff --git a/doc/LICENSE.md b/doc/LICENSE.md index c56b38daa2..ce29d7e7d4 100644 --- a/doc/LICENSE.md +++ b/doc/LICENSE.md @@ -2,9 +2,9 @@ Scala is licensed under the [BSD 3-Clause License](http://opensource.org/license ## Scala License -Copyright (c) 2002-2016 EPFL +Copyright (c) 2002-2017 EPFL -Copyright (c) 2011-2016 Lightbend, Inc. +Copyright (c) 2011-2017 Lightbend, Inc. All rights reserved. diff --git a/doc/License.rtf b/doc/License.rtf index c8f24838a2..adc7dfdcb8 100644 --- a/doc/License.rtf +++ b/doc/License.rtf @@ -10,8 +10,8 @@ \fs48 Scala License \fs40 \ -\fs26 Copyright (c) 2002-2016 EPFL\ -Copyright (c) 2011-2016 Lightbend, Inc.\ +\fs26 Copyright (c) 2002-2017 EPFL\ +Copyright (c) 2011-2017 Lightbend, Inc.\ All rights reserved.\ \ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\ diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index 4705bbb6ce..3f9b727ef0 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -18,7 +18,7 @@ object VersionUtil { ) lazy val generatePropertiesFileSettings = Seq[Setting[_]]( - copyrightString := "Copyright 2002-2016, LAMP/EPFL", + copyrightString := "Copyright 2002-2017, LAMP/EPFL and Lightbend, Inc.", resourceGenerators in Compile += generateVersionPropertiesFile.map(file => Seq(file)).taskValue, generateVersionPropertiesFile := generateVersionPropertiesFileImpl.value ) @@ -45,17 +45,19 @@ object VersionUtil { /** Compute the canonical, Maven and OSGi version number from `baseVersion` and `baseVersionSuffix`. * Examples of the generated versions: * - * ("2.11.8", "SNAPSHOT" ) -> ("2.11.8-20151215-133023-7559aed3c5", "2.11.8-SNAPSHOT", "2.11.8.v20151215-133023-7559aed3c5") - * ("2.11.8", "SHA-SNAPSHOT") -> ("2.11.8-20151215-133023-7559aed3c5", "2.11.8-7559aed3c5-SNAPSHOT", "2.11.8.v20151215-133023-7559aed3c5") - * ("2.11.8", "" ) -> ("2.11.8", "2.11.8", "2.11.8.v20151215-133023-VFINAL-7559aed3c5") - * ("2.11.8", "M3" ) -> ("2.11.8-M3", "2.11.8-M3", "2.11.8.v20151215-133023-M3-7559aed3c5") - * ("2.11.8", "RC4" ) -> ("2.11.8-RC4", "2.11.8-RC4", "2.11.8.v20151215-133023-RC4-7559aed3c5") - * ("2.11.8-RC4", "SPLIT" ) -> ("2.11.8-RC4", "2.11.8-RC4", "2.11.8.v20151215-133023-RC4-7559aed3c5") + * ("2.11.8", "SNAPSHOT" ) -> ("2.11.8-20151215-133023-7559aed", "2.11.8-bin-SNAPSHOT", "2.11.8.v20151215-133023-7559aed") + * ("2.11.8", "SHA-SNAPSHOT") -> ("2.11.8-20151215-133023-7559aed", "2.11.8-bin-7559aed-SNAPSHOT", "2.11.8.v20151215-133023-7559aed") + * ("2.11.8", "SHA" ) -> ("2.11.8-7559aed", "2.11.8-bin-7559aed", "2.11.8.v20151215-133023-7559aed") + * ("2.11.0", "SHA" ) -> ("2.11.0-7559aed", "2.11.0-pre-7559aed", "2.11.0.v20151215-133023-7559aed") + * ("2.11.8", "" ) -> ("2.11.8", "2.11.8", "2.11.8.v20151215-133023-VFINAL-7559aed") + * ("2.11.8", "M3" ) -> ("2.11.8-M3", "2.11.8-M3", "2.11.8.v20151215-133023-M3-7559aed") + * ("2.11.8", "RC4" ) -> ("2.11.8-RC4", "2.11.8-RC4", "2.11.8.v20151215-133023-RC4-7559aed") + * ("2.11.8-RC4", "SPLIT" ) -> ("2.11.8-RC4", "2.11.8-RC4", "2.11.8.v20151215-133023-RC4-7559aed") * * A `baseVersionSuffix` of "SNAPSHOT" is the default, which is used for local snapshot builds. The PR validation - * job uses "SHA-SNAPSHOT". An empty suffix is used for releases. All other suffix values are treated as RC / - * milestone builds. The special suffix value "SPLIT" is used to split the real suffix off from `baseVersion` - * instead and then apply the usual logic. */ + * job uses "SHA-SNAPSHOT". A proper version number for an integration build can be computed with "SHA". An empty + * suffix is used for releases. All other suffix values are treated as RC / milestone builds. The special suffix + * value "SPLIT" is used to split the real suffix off from `baseVersion` instead and then apply the usual logic. */ private lazy val versionPropertiesImpl: Def.Initialize[Versions] = Def.setting { val (base, suffix) = { @@ -78,11 +80,18 @@ object VersionUtil { val date = executeTool("get-scala-commit-date") val sha = executeTool("get-scala-commit-sha").substring(0, 7) // The script produces 10 digits at the moment + val Patch = """\d+\.\d+\.(\d+)""".r + def cross = base match { + case Patch(p) if p.toInt > 0 => "bin" + case _ => "pre" + } + val (canonicalV, mavenV, osgiV, release) = suffix match { - case "SNAPSHOT" => (s"$base-$date-$sha", s"$base-SNAPSHOT", s"$base.v$date-$sha", false) - case "SHA-SNAPSHOT" => (s"$base-$date-$sha", s"$base-$sha-SNAPSHOT", s"$base.v$date-$sha", false) - case "" => (s"$base", s"$base", s"$base.v$date-VFINAL-$sha", true) - case suffix => (s"$base-$suffix", s"$base-$suffix", s"$base.v$date-$suffix-$sha", true) + case "SNAPSHOT" => (s"$base-$date-$sha", s"$base-$cross-SNAPSHOT", s"$base.v$date-$sha", false) + case "SHA-SNAPSHOT" => (s"$base-$date-$sha", s"$base-$cross-$sha-SNAPSHOT", s"$base.v$date-$sha", false) + case "SHA" => (s"$base-$sha", s"$base-$cross-$sha", s"$base.v$date-$sha", false) + case "" => (s"$base", s"$base", s"$base.v$date-VFINAL-$sha", true) + case suffix => (s"$base-$suffix", s"$base-$suffix", s"$base.v$date-$suffix-$sha", true) } Versions(canonicalV, mavenV, osgiV, sha, date, release) diff --git a/scripts/jobs/integrate/bootstrap b/scripts/jobs/integrate/bootstrap index abb5b283c6..85be794276 100644 --- a/scripts/jobs/integrate/bootstrap +++ b/scripts/jobs/integrate/bootstrap @@ -3,16 +3,16 @@ # Script Overview # - determine scala version # - determine module versions -# - build minimal core (aka locker) of Scala, use the determined version number, publish to private-repo -# - build those modules where a binary compatible version doesn't exist, publish to private-repo -# - build Scala using the previously built core and bootstrap modules, publish to private-repo (overwrites the minimal core version on private-repo) -# - for releases (not nightlies) +# - build minimal core (aka locker) of Scala, use the determined version number, publish to scala-integration +# - build those modules where a binary compatible version doesn't exist, publish to scala-integration +# - build Scala using the previously built core and bootstrap modules, publish to scala-integration, overwriting +# the existing artifacts +# - for releases # - stage Scala on sonatype # - rebuild modules that needed a rebuild with this Scala build, and stage them on sonatype # - for nightlies # - force rebuild all modules and publish them locally (for testing purposes) # - the Scala version is serialized to jenkins.properties, which is passed downstream to scala-release jobs -# - this removes the need to tag scala/scala-dist (it's still encouraged for releases, but not a hard requirement) # Specifying the Scala version: @@ -23,8 +23,8 @@ # - Note: After building a release, the jenkins job provides an updated versions.properties file as artifact. # Put this file in the Scala repo and create a pull request, and also update the file build.number. # -# - Otherwise, a nightly release is built: -# - version number is read from the build.number file, extended with -$sha-nightly +# - Otherwise, an integration build is performed: +# - version number is read from the build.number file, extended with -[bin|pre]-$sha # Specifying module versions: there are two modes @@ -56,7 +56,7 @@ # to be re-built using the 2.11.1 release, we could not use 2.11.0. We could also not release the modules # after 2.11.1 was out, because that way the scala-library-all pom of 2.11.1 would depend on the old modules. # -# (*) https://github.com/sbt/sbt/blob/0.13.8/util/cross/src/main/input_sources/CrossVersionUtil.scala#L39 +# (*) https://github.com/sbt/sbt/blob/v0.13.13/util/cross/src/main/input_sources/CrossVersionUtil.scala#L41 # Binary incompatible changes in Modules: example with Scala 2.11 / 2.12 and scala-parser-combinators @@ -107,24 +107,30 @@ mkdir -p $baseDir/ivy2 rm -rf $baseDir/resolutionScratch_ mkdir -p $baseDir/resolutionScratch_ -# repo used to publish "locker" scala to (to start the bootstrap) -releaseTempRepoCred="private-repo" -releaseTempRepoUrl=${releaseTempRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-release-temp/"} -jcenterCacheUrl=${jcenterCacheUrl-"https://scala-ci.typesafe.com/artifactory/jcenter/"} - -# Used below in sbtArgs since we use a dedicated repository to share artifcacts between jobs, -# so we need to configure SBT to use these rather than its default, Maven Central. -# See http://www.scala-sbt.org/0.13/docs/Proxy-Repositories.html -sbtRepositoryConfig="$scriptsDir/repositories-scala-release" -cat > "$sbtRepositoryConfig" << EOF +function generateRepositoriesConfig(){ + # Used below in sbtArgs since we use a dedicated repository to share artifcacts between jobs, + # so we need to configure SBT to use these rather than its default, Maven Central. + # See http://www.scala-sbt.org/0.13/docs/Proxy-Repositories.html + sbtRepositoryConfig="$scriptsDir/repositories-scala-release" + jcenterCacheUrl=${jcenterCacheUrl-"https://scala-ci.typesafe.com/artifactory/jcenter/"} + cat > "$sbtRepositoryConfig" << EOF [repositories] - private-repo: $releaseTempRepoUrl + script-repo: $1 jcenter-cache: $jcenterCacheUrl typesafe-ivy-releases: https://dl.bintray.com/typesafe/ivy-releases/, [organisation]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly sbt-plugin-releases: https://dl.bintray.com/sbt/sbt-plugin-releases/, [organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] maven-central local EOF +} + +integrationRepoCred="private-repo" + +# repo for locker, quick and the modules +integrationRepoUrl=${integrationRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-integration/"} + +# adding `integrationRepoUrl` to find the locker scala version when building modules +generateRepositoriesConfig $integrationRepoUrl ##### git gfxd() { @@ -198,15 +204,15 @@ sbtResolve() { # then set the version to the right one and publish (which won't re-gen the docs). # Also tried publish-local without docs using 'set publishArtifact in (Compile, packageDoc) := false' and republishing, no dice. -# Each buildModule() function is invoked twice: first to build against locker and publish to private-repo, then +# Each buildModule() function is invoked twice: first to build against locker and publish to artifactory, then # to build against the release and publish to sonatype (or publish-local if publishToSonatype is not "yes"). -# In the second round, sbtResolve is always true: the module will be found in the private-repo! +# In the second round, sbtResolve is always true: the module will be found in the artifactory! # Therefore, if MODULE_BUILT is "yes" (in the second round), we know that we need to build (and publish) the # module again. # -# Note: we tried an alternative solution in which sbtResolve would not look at private-repo, but that fails. For example, +# Note: we tried an alternative solution in which sbtResolve would not look at artifactory, but that fails. For example, # scala-xml depends on scala-library, so sbt tries to find the scala-library of the version that we are currently building, -# which exists only in private-repo. +# which exists only in artifactory. buildXML() { if [ "$XML_BUILT" != "yes" ] && [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scala-lang.modules" "scala-xml" $XML_VER ) @@ -281,7 +287,7 @@ buildActorsMigration(){ fi } -# should only be called with publishTasks publishing to private-repo +# should only be called with publishTasks publishing to artifactory buildScalacheck(){ if [ "$SCALACHECK_BUILT" != "yes" ] && [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scalacheck" "scalacheck" $SCALACHECK_VER ) then echo "Found scalacheck $SCALACHECK_VER; not building." @@ -292,9 +298,9 @@ buildScalacheck(){ fi } -# build modules, using ${buildTasks[@]} (except for Scalacheck, which is hard-coded to publish to private-repo) +# build modules, using ${buildTasks[@]} (except for Scalacheck, which is hard-coded to publish to artifactory) buildModules() { - publishTasks=('set credentials += Credentials(Path.userHome / ".credentials-private-repo")' "set every publishTo := Some(\"private-repo\" at \"$releaseTempRepoUrl\")") + publishTasks=('set credentials += Credentials(Path.userHome / ".credentials-private-repo")' "set every publishTo := Some(\"publish-repo\" at \"$integrationRepoUrl\")") buildTasks=($publishPrivateTask) buildXML buildParsers @@ -329,20 +335,19 @@ scalaVerToBinary() { local patch="$(echo $2 | sed -e "s#$RE#\3#")" # The binary version is majMin (e.g. "2.12") if - # - there's no suffix : 2.12.0, 2.12.1 - # - the suffix starts with "-bin": 2.12.0-bin-M1 - # - the patch version is > 0 : 2.12.1-M1, 1.12.3-RC2, 2.12.1-sha-nightly, 2.12.2-SNAPSHOT + # - there's no suffix : 2.12.0, 2.12.1 + # - the suffix starts with "-bin" : 2.12.1-bin-sha, 2.12.1-bin-sha-custom, 2.12.1-bin-SNAPSHOT + # - the suffix is \w+ and patch version is > 0: 2.12.1-M1, 2.12.1-RC2 (also 2.12.1-sha, 2.12.1-SNAPSHOT, which we don't use) # - # Otherwise, the binary version is the full version: 2.12.0-M1, 2.12.0-RC2, 2.12.0-sha-nightly, 2.12.0-SNAPSHOT + # Otherwise, the binary version is the full version: 2.12.0-M1, 2.12.0-RC2, 2.12.0-pre-sha, 2.12.0-pre-SNAPSHOT + # (also 2.12.0-sha, 2.12.0-SNAPSHOT, which we don't use) # - # Adapted from sbt: https://github.com/sbt/sbt/blob/0.13.8/util/cross/src/main/input_sources/CrossVersionUtil.scala#L39 + # Adapted from sbt: https://github.com/sbt/sbt/blob/v0.13.13/util/cross/src/main/input_sources/CrossVersionUtil.scala#L42 # - # Note: during the pre-release cycle of a major release (e.g. before 2.12.0), the SCALA_BINARY_VER of nightly / SNAPSHOT - # versions is the full version, e.g. 2.12.0-sha-nightly, so modules are always re-built. This is in line with what sbt - # does: for example, with scalaVersion := "2.12.0-SNAPSHOT", sbt will resolve scala-xml as scala-xml_2.12.0-SNAPSHOT. - # Once the 2.12.0 release is out, the binary version is 2.12 for all versions (e.g. for 2.12.1-sha-nightly). + # During the pre-release cycle of a major release (e.g. before 2.12.0), the SCALA_BINARY_VER of integration / SNAPSHOT + # versions is the full version, e.g. 2.12.0-pre-sha, so modules are always re-built. - if [[ "$3" == "" || "${3:0:4}" == "-bin" || "$patch" != "0" ]]; then + if [[ "$3" == "" || "${3:0:4}" == "-bin" || ("$patch" != "0" && "$3" =~ ^-[a-zA-Z0-9_]+$) ]]; then echo "$majMin" else echo "$1" @@ -363,10 +368,15 @@ determineScalaVersion() { if [ -z "$scalaTag" ] then - echo "No tag found, building nightly snapshot." + echo "No tag found, running an integration build." parseScalaProperties "build.number" SCALA_VER_BASE="$version_major.$version_minor.$version_patch" - SCALA_VER_SUFFIX="-$(git rev-parse --short HEAD)-nightly" + local shaSuffix=$(git rev-parse HEAD | cut -c1-7) + local cross="bin" + if [[ $SCALA_VER_BASE =~ ^.*\.0$ ]]; then + cross="pre" + fi + SCALA_VER_SUFFIX="-$cross-$shaSuffix" SCALADOC_SOURCE_LINKS_VER=$(git rev-parse HEAD) # TODO: publish nightly snapshot using this script - currently it's a separate jenkins job still running at EPFL. @@ -468,20 +478,31 @@ createNetrcFile() { grep 'password=' $1 | sed 's/password=\(.*\)/password \1/' >> $netrcFile } +# deletes existing artifacts (core and modules) matching the $SCALA_VER from the repository passed as argument removeExistingBuilds() { - createNetrcFile "$HOME/.credentials-private-repo" - local netrcFile="$HOME/.credentials-private-repo-netrc" - - local storageApiUrl=`echo $releaseTempRepoUrl | sed 's/\(scala-release-temp\)/api\/storage\/\1/'` - local scalaLangModules=`curl -s $storageApiUrl/org/scala-lang | jq -r '.children | .[] | "org/scala-lang" + .uri'` - - for module in "org/scalacheck" $scalaLangModules; do - local artifacts=`curl -s $storageApiUrl/$module | jq -r ".children | .[] | select(.uri | endswith(\"$SCALA_VER\")) | .uri"` - for artifact in $artifacts; do - echo "Deleting $releaseTempRepoUrl$module$artifact" - curl -s --netrc-file $netrcFile -X DELETE $releaseTempRepoUrl$module$artifact + local repoUrl=$1 + local repoPrefix="https://scala-ci.typesafe.com/artifactory/" + if [[ $repoUrl == "$repoPrefix"* ]]; then + local repoId=${1#$repoPrefix} + local storageApiUrl="${repoPrefix}api/storage/$repoId" + + createNetrcFile "$HOME/.credentials-private-repo" + local netrcFile="$HOME/.credentials-private-repo-netrc" + + # "module" is not a scala module (like scala-xml), but an artifact of a boostrap build. the variable + # contains: "org/scala-lang/modules", "org/scala-lang/scala-compiler", "org/scala-lang/scala-library", ... + local scalaLangModules=`curl -s $storageApiUrl/org/scala-lang | jq -r '.children | .[] | "org/scala-lang" + .uri' | grep -v actors-migration` + + for module in $scalaLangModules; do + local artifacts=`curl -s $storageApiUrl/$module | jq -r ".children | .[] | select(.uri | endswith(\"$SCALA_VER\")) | .uri"` + for artifact in $artifacts; do + echo "Deleting $repoUrl$module$artifact" + curl -s --netrc-file $netrcFile -X DELETE $repoUrl$module$artifact + done done - done + else + echo "Unknown repo, not deleting anything: $repoUrl" + fi } constructUpdatedModuleVersions() { @@ -507,7 +528,7 @@ constructUpdatedModuleVersions() { if [ ! -z "$SCALA_BINARY_VER" ]; then updatedModuleVersions=("${updatedModuleVersions[@]}" "-Dscala.binary.version=$SCALA_BINARY_VER"); fi } -# build locker (scala + modules) and quick, publishing everything to private-repo +# build locker (scala + modules) and quick, publishing everything to artifactory bootstrap() { echo "### Bootstrapping" @@ -524,8 +545,8 @@ bootstrap() { # in sabbus lingo, the resulting Scala build will be used as starr to build the released Scala compiler ant -Dmaven.version.number=$SCALA_VER\ -Dremote.snapshot.repository=NOPE\ - -Dremote.release.repository=$releaseTempRepoUrl\ - -Drepository.credentials.id=$releaseTempRepoCred\ + -Dremote.release.repository=$integrationRepoUrl\ + -Drepository.credentials.id=$integrationRepoCred\ -Dscalac.args.optimise=-optimise\ -Ddocs.skip=1\ -Dlocker.skip=1\ @@ -561,14 +582,14 @@ bootstrap() { # which is fully cross-versioned (for $SCALA_VER, the version we're releasing) ant -Dstarr.version=$SCALA_VER\ -Dscala.full.version=$SCALA_VER\ - -Dextra.repo.url=$releaseTempRepoUrl\ + -Dextra.repo.url=$integrationRepoUrl\ -Dmaven.version.suffix=$SCALA_VER_SUFFIX\ ${updatedModuleVersions[@]} \ -Dupdate.versions=1\ -Dscaladoc.git.commit=$SCALADOC_SOURCE_LINKS_VER\ -Dremote.snapshot.repository=NOPE\ - -Dremote.release.repository=$releaseTempRepoUrl\ - -Drepository.credentials.id=$releaseTempRepoCred\ + -Dremote.release.repository=$integrationRepoUrl\ + -Drepository.credentials.id=$integrationRepoCred\ -Dscalac.args.optimise=-optimise\ $antBuildTask $publishPrivateTask @@ -614,7 +635,7 @@ determineScalaVersion deriveModuleVersions -removeExistingBuilds +removeExistingBuilds $integrationRepoUrl bootstrap diff --git a/spec/05-classes-and-objects.md b/spec/05-classes-and-objects.md index 69828ec7fe..65666e31cb 100644 --- a/spec/05-classes-and-objects.md +++ b/spec/05-classes-and-objects.md @@ -851,9 +851,8 @@ already a `val` or `var` modifier. Hence, an accessor definition for the parameter is [generated](#class-definitions). A case class definition of `$c$[$\mathit{tps}\,$]($\mathit{ps}_1\,$)$\ldots$($\mathit{ps}_n$)` with type -parameters $\mathit{tps}$ and value parameters $\mathit{ps}$ implicitly -generates an [extractor object](08-pattern-matching.html#extractor-patterns) which is -defined as follows: +parameters $\mathit{tps}$ and value parameters $\mathit{ps}$ implies +the definition of a companion object, which serves as an [extractor object](08-pattern-matching.html#extractor-patterns). It has the following shape: ```scala object $c$ { @@ -870,11 +869,13 @@ each $\mathit{xs}\_i$ denotes the parameter names of the parameter section $\mathit{ps}\_i$, and $\mathit{xs}\_{11}, \ldots , \mathit{xs}\_{1k}$ denote the names of all parameters in the first parameter section $\mathit{xs}\_1$. -If a type parameter section is missing in the -class, it is also missing in the `apply` and -`unapply` methods. -The definition of `apply` is omitted if class $c$ is -`abstract`. +If a type parameter section is missing in the class, it is also missing in the `apply` and `unapply` methods. + +If the companion object $c$ is already defined, +the `apply` and `unapply` methods are added to the existing object. +If the object $c$ already has a [matching](#definition-matching) +`apply` (or `unapply`) member, no new definition is added. +The definition of `apply` is omitted if class $c$ is `abstract`. If the case class definition contains an empty value parameter list, the `unapply` method returns a `Boolean` instead of an `Option` type and @@ -887,9 +888,6 @@ def unapply[$\mathit{tps}\,$]($x$: $c$[$\mathit{tps}\,$]) = x ne null The name of the `unapply` method is changed to `unapplySeq` if the first parameter section $\mathit{ps}_1$ of $c$ ends in a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters). -If a companion object $c$ exists already, no new object is created, -but the `apply` and `unapply` methods are added to the existing -object instead. A method named `copy` is implicitly added to every case class unless the class already has a member (directly defined or inherited) with that name, or the diff --git a/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala b/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala index 43bdad5882..d7901730a4 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala @@ -135,7 +135,7 @@ abstract class ScalaBuild extends Ordered[ScalaBuild] { def unparse: String } /** - * A development, test, nightly, snapshot or other "unofficial" build + * A development, test, integration, snapshot or other "unofficial" build */ case class Development(id: String) extends ScalaBuild { def unparse = s"-${id}" diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index ee64a6646f..69b8cb12e6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -610,7 +610,15 @@ trait Namers extends MethodSynthesis { noDuplicates(selectors map (_.rename), AppearsTwice) } - def enterCopyMethod(copyDef: DefDef): Symbol = { + class CompleterWrapper(completer: TypeCompleter) extends TypeCompleter { + val tree = completer.tree + + override def complete(sym: Symbol): Unit = { + completer.complete(sym) + } + } + + def copyMethodCompleter(copyDef: DefDef): TypeCompleter = { val sym = copyDef.symbol val lazyType = completerOf(copyDef) @@ -629,13 +637,72 @@ trait Namers extends MethodSynthesis { ) } - sym setInfo { - mkTypeCompleter(copyDef) { sym => - assignParamTypes() - lazyType complete sym + mkTypeCompleter(copyDef) { sym => + assignParamTypes() + lazyType complete sym + } + } + + // for apply/unapply, which may need to disappear when they clash with a user-defined method of matching signature + def applyUnapplyMethodCompleter(un_applyDef: DefDef, companionContext: Context): TypeCompleter = + new CompleterWrapper(completerOf(un_applyDef)) { + override def complete(sym: Symbol): Unit = { + assert(sym hasAllFlags CASE | SYNTHETIC, sym.defString) + + super.complete(sym) + + // owner won't be locked + val ownerInfo = companionContext.owner.info + + // If there's a same-named locked symbol, we're currently completing its signature. + // If `scopePartiallyCompleted`, the program is known to have a type error, since + // this means a user-defined method is missing a result type while its rhs refers to `sym` or an overload. + // This is an error because overloaded/recursive methods must have a result type. + // The method would be overloaded if its signature, once completed, would not match the synthetic method's, + // or recursive if it turned out we should unlink our synthetic method (matching sig). + // In any case, error out. We don't unlink the symbol so that `symWasOverloaded` says yes, + // which would be wrong if the method is in fact recursive, but it seems less confusing. + val scopePartiallyCompleted = new HasMember(ownerInfo, sym.name, BridgeFlags | SYNTHETIC, LOCKED).apply() + + // Check `scopePartiallyCompleted` first to rule out locked symbols from the owner.info.member call, + // as FindMember will call info on a locked symbol (while checking type matching to assemble an overloaded type), + // and throw a TypeError, so that we are aborted. + // Do not consider deferred symbols, as suppressing our concrete implementation would be an error regardless + // of whether the signature matches (if it matches, we omitted a valid implementation, if it doesn't, + // we would get an error for the missing implementation it isn't implemented by some overload other than our synthetic one) + val suppress = scopePartiallyCompleted || { + // can't exclude deferred members using DEFERRED flag here (TODO: why?) + val userDefined = ownerInfo.memberBasedOnName(sym.name, BridgeFlags | SYNTHETIC) + + (userDefined != NoSymbol) && { + assert(userDefined != sym) + val alts = userDefined.alternatives // could be just the one, if this member isn't overloaded + // don't compute any further `memberInfo`s if there's an error somewhere + alts.exists(_.isErroneous) || { + val self = companionContext.owner.thisType + val memberInfo = self.memberInfo(sym) + alts.exists(alt => !alt.isDeferred && (self.memberInfo(alt) matches memberInfo)) + } + } + } + + if (suppress) { + sym setInfo ErrorType + sym setFlag IS_ERROR + + // Don't unlink in an error situation to generate less confusing error messages. + // Ideally, our error reporting would distinguish overloaded from recursive user-defined apply methods without signature, + // but this would require some form of partial-completion of method signatures, so that we can + // know what the argument types were, even though we can't complete the result type, because + // we hit a cycle while trying to compute it (when we get here with locked user-defined symbols, we + // are in the complete for that symbol, and thus the locked symbol has not yet received enough info; + // I hesitate to provide more info, because it would involve a WildCard or something for its result type, + // which could upset other code paths) + if (!scopePartiallyCompleted) + companionContext.scope.unlink(sym) + } } } - } def completerOf(tree: Tree): TypeCompleter = { val mono = namerOf(tree.symbol) monoTypeCompleter tree @@ -697,11 +764,15 @@ trait Namers extends MethodSynthesis { val bridgeFlag = if (mods hasAnnotationNamed tpnme.bridgeAnnot) BRIDGE | ARTIFACT else 0 val sym = assignAndEnterSymbol(tree) setFlag bridgeFlag - if (name == nme.copy && sym.isSynthetic) - enterCopyMethod(tree) - else - sym setInfo completerOf(tree) - } + val completer = + if (sym hasFlag SYNTHETIC) { + if (name == nme.copy) copyMethodCompleter(tree) + else if (sym hasFlag CASE) applyUnapplyMethodCompleter(tree, context) + else completerOf(tree) + } else completerOf(tree) + + sym setInfo completer + } def enterClassDef(tree: ClassDef) { val ClassDef(mods, _, _, impl) = tree @@ -1351,7 +1422,7 @@ trait Namers extends MethodSynthesis { val defTpt = // don't mess with tpt's of case copy default getters, because assigning something other than TypeTree() - // will break the carefully orchestrated naming/typing logic that involves enterCopyMethod and caseClassCopyMeth + // will break the carefully orchestrated naming/typing logic that involves copyMethodCompleter and caseClassCopyMeth if (meth.isCaseCopy) TypeTree() else { // If the parameter type mentions any type parameter of the method, let the compiler infer the diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index 7ea597eac9..416aeeccb3 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -105,7 +105,7 @@ private[scala] trait PropertiesTrait { * or "version (unknown)" if it cannot be determined. */ val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") - val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2016, LAMP/EPFL") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2017, LAMP/EPFL and Lightbend, Inc.") /** This is the encoding to use reading in source files, overridden with -encoding. * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. diff --git a/src/reflect/scala/reflect/internal/tpe/FindMembers.scala b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala index 83a5d23e7c..1b00815bca 100644 --- a/src/reflect/scala/reflect/internal/tpe/FindMembers.scala +++ b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala @@ -285,4 +285,18 @@ trait FindMembers { initBaseClasses.head.newOverloaded(tpe, members) } } + + private[scala] final class HasMember(tpe: Type, name: Name, excludedFlags: Long, requiredFlags: Long) extends FindMemberBase[Boolean](tpe, name, excludedFlags, requiredFlags) { + private[this] var _result = false + override protected def result: Boolean = _result + + protected def shortCircuit(sym: Symbol): Boolean = { + _result = true + true // prevents call to addMemberIfNew + } + + // Not used + protected def addMemberIfNew(sym: Symbol): Unit = {} + } + } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala index f70bac8f83..08d3508a78 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala @@ -280,7 +280,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp { if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value)) - <div id="footer">Scala programming documentation. Copyright (c) 2003-2016 <a href="http://www.epfl.ch" target="_top">EPFL</a>, with contributions from <a href="http://www.lightbend.com" target="_top">Lightbend</a>.</div> + <div id="footer">Scala programming documentation. Copyright (c) 2003-2017 <a href="http://www.epfl.ch" target="_top">EPFL</a>, with contributions from <a href="http://www.lightbend.com" target="_top">Lightbend</a>.</div> else <div id="footer"> { tpl.universe.settings.docfooter.value } </div> } diff --git a/src/scalap/decoder.properties b/src/scalap/decoder.properties index 9bb8d130ea..0bff4c81d4 100644 --- a/src/scalap/decoder.properties +++ b/src/scalap/decoder.properties @@ -1,2 +1,2 @@ version.number=2.0.1 -copyright.string=(c) 2002-2016 LAMP/EPFL +copyright.string=(c) 2002-2017 LAMP/EPFL diff --git a/test/files/neg/userdefined_apply.check b/test/files/neg/userdefined_apply.check new file mode 100644 index 0000000000..c8c8976f5f --- /dev/null +++ b/test/files/neg/userdefined_apply.check @@ -0,0 +1,25 @@ +userdefined_apply.scala:3: error: overloaded method apply needs result type + private def apply(x: Int) = if (x > 0) new ClashOverloadNoSig(x) else apply("") + ^ +userdefined_apply.scala:14: error: overloaded method apply needs result type + private def apply(x: Int) = if (x > 0) ClashRecNoSig(1) else ??? + ^ +userdefined_apply.scala:21: error: overloaded method apply needs result type + private def apply(x: Boolean) = if (x) NoClashNoSig(1) else ??? + ^ +userdefined_apply.scala:28: error: overloaded method apply needs result type + private def apply(x: Boolean) = if (x) NoClashOverload(1) else apply("") + ^ +userdefined_apply.scala:45: error: recursive method apply needs result type +case class NoClashNoSigPoly private(x: Int) + ^ +userdefined_apply.scala:39: error: NoClashNoSigPoly.type does not take parameters + def apply(x: T) = if (???) NoClashNoSigPoly(1) else ??? + ^ +userdefined_apply.scala:57: error: recursive method apply needs result type +case class ClashNoSigPoly private(x: Int) + ^ +userdefined_apply.scala:51: error: ClashNoSigPoly.type does not take parameters + def apply(x: T) = if (???) ClashNoSigPoly(1) else ??? + ^ +8 errors found diff --git a/test/files/neg/userdefined_apply.scala b/test/files/neg/userdefined_apply.scala new file mode 100644 index 0000000000..0a0d960b39 --- /dev/null +++ b/test/files/neg/userdefined_apply.scala @@ -0,0 +1,57 @@ +object ClashOverloadNoSig { + // error: overloaded method apply needs result type + private def apply(x: Int) = if (x > 0) new ClashOverloadNoSig(x) else apply("") + + def apply(x: String): ClashOverloadNoSig = ??? +} + +case class ClashOverloadNoSig private(x: Int) + +object ClashRecNoSig { + // TODO: status quo is that the error refers to an overloaded method, which is actually recursive + // (we should have unlinked the symbol in the `if(suppress)` part of `applyUnapplyMethodCompleter`) + // error: recursive method apply needs result type + private def apply(x: Int) = if (x > 0) ClashRecNoSig(1) else ??? +} + +case class ClashRecNoSig private(x: Int) + +object NoClashNoSig { + // error: overloaded method apply needs result type + private def apply(x: Boolean) = if (x) NoClashNoSig(1) else ??? +} + +case class NoClashNoSig private(x: Int) + +object NoClashOverload { + // error: overloaded method apply needs result type + private def apply(x: Boolean) = if (x) NoClashOverload(1) else apply("") + + def apply(x: String): NoClashOverload = ??? +} + +case class NoClashOverload private(x: Int) + + +class BaseNCNSP[T] { + // TODO: suppress the following error + // error: NoClashNoSigPoly.type does not take parameters + def apply(x: T) = if (???) NoClashNoSigPoly(1) else ??? +} + +object NoClashNoSigPoly extends BaseNCNSP[Boolean] +// TODO: position error at definition of apply in superclass instead of on case clss +// error: recursive method apply needs result type +case class NoClashNoSigPoly private(x: Int) + + +class BaseCNSP[T] { + // TODO: suppress the following error + // error: ClashNoSigPoly.type does not take parameters + def apply(x: T) = if (???) ClashNoSigPoly(1) else ??? +} + +object ClashNoSigPoly extends BaseCNSP[Int] +// TODO: position error at definition of apply in superclass instead of on case clss +// error: recursive method apply needs result type +case class ClashNoSigPoly private(x: Int) diff --git a/test/files/pos/userdefined_apply.scala b/test/files/pos/userdefined_apply.scala new file mode 100644 index 0000000000..e29f9f5141 --- /dev/null +++ b/test/files/pos/userdefined_apply.scala @@ -0,0 +1,54 @@ +// NOTE: the companion inherits a public apply method from Function1! +case class NeedsCompanion private (x: Int) + +object ClashNoSig { // ok + private def apply(x: Int) = if (x > 0) new ClashNoSig(x) else ??? +} +case class ClashNoSig private (x: Int) + + +object Clash { + private def apply(x: Int) = if (x > 0) new Clash(x) else ??? +} +case class Clash private (x: Int) + +object ClashSig { + private def apply(x: Int): ClashSig = if (x > 0) new ClashSig(x) else ??? +} +case class ClashSig private (x: Int) + +object ClashOverload { + private def apply(x: Int): ClashOverload = if (x > 0) new ClashOverload(x) else apply("") + def apply(x: String): ClashOverload = ??? +} +case class ClashOverload private (x: Int) + +object NoClashSig { + private def apply(x: Boolean): NoClashSig = if (x) NoClashSig(1) else ??? +} +case class NoClashSig private (x: Int) + +object NoClashOverload { + // needs full sig + private def apply(x: Boolean): NoClashOverload = if (x) NoClashOverload(1) else apply("") + def apply(x: String): NoClashOverload = ??? +} +case class NoClashOverload private (x: Int) + + + +class BaseNCP[T] { + // error: overloaded method apply needs result type + def apply(x: T): NoClashPoly = if (???) NoClashPoly(1) else ??? +} + +object NoClashPoly extends BaseNCP[Boolean] +case class NoClashPoly private(x: Int) + + +class BaseCP[T] { + // error: overloaded method apply needs result type + def apply(x: T): ClashPoly = if (???) ClashPoly(1) else ??? +} +object ClashPoly extends BaseCP[Int] +case class ClashPoly private(x: Int) |