package dotty.tools
package dotc
package typer
import core._
import ast._
import Trees._
import Constants._
import StdNames._
import Scopes._
import Denotations._
import Inferencing._
import Contexts._
import Symbols._
import Types._
import SymDenotations._
import Annotations._
import Names._
import NameOps._
import Flags._
import Decorators._
import ErrorReporting._
import Inferencing.{FunProto, PolyProto, Compatibility, normalize}
import EtaExpansion.etaExpand
import util.Positions._
import util.SourcePosition
import collection.mutable
import annotation.tailrec
import language.implicitConversions
trait TyperContextOps { ctx: Context => }
object Typer {
import tpd.{cpy => _, _}
object BindingPrec {
val definition = 4
val namedImport = 3
val wildImport = 2
val packageClause = 1
val nothingBound = 0
def isImportPrec(prec: Int) = prec == namedImport || prec == wildImport
}
implicit class TreeDecorator(tree: Tree) {
def qualifierType(implicit ctx: Context): Type = tree.tpe match {
case tpe: TermRef if !tpe.symbol.isStable => tpe.info
case tpe => tpe
}
}
case class StateFul[T](value: T, state: TyperState) {
def commit()(implicit ctx: Context): T = {
state.commit()
value
}
}
}
class Typer extends Namer with Applications with Implicits {
import Typer._
import tpd.{cpy => _, _}
import untpd.cpy
/** A temporary data item valid for a single typed ident:
* The set of all root import symbols that have been
* encountered as a qualifier of an import so far.
* Note: It would be more proper to move importedFromRoot into typedIdent.
* We should check that this has no performance degradation, however.
*/
private var importedFromRoot: Set[Symbol] = Set()
def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = {
val ref =
if (name == nme.CONSTRUCTOR) site.decl(name)
else site.member(name)
if (ref.exists) NamedType(site, name).withDenot(ref)
else {
if (!site.isErroneous)
ctx.error(
if (name == nme.CONSTRUCTOR) i"$site does not have a constructor"
else i"$name is not a member of $site", pos)
ErrorType
}
}
def checkedSelectionType(qual1: Tree, tree: untpd.RefTree)(implicit ctx: Context): Type = {
val ownType = selectionType(qual1.qualifierType, tree.name, tree.pos)
if (!ownType.isError) checkAccessible(ownType, qual1.isInstanceOf[Super], tree.pos)
ownType
}
def checkValue(tpe: Type, proto: Type, pos: Position)(implicit ctx: Context): Unit =
if (!proto.isInstanceOf[SelectionProto]) {
val sym = tpe.termSymbol
if ((sym is Package) || (sym is JavaModule)) ctx.error(i"$sym is not a value", pos)
}
def checkAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = tpe match {
case tpe: NamedType =>
val pre = tpe.prefix
val name = tpe.name
val d = tpe.denot.accessibleFrom(pre, superAccess)
if (!d.exists) {
val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists)
val where = pre.typeSymbol
val what = alts match {
case Nil =>
name.toString
case sym :: Nil =>
if (sym.owner == where) sym.show else sym.showLocated
case _ =>
i"none of the overloaded alternatives named $name"
}
val whyNot = new StringBuffer
val addendum =
alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot))
ctx.error(i"$what cannot be accessed in $where.$whyNot")
ErrorType
} else tpe withDenot d
case _ =>
tpe
}
/** The qualifying class
* of a this or super with prefix `qual`.
* packageOk is equal false when qualifying class symbol
*/
def qualifyingClass(tree: untpd.Tree, qual: Name, packageOK: Boolean)(implicit ctx: Context): Symbol =
ctx.owner.enclosingClass.ownersIterator.find(o => qual.isEmpty || o.isClass && o.name == qual) 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
}
/** Attribute an identifier consisting of a simple name or an outer reference.
*
* @param tree The tree representing the identifier.
* Transformations: (1) Prefix class members with this.
* (2) Change imported symbols to selections
*
*/
def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = {
val name = tree.name
/** Method is necessary because error messages need to bind to
* to typedIdent's context which is lost in nested calls to findRef
*/
def error(msg: => String, pos: Position) = ctx.error(msg, pos)
/** Is this import a root import that has been shadowed by an explicit
* import in the same program?
*/
def isDisabled(imp: ImportInfo, site: Type): Boolean = {
val qualSym = site.termSymbol
if (defn.RootImports contains qualSym) {
if (imp.rootImport && (importedFromRoot contains qualSym)) return true
importedFromRoot += qualSym
}
false
}
/** Does this identifier appear as a constructor of a pattern? */
def isPatternConstr =
if (ctx.mode.isExpr && (ctx.outer.mode is Mode.Pattern))
ctx.outer.tree match {
case Apply(`tree`, _) => true
case _ => false
}
else false
/** A symbol qualifies if it exists and is not stale. Stale symbols
* are made to disappear here. In addition,
* if we are in a constructor of a pattern, we ignore all definitions
* which are methods (note: if we don't do that
* case x :: xs in class List would return the :: method)
* unless they are stable or are accessors (the latter exception is for better error messages)
*/
def qualifies(sym: Symbol): Boolean = !(
sym.isAbsent
|| isPatternConstr && (sym is (Method, butNot = Accessor))
)
/** Find the denotation of enclosing `name` in given context `ctx`.
* @param previous A denotation that was found in a more deeply nested scope,
* or else `NoDenotation` if nothing was found yet.
* @param prevPrec The binding precedence of the previous denotation,
* or else `nothingBound` if nothing was found yet.
* @param prevCtx The context of the previous denotation,
* or else `NoContext` if nothing was found yet.
*/
def findRef(previous: Type, prevPrec: Int, prevCtx: Context)(implicit ctx: Context): Type = {
import BindingPrec._
/** A string which explains how something was bound; Depending on `prec` this is either
* imported by <tree>
* or defined in <symbol>
*/
def bindingString(prec: Int, whereFound: Context, qualifier: String = "") =
if (prec == wildImport || prec == namedImport) i"imported$qualifier by ${whereFound.importInfo}"
else i"defined$qualifier in ${whereFound.owner}"
/** Check that any previously found result from an inner context
* does properly shadow the new one from an outer context.
*/
def checkNewOrShadowed(found: Type, newPrec: Int): Type =
if (!previous.exists || (previous == found)) found
else {
if (!previous.isError && !found.isError)
error(
i"""reference to $name is ambiguous;
|it is both ${bindingString(newPrec, ctx, "")}
|and ${bindingString(prevPrec, prevCtx, " subsequently")}""".stripMargin,
tree.pos)
previous
}
/** The type representing a named import with enclosing name when imported
* from given `site` and `selectors`.
*/
def namedImportRef(site: Type, selectors: List[untpd.Tree]): Type = {
def checkUnambiguous(found: Type) = {
val other = namedImportRef(site, selectors.tail)
if (other.exists && (found != other))
error(i"""reference to $name is ambiguous; it is imported twice in
|${ctx.tree}""".stripMargin,
tree.pos)
found
}
val Name = name.toTermName
selectors match {
case Pair(Ident(from), Ident(Name)) :: rest =>
checkUnambiguous(selectionType(site, name, tree.pos))
case Ident(Name) :: rest =>
checkUnambiguous(selectionType(site, name, tree.pos))
case _ :: rest =>
namedImportRef(site, rest)
case nil =>
NoType
}
}
/** The type representing a wildcard import with enclosing name when imported
* from given import info
*/
def wildImportRef(imp: ImportInfo): Type = {
if (imp.wildcardImport && !(imp.excluded contains name.toTermName)) {
val pre = imp.site
if (!isDisabled(imp, pre)) {
val denot = pre.member(name)
if (denot.exists) return NamedType(pre, name).withDenot(denot)
}
}
NoType
}
/** Is (some alternative of) the given predenotation `denot`
* defined in current compilation unit?
*/
def isDefinedInCurrentUnit(denot: PreDenotation): Boolean = denot match {
case DenotUnion(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2)
case denot: SingleDenotation => denot.symbol.sourceFile == ctx.source
}
// begin findRef
if (ctx.scope == null) previous
else {
val outer = ctx.outer
if ((ctx.scope ne outer.scope) || (ctx.owner ne outer.owner)) {
val defDenots = ctx.denotsNamed(name)
if (defDenots.exists) {
val curOwner = ctx.owner
val pre = curOwner.thisType
val found = NamedType(pre, name).withDenot(defDenots toDenot pre)
if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenots))
return checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry
else if (prevPrec < packageClause)
return findRef(found, packageClause, ctx)(outer)
}
}
val curImport = ctx.importInfo
if (curImport != null && curImport.rootImport && previous.exists) return previous
if (prevPrec < namedImport && (curImport ne outer.importInfo)) {
val namedImp = namedImportRef(curImport.site, curImport.selectors)
if (namedImp.exists)
return findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer)
if (prevPrec < wildImport) {
val wildImp = wildImportRef(curImport)
if (wildImp.exists)
return findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer)
}
}
findRef(previous, prevPrec, prevCtx)(outer)
}
}
// begin typedIdent
def kind = if (name.isTermName) "term" else "type" // !!! DEBUG
println(s"typed ident $kind $name in ${ctx.owner}")
if (ctx.mode is Mode.Pattern) {
if (name == nme.WILDCARD)
return tree.withType(pt)
if (isVarPattern(tree))
return typed(untpd.Bind(name, untpd.Ident(nme.WILDCARD)).withPos(tree.pos), pt)
}
val saved = importedFromRoot
importedFromRoot = Set()
val rawType =
try findRef(NoType, BindingPrec.nothingBound, NoContext)
finally importedFromRoot = saved
checkValue(rawType, pt, tree.pos)
val ownType =
if (rawType.exists)
checkAccessible(rawType, superAccess = false, tree.pos)
else {
error(i"not found: $name", tree.pos)
ErrorType
}
tree.withType(ownType.underlyingIfRepeated)
}
def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt))
val ownType = checkedSelectionType(qual1, tree)
checkValue(ownType, pt, tree.pos)
cpy.Select(tree, qual1, tree.name).withType(ownType)
}
def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = {
val cls = qualifyingClass(tree, tree.qual, packageOK = false)
tree.withType(cls.thisType)
}
def typedSuper(tree: untpd.Super)(implicit ctx: Context): Tree = {
val mix = tree.mix
val qual1 = typed(tree.qual)
val cls = qual1.tpe.typeSymbol
def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix) match {
case p :: Nil =>
p
case Nil =>
errorType(i"$mix does not name a parent class of $cls", tree.pos)
case p :: q :: _ =>
errorType(s"ambiguous parent class qualifier", tree.pos)
}
val owntype =
if (!mix.isEmpty) findMixinSuper(cls.info)
else if (ctx.mode is Mode.InSuperInit) cls.info.firstParent
else cls.info.parents.reduceLeft((x: Type, y: Type) => AndType(x, y))
cpy.Super(tree, qual1, mix).withType(SuperType(cls.thisType, owntype))
}
def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) =
tree.withType(if (tree.const.tag == UnitTag) defn.UnitType else ConstantType(tree.const))
def typedNew(tree: untpd.New, pt: Type)(implicit ctx: Context) = tree.tpt match {
case templ: Template =>
import untpd._
val x = tpnme.ANON_CLASS
val clsDef = TypeDef(Modifiers(Final), x, templ)
typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt)
case _ =>
val tpt1 = typedType(tree.tpt)
val cls = checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos)
// todo in a later phase: checkInstantiatable(cls, tpt1.pos)
cpy.New(tree, tpt1).withType(tpt1.tpe)
}
def typedPair(tree: untpd.Pair, pt: Type)(implicit ctx: Context) = {
val (leftProto, rightProto) = pt.typeArgs match {
case l :: r :: Nil if pt.typeSymbol == defn.PairClass => (l, r)
case _ => (WildcardType, WildcardType)
}
val left1 = typed(tree.left, leftProto)
val right1 = typed(tree.right, rightProto)
cpy.Pair(tree, left1, right1).withType(defn.PairType.appliedTo(left1.tpe :: right1.tpe :: Nil))
}
def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = tree.expr match {
case id: Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) && id.name != nme.WILDCARD =>
import untpd._
typed(Bind(id.name, Typed(Ident(nme.WILDCARD), tree.tpt)).withPos(id.pos))
case _ =>
val tpt1 = typedType(tree.tpt)
val expr1 = typedExpr(tree.expr, tpt1.tpe)
cpy.Typed(tree, expr1, tpt1).withType(tpt1.tpe)
}
def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = {
val arg1 = typed(tree.arg, pt)
cpy.NamedArg(tree, tree.name, arg1).withType(arg1.tpe)
}
def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context) = tree.lhs match {
case lhs @ Apply(fn, args) =>
typed(cpy.Apply(lhs, untpd.Select(fn, nme.update), args :+ tree.rhs), pt)
case lhs =>
val lhs1 = typed(lhs)
def reassignmentToVal =
errorTree(cpy.Assign(tree, lhs1, typed(tree.rhs, lhs1.tpe.widen)),
"reassignment to val")
lhs1.tpe match {
case ref: TermRef if ref.symbol is Mutable =>
cpy.Assign(tree, lhs1, typed(tree.rhs, ref.info)).withType(defn.UnitType)
case ref: TermRef if ref.info.isParameterless =>
val pre = ref.prefix
val setterName = ref.name.setterName
val setter = pre.member(setterName)
lhs1 match {
case lhs1: RefTree if setter.exists =>
val setterTypeRaw = TermRef(pre, setterName).withDenot(setter)
val setterType = checkAccessible(setterTypeRaw, isSuperSelection(tree), tree.pos)
val lhs2 = lhs1.withName(setterName).withType(setterType)
typed(cpy.Apply(tree, untpd.TypedSplice(lhs2), tree.rhs :: Nil))
case _ =>
reassignmentToVal
}
case _ =>
reassignmentToVal
}
}
def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = {
val exprCtx = index(tree.stats)
val stats1 = typedStats(tree.stats, ctx.owner)
val expr1 = typedExpr(tree.expr, pt)(exprCtx)
val result = cpy.Block(tree, stats1, expr1).withType(blockType(stats1, expr1.tpe))
val leaks = CheckTrees.escapingRefs(result)
if (leaks.isEmpty) result
else if (isFullyDefined(pt)) {
val expr2 = typed(untpd.Typed(untpd.TypedSplice(expr1), untpd.TypeTree(pt)))
untpd.Block(stats1, expr2) withType expr2.tpe
} else errorTree(result,
i"local definition of ${leaks.head.name} escapes as part of block's type ${result.tpe}")
}
def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = {
val cond1 = typed(tree.cond, defn.BooleanType)
val thenp1 = typed(tree.thenp, pt)
val elsep1 = typed(if (tree.elsep.isEmpty) unitLiteral else tree.elsep, pt)
cpy.If(tree, cond1, thenp1, elsep1).withType(thenp1.tpe | elsep1.tpe)
}
def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = {
val untpd.Function(args, body) = tree
if (ctx.mode is Mode.Type)
typed(cpy.AppliedTypeTree(tree,
ref(defn.FunctionClass(args.length).typeConstructor), args :+ body), pt)
else {
val params = args.asInstanceOf[List[ValDef]]
val protoFormals: List[Type] = pt match {
case _ if pt.typeSymbol == defn.FunctionClass(params.length) =>
pt.typeArgs take params.length
case SAMType(meth) =>
val MethodType(_, paramTypes) = meth.info
paramTypes
case _ =>
params map Function.const(WildcardType)
}
val inferredParams: List[untpd.ValDef] =
for ((param, formal) <- params zip protoFormals) yield
if (!param.tpt.isEmpty) param
else {
val paramType =
if (isFullyDefined(formal)) formal
else errorType("missing parameter type", param.pos)
cpy.ValDef(param, param.mods, param.name, untpd.TypeTree(paramType), param.rhs)
}
typed(desugar.makeClosure(inferredParams, body), pt)
}
}
def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = {
val env1 = tree.env mapconserve (typed(_))
val meth1 = typedUnadapted(tree.meth)
val ownType = meth1.tpe.widen match {
case mt: MethodType if !mt.isDependent =>
mt.toFunctionType
case mt: MethodType =>
errorType(i"internal error: cannot turn dependent method type $mt into closure", tree.pos)
case tp =>
errorType(i"internal error: closing over non-method $tp", tree.pos)
}
cpy.Closure(tree, env1, meth1, EmptyTree).withType(ownType)
}
def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = tree.selector match {
case EmptyTree =>
typed(desugar.makeCaseLambda(tree.cases) withPos tree.pos, pt)
case _ =>
val sel1 = typedExpr(tree.selector)
val selType =
if (isFullyDefined(sel1.tpe)) sel1.tpe
else errorType("internal error: type of pattern selector is not fully defined", tree.pos)
/** gadtSyms = "all type parameters of enclosing methods that appear
* non-variantly in the selector type
*/
val gadtSyms: Set[Symbol] = {
val accu = new TypeAccumulator[Set[Symbol]] {
def apply(tsyms: Set[Symbol], t: Type): Set[Symbol] = {
val tsyms1 = t match {
case tr: TypeRef if (tr.symbol is TypeParam) && tr.symbol.owner.isTerm && variance == 0 =>
tsyms + tr.symbol
case _ =>
tsyms
}
foldOver(tsyms1, t)
}
}
accu(Set.empty, selType)
}
def typedCase(tree: untpd.CaseDef): CaseDef = {
def caseRest(pat: Tree)(implicit ctx: Context) = {
gadtSyms foreach (_.resetGADTFlexType)
foreachSubTreeOf(pat) {
case b: Bind =>
if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
else ctx.error(i"duplicate pattern variable: ${b.name}", b.pos)
case _ =>
}
val guard1 = typedExpr(tree.guard, defn.BooleanType)
val body1 = typedExpr(tree.body, pt)
cpy.CaseDef(tree, pat, guard1, body1) withType body1.tpe
}
val doCase: () => CaseDef =
() => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.withNewScope)
(doCase /: gadtSyms) ((op, tsym) => tsym.withGADTFlexType(op)) ()
}
val cases1 = tree.cases mapconserve typedCase
cpy.Match(tree, sel1, cases1).withType(ctx.lub(cases1.tpes))
}
def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = {
def enclMethInfo(cx: Context): (Tree, Type) =
if (cx == NoContext || cx.tree.isInstanceOf[Trees.TypeDef[_]]) {
ctx.error("return outside method definition")
(EmptyTree, WildcardType)
}
else cx.tree match {
case ddef: DefDef =>
val meth = ddef.symbol
val from = Ident(TermRef.withSym(NoPrefix, meth.asTerm))
val proto =
if (meth.isConstructor)
defn.UnitType
else if (ddef.tpt.isEmpty)
errorType(i"method $meth has return statement; needs result type", tree.pos)
else
ddef.tpt.tpe
(from, proto)
case _ =>
enclMethInfo(cx.outer)
}
val (from, proto) = enclMethInfo(ctx)
val expr1 = typedExpr(if (tree.expr.isEmpty) untpd.unitLiteral else tree.expr, proto)
cpy.Return(tree, expr1, from) withType defn.NothingType
}
def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = {
val expr1 = typed(tree.expr, pt)
val handler1 = typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt))
val finalizer1 = typed(tree.finalizer, defn.UnitType)
val handlerResultType = handler1.tpe match {
case defn.FunctionType(_, resultType) => resultType
case _ => defn.NothingType
}
cpy.Try(tree, expr1, handler1, finalizer1).withType(expr1.tpe | handlerResultType)
}
def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Throw = {
val expr1 = typed(tree.expr, defn.ThrowableType)
cpy.Throw(tree, expr1) withType defn.NothingType
}
def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = {
val proto1 = pt.elemType orElse WildcardType
val elems1 = tree.elems mapconserve (typed(_, proto1))
cpy.SeqLiteral(tree, elems1) withType ctx.lub(elems1.tpes)
}
def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = {
val (original1, ownType) = tree.original match {
case untpd.EmptyTree =>
assert(isFullyDefined(pt))
(EmptyTree, pt)
case original: ValDef =>
val meth = symbolOfTree(original)
assert(meth.exists, meth)
(EmptyTree, meth.info)
case original =>
val original1 = typed(original)
(original1, original1.tpe)
}
cpy.TypeTree(tree, original1) withType ownType
}
def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = {
val ref1 = typedExpr(tree.ref)
checkStable(ref1.qualifierType, tree.pos)
cpy.SingletonTypeTree(tree, ref1) withType ref1.tpe
}
def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = {
val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt))
cpy.SelectFromTypeTree(tree, qual1, tree.name).withType(checkedSelectionType(qual1, tree))
}
def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = {
val left1 = typed(tree.left)
val right1 = typed(tree.right)
cpy.AndTypeTree(tree, left1, right1) withType left1.tpe & right1.tpe
}
def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = {
val left1 = typed(tree.left)
val right1 = typed(tree.right)
cpy.OrTypeTree(tree, left1, right1) withType left1.tpe | right1.tpe
}
def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = {
val tpt1 = typedAheadType(tree.tpt)
val refineClsDef = desugar.refinedTypeToClass(tree)
val refineCls = createSymbol(refineClsDef).asClass
val TypeDef(_, _, Template(_, _, _, refinements1)) = typed(refineClsDef)
assert(tree.refinements.length == refinements1.length, s"${tree.refinements} != $refinements1")
def addRefinement(parent: Type, refinement: Tree): Type = {
foreachSubTreeOf(refinement) {
case tree: RefTree =>
if (tree.symbol.owner == refineCls && tree.pos.start <= tree.symbol.pos.end)
ctx.error("illegal forward reference in refinement", tree.pos)
case _ =>
}
val rsym = refinement.symbol
val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info
RefinedType(parent, rsym.name, rt => rinfo.substThis(refineCls, RefinedThis(rt)))
}
cpy.RefinedTypeTree(tree, tpt1, refinements1) withType
(tpt1.tpe /: refinements1)(addRefinement)
}
def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): AppliedTypeTree = {
val tpt1 = typed(tree.tpt)
val args1 = tree.args mapconserve (typed(_))
val tparams = tpt1.tpe.typeParams
if (args1.length != tparams.length)
ctx.error(i"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}")
// todo in later phase: check arguments conform to parameter bounds
cpy.AppliedTypeTree(tree, tpt1, args1) withType tpt1.tpe.appliedTo(args1.tpes)
}
def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = {
val lo1 = typed(tree.lo)
val hi1 = typed(tree.hi)
if (!(lo1.tpe <:< hi1.tpe))
ctx.error(i"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos)
cpy.TypeBoundsTree(tree, lo1, hi1) withType TypeBounds(lo1.tpe, hi1.tpe)
}
def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = {
val body1 = typed(tree.body, pt)
val sym = ctx.newSymbol(ctx.owner, tree.name.asTermName, EmptyFlags, pt, coord = tree.pos)
cpy.Bind(tree, tree.name, body1) withType TermRef.withSym(NoPrefix, sym)
}
def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = {
val trees1 = tree.trees mapconserve (typed(_, pt))
cpy.Alternative(tree, trees1) withType ctx.lub(trees1.tpes)
}
def typedModifiers(mods: untpd.Modifiers)(implicit ctx: Context): Modifiers = {
val annotations1 = mods.annotations mapconserve typedAnnotation
if (annotations1 eq mods.annotations) mods.asInstanceOf[Modifiers]
else Modifiers(mods.flags, mods.privateWithin, annotations1)
}
def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree =
typed(annot, defn.AnnotationClass.typeConstructor)
def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = {
val ValDef(mods, name, tpt, rhs) = vdef
val mods1 = typedModifiers(mods)
val tpt1 = typedType(tpt)
val rhs1 = typedExpr(rhs, tpt1.tpe)
val refType = if (sym.exists) sym.symRef else NoType
cpy.ValDef(vdef, mods1, name, tpt1, rhs1).withType(refType)
}
def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = {
val DefDef(mods, name, tparams, vparamss, tpt, rhs) = ddef
val mods1 = typedModifiers(mods)
val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef])
val vparamss1 = vparamss mapconserve(_ mapconserve (typed(_).asInstanceOf[ValDef]))
val tpt1 = typedType(tpt)
val rhs1 = typedExpr(rhs, tpt1.tpe)
cpy.DefDef(ddef, mods1, name, tparams1, vparamss1, tpt1, rhs1).withType(sym.symRef)
//todo: make sure dependent method types do not depend on implicits or by-name params
}
def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): TypeDef = {
val TypeDef(mods, name, rhs) = tdef
val mods1 = typedModifiers(mods)
val rhs1 = typedType(rhs)
cpy.TypeDef(tdef, mods1, name, rhs1).withType(sym.symRef)
}
def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = {
val TypeDef(mods, name, impl @ Template(constr, parents, self, body)) = cdef
val mods1 = typedModifiers(mods)
val constr1 = typed(constr).asInstanceOf[DefDef]
val parents1 = parents mapconserve (typed(_))
val self1 = typed(self).asInstanceOf[ValDef]
val localDummy = ctx.newLocalDummy(cls, impl.pos)
val body1 = typedStats(body, localDummy)(inClassContext(self1.symbol))
val impl1 = cpy.Template(impl, constr1, parents1, self1, body1)
.withType(localDummy.symRef)
cpy.TypeDef(cdef, mods1, name, impl1).withType(cls.symRef)
// todo later: check that
// 1. If class is non-abstract, it is instantiatable:
// - self type is s supertype of own type
// - all type members have consistent bounds
// 2. all private type members have consistent bounds
// 3. Types do not override classes.
// 4. Polymorphic type defs override nothing.
}
def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = {
val expr1 = typedExpr(imp.expr, AnySelectionProto)
cpy.Import(imp, expr1, imp.selectors).withType(sym.symRef)
}
def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = {
val annot1 = typed(tree.annot, defn.AnnotationClass.typeConstructor)
val arg1 = typed(tree.arg, pt)
val ownType = AnnotatedType(Annotation(annot1), arg1.tpe)
if (ctx.mode is Mode.Type)
cpy.Annotated(tree, annot1, arg1) withType ownType
else
cpy.Typed(tree, arg1, TypeTree(ownType)) withType ownType
}
def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = {
val pid1 = typedExpr(tree.pid, AnySelectionProto)
val pkg = pid1.symbol
val packageContext =
if (pkg is Package) ctx.fresh withOwner pkg.moduleClass
else {
ctx.error(i"$pkg is not a packge", tree.pos)
ctx
}
val stats1 = typedStats(tree.stats, NoSymbol)(packageContext)
cpy.PackageDef(tree, pid1.asInstanceOf[RefTree], stats1) withType pkg.symRef
}
def typedUnadapted(initTree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = {
val xtree = expanded(initTree)
typedTree remove xtree match {
case Some(ttree) => ttree
case none =>
val sym = symOfTree.remove(xtree).getOrElse(NoSymbol)
sym.ensureCompleted()
def localContext = ctx.fresh.withOwner(sym)
def typedNamed(tree: untpd.NameTree): Tree = tree match {
case tree: untpd.Ident => typedIdent(tree, pt)
case tree: untpd.Select => typedSelect(tree, pt)
case tree: untpd.SelectFromTypeTree => typedSelectFromTypeTree(tree, pt)
case tree: untpd.Bind => typedBind(tree, pt)
case tree: untpd.ValDef =>
if (tree.isEmpty) tpd.EmptyValDef
else typedValDef(tree, sym)(localContext)
case tree: untpd.DefDef =>
val typer1 = nestedTyper.remove(sym).get
typer1.typedDefDef(tree, sym)(localContext.withTyper(typer1))
case tree: untpd.TypeDef =>
if (tree.isClassDef) typedClassDef(tree, sym.asClass)(localContext)
else typedTypeDef(tree, sym)(localContext.withNewScope)
case _ => typedUnadapted(desugar(tree), pt)
}
def typedUnnamed(tree: untpd.Tree): Tree = tree match {
case tree: untpd.Apply => typedApply(tree, pt)
case tree: untpd.This => typedThis(tree)
case tree: untpd.Literal => typedLiteral(tree)
case tree: untpd.New => typedNew(tree, pt)
case tree: untpd.Pair => typedPair(tree, pt)
case tree: untpd.Typed => typedTyped(tree, pt)
case tree: untpd.NamedArg => typedNamedArg(tree, pt)
case tree: untpd.Assign => typedAssign(tree, pt)
case tree: untpd.Block => typedBlock(tree, pt)
case tree: untpd.If => typedIf(tree, pt)
case tree: untpd.Function => typedFunction(tree, pt)
case tree: untpd.Closure => typedClosure(tree, pt)
case tree: untpd.Match => typedMatch(tree, pt)
case tree: untpd.Return => typedReturn(tree)
case tree: untpd.Try => typedTry(tree, pt)
case tree: untpd.Throw => typedThrow(tree)
case tree: untpd.TypeApply => typedTypeApply(tree, pt)
case tree: untpd.Super => typedSuper(tree)
case tree: untpd.SeqLiteral => typedSeqLiteral(tree, pt)
case tree: untpd.TypeTree => typedTypeTree(tree, pt)
case tree: untpd.SingletonTypeTree => typedSingletonTypeTree(tree)
case tree: untpd.AndTypeTree => typedAndTypeTree(tree)
case tree: untpd.OrTypeTree => typedOrTypeTree(tree)
case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree)
case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree)
case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree)
case tree: untpd.Alternative => typedAlternative(tree, pt)
case tree: untpd.Import => typedImport(tree, sym)
case tree: untpd.PackageDef => typedPackageDef(tree)
case tree: untpd.Annotated => typedAnnotated(tree, pt)
case tree: untpd.TypedSplice => tree.tree
case untpd.PostfixOp(tree, nme.WILDCARD) => typed(tree, AnyFunctionProto)
case untpd.EmptyTree => tpd.EmptyTree
case _ => typedUnadapted(desugar(tree), pt)
}
xtree match {
case xtree: untpd.NameTree => typedNamed(xtree withName xtree.name.encode)
case xtree => typedUnnamed(xtree)
}
}
}
def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = ctx.traceIndented (i"typing $tree", show = true) {
val tree1 = typedUnadapted(tree, pt)
ctx.interpolateUndetVars(tree1.tpe.widen, tree1.pos)
adapt(tree1, pt)
}
def typedTrees(trees: List[untpd.Tree])(implicit ctx: Context): List[Tree] =
trees mapconserve (typed(_))
def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[tpd.Tree] = {
val buf = new mutable.ListBuffer[Tree]
@tailrec def traverse(stats: List[untpd.Tree])(implicit ctx: Context): List[Tree] = stats match {
case (imp: untpd.Import) :: rest =>
val imp1 = typed(imp)
buf += imp1
traverse(rest)(importContext(imp1.symbol, imp.selectors))
case (mdef: untpd.DefTree) :: rest =>
expandedTree remove mdef match {
case Some(xtree) =>
traverse(xtree :: rest)
case none =>
buf += typed(mdef)
traverse(rest)
}
case Thicket(stats) :: rest =>
traverse(stats ++ rest)
case stat :: rest =>
val nestedCtx = if (exprOwner == ctx.owner) ctx else ctx.fresh.withOwner(exprOwner)
buf += typed(stat)(nestedCtx)
traverse(rest)
case _ =>
buf.toList
}
traverse(stats)
}
def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
typed(tree, pt)(ctx retractMode Mode.PatternOrType)
def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
typed(tree, pt)(ctx addMode Mode.Type)
def typedPattern(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
typed(tree, pt)(ctx addMode Mode.Pattern)
def tryEither[T](op: Context => T)(fallBack: StateFul[T] => T)(implicit ctx: Context) = {
val nestedCtx = ctx.fresh.withNewTyperState
val result = op(nestedCtx)
if (nestedCtx.reporter.hasErrors)
fallBack(StateFul(result, nestedCtx.typerState))
else {
nestedCtx.typerState.commit()
result
}
}
def tryInsertApply(tree: Tree, pt: Type)(fallBack: StateFul[Tree] => Tree)(implicit ctx: Context): Tree =
tryEither {
implicit ctx => typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
} {
fallBack
}
/** (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
* (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode)
*/
/** Perform the following adaptations of expression, pattern or type `tree` wrt to
* given prototype `pt`:
* (1) Resolve overloading
* (2) Apply parameterless functions
* (3) Apply polymorphic types to fresh instances of their type parameters and
* store these instances in context.undetparams,
* unless followed by explicit type application.
* (4) Do the following to unapplied methods used as values:
* (4.1) If the method has only implicit parameters pass implicit arguments
* (4.2) otherwise, if `pt` is a function type and method is not a constructor,
* convert to function by eta-expansion,
* (4.3) otherwise, if the method is nullary with a result type compatible to `pt`
* and it is not a constructor, apply it to ()
* otherwise issue an error
* (5) Convert constructors in a pattern as follows:
* (5.1) If constructor refers to a case class factory, set tree's type to the unique
* instance of its primary constructor that is a subtype of the expected type.
* (5.2) If constructor refers to an extractor, convert to application of
* unapply or unapplySeq method.
*
* (6) Convert all other types to TypeTree nodes.
* (7) When in TYPEmode but not FUNmode or HKmode, check that types are fully parameterized
* (7.1) In HKmode, higher-kinded types are allowed, but they must have the expected kind-arity
* (8) When in both EXPRmode and FUNmode, add apply method calls to values of object type.
* (9) If there are undetermined type variables and not POLYmode, infer expression instance
* Then, if tree's type is not a subtype of expected type, try the following adaptations:
* (10) If the expected type is Byte, Short or Char, and the expression
* is an integer fitting in the range of that type, convert it to that type.
* (11) Widen numeric literals to their expected type, if necessary
* (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit.
* (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated.
* (14) When in mode EXPRmode, apply a view
* If all this fails, error
*/
def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", show = false) {
assert(pt.exists)
def methodStr = err.refStr(methPart(tree).tpe)
def adaptOverloaded(ref: TermRef) = {
val altDenots = ref.denot.alternatives
val alts = altDenots map (alt =>
TermRef.withSym(ref.prefix, alt.symbol.asTerm))
def expectedStr = err.expectedTypeStr(pt)
resolveOverloaded(alts, pt) match {
case alt :: Nil =>
adapt(tree.withType(alt), pt)
case Nil =>
def noMatches =
errorTree(tree,
i"""none of the ${err.overloadedAltsStr(altDenots)}
|match $expectedStr""".stripMargin)
pt match {
case pt: FunProto => tryInsertApply(tree, pt)(_ => noMatches)
case _ => noMatches
}
case alts =>
errorTree(tree,
i"""Ambiguous overload. The ${err.overloadedAltsStr(altDenots take 2)}
|both match $expectedStr""".stripMargin)
}
}
def adaptToArgs(wtp: Type, pt: FunProto) = wtp match {
case _: MethodType => tree
case _ => tryInsertApply(tree, pt) {
val more = tree match {
case Apply(_, _) => " more"
case _ => ""
}
_ => errorTree(tree, i"$methodStr does not take$more parameters")
}
}
def adaptNoArgs(wtp: Type) = wtp match {
case wtp: ExprType =>
adapt(tree.withType(wtp.resultType), pt)
case wtp: ImplicitMethodType =>
val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) =>
val arg = inferImplicit(formal, EmptyTree, tree.pos.endPos)
if (arg.isEmpty)
ctx.error(i"no implicit argument of type $formal found for parameter $pname of $methodStr", tree.pos.endPos)
arg
}
adapt(tpd.Apply(tree, args), pt)
case wtp: MethodType =>
if ((defn.isFunctionType(pt) || (pt eq AnyFunctionProto)) &&
!tree.symbol.isConstructor)
etaExpand(tree, wtp)
else if (wtp.paramTypes.isEmpty)
adapt(tpd.Apply(tree, Nil), pt)
else
errorTree(tree,
i"""missing arguments for $methodStr
|follow this method with `_' if you want to treat it as a partially applied function""".stripMargin)
case _ =>
if (tree.tpe <:< pt) tree
else if (ctx.mode is Mode.Pattern) tree // no subtype check for patterns
else if (ctx.mode is Mode.Type) err.typeMismatch(tree, pt)
else adaptToSubType(wtp)
}
def adaptToSubType(wtp: Type): Tree = {
// try converting a constant to the target type
val folded = ConstFold(tree, pt)
if (folded ne EmptyTree) return folded
// drop type if prototype is Unit
if (pt.typeSymbol == defn.UnitClass)
return tpd.Block(tree :: Nil, Literal(Constant()))
// convert function literal to SAM closure
tree match {
case Closure(Nil, id @ Ident(nme.ANON_FUN), _)
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
pt match {
case SAMType(meth)
if wtp <:< meth.info.toFunctionType && isFullyDefined(pt, forceIt = false) =>
return cpy.Closure(tree, Nil, id, TypeTree(pt)).withType(pt)
case _ =>
}
case _ =>
}
// try an implicit conversion
val adapted = inferView(tree, pt)
if (adapted ne EmptyTree) return adapted
// if everything fails issue a type error
err.typeMismatch(tree, pt)
}
tree match {
case _: MemberDef | _: PackageDef | _: Import | _: WithoutType[_] => tree
case _ => tree.tpe.widen match {
case ref: TermRef =>
adaptOverloaded(ref)
case poly: PolyType =>
if (pt.isInstanceOf[PolyProto]) tree
else {
val tracked = ctx.track(poly)
val tvars = ctx.newTypeVars(tracked, tree.pos)
adapt(tpd.TypeApply(tree, tvars map (tpd.TypeTree(_))), pt)
}
case wtp =>
pt match {
case pt: FunProto => adaptToArgs(wtp, pt)
case _ => adaptNoArgs(wtp)
}
}
}
}
}