diff options
Diffstat (limited to 'src/dotty/tools/dotc/ast/TreeInfo.scala')
-rw-r--r-- | src/dotty/tools/dotc/ast/TreeInfo.scala | 356 |
1 files changed, 178 insertions, 178 deletions
diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index cde8c10b0..de00cee7e 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -7,14 +7,9 @@ import Flags._, Trees._, Types._, Contexts._ import Names._, StdNames._, NameOps._, Decorators._, Symbols._ import util.HashSet -/** This class ... - * - * @author Martin Odersky - * @version 1.0 - */ -abstract class TreeInfo { - - def isDeclarationOrTypeDef(tree: Tree[_ >: Untyped]): Boolean = tree match { +trait TreeInfo[T >: Untyped] { self: Trees.Instance[T] => + + def isDeclarationOrTypeDef(tree: Tree): Boolean = tree match { case DefDef(_, _, _, _, _, EmptyTree) | ValDef(_, _, _, EmptyTree) | TypeDef(_, _, _) => true @@ -23,7 +18,7 @@ abstract class TreeInfo { /** Is tree legal as a member definition of an interface? */ - def isInterfaceMember(tree: Tree[_ >: Untyped]): Boolean = tree match { + def isInterfaceMember(tree: Tree): Boolean = tree match { case EmptyTree => true case Import(_, _) => true case TypeDef(_, _, _) => true @@ -32,71 +27,14 @@ abstract class TreeInfo { case _ => false } - /** Is tree a definition that has no side effects when - * evaluated as part of a block after the first time? - */ - def isIdempotentDef(tree: Tree[Type])(implicit ctx: Context): Boolean = tree match { - case EmptyTree - | TypeDef(_, _, _) - | Import(_, _) - | DefDef(_, _, _, _, _, _) => - true - case ValDef(mods, _, _, rhs) => - !(mods is Mutable) && isIdempotentExpr(rhs) - case _ => - false - } - - def isOpAssign(tree: untpd.Tree) = tree match { + def isOpAssign(tree: Tree) = tree match { case Apply(Select(_, name), _ :: Nil) if name.isOpAssignmentName => true case _ => false } - /** Is tree an expression which can be inlined without affecting program semantics? - * - * Note that this is not called "isExprPure" since purity (lack of side-effects) - * is not the litmus test. References to modules and lazy vals are side-effecting, - * both because side-effecting code may be executed and because the first reference - * takes a different code path than all to follow; but they are safe to inline - * because the expression result from evaluating them is always the same. - */ - def isIdempotentExpr(tree: Tree[Type])(implicit ctx: Context): Boolean = tree match { - case EmptyTree - | This(_) - | Super(_, _) - | Literal(_) => - true - case Ident(_) => - tree.symbol is Stable - case Select(qual, _) => - tree.symbol.isStable && isIdempotentExpr(qual) - case TypeApply(fn, _) => - isIdempotentExpr(fn) -/* - * Not sure we'll need that. Comment out until we find out - case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => - // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` - free.symbol.hasStableFlag && isIdempotentExpr(free) -*/ - case Apply(fn, Nil) => - // Note: After uncurry, field accesses are represented as Apply(getter, Nil), - // so an Apply can also be pure. - // However, before typing, applications of nullary functional values are also - // Apply(function, Nil) trees. To prevent them from being treated as pure, - // we check that the callee is a method. - // The callee might also be a Block, which has a null symbol, so we guard against that (SI-7185) - fn.symbol != null && (fn.symbol is (Method, butNot = Lazy)) && isIdempotentExpr(fn) - case Typed(expr, _) => - isIdempotentExpr(expr) - case Block(stats, expr) => - (stats forall isIdempotentDef) && isIdempotentExpr(expr) - case _ => - false - } - - class MatchingArgs[T >: Untyped](params: List[Symbol], args: List[Tree[T]])(implicit ctx: Context) { - def foreach(f: (Symbol, Tree[T]) => Unit): Boolean = { - def recur(params: List[Symbol], args: List[Tree[T]]): Boolean = params match { + class MatchingArgs(params: List[Symbol], args: List[Tree])(implicit ctx: Context) { + def foreach(f: (Symbol, Tree) => Unit): Boolean = { + def recur(params: List[Symbol], args: List[Tree]): Boolean = params match { case Nil => args.isEmpty case param :: params1 => if (param.info.isRepeatedParam) { @@ -111,8 +49,8 @@ abstract class TreeInfo { } recur(params, args) } - def zipped: List[(Symbol, Tree[T])] = map((_, _)) - def map[R](f: (Symbol, Tree[T]) => R): List[R] = { + def zipped: List[(Symbol, Tree)] = map((_, _)) + def map[R](f: (Symbol, Tree) => R): List[R] = { val b = List.newBuilder[R] foreach(b += f(_, _)) b.result @@ -129,7 +67,7 @@ abstract class TreeInfo { * <init>(x$2, x$1) * } */ - def methPart[T >: Untyped](tree: Tree[T]): Tree[T] = tree match { + def methPart(tree: Tree): Tree = tree match { case Apply(fn, _) => methPart(fn) case TypeApply(fn, _) => methPart(fn) case AppliedTypeTree(fn, _) => methPart(fn) @@ -138,7 +76,7 @@ abstract class TreeInfo { } /** The number of arguments in an application */ - def numArgs[T >: Untyped](tree: Tree[T]): Int = tree match { + def numArgs(tree: Tree): Int = tree match { case Apply(fn, args) => numArgs(fn) + args.length case TypeApply(fn, args) => numArgs(fn) + args.length case AppliedTypeTree(fn, args) => numArgs(fn) + args.length @@ -146,120 +84,75 @@ abstract class TreeInfo { case _ => 0 } - /** Is symbol potentially a getter of a mutable variable? - */ - def mayBeVarGetter(sym: Symbol)(implicit ctx: Context): Boolean = { - def maybeGetterType(tpe: Type): Boolean = tpe match { - case _: ExprType | _: ImplicitMethodType => true - case tpe: PolyType => maybeGetterType(tpe.resultType) - case _ => false - } - sym.owner.isClass && !sym.isStable && maybeGetterType(sym.info) - } - - /** Is tree a reference to a mutable variable, or to a potential getter - * that has a setter in the same class? - */ - def isVariableOrGetter(tree: Tree[Type])(implicit ctx: Context) = { - def sym = tree.symbol - def isVar = sym is Mutable - def isGetter = - mayBeVarGetter(sym) && sym.owner.info.member(sym.name.asTermName.getterToSetter).exists - - tree match { - case Ident(_) => isVar - case Select(_, _) => isVar || isGetter - case Apply(_, _) => - methPart(tree) match { - case Select(qual, nme.apply) => qual.tpe.member(nme.update).exists - case _ => false - } - case _ => false - } - } - /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the * same object? */ - def isSelfConstrCall(tree: Tree[_ >: Untyped]): Boolean = methPart(tree) match { + def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match { case Ident(nme.CONSTRUCTOR) | Select(This(_), nme.CONSTRUCTOR) => true case _ => false } /** Is tree a super constructor call? */ - def isSuperConstrCall(tree: Tree[_ >: Untyped]): Boolean = methPart(tree) match { + def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { case Select(Super(_, _), nme.CONSTRUCTOR) => true case _ => false } - def isSelfOrSuperConstrCall(tree: Tree[_ >: Untyped]): Boolean = methPart(tree) match { + def isSelfOrSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { case Ident(nme.CONSTRUCTOR) | Select(This(_), nme.CONSTRUCTOR) | Select(Super(_, _), nme.CONSTRUCTOR) => true case _ => false } - /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ - def stripCast(tree: Tree[Type])(implicit ctx: Context): Tree[Type] = { - def isCast(sel: Tree[Type]) = defn.asInstanceOfMethods contains sel.symbol - tree match { - case TypeApply(sel @ Select(inner, _), _) if isCast(sel) => - stripCast(inner) - case Apply(TypeApply(sel @ Select(inner, _), _), Nil) if isCast(sel) => - stripCast(inner) - case t => - t - } - } - /** Is tree a variable pattern? */ - def isVarPattern[T >: Untyped](pat: Tree[T]): Boolean = pat match { - case x: BackquotedIdent[_] => false - case x: Ident[_] => x.name.isVariableName + def isVarPattern(pat: Tree): Boolean = pat match { + case x: BackquotedIdent => false + case x: Ident => x.name.isVariableName case _ => false } /** The first constructor definition in `stats` */ - def firstConstructor[T >: Untyped](stats: List[Tree[T]]): Tree[T] = stats match { - case (meth: DefDef[_]) :: _ if meth.name.isConstructorName => meth + def firstConstructor(stats: List[Tree]): Tree = stats match { + case (meth: DefDef) :: _ if meth.name.isConstructorName => meth case stat :: stats => firstConstructor(stats) - case nil => emptyTree() + case nil => EmptyTree } /** The arguments to the first constructor in `stats`. */ - def firstConstructorArgs[T >: Untyped](stats: List[Tree[T]]): List[Tree[T]] = firstConstructor(stats) match { + def firstConstructorArgs(stats: List[Tree]): List[Tree] = firstConstructor(stats) match { case DefDef(_, _, _, args :: _, _, _) => args case _ => Nil } /** The value definitions marked PRESUPER in this statement sequence */ - def preSuperFields[T >: Untyped](stats: List[Tree[T]]): List[ValDef[T]] = - (stats filter isEarlyValDef).asInstanceOf[List[ValDef[T]]] + def preSuperFields(stats: List[Tree]): List[ValDef] = + (stats filter isEarlyValDef).asInstanceOf[List[ValDef]] - def isEarlyDef(tree: Tree[_ >: Untyped]) = isEarlyValDef(tree) || isEarlyTypeDef(tree) + def isEarlyDef(tree: Tree) = isEarlyValDef(tree) || isEarlyTypeDef(tree) - def isEarlyValDef(tree: Tree[_ >: Untyped]) = tree match { + def isEarlyValDef(tree: Tree) = tree match { case ValDef(mods, _, _, _) => mods is Scala2PreSuper case _ => false } - def isEarlyTypeDef(tree: Tree[_ >: Untyped]) = tree match { + def isEarlyTypeDef(tree: Tree) = tree match { case TypeDef(mods, _, _) => mods is Scala2PreSuper case _ => false } /** Is tpt a vararg type of the form T* ? */ - def isRepeatedParamType(tpt: Tree[_ >: Untyped])(implicit ctx: Context) = tpt match { - case tpt: TypeTree[_] => tpt.typeOpt.isRepeatedParam + def isRepeatedParamType(tpt: Tree)(implicit ctx: Context) = tpt match { + case tpt: TypeTree => tpt.typeOpt.isRepeatedParam case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true case AppliedTypeTree(Select(_, tpnme.JAVA_REPEATED_PARAM_CLASS), _) => true case _ => false } /** Is tpt a by-name parameter type of the form => T? */ - def isByNameParamType(tpt: Tree[_ >: Untyped])(implicit ctx: Context) = tpt match { - case tpt: TypeTree[_] => tpt.typeOpt.typeSymbol == defn.ByNameParamClass + def isByNameParamType(tpt: Tree)(implicit ctx: Context) = tpt match { + case tpt: TypeTree => tpt.typeOpt.typeSymbol == defn.ByNameParamClass case AppliedTypeTree(Select(_, tpnme.BYNAME_PARAM_CLASS), _) => true case _ => false } @@ -267,22 +160,12 @@ abstract class TreeInfo { /** Is name a left-associative operator? */ def isLeftAssoc(operator: Name) = operator.nonEmpty && (operator.last != ':') - /** Is tree a `this` node which belongs to `enclClass`? */ - def isSelf(tree: Tree[_ >: Untyped], enclClass: Symbol)(implicit ctx: Context): Boolean = tree match { - case This(_) => tree.symbol == enclClass - case _ => false - } - - /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) - def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation defn.SwitchClass - */ - /** can this type be a type pattern? */ - def mayBeTypePat(tree: Tree[Untyped]): Boolean = tree match { + def mayBeTypePat(tree: untpd.Tree): Boolean = tree match { case AndTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) case OrTypeTree(tpt1, tpt2) => mayBeTypePat(tpt1) || mayBeTypePat(tpt2) - case RefinedTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind[_]]) - case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind[_]]) + case RefinedTypeTree(tpt, refinements) => mayBeTypePat(tpt) || refinements.exists(_.isInstanceOf[Bind]) + case AppliedTypeTree(tpt, args) => mayBeTypePat(tpt) || args.exists(_.isInstanceOf[Bind]) case SelectFromTypeTree(tpt, _) => mayBeTypePat(tpt) case Annotated(_, tpt) => mayBeTypePat(tpt) case _ => false @@ -290,13 +173,13 @@ abstract class TreeInfo { /** Is this argument node of the form <expr> : _* ? */ - def isWildcardStarArg(tree: Tree[_ >: Untyped]): Boolean = tree match { + def isWildcardStarArg(tree: untpd.Tree): Boolean = tree match { case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true case _ => false } /** If this tree has type parameters, those. Otherwise Nil. - def typeParameters(tree: Tree[_]): List[TypeDef] = tree match { + def typeParameters(tree: Tree): List[TypeDef] = tree match { case DefDef(_, _, tparams, _, _, _) => tparams case ClassDef(_, _, tparams, _) => tparams case TypeDef(_, _, tparams, _) => tparams @@ -304,42 +187,42 @@ abstract class TreeInfo { }*/ /** Does this argument list end with an argument of the form <expr> : _* ? */ - def isWildcardStarArgList(trees: List[Tree[_ >: Untyped]]) = + def isWildcardStarArgList(trees: List[Tree]) = trees.nonEmpty && isWildcardStarArg(trees.last) /** Is the argument a wildcard argument of the form `_` or `x @ _`? */ - def isWildcardArg(tree: Tree[_ >: Untyped]): Boolean = unbind(tree) match { + def isWildcardArg(tree: Tree): Boolean = unbind(tree) match { case Ident(nme.WILDCARD) => true case _ => false } /** Is the argument a wildcard star type of the form `_*`? */ - def isWildcardStarType(tree: Tree[_ >: Untyped]): Boolean = tree match { + def isWildcardStarType(tree: Tree): Boolean = tree match { case Ident(tpnme.WILDCARD_STAR) => true case _ => false } /** Is this pattern node a catch-all (wildcard or variable) pattern? */ - def isDefaultCase(cdef: CaseDef[_ >: Untyped]) = cdef match { + def isDefaultCase(cdef: CaseDef) = cdef match { case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat) case _ => false } /** Is this pattern node a synthetic catch-all case, added during PartialFuction synthesis before we know * whether the user provided cases are exhaustive. */ - def isSyntheticDefaultCase(cdef: CaseDef[_ >: Untyped]) = cdef match { + def isSyntheticDefaultCase(cdef: CaseDef) = cdef match { case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree, _) => true case _ => false } /** Does this CaseDef catch Throwable? */ - def catchesThrowable(cdef: CaseDef[_ >: Untyped])(implicit ctx: Context) = + def catchesThrowable(cdef: CaseDef)(implicit ctx: Context) = catchesAllOf(cdef, defn.ThrowableClass.typeConstructor) /** Does this CaseDef catch everything of a certain Type? */ - def catchesAllOf(cdef: CaseDef[_ >: Untyped], threshold: Type)(implicit ctx: Context) = + def catchesAllOf(cdef: CaseDef, threshold: Type)(implicit ctx: Context) = isDefaultCase(cdef) || cdef.guard.isEmpty && { unbind(cdef.pat) match { @@ -348,8 +231,135 @@ abstract class TreeInfo { } } + /** Is this case guarded? */ + def isGuardedCase(cdef: CaseDef) = cdef.guard ne EmptyTree + + /** Is this pattern node a sequence-valued pattern? */ + def isSequenceValued(tree: Tree): Boolean = unbind(tree) match { + case Alternative(ts) => ts exists isSequenceValued + case SeqLiteral(_) => true + case _ => false + } + + /** The underlying pattern ignoring any bindings */ + def unbind(x: Tree): Tree = x match { + case Bind(_, y) => unbind(y) + case y => y + } +} + +trait TypedTreeInfo extends TreeInfo[Type] {self: Trees.Instance[Type] => + + /** Is tree a definition that has no side effects when + * evaluated as part of a block after the first time? + */ + def isIdempotentDef(tree: tpd.Tree)(implicit ctx: Context): Boolean = tree match { + case EmptyTree + | TypeDef(_, _, _) + | Import(_, _) + | DefDef(_, _, _, _, _, _) => + true + case ValDef(mods, _, _, rhs) => + !(mods is Mutable) && isIdempotentExpr(rhs) + case _ => + false + } + + /** Is tree an expression which can be inlined without affecting program semantics? + * + * Note that this is not called "isExprPure" since purity (lack of side-effects) + * is not the litmus test. References to modules and lazy vals are side-effecting, + * both because side-effecting code may be executed and because the first reference + * takes a different code path than all to follow; but they are safe to inline + * because the expression result from evaluating them is always the same. + */ + def isIdempotentExpr(tree: tpd.Tree)(implicit ctx: Context): Boolean = tree match { + case EmptyTree + | This(_) + | Super(_, _) + | Literal(_) => + true + case Ident(_) => + tree.symbol is Stable + case Select(qual, _) => + tree.symbol.isStable && isIdempotentExpr(qual) + case TypeApply(fn, _) => + isIdempotentExpr(fn) +/* + * Not sure we'll need that. Comment out until we find out + case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => + // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` + free.symbol.hasStableFlag && isIdempotentExpr(free) +*/ + case Apply(fn, Nil) => + // Note: After uncurry, field accesses are represented as Apply(getter, Nil), + // so an Apply can also be pure. + // However, before typing, applications of nullary functional values are also + // Apply(function, Nil) trees. To prevent them from being treated as pure, + // we check that the callee is a method. + // The callee might also be a Block, which has a null symbol, so we guard against that (SI-7185) + fn.symbol != null && (fn.symbol is (Method, butNot = Lazy)) && isIdempotentExpr(fn) + case Typed(expr, _) => + isIdempotentExpr(expr) + case Block(stats, expr) => + (stats forall isIdempotentDef) && isIdempotentExpr(expr) + case _ => + false + } + + /** Is symbol potentially a getter of a mutable variable? + */ + def mayBeVarGetter(sym: Symbol)(implicit ctx: Context): Boolean = { + def maybeGetterType(tpe: Type): Boolean = tpe match { + case _: ExprType | _: ImplicitMethodType => true + case tpe: PolyType => maybeGetterType(tpe.resultType) + case _ => false + } + sym.owner.isClass && !sym.isStable && maybeGetterType(sym.info) + } + + /** Is tree a reference to a mutable variable, or to a potential getter + * that has a setter in the same class? + */ + def isVariableOrGetter(tree: tpd.Tree)(implicit ctx: Context) = { + def sym = tree.symbol + def isVar = sym is Mutable + def isGetter = + mayBeVarGetter(sym) && sym.owner.info.member(sym.name.asTermName.getterToSetter).exists + + tree match { + case Ident(_) => isVar + case Select(_, _) => isVar || isGetter + case Apply(_, _) => + methPart(tree) match { + case Select(qual, nme.apply) => qual.tpe.member(nme.update).exists + case _ => false + } + case _ => false + } + } + + /** Is tree a `this` node which belongs to `enclClass`? */ + def isSelf(tree: Tree, enclClass: Symbol)(implicit ctx: Context): Boolean = tree match { + case This(_) => tree.symbol == enclClass + case _ => false + } + + /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ + def stripCast(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = { + def isCast(sel: tpd.Tree) = defn.asInstanceOfMethods contains sel.symbol + tree match { + case TypeApply(sel @ Select(inner, _), _) if isCast(sel) => + stripCast(inner) + case Apply(TypeApply(sel @ Select(inner, _), _), Nil) if isCast(sel) => + stripCast(inner) + case t => + t + } + } + /** Is this pattern node a catch-all or type-test pattern? */ - def isCatchCase(cdef: CaseDef[Type])(implicit ctx: Context) = cdef match { + def isCatchCase(cdef: CaseDef)(implicit ctx: Context) = cdef match { case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) => isSimpleThrowable(tpt.tpe) case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) => @@ -366,21 +376,10 @@ abstract class TreeInfo { false } - /** Is this case guarded? */ - def isGuardedCase(cdef: CaseDef[_ >: Untyped]) = cdef.guard ne EmptyTree - - /** Is this pattern node a sequence-valued pattern? */ - def isSequenceValued(tree: Tree[_ >: Untyped]): Boolean = unbind(tree) match { - case Alternative(ts) => ts exists isSequenceValued - case SeqLiteral(_) => true - case _ => false - } - - /** The underlying pattern ignoring any bindings */ - def unbind[T >: Untyped](x: Tree[T]): Tree[T] = x match { - case Bind(_, y) => unbind(y) - case y => y - } + /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) + def isSwitchAnnotation(tpe: Type) = tpe hasAnnotation defn.SwitchClass + */ +} /** Does list of trees start with a definition of @@ -489,5 +488,6 @@ abstract class TreeInfo { case tree: RefTree => true case _ => false })*/ -} -object TreeInfo extends TreeInfo
\ No newline at end of file + + + |