diff options
-rw-r--r-- | src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Run.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/tpd.scala | 33 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Flags.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/StdNames.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Symbols.scala | 24 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 10 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/ExpandSAMs.scala | 88 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/SymUtils.scala | 24 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/TreeTransform.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 5 | ||||
-rw-r--r-- | test/dotc/tests.scala | 3 | ||||
-rw-r--r-- | tests/disabled/simplesams.scala | 9 | ||||
-rw-r--r-- | tests/neg/sammy_poly.scala | 7 | ||||
-rw-r--r-- | tests/pos/Patterns.scala | 5 | ||||
-rw-r--r-- | tests/pos/sams.scala | 75 |
17 files changed, 265 insertions, 37 deletions
diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 7313070e0..dc6752460 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -647,7 +647,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{ } def parentSymbols: List[Symbol] = toDenot(sym).info.parents.map(_.typeSymbol) def superClass: Symbol = { - val t = toDenot(sym).superClass + val t = toDenot(sym).asClass.superClass if (t.exists) t else if (sym is Flags.ModuleClass) { // workaround #371 @@ -712,7 +712,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{ * All interfaces implemented by a class, except for those inherited through the superclass. * */ - def superInterfaces: List[Symbol] = decorateSymbol(sym).superInterfaces + def superInterfaces: List[Symbol] = decorateSymbol(sym).directlyInheritedTraits /** * True for module classes of package level objects. The backend will generate a mirror class for diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 93a4edd6f..d4837ae74 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -45,6 +45,7 @@ class Compiler { new ElimRepeated, new NormalizeFlags, new ExtensionMethods, + new ExpandSAMs, new TailRec), List(new PatternMatcher, new ExplicitOuter, diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index c99f5efb9..151288d23 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -33,7 +33,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { compileSources(sources) } catch { case NonFatal(ex) => - println(s"exception occurred while compiling $units%, %") + println(i"exception occurred while compiling $units%, %") throw ex } diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index de0ef3344..dce06da95 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -210,7 +210,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.TypeDef(sym.name, TypeTree(sym.info)), sym) def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(implicit ctx: Context): TypeDef = { - val firstParent :: otherParents = cls.info.parents + val firstParentRef :: otherParentRefs = cls.info.parents + val firstParent = cls.typeRef.baseTypeWithArgs(firstParentRef.symbol) val superRef = if (cls is Trait) TypeTree(firstParent) else { @@ -225,7 +226,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(constr => isApplicable(constr.info)) New(firstParent, constr.symbol.asTerm, superArgs) } - val parents = superRef :: otherParents.map(TypeTree(_)) + val parents = superRef :: otherParentRefs.map(TypeTree(_)) val selfType = if (cls.classInfo.selfInfo ne NoType) ValDef(ctx.newSelfSym(cls)) @@ -244,6 +245,33 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.TypeDef(cls.name, impl), cls) } + /** An anonymous class + * + * new parents { forwarders } + * + * where `forwarders` contains forwarders for all functions in `fns`. + * @param parents a non-empty list of class types + * @param fns a non-empty of functions for which forwarders should be defined in the class. + * The class has the same owner as the first function in `fns`. + * Its position is the union of all functions in `fns`. + */ + def AnonClass(parents: List[Type], fns: List[TermSymbol], methNames: List[TermName])(implicit ctx: Context): Block = { + val owner = fns.head.owner + val parents1 = + if (parents.head.classSymbol.is(Trait)) defn.ObjectClass.typeRef :: parents + else parents + val cls = ctx.newNormalizedClassSymbol(owner, tpnme.ANON_FUN, Synthetic, parents1, + coord = fns.map(_.pos).reduceLeft(_ union _)) + val constr = ctx.newConstructor(cls, Synthetic, Nil, Nil).entered + def forwarder(fn: TermSymbol, name: TermName) = { + val fwdMeth = fn.copy(cls, name, Synthetic | Method).entered.asTerm + DefDef(fwdMeth, prefss => ref(fn).appliedToArgss(prefss)) + } + val forwarders = (fns, methNames).zipped.map(forwarder) + val cdef = ClassDef(cls, DefDef(constr), forwarders) + Block(cdef :: Nil, New(cls.typeRef, Nil)) + } + // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() } def WhileDo(owner: Symbol, cond: Tree, body: List[Tree])(implicit ctx: Context): Tree = { val sym = ctx.newSymbol(owner, nme.WHILE_PREFIX, Flags.Label | Flags.Synthetic, @@ -254,7 +282,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Block(List(DefDef(sym, rhs)), call) } - def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(ctx.owner, expr)) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index b6f3c99e7..cfa0faef9 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -332,7 +332,7 @@ object Flags { final val JavaStaticTerm = JavaStatic.toTermFlags final val JavaStaticType = JavaStatic.toTypeFlags - /** Trait is not an interface, but does not have fields or initialization code */ + /** Trait does not have fields or initialization code */ final val NoInits = typeFlag(32, "<noInits>") /** Variable is accessed from nested function. */ diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 74a121b47..826cbe2c6 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -418,6 +418,7 @@ object StdNames { val isArray: N = "isArray" val isDefined: N = "isDefined" val isDefinedAt: N = "isDefinedAt" + val isDefinedAtImpl: N = "$isDefinedAt" val isEmpty: N = "isEmpty" val isInstanceOf_ : N = "isInstanceOf" val java: N = "java" diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 2b91efbcd..94ac258da 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -109,6 +109,30 @@ trait Symbols { this: Context => ClassInfo(owner.thisType, _, parents, decls, selfInfo), privateWithin, coord, assocFile) + /** Same as `newCompleteClassSymbol` except that `parents` can be a list of arbitary + * types which get normalized into type refs and parameter bindings. + */ + def newNormalizedClassSymbol( + owner: Symbol, + name: TypeName, + flags: FlagSet, + parentTypes: List[Type], + decls: Scope = newScope, + selfInfo: Type = NoType, + privateWithin: Symbol = NoSymbol, + coord: Coord = NoCoord, + assocFile: AbstractFile = null): ClassSymbol = { + def completer = new LazyType { + def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { + val cls = denot.asClass.classSymbol + val decls = newScope + val parentRefs: List[TypeRef] = normalizeToClassRefs(parentTypes, cls, decls) + denot.info = ClassInfo(owner.thisType, cls, parentRefs, decls) + } + } + newClassSymbol(owner, name, flags, completer, privateWithin, coord, assocFile) + } + /** Create a module symbol with associated module class * from its non-info fields and a function producing the info * of the module class (this info may be lazy). diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 595732b37..80cceed38 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -545,6 +545,11 @@ object Types { (name, buf) => buf ++= member(name).altsWith(x => x.isClass)) } + final def fields(implicit ctx: Context): Seq[SingleDenotation] = track("fields") { + memberDenots(fieldFilter, + (name, buf) => buf ++= member(name).altsWith(x => !x.is(Method))) + } + /** The set of members of this type having at least one of `requiredFlags` but none of `excludedFlags` set */ final def membersBasedOnFlags(requiredFlags: FlagSet, excludedFlags: FlagSet)(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") { memberDenots(takeAllFilter, @@ -3049,6 +3054,11 @@ object Types { def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = name.isTypeName } + object fieldFilter extends NameFilter { + def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = + name.isTermName && (pre member name).hasAltWith(!_.symbol.is(Method)) + } + object takeAllFilter extends NameFilter { def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true } diff --git a/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/src/dotty/tools/dotc/transform/ExpandSAMs.scala new file mode 100644 index 000000000..bba42f403 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -0,0 +1,88 @@ +package dotty.tools.dotc +package transform + +import core._ +import Contexts._, Symbols._, Types._, Flags._, Decorators._, StdNames._, Constants._ +import SymDenotations.SymDenotation +import TreeTransforms._ +import SymUtils._ +import ast.untpd +import ast.Trees._ + +/** Expand SAM closures that cannot be represented by the JVM as lambdas to anonymous classes. + * These fall into five categories + * + * 1. Partial function closures, we need to generate a isDefinedAt method for these. + * 2. Closures implementing non-trait classes. + * 3. Closures implementing classes that inherit from a class other than Object + * (a lambda cannot not be a run-time subtype of such a class) + * 4. Closures that implement traits which run initialization code. + * 5. Closures that get synthesized abstract methods in the transformation pipeline. These methods can be + * (1) superaccessors, (2) outer references, (3) accessors for fields. + */ +class ExpandSAMs extends MiniPhaseTransform { thisTransformer => + override def phaseName = "expandSAMs" + + import ast.tpd._ + + def noJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean = + !cls.is(Trait) || + cls.superClass != defn.ObjectClass || + !cls.is(NoInits) || + !cls.directlyInheritedTraits.forall(_.is(NoInits)) || + ExplicitOuter.needsOuterIfReferenced(cls) || + cls.typeRef.fields.nonEmpty // Superaccessors already show up as abstract methods here, so no test necessary + + + override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { + case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol => + tpt.tpe match { + case NoType => tree // it's a plain function + case tpe @ SAMType(_) if !noJvmSam(tpe.classSymbol.asClass) => + if (tpe isRef defn.PartialFunctionClass) toPartialFunction(tree) + else tree + case tpe => + cpy.Block(tree)(stats, + AnonClass(tpe :: Nil, fn.symbol.asTerm :: Nil, nme.apply :: Nil)) + } + case _ => + tree + } + + private def toPartialFunction(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = { + val Block( + (applyDef @ DefDef(nme.ANON_FUN, Nil, List(List(param)), _, _)) :: Nil, + Closure(_, _, tpt)) = tree + val applyRhs: Tree = applyDef.rhs + val applyFn = applyDef.symbol.asTerm + + val MethodType(paramNames, paramTypes) = applyFn.info + val isDefinedAtFn = applyFn.copy( + name = nme.isDefinedAtImpl, + flags = Synthetic | Method, + info = MethodType(paramNames, paramTypes, defn.BooleanType)).asTerm + val tru = Literal(Constant(true)) + def isDefinedAtRhs(paramRefss: List[List[Tree]]) = applyRhs match { + case Match(selector, cases) => + assert(selector.symbol == param.symbol) + val paramRef = paramRefss.head.head + // Again, the alternative + // val List(List(paramRef)) = paramRefs + // fails with a similar self instantiation error + def translateCase(cdef: CaseDef): CaseDef = + cpy.CaseDef(cdef)(body = tru).changeOwner(applyFn, isDefinedAtFn) + val defaultSym = ctx.newSymbol(isDefinedAtFn, nme.WILDCARD, Synthetic, selector.tpe.widen) + val defaultCase = + CaseDef( + Bind(defaultSym, untpd.Ident(nme.WILDCARD).withType(selector.tpe.widen)), + EmptyTree, + Literal(Constant(false))) + cpy.Match(applyRhs)(paramRef, cases.map(translateCase) :+ defaultCase) + case _ => + tru + } + val isDefinedAtDef = transformFollowingDeep(DefDef(isDefinedAtFn, isDefinedAtRhs(_))) + val anonCls = AnonClass(tpt.tpe :: Nil, List(applyFn, isDefinedAtFn), List(nme.apply, nme.isDefinedAt)) + cpy.Block(tree)(List(applyDef, isDefinedAtDef), anonCls) + } +} diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index 41a0938b8..9b4b06601 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -24,30 +24,20 @@ object SymUtils { class SymUtils(val self: Symbol) extends AnyVal { import SymUtils._ - def superClass(implicit ctx: Context) = { - val parents = self.asClass.classInfo.parents - if (parents.isEmpty) NoSymbol - else parents.head.symbol - } - - - /** - * For a class: All interfaces implemented by a class except for those inherited through the superclass. - * For a trait: all parent traits - */ - - def superInterfaces(implicit ctx: Context) = { - val superCls = self.superClass + /** All traits implemented by a class or trait except for those inherited through the superclass. */ + def directlyInheritedTraits(implicit ctx: Context) = { + val superCls = self.asClass.superClass val baseClasses = self.asClass.baseClasses if (baseClasses.isEmpty) Nil else baseClasses.tail.takeWhile(_ ne superCls).reverse - } - /** All interfaces implemented by a class, except for those inherited through the superclass. */ + /** All traits implemented by a class, except for those inherited through the superclass. + * The empty list if `self` is a trait. + */ def mixins(implicit ctx: Context) = { if (self is Trait) Nil - else superInterfaces + else directlyInheritedTraits } def isTypeTestOrCast(implicit ctx: Context): Boolean = diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 3bd005991..0aba1e213 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -486,7 +486,14 @@ object TreeTransforms { var nxCopied = false var result = info.transformers var resultNX = info.nx - var i = mutationPlan(0) // if TreeTransform.transform() method didn't exist we could have used mutationPlan(cur) + var i = mutationPlan(cur) + // @DarkDimius You commented on the previous version + // + // var i = mutationPlan(0) // if TreeTransform.transform() method didn't exist we could have used mutationPlan(cur) + // + // But we need to use `cur` or otherwise we call prepare actions preceding the + // phase that issued a transformFollowing. This can lead to "denotation not defined + // here" errors. Note that tests still pass with the current modified code. val l = result.length var allDone = i < l while (i < l) { diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index b58f48728..191a13ad0 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -844,8 +844,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = track("typedBind") { - val body1 = typed(tree.body, pt) - typr.println(i"typed bind $tree pt = $pt bodytpe = ${body1.tpe}") + val pt1 = fullyDefinedType(pt, "pattern variable", tree.pos) + val body1 = typed(tree.body, pt1) + typr.println(i"typed bind $tree pt = $pt1 bodytpe = ${body1.tpe}") val flags = if (tree.isType) BindDefinedType else EmptyFlags val sym = ctx.newSymbol(ctx.owner, tree.name, flags, body1.tpe, coord = tree.pos) assignType(cpy.Bind(tree)(tree.name, body1), sym) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 7761589ad..0e86303a2 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -106,9 +106,6 @@ class tests extends CompilerTest { @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 6) @Test def neg_t0273_doubledefs = compileFile(negDir, "t0273", xerrors = 1) @Test def neg_zoo = compileFile(negDir, "zoo", xerrors = 12) - @Test def neg_sam = compileFile(negDir, "sammy_poly", xerrors = 1) - // TODO: this test file doesn't exist (anymore?), remove? - // @Test def neg_t1192_legalPrefix = compileFile(negDir, "t1192", xerrors = 1) val negTailcallDir = negDir + "tailcall/" @Test def neg_tailcall_t1672b = compileFile(negTailcallDir, "t1672b", xerrors = 6) diff --git a/tests/disabled/simplesams.scala b/tests/disabled/simplesams.scala new file mode 100644 index 000000000..14a7ba6c0 --- /dev/null +++ b/tests/disabled/simplesams.scala @@ -0,0 +1,9 @@ +package test + +trait X { def foo(x: Int): Int; def bar = foo(2) } +trait XX extends X + +object test { + val x: X = (x: Int) => 2 // should be a closure + val xx: XX = (x: Int) => 2 // should be a closure, but blows up in backend +} diff --git a/tests/neg/sammy_poly.scala b/tests/neg/sammy_poly.scala deleted file mode 100644 index 8d0236496..000000000 --- a/tests/neg/sammy_poly.scala +++ /dev/null @@ -1,7 +0,0 @@ -// test synthesizeSAMFunction where the sam type is not fully defined -class T { - trait F[T, U] { def apply(x: T): U } - // this is an inner trait, that will recieve an abstract $outer pointer. Not a SAM. - def app[T, U](x: T)(f: F[T, U]): U = f(x) - app(1)(x => List(x)) -} diff --git a/tests/pos/Patterns.scala b/tests/pos/Patterns.scala index e443c2ab5..e9bce87a9 100644 --- a/tests/pos/Patterns.scala +++ b/tests/pos/Patterns.scala @@ -94,3 +94,8 @@ object Patterns { t } } + +object NestedPattern { + val xss: List[List[String]] = ??? + val List(List(x)) = xss +} diff --git a/tests/pos/sams.scala b/tests/pos/sams.scala new file mode 100644 index 000000000..b7ef7dd2d --- /dev/null +++ b/tests/pos/sams.scala @@ -0,0 +1,75 @@ +object test { + + trait X { def foo(x: Int): Int; def bar = foo(2) } + + val x: X = (x: Int) => 2 // should be a closure + + trait T { + var f = 2 + def foo(x: Int): Int + } + + val t: T = (x: Int) => 2 // needs to be an anonymous class because of defined field + + trait U extends T + + val u: U = (x: Int) => 2 // needs to be an anonymous class because of inherited field + + trait V extends Exception { def foo(x: Int): Int } + + val v: V = (x: Int) => 2 // needs to be an anonymous class because the trait extends a non-object class + + trait Y extends X { + def baz = super.bar + } + + val y: Y = (x: Int) => 2 // needs to be an anonymous class because of super accessor + + trait Z { + def foo(x: Int): Int; println("hi there!") + } + trait ZZ extends Z + + val z: Z = (x: Int) => 2 // needs to be an anonymous class because trait has initialization code + val zz: ZZ = (x: Int) => 2 // needs to be an anonymous class becaiuse trait has initialization code + + abstract class C { + def foo(x: Int): Int + + trait I { def foo(x: Int): Int } + + } + + val c: C = (x: Int) => 2 // needs to be an anonymous class because C is not a trait + + val ci: c.I = (x: Int) => 2 // needs to be an anonymous class because it needs an outer pointer + + + val pf: PartialFunction[Int, Int] = { + case 1 => 1 + case 2 => 2 + } + + val qf: PartialFunction[(Int, String), Int] = { + case (1, "abc") => 1 + case _ => 2 + } + + val rf: PartialFunction[(Int, AnyRef), Int] = { + case (_: Int, _: String) => 1 + case _ => 2 + } + + val sf: PartialFunction[Any, Int] = { + case x: String if x == "abc" => 1 + } +} + +// From: neg/sammy_poly +// synthesizeSAMFunction where the sam type is not fully defined +class T { + trait F[T, U] { def apply(x: T): U } + // this is an inner trait, that will recieve an abstract $outer pointer. Not a SAM. + def app[T, U](x: T)(f: F[T, U]): U = f(x) + app(1)(x => List(x)) +} |