diff options
Diffstat (limited to 'compiler/src')
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 => |