diff options
Diffstat (limited to 'compiler/src/dotty')
34 files changed, 417 insertions, 250 deletions
diff --git a/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala b/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala index 371396e36..654507991 100644 --- a/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala +++ b/compiler/src/dotty/tools/backend/jvm/LabelDefs.scala @@ -37,44 +37,47 @@ import StdNames.nme /** * Verifies that each Label DefDef has only a single address to jump back and - * reorders them such that they are not nested and this address is a fall-through address for JVM - * - * ei such code - * + * reorders them such that they are not nested and this address is a + * fall-through address for the JVM. * + * ```scala * <label> def foo(i: Int) = { * <label> def bar = 0 * <label> def dough(i: Int) = if (i == 0) bar else foo(i-1) * dough(i) - * } + * } * * foo(100) + * ``` * - * will get rewritten to + * will get rewritten to: * - * \ + * ```scala * <label> def foo(i: Int) = dough(i) * <label> def dough(i: Int) = if (i == 0) bar else foo(i-1) * <label> def bar = 2 * foo(100) + * ``` * - * Proposed way to generate this pattern in backend is: + * Proposed way to generate this pattern in backend is: * - * foo(100) - * <jump foo> - * <label> def foo(i: Int) = dough(i) - * // <jump a> // unreachable - * <label> def dough(i: Int) = if (i == 0) bar else foo(i-1) - * // <jump a> // unreachable - * <label> def bar = 2 - * // <jump a> // unreachable - * <asm point a> + * ```scala + * foo(100) + * <jump foo> + * <label> def foo(i: Int) = dough(i) + * // <jump a> // unreachable + * <label> def dough(i: Int) = if (i == 0) bar else foo(i-1) + * // <jump a> // unreachable + * <label> def bar = 2 + * // <jump a> // unreachable + * <asm point a> + * ``` * - * Unreachable jumps will be eliminated by local dead code analysis. - * After JVM is smart enough to remove next-line jumps + * Unreachable jumps will be eliminated by local dead code analysis. + * After JVM is smart enough to remove next-line jumps * - * Note that Label DefDefs can be only nested in Block, otherwise no one would be able to call them - * Other DefDefs are eliminated + * Note that Label DefDefs can be only nested in Block, otherwise no one would + * be able to call them Other DefDefs are eliminated */ class LabelDefs extends MiniPhaseTransform { def phaseName: String = "labelDef" diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 0f652ff0b..38bf80bad 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -15,6 +15,7 @@ import reporting.Reporter import transform.TreeChecker import rewrite.Rewrites import java.io.{BufferedWriter, OutputStreamWriter} +import printing.XprintMode import scala.annotation.tailrec import scala.reflect.io.VirtualFile @@ -95,7 +96,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { val unit = ctx.compilationUnit val prevPhase = ctx.phase.prev // can be a mini-phase val squashedPhase = ctx.squashed(prevPhase) - val treeString = unit.tpdTree.show + val treeString = unit.tpdTree.show(ctx.withProperty(XprintMode, Some(()))) ctx.echo(s"result of $unit after $squashedPhase:") diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 2e471e046..eda4a12dc 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -353,7 +353,7 @@ object desugar { for (i <- 0 until arity if nme.selectorName(i) `ne` caseParams(i).name) yield syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) def isRepeated(tree: Tree): Boolean = tree match { - case PostfixOp(_, nme.raw.STAR) => true + case PostfixOp(_, Ident(nme.raw.STAR)) => true case ByNameTypeTree(tree1) => isRepeated(tree1) case _ => false } @@ -739,23 +739,25 @@ object desugar { /** Translate infix operation expression left op right */ - def makeBinop(left: Tree, op: Name, right: Tree): Tree = { + def makeBinop(left: Tree, op: Ident, right: Tree): Tree = { def assignToNamedArg(arg: Tree) = arg match { case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs) case _ => arg } - if (isLeftAssoc(op)) { + if (isLeftAssoc(op.name)) { val args: List[Tree] = right match { case Parens(arg) => assignToNamedArg(arg) :: Nil case Tuple(args) => args mapConserve assignToNamedArg case _ => right :: Nil } - Apply(Select(left, op), args) + val selectPos = Position(left.pos.start, op.pos.end, op.pos.start) + Apply(Select(left, op.name).withPos(selectPos), args) } else { val x = ctx.freshName().toTermName + val selectPos = Position(op.pos.start, right.pos.end, op.pos.start) new InfixOpBlock( ValDef(x, TypeTree(), left).withMods(synthetic), - Apply(Select(right, op), Ident(x))) + Apply(Select(right, op.name).withPos(selectPos), Ident(x).withPos(left.pos))) } } @@ -956,25 +958,25 @@ object desugar { Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems) case InfixOp(l, op, r) => if (ctx.mode is Mode.Type) - if (op == tpnme.raw.AMP) AndTypeTree(l, r) // l & r - else if (op == tpnme.raw.BAR) OrTypeTree(l, r) // l | r - else AppliedTypeTree(Ident(op), l :: r :: Nil) // op[l, r] + if (!op.isBackquoted && op.name == tpnme.raw.AMP) AndTypeTree(l, r) // l & r + else if (!op.isBackquoted && op.name == tpnme.raw.BAR) OrTypeTree(l, r) // l | r + else AppliedTypeTree(op, l :: r :: Nil) // op[l, r] else if (ctx.mode is Mode.Pattern) - Apply(Ident(op), l :: r :: Nil) // op(l, r) + Apply(op, l :: r :: Nil) // op(l, r) else // l.op(r), or val x = r; l.op(x), plus handle named args specially makeBinop(l, op, r) case PostfixOp(t, op) => - if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) { + if ((ctx.mode is Mode.Type) && !op.isBackquoted && op.name == nme.raw.STAR) { val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType Annotated( AppliedTypeTree(ref(seqType), t), New(ref(defn.RepeatedAnnotType), Nil :: Nil)) } else { assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode) - Select(t, op) + Select(t, op.name) } case PrefixOp(op, t) => - Select(t, nme.UNARY_PREFIX ++ op) + Select(t, nme.UNARY_PREFIX ++ op.name) case Parens(t) => t case Tuple(ts) => diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala index bb6817603..51949c6fe 100644 --- a/compiler/src/dotty/tools/dotc/ast/Positioned.scala +++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala @@ -123,6 +123,8 @@ abstract class Positioned extends DotClass with Product { private def unionPos(pos: Position, xs: List[_]): Position = xs match { case (p: Positioned) :: xs1 => unionPos(pos union p.pos, xs1) + case (xs0: List[_]) :: xs1 => unionPos(unionPos(pos, xs0), xs1) + case _ :: xs1 => unionPos(pos, xs1) case _ => pos } 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/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 798f0f567..bf11a442e 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -3,7 +3,7 @@ package dotc package ast import core._ -import Types._, Names._, Flags._, util.Positions._, Contexts._, Constants._ +import Types._, Names._, NameOps._, Flags._, util.Positions._, Contexts._, Constants._ import SymDenotations._, Symbols._, Denotations._, StdNames._, Comments._ import annotation.tailrec import language.higherKinds @@ -316,7 +316,7 @@ object Trees { def namePos = if (pos.exists) if (rawMods.is(Synthetic)) Position(pos.point, pos.point) - else Position(pos.point, pos.point + name.length, pos.point) + else Position(pos.point, pos.point + name.stripModuleClassSuffix.length, pos.point) else pos } @@ -334,10 +334,15 @@ object Trees { extends RefTree[T] { type ThisTree[-T >: Untyped] = Ident[T] def qualifier: Tree[T] = genericEmptyTree + + /** Is this a `BackquotedIdent` ? */ + def isBackquoted: Boolean = false } class BackquotedIdent[-T >: Untyped] private[ast] (name: Name) extends Ident[T](name) { + override def isBackquoted: Boolean = true + override def toString = s"BackquotedIdent($name)" } @@ -1069,95 +1074,100 @@ object Trees { Template(tree: Tree)(constr, parents, self, body) } + /** Hook to indicate that a transform of some subtree should be skipped */ + protected def skipTransform(tree: Tree)(implicit ctx: Context): Boolean = false + abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { - def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Ident(name) => - tree - case Select(qualifier, name) => - cpy.Select(tree)(transform(qualifier), name) - case This(qual) => - tree - case Super(qual, mix) => - cpy.Super(tree)(transform(qual), mix) - case Apply(fun, args) => - cpy.Apply(tree)(transform(fun), transform(args)) - case TypeApply(fun, args) => - cpy.TypeApply(tree)(transform(fun), transform(args)) - case Literal(const) => - tree - case New(tpt) => - cpy.New(tree)(transform(tpt)) - case Typed(expr, tpt) => - cpy.Typed(tree)(transform(expr), transform(tpt)) - case NamedArg(name, arg) => - cpy.NamedArg(tree)(name, transform(arg)) - case Assign(lhs, rhs) => - cpy.Assign(tree)(transform(lhs), transform(rhs)) - case Block(stats, expr) => - cpy.Block(tree)(transformStats(stats), transform(expr)) - case If(cond, thenp, elsep) => - cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) - case Closure(env, meth, tpt) => - cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) - case Match(selector, cases) => - cpy.Match(tree)(transform(selector), transformSub(cases)) - case CaseDef(pat, guard, body) => - cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) - case Return(expr, from) => - cpy.Return(tree)(transform(expr), transformSub(from)) - case Try(block, cases, finalizer) => - cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) - case SeqLiteral(elems, elemtpt) => - cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) - case Inlined(call, bindings, expansion) => - cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)) - case TypeTree() => - tree - case SingletonTypeTree(ref) => - cpy.SingletonTypeTree(tree)(transform(ref)) - case AndTypeTree(left, right) => - cpy.AndTypeTree(tree)(transform(left), transform(right)) - case OrTypeTree(left, right) => - cpy.OrTypeTree(tree)(transform(left), transform(right)) - case RefinedTypeTree(tpt, refinements) => - cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) - case AppliedTypeTree(tpt, args) => - cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) - case PolyTypeTree(tparams, body) => - cpy.PolyTypeTree(tree)(transformSub(tparams), transform(body)) - case ByNameTypeTree(result) => - cpy.ByNameTypeTree(tree)(transform(result)) - case TypeBoundsTree(lo, hi) => - cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) - case Bind(name, body) => - cpy.Bind(tree)(name, transform(body)) - case Alternative(trees) => - cpy.Alternative(tree)(transform(trees)) - case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) - case EmptyValDef => - tree - case tree @ ValDef(name, tpt, _) => - val tpt1 = transform(tpt) - val rhs1 = transform(tree.rhs) - cpy.ValDef(tree)(name, tpt1, rhs1) - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) - case tree @ TypeDef(name, rhs) => - cpy.TypeDef(tree)(name, transform(rhs)) - case tree @ Template(constr, parents, self, _) => - cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(tree.body)) - case Import(expr, selectors) => - cpy.Import(tree)(transform(expr), selectors) - case PackageDef(pid, stats) => - cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)) - case Annotated(arg, annot) => - cpy.Annotated(tree)(transform(arg), transform(annot)) - case Thicket(trees) => - val trees1 = transform(trees) - if (trees1 eq trees) tree else Thicket(trees1) - } + def transform(tree: Tree)(implicit ctx: Context): Tree = + if (skipTransform(tree)) tree + else tree match { + case Ident(name) => + tree + case Select(qualifier, name) => + cpy.Select(tree)(transform(qualifier), name) + case This(qual) => + tree + case Super(qual, mix) => + cpy.Super(tree)(transform(qual), mix) + case Apply(fun, args) => + cpy.Apply(tree)(transform(fun), transform(args)) + case TypeApply(fun, args) => + cpy.TypeApply(tree)(transform(fun), transform(args)) + case Literal(const) => + tree + case New(tpt) => + cpy.New(tree)(transform(tpt)) + case Typed(expr, tpt) => + cpy.Typed(tree)(transform(expr), transform(tpt)) + case NamedArg(name, arg) => + cpy.NamedArg(tree)(name, transform(arg)) + case Assign(lhs, rhs) => + cpy.Assign(tree)(transform(lhs), transform(rhs)) + case Block(stats, expr) => + cpy.Block(tree)(transformStats(stats), transform(expr)) + case If(cond, thenp, elsep) => + cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) + case Closure(env, meth, tpt) => + cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) + case Match(selector, cases) => + cpy.Match(tree)(transform(selector), transformSub(cases)) + case CaseDef(pat, guard, body) => + cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) + case Return(expr, from) => + cpy.Return(tree)(transform(expr), transformSub(from)) + case Try(block, cases, finalizer) => + cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) + case SeqLiteral(elems, elemtpt) => + cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) + case Inlined(call, bindings, expansion) => + cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)) + case TypeTree() => + tree + case SingletonTypeTree(ref) => + cpy.SingletonTypeTree(tree)(transform(ref)) + case AndTypeTree(left, right) => + cpy.AndTypeTree(tree)(transform(left), transform(right)) + case OrTypeTree(left, right) => + cpy.OrTypeTree(tree)(transform(left), transform(right)) + case RefinedTypeTree(tpt, refinements) => + cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) + case AppliedTypeTree(tpt, args) => + cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) + case PolyTypeTree(tparams, body) => + cpy.PolyTypeTree(tree)(transformSub(tparams), transform(body)) + case ByNameTypeTree(result) => + cpy.ByNameTypeTree(tree)(transform(result)) + case TypeBoundsTree(lo, hi) => + cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) + case Bind(name, body) => + cpy.Bind(tree)(name, transform(body)) + case Alternative(trees) => + cpy.Alternative(tree)(transform(trees)) + case UnApply(fun, implicits, patterns) => + cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) + case EmptyValDef => + tree + case tree @ ValDef(name, tpt, _) => + val tpt1 = transform(tpt) + val rhs1 = transform(tree.rhs) + cpy.ValDef(tree)(name, tpt1, rhs1) + case tree @ DefDef(name, tparams, vparamss, tpt, _) => + cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) + case tree @ TypeDef(name, rhs) => + cpy.TypeDef(tree)(name, transform(rhs)) + case tree @ Template(constr, parents, self, _) => + cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(tree.body)) + case Import(expr, selectors) => + cpy.Import(tree)(transform(expr), selectors) + case PackageDef(pid, stats) => + cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)) + case Annotated(arg, annot) => + cpy.Annotated(tree)(transform(arg), transform(annot)) + case Thicket(trees) => + val trees1 = transform(trees) + if (trees1 eq trees) tree else Thicket(trees1) + } def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] = transform(trees) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 433808e8e..565ec5ce2 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -573,6 +573,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Try(tree: Tree)(expr, cases, finalizer) } + override def skipTransform(tree: Tree)(implicit ctx: Context) = tree.tpe.isError + implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal { def isValue(implicit ctx: Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index f3ffce8f8..99bc3240f 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -15,9 +15,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { // ----- Tree cases that exist in untyped form only ------------------ trait OpTree extends Tree { - def op: Name - override def isTerm = op.isTermName - override def isType = op.isTypeName + def op: Ident + override def isTerm = op.name.isTermName + override def isType = op.name.isTypeName } /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice @@ -66,9 +66,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { */ class WildcardFunction(placeholderParams: List[ValDef], body: Tree) extends Function(placeholderParams, body) - case class InfixOp(left: Tree, op: Name, right: Tree) extends OpTree - case class PostfixOp(od: Tree, op: Name) extends OpTree - case class PrefixOp(op: Name, od: Tree) extends OpTree + case class InfixOp(left: Tree, op: Ident, right: Tree) extends OpTree + case class PostfixOp(od: Tree, op: Ident) extends OpTree + case class PrefixOp(op: Ident, od: Tree) extends OpTree case class Parens(t: Tree) extends ProxyTree { def forwardTo = t } @@ -357,7 +357,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { * parameter, the reference will be a repeated argument. */ def refOfDef(tree: MemberDef)(implicit ctx: Context) = tree match { - case ValDef(_, PostfixOp(_, nme.raw.STAR), _) => repeated(Ident(tree.name)) + case ValDef(_, PostfixOp(_, Ident(nme.raw.STAR)), _) => repeated(Ident(tree.name)) case _ => Ident(tree.name) } @@ -409,15 +409,15 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Function if (args eq tree.args) && (body eq tree.body) => tree case _ => untpd.Function(args, body).withPos(tree.pos) } - def InfixOp(tree: Tree)(left: Tree, op: Name, right: Tree) = tree match { + def InfixOp(tree: Tree)(left: Tree, op: Ident, right: Tree) = tree match { case tree: InfixOp if (left eq tree.left) && (op eq tree.op) && (right eq tree.right) => tree case _ => untpd.InfixOp(left, op, right).withPos(tree.pos) } - def PostfixOp(tree: Tree)(od: Tree, op: Name) = tree match { + def PostfixOp(tree: Tree)(od: Tree, op: Ident) = tree match { case tree: PostfixOp if (od eq tree.od) && (op eq tree.op) => tree case _ => untpd.PostfixOp(od, op).withPos(tree.pos) } - def PrefixOp(tree: Tree)(op: Name, od: Tree) = tree match { + def PrefixOp(tree: Tree)(op: Ident, od: Tree) = tree match { case tree: PrefixOp if (op eq tree.op) && (od eq tree.od) => tree case _ => untpd.PrefixOp(op, od).withPos(tree.pos) } @@ -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/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 21a6c1165..e4c90789c 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -101,6 +101,7 @@ class ScalaSettings extends Settings.SettingGroup { val XoldPatmat = BooleanSetting("-Xoldpatmat", "Use the pre-2.10 pattern matcher. Otherwise, the 'virtualizing' pattern matcher is used in 2.10.") val XnoPatmatAnalysis = BooleanSetting("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") val XfullLubs = BooleanSetting("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.") + val wikiSyntax = BooleanSetting("-Xwiki-syntax", "Retains the Scala2 behavior of using Wiki Syntax in Scaladoc") /** -Y "Private" settings */ @@ -212,11 +213,18 @@ class ScalaSettings extends Settings.SettingGroup { "A directory containing static resources needed for the API documentation" ) - val DocTitle = StringSetting ( - "-Ydoc-title", - "title", - "The overall name of the Scaladoc site", - "" + val siteRoot = StringSetting( + "-siteroot", + "site root", + "A directory containing static files from which to generate documentation", + sys.props("user.dir") + ) + + val projectName = StringSetting ( + "-project", + "project title", + "The name of the project", + sys.props("user.dir").split(sys.props("file.separator")).last ) val DocVersion = StringSetting ( 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..44608296a 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) } @@ -266,6 +263,13 @@ object Phases { trait Phase extends DotClass { + /** A name given to the `Phase` that can be used to debug the compiler. For + * instance, it is possible to print trees after a given phase using: + * + * ```bash + * $ ./bin/dotc -Xprint:<phaseNameHere> sourceFile.scala + * ``` + */ def phaseName: String /** List of names of phases that should precede this 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/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 636204f64..ae9122853 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -41,9 +41,11 @@ object Types { implicit def eqType: Eq[Type, Type] = Eq - /** The class of types. + /** Main class representing types. + * * The principal subclasses and sub-objects are as follows: * + * ```none * Type -+- ProxyType --+- NamedType ----+--- TypeRef * | | \ * | +- SingletonType-+-+- TermRef @@ -74,6 +76,7 @@ object Types { * +- NoPrefix * +- ErrorType * +- WildcardType + * ``` * * Note: please keep in sync with copy in `docs/docs/internals/type-system.md`. */ 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/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index 0dc8d8fea..fb37c9e7d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -31,8 +31,10 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { def nameRefToString(ref: NameRef): String = nameToString(tastyName(ref)) def printNames() = - for ((name, idx) <- tastyName.contents.zipWithIndex) - println(f"$idx%4d: " + nameToString(name)) + for ((name, idx) <- tastyName.contents.zipWithIndex) { + val index = "%4d: ".format(idx) + println(index + nameToString(name)) + } def printContents(): Unit = { println("Names:") @@ -47,7 +49,10 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { import reader._ var indent = 0 - def newLine() = print(f"\n ${index(currentAddr) - index(startAddr)}%5d:" + " " * indent) + def newLine() = { + val length = "%5d:".format(index(currentAddr) - index(startAddr)) + print(s"\n $length" + " " * indent) + } def printNat() = print(" " + readNat()) def printName() = { val idx = readNat() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 51f08a295..ffd594454 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -733,11 +733,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle // no longer necessary. goto(end) setPos(start, tree) - - // This is also done in PostTyper but needs to be redone here - if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) { - sym.info = Checking.checkNoPrivateLeaks(sym, tree.pos) - } + sym.info = ta.avoidPrivateLeaks(sym, tree.pos) tree } diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 92ab10db9..d35594823 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -448,7 +448,7 @@ object JavaParsers { if (in.token == DOTDOTDOT) { in.nextToken() t = atPos(t.pos.start) { - PostfixOp(t, nme.raw.STAR) + PostfixOp(t, Ident(nme.raw.STAR)) } } atPos(start, in.offset) { diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index a733eb65d..3162f61b4 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -30,7 +30,7 @@ object Parsers { import reporting.diagnostic.Message import reporting.diagnostic.messages._ - case class OpInfo(operand: Tree, operator: Name, offset: Offset) + case class OpInfo(operand: Tree, operator: Ident, offset: Offset) class ParensCounters { private var parCounts = new Array[Int](lastParen - firstParen) @@ -414,18 +414,17 @@ object Parsers { "left- and right-associative operators with same precedence may not be mixed", offset) def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean): Tree = { - if (opStack != base && precedence(opStack.head.operator) == prec) - checkAssoc(opStack.head.offset, opStack.head.operator, leftAssoc) + if (opStack != base && precedence(opStack.head.operator.name) == prec) + checkAssoc(opStack.head.offset, opStack.head.operator.name, leftAssoc) def recur(top: Tree): Tree = { if (opStack == base) top else { val opInfo = opStack.head - val opPrec = precedence(opInfo.operator) + val opPrec = precedence(opInfo.operator.name) if (prec < opPrec || leftAssoc && prec == opPrec) { opStack = opStack.tail recur { - val opPos = Position(opInfo.offset, opInfo.offset + opInfo.operator.length, opInfo.offset) - atPos(opPos union opInfo.operand.pos union top.pos) { + atPos(opInfo.operator.pos union opInfo.operand.pos union top.pos) { InfixOp(opInfo.operand, opInfo.operator, top) } } @@ -449,10 +448,9 @@ object Parsers { val base = opStack var top = first while (isIdent && in.name != notAnOperator) { - val op = if (isType) in.name.toTypeName else in.name - top = reduceStack(base, top, precedence(op), isLeftAssoc(op)) + val op = if (isType) typeIdent() else termIdent() + top = reduceStack(base, top, precedence(op.name), isLeftAssoc(op.name)) opStack = OpInfo(top, op, in.offset) :: opStack - ident() newLineOptWhenFollowing(canStartOperand) if (maybePostfix && !canStartOperand(in.token)) { val topInfo = opStack.head @@ -870,7 +868,7 @@ object Parsers { val t = toplevelTyp() if (isIdent(nme.raw.STAR)) { in.nextToken() - atPos(startOffset(t)) { PostfixOp(t, nme.raw.STAR) } + atPos(startOffset(t)) { PostfixOp(t, Ident(nme.raw.STAR)) } } else t } @@ -1189,11 +1187,11 @@ object Parsers { val prefixExpr = () => if (isIdent && nme.raw.isUnary(in.name)) { val start = in.offset - val name = ident() - if (name == nme.raw.MINUS && isNumericLit) + val op = termIdent() + if (op.name == nme.raw.MINUS && isNumericLit) simpleExprRest(literal(start), canApply = true) else - atPos(start) { PrefixOp(name, simpleExpr()) } + atPos(start) { PrefixOp(op, simpleExpr()) } } else simpleExpr() @@ -1260,7 +1258,7 @@ object Parsers { val app = atPos(startOffset(t), in.offset) { Apply(t, argumentExprs()) } simpleExprRest(app, canApply = true) case USCORE => - atPos(startOffset(t), in.skipToken()) { PostfixOp(t, nme.WILDCARD) } + atPos(startOffset(t), in.skipToken()) { PostfixOp(t, Ident(nme.WILDCARD)) } case _ => t } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 3085ad8fd..5b75df6b2 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -61,7 +61,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def nameString(name: Name): String = name.decode.toString override protected def simpleNameString(sym: Symbol): String = { - val name = sym.originalName + val name = if (ctx.property(XprintMode).isEmpty) sym.originalName else sym.name nameString(if (sym is ExpandedTypeParam) name.asTypeName.unexpandedName else name) } @@ -516,7 +516,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { argsText ~ " => " ~ toText(body) } case InfixOp(l, op, r) => - val opPrec = parsing.precedence(op) + val opPrec = parsing.precedence(op.name) changePrec(opPrec) { toText(l) ~ " " ~ toText(op) ~ " " ~ toText(r) } case PostfixOp(l, op) => changePrec(InfixPrec) { toText(l) ~ " " ~ toText(op) } diff --git a/compiler/src/dotty/tools/dotc/printing/package.scala b/compiler/src/dotty/tools/dotc/printing/package.scala index 814eb2ad0..e2c0dda1b 100644 --- a/compiler/src/dotty/tools/dotc/printing/package.scala +++ b/compiler/src/dotty/tools/dotc/printing/package.scala @@ -2,6 +2,7 @@ package dotty.tools.dotc import core.StdNames.nme import parsing.{precedence, minPrec, maxPrec, minInfixPrec} +import util.Property.Key package object printing { @@ -14,4 +15,9 @@ package object printing { val GlobalPrec = parsing.minPrec val TopLevelPrec = parsing.minPrec - 1 + /** A property to indicate whether the compiler is currently doing -Xprint + * + * -Xprint will print `sym.name` instead of `sym.originalName` + */ + val XprintMode = new Key[Unit] } 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/IsInstanceOfEvaluator.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala index 8bc4a2aa9..ebd2ae436 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala @@ -7,36 +7,37 @@ import core._ import Contexts.Context, Types._, Constants._, Decorators._, Symbols._ import TypeUtils._, TypeErasure._, Flags._ - /** Implements partial evaluation of `sc.isInstanceOf[Sel]` according to: * - * +-------------+----------------------------+----------------------------+------------------+ * | Sel\sc | trait | class | final class | - * +-------------+----------------------------+----------------------------+------------------+ + * | ----------: | :------------------------: | :------------------------: | :--------------: | * | trait | ? | ? | statically known | * | class | ? | false if classes unrelated | statically known | * | final class | false if classes unrelated | false if classes unrelated | statically known | - * +-------------+----------------------------+----------------------------+------------------+ * * This is a generalized solution to raising an error on unreachable match * cases and warnings on other statically known results of `isInstanceOf`. * * Steps taken: * - * 1. evalTypeApply will establish the matrix and choose the appropriate - * handling for the case: - * 2. a) Sel/sc is a value class or scrutinee is `Any` - * b) handleStaticallyKnown - * c) falseIfUnrelated with `scrutinee <:< selector` - * d) handleFalseUnrelated - * e) leave as is (aka `happens`) - * 3. Rewrite according to step taken in `2` + * 1. `evalTypeApply` will establish the matrix and choose the appropriate + * handling for the case: + * - Sel/sc is a value class or scrutinee is `Any` + * - `handleStaticallyKnown` + * - `falseIfUnrelated` with `scrutinee <:< selector` + * - `handleFalseUnrelated` + * - leave as is (`happens`) + * 2. Rewrite according to steps taken in 1 */ class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer => import dotty.tools.dotc.ast.tpd._ - def phaseName = "isInstanceOfEvaluator" + val phaseName = "isInstanceOfEvaluator" + + /** Transforms a [TypeApply](dotty.tools.dotc.ast.Trees.TypeApply) in order to + * evaluate an `isInstanceOf` check according to the rules defined above. + */ override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { val defn = ctx.definitions 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/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 181dfccd9..7c49e68dd 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -1077,8 +1077,6 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { trait MatchTranslator extends TreeMakers with ScalacPatternExpanders { - def isBackquoted(x: Ident) = x.isInstanceOf[BackquotedIdent] - def isVarPattern(pat: Tree): Boolean = pat match { case x: BackquotedIdent => false case x: Ident => x.name.isVariableName diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index e7c0df1b9..8dff58dea 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -107,12 +107,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = { val sym = tree.symbol sym.transformAnnotations(transformAnnot) - // Has to be redone in TreeUnpickler - if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) { - val info1 = Checking.checkNoPrivateLeaks(sym, tree.pos) - if (info1 ne sym.info) - sym.copySymDenotation(info = info1).installAfter(thisTransformer) - } } private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 321c275d7..ca62ed0a9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -415,6 +415,8 @@ object Checking { /** Verify classes extending AnyVal meet the requirements */ def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = { def checkValueClassMember(stat: Tree) = stat match { + case _: TypeDef if stat.symbol.isClass => + ctx.error(s"value class may not define an inner class", stat.pos) case _: ValDef if !stat.symbol.is(ParamAccessor) => ctx.error(s"value class may not define non-parameter field", stat.pos) case d: DefDef if d.symbol.isConstructor => 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/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index c390ae808..e7e7ece78 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -144,7 +144,7 @@ object EtaExpansion { ids = ids.init :+ repeated(ids.last) var body: Tree = Apply(lifted, ids) mt.resultType match { - case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, nme.WILDCARD) + case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, Ident(nme.WILDCARD)) case _ => } val fn = untpd.Function(params, body) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f7d8556a7..6949221fb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -69,9 +69,9 @@ object Implicits { case mt: MethodType => mt.isImplicit || mt.paramTypes.length != 1 || - !(argType relaxed_<:< wildApprox(mt.paramTypes.head)(ctx.fresh.setExploreTyperState)) + !(argType relaxed_<:< wildApprox(mt.paramTypes.head, null, Set.empty)(ctx.fresh.setExploreTyperState)) case rtp => - discardForView(wildApprox(rtp), argType) + discardForView(wildApprox(rtp, null, Set.empty), argType) } case tpw: TermRef => false // can't discard overloaded refs @@ -664,7 +664,7 @@ trait Implicits { self: Typer => } /** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */ - val wildProto = implicitProto(pt, wildApprox(_)) + val wildProto = implicitProto(pt, wildApprox(_, null, Set.empty)) /** Search failures; overridden in ExplainedImplicitSearch */ protected def nonMatchingImplicit(ref: TermRef, trail: List[MessageContainer]): SearchFailure = NoImplicitMatches diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 09487570d..cfc0003c6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -327,7 +327,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { (tp.paramNames, tp.paramTypes, argss.head).zipped.foreach { (name, paramtp, arg) => def isByName = paramtp.dealias.isInstanceOf[ExprType] paramBinding(name) = arg.tpe.stripAnnots.stripTypeVar match { - case argtpe: SingletonType if isByName || isIdempotentExpr(arg) => argtpe + case argtpe: SingletonType if isIdempotentExpr(arg) => argtpe case argtpe => val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags val (bindingFlags, bindingType) = diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 068ef3e4b..3860ba225 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -491,22 +491,26 @@ class Namer { typer: Typer => if (cdef.isClassDef) { classDef(name) = cdef cdef.attachmentOrElse(ExpandedTree, cdef) match { - case Thicket(cls :: mval :: (mcls @ TypeDef(_, _: Template)) :: crest) => + case Thicket(cls :: mval :: (mcls @ TypeDef(mname, _: Template)) :: crest) + if name.moduleClassName == mname => moduleDef(name) = mcls case _ => } } for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) { val typName = name.toTypeName - val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree) + // Expansion of object is a flattened thicket with the first two elements being: + // module val :: module class :: rest + val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: rest) = expanded(mdef) moduleDef(typName) = mcls classDef get name.toTypeName match { case Some(cdef) => cdef.attachmentOrElse(ExpandedTree, cdef) match { - case Thicket(cls :: mval :: TypeDef(_, compimpl: Template) :: crest) => + case Thicket(cls :: mval :: TypeDef(mname, compimpl: Template) :: crest) + if name.moduleClassName == mname => val mcls1 = cpy.TypeDef(mcls)( rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body)) - mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil)) + mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: rest)) moduleDef(typName) = mcls1 cdef.putAttachment(ExpandedTree, Thicket(cls :: crest)) case _ => @@ -668,13 +672,15 @@ class Namer { typer: Typer => * to pick up the context at the point where the completer was created. */ def completeInCreationContext(denot: SymDenotation): Unit = { + val sym = denot.symbol original match { - case original: MemberDef => addAnnotations(denot.symbol, original) + case original: MemberDef => addAnnotations(sym, original) case _ => } addInlineInfo(denot) - denot.info = typeSig(denot.symbol) - Checking.checkWellFormed(denot.symbol) + denot.info = typeSig(sym) + Checking.checkWellFormed(sym) + denot.info = avoidPrivateLeaks(sym, sym.pos) } } @@ -854,6 +860,7 @@ class Namer { typer: Typer => if (isDerivedValueClass(cls)) cls.setFlag(Final) cls.setApplicableFlags( (NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat))) + cls.info = avoidPrivateLeaks(cls, cls.pos) } } @@ -965,7 +972,7 @@ class Namer { typer: Typer => ctx.defContext(sym).denotNamed(original) def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { case params :: paramss1 => - if (idx < params.length) wildApprox(params(idx)) + if (idx < params.length) wildApprox(params(idx), null, Set.empty) else paramProto(paramss1, idx - params.length) case nil => WildcardType diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index eb46a131f..d666b563e 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -437,65 +437,82 @@ object ProtoTypes { /** Approximate occurrences of parameter types and uninstantiated typevars * by wildcard types. */ - final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match { + final def wildApprox(tp: Type, theMap: WildApproxMap, seen: Set[PolyParam])(implicit ctx: Context): Type = tp match { case tp: NamedType => // default case, inlined for speed if (tp.symbol.isStatic) tp - else tp.derivedSelect(wildApprox(tp.prefix, theMap)) + else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen)) case tp: RefinedType => // default case, inlined for speed - tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) + tp.derivedRefinedType( + wildApprox(tp.parent, theMap, seen), + tp.refinedName, + wildApprox(tp.refinedInfo, theMap, seen)) case tp: TypeAlias => // default case, inlined for speed - tp.derivedTypeAlias(wildApprox(tp.alias, theMap)) + tp.derivedTypeAlias(wildApprox(tp.alias, theMap, seen)) case tp @ PolyParam(poly, pnum) => - def unconstrainedApprox = WildcardType(wildApprox(poly.paramBounds(pnum)).bounds) - if (ctx.mode.is(Mode.TypevarsMissContext)) - unconstrainedApprox - else - ctx.typerState.constraint.entry(tp) match { - case bounds: TypeBounds => wildApprox(WildcardType(bounds)) - case NoType => unconstrainedApprox - case inst => wildApprox(inst) - } + def wildApproxBounds(bounds: TypeBounds) = + if (bounds.lo.isInstanceOf[NamedType] && bounds.hi.isInstanceOf[NamedType]) + WildcardType(wildApprox(bounds, theMap, seen).bounds) + else if (seen.contains(tp)) WildcardType + else WildcardType(wildApprox(bounds, theMap, seen + tp).bounds) + def unconstrainedApprox = wildApproxBounds(poly.paramBounds(pnum)) + def approxPoly = + if (ctx.mode.is(Mode.TypevarsMissContext)) unconstrainedApprox + else + ctx.typerState.constraint.entry(tp) match { + case bounds: TypeBounds => wildApproxBounds(bounds) + case NoType => unconstrainedApprox + case inst => wildApprox(inst, theMap, seen) + } + approxPoly case MethodParam(mt, pnum) => - WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) + WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum), theMap, seen))) case tp: TypeVar => - wildApprox(tp.underlying) + wildApprox(tp.underlying, theMap, seen) case tp @ HKApply(tycon, args) => - wildApprox(tycon) match { + wildApprox(tycon, theMap, seen) match { case _: WildcardType => WildcardType // this ensures we get a * type - case tycon1 => tp.derivedAppliedType(tycon1, args.mapConserve(wildApprox(_))) + case tycon1 => tp.derivedAppliedType(tycon1, args.mapConserve(wildApprox(_, theMap, seen))) } case tp: AndType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - def wildBounds(tp: Type) = - if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(wildBounds(tp1a) & wildBounds(tp2a)) - else - tp.derivedAndType(tp1a, tp2a) + def approxAnd = { + val tp1a = wildApprox(tp.tp1, theMap, seen) + val tp2a = wildApprox(tp.tp2, theMap, seen) + def wildBounds(tp: Type) = + if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) + if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) + WildcardType(wildBounds(tp1a) & wildBounds(tp2a)) + else + tp.derivedAndType(tp1a, tp2a) + } + approxAnd case tp: OrType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) - if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) - WildcardType(tp1a.bounds | tp2a.bounds) - else - tp.derivedOrType(tp1a, tp2a) + def approxOr = { + val tp1a = wildApprox(tp.tp1, theMap, seen) + val tp2a = wildApprox(tp.tp2, theMap, seen) + if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) + WildcardType(tp1a.bounds | tp2a.bounds) + else + tp.derivedOrType(tp1a, tp2a) + } + approxOr case tp: LazyRef => WildcardType case tp: SelectionProto => - tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed) + tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen), NoViewsAllowed) case tp: ViewProto => - tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType)) + tp.derivedViewProto( + wildApprox(tp.argType, theMap, seen), + wildApprox(tp.resultType, theMap, seen)) case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed tp case _ => - (if (theMap != null) theMap else new WildApproxMap).mapOver(tp) + (if (theMap != null) theMap else new WildApproxMap(seen)).mapOver(tp) } @sharable object AssignProto extends UncachedGroundType with MatchAlways - private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap { - def apply(tp: Type) = wildApprox(tp, this) + private[ProtoTypes] class WildApproxMap(val seen: Set[PolyParam])(implicit ctx: Context) extends TypeMap { + def apply(tp: Type) = wildApprox(tp, this, seen) } /** Dummy tree to be used as an argument of a FunProto or ViewProto type */ diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 5c07b7bcf..53ce5555b 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -132,6 +132,11 @@ trait TypeAssigner { def avoidingType(expr: Tree, bindings: List[Tree])(implicit ctx: Context): Type = avoid(expr.tpe, localSyms(bindings).filter(_.isTerm)) + def avoidPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = + if (!sym.is(SyntheticOrPrivate) && sym.owner.isClass) + Checking.checkNoPrivateLeaks(sym, pos) + else sym.info + def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass))) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 22a4221ba..652c89094 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) } @@ -1441,7 +1440,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(implicit ctx: Context): Tree = { - val untpd.PostfixOp(qual, nme.WILDCARD) = tree + val untpd.PostfixOp(qual, Ident(nme.WILDCARD)) = tree val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto var res = typed(qual, pt1) if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) { @@ -1542,7 +1541,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tree: untpd.Annotated => typedAnnotated(tree, pt) case tree: untpd.TypedSplice => typedTypedSplice(tree) case tree: untpd.UnApply => typedUnApply(tree, pt) - case tree @ untpd.PostfixOp(qual, nme.WILDCARD) => typedAsFunction(tree, pt) + case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree case _ => typedUnadapted(desugar(tree), pt) } @@ -2068,7 +2067,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 => |