aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TypedTrees.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-02-21 14:20:08 +0100
committerMartin Odersky <odersky@gmail.com>2013-02-21 14:20:08 +0100
commit9fab0e3edef2a100970c007f14148d68e9ae0889 (patch)
tree5d3780c4867acd1f2dd5e477b14799e6d498b331 /src/dotty/tools/dotc/core/TypedTrees.scala
parent58b7b61fb8ffda09dbec4427476a47b4e3a505bc (diff)
downloaddotty-9fab0e3edef2a100970c007f14148d68e9ae0889.tar.gz
dotty-9fab0e3edef2a100970c007f14148d68e9ae0889.tar.bz2
dotty-9fab0e3edef2a100970c007f14148d68e9ae0889.zip
Changed function expansion and fleshed out checkType
Function expansion got changed so that it requires an explicit target type and that target type can be an arbitrary SAM type.
Diffstat (limited to 'src/dotty/tools/dotc/core/TypedTrees.scala')
-rw-r--r--src/dotty/tools/dotc/core/TypedTrees.scala291
1 files changed, 244 insertions, 47 deletions
diff --git a/src/dotty/tools/dotc/core/TypedTrees.scala b/src/dotty/tools/dotc/core/TypedTrees.scala
index b2df92f50..65ab95693 100644
--- a/src/dotty/tools/dotc/core/TypedTrees.scala
+++ b/src/dotty/tools/dotc/core/TypedTrees.scala
@@ -12,6 +12,7 @@ object TypedTrees {
type Tree = Trees.Tree[Type]
type TypTree = Trees.TypTree[Type]
type TermTree = Trees.TermTree[Type]
+ type PatternTree = Trees.PatternTree[Type]
type SymTree = Trees.SymTree[Type]
type ProxyTree = Trees.ProxyTree[Type]
type NameTree = Trees.NameTree[Type]
@@ -41,7 +42,7 @@ object TypedTrees {
type Return = Trees.Return[Type]
type Try = Trees.Try[Type]
type Throw = Trees.Throw[Type]
- type ArrayValue = Trees.ArrayValue[Type]
+ type SeqLiteral = Trees.SeqLiteral[Type]
type TypeTree = Trees.TypeTree[Type]
type SingletonTypeTree = Trees.SingletonTypeTree[Type]
type SelectFromTypeTree = Trees.SelectFromTypeTree[Type]
@@ -126,11 +127,6 @@ object TypedTrees {
def widen(tp: Type): Type = tp match {
case tp: TermRef if locals contains tp.symbol =>
widen(tp.info)
- case tp: MethodType =>
- assert(!tp.isDependent, s"Dependent method type in result of block $blk")
- defn.FunctionType(tp.paramTypes, widen(tp.resultType))
- case tp: PolyType =>
- throw new AssertionError(s"Uninstantiated polytype in result of block $blk")
case _ => tp
}
blk.withType(widen(expr.tpe))
@@ -151,8 +147,8 @@ object TypedTrees {
def Throw(expr: Tree)(implicit ctx: Context): Throw =
Trees.Throw(expr).withType(defn.NothingType).checked
- def ArrayValue(elemtpt: Tree, elems: List[Tree])(implicit ctx: Context): ArrayValue =
- Trees.ArrayValue(elemtpt, elems).withType(defn.ArrayType.appliedTo(elemtpt.tpe)).checked
+ def SeqLiteral(elemtpt: Tree, elems: List[Tree])(implicit ctx: Context): SeqLiteral =
+ Trees.SeqLiteral(elemtpt, elems).withType(defn.RepeatedParamType.appliedTo(elemtpt.tpe)).checked
def TypeTree(tp: Type, original: Tree = EmptyTree)(implicit ctx: Context): TypeTree =
Trees.TypeTree(original).withType(tp).checked
@@ -160,8 +156,8 @@ object TypedTrees {
def SingletonTypeTree(ref: Tree)(implicit ctx: Context): SingletonTypeTree =
Trees.SingletonTypeTree(ref).withType(ref.tpe).checked
- def SelectFromTypeTree(qualifier: Tree, name: TypeName)(implicit ctx: Context): SelectFromTypeTree =
- Trees.SelectFromTypeTree(qualifier, name).withType(TypeRef(qualifier.tpe, name)).checked
+ def SelectFromTypeTree(qualifier: Tree, tp: TypeRef)(implicit ctx: Context): SelectFromTypeTree =
+ Trees.SelectFromTypeTree(qualifier, tp.name).withType(tp).checked
def AndTypeTree(left: Tree, right: Tree)(implicit ctx: Context): AndTypeTree =
Trees.AndTypeTree(left, right).withType(left.tpe & right.tpe).checked
@@ -185,15 +181,15 @@ object TypedTrees {
def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree =
Trees.TypeBoundsTree(lo, hi).withType(TypeBounds(lo.tpe, hi.tpe)).checked
- def Bind(sym: Symbol, body: Tree)(implicit ctx: Context): Bind =
- Trees.Bind(sym.name, body)(defPos(sym)).withType(sym.info).checked
+ def Bind(sym: TermSymbol, body: Tree)(implicit ctx: Context): Bind =
+ Trees.Bind(sym.name, body)(defPos(sym)).withType(refType(sym)).checked
def Alternative(trees: List[Tree])(implicit ctx: Context): Alternative =
Trees.Alternative(trees).withType(ctx.lub(trees map (_.tpe))).checked
def UnApply(fun: Tree, args: List[Tree])(implicit ctx: Context): UnApply =
- Trees.UnApply(fun, args).withType(fun.tpe match {
- case MethodType(_, paramTypes) => paramTypes.head
+ Trees.UnApply(fun, args).withType(fun.tpe.widen match {
+ case MethodType(_, paramType :: Nil) => paramType
}).checked
def refType(sym: Symbol)(implicit ctx: Context) = NamedType(sym.owner.thisType, sym)
@@ -255,7 +251,7 @@ object TypedTrees {
}
def Import(expr: Tree, selectors: List[Trees.UntypedTree])(implicit ctx: Context): Import =
- Trees.Import(expr, selectors).withType(refType(ctx.newImportSymbol(expr))).checked
+ Trees.Import(expr, selectors).withType(refType(ctx.newImportSymbol(Shared(expr)))).checked
def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef =
Trees.PackageDef(pid, stats).withType(refType(pid.symbol)).checked
@@ -279,6 +275,33 @@ object TypedTrees {
TermRef(tp.normalizedPrefix, tp.typeSymbol.primaryConstructor.asTerm)),
args)
+ /** An object def
+ *
+ * object obs extends parents { decls }
+ *
+ * gets expanded to
+ *
+ * <module> lazy val obj = {
+ * class obj$ extends parents { this: obj.type => decls }
+ * new obj$
+ * }
+ *
+ * What's interesting here is that the block is well typed
+ * (because class obj$ is hoistable), but the type of the `obj` val is
+ * not expressible. What needs to happen in general when
+ * inferring the type of a val from its RHS, is: if the type contains
+ * a class that has the val itself as owner, then that class
+ * is remapped to have the val's owner as owner. Remapping could be
+ * done by cloning the class with the new owner and substituting
+ * everywhere in the tree. We know that remapping is safe
+ * because the only way a local class can appear in the RHS of a val is
+ * by being hoisted outside of a block, and the necessary checks are
+ * done at this point already.
+ *
+ * On the other hand, for method result type inference, if the type of
+ * the RHS of a method contains a class owned by the method, this would be
+ * an error.
+ */
def ModuleDef(sym: TermSymbol, body: List[Tree])(implicit ctx: Context): ValDef = {
val modcls = sym.moduleClass.asClass
val clsdef = ClassDef(modcls, Nil, body)
@@ -286,8 +309,29 @@ object TypedTrees {
ValDef(sym, rhs)
}
- def Function(meth: TermSymbol, body: Tree)(implicit ctx: Context): Block =
- Block(DefDef(meth, body) :: Nil, Ident(TermRef(NoPrefix, meth)))
+ /** A function def
+ *
+ * vparams => expr
+ *
+ * gets expanded to
+ *
+ * { def $anonfun(vparams) = expr; $anonfun: pt }
+ *
+ * where pt is the target type of the expression (FunctionN) unless
+ * otherwise specified.
+ */
+ def Function(meth: TermSymbol, body: Tree, target: Type = NoType)(implicit ctx: Context): Block = {
+ val funtpe =
+ if (target.exists) target
+ else meth.info match {
+ case mt @ MethodType(_, formals) =>
+ assert(!mt.isDependent)
+ defn.FunctionType(formals, mt.resultType)
+ }
+ Block(
+ DefDef(meth, body) :: Nil,
+ Typed(Ident(TermRef(NoPrefix, meth)), TypeTree(funtpe)))
+ }
private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] {
def apply(sym: Symbol, tree: Tree) =
@@ -310,87 +354,240 @@ object TypedTrees {
import Trees._
- def check(p: Boolean): Unit = assert(p)
+ def check(p: Boolean)(implicit ctx: Context): Unit = assert(p)
def checkType(tree: tpd.Tree)(implicit ctx: Context): Unit = tree match {
case Ident(name) =>
- case Select(pre, name) =>
- val mt = pre.tpe.member(name)
- check(pre.isTerm)
- check(mt.exists)
- check((mt filter (_.info <:< tree.tpe)).exists)
+ case Select(qualifier, name) =>
+ check(qualifier.isValue)
+ val denot = qualifier.tpe.member(name)
+ check(denot.hasAltWith(_.symbol == tree.symbol))
case This(cls) =>
case Super(qual, mixin) =>
- check(qual.isTerm)
+ check(qual.isValue)
val cls = qual.tpe.typeSymbol
check(cls.isClass)
check(mixin == NoSymbol || (cls.asClass.parents map (_.typeSymbol) contains mixin))
case Apply(fn, args) =>
- def checkArg(arg: tpd.Tree, name: Name, tpe: Type): Unit = {
- check(arg.isTerm)
- check(arg.tpe <:< tpe)
+ def checkArg(arg: tpd.Tree, name: Name, formal: Type): Unit = {
arg match {
- case NamedArg(argName, _) => check(argName == name)
+ case NamedArg(argName, _) =>
+ check(argName == name)
+ case SeqLiteral(_, _) =>
+ check(defn.RepeatedParamClasses contains formal.typeSymbol)
case _ =>
+ check(arg.isValue)
}
+ check(arg.tpe <:< formal)
}
- fn.tpe match {
+ fn.tpe.widen match {
case MethodType(paramNames, paramTypes) =>
(args, paramNames, paramTypes).zipped foreach checkArg
case _ =>
check(false)
}
case TypeApply(fn, args) =>
- fn.tpe match {
+ def checkArg(arg: tpd.Tree, bounds: TypeBounds): Unit = {
+ check(arg.isType)
+ check(bounds contains arg.tpe)
+ }
+ fn.tpe.widen match {
case pt: PolyType =>
- val argTypes = args map (_.tpe)
- check((pt.instantiateBounds(argTypes) corresponds argTypes) (_ contains _))
+ (args, pt.instantiateBounds(args map (_.tpe))).zipped foreach checkArg
case _ =>
check(false)
}
case Literal(const: Constant) =>
- try const.tag catch { case ex: Throwable => check(false) }
case New(tpt) =>
check(tpt.isType)
val cls = tpt.tpe.typeSymbol
check(cls.isClass)
- check(!(cls is (AbstractOrTrait)))
+ check(!(cls is AbstractOrTrait))
case Pair(left, right) =>
- check(left.isTerm)
- check(right.isTerm)
+ check(left.isValue)
+ check(right.isValue)
case Typed(expr, tpt) =>
- check(expr.isTerm)
check(tpt.isType)
- check(expr.tpe <:< tpt.tpe)
+ expr.tpe.widen match {
+ case tp: MethodType =>
+ val cls = tpt.tpe.typeSymbol
+ check(cls.isClass)
+ check((cls is Trait) ||
+ cls.primaryConstructor.info.paramTypess.flatten.isEmpty)
+ val absMembers = tpt.tpe.abstractTermMembers
+ check(absMembers.size == 1)
+ check(tp <:< absMembers.head.info)
+ case _ =>
+ check(expr.isValueOrPattern)
+ }
case NamedArg(name, arg) =>
- // missing because it cannot be done easily bottom-up:
- // check that NamedArgs only occur in parameter lists
case Assign(lhs, rhs) =>
- check(rhs.isTerm)
+ check(lhs.isValue); check(rhs.isValue)
lhs.tpe match {
case ltpe: TermRef =>
check(ltpe.symbol is Mutable)
case _ =>
check(false)
}
- check(rhs.tpe <:< lhs.tpe)
+ check(rhs.tpe <:< lhs.tpe.widen)
case Block(stats, expr) =>
+ var hoisted: Set[Symbol] = Set()
lazy val locals = localSyms(stats)
+ check(expr.isValue)
def isNonLocal(sym: Symbol): Boolean =
!(locals contains sym) || isHoistableClass(sym)
def isHoistableClass(sym: Symbol) =
- sym.isClass && noLeaksIn(sym.info)
+ sym.isClass && {
+ (hoisted contains sym) || {
+ hoisted += sym
+ noLeaksInClass(sym.asClass)
+ }
+ }
def noLeaksIn(tp: Type): Boolean = tp forall {
case tp: NamedType => isNonLocal(tp.symbol)
- case tp: ClassInfo =>
- noLeaksIn(tp.prefix) &&
- (tp.parents forall noLeaksIn) &&
- (tp.decls.toList forall (t => noLeaksIn(t.info)))
case _ => true
}
+ def noLeaksInClass(sym: ClassSymbol): Boolean =
+ (sym.parents forall noLeaksIn) &&
+ (sym.decls.toList forall (t => noLeaksIn(t.info)))
check(noLeaksIn(tree.tpe))
+ case If(cond, thenp, elsep) =>
+ check(cond.isValue); check(thenp.isValue); check(elsep.isValue)
+ check(cond.tpe <:< defn.BooleanType)
+ case Match(selector, cases) =>
+ check(selector.isValue)
+ // are any checks that relate selector and patterns desirable?
+ case CaseDef(pat, guard, body) =>
+ check(pat.isValueOrPattern); check(guard.isValue); check(body.isValue)
+ check(guard.tpe <:< defn.BooleanType)
+ case Return(expr, from) =>
+ check(expr.isValue); check(from.isTerm)
+ check(from.tpe.termSymbol.isSourceMethod)
+ case Throw(expr) =>
+ check(expr.isValue)
+ check(expr.tpe <:< defn.ThrowableType)
+ case SeqLiteral(elemtpt, elems) =>
+ check(elemtpt.isType);
+ for (elem <- elems) {
+ check(elem.isValue)
+ check(elem.tpe <:< elemtpt.tpe)
+ }
+ case TypeTree(original) =>
+ if (!original.isEmpty) check(original.tpe == tree.tpe)
+ case SingletonTypeTree(ref) =>
+ check(ref.isValue)
+ check(ref.symbol.isStable)
+ case SelectFromTypeTree(qualifier, name) =>
+ check(qualifier.isType)
+ val denot = qualifier.tpe.member(name)
+ check(denot.exists)
+ check(denot.symbol == tree.symbol)
+ case AndTypeTree(left, right) =>
+ check(left.isType); check(right.isType)
+ case OrTypeTree(left, right) =>
+ check(left.isType); check(right.isType)
+ case RefineTypeTree(tpt, refinements) =>
+ check(tpt.isType)
+ def checkRefinements(forbidden: Set[Symbol], rs: List[tpd.DefTree]): Unit = rs match {
+ case r :: rs1 =>
+ val rsym = r.symbol
+ check(rsym.isTerm || rsym.isAbstractType)
+ if (rsym.isAbstractType) check(tpt.tpe.member(rsym.name).exists)
+ check(rsym.info forall {
+ case nt: NamedType => !(forbidden contains nt.symbol)
+ case _ => true
+ })
+ checkRefinements(forbidden - rsym, rs1)
+ case nil =>
+ }
+ checkRefinements(localSyms(refinements).toSet, refinements)
+ case AppliedTypeTree(tpt, args) =>
+ check(tpt.isType)
+ for (arg <- args) check(arg.isType)
+ check(sameLength(tpt.tpe.typeParams, args))
+ case TypeBoundsTree(lo, hi) =>
+ check(lo.isType); check(hi.isType)
+ check(lo.tpe <:< hi.tpe)
+ case Bind(sym, body) =>
+ // missing because it cannot be done easily bottom-up:
+ // check that Bind (and Alternative, and UnApply) only occur in patterns
+ check(body.isValueOrPattern)
+ check(!(tree.symbol is Method))
+ body match {
+ case Ident(nme.WILDCARD) =>
+ case _ => check(body.tpe <:< tree.symbol.info)
+ }
+ case Alternative(alts) =>
+ for (alt <- alts) check(alt.isValueOrPattern)
+ case UnApply(fun, args) =>
+ check(fun.isTerm)
+ val funtpe = fun.tpe.widen match {
+ case mt: MethodType => mt
+ case _ => check(false); ErrorType
+ }
+ for (arg <- args) check(arg.isValueOrPattern)
+ fun.symbol.name match { // check arg arity
+ case nme.unapplySeq =>
+ // args need to be wrapped in (...: _*)
+ check(args.length == 1)
+ check(args.head.tpe.typeSymbol == defn.RepeatedParamClass)
+ case nme.unapply =>
+ val rtp = funtpe.resultType
+ if (rtp == defn.BooleanType)
+ check(args.isEmpty)
+ else {
+ val (tycon, resArgs) = rtp.splitArgs
+ check(tycon == defn.OptionType)
+ check(resArgs.length == 1)
+ val normArgs = {
+ val (tp1, args1) = resArgs.head.splitArgs
+ if (defn.TupleClasses contains tp1.typeSymbol) args1
+ else args.head :: Nil
+ }
+ check(sameLength(normArgs, args))
+ }
+ }
+ case ValDef(mods, name, tpt, rhs) =>
+ check(!(tree.symbol is Method))
+ if (!rhs.isEmpty) {
+ check(rhs.isValue)
+ check(rhs.tpe <:< tpt.tpe)
+ }
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ check(tree.symbol is Method)
+ if (!rhs.isEmpty) {
+ check(rhs.isValue)
+ check(rhs.tpe <:< tpt.tpe)
+ }
+ case TypeDef(mods, name, tpt) =>
+ check(tpt.tpe.isInstanceOf[TypeBounds])
+ case Template(parents, selfType, body) =>
+ case ClassDef(mods, name, tparams, impl) =>
+ case Import(expr, selectors) =>
+ check(expr.isValue)
+ check(expr.tpe.termSymbol.isStable)
+ case PackageDef(pid, stats) =>
+ check(pid.isValue)
+ check(pid.symbol.isPackage)
+ case Annotated(annot, arg) =>
+ check(annot.symbol.isClassConstructor)
+ check(annot.symbol.owner.isSubClass(defn.AnnotationClass))
+ check(arg.isType || arg.isValue)
+ case tpd.EmptyTree =>
+ case Shared(shared) =>
+ check(shared.isType || shared.isTerm)
}
+ implicit class TreeInfo(val tree: tpd.Tree) extends AnyVal {
+ def isValue(implicit ctx: Context): Boolean =
+ tree.isTerm && tree.tpe.widen.isValueType
+ def isValueOrPattern(implicit ctx: Context) =
+ tree.isValue || tree.isPattern
+ }
+
+ // ensure that constructors are fully applied?
+ // ensure that normal methods are fully applied?
+
def localSyms(stats: List[tpd.Tree])(implicit ctx: Context) =
for (stat <- stats if (stat.isDef)) yield stat.symbol
}