aboutsummaryrefslogtreecommitdiff
path: root/compiler/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src')
-rw-r--r--compiler/src/dotty/tools/dotc/core/Types.scala12
-rw-r--r--compiler/src/dotty/tools/dotc/parsing/Parsers.scala22
-rw-r--r--compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala12
-rw-r--r--compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala48
-rw-r--r--compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala68
-rw-r--r--compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala11
6 files changed, 156 insertions, 17 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala
index 6c27e53f6..c80107f93 100644
--- a/compiler/src/dotty/tools/dotc/core/Types.scala
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -1719,7 +1719,7 @@ object Types {
* under both private and public names, so it could still be found by looking up
* the public name.
*/
- final def shadowed(implicit ctx: Context): NamedType =
+ def shadowed(implicit ctx: Context): NamedType =
NamedType(prefix, name.shadowedName)
override def equals(that: Any) = that match {
@@ -1782,15 +1782,19 @@ object Types {
else d.atSignature(sig).checkUnique
}
- override def newLikeThis(prefix: Type)(implicit ctx: Context): TermRef = {
- val candidate = TermRef.withSig(prefix, name, sig)
+ private def fixDenot(candidate: TermRef, prefix: Type)(implicit ctx: Context): TermRef =
if (symbol.exists && !candidate.symbol.exists) { // recompute from previous symbol
val ownSym = symbol
val newd = asMemberOf(prefix, allowPrivate = ownSym.is(Private))
candidate.withDenot(newd.suchThat(_.signature == ownSym.signature))
}
else candidate
- }
+
+ override def newLikeThis(prefix: Type)(implicit ctx: Context): TermRef =
+ fixDenot(TermRef.withSig(prefix, name, sig), prefix)
+
+ override def shadowed(implicit ctx: Context): NamedType =
+ fixDenot(TermRef.withSig(prefix, name.shadowedName, sig), prefix)
override def equals(that: Any) = that match {
case that: TermRefWithSignature =>
diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala
index 65c7a290d..b644c94cc 100644
--- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -12,7 +12,7 @@ import core._
import Flags._
import Contexts._
import Names._
-import ast.Positioned
+import ast.{Positioned, Trees, untpd}
import ast.Trees._
import Decorators._
import StdNames._
@@ -20,6 +20,7 @@ import util.Positions._
import Constants._
import ScriptParsers._
import Comments._
+
import scala.annotation.{tailrec, switch}
import util.DotClass
import rewrite.Rewrites.patch
@@ -1800,6 +1801,10 @@ object Parsers {
case _ => syntaxError(AuxConstructorNeedsNonImplicitParameter(), start)
}
}
+ val listOfErrors = checkVarArgsRules(result)
+ listOfErrors.foreach { vparam =>
+ syntaxError(VarArgsParamMustComeLast(), vparam.tpt.pos)
+ }
result
}
@@ -1921,6 +1926,21 @@ object Parsers {
}
}
+
+
+ private def checkVarArgsRules(vparamss: List[List[untpd.ValDef]]): List[untpd.ValDef] = {
+ def isVarArgs(tpt: Trees.Tree[Untyped]): Boolean = tpt match {
+ case PostfixOp(_, op) if op.name == nme.raw.STAR => true
+ case _ => false
+ }
+
+ vparamss.flatMap { params =>
+ if (params.nonEmpty) {
+ params.init.filter(valDef => isVarArgs(valDef.tpt))
+ } else List()
+ }
+ }
+
/** DefDef ::= DefSig (`:' Type [`=' Expr] | "=" Expr)
* | this ParamClause ParamClauses `=' ConstrExpr
* DefDcl ::= DefSig `:' Type
diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
index 6fa056646..57365658e 100644
--- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala
@@ -1209,4 +1209,16 @@ object messages {
|${parents.mkString(" - ", "\n - ", "")}
|""".stripMargin
}
+
+ case class VarArgsParamMustComeLast()(implicit ctx: Context)
+ extends Message(IncorrectRepeatedParameterSyntaxID) {
+ override def msg: String = "varargs parameter must come last"
+
+ override def kind: String = "Syntax"
+
+ override def explanation: String =
+ hl"""|The varargs field must be the last field in the method signature.
+ |Attempting to define a field in a method signature after a varargs field is an error.
+ |""".stripMargin
+ }
}
diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
index 43202ef87..082a80b58 100644
--- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
+++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
@@ -114,6 +114,11 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
private[this] val classLikeCache = new mutable.HashMap[ClassSymbol, api.ClassLike]
/** This cache is optional, it avoids recomputing representations */
private[this] val typeCache = new mutable.HashMap[Type, api.Type]
+ /** This cache is necessary to avoid unstable name hashing when `typeCache` is present,
+ * see the comment in the `RefinedType` case in `computeType`
+ * The cache key is (api of RefinedType#parent, api of RefinedType#refinedInfo).
+ */
+ private[this] val refinedTypeCache = new mutable.HashMap[(api.Type, api.Definition), api.Structure]
private[this] object Constants {
val emptyStringArray = Array[String]()
@@ -349,7 +354,14 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
Constants.emptyType
case tp: NamedType =>
val sym = tp.symbol
- // Normalize package prefix to avoid instability of representation
+ // A type can sometimes be represented by multiple different NamedTypes
+ // (they will be `=:=` to each other, but not `==`), and the compiler
+ // may choose to use any of these representation, there is no stability
+ // guarantee. We avoid this instability by always normalizing the
+ // prefix: if it's a package, if we didn't do this sbt might conclude
+ // that some API changed when it didn't, leading to overcompilation
+ // (recompiling more things than what is needed for incremental
+ // compilation to be correct).
val prefix = if (sym.owner.is(Package))
sym.owner.thisType
else
@@ -389,15 +401,39 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
new api.TypeDeclaration(apiType(lo), apiType(hi),
Array(), name, Constants.public, Constants.emptyModifiers, Array())
}
-
- val decl: Array[api.Definition] = rt.refinedInfo match {
+ val decl = rt.refinedInfo match {
case rinfo: TypeBounds =>
- Array(typeRefinement(name, rinfo))
+ typeRefinement(name, rinfo)
case _ =>
ctx.debuglog(i"sbt-api: skipped structural refinement in $rt")
- Array()
+ null
}
- new api.Structure(strict2lzy(Array(parent)), strict2lzy(decl), strict2lzy(Array()))
+
+ // Aggressive caching for RefinedTypes: `typeCache` is enough as long as two
+ // RefinedType are `==`, but this is only the case when their `refinedInfo`
+ // are `==` and this is not always the case, consider:
+ //
+ // val foo: { type Bla = a.b.T }
+ // val bar: { type Bla = a.b.T }
+ //
+ // The sbt API representations of `foo` and `bar` (let's call them `apiFoo`
+ // and `apiBar`) will both be instances of `Structure`. If `typeCache` was
+ // the only cache, then in some cases we would have `apiFoo eq apiBar` and
+ // in other cases we would just have `apiFoo == apiBar` (this happens
+ // because the dotty representation of `a.b.T` is unstable, see the comment
+ // in the `NamedType` case above).
+ //
+ // The fact that we may or may not have `apiFoo eq apiBar` is more than
+ // an optimisation issue: it will determine whether the sbt name hash for
+ // `Bla` contains one or two entries (because sbt `NameHashing` will not
+ // traverse both `apiFoo` and `apiBar` if they are `eq`), therefore the
+ // name hash of `Bla` will be unstable, unless we make sure that
+ // `apiFoo == apiBar` always imply `apiFoo eq apiBar`. This is what
+ // `refinedTypeCache` is for.
+ refinedTypeCache.getOrElseUpdate((parent, decl), {
+ val adecl: Array[api.Definition] = if (decl == null) Array() else Array(decl)
+ new api.Structure(strict2lzy(Array(parent)), strict2lzy(adecl), strict2lzy(Array()))
+ })
case tp: RecType =>
apiType(tp.parent)
case RecThis(recType) =>
diff --git a/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala b/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala
index e377de6da..abdd5cfdd 100644
--- a/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala
+++ b/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc
+package dotty.tools
+package dotc
package sbt
import scala.annotation.tailrec
@@ -24,7 +25,7 @@ private[sbt] trait ThunkHolder {
* It will be forced by the next call to `forceThunks()`
*/
def lzy[T <: AnyRef](t: => T): api.Lazy[T] = {
- val l = SafeLazy(() => t)
+ val l = SafeLazyWrapper(() => t)
thunks += l
l
}
@@ -37,10 +38,69 @@ private[sbt] trait ThunkHolder {
* see https://github.com/sbt/zinc/issues/114
*/
def strict2lzy[T <: AnyRef](t: T): api.Lazy[T] =
- SafeLazy.strict(t)
+ SafeLazyWrapper.strict(t)
}
-// TODO: Use xsbti.SafeLazy once https://github.com/sbt/zinc/issues/113 is fixed
+/** Wrapper around SafeLazy implementations.
+ *
+ * `xsbti.SafeLazy` is part of sbt but it is not part of the `interface` jar
+ * that dotty depends on, therefore we can only access it by reflection,
+ * and this will only succeed when dotty is run by sbt (otherwise
+ * `xsbti.SafeLazy` won't be on the classpath at all).
+ *
+ * For testing purposes, we still want to be able to run the sbt phases outside
+ * of sbt, using `-Yforce-sbt-phases` and `-Ydump-sbt-inc`, therefore we
+ * provide a copy of SafeLazy in `dotty.tools.dotc.sbt.SafeLazy` that we use
+ * when `xsbti.SafeLazy` is unavailable.
+ *
+ * This raises a question: why bother with `xsbti.SafeLazy` if we have our own
+ * version anyway? Because sbt uses Java serialization to persist the output of
+ * the incremental compilation analysis when sbt is stopped and restarted. If
+ * we used `dotty.tools.dotc.sbt.SafeLazy` with sbt, deserialization would fail
+ * and every restart of sbt would require a full recompilation.
+ *
+ * Note: this won't be needed once we switch to zinc 1.0 where `SafeLazy` becomes
+ * part of the `interface` jar, see https://github.com/sbt/zinc/issues/113
+ */
+private object SafeLazyWrapper {
+
+ @sharable private[this] val safeLazy =
+ try {
+ Class.forName("xsbti.SafeLazy")
+ } catch {
+ case e: ClassNotFoundException =>
+ null
+ }
+
+ @sharable private[this] val safeLazyApply =
+ if (safeLazy != null)
+ safeLazy.getMethod("apply", classOf[xsbti.F0[_]])
+ else
+ null
+ @sharable private[this] val safeLazyStrict =
+ if (safeLazy != null)
+ safeLazy.getMethod("strict", classOf[Object])
+ else
+ null
+
+ def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] =
+ if (safeLazyApply != null)
+ safeLazyApply
+ .invoke(null, new xsbti.F0[T] { def apply() = eval() })
+ .asInstanceOf[xsbti.api.Lazy[T]]
+ else
+ SafeLazy(eval)
+
+ def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] =
+ if (safeLazyStrict != null)
+ safeLazyStrict
+ .invoke(null, value)
+ .asInstanceOf[xsbti.api.Lazy[T]]
+ else
+ SafeLazy.strict(value)
+}
+
+// Adapted from https://github.com/sbt/sbt/blob/0.13/compile/api/src/main/scala/xsbti/SafeLazy.scala
private object SafeLazy {
def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] =
new Impl(eval)
diff --git a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala
index 859ac8b06..2836ebcfe 100644
--- a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala
+++ b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala
@@ -4,6 +4,8 @@ package transform
import core._
import ast.Trees._
import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._, StdNames._
+import Decorators._
+import config.Printers.typr
/** For all parameter accessors
*
@@ -48,7 +50,7 @@ class ParamForwarding(thisTransformer: DenotTransformer) {
val candidate = sym.owner.asClass.superClass
.info.decl(sym.name).suchThat(_ is (ParamAccessor, butNot = Mutable)).symbol
if (candidate.isAccessibleFrom(currentClass.thisType, superAccess = true)) candidate
- else if (candidate is Method) inheritedAccessor(candidate)
+ else if (candidate.exists) inheritedAccessor(candidate)
else NoSymbol
}
def forwardParamAccessor(stat: Tree): Tree = {
@@ -65,8 +67,13 @@ class ParamForwarding(thisTransformer: DenotTransformer) {
def forwarder(implicit ctx: Context) = {
sym.copySymDenotation(initFlags = sym.flags | Method | Stable, info = sym.info.ensureMethodic)
.installAfter(thisTransformer)
- val superAcc =
+ var superAcc =
Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias)
+ if (alias.owner != currentClass.superClass)
+ // need to use shadowed in order not to accidentally address an
+ // intervening private forwarder in the superclass
+ superAcc = superAcc.withType(superAcc.tpe.asInstanceOf[TermRef].shadowed)
+ typr.println(i"adding param forwarder $superAcc")
DefDef(sym, superAcc.ensureConforms(sym.info.widen))
}
return forwarder(ctx.withPhase(thisTransformer.next))