diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-11-02 11:08:28 +0100 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-11-22 01:35:07 +0100 |
commit | 8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch) | |
tree | a8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | |
parent | 6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff) | |
download | dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2 dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip |
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala new file mode 100644 index 000000000..ee2d68278 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -0,0 +1,524 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ +import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ +import TypeApplications.AppliedType +import util.Positions._ +import config.Printers.typr +import ast.Trees._ +import NameOps._ +import collection.mutable +import reporting.diagnostic.Message +import reporting.diagnostic.messages._ + +trait TypeAssigner { + import tpd._ + + /** The qualifying class of a this or super with prefix `qual` (which might be empty). + * @param packageOk The qualifier may refer to a package. + */ + def qualifyingClass(tree: untpd.Tree, qual: Name, packageOK: Boolean)(implicit ctx: Context): Symbol = { + def qualifies(sym: Symbol) = + sym.isClass && ( + qual.isEmpty || + sym.name == qual || + sym.is(Module) && sym.name.stripModuleClassSuffix == qual) + ctx.outersIterator.map(_.owner).find(qualifies) match { + case Some(c) if packageOK || !(c is Package) => + c + case _ => + ctx.error( + if (qual.isEmpty) tree.show + " can be used only in a class, object, or template" + else qual.show + " is not an enclosing class", tree.pos) + NoSymbol + } + } + + /** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`. + * Approximation steps are: + * + * - follow aliases and upper bounds if the original refers to a forbidden symbol + * - widen termrefs that refer to a forbidden symbol + * - replace ClassInfos of forbidden classes by the intersection of their parents, refined by all + * non-private fields, methods, and type members. + * - if the prefix of a class refers to a forbidden symbol, first try to replace the prefix, + * if this is not possible, replace the ClassInfo as above. + * - drop refinements referring to a forbidden symbol. + */ + def avoid(tp: Type, symsToAvoid: => List[Symbol])(implicit ctx: Context): Type = { + val widenMap = new TypeMap { + lazy val forbidden = symsToAvoid.toSet + def toAvoid(tp: Type): Boolean = + // TODO: measure the cost of using `existsPart`, and if necessary replace it + // by a `TypeAccumulator` where we have set `stopAtStatic = true`. + tp existsPart { + case tp: NamedType => forbidden contains tp.symbol + case tp: ThisType => forbidden contains tp.cls + case _ => false + } + def apply(tp: Type): Type = tp match { + case tp: TermRef + if toAvoid(tp) && (variance > 0 || tp.info.widenExpr <:< tp) => + // Can happen if `x: y.type`, then `x.type =:= y.type`, hence we can widen `x.type` + // to y.type in all contexts, not just covariant ones. + apply(tp.info.widenExpr) + case tp: TypeRef if toAvoid(tp) => + tp.info match { + case TypeAlias(ref) => + apply(ref) + case info: ClassInfo if variance > 0 => + if (!(forbidden contains tp.symbol)) { + val prefix = apply(tp.prefix) + val tp1 = tp.derivedSelect(prefix) + if (tp1.typeSymbol.exists) + return tp1 + } + val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _)) + def addRefinement(parent: Type, decl: Symbol) = { + val inherited = + parentType.findMember(decl.name, info.cls.thisType, Private) + .suchThat(decl.matches(_)) + val inheritedInfo = inherited.info + if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) { + val r = RefinedType(parent, decl.name, decl.info) + typr.println(i"add ref $parent $decl --> " + r) + r + } + else + parent + } + val refinableDecls = info.decls.filterNot( + sym => sym.is(TypeParamAccessor | Private) || sym.isConstructor) + val fullType = (parentType /: refinableDecls)(addRefinement) + mapOver(fullType) + case TypeBounds(lo, hi) if variance > 0 => + apply(hi) + case _ => + mapOver(tp) + } + case tp @ HKApply(tycon, args) if toAvoid(tycon) => + apply(tp.superType) + case tp @ AppliedType(tycon, args) if toAvoid(tycon) => + val base = apply(tycon) + var args = tp.baseArgInfos(base.typeSymbol) + if (base.typeParams.length != args.length) + args = base.typeParams.map(_.paramBounds) + apply(base.appliedTo(args)) + case tp @ RefinedType(parent, name, rinfo) if variance > 0 => + val parent1 = apply(tp.parent) + val refinedInfo1 = apply(rinfo) + if (toAvoid(refinedInfo1)) { + typr.println(s"dropping refinement from $tp") + if (name.isTypeName) tp.derivedRefinedType(parent1, name, TypeBounds.empty) + else parent1 + } else { + tp.derivedRefinedType(parent1, name, refinedInfo1) + } + case tp: TypeVar if ctx.typerState.constraint.contains(tp) => + val lo = ctx.typerState.constraint.fullLowerBound(tp.origin) + val lo1 = avoid(lo, symsToAvoid) + if (lo1 ne lo) lo1 else tp + case _ => + mapOver(tp) + } + } + widenMap(tp) + } + + def avoidingType(expr: Tree, bindings: List[Tree])(implicit ctx: Context): Type = + avoid(expr.tpe, localSyms(bindings).filter(_.isTerm)) + + def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = + Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass))) + + /** A denotation exists really if it exists and does not point to a stale symbol. */ + final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try + denot match { + case denot: SymDenotation => + denot.exists && { + denot.ensureCompleted + !denot.isAbsent + } + case denot: SingleDenotation => + val sym = denot.symbol + (sym eq NoSymbol) || reallyExists(sym.denot) + case _ => + true + } + catch { + case ex: StaleSymbol => false + } + + /** If `tpe` is a named type, check that its denotation is accessible in the + * current context. Return the type with those alternatives as denotations + * which are accessible. + * + * Also performs the following normalizations on the type `tpe`. + * (1) parameter accessors are always dereferenced. + * (2) if the owner of the denotation is a package object, it is assured + * that the package object shows up as the prefix. + */ + def ensureAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = { + def test(tpe: Type, firstTry: Boolean): Type = tpe match { + case tpe: NamedType => + val pre = tpe.prefix + val name = tpe.name + val d = tpe.denot.accessibleFrom(pre, superAccess) + if (!d.exists) { + // it could be that we found an inaccessible private member, but there is + // an inherited non-private member with the same name and signature. + val d2 = pre.nonPrivateMember(name) + if (reallyExists(d2) && firstTry) + test(tpe.shadowed.withDenot(d2), false) + else if (pre.derivesFrom(defn.DynamicClass)) { + TryDynamicCallType + } else { + val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) + val what = alts match { + case Nil => + name.toString + case sym :: Nil => + if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated + case _ => + em"none of the overloaded alternatives named $name" + } + val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" + val whyNot = new StringBuffer + alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot)) + if (!tpe.isError) + ctx.error(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos) + ErrorType + } + } + else if (d.symbol is TypeParamAccessor) + if (d.info.isAlias) + ensureAccessible(d.info.bounds.hi, superAccess, pos) + else // It's a named parameter, use the non-symbolic representation to pick up inherited versions as well + d.symbol.owner.thisType.select(d.symbol.name) + else + ctx.makePackageObjPrefixExplicit(tpe withDenot d) + case _ => + tpe + } + test(tpe, true) + } + + /** The type of a selection with `name` of a tree with type `site`. + */ + def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = { + val mbr = site.member(name) + if (reallyExists(mbr)) site.select(name, mbr) + else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) { + TryDynamicCallType + } else { + if (!site.isErroneous) { + def kind = if (name.isTypeName) "type" else "value" + def addendum = + if (site.derivesFrom(defn.DynamicClass)) "\npossible cause: maybe a wrong Dynamic method signature?" + else "" + ctx.error( + if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor" + else NotAMember(site, name, kind), + pos) + } + ErrorType + } + } + + /** The selection type, which is additionally checked for accessibility. + */ + def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(implicit ctx: Context): Type = { + val ownType = selectionType(qual1.tpe.widenIfUnstable, tree.name, tree.pos) + ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.pos) + } + + /** Type assignment method. Each method takes as parameters + * - an untpd.Tree to which it assigns a type, + * - typed child trees it needs to access to cpmpute that type, + * - any further information it needs to access to compute that type. + */ + + def assignType(tree: untpd.Ident, tp: Type)(implicit ctx: Context) = + tree.withType(tp) + + def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context): Select = { + def qualType = qual.tpe.widen + def arrayElemType = { + val JavaArrayType(elemtp) = qualType + elemtp + } + val p = nme.primitive + val tp = tree.name match { + case p.arrayApply => MethodType(defn.IntType :: Nil, arrayElemType) + case p.arrayUpdate => MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType) + case p.arrayLength => MethodType(Nil, defn.IntType) + + // Note that we do not need to handle calls to Array[T]#clone() specially: + // The JLS section 10.7 says "The return type of the clone method of an array type + // T[] is T[]", but the actual return type at the bytecode level is Object which + // is casted to T[] by javac. Since the return type of Array[T]#clone() is Array[T], + // this is exactly what Erasure will do. + + case _ => accessibleSelectionType(tree, qual) + } + tree.withType(tp) + } + + def assignType(tree: untpd.New, tpt: Tree)(implicit ctx: Context) = + tree.withType(tpt.tpe) + + def assignType(tree: untpd.Literal)(implicit ctx: Context) = + tree.withType { + val value = tree.const + value.tag match { + case UnitTag => defn.UnitType + case NullTag => defn.NullType + case _ => if (ctx.erasedTypes) value.tpe else ConstantType(value) + } + } + + def assignType(tree: untpd.This)(implicit ctx: Context) = { + val cls = qualifyingClass(tree, tree.qual.name, packageOK = false) + tree.withType(cls.thisType) + } + + def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = { + val mix = tree.mix + val qtype @ ThisType(_) = qual.tpe + val cls = qtype.cls + + def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match { + case p :: Nil => + p + case Nil => + errorType(em"$mix does not name a parent class of $cls", tree.pos) + case p :: q :: _ => + errorType("ambiguous parent class qualifier", tree.pos) + } + val owntype = + if (mixinClass.exists) mixinClass.typeRef + else if (!mix.isEmpty) findMixinSuper(cls.info) + else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent + else { + val ps = cls.classInfo.parentsWithArgs + if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) + } + tree.withType(SuperType(cls.thisType, owntype)) + } + + def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { + val ownType = fn.tpe.widen match { + case fntpe @ MethodType(_, ptypes) => + if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) + else wrongNumberOfArgs(fn.tpe, "", fntpe.typeParams, args, tree.pos) + case t => + errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos) + } + tree.withType(ownType) + } + + def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { + val ownType = fn.tpe.widen match { + case pt: PolyType => + val paramNames = pt.paramNames + if (hasNamedArg(args)) { + // Type arguments which are specified by name (immutable after this first loop) + val namedArgMap = new mutable.HashMap[Name, Type] + for (NamedArg(name, arg) <- args) + if (namedArgMap.contains(name)) + ctx.error("duplicate name", arg.pos) + else if (!paramNames.contains(name)) + ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos) + else + namedArgMap(name) = arg.tpe + + // Holds indexes of non-named typed arguments in paramNames + val gapBuf = new mutable.ListBuffer[Int] + def nextPoly(idx: Int) = { + val newIndex = gapBuf.length + gapBuf += idx + // Re-index unassigned type arguments that remain after transformation + PolyParam(pt, newIndex) + } + + // Type parameters after naming assignment, conserving paramNames order + val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) => + namedArgMap.getOrElse(pname, nextPoly(idx)) + } + + val transform = new TypeMap { + def apply(t: Type) = t match { + case PolyParam(`pt`, idx) => normArgs(idx) + case _ => mapOver(t) + } + } + val resultType1 = transform(pt.resultType) + if (gapBuf.isEmpty) resultType1 + else { + val gaps = gapBuf.toList + pt.derivedPolyType( + gaps.map(paramNames), + gaps.map(idx => transform(pt.paramBounds(idx)).bounds), + resultType1) + } + } + else { + val argTypes = args.tpes + if (sameLength(argTypes, paramNames) || ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) + else wrongNumberOfArgs(fn.tpe, "type", pt.typeParams, args, tree.pos) + } + case _ => + errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) + } + + tree.withType(ownType) + } + + def assignType(tree: untpd.Typed, tpt: Tree)(implicit ctx: Context) = + tree.withType(tpt.tpe) + + def assignType(tree: untpd.NamedArg, arg: Tree)(implicit ctx: Context) = + tree.withType(arg.tpe) + + def assignType(tree: untpd.Assign)(implicit ctx: Context) = + tree.withType(defn.UnitType) + + def assignType(tree: untpd.Block, stats: List[Tree], expr: Tree)(implicit ctx: Context) = + tree.withType(avoidingType(expr, stats)) + + def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(implicit ctx: Context) = + tree.withType(avoidingType(expansion, bindings)) + + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) = + tree.withType(thenp.tpe | elsep.tpe) + + def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = + tree.withType( + if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length) + else target.tpe) + + def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = + tree.withType(body.tpe) + + def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) = + tree.withType(ctx.typeComparer.lub(cases.tpes)) + + def assignType(tree: untpd.Return)(implicit ctx: Context) = + tree.withType(defn.NothingType) + + def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) = + if (cases.isEmpty) tree.withType(expr.tpe) + else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes)) + + def assignType(tree: untpd.SeqLiteral, elems: List[Tree], elemtpt: Tree)(implicit ctx: Context) = { + val ownType = tree match { + case tree: untpd.JavaSeqLiteral => defn.ArrayOf(elemtpt.tpe) + case _ => if (ctx.erasedTypes) defn.SeqType else defn.SeqType.appliedTo(elemtpt.tpe) + } + tree.withType(ownType) + } + + def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) = + tree.withType(ref.tpe) + + def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + tree.withType(left.tpe & right.tpe) + + def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = + tree.withType(left.tpe | right.tpe) + + /** Assign type of RefinedType. + * Refinements are typed as if they were members of refinement class `refineCls`. + */ + def assignType(tree: untpd.RefinedTypeTree, parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context) = { + def addRefinement(parent: Type, refinement: Tree): Type = { + val rsym = refinement.symbol + val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info + RefinedType(parent, rsym.name, rinfo) + } + val refined = (parent.tpe /: refinements)(addRefinement) + tree.withType(RecType.closeOver(rt => refined.substThis(refineCls, RecThis(rt)))) + } + + def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { + val tparams = tycon.tpe.typeParams + lazy val ntparams = tycon.tpe.namedTypeParams + def refineNamed(tycon: Type, arg: Tree) = arg match { + case ast.Trees.NamedArg(name, argtpt) => + // Dotty deviation: importing ast.Trees._ and matching on NamedArg gives a cyclic ref error + val tparam = tparams.find(_.paramName == name) match { + case Some(tparam) => tparam + case none => ntparams.find(_.name == name).getOrElse(NoSymbol) + } + if (tparam.isTypeParam) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) + else errorType(i"$tycon does not have a parameter or abstract type member named $name", arg.pos) + case _ => + errorType(s"named and positional type arguments may not be mixed", arg.pos) + } + val ownType = + if (hasNamedArg(args)) (tycon.tpe /: args)(refineNamed) + else if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) + else wrongNumberOfArgs(tycon.tpe, "type", tparams, args, tree.pos) + tree.withType(ownType) + } + + def assignType(tree: untpd.PolyTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) = + tree.withType(body.tpe.LambdaAbstract(tparamDefs.map(_.symbol))) + + def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = + tree.withType(ExprType(result.tpe)) + + def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) = + tree.withType(if (lo eq hi) TypeAlias(lo.tpe) else TypeBounds(lo.tpe, hi.tpe)) + + def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) = + tree.withType(NamedType.withFixedSym(NoPrefix, sym)) + + def assignType(tree: untpd.Alternative, trees: List[Tree])(implicit ctx: Context) = + tree.withType(ctx.typeComparer.lub(trees.tpes)) + + def assignType(tree: untpd.UnApply, proto: Type)(implicit ctx: Context) = + tree.withType(proto) + + def assignType(tree: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = + tree.withType(if (sym.exists) assertExists(symbolicIfNeeded(sym).orElse(sym.valRef)) else NoType) + + def assignType(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = + tree.withType(symbolicIfNeeded(sym).orElse(sym.termRefWithSig)) + + def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = + tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) + + private def symbolicIfNeeded(sym: Symbol)(implicit ctx: Context) = { + val owner = sym.owner + owner.infoOrCompleter match { + case info: ClassInfo if info.givenSelfType.exists => + // In that case a simple typeRef/termWithWithSig could return a member of + // the self type, not the symbol itself. To avoid this, we make the reference + // symbolic. In general it seems to be faster to keep the non-symblic + // reference, since there is less pressure on the uniqueness tables that way + // and less work to update all the different references. That's why symbolic references + // are only used if necessary. + NamedType.withFixedSym(owner.thisType, sym) + case _ => NoType + } + } + + def assertExists(tp: Type) = { assert(tp != NoType); tp } + + def assignType(tree: untpd.Import, sym: Symbol)(implicit ctx: Context) = + tree.withType(sym.nonMemberTermRef) + + def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(implicit ctx: Context) = + tree.withType(AnnotatedType(arg.tpe.widen, Annotation(annot))) + + def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) = + tree.withType(pid.symbol.valRef) +} + +object TypeAssigner extends TypeAssigner + |