diff options
18 files changed, 186 insertions, 62 deletions
diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 65dcb6c79..8fd6d1bc0 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -1,19 +1,18 @@ package dotty.tools.backend.jvm import dotty.tools.dotc.CompilationUnit -import dotty.tools.dotc.ast.Trees.{ValDef, PackageDef} +import dotty.tools.dotc.ast.Trees.{PackageDef, ValDef} import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Names.TypeName import scala.collection.mutable -import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor} +import scala.tools.asm.{ClassVisitor, CustomAttr, FieldVisitor, MethodVisitor} import scala.tools.nsc.Settings import scala.tools.nsc.backend.jvm._ import dotty.tools.dotc import dotty.tools.dotc.backend.jvm.DottyPrimitives import dotty.tools.dotc.transform.Erasure - import dotty.tools.dotc.interfaces import java.util.Optional @@ -27,14 +26,15 @@ import Symbols._ import Denotations._ import Phases._ import java.lang.AssertionError -import java.io.{ File => JFile } +import java.io.{FileOutputStream, File => JFile} + import scala.tools.asm import scala.tools.asm.tree._ -import dotty.tools.dotc.util.{Positions, DotClass} +import dotty.tools.dotc.util.{DotClass, Positions} import tpd._ import StdNames._ -import scala.reflect.io.{Directory, PlainDirectory, AbstractFile} +import scala.reflect.io.{AbstractFile, Directory, PlainDirectory} import scala.tools.nsc.backend.jvm.opt.LocalOpt class GenBCode extends Phase { @@ -205,7 +205,15 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter if (claszSymbol.isClass) // @DarkDimius is this test needed here? for (binary <- ctx.compilationUnit.pickled.get(claszSymbol.asClass)) { val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary) - (if (mirrorC ne null) mirrorC else plainC).visitAttribute(dataAttr) + val store = if (mirrorC ne null) mirrorC else plainC + store.visitAttribute(dataAttr) + if (ctx.settings.emitTasty.value) { + val outTastyFile = getFileForClassfile(outF, store.name, ".tasty").file + val fos = new FileOutputStream(outTastyFile, false) + fos.write(binary) + fos.close() + + } } // -------------- bean info class, if needed -------------- diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 7020e4dac..a5feecc77 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -486,7 +486,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case SymbolLit(str) => cpy.SymbolLit(tree)(str) case InterpolatedString(id, segments) => - cpy.InterpolatedString(tree)(id, segments.map(transform(_))) + cpy.InterpolatedString(tree)(id, segments.mapConserve(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/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 318295751..49b64d869 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -71,6 +71,7 @@ class ScalaSettings extends Settings.SettingGroup { val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)") val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val log = PhasesSetting("-Ylog", "Log operations during") + val emitTasty = BooleanSetting("-YemitTasty", "Generate tasty in separate *.tasty file.") val Ylogcp = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.") val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") val YnoPredef = BooleanSetting("-Yno-predef", "Compile without importing Predef.") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala index 6c7982d78..86e5be2e2 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala @@ -159,7 +159,6 @@ class TreeBuffer extends TastyBuffer(50000) { val tree = it.next treeAddrs.get(tree) match { case addr: Addr => treeAddrs.put(tree, adjusted(addr)) - case addrs: List[Addr] => treeAddrs.put(tree, addrs.map(adjusted)) } } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index a9ea49ad1..88b6eef7a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -736,7 +736,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle // no longer necessary. goto(end) setPos(start, tree) - sym.info = ta.avoidPrivateLeaks(sym, tree.pos) + if (!sym.isType) { // Only terms might have leaky aliases, see the documentation of `checkNoPrivateLeaks` + sym.info = ta.avoidPrivateLeaks(sym, tree.pos) + } tree } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 8dff58dea..9821757e8 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc package transform import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} -import dotty.tools.dotc.ast.{Trees, tpd} +import dotty.tools.dotc.ast.{Trees, tpd, untpd} import scala.collection.{ mutable, immutable } import ValueClasses._ import scala.annotation.tailrec @@ -258,15 +258,15 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran ) case Import(expr, selectors) => val exprTpe = expr.tpe - def checkIdent(ident: Ident): Unit = { + def checkIdent(ident: untpd.Ident): Unit = { val name = ident.name.asTermName.encode if (name != nme.WILDCARD && !exprTpe.member(name).exists && !exprTpe.member(name.toTypeName).exists) ctx.error(s"${ident.name} is not a member of ${expr.show}", ident.pos) } selectors.foreach { - case ident: Ident => checkIdent(ident) - case Thicket((ident: Ident) :: _) => checkIdent(ident) - case _ => + case ident: untpd.Ident => checkIdent(ident) + case Thicket((ident: untpd.Ident) :: _) => checkIdent(ident) + case _ => } super.transform(tree) case tree => diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index aa0845605..8a695bf22 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -325,6 +325,10 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete else rewriteApply(tree, meth) + case TypeApply(fun, targs) => + val meth = fun.symbol + rewriteApply(tree, meth) + case tree@Block(stats, expr) => tpd.cpy.Block(tree)( noTailTransforms(stats), diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 51e2469b2..dd4d95257 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -139,13 +139,13 @@ class TreeChecker extends Phase with SymTransformer { class Checker(phasesToCheck: Seq[Phase]) extends ReTyper with Checking { val nowDefinedSyms = new mutable.HashSet[Symbol] - val everDefinedSyms = new mutable.HashMap[Symbol, Tree] + val everDefinedSyms = new mutable.HashMap[Symbol, untpd.Tree] // don't check value classes after typer, as the constraint about constructors doesn't hold after transform override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = () def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match { - case tree: DefTree => + case tree: untpd.DefTree => val sym = tree.symbol assert(isValidJVMName(sym.name), s"${sym.fullName} name is invalid on jvm") everDefinedSyms.get(sym) match { @@ -160,7 +160,7 @@ class TreeChecker extends Phase with SymTransformer { if (ctx.settings.YcheckMods.value) { tree match { - case t: MemberDef => + case t: untpd.MemberDef => if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") // todo: compare trees inside annotations case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index b43391592..148ccd6e3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -348,12 +348,23 @@ object Checking { /** Check the type signature of the symbol `M` defined by `tree` does not refer * to a private type or value which is invisible at a point where `M` is still - * visible. As an exception, we allow references to type aliases if the underlying - * type of the alias is not a leak. So type aliases are transparent as far as - * leak testing is concerned. + * visible. + * + * As an exception, we allow references to type aliases if the underlying + * type of the alias is not a leak, and if `sym` is not a type. The rationale + * for this is that the inferred type of a term symbol might contain leaky + * aliases which should be removed (see leak-inferred.scala for an example), + * but a type symbol definition will not contain leaky aliases unless the + * user wrote them, so we can ask the user to change his definition. The more + * practical reason for not transforming types is that `checkNoPrivateLeaks` + * can force a lot of denotations, and this restriction means that we never + * need to run `TypeAssigner#avoidPrivateLeaks` on type symbols when + * unpickling, which avoids some issues related to forcing order. + * + * See i997.scala for negative tests, and i1130.scala for a case where it + * matters that we transform leaky aliases away. + * * @return The `info` of `sym`, with problematic aliases expanded away. - * See i997.scala for tests, i1130.scala for a case where it matters that we - * transform leaky aliases away. */ def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = { class NotPrivate extends TypeMap { @@ -388,7 +399,7 @@ object Checking { tp } else mapOver(tp) - if ((errors ne prevErrors) && tp.info.isAlias) { + if ((errors ne prevErrors) && !sym.isType && tp.info.isAlias) { // try to dealias to avoid a leak error val savedErrors = errors errors = prevErrors @@ -400,8 +411,14 @@ object Checking { case tp: ClassInfo => tp.derivedClassInfo( prefix = apply(tp.prefix), - classParents = tp.parentsWithArgs.map(p => - apply(p).underlyingClassRef(refinementOK = false).asInstanceOf[TypeRef])) + classParents = + tp.parentsWithArgs.map { p => + apply(p).underlyingClassRef(refinementOK = false) match { + case ref: TypeRef => ref + case _ => defn.ObjectType // can happen if class files are missing + } + } + ) case _ => mapOver(tp) } diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index f7dd725c8..f96b8ae1d 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -539,7 +539,7 @@ object ProtoTypes { /** Dummy tree to be used as an argument of a FunProto or ViewProto type */ object dummyTreeOfType { def apply(tp: Type): Tree = untpd.Literal(Constant(null)) withTypeUnchecked tp - def unapply(tree: Tree): Option[Type] = tree match { + def unapply(tree: untpd.Tree): Option[Type] = tree match { case Literal(Constant(null)) => Some(tree.typeOpt) case _ => None } diff --git a/doc-tool/resources/_layouts/main.html b/doc-tool/resources/_layouts/main.html index 7c63ec610..eb0dd51fd 100644 --- a/doc-tool/resources/_layouts/main.html +++ b/doc-tool/resources/_layouts/main.html @@ -13,6 +13,7 @@ integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi" crossorigin="anonymous" > + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" diff --git a/doc-tool/resources/_layouts/sidebar.html b/doc-tool/resources/_layouts/sidebar.html index b3947c884..24ed1bed7 100644 --- a/doc-tool/resources/_layouts/sidebar.html +++ b/doc-tool/resources/_layouts/sidebar.html @@ -33,9 +33,12 @@ layout: main </ul> </div> <div id="content-body"> - <div id="menu-toggle" onclick="toggleMenu()"> - <i class="fa fa-bars" aria-hidden="true"></i> - </div> + <button type="button" id="menu-toggle" onclick="toggleMenu()" aria-expanded="false"> + <span class="sr-only" aria-hidden="true">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> {{ content }} </div> </div> diff --git a/doc-tool/resources/css/dottydoc.css b/doc-tool/resources/css/dottydoc.css index a89e23375..140f15eba 100644 --- a/doc-tool/resources/css/dottydoc.css +++ b/doc-tool/resources/css/dottydoc.css @@ -1,23 +1,31 @@ +html, body { + overflow-x: hidden; +} + html { height: 100%; } + body { min-height: 100%; } div#content-wrapper { min-height: 100vh; - padding-left: 250px; - transition: all 0.5s ease; + padding-left: 0; } -div#content-wrapper.toggled { - padding-left: 0; +div#content-wrapper div#content-body { + background-color: #f4f3f4; + border-left: 1px solid #e0e0e0; + box-shadow: -3px 0 5px -2px rgba(0, 0, 0, 0.14); + padding: 40px 1px 1px 1px; + position: relative; + min-height: 100vh; } -div.index-wrapper { +div#content-wrapper div.index-wrapper { background-color: #fafafa; - width: 250px; position: fixed; top: 0; left: 0; @@ -26,6 +34,77 @@ div.index-wrapper { overflow-x: hidden; } +/** Animations: Easing for content body slide on toggle */ +div#content-wrapper, +div#content-wrapper div#content-body { + -webkit-transition: all .25s ease-out; + -o-transition: all .25s ease-out; + transition: all .25s ease-out; +} + +/** Mobile (x < 576px) sidebar: Defaults closed with 60% wide sidebar */ +div#content-wrapper {} +div#content-wrapper div#content-body { left: 0; } +div#content-wrapper div.index-wrapper { width: 60%; } + +div#content-wrapper.toggled {} +div#content-wrapper.toggled div#content-body { left: 60%; } +div#content-wrapper.toggled div.index-wrapper {} + +/** Tablet (576px <= x < 768px) sidebar: Defaults closed with 250px wide sidebar */ +@media screen and (min-width: 576px) { + + div#content-wrapper {} + div#content-wrapper div#content-body {} + div#content-wrapper div.index-wrapper { width: 250px; } + + div#content-wrapper.toggled {} + div#content-wrapper.toggled div#content-body { left: 250px; } + div#content-wrapper.toggled div.index-wrapper {} + +} + +/** Desktop (x >= 768px) sidebar: Defaults open with 250px wide sidebar */ +@media screen and (min-width: 768px) { + + div#content-wrapper { padding-left: 250px; } + div#content-wrapper div#content-body {} + div#content-wrapper div.index-wrapper { width: 250px; } + + div#content-wrapper.toggled { padding-left: 0; } + div#content-wrapper.toggled div#content-body { left: 0; } + div#content-wrapper.toggled div.index-wrapper {} + +} + +div#content-wrapper button#menu-toggle { + background: rgba(244, 243, 244, 0.4) none; + border: 1px solid transparent; + color: #837f84; + margin: -30px 0 0 10px; + padding: 9px 10px; + position: absolute; +} + +div#content-wrapper button#menu-toggle:hover { + cursor: pointer; +} + +div#content-wrapper button#menu-toggle:focus { + outline: none; +} + +div#content-wrapper button#menu-toggle span.icon-bar { + background-color: #837f84; + border-radius: 2px; + border-color: #837f84; + color: #837f84; + display: block; + margin: 3px 0; + width: 15px; + height: 2px; +} + div#doc-page-container > h1 { border-bottom: 1px solid #eee; padding-bottom: 0.3em; @@ -93,27 +172,6 @@ div#doc-page-container > h6 > a:focus { outline: none; } -div#content-body { - border-left: 1px solid #e0e0e0; - box-shadow: -3px 0px 5px -2px rgba(0,0,0,0.14); - position: relative; - padding: 10px; - background-color: #f4f3f4; - min-height: 100vh; -} - -div#menu-toggle { - color: #837F84; - outline: none; - padding-left: 20px; - padding-top: 10px; -} - -div#menu-toggle:hover { - color: rgba(0, 0, 0, 0.4); - cursor: pointer; -} - ul.index-entities { list-style-type: none; padding-left: 0; @@ -195,7 +253,7 @@ ul.index-entities > li > a.entity-name { font-size: 13px; display: block; padding: 0 0 0 24px; - color: rgba(0,0,0,.87); + color: rgba(0, 0, 0, 0.87); background: transparent; cursor: pointer; float: left; @@ -245,7 +303,7 @@ ul.toc > li > ul.hide { ul.index-entities > li.index-title > span { font-size: 16px; font-weight: bold; - color: rgba(0,0,0,.87); + color: rgba(0, 0, 0, 0.87); padding: 0 24px; } @@ -319,7 +377,7 @@ pre { background: rgba(244, 243, 244, 0.6); border-radius: 2px; margin-top: 20px; - border: 1px solid rgba(0,0,0,0.1); + border: 1px solid rgba(0, 0, 0, 0.1); } pre > code.language-none, diff --git a/tests/neg/leak-type.scala b/tests/neg/leak-type.scala new file mode 100644 index 000000000..30ecab70b --- /dev/null +++ b/tests/neg/leak-type.scala @@ -0,0 +1,13 @@ +trait A { + private type Foo = Int + + + class Inner[T <: Foo] { // error: non-private type T refers to private type Foo in its type signature + def get: T = ??? + } +} +class B extends A { + def foo(x: Inner[_]): Unit = { + val a = x.get // error: cannot resolve reference to type B(B.this).Foo + } +} diff --git a/tests/pos/i1130.scala b/tests/pos/i1130.scala index 8d71de5e8..c28eaa169 100644 --- a/tests/pos/i1130.scala +++ b/tests/pos/i1130.scala @@ -3,4 +3,6 @@ trait A { def foo: Foo = 1 } -class B extends A +class B extends A { + foo +} diff --git a/tests/pos/leak-inferred.scala b/tests/pos/leak-inferred.scala new file mode 100644 index 000000000..5d8a7e3bc --- /dev/null +++ b/tests/pos/leak-inferred.scala @@ -0,0 +1,12 @@ +class A { + private val x = List(1,2) + + val elem = x.head +} + +class B extends A { + val a: Int = elem + // Without `checkNoPrivateLeaks`, we get: + // found: B.this.x.scala$collection$immutable$List$$A(B.this.elem) + // required: Int +} diff --git a/tests/pos/tailcall/i2024.scala b/tests/pos/tailcall/i2024.scala new file mode 100644 index 000000000..aba1db2e5 --- /dev/null +++ b/tests/pos/tailcall/i2024.scala @@ -0,0 +1,4 @@ +object Test { +// def main(args: Array[String]): Unit = { } + def assume[T]: Any = assume +} diff --git a/tests/pos/tailcall/return.scala b/tests/pos/tailcall/return.scala index 454686c3d..3724b5b32 100644 --- a/tests/pos/tailcall/return.scala +++ b/tests/pos/tailcall/return.scala @@ -1,4 +1,4 @@ -object Test { +object Return { def foo(x: Int): Int = return 3 |