aboutsummaryrefslogtreecommitdiff
path: root/compiler/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src')
-rw-r--r--compiler/src/dotty/tools/dotc/ast/TreeInfo.scala22
-rw-r--r--compiler/src/dotty/tools/dotc/ast/untpd.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala18
-rw-r--r--compiler/src/dotty/tools/dotc/core/Phases.scala3
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala12
-rw-r--r--compiler/src/dotty/tools/dotc/transform/Erasure.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/transform/Mixin.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala17
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Checking.scala5
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Dynamic.scala66
-rw-r--r--compiler/src/dotty/tools/dotc/typer/RefChecks.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala8
14 files changed, 145 insertions, 19 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
index da83d0644..bcda4b92f 100644
--- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -629,6 +629,28 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case nil =>
Nil
}
+
+ /** Is this a selection of a member of a structural type that is not a member
+ * of an underlying class or trait?
+ */
+ def isStructuralTermSelect(tree: Tree)(implicit ctx: Context) = tree match {
+ case tree: Select =>
+ def hasRefinement(qualtpe: Type): Boolean = qualtpe.dealias match {
+ case RefinedType(parent, rname, rinfo) =>
+ rname == tree.name || hasRefinement(parent)
+ case tp: TypeProxy =>
+ hasRefinement(tp.underlying)
+ case tp: OrType =>
+ hasRefinement(tp.tp1) || hasRefinement(tp.tp2)
+ case tp: AndType =>
+ hasRefinement(tp.tp1) && hasRefinement(tp.tp2)
+ case _ =>
+ false
+ }
+ !tree.symbol.exists && tree.isTerm && hasRefinement(tree.qualifier.tpe)
+ case _ =>
+ false
+ }
}
object TreeInfo {
diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala
index f3ffce8f8..37dc32c0e 100644
--- a/compiler/src/dotty/tools/dotc/ast/untpd.scala
+++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala
@@ -476,7 +476,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case SymbolLit(str) =>
cpy.SymbolLit(tree)(str)
case InterpolatedString(id, segments) =>
- cpy.InterpolatedString(tree)(id, transform(segments))
+ cpy.InterpolatedString(tree)(id, segments.map(transform(_)))
case Function(args, body) =>
cpy.Function(tree)(transform(args), transform(body))
case InfixOp(left, op, right) =>
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index 0aeb28d36..7fe6505ff 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -26,6 +26,9 @@ object Definitions {
* else without affecting the set of programs that can be compiled.
*/
val MaxImplementedFunctionArity = 22
+
+ /** The maximal arity of a function that can be accessed as member of a structural type */
+ val MaxStructuralMethodArity = 7
}
/** A class defining symbols and types of standard definitions
@@ -130,7 +133,7 @@ class Definitions {
ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: parentTraits, decls)
}
}
- newClassSymbol(ScalaPackageClass, name, Trait, completer)
+ newClassSymbol(ScalaPackageClass, name, Trait | NoInits, completer)
}
private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol =
@@ -515,6 +518,7 @@ class Definitions {
lazy val LanguageModuleRef = ctx.requiredModule("scala.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
+ lazy val SelectableType: TypeRef = ctx.requiredClassRef("scala.Selectable")
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
def ClassTagClass(implicit ctx: Context) = ClassTagType.symbol.asClass
@@ -766,6 +770,18 @@ class Definitions {
lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
+ /** Classes that are known not to have an initializer irrespective of
+ * whether NoInits is set. Note: FunctionXXLClass is in this set
+ * because if it is compiled by Scala2, it does not get a NoInit flag.
+ * But since it is introduced only at erasure, there's no chance
+ * for augmentScala2Traits to do anything on a class that inherits it. So
+ * it also misses an implementation class, which means that the usual scheme
+ * of calling a superclass init in the implementation class of a Scala2
+ * trait gets screwed up. Therefore, it is mandatory that FunctionXXL
+ * is treated as a NoInit trait.
+ */
+ lazy val NoInitClasses = PhantomClasses + FunctionXXLClass
+
def isPolymorphicAfterErasure(sym: Symbol) =
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala
index 6a53e1b30..d217afed8 100644
--- a/compiler/src/dotty/tools/dotc/core/Phases.scala
+++ b/compiler/src/dotty/tools/dotc/core/Phases.scala
@@ -40,9 +40,6 @@ trait Phases {
def atPhaseNotLaterThan[T](limit: Phase)(op: Context => T): T =
if (!limit.exists || phase <= limit) op(this) else atPhase(limit)(op)
- def atPhaseNotLaterThanTyper[T](op: Context => T): T =
- atPhaseNotLaterThan(base.typerPhase)(op)
-
def isAfterTyper: Boolean = base.isAfterTyper(phase)
}
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index 716959648..4a9c50dad 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -481,6 +481,7 @@ object StdNames {
val sameElements: N = "sameElements"
val scala_ : N = "scala"
val selectDynamic: N = "selectDynamic"
+ val selectDynamicMethod: N = "selectDynamicMethod"
val selectOverloadedMethod: N = "selectOverloadedMethod"
val selectTerm: N = "selectTerm"
val selectType: N = "selectType"
diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
index 97a82e80d..5b751ef3c 100644
--- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
+++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
@@ -818,7 +818,7 @@ class ClassfileParser(
case Some(entry) =>
val outerName = entry.outerName.stripModuleClassSuffix
val owner = classSymbol(outerName)
- val result = ctx.atPhaseNotLaterThanTyper { implicit ctx =>
+ val result = ctx.atPhaseNotLaterThan(ctx.typerPhase) { implicit ctx =>
getMember(owner, innerName.toTypeName)
}
assert(result ne NoSymbol,
diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
index 1fffe6841..7f44af486 100644
--- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
+++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala
@@ -6,6 +6,7 @@ import core._, core.Decorators._
import Annotations._, Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._
import Names._, NameOps._, StdNames._
import typer.Inliner
+import typer.ErrorReporting.cyclicErrorMsg
import dotty.tools.io.Path
import java.io.PrintWriter
@@ -190,7 +191,16 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
def apiClassStructure(csym: ClassSymbol): api.Structure = {
val cinfo = csym.classInfo
- val bases = linearizedAncestorTypes(cinfo)
+ val bases =
+ try linearizedAncestorTypes(cinfo)
+ catch {
+ case ex: CyclicReference =>
+ // See neg/i1750a for an example where a cyclic error can arise.
+ // The root cause in this example is an illegal "override" of an inner trait
+ ctx.error(cyclicErrorMsg(ex), csym.pos)
+ defn.ObjectType :: Nil
+ }
+
val apiBases = bases.map(apiType)
// Synthetic methods that are always present do not affect the API
diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala
index 71ecb5c65..00abddd91 100644
--- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala
@@ -542,7 +542,9 @@ object Erasure extends TypeTestsCasts{
* to deal with boxing and unboxing of value classes ourselves.
*/
override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = {
- val implClosure @ Closure(_, meth, _) = super.typedClosure(tree, pt)
+ val xxl = defn.isUnimplementedFunctionClass(tree.typeOpt.typeSymbol)
+ var implClosure @ Closure(_, meth, _) = super.typedClosure(tree, pt)
+ if (xxl) implClosure = cpy.Closure(implClosure)(tpt = TypeTree(defn.FunctionXXLType))
implClosure.tpe match {
case SAMType(sam) =>
val implType = meth.tpe.widen
diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala
index 27cfc835a..fd4370d3e 100644
--- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala
@@ -175,7 +175,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
case Some(call) =>
if (defn.PhantomClasses.contains(baseCls)) Nil else call :: Nil
case None =>
- if (baseCls.is(NoInitsTrait) || defn.PhantomClasses.contains(baseCls)) Nil
+ if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil
else {
//println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}")
transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil
diff --git a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala
index 650a03054..1f1f371a6 100644
--- a/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala
+++ b/compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala
@@ -6,6 +6,7 @@ import Flags._, Symbols._, Contexts._, Types._, Scopes._, Decorators._
import util.HashSet
import collection.mutable
import collection.immutable.BitSet
+import typer.ErrorReporting.cyclicErrorMsg
import scala.annotation.tailrec
/** A module that can produce a kind of iterator (`Cursor`),
@@ -122,10 +123,18 @@ object OverridingPairs {
if (nextEntry ne null) {
nextEntry = decls.lookupNextEntry(nextEntry)
if (nextEntry ne null) {
- overridden = nextEntry.sym
- if (overriding.owner != overridden.owner && matches(overriding, overridden)) {
- visited += overridden
- if (!hasCommonParentAsSubclass(overriding.owner, overridden.owner)) return
+ try {
+ overridden = nextEntry.sym
+ if (overriding.owner != overridden.owner && matches(overriding, overridden)) {
+ visited += overridden
+ if (!hasCommonParentAsSubclass(overriding.owner, overridden.owner)) return
+ }
+ }
+ catch {
+ case ex: CyclicReference =>
+ // See neg/i1750a for an example where a cyclic error can arise.
+ // The root cause in this example is an illegal "override" of an inner trait
+ ctx.error(cyclicErrorMsg(ex), base.pos)
}
} else {
curEntry = curEntry.prev
diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala
index 5d3096e24..ca62ed0a9 100644
--- a/compiler/src/dotty/tools/dotc/typer/Checking.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala
@@ -338,6 +338,11 @@ object Checking {
checkNoConflict(Final, Sealed)
checkNoConflict(Private, Protected)
checkNoConflict(Abstract, Override)
+ if (sym.isType && !sym.is(Deferred))
+ for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) {
+ fail(i"$sym cannot have the same name as ${cls.showLocated} -- class definitions cannot be overridden")
+ sym.setFlag(Private) // break the overriding relationship by making sym Private
+ }
}
/** Check the type signature of the symbol `M` defined by `tree` does not refer
diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
index 4039c8b81..000cfd026 100644
--- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
@@ -7,10 +7,13 @@ import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.Contexts.Context
-import dotty.tools.dotc.core.Names.Name
+import dotty.tools.dotc.core.Names.{Name, TermName}
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.Decorators._
+import core.Symbols._
+import core.Definitions
+import Inferencing._
import ErrorReporting._
object Dynamic {
@@ -18,7 +21,10 @@ object Dynamic {
name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed
}
-/** Translates selection that does not typecheck according to the scala.Dynamic rules:
+/** Handles programmable member selections of `Dynamic` instances and values
+ * with structural types. Two functionalities:
+ *
+ * 1. Translates selection that does not typecheck according to the scala.Dynamic rules:
* foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux)
* foo.bar = baz ~~> foo.updateDynamic("bar")(baz)
* foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
@@ -26,6 +32,10 @@ object Dynamic {
* foo.bar ~~> foo.selectDynamic(bar)
*
* The first matching rule of is applied.
+ *
+ * 2. Translates member selections on structural types to calls of `selectDynamic`
+ * or `selectDynamicMethod` on a `Selectable` instance. @See handleStructural.
+ *
*/
trait Dynamic { self: Typer with Applications =>
import Dynamic._
@@ -100,4 +110,56 @@ trait Dynamic { self: Typer with Applications =>
else untpd.TypeApply(select, targs)
untpd.Apply(selectWithTypes, Literal(Constant(name.toString)))
}
+
+ /** Handle reflection-based dispatch for members of structural types.
+ * Given `x.a`, where `x` is of (widened) type `T` and `x.a` is of type `U`:
+ *
+ * If `U` is a value type, map `x.a` to the equivalent of:
+ *
+ * (x: Selectable).selectDynamic(x, "a").asInstanceOf[U]
+ *
+ * If `U` is a method type (T1,...,Tn)R, map `x.a` to the equivalent of:
+ *
+ * (x: Selectable).selectDynamicMethod("a", CT1, ..., CTn).asInstanceOf[(T1,...,Tn) => R]
+ *
+ * where CT1,...,CTn are the class tags representing the erasure of T1,...,Tn.
+ *
+ * It's an error if U is neither a value nor a method type, or a dependent method
+ * type, or of too large arity (limit is Definitions.MaxStructuralMethodArity).
+ */
+ def handleStructural(tree: Tree)(implicit ctx: Context): Tree = {
+ val Select(qual, name) = tree
+
+ def structuralCall(selectorName: TermName, formals: List[Tree]) = {
+ val selectable = adapt(qual, defn.SelectableType)
+ val scall = untpd.Apply(
+ untpd.TypedSplice(selectable.select(selectorName)),
+ (Literal(Constant(name.toString)) :: formals).map(untpd.TypedSplice(_)))
+ typed(scall)
+ }
+
+ def fail(reason: String) =
+ errorTree(tree, em"Structural access not allowed on method $name because it $reason")
+
+ tree.tpe.widen match {
+ case tpe: MethodType =>
+ if (tpe.isDependent)
+ fail(i"has a dependent method type")
+ else if (tpe.paramNames.length > Definitions.MaxStructuralMethodArity)
+ fail(i"""takes too many parameters.
+ |Structural types only support methods taking up to ${Definitions.MaxStructuralMethodArity} arguments""")
+ else {
+ def issueError(msgFn: String => String): Unit = ctx.error(msgFn(""), tree.pos)
+ val ctags = tpe.paramTypes.map(pt =>
+ inferImplicitArg(defn.ClassTagType.appliedTo(pt :: Nil), issueError, tree.pos.endPos))
+ structuralCall(nme.selectDynamicMethod, ctags).asInstance(tpe.toFunctionType())
+ }
+ case tpe: ValueType =>
+ structuralCall(nme.selectDynamic, Nil).asInstance(tpe)
+ case tpe: PolyType =>
+ fail("is polymorphic")
+ case tpe =>
+ fail(i"has an unsupported type: $tpe")
+ }
+ }
}
diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
index 3192546cd..e113399c5 100644
--- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
+++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -289,6 +289,8 @@ object RefChecks {
if (!isOverrideAccessOK) {
overrideAccessError()
} else if (other.isClass) {
+ // direct overrides were already checked on completion (see Checking.chckWellFormed)
+ // the test here catches indirect overriddes between two inherited base types.
overrideError("cannot be used here - class definitions cannot be overridden")
} else if (!other.is(Deferred) && member.isClass) {
overrideError("cannot be used here - classes can only override abstract types")
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index d05a0aaa7..59df98a93 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1040,9 +1040,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
typr.println(s"adding refinement $refinement")
checkRefinementNonCyclic(refinement, refineCls, seen)
val rsym = refinement.symbol
- if (rsym.is(Method) && rsym.allOverriddenSymbols.isEmpty)
- ctx.error(i"refinement $rsym without matching type in parent $tpt1", refinement.pos)
- }
+ if (rsym.info.isInstanceOf[PolyType] && rsym.allOverriddenSymbols.isEmpty)
+ ctx.error(i"polymorphic refinement $rsym without matching type in parent $tpt1 is no longer allowed", refinement.pos) }
assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls)
}
@@ -2067,7 +2066,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
adaptInterpolated(tree.appliedToTypeTrees(typeArgs), pt, original))
}
case wtp =>
- pt match {
+ if (isStructuralTermSelect(tree)) adapt(handleStructural(tree), pt)
+ else pt match {
case pt: FunProto =>
adaptToArgs(wtp, pt)
case pt: PolyProto =>