diff options
25 files changed, 1509 insertions, 588 deletions
diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala b/src/dotty/tools/dotc/ast/CheckTrees.scala index 4c3413ada..4790f231b 100644 --- a/src/dotty/tools/dotc/ast/CheckTrees.scala +++ b/src/dotty/tools/dotc/ast/CheckTrees.scala @@ -36,7 +36,7 @@ object CheckTrees { case NamedArg(argName, _) => check(argName == name) case SeqLiteral(_, _) => - check(defn.RepeatedParamClasses contains formal.typeSymbol) + check(formal.isRepeatedParam) case _ => check(arg.isValue) } @@ -187,7 +187,7 @@ object CheckTrees { case nme.unapplySeq => // args need to be wrapped in (...: _*) check(args.length == 1) - check(args.head.tpe.typeSymbol == defn.RepeatedParamClass) + check(args.head.tpe.isRepeatedParam) case nme.unapply => val rtp = funtpe.resultType val rsym = rtp.dealias.typeSymbol diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index faa015793..ae1a06a74 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -34,7 +34,7 @@ object desugar { } } - def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false): DefDef = { + def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false): Tree = { val DefDef(mods, name, tparams, vparamss, tpt, rhs) = meth val epbuf = new ListBuffer[ValDef] val tparams1 = tparams mapConserve { @@ -49,7 +49,8 @@ object desugar { case tparam => tparam } - epbuf.toList match { + + val meth1 = epbuf.toList match { case Nil => meth case evidenceParams => @@ -61,6 +62,42 @@ object desugar { } meth.derivedDefDef(mods, name, tparams1, vparamss1, tpt, rhs) } + + def take(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match { + case vparams :: vparamss1 => + val len = vparams.length + if (len <= n) vparams :: take(vparamss1, n - len) else Nil + case _ => + Nil + } + + def defaultGetters(vparamss: List[List[ValDef]], n: Int = 0): List[DefDef] = vparamss match { + case (vparam :: vparams) :: vparamss1 => + def defaultGetter: DefDef = + DefDef( + mods = vparam.mods & AccessFlags, + name = meth.name.defaultGetterName(n + 1), + tparams = meth.tparams, + vparamss = take(meth.vparamss, n), + tpt = TypeTree(), + rhs = vparam.rhs) + val rest = defaultGetters(vparams :: vparamss1, n + 1) + if (vparam.rhs.isEmpty) rest else defaultGetter :: rest + case Nil :: vparamss1 => + defaultGetters(vparamss1) + case nil => + Nil + } + + val defGetters = defaultGetters(vparamss) + if (defGetters.isEmpty) meth1 + else { + val mods1 = meth1.mods | DefaultParameterized + val vparamss1 = vparamss map (_ map (vparam => + vparam.derivedValDef(vparam.mods, vparam.name, vparam.tpt, EmptyTree))) + val meth2 = meth1.derivedDefDef(mods1, meth1.name, meth1.tparams, vparamss1, meth1.tpt, meth1.rhs) + Thicket(meth2 :: defGetters) + } } def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = { @@ -81,7 +118,10 @@ object desugar { val TypeDef( mods, name, impl @ Template(constr0, parents, self, body)) = cdef - val constr1 = defDef(constr0, isPrimaryConstructor = true) + val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match { + case meth: DefDef => (meth, Nil) + case Thicket((meth: DefDef) :: defaults) => (meth, defaults) + } val tparams = constr1.tparams.map(tparam => tparam.derivedTypeDef( Modifiers(Param), tparam.name, tparam.rhs, tparam.tparams)) @@ -129,10 +169,18 @@ object desugar { } else Nil - val caseCompanions = + def anyRef = ref(defn.AnyRefAlias.typeConstructor) + + def companionDefs(parent: Tree, defs: List[Tree]) = + moduleDef( + ModuleDef( + Modifiers(Synthetic), name.toTermName, + Template(emptyConstructor, parent :: Nil, EmptyValDef(), defs))).toList + + val companions = if (mods is Case) { val parent = - if (tparams.nonEmpty) ref(defn.AnyRefAlias.typeConstructor) + if (tparams.nonEmpty) anyRef else (vparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe)) val applyMeths = if (mods is Abstract) Nil @@ -141,12 +189,10 @@ object desugar { val unapplyParam = makeSyntheticParameter(tpt = classTypeRef) DefDef(synthetic, nme.unapply, tparams, (unapplyParam :: Nil) :: Nil, EmptyTree, This(EmptyTypeName)) } - moduleDef( - ModuleDef( - Modifiers(Synthetic), name.toTermName, - Template(emptyConstructor, parent :: Nil, EmptyValDef(), applyMeths ::: unapplyMeth :: Nil)) - ).toList + companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters) } + else if (defaultGetters.nonEmpty) + companionDefs(anyRef, defaultGetters) else Nil val implicitWrappers = @@ -163,7 +209,7 @@ object desugar { val cdef1 = cdef.derivedTypeDef(mods, name, impl.derivedTemplate(constr, parents, self, constr1.tparams ::: constr1.vparamss.flatten ::: body ::: caseClassMeths)) - Thicket.make(cdef1 :: caseCompanions ::: implicitWrappers) + Thicket.make(cdef1 :: companions ::: implicitWrappers) } /** Expand to: diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 7e9c404eb..a3af19263 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -94,7 +94,7 @@ abstract class TreeInfo { def recur(params: List[Symbol], args: List[Tree[T]]): Boolean = params match { case Nil => args.isEmpty case param :: params1 => - if (defn.RepeatedParamClasses contains param.info.typeSymbol) { + if (param.info.isRepeatedParam) { for (arg <- args) f(param, arg) true } else args match { @@ -128,10 +128,19 @@ abstract class TreeInfo { case Apply(fn, _) => methPart(fn) case TypeApply(fn, _) => methPart(fn) case AppliedTypeTree(fn, _) => methPart(fn) - case Block(stats, expr) if stats forall (_.isInstanceOf[ValDef[_]]) => methPart(expr) + case Block(stats, expr) => methPart(expr) case _ => tree } + /** The number of arguments in an application */ + def numArgs[T >: Untyped](tree: Tree[T]): 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 + case Block(stats, expr) => numArgs(expr) + case _ => 0 + } + /** Is symbol potentially a getter of a mutable variable? */ def mayBeVarGetter(sym: Symbol)(implicit ctx: Context): Boolean = { @@ -237,7 +246,7 @@ abstract class TreeInfo { /** Is tpt a vararg type of the form T* ? */ def isRepeatedParamType(tpt: Tree[_ >: Untyped])(implicit ctx: Context) = tpt match { - case tpt: TypeTree[_] => defn.RepeatedParamClasses contains tpt.typeOpt.typeSymbol + 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 diff --git a/src/dotty/tools/dotc/ast/TypedTrees.scala b/src/dotty/tools/dotc/ast/TypedTrees.scala index 5e4f7342d..cce905d92 100644 --- a/src/dotty/tools/dotc/ast/TypedTrees.scala +++ b/src/dotty/tools/dotc/ast/TypedTrees.scala @@ -87,6 +87,9 @@ object tpd extends Trees.Instance[Type] { blk.withType(widen(expr.tpe)) } + def maybeBlock(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree = + if (stats.isEmpty) expr else Block(stats, expr) + def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = Trees.If(cond, thenp, elsep).withType(thenp.tpe | elsep.tpe).checked diff --git a/src/dotty/tools/dotc/core/Constraints.scala b/src/dotty/tools/dotc/core/Constraints.scala new file mode 100644 index 000000000..b997ca42a --- /dev/null +++ b/src/dotty/tools/dotc/core/Constraints.scala @@ -0,0 +1,89 @@ +package dotty.tools +package dotc +package core + +import Types._, Contexts._ +import util.SimpleMap + +/** Constraint over undetermined type parameters + * @param map a map from PolyType to the type bounds that constrain the + * polytype's type parameters. A type parameter that does not + * have a constraint is represented by a `NoType` in the corresponding + * array entry. + */ +class Constraint(val map: SimpleMap[PolyType, Array[Type]]) extends AnyVal { + + /** Does the constraint's domain contain the type parameters of `pt`? */ + def contains(pt: PolyType): Boolean = map(pt) != null + + /** The constraint for given type parameter `param`, or NoType if `param` is not part of + * the constraint domain. + */ + def apply(param: PolyParam): Type = { + val entries = map(param.binder) + if (entries == null) NoType else entries(param.paramNum) + } + + /** The constraint for the type parameters of `pt`. + * @pre The polytype's type parameters are contained in the constraint's domain. + */ + def apply(pt: PolyType): Array[Type] = map(pt) + + /** A new constraint which is derived from this constraint by adding or replacing + * the entries corresponding to `pt` with `entries`. + */ + def updated(pt: PolyType, entries: Array[Type]) = + new Constraint(map.updated(pt, entries)) + + /** A new constraint which is derived from this constraint by removing + * the type parameter `param` from the domain. + */ + def - (param: PolyParam) = { + val pt = param.binder + val pnum = param.paramNum + val entries = map(pt) + var noneLeft = true + var i = 0 + while (noneLeft && (i < entries.length)) { + noneLeft = (entries(i) eq NoType) || i == pnum + i += 1 + } + new Constraint( + if (noneLeft) map remove pt + else { + val newEntries = entries.clone + newEntries(pnum) = NoType + map.updated(pt, newEntries) + }) + } + + def +(pt: PolyType) = + new Constraint(map.updated(pt, pt.paramBounds.toArray)) + + /** A new constraint which is derived from this constraint by removing + * the type parameter `param` from the domain and replacing all occurrences + * of the parameter elsewhere in the constraint by type `tp`. + */ + def replace(param: PolyParam, tp: Type)(implicit ctx: Context) = { + def subst(entries: Array[Type]) = { + var result = entries + var i = 0 + while (i < entries.length) { + entries(i) match { + case oldBounds: TypeBounds => + val newBounds = oldBounds.substParam(param, tp) + if (oldBounds ne newBounds) { + + if (result eq entries) result = entries.clone + result(i) = newBounds + } + case _ => + } + i += 1 + } + result + } + + new Constraint((this - param).map mapValues subst) + } +} diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index e2273738f..b1523144d 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -9,8 +9,11 @@ import Phases._ import Types._ import Symbols._ import Scopes._ -import TypeComparers._, NameOps._, SymDenotations._, util.Positions._ -import ast.Trees._, ast.untpd +import NameOps._ +import SymDenotations._ +import util.Positions._ +import ast.Trees._ +import ast.untpd import util.{FreshNameCreator, SimpleMap} import typer._ import config.Settings._ @@ -83,9 +86,9 @@ object Contexts { } /** The current type comparer */ - private[this] var _typeComparer: TypeComparer = _ - protected def typeComparer_=(typeComparer: TypeComparer) = _typeComparer = typeComparer - def typeComparer: TypeComparer = _typeComparer + private[this] var _typerState: TyperState = _ + protected def typerState_=(typerState: TyperState) = _typerState = typerState + def typerState: TyperState = _typerState /** The current position */ private[this] var _position: Position = _ @@ -149,6 +152,12 @@ object Contexts { protected def moreProperties_=(moreProperties: Map[String, Any]) = _moreProperties = moreProperties def moreProperties: Map[String, Any] = _moreProperties + private var _typeComparer: TypeComparer = _ + def typeComparer: TypeComparer = { + if (_typeComparer == null || (_typeComparer.ctx ne this)) + _typeComparer = new TypeComparer()(this) + _typeComparer + } /** If -Ydebug is on, the top of the stack trace where this context * was created, otherwise `null`. @@ -208,7 +217,7 @@ object Contexts { if (_condensed eq outer.condensed) _condensed = base.initialCtx.fresh .withPeriod(period) - // typeComparer and its constraints is not preserved in condensed + // typerState and its constraint is not preserved in condensed .withPlainPrinter(plainPrinter) .withRefinedPrinter(refinedPrinter) .withOwner(owner) @@ -242,7 +251,8 @@ object Contexts { */ abstract class FreshContext extends CondensedContext { def withPeriod(period: Period): this.type = { this.period = period; this } - def withTypeComparer(typeComparer: TypeComparer): this.type = { this.typeComparer = typeComparer; this } + def withTyperState(typerState: TyperState): this.type = { this.typerState = typerState; this } + def withNewTyperState: this.type = withTyperState(typerState.fresh) def withPosition(position: Position): this.type = { this.position = position; this } def withPlainPrinter(printer: Context => Printer): this.type = { this.plainPrinter = printer; this } def withRefinedPrinter(printer: Context => Printer): this.type = { this.refinedPrinter = printer; this } @@ -273,7 +283,7 @@ object Contexts { private class InitialContext(val base: ContextBase, settings: SettingGroup) extends FreshContext { outer = NoContext period = InitialPeriod - typeComparer = new TypeComparer + typerState = new TyperState position = NoPosition plainPrinter = new PlainPrinter(_) refinedPrinter = new RefinedPrinter(_) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 9d7f84449..66ed61a7f 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -263,8 +263,18 @@ class Definitions(implicit ctx: Context) { // - .linkedClass: the ClassSymbol of the enumeration (class E) sym.owner.linkedClass.typeConstructor - def FunctionType(args: List[Type], resultType: Type) = - FunctionClass(args.length).typeConstructor.appliedTo(args :+ resultType) + object FunctionType { + def apply(args: List[Type], resultType: Type) = + FunctionClass(args.length).typeConstructor.appliedTo(args :+ resultType) + def unapply(ft: Type) = { + val tsym = ft.typeSymbol + lazy val targs = ft.typeArgs + if ((FunctionClasses contains tsym) && + (targs.length - 1 <= MaxFunctionArity) && + (FunctionClass(targs.length - 1) == tsym)) Some(targs.init, targs.last) + else None + } + } // ----- Symbol sets --------------------------------------------------- diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 87fc33a6c..cb010ed8e 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -449,6 +449,7 @@ object Denotations { * is still a member of its enclosing package, then the whole flock * is brought forward to be valid in the new runId. Otherwise * the symbol is stale, which constitutes an internal error. + * TODO: Ensure that a subclass is renewed whenever one of its parents is. */ def current(implicit ctx: Context): SingleDenotation = { val currentPeriod = ctx.period @@ -529,6 +530,8 @@ object Denotations { final def toDenot(pre: Type)(implicit ctx: Context) = this final def containsSig(sig: Signature)(implicit ctx: Context) = exists && signature == sig + final def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation = + if (p(this)) this else NoDenotation final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation = if (denots.containsSig(signature)) NoDenotation else this def disjointAsSeenFrom(denots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation = @@ -598,6 +601,8 @@ object Denotations { /** Group contains a denotation with given signature */ def containsSig(sig: Signature)(implicit ctx: Context): Boolean + def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation + /** Keep only those denotations in this group which have a signature * that's not already defined by `denots`. */ @@ -627,6 +632,8 @@ object Denotations { def toDenot(pre: Type)(implicit ctx: Context) = (denots1 toDenot pre) & (denots2 toDenot pre, pre) def containsSig(sig: Signature)(implicit ctx: Context) = (denots1 containsSig sig) || (denots2 containsSig sig) + def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation = + derivedUnion(denots1 filterWithPredicate p, denots2 filterWithPredicate p) def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation = derivedUnion(denots1 filterDisjoint denots, denots2 filterDisjoint denots) def disjointAsSeenFrom(denots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation = diff --git a/src/dotty/tools/dotc/core/Diagnostic.scala b/src/dotty/tools/dotc/core/Diagnostic.scala new file mode 100644 index 000000000..e1b7ee848 --- /dev/null +++ b/src/dotty/tools/dotc/core/Diagnostic.scala @@ -0,0 +1,10 @@ +package dotty.tools +package dotc +package core + +import util.SourcePosition +import reporting.Reporter + +class Diagnostic(msgFn: => String, val pos: SourcePosition, severity: Reporter.Severity.Value) { + lazy val msg: String = msgFn +} diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 9f4cce728..82fd86c05 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -291,8 +291,8 @@ object Flags { /** A super accessor */ final val SuperAccessor = termFlag(27, "<superaccessor>") - /** A parameter with a default value */ - final val DefaultParam = termFlag(28, "<defaultparam>") + /** A method that has default params */ // TODO: drop + final val DefaultParameterized = termFlag(28, "<defaultparam>") /** Symbol is initialized to the default value, e.g. var x: T = _ */ final val DefaultInit = termFlag(29, "<defaultinit>") @@ -360,6 +360,12 @@ object Flags { /** A definition that's initialized before the super call (Scala 2.x only) */ final val Scala2PreSuper = termFlag(58, "<presuper>") + /** A method that is known to have inherited default parameters */ + final val InheritedDefaultParams = termFlag(59, "<inherited-default-param>") + + /** A method that is known to no default parameters */ + final val NoDefaultParams = termFlag(60, "<no-default-param>") + // --------- Combined Flag Sets and Conjunctions ---------------------- /** Flags representing source modifiers */ @@ -437,6 +443,12 @@ object Flags { /** A parameter or parameter accessor */ final val ParamOrAccessor = Param | Accessor + /** Has defined or inherited default parameters */ + final val HasDefaultParams = DefaultParameterized | InheritedDefaultParams + + /** Is a default parameter in Scala 2*/ + final val DefaultParameter = allOf(Param, DefaultParameterized) + /** A Java interface */ final val JavaInterface = allOf(JavaDefined, Trait) diff --git a/src/dotty/tools/dotc/core/Substituters.scala b/src/dotty/tools/dotc/core/Substituters.scala index 7a7481293..26095450e 100644 --- a/src/dotty/tools/dotc/core/Substituters.scala +++ b/src/dotty/tools/dotc/core/Substituters.scala @@ -7,22 +7,6 @@ import Types._, Symbols._, Contexts._ */ trait Substituters { this: Context => - final def subst(tp: Type, from: BoundType, to: Type, map: SubstBoundMap): Type = - tp match { - case tp: BoundType => - if (tp == from) to else tp - case tp: NamedType => - if (tp.symbol.isStatic) tp - else tp.derivedNamedType(subst(tp.prefix, from, to, map)) - case _: ThisType | NoPrefix => - tp - case tp: RefinedType => - tp.derivedRefinedType(subst(tp.parent, from, to, map), tp.refinedName, subst(tp.refinedInfo, from, to, map)) - case _ => - (if (map != null) map else new SubstBoundMap(from, to)) - .mapOver(tp) - } - final def subst(tp: Type, from: BindingType, to: BindingType, map: SubstBindingMap): Type = tp match { case tp: BoundType => @@ -151,15 +135,43 @@ trait Substituters { this: Context => .mapOver(tp) } + final def substParam(tp: Type, from: ParamType, to: Type, map: SubstParamMap): Type = + tp match { + case tp: BoundType => + if (tp == from) to else tp + case tp: NamedType => + if (tp.symbol.isStatic) tp + else tp.derivedNamedType(substParam(tp.prefix, from, to, map)) + case _: ThisType | NoPrefix => + tp + case tp: RefinedType => + tp.derivedRefinedType(substParam(tp.parent, from, to, map), tp.refinedName, substParam(tp.refinedInfo, from, to, map)) + case _ => + (if (map != null) map else new SubstParamMap(from, to)) + .mapOver(tp) + } + + final def substParams(tp: Type, from: BindingType, to: List[Type], map: SubstParamsMap): Type = + tp match { + case tp: ParamType => + if (tp.binder == from) to(tp.paramNum) else tp + case tp: NamedType => + if (tp.symbol.isStatic) tp + else tp.derivedNamedType(substParams(tp.prefix, from, to, map)) + case _: ThisType | NoPrefix | _: RefinedThis => + tp + case tp: RefinedType => + tp.derivedRefinedType(substParams(tp.parent, from, to, map), tp.refinedName, substParams(tp.refinedInfo, from, to, map)) + case _ => + (if (map != null) map else new SubstParamsMap(from, to)) + .mapOver(tp) + } + private def existsStatic(syms: List[Symbol]): Boolean = syms match { case sym :: syms1 => sym.isStatic || existsStatic(syms1) case nil => false } - final class SubstBoundMap(from: BoundType, to: Type) extends TypeMap { - def apply(tp: Type) = subst(tp, from, to, this) - } - final class SubstBindingMap(from: BindingType, to: BindingType) extends TypeMap { def apply(tp: Type) = subst(tp, from, to, this) } @@ -187,4 +199,12 @@ trait Substituters { this: Context => final class SubstRefinedThisMap(from: RefinedType, to: Type) extends TypeMap { def apply(tp: Type): Type = substThis(tp, from, to, this) } + + final class SubstParamMap(from: ParamType, to: Type) extends TypeMap { + def apply(tp: Type) = substParam(tp, from, to, this) + } + + final class SubstParamsMap(from: BindingType, to: List[Type]) extends TypeMap { + def apply(tp: Type) = substParams(tp, from, to, this) + } }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 63c148e20..e3c63b6a5 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -30,6 +30,10 @@ trait SymDenotations { this: Context => result.validFor = stablePeriod result } + + def lookup(name: Name): PreDenotation = + if (owner.isClass && (owner ne outer.owner)) owner.asClass.membersNamed(name) + else scope.denotsNamed(name) } object SymDenotations { @@ -38,7 +42,7 @@ object SymDenotations { */ class SymDenotation private[SymDenotations] ( final val symbol: Symbol, - _owner: Symbol, + ownerIfExists: Symbol, final val name: Name, initFlags: FlagSet, initInfo: Type, @@ -48,54 +52,54 @@ object SymDenotations { // ------ Getting and setting fields ----------------------------- - private[this] var _flags: FlagSet = adaptFlags(initFlags) - private[this] var _info: Type = initInfo - private[this] var _privateWithin: Symbol = initPrivateWithin - private[this] var _annotations: List[Annotation] = Nil + private[this] var myFlags: FlagSet = adaptFlags(initFlags) + private[this] var myInfo: Type = initInfo + private[this] var myPrivateWithin: Symbol = initPrivateWithin + private[this] var myAnnotations: List[Annotation] = Nil /** The owner of the symbol */ - def owner: Symbol = _owner + def owner: Symbol = ownerIfExists /** The flag set */ - final def flags: FlagSet = { ensureCompleted(); _flags } + final def flags: FlagSet = { ensureCompleted(); myFlags } - final def flagsUNSAFE = _flags // !!! DEBUG; drop when no longer needed + final def flagsUNSAFE = myFlags // !!! DEBUG; drop when no longer needed /** Adapt flag set to this denotation's term or type nature */ def adaptFlags(flags: FlagSet) = if (isType) flags.toTypeFlags else flags.toTermFlags /** Update the flag set */ private final def flags_=(flags: FlagSet): Unit = - _flags = adaptFlags(flags) + myFlags = adaptFlags(flags) /** Set given flags(s) of this denotation */ - final def setFlag(flags: FlagSet): Unit = { _flags |= flags } + final def setFlag(flags: FlagSet): Unit = { myFlags |= flags } /** UnsSet given flags(s) of this denotation */ - final def resetFlag(flags: FlagSet): Unit = { _flags &~= flags } + final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags } final def is(fs: FlagSet) = { - (if (fs <= FromStartFlags) _flags else flags) is fs + (if (fs <= FromStartFlags) myFlags else flags) is fs } final def is(fs: FlagSet, butNot: FlagSet) = - (if (fs <= FromStartFlags && butNot <= FromStartFlags) _flags else flags) is (fs, butNot) + (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot) final def is(fs: FlagConjunction) = - (if (fs <= FromStartFlags) _flags else flags) is fs + (if (fs <= FromStartFlags) myFlags else flags) is fs final def is(fs: FlagConjunction, butNot: FlagSet) = - (if (fs <= FromStartFlags && butNot <= FromStartFlags) _flags else flags) is (fs, butNot) + (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot) /** The type info. * The info is an instance of TypeType iff this is a type denotation - * Uncompleted denotations set _info to a LazyType. + * Uncompleted denotations set myInfo to a LazyType. */ - final def info: Type = _info match { - case _info: LazyType => completeFrom(_info); info - case _ => _info + final def info: Type = myInfo match { + case myInfo: LazyType => completeFrom(myInfo); info + case _ => myInfo } private def completeFrom(completer: LazyType): Unit = { - if (_flags is Touched) throw new CyclicReference(this) - _flags |= Touched + if (myFlags is Touched) throw new CyclicReference(this) + myFlags |= Touched Context.theBase.initialCtx.debugTraceIndented(s"completing ${this.debugString}") { completer.complete(this) @@ -109,36 +113,36 @@ object SymDenotations { assert(ost.isInstanceOf[TermRef], tp) case _ => } - _info = tp + myInfo = tp } /** The denotation is completed: all attributes are fully defined */ - final def isCompleted: Boolean = ! _info.isInstanceOf[LazyType] + final def isCompleted: Boolean = !myInfo.isInstanceOf[LazyType] - final def isCompleting: Boolean = (_flags is Touched) && !isCompleted + final def isCompleting: Boolean = (myFlags is Touched) && !isCompleted /** The completer of this denotation. @pre: Denotation is not yet completed */ - final def completer: LazyType = _info.asInstanceOf[LazyType] + final def completer: LazyType = myInfo.asInstanceOf[LazyType] /** Make sure this denotation is completed */ final def ensureCompleted(): Unit = info /** The privateWithin boundary, NoSymbol if no boundary is given. */ - final def privateWithin: Symbol = { ensureCompleted(); _privateWithin } + final def privateWithin: Symbol = { ensureCompleted(); myPrivateWithin } /** Set privateWithin. */ protected[core] final def privateWithin_=(sym: Symbol): Unit = - _privateWithin = sym + myPrivateWithin = sym /** The annotations of this denotation */ final def annotations: List[Annotation] = { - ensureCompleted(); _annotations + ensureCompleted(); myAnnotations } /** Update the annotations of this denotation */ private[core] final def annotations_=(annots: List[Annotation]): Unit = - _annotations = annots + myAnnotations = annots /** Does this denotation have an annotation matching the given class symbol? */ final def hasAnnotation(cls: Symbol)(implicit ctx: Context) = @@ -146,7 +150,7 @@ object SymDenotations { /** Add given annotation to the annotations of this denotation */ final def addAnnotation(annot: Annotation): Unit = - annotations = annot :: _annotations + annotations = annot :: myAnnotations @tailrec private def dropOtherAnnotations(anns: List[Annotation], cls: Symbol)(implicit ctx: Context): List[Annotation] = anns match { @@ -156,7 +160,7 @@ object SymDenotations { /** The symbols defined in this class. */ - final def decls(implicit ctx: Context): Scope = _info match { + final def decls(implicit ctx: Context): Scope = myInfo match { case cinfo: ClassCompleterWithDecls => cinfo.decls case cinfo: LazyType => completeFrom(cinfo); decls // complete-once case _ => info.decls @@ -206,11 +210,11 @@ object SymDenotations { /** Make denotation not exist */ final def markAbsent(): Unit = - _info = NoType + myInfo = NoType /** Is symbol known to not exist? */ final def isAbsent: Boolean = - _info == NoType + myInfo == NoType /** Is this symbol the root class or its companion object? */ final def isRoot: Boolean = name.toTermName == nme.ROOT @@ -416,6 +420,17 @@ object SymDenotations { def isAsConcrete(that: Symbol)(implicit ctx: Context): Boolean = !(this is Deferred) || (that is Deferred) + /** Does this symbol have defined or inherited default parameters? */ + def hasDefaultParams(implicit ctx: Context): Boolean = + (this is HasDefaultParams) || + !(this is NoDefaultParams) && computeDefaultParams + + private def computeDefaultParams(implicit ctx: Context) = { + val result = allOverriddenSymbols exists (_.hasDefaultParams) + setFlag(if (result) InheritedDefaultParams else NoDefaultParams) + result + } + // def isOverridable: Boolean = !!! need to enforce that classes cannot be redefined // def isSkolem: Boolean = ??? @@ -432,14 +447,14 @@ object SymDenotations { * the completers. */ /** The class implementing this module, NoSymbol if not applicable. */ - final def moduleClass: Symbol = _info match { + final def moduleClass: Symbol = myInfo match { case info: TypeRefBySym if this is ModuleVal => info.fixedSym case info: ModuleCompleter => info.mclass case _ => NoSymbol } /** The module implemented by this module class, NoSymbol if not applicable. */ - final def sourceModule: Symbol = _info match { + final def sourceModule: Symbol = myInfo match { case ClassInfo(_, _, _, _, selfType: TermRefBySym) if this is ModuleClass => selfType.fixedSym case info: ClassCompleterWithDecls => diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 9e68aab6d..f45e3e140 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -224,7 +224,7 @@ trait Symbols { this: Context => owner: Symbol, names: List[TypeName], flags: FlagSet, - boundsFn: List[TypeRef] => List[Type]) = { + boundsFn: List[TypeRef] => List[Type]): List[TypeSymbol] = { val tparams = names map (_ => newNakedSymbol[TypeName](NoCoord)) val bounds = boundsFn(tparams map (_.symTypeRef)) (names, tparams, bounds).zipped foreach { (name, tparam, bound) => diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala new file mode 100644 index 000000000..479793326 --- /dev/null +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -0,0 +1,336 @@ +package dotty.tools +package dotc +package core + +import Types._, Contexts._, Symbols._, Flags._ +import collection.mutable +import util.SimpleMap + +/** Provides methods to compare types. + * @param constraint The initial constraint which is assumed to hold for the comparisons. + * The constraint set is updated when undetermined type parameters + * in the constraint's domain are compared. + */ +class TypeComparer(implicit val ctx: Context) extends DotClass { + + val state = ctx.typerState + import state.constraint + + private var pendingSubTypes: mutable.Set[(Type, Type)] = null + private var recCount = 0 + + /** Add the constraint `<bounds.lo <: param <: bounds.hi>` + * to `constraint`. + * @pre `param` is in the constraint's domain + */ + def addConstraint(param: PolyParam, bounds: TypeBounds): Boolean = { + val pt = param.binder + val pnum = param.paramNum + val oldEntries = constraint(pt) + val oldBounds = oldEntries(pnum).asInstanceOf[TypeBounds] + val newBounds = oldBounds & bounds + if (oldBounds ne newBounds) { + val newEntries = oldEntries.clone + newEntries(pnum) = newBounds + constraint = constraint.updated(pt, newEntries) + } + isSubType(newBounds.lo, newBounds.hi) + } + + /** Solve constraint for given type parameter `param`. + * If `fromBelow` is true the parameter is approximated by its lower bound, + * otherwise it is approximated by its upper bound. However, any occurrences + * of the parameter in a refinement somewhere in the bound are removed. + * (Such occurrences can arise for F-bounded types). + * The type parameter is removed from the constraint's domain and all its + * occurrences are replaced by its approximation. + * @return the instantiating type + * @pre `param` is associated with type bounds in the current constraint. + */ + def approximate(param: PolyParam, fromBelow: Boolean): Type = { + val removeParam = new TypeMap { + override def apply(tp: Type) = mapOver { + tp match { + case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent + case _ => tp + } + } + } + val bounds = constraint(param).asInstanceOf[TypeBounds] + val bound = if (fromBelow) bounds.lo else bounds.hi + val inst = removeParam(bound) + constraint = constraint.replace(param, inst) + inst + } + + def isSubType(tp1: Type, tp2: Type): Boolean = + if (tp1 == NoType || tp2 == NoType) false + else if (tp1 eq tp2) true + else { + val cs = constraint + try { + recCount += 1 + val result = + if (recCount < LogPendingSubTypesThreshold) firstTry(tp1, tp2) + else monitoredIsSubType(tp1, tp2) + recCount -= 1 + if (!result) constraint = cs + result + } catch { + case ex: Throwable => + recCount -= 1 + constraint = cs + throw ex + } + } + + def monitoredIsSubType(tp1: Type, tp2: Type) = { + if (pendingSubTypes == null) { + pendingSubTypes = new mutable.HashSet[(Type, Type)] + ctx.log(s"!!! deep subtype recursion involving $tp1 <:< $tp2") + } + val p = (tp1, tp2) + !pendingSubTypes(p) && { + try { + pendingSubTypes += p + firstTry(tp1, tp2) + } finally { + pendingSubTypes -= p + } + } + } + + def firstTry(tp1: Type, tp2: Type): Boolean = ctx.debugTraceIndented(s"$tp1 <:< $tp2") { + tp2 match { + case tp2: NamedType => + tp1 match { + case tp1: NamedType => + val sym1 = tp1.symbol + val sym2 = tp2.symbol + val pre1 = tp1.prefix + val pre2 = tp2.prefix + if (sym1 == sym2) ( + ctx.erasedTypes + || sym1.isStaticOwner + || isSubType(pre1, pre2)) + else ( + tp1.name == tp2.name && isSubType(pre1, pre2) + || sym2.isClass && { + val base = tp1.baseType(sym2) + (base ne tp1) && isSubType(base, tp2) + } + || thirdTryNamed(tp1, tp2)) + case _ => + secondTry(tp1, tp2) + } + case WildcardType | ErrorType => + true + case tp2: TypeVar => + firstTry(tp1, tp2.thisInstance) + case tp2: PolyParam => + constraint(tp2) match { + case TypeBounds(lo, _) => isSubType(tp1, lo) || addConstraint(tp2, TypeBounds.lower(tp1)) + case _ => secondTry(tp1, tp2) + } + case _ => + secondTry(tp1, tp2) + } + } + + def secondTry(tp1: Type, tp2: Type): Boolean = tp1 match { + case WildcardType | ErrorType => + true + case tp1: TypeVar => + secondTry(tp1.thisInstance, tp2) + case tp1: PolyParam => + constraint(tp1) match { + case TypeBounds(_, hi) => isSubType(hi, tp2) || addConstraint(tp1, TypeBounds.upper(tp2)) + case _ => thirdTry(tp1, tp2) + } + case _ => + thirdTry(tp1, tp2) + } + + def thirdTryNamed(tp1: Type, tp2: NamedType): Boolean = tp2.info match { + case TypeBounds(lo, _) => + isSubType(tp1, lo) + case _ => + val cls2 = tp2.symbol + (cls2 == defn.SingletonClass && tp1.isStable + || cls2 == defn.NotNullClass && tp1.isNotNull + || (defn.hkTraits contains cls2) && isSubTypeHK(tp1, tp2) + || fourthTry(tp1, tp2)) + } + + def thirdTry(tp1: Type, tp2: Type): Boolean = tp2 match { + case tp2: NamedType => + thirdTryNamed(tp1, tp2) + case tp2: RefinedType => + isSubType(tp1, tp2.parent) && + isSubType(tp1.member(tp2.refinedName).info, tp2.refinedInfo) + case AndType(tp21, tp22) => + isSubType(tp1, tp21) && isSubType(tp1, tp22) + case OrType(tp21, tp22) => + isSubType(tp1, tp21) || isSubType(tp1, tp22) + case tp2 @ MethodType(_, formals1) => + tp1 match { + case tp1 @ MethodType(_, formals2) => + tp1.signature == tp2.signature && + matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && + tp1.isImplicit == tp2.isImplicit && // needed? + isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) + case _ => + false + } + case tp2: PolyType => + tp1 match { + case tp1: PolyType => + tp1.signature == tp2.signature && + (tp1.paramBounds corresponds tp2.paramBounds)((b1, b2) => + isSameType(b1, b2.subst(tp2, tp1))) && + isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) + case _ => + false + } + case tp2 @ ExprType(restpe1) => + tp1 match { + case tp1 @ ExprType(restpe2) => + isSubType(restpe1, restpe2) + case _ => + false + } + case TypeBounds(lo2, hi2) => + tp1 match { + case TypeBounds(lo1, hi1) => + isSubType(lo2, lo1) && isSubType(hi1, hi2) + case tp1: ClassInfo => + val tt = tp1.typeConstructor // was typeTemplate + isSubType(lo2, tt) && isSubType(tt, hi2) + case _ => + false + } + /* needed? + case ClassInfo(pre2, denot2) => + tp1 match { + case ClassInfo(pre1, denot1) => + (denot1 eq denot2) && isSubType(pre2, pre1) // !!! or isSameType? + } +*/ + case _ => + fourthTry(tp1, tp2) + } + + def fourthTry(tp1: Type, tp2: Type): Boolean = tp1 match { + case tp1: TypeRef => + ((tp1 eq defn.NothingType) + || (tp1 eq defn.NullType) && tp2.dealias.typeSymbol.isNonValueClass + || !tp1.symbol.isClass && isSubType(tp1.info.bounds.hi, tp2)) + case tp1: SingletonType => + isSubType(tp1.underlying, tp2) + case tp1: RefinedType => + isSubType(tp1.parent, tp2) + case AndType(tp11, tp12) => + isSubType(tp11, tp2) || isSubType(tp12, tp2) + case OrType(tp11, tp12) => + isSubType(tp11, tp2) && isSubType(tp12, tp2) + case _ => + false + } + /* not needed + def isSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[TypeSymbol]): Boolean = tparams match { + case tparam :: tparams1 => + val variance = tparam.variance + val t1 = tps1.head + val t2 = tps2.head + (variance > 0 || isSubType(t2, t1)) && + (variance < 0 || isSubType(t1, t2)) && + isSubArgs(tps1.tail, tps2.tail, tparams1) + case _ => + assert(tps1.isEmpty && tps2.isEmpty) + true + } +*/ + /** Is `tp1` a subtype of a type `tp2` of the form + * `scala.HigerKindedXYZ { ... }? + * This is the case if `tp1` and `tp2` have the same number + * of type parameters, the bounds of tp1's paremeters + * are contained in the corresponding bounds of tp2's parameters + * and the variances of correesponding parameters agree. + */ + def isSubTypeHK(tp1: Type, tp2: Type): Boolean = { + val tparams = tp1.typeParams + val hkArgs = tp2.typeArgs + (hkArgs.length == tparams.length) && { + val base = ctx.newSkolemSingleton(tp1) + (tparams, hkArgs).zipped.forall { (tparam, hkArg) => + base.memberInfo(tparam) <:< hkArg.bounds // TODO: base.memberInfo needed? + } && + (tparams, tp2.typeSymbol.typeParams).zipped.forall { (tparam, tparam2) => + tparam.variance == tparam2.variance + } + } + } + + /** A function implementing `tp1` matches `tp2`. */ + final def matchesType(tp1: Type, tp2: Type, alwaysMatchSimple: Boolean): Boolean = tp1 match { + case tp1: MethodType => + tp2 match { + case tp2: MethodType => + tp1.isImplicit == tp2.isImplicit && + matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && + matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), alwaysMatchSimple) + case tp2: ExprType => + tp1.paramNames.isEmpty && + matchesType(tp1.resultType, tp2.resultType, alwaysMatchSimple) + case _ => + false + } + case tp1: ExprType => + tp2 match { + case tp2: MethodType => + tp2.paramNames.isEmpty && + matchesType(tp1.resultType, tp2.resultType, alwaysMatchSimple) + case tp2: ExprType => + matchesType(tp1.resultType, tp2.resultType, alwaysMatchSimple) + case _ => + false // was: matchesType(tp1.resultType, tp2, alwaysMatchSimple) + } + case tp1: PolyType => + tp2 match { + case tp2: PolyType => + sameLength(tp1.paramNames, tp2.paramNames) && + matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), alwaysMatchSimple) + case _ => + false + } + case _ => + tp2 match { + case _: MethodType | _: PolyType => + false + case tp2: ExprType => + false // was: matchesType(tp1, tp2.resultType, alwaysMatchSimple) + case _ => + alwaysMatchSimple || isSameType(tp1, tp2) + } + } + + /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ + private def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match { + case formal1 :: rest1 => + formals2 match { + case formal2 :: rest2 => + (isSameType(formal1, formal2) + || isJava1 && formal2 == defn.ObjectType && formal1 == defn.AnyType + || isJava2 && formal1 == defn.ObjectType && formal2 == defn.AnyType) && matchingParams(rest1, rest2, isJava1, isJava2) + case nil => + false + } + case nil => + formals2.isEmpty + } + + def isSameType(tp1: Type, tp2: Type): Boolean = + if (tp1 == NoType || tp2 == NoType) false + else if (tp1 eq tp2) true + else isSubType(tp1, tp2) && isSubType(tp2, tp1) +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/TypeComparers.scala b/src/dotty/tools/dotc/core/TypeComparers.scala deleted file mode 100644 index 7afeb6388..000000000 --- a/src/dotty/tools/dotc/core/TypeComparers.scala +++ /dev/null @@ -1,439 +0,0 @@ -package dotty.tools -package dotc -package core - -import Types._, Contexts._, Symbols._, Flags._ -import collection.mutable -import util.SimpleMap - -object TypeComparers { - - /** Constraints over undetermined type parameters - * @param map a map from PolyType to the type bounds that constrain the - * polytype's type parameters. A type parameter that does not - * have a constraint is represented by a `NoType` in the corresponding - * array entry. - */ - class Constraints(val map: SimpleMap[PolyType, Array[Type]]) extends AnyVal { - - /** Does the constraint's domain contain the type parameters of `pt`? */ - def contains(pt: PolyType): Boolean = map(pt) != null - - /** The constraints for given type parameter `param`, or NoType if `param` is not part of - * the constraint domain. - */ - def apply(param: PolyParam): Type = { - val entries = map(param.binder) - if (entries == null) NoType else entries(param.paramNum) - } - - /** The constraints for the type parameters of `pt`. - * @pre The polytype's type parameters are contained in the constraint's domain. - */ - def apply(pt: PolyType): Array[Type] = map(pt) - - /** A new constraint which is derived from this constraint by adding or replacing - * the entries corresponding to `pt` with `entries`. - */ - def updated(pt: PolyType, entries: Array[Type]) = - new Constraints(map.updated(pt, entries)) - - /** A new constraint which is derived from this constraint by removing - * the type parameter `param` from the domain. - */ - def - (param: PolyParam) = { - val pt = param.binder - val pnum = param.paramNum - val entries = map(pt) - var noneLeft = true - var i = 0 - while (noneLeft && (i < entries.length)) { - noneLeft = (entries(i) eq NoType) || i == pnum - i += 1 - } - new Constraints( - if (noneLeft) map remove pt - else { - val newEntries = entries.clone - newEntries(pnum) = NoType - map.updated(pt, newEntries) - }) - } - - def + (pt: PolyType) = - new Constraints(map.updated(pt, pt.paramBounds.toArray)) - - /** A new constraint which is derived from this constraint by removing - * the type parameter `param` from the domain and replacing all occurrences - * of the parameter elsewhere in the constraint by type `tpe`. - */ - def replace(param: PolyParam, tpe: Type)(implicit ctx: Context) = { - def subst(entries: Array[Type]) = { - var result = entries - var i = 0 - while (i < entries.length) { - entries(i) match { - case oldBounds: TypeBounds => - val newBounds = oldBounds.subst(param, tpe) - if (oldBounds ne newBounds) { - if (result eq entries) result = entries.clone - result(i) = newBounds - } - case _ => - } - i += 1 - } - result - } - new Constraints((this - param).map mapValues subst) - } - } - - /** Provides methods to compare types. - * @param constraints The initial constraint which is assumed to hold for the comparisons. - * The constraint set is updated when undetermined type parameters - * in the constraint's domain are compared. - */ - class TypeComparer(initConstraints: Constraints = new Constraints(SimpleMap.Empty)) - (implicit val ctx: Context) extends DotClass { - - private var constrs = initConstraints - - final def constraints = constrs - private def constraints_=(c: Constraints) = constrs = c - - private var pendingSubTypes: mutable.Set[(Type, Type)] = null - private var recCount = 0 - - /** Add the constraint `<bounds.lo <: param <: bounds.hi>` - * to `constraints`. - * @pre `param` is in the constraint's domain - */ - def addConstraint(param: PolyParam, bounds: TypeBounds): Boolean = { - val pt = param.binder - val pnum = param.paramNum - val oldEntries = constraints(pt) - val oldBounds = oldEntries(pnum).asInstanceOf[TypeBounds] - val newBounds = oldBounds & bounds - if (oldBounds ne newBounds) { - val newEntries = oldEntries.clone - newEntries(pnum) = newBounds - constraints.updated(pt, newEntries) - } - isSubType(newBounds.lo, newBounds.hi) - } - - /** Add all parameters in given polytype `pt` to the constraint's domain. - * If the constraint contains already some of these parameters in its domain, - * make a copy of the polytype and add the copy's type parameters instead. - * Return either the original polytype, or the copy, if one was made. - */ - def track(pt: PolyType)(implicit ctx: Context): PolyType = { - val tracked = - if (constraints contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType) - else pt - constraints = constraints + tracked - tracked - } - - /** Solve constraints for given type parameter `param`. - * If `fromBelow` is true the parameter is approximated by its lower bound, - * otherwise it is approximated by its upper bound. However, any occurrences - * of the parameter in a refinement somewhere in the bound are removed. - * (Such occurrences can arise for F-bounded types). - * The type parameter is removed from the constraint's domain and all its - * occurrences are replaced by its approximation. - * @return the instantiating type - * @pre `param` is associated with type bounds in the current constraint. - */ - def approximate(param: PolyParam, fromBelow: Boolean): Type = { - val removeParam = new TypeMap { - override def apply(tp: Type) = mapOver { - tp match { - case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent - case _ => tp - } - } - } - val bounds = constraints(param).asInstanceOf[TypeBounds] - val bound = if (fromBelow) bounds.lo else bounds.hi - val inst = removeParam(bound) - constraints = constraints.replace(param, inst) - inst - } - - def isSubType(tp1: Type, tp2: Type): Boolean = - if (tp1 == NoType || tp2 == NoType) false - else if (tp1 eq tp2) true - else { - val cs = constraints - try { - recCount += 1 - val result = - if (recCount < LogPendingSubTypesThreshold) firstTry(tp1, tp2) - else monitoredIsSubType(tp1, tp2) - recCount -= 1 - if (!result) constraints = cs - result - } catch { - case ex: Throwable => - recCount -= 1 - constraints = cs - throw ex - } - } - - def monitoredIsSubType(tp1: Type, tp2: Type) = { - if (pendingSubTypes == null) { - pendingSubTypes = new mutable.HashSet[(Type, Type)] - ctx.log(s"!!! deep subtype recursion involving $tp1 <:< $tp2") - } - val p = (tp1, tp2) - !pendingSubTypes(p) && { - try { - pendingSubTypes += p - firstTry(tp1, tp2) - } finally { - pendingSubTypes -= p - } - } - } - - def firstTry(tp1: Type, tp2: Type): Boolean = ctx.debugTraceIndented(s"$tp1 <:< $tp2") { - tp2 match { - case tp2: NamedType => - tp1 match { - case tp1: NamedType => - val sym1 = tp1.symbol - val sym2 = tp2.symbol - val pre1 = tp1.prefix - val pre2 = tp2.prefix - if (sym1 == sym2) ( - ctx.erasedTypes - || sym1.isStaticOwner - || isSubType(pre1, pre2)) - else ( - tp1.name == tp2.name && isSubType(pre1, pre2) - || sym2.isClass && { - val base = tp1.baseType(sym2) - (base ne tp1) && isSubType(base, tp2) - } - || thirdTryNamed(tp1, tp2)) - case _ => - secondTry(tp1, tp2) - } - case WildcardType | ErrorType => - true - case tp2: TypeVar => - firstTry(tp1, tp2.thisInstance) - case tp2: PolyParam => - constraints(tp2) match { - case TypeBounds(lo, _) => isSubType(tp1, lo) || addConstraint(tp2, TypeBounds.lower(tp1)) - case _ => secondTry(tp1, tp2) - } - case _ => - secondTry(tp1, tp2) - } - } - - def secondTry(tp1: Type, tp2: Type): Boolean = tp1 match { - case WildcardType | ErrorType => - true - case tp1: TypeVar => - secondTry(tp1.thisInstance, tp2) - case tp1: PolyParam => - constraints(tp1) match { - case TypeBounds(_, hi) => isSubType(hi, tp2) || addConstraint(tp1, TypeBounds.upper(tp2)) - case _ => thirdTry(tp1, tp2) - } - case _ => - thirdTry(tp1, tp2) - } - - def thirdTryNamed(tp1: Type, tp2: NamedType): Boolean = tp2.info match { - case TypeBounds(lo, _) => - isSubType(tp1, lo) - case _ => - val cls2 = tp2.symbol - ( cls2 == defn.SingletonClass && tp1.isStable - || cls2 == defn.NotNullClass && tp1.isNotNull - || (defn.hkTraits contains cls2) && isSubTypeHK(tp1, tp2) - || fourthTry(tp1, tp2) - ) - } - - def thirdTry(tp1: Type, tp2: Type): Boolean = tp2 match { - case tp2: NamedType => - thirdTryNamed(tp1, tp2) - case tp2: RefinedType => - isSubType(tp1, tp2.parent) && - isSubType(tp1.member(tp2.refinedName).info, tp2.refinedInfo) - case AndType(tp21, tp22) => - isSubType(tp1, tp21) && isSubType(tp1, tp22) - case OrType(tp21, tp22) => - isSubType(tp1, tp21) || isSubType(tp1, tp22) - case tp2 @ MethodType(_, formals1) => - tp1 match { - case tp1 @ MethodType(_, formals2) => - tp1.signature == tp2.signature && - matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit && // needed? - isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) - case _ => - false - } - case tp2: PolyType => - tp1 match { - case tp1: PolyType => - tp1.signature == tp2.signature && - (tp1.paramBounds corresponds tp2.paramBounds)((b1, b2) => - isSameType(b1, b2.subst(tp2, tp1))) && - isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) - case _ => - false - } - case tp2 @ ExprType(restpe1) => - tp1 match { - case tp1 @ ExprType(restpe2) => - isSubType(restpe1, restpe2) - case _ => - false - } - case TypeBounds(lo2, hi2) => - tp1 match { - case TypeBounds(lo1, hi1) => - isSubType(lo2, lo1) && isSubType(hi1, hi2) - case tp1: ClassInfo => - val tt = tp1.typeConstructor // was typeTemplate - isSubType(lo2, tt) && isSubType(tt, hi2) - case _ => - false - } -/* needed? - case ClassInfo(pre2, denot2) => - tp1 match { - case ClassInfo(pre1, denot1) => - (denot1 eq denot2) && isSubType(pre2, pre1) // !!! or isSameType? - } -*/ - case _ => - fourthTry(tp1, tp2) - } - - def fourthTry(tp1: Type, tp2: Type): Boolean = tp1 match { - case tp1: TypeRef => - ( (tp1 eq defn.NothingType) - || (tp1 eq defn.NullType) && tp2.dealias.typeSymbol.isNonValueClass - || !tp1.symbol.isClass && isSubType(tp1.info.bounds.hi, tp2) - ) - case tp1: SingletonType => - isSubType(tp1.underlying, tp2) - case tp1: RefinedType => - isSubType(tp1.parent, tp2) - case AndType(tp11, tp12) => - isSubType(tp11, tp2) || isSubType(tp12, tp2) - case OrType(tp11, tp12) => - isSubType(tp11, tp2) && isSubType(tp12, tp2) - case _ => - false - } -/* not needed - def isSubArgs(tps1: List[Type], tps2: List[Type], tparams: List[TypeSymbol]): Boolean = tparams match { - case tparam :: tparams1 => - val variance = tparam.variance - val t1 = tps1.head - val t2 = tps2.head - (variance > 0 || isSubType(t2, t1)) && - (variance < 0 || isSubType(t1, t2)) && - isSubArgs(tps1.tail, tps2.tail, tparams1) - case _ => - assert(tps1.isEmpty && tps2.isEmpty) - true - } -*/ - /** Is `tp1` a subtype of a type `tp2` of the form - * `scala.HigerKindedXYZ { ... }? - * This is the case if `tp1` and `tp2` have the same number - * of type parameters, the bounds of tp1's paremeters - * are contained in the corresponding bounds of tp2's parameters - * and the variances of correesponding parameters agree. - */ - def isSubTypeHK(tp1: Type, tp2: Type): Boolean = { - val tparams = tp1.typeParams - val hkArgs = tp2.typeArgs - (hkArgs.length == tparams.length) && { - val base = ctx.newSkolemSingleton(tp1) - (tparams, hkArgs).zipped.forall { (tparam, hkArg) => - base.memberInfo(tparam) <:< hkArg.bounds // TODO: base.memberInfo needed? - } && - (tparams, tp2.typeSymbol.typeParams).zipped.forall { (tparam, tparam2) => - tparam.variance == tparam2.variance - } - } - } - - /** A function implementing `tp1` matches `tp2`. */ - final def matchesType(tp1: Type, tp2: Type, alwaysMatchSimple: Boolean): Boolean = tp1 match { - case tp1: MethodType => - tp2 match { - case tp2: MethodType => - tp1.isImplicit == tp2.isImplicit && - matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), alwaysMatchSimple) - case tp2: ExprType => - tp1.paramNames.isEmpty && - matchesType(tp1.resultType, tp2.resultType, alwaysMatchSimple) - case _ => - false - } - case tp1: ExprType => - tp2 match { - case tp2: MethodType => - tp2.paramNames.isEmpty && - matchesType(tp1.resultType, tp2.resultType, alwaysMatchSimple) - case tp2: ExprType => - matchesType(tp1.resultType, tp2.resultType, alwaysMatchSimple) - case _ => - false // was: matchesType(tp1.resultType, tp2, alwaysMatchSimple) - } - case tp1: PolyType => - tp2 match { - case tp2: PolyType => - sameLength(tp1.paramNames, tp2.paramNames) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), alwaysMatchSimple) - case _ => - false - } - case _ => - tp2 match { - case _: MethodType | _: PolyType => - false - case tp2: ExprType => - false // was: matchesType(tp1, tp2.resultType, alwaysMatchSimple) - case _ => - alwaysMatchSimple || isSameType(tp1, tp2) - } - } - - /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ - private def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match { - case formal1 :: rest1 => - formals2 match { - case formal2 :: rest2 => - ( isSameType(formal1, formal2) - || isJava1 && formal2 == defn.ObjectType && formal1 == defn.AnyType - || isJava2 && formal1 == defn.ObjectType && formal2 == defn.AnyType - ) && matchingParams(rest1, rest2, isJava1, isJava2) - case nil => - false - } - case nil => - formals2.isEmpty - } - - def isSameType(tp1: Type, tp2: Type): Boolean = - if (tp1 == NoType || tp2 == NoType) false - else if (tp1 eq tp2) true - else isSubType(tp1, tp2) && isSubType(tp2, tp1) - } -}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala new file mode 100644 index 000000000..e90070253 --- /dev/null +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -0,0 +1,43 @@ +package dotty.tools +package dotc +package core + +import Types._ +import Flags._ +import Contexts._ +import util.SimpleMap + +class TyperState extends DotClass { + + /** The current constraint set */ + def constraint: Constraint = new Constraint(SimpleMap.Empty) + + /** The currently uninstantiated TypeVars */ + def undetVars: List[TypeVar] = Nil + + /** The currently outstanding errors, warnings, or infos */ + def diagnostics: List[Diagnostic] = Nil + + def constraint_=(c: Constraint): Unit = {} + def undetVars_=(vs: List[TypeVar]): Unit = unsupported("undetVars_=") + def diagnostics_=(ds: List[Diagnostic]): Unit = unsupported("diagnostics_=") + + def fresh: TyperState = this +} + +class MutableTyperState (previous: TyperState) extends TyperState { + + private var myConstraint: Constraint = previous.constraint + private var myUndetVars: List[TypeVar] = previous.undetVars + private var myDiagnostics: List[Diagnostic] = Nil + + override def constraint = myConstraint + override def undetVars = myUndetVars + override def diagnostics = myDiagnostics + + override def constraint_=(c: Constraint) = myConstraint = c + override def undetVars_=(vs: List[TypeVar]) = myUndetVars = vs + override def diagnostics_=(ds: List[Diagnostic]) = myDiagnostics = ds + + override def fresh: TyperState = new MutableTyperState(this) +} diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index ce889ecd2..924517e09 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -3,7 +3,6 @@ package core import util.HashSet import Symbols._ -import TypeComparers._ import Flags._ import Names._ import StdNames._, NameOps._ @@ -152,6 +151,9 @@ object Types { /** Does this type occur as a part of type `that`? */ final def occursIn(that: Type): Boolean = that.existsPart(this == _) + def isRepeatedParam(implicit ctx: Context): Boolean = + defn.RepeatedParamClasses contains typeSymbol + // ----- Higher-order combinators ----------------------------------- /** Returns true if there is a part of this type that satisfies predicate `p`. @@ -547,7 +549,13 @@ object Types { case pt: PolyType => pt.paramTypess case _ => Nil } - +/* Not sure whether we'll need this + final def firstParamTypes: List[Type] = this match { + case mt: MethodType => mt.paramTypes + case pt: PolyType => pt.firstParamTypes + case _ => Nil + } +*/ /** The resultType of a PolyType, MethodType, or ExprType, the type itself for others */ def resultType: Type = this @@ -616,10 +624,6 @@ object Types { } } - /** Substitute a bound type by some other type */ - final def subst(from: BoundType, to: Type)(implicit ctx: Context): Type = - ctx.subst(this, from, to, null) - /** Substitute all types of the form `PolyParam(from, N)` by * `PolyParam(to, N)`. */ @@ -634,6 +638,14 @@ object Types { final def substThis(rt: RefinedType, tp: Type)(implicit ctx: Context): Type = ctx.substThis(this, rt, tp, null) + /** Substitute a bound type by some other type */ + final def substParam(from: ParamType, to: Type)(implicit ctx: Context): Type = + ctx.substParam(this, from, to, null) + + /** Substitute bound types by some other types */ + final def substParams(from: BindingType, to: List[Type])(implicit ctx: Context): Type = + ctx.substParams(this, from, to, null) + /** Substitute all occurrences of symbols in `from` by references to corresponding symbols in `to` */ final def substSym(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): Type = @@ -1351,10 +1363,22 @@ object Types { else MethodType(paramNames, paramTypes)(restpeExpr) } - def instantiate(argTypes: List[Type])(implicit ctx: Context): Type = + def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = if (isDependent) new InstMethodMap(this, argTypes) apply resultType else resultType + /* probably won't be needed + private var _isVarArgs: Boolean = _ + private var knownVarArgs: Boolean = false + + def isVarArgs(implicit ctx: Context) = { + if (!knownVarArgs) { + _isVarArgs = paramTypes.nonEmpty && paramTypes.last.isRepeatedParam + knownVarArgs = true + } + _isVarArgs + } +*/ override def equals(that: Any) = that match { case that: MethodType => this.paramNames == that.paramNames && @@ -1482,7 +1506,11 @@ object Types { def copy(bt: BT): Type } - case class MethodParam(binder: MethodType, paramNum: Int) extends BoundType with SingletonType { + abstract class ParamType extends BoundType { + def paramNum: Int + } + + case class MethodParam(binder: MethodType, paramNum: Int) extends ParamType with SingletonType { type BT = MethodType override def underlying(implicit ctx: Context): Type = binder.paramTypes(paramNum) def copy(bt: BT) = MethodParam(bt, paramNum) @@ -1500,7 +1528,7 @@ object Types { override def toString = s"MethodParam(${binder.paramNames(paramNum)})" } - case class PolyParam(binder: PolyType, paramNum: Int) extends BoundType { + case class PolyParam(binder: PolyType, paramNum: Int) extends ParamType { type BT = PolyType def copy(bt: BT) = PolyParam(bt, paramNum) override def underlying(implicit ctx: Context): Type = binder.paramBounds(paramNum) diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala index e42ca58af..48aa9317e 100644 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -217,7 +217,7 @@ class PickleBuffer(data: Array[Byte], from: Int, to: Int) { STABLE -> Stable, STATIC -> Static, CASEACCESSOR -> CaseAccessor, - DEFAULTPARAM -> (DefaultParam, Trait), + DEFAULTPARAM -> (DefaultParameterized, Trait), BRIDGE -> Bridge, ACCESSOR -> Accessor, SUPERACCESSOR -> SuperAccessor, diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index ea42b40a4..24259211a 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -323,7 +323,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: val owner = if (atEnd) loadingMirror.RootClass else readSymbolRef() def adjust(denot: Denotation) = { - val denot1 = denot.disambiguate(p) + val denot1 = denot.disambiguate(d => p(d.symbol)) val sym = denot1.symbol if (denot.exists && !denot1.exists) { // !!!DEBUG val alts = denot.alternatives map (d => d+":"+d.info+"/"+d.signature) @@ -393,7 +393,15 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: val nameref = readNat() val name0 = at(nameref, readName) val owner = readSymbolRef() - val flags = unpickleScalaFlags(readLongNat(), name0.isTypeName) + + var flags = unpickleScalaFlags(readLongNat(), name0.isTypeName) + if (flags is DefaultParameter) { + // DefaultParameterized flag now on method, not parameter + //assert(flags is Param, s"$name0 in $owner") + flags = flags &~ DefaultParameterized + owner.setFlag(DefaultParameterized) + } + val name = name0.adjustIfModuleClass(flags) def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) && !(flags is ModuleClass) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index dc3956fca..01d267aec 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -79,7 +79,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tycon = tp.unrefine val cls = tycon.typeSymbol if (cls.typeParams.length == args.length) { - if (cls == defn.RepeatedParamClass) return toTextLocal(args.head) ~ "*" + if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" if (cls == defn.ByNameParamClass) return "=> " ~ toText(args.head) if (defn.FunctionClasses contains cls) return toTextFunction(args) if (defn.TupleClasses contains cls) return toTextTuple(args) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala new file mode 100644 index 000000000..9fdf5084c --- /dev/null +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -0,0 +1,645 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast.{Trees, untpd, tpd, TreeInfo} +import util.Positions._ +import Trees.Untyped +import Contexts._ +import Types._ +import Flags._ +import Denotations._ +import NameOps._ +import Symbols._ +import Types._ +import Decorators._ +import Names._ +import StdNames._ +import Constants._ +import Inferencing._ +import collection.mutable +import language.implicitConversions + +object Applications { + + private val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]] + def hasNamedArg(args: List[Any]) = args exists isNamedArg +} + +trait Applications { self: Typer => + + import Applications._ + import Trees._ + + private def state(implicit ctx: Context) = ctx.typerState + + def lift(defs: mutable.ListBuffer[tpd.Tree], expr: tpd.Tree, prefix: String = "")(implicit ctx: Context): tpd.Tree = + if (TreeInfo.isIdempotentExpr(expr)) expr + else { + val name = ctx.freshName(prefix).toTermName + val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, expr.tpe, coord = positionCoord(expr.pos)) + defs += tpd.ValDef(sym, expr) + tpd.Ident(sym.symRef) + } + + def liftArgs(defs: mutable.ListBuffer[tpd.Tree], methType: Type, args: List[tpd.Tree])(implicit ctx: Context) = { + val paramPrefixes = methType match { + case MethodType(paramNames, _) => paramNames map (_.toString) + case _ => args map (_ => "") + } + for ((arg, prefix) <- args zip paramPrefixes) yield lift(defs, arg, prefix) + } + + def liftApp(defs: mutable.ListBuffer[tpd.Tree], tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + case Apply(fn, args) => + tree.derivedApply(liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) + case TypeApply(fn, targs) => + tree.derivedTypeApply(liftApp(defs, fn), targs) + case Select(pre, name) => + tree.derivedSelect(lift(defs, pre), name) + case Ident(name) => + lift(defs, tree) + case Block(stats, expr) => + liftApp(defs ++= stats, expr) + case _ => + tree + } + + def isCompatible(tp: Type, pt: Type): Boolean = ??? + + /** + * @param Arg the type of arguments, could be tpd.Tree, untpd.Tree, or Type + * @param methRef the reference to the method of the application + * @param funType the type of the function part of the application + * @param args the arguments of the application + * @param resultType the expected result type of the application + */ + abstract class Application[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) { + + /** The type of typed arguments: either tpd.Tree or Type */ + type TypedArg + + /** Given an original argument and the type of the corresponding formal + * parameter, produce a typed argument. + */ + protected def typedArg(arg: Arg, formal: Type): TypedArg + + /** Turn a typed tree into an argument */ + protected def treeToArg(arg: tpd.Tree): Arg + + /** Check that argument corresponds to type `formal` and + * possibly add it to the list of adapted arguments + */ + protected def addArg(arg: TypedArg, formal: Type): Unit + + /** Is this an argument of the form `expr: _*` or a RepeatedParamType + * derived from such an argument? + */ + protected def isVarArg(arg: Arg): Boolean + + /** If constructing trees, turn last `n` processed arguments into a + * `SeqLiteral` tree with element type `elemFormal`. + */ + protected def makeVarArg(n: Int, elemFormal: Type): Unit + + /** Signal failure with given message at position of given argument */ + protected def fail(msg: => String, arg: Arg): Unit + + /** Signal failure with given message at position of the application itself */ + protected def fail(msg: => String): Unit + + /** If constructing trees, the current function part, which might be + * affected by lifting. EmptyTree otherwise. + */ + protected def normalizedFun: tpd.Tree + + /** If constructing trees, pull out all parts of the function + * which are not idempotent into separate prefix definitions + */ + protected def liftFun(): Unit = () + + /** A flag signalling that the application was so far succesful */ + protected var ok = true + + /** The function's type after widening and instantiating polytypes + * with polyparams or typevars in constraint set + */ + val methType = funType.widen match { + case funType: MethodType => funType + case funType: PolyType => polyResult(ctx.track(funType)) + case _ => funType + } + + /** The arguments re-ordered so that each named argument matches the + * same-named formal parameter. + */ + val orderedArgs = + if (hasNamedArg(args)) + reorder(args.asInstanceOf[List[untpd.Tree]]).asInstanceOf[List[Arg]] + else + args + + methType match { + case methType: MethodType => + // apply the result type constraint, unless method type is dependent + if (!methType.isDependent) + ok = ok && constrainResult(methType.resultType, resultType) + // match all arguments with corresponding formal parameters + matchArgs(orderedArgs, methType.paramTypes, 0) + case _ => + if (methType.isError) ok = false + else fail(s"$methString does not take parameters") + } + + /** The application was succesful */ + def success = ok + + private def state = ctx.typerState + + protected def methodType = methType.asInstanceOf[MethodType] + private def methString: String = s"method ${methRef.name}: ${methType.show}" + + /** The result type of a polytype; overridden in TypedApplication */ + protected def polyResult(polytpe: PolyType): Type = polytpe.resultType + + /** Re-order arguments to correctly align named arguments */ + def reorder[T >: Untyped](args: List[Tree[T]]): List[Tree[T]] = { + var namedToArg: Map[Name, Tree[T]] = + (for (NamedArg(name, arg1) <- args) yield (name, arg1)).toMap + + def badNamedArg(arg: Tree[_ >: Untyped]): Unit = { + val NamedArg(name, _) = arg + def msg = + if (methodType.paramNames contains name) + s"parameter $name of $methString is already instantiated" + else + s"$methString does not have a parameter $name" + fail(msg, arg.asInstanceOf[Arg]) + } + + def recur(pnames: List[Name], args: List[Tree[T]]): List[Tree[T]] = pnames match { + case pname :: pnames1 => + namedToArg get pname match { + case Some(arg) => + namedToArg -= pname + arg :: recur(pnames1, args) + case None => + args match { + case (arg @ NamedArg(aname, _)) :: args1 => + if (namedToArg contains aname) + emptyTree[T]() :: recur(pnames1, args) + else { + badNamedArg(arg) + recur(pnames1, args1) + } + case arg :: args1 => + arg :: recur(pnames1, args1) + case Nil => + recur(pnames1, args) + } + } + case nil => + if (hasNamedArg(args)) { + val (namedArgs, otherArgs) = args partition isNamedArg + namedArgs foreach badNamedArg + otherArgs + } + else args + } + + recur(methodType.paramNames, args) + } + + /** Splice new method reference into existing application */ + def spliceMeth(meth: tpd.Tree, app: tpd.Tree): tpd.Tree = app match { + case Apply(fn, args) => tpd.Apply(spliceMeth(meth, fn), args) + case TypeApply(fn, targs) => tpd.TypeApply(spliceMeth(meth, fn), targs) + case _ => meth + } + + /** Find reference to default parameter getter for parameter #n in current + * parameter list, or NoType if none was found + */ + def findDefaultGetter(n: Int)(implicit ctx: Context): Type = { + def getterName = methRef.name.toTermName.defaultGetterName(n) + def ref(pre: Type, sym: Symbol): Type = + if (pre.exists && sym.isTerm) TermRef.withSym(pre, sym.asTerm) else NoType + val meth = methRef.symbol + if (meth.hasDefaultParams) + methRef.prefix match { + case NoPrefix => + def findDefault(cx: Context): Type = { + if (cx eq NoContext) NoType + else if (cx.scope != cx.outer.scope && + cx.lookup(methRef.name) + .filterWithPredicate(_.symbol == meth).exists) { + val denot = cx.lookup(getterName).toDenot(NoPrefix) + NamedType(NoPrefix, getterName).withDenot(denot) + } else findDefault(cx.outer) + } + findDefault(ctx) + case mpre => + val cls = meth.owner + val pre = + if (meth.isClassConstructor) { + mpre.baseType(cls) match { + case TypeRef(clspre, _) => ref(clspre, cls.companionModule) + case _ => NoType + } + } else mpre + ref(pre, pre.member(getterName).symbol) + } + else NoType + } + + /** Match re-ordered arguments against formal parameters + * @param n The position of the first parameter in formals in `methType`. + */ + def matchArgs(args: List[Arg], formals: List[Type], n: Int): Unit = { + if (success) formals match { + case formal :: formals1 => + + def addTyped(arg: Arg, formal: Type) = + addArg(typedArg(arg, formal), formal) + + def missingArg(n: Int): Unit = { + val pname = methodType.paramNames(n) + fail( + if (pname contains '$') s"not enough arguments for $methString" + else s"missing argument for parameter $pname of $methString") + } + + def tryDefault(n: Int, args1: List[Arg]): Unit = { + findDefaultGetter(n + TreeInfo.numArgs(normalizedFun)) match { + case dref: NamedType => + liftFun() + addTyped(treeToArg(spliceMeth(tpd.Ident(dref), normalizedFun)), formal) + matchArgs(args1, formals1, n + 1) + case _ => + missingArg(n) + } + } + + if (formal.isRepeatedParam) + args match { + case arg :: Nil if isVarArg(arg) => + addTyped(arg, formal) + case _ => + val elemFormal = formal.typeArgs.head + args foreach (addTyped(_, elemFormal)) + makeVarArg(args.length, elemFormal) + } + else args match { + case EmptyTree :: args1 => + tryDefault(n, args1) + case arg :: args1 => + addTyped(arg, formal) + matchArgs(args1, formals1, n + 1) + case nil => + tryDefault(n, args) + } + + case nil => + args match { + case arg :: args1 => fail(s"too many arguments for $methString", arg) + case nil => + } + } + } + + /** Take into account that the result type of the current method + * must fit the given expected result type. + */ + def constrainResult(mt: Type, pt: Type): Boolean = pt match { + case FunProtoType(_, result) => + mt match { + case mt: MethodType if !mt.isDependent => + constrainResult(mt.resultType, pt.resultType) + case _ => + true + } + case pt: ValueType => + mt match { + case mt: ImplicitMethodType if !mt.isDependent => + constrainResult(mt.resultType, pt) + case _ => + isCompatible(mt, pt) + } + case _ => + true + } + } + + /** Subclass of Application for the cases where we are interested only + * in a "can/cannot apply" answer, without needing to construct trees or + * issue error messages. + */ + abstract class TestApplication[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) + extends Application[Arg](methRef, funType, args, resultType) { + type TypedArg = Arg + type Result = Unit + + /** The type of the given argument */ + protected def argType(arg: Arg): Type + + def typedArg(arg: Arg, formal: Type): Arg = arg + def addArg(arg: TypedArg, formal: Type) = + ok = ok & isCompatible(argType(arg), formal) + def makeVarArg(n: Int, elemFormal: Type) = {} + def fail(msg: => String, arg: Arg) = + ok = false + def fail(msg: => String) = + ok = false + def normalizedFun = tpd.EmptyTree + } + + /** Subtrait of Application for the cases where arguments are (typed or + * untyped) trees. + */ + trait TreeApplication[T >: Untyped] extends Application[Tree[T]] { + type TypeArg = tpd.Tree + def isVarArg(arg: Tree[T]): Boolean = TreeInfo.isWildcardStarArg(arg) + } + + /** Subclass of Application for applicability tests with trees as arguments. */ + class ApplicableToTrees(methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context) + extends TestApplication(methRef, methRef, args, resultType) with TreeApplication[Type] { + def argType(arg: tpd.Tree): Type = arg.tpe + def treeToArg(arg: tpd.Tree): tpd.Tree = arg + } + + /** Subclass of Application for applicability tests with types as arguments. */ + class ApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context) + extends TestApplication(methRef, methRef, args, resultType) { + def argType(arg: Type): Type = arg + def treeToArg(arg: tpd.Tree): Type = arg.tpe + def isVarArg(arg: Type): Boolean = arg.isRepeatedParam + } + + /** Subclass of Application for type checking an Apply node, where + * types of arguments are either known or unknown. + */ + abstract class TypedApply[T >: Untyped]( + app: untpd.Apply, fun: tpd.Tree, methRef: TermRef, args: List[Tree[T]], resultType: Type)(implicit ctx: Context) + extends Application(methRef, fun.tpe, args, resultType) with TreeApplication[T] { + type TypedArg = tpd.Tree + private var typedArgBuf = new mutable.ListBuffer[tpd.Tree] + private var liftedDefs: mutable.ListBuffer[tpd.Tree] = null + private var myNormalizedFun: tpd.Tree = fun + + def addArg(arg: tpd.Tree, formal: Type): Unit = + typedArgBuf += adapt(arg, Mode.Expr, formal) + + def makeVarArg(n: Int, elemFormal: Type): Unit = { + val args = typedArgBuf.takeRight(n).toList + typedArgBuf.trimEnd(n) + typedArgBuf += SeqLiteral(TypeTree(elemFormal), args) + } + + def fail(msg: => String, arg: Tree[T]) = { + ctx.error(msg, arg.pos) + ok = false + } + + def fail(msg: => String) = { + ctx.error(msg, app.pos) + ok = false + } + + def normalizedFun = myNormalizedFun + + override def liftFun(): Unit = + if (liftedDefs == null) { + liftedDefs = new mutable.ListBuffer[tpd.Tree] + myNormalizedFun = liftApp(liftedDefs, myNormalizedFun) + } + + /** Replace all parameters of tracked polytype by fresh type vars, + * and make function a TypeApply node with these type vars as arguments. + */ + override def polyResult(poly: PolyType): Type = { + val tvars = ctx.newTypeVars(poly) + myNormalizedFun = tpd.TypeApply(normalizedFun, tvars map (tpd.TypeTree(_))) + poly.substParams(poly, tvars) + } + + /** The index of the first difference between lists of trees `xs` and `ys`, + * where `EmptyTree`s in the second list are skipped. + * -1 if there are no differences. + */ + private def firstDiff[T <: Tree[_]](xs: List[T], ys: List[T], n: Int = 0): Int = xs match { + case x :: xs1 => + ys match { + case EmptyTree :: ys1 => firstDiff(xs1, ys1, n) + case y :: ys1 => if (x ne y) n else firstDiff(xs1, ys1, n + 1) + case nil => n + } + case nil => + ys match { + case EmptyTree :: ys1 => firstDiff(xs, ys1, n) + case y :: ys1 => n + case nil => -1 + } + } + def sameSeq[T <: Tree[_]](xs: List[T], ys: List[T]): Boolean = firstDiff(xs, ys) < 0 + + val result: tpd.Tree = + if (!success) app withType ErrorType + else { + var typedArgs = typedArgBuf.toList + if (!sameSeq(app.args, orderedArgs)) { + // need to lift arguments to maintain evaluation order in the + // presence of argument reorderings. + liftFun() + val eqSuffixLength = firstDiff(app.args.reverse, orderedArgs.reverse) + val (liftable, rest) = typedArgs splitAt (typedArgs.length - eqSuffixLength) + typedArgs = liftArgs(liftedDefs, methType, liftable) ++ rest + } + if (sameSeq(typedArgs, args)) // trick to cut down on tree copying + typedArgs = args.asInstanceOf[List[tpd.Tree]] + val app1 = app.withType(methodType.instantiate(typedArgs map (_.tpe))) + .derivedApply(normalizedFun, typedArgs) + if (liftedDefs != null && liftedDefs.nonEmpty) tpd.Block(liftedDefs.toList, app1) + else app1 + } + } + + /** Subclass of Application for type checking an Apply node with untyped arguments. */ + class ApplyToUntyped(app: untpd.Apply, fun: tpd.Tree, methRef: TermRef, args: List[untpd.Tree], resultType: Type)(implicit ctx: Context) + extends TypedApply(app, fun, methRef, args, resultType) { + def typedArg(arg: untpd.Tree, formal: Type): TypedArg = typed(arg, Mode.Expr, formal) + def treeToArg(arg: tpd.Tree): untpd.Tree = untpd.TypedSplice(arg) + } + + /** Subclass of Application for type checking an Apply node with typed arguments. */ + class ApplyToTyped(app: untpd.Apply, fun: tpd.Tree, methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context) + extends TypedApply(app, fun, methRef, args, resultType) { + def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg + def treeToArg(arg: tpd.Tree): tpd.Tree = arg + } + + /** Is given method reference applicable to argument types `args`? + * @param resultType The expected result type of the application + */ + def isApplicableToTrees(methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context) = + new ApplicableToTrees(methRef, args, resultType)(ctx.fresh.withNewTyperState).success + + /** Is given method reference applicable to arguments `args`? + * @param resultType The expected result type of the application + */ + def isApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type = WildcardType)(implicit ctx: Context) = + new ApplicableToTypes(methRef, args, resultType)(ctx.fresh.withNewTyperState).success + + /** Is `tp` a subtype of `pt`? */ + def isSubType(tp: Type, pt: Type)(implicit ctx: Context) = (tp <:< pt)(ctx.fresh.withNewTyperState) + + /** In a set of overloaded applicable alternatives, is `alt1` at least as good as + * `alt2`? `alt1` and `alt2` are nonoverloaded references. + */ + def isAsGood(alt1: TermRef, alt2: TermRef)(implicit ctx: Context): Boolean = { + + /** Is class or module class `sym1` derived from class or module class `sym2`? */ + def isDerived(sym1: Symbol, sym2: Symbol): Boolean = + if (sym1 isSubClass sym2) true + else if (sym2 is Module) isDerived(sym1, sym2.companionClass) + else (sym1 is Module) && isDerived(sym1.companionClass, sym2) + + /** Is alternative `alt1` with type `tp1` as specific as alternative + * `alt2` with type `tp2` ? This is the case if `tp2` can be applied to + * `tp1` or `tp2` is a supertype of `tp1`. + */ + def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = tp1 match { + case tp1: PolyType => + def bounds(tparamRefs: List[TypeRef]) = tp1.paramBounds map (_.substParams(tp1, tparamRefs)) + val tparams = ctx.newTypeParams(alt1.symbol.owner, tp1.paramNames, EmptyFlags, bounds) + isAsSpecific(alt1, tp1.instantiate(tparams map (_.symRef)), alt2, tp2) + case tp1: MethodType => + isApplicableToTypes(alt2, tp1.paramTypes) + case _ => + isSubType(tp1, tp2) + } + + val owner1 = alt1.symbol.owner + val owner2 = alt2.symbol.owner + val tp1 = alt1.widen + val tp2 = alt2.widen + + def winsOwner1 = isDerived(owner1, owner2) + def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2) + def winsOwner2 = isDerived(owner2, owner1) + def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1) + + // Assume the following probabilities: + // + // P(winsOwnerX) = 2/3 + // P(winsTypeX) = 1/3 + // + // Then the call probabilities of the 4 basic operations are as follows: + // + // winsOwner1: 1/1 + // winsOwner2: 1/1 + // winsType1 : 7/9 + // winsType2 : 4/9 + + if (winsOwner1) /* 6/9 */ !winsOwner2 || /* 4/9 */ winsType1 || /* 8/27 */ !winsType2 + else if (winsOwner2) /* 2/9 */ winsType1 && /* 2/27 */ !winsType2 + else /* 1/9 */ winsType1 || /* 2/27 */ !winsType2 + } + + /** Resolve overloaded alternative `alts`, given expected type `pt`. */ + def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = { + + def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty + + /** The shape of given tree as a type; cannot handle named arguments. */ + def typeShape(tree: untpd.Tree): Type = tree match { + case untpd.Function(args, body) => + defn.FunctionType(args map Function.const(defn.AnyType), typeShape(body)) + case _ => + defn.NothingType + } + + /** The shape of given tree as a type; is more expensive than + * typeShape but can can handle named arguments. + */ + def treeShape(tree: untpd.Tree): tpd.Tree = tree match { + case NamedArg(name, arg) => + val argShape = treeShape(arg) + tree.withType(argShape.tpe).derivedNamedArg(name, argShape) + case _ => + Literal(Constant(null)) withType typeShape(tree) + } + + def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] = + alts filter (isApplicableToTypes(_, argTypes, resultType)) + + def narrowMostSpecific(alts: List[TermRef]): List[TermRef] = (alts: @unchecked) match { + case alt :: alts1 => + def winner(bestSoFar: TermRef, alts: List[TermRef]): TermRef = alts match { + case alt :: alts1 => + winner(if (isAsGood(alt, bestSoFar)) alt else bestSoFar, alts1) + case nil => + bestSoFar + } + val best = winner(alt, alts1) + def asGood(alts: List[TermRef]): List[TermRef] = alts match { + case alt :: alts1 => + if ((alt eq best) || !isAsGood(alt, best)) asGood(alts1) + else alt :: asGood(alts1) + case nil => + Nil + } + best :: asGood(alts1) + } + + val candidates = pt match { + case pt @ FunProtoType(args, resultType) => + val numArgs = args.length + + def sizeFits(alt: TermRef, tp: Type): Boolean = tp match { + case tp: PolyType => sizeFits(alt, tp.resultType) + case MethodType(_, ptypes) => + val numParams = ptypes.length + def isVarArgs = ptypes.nonEmpty && ptypes.last.isRepeatedParam + def hasDefault = alt.symbol.hasDefaultParams + if (numParams == numArgs) true + else if (numParams < numArgs) isVarArgs + else if (numParams > numArgs + 1) hasDefault + else isVarArgs || hasDefault + } + + def narrowBySize(alts: List[TermRef]): List[TermRef] = + alts filter (alt => sizeFits(alt, alt.widen)) + + def narrowByShapes(alts: List[TermRef]): List[TermRef] = + if (args exists (_.isInstanceOf[untpd.Function])) + if (args exists (_.isInstanceOf[NamedArg[_]])) + narrowByTrees(alts, args map treeShape, resultType) + else + narrowByTypes(alts, args map typeShape, resultType) + else + alts + + def narrowByTrees(alts: List[TermRef], args: List[tpd.Tree], resultType: Type): List[TermRef] = + alts filter (isApplicableToTrees(_, args, resultType)) + + val alts1 = narrowBySize(alts) + if (isDetermined(alts1)) alts1 + else { + val alts2 = narrowByShapes(alts1) + if (isDetermined(alts2)) alts2 + else narrowByTrees(alts2, pt.typedArgs, resultType) + } + + case defn.FunctionType(args, resultType) => + narrowByTypes(alts, args, resultType) + + case tp => + alts filter (isSubType(_, tp)) + } + + if (isDetermined(candidates)) candidates + else narrowMostSpecific(candidates) + } +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala new file mode 100644 index 000000000..9d244ea3e --- /dev/null +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -0,0 +1,82 @@ +package dotty.tools +package dotc +package typer + +import core._ +import Contexts._, Types._, Flags._, Denotations._, NameOps._, Symbols._ +import annotation.unchecked + +object Inferencing { + + implicit class Infer(val ictx: Context) extends AnyVal { + + implicit private def ctx = ictx + private def state = ctx.typerState + + /** Add all parameters in given polytype `pt` to the constraint's domain. + * If the constraint contains already some of these parameters in its domain, + * make a copy of the polytype and add the copy's type parameters instead. + * Return either the original polytype, or the copy, if one was made. + */ + def track(pt: PolyType): PolyType = { + val tracked = + if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType) + else pt + state.constraint = state.constraint + tracked + tracked + } + + /** Interpolate undetermined variables. + * If a variable appears covariantly in type `tp`, approximate it by + * its lower bound. Otherwise, if it appears contravariantly in type `tp`, + * approximate it by its upper bound. Otherwise, if `always` is true, + * approximate it also by its lower bound. + * Instantiated variables are removed from `undetVars`. + */ + def interpolateUndetVars(upTo: List[TypeVar], tp: Type, always: Boolean = false): Unit = { + def recur(undets: List[TypeVar]): List[TypeVar] = + if (undets eq upTo) undets + else (undets: @unchecked) match { + case tvar :: rest => + def instantiate(fromBelow: Boolean) = { + tvar.instantiateWith(ctx.typeComparer.approximate(tvar.origin, fromBelow)) + recur(rest) + } + val v = tp varianceOf tvar + if (v is Covariant) instantiate(fromBelow = true) + else if (v is Contravariant) instantiate(fromBelow = false) + else if (always) instantiate(fromBelow = true) + else tvar :: recur(rest) + } + state.undetVars = recur(state.undetVars) + } + + def newTypeVars(pt: PolyType): List[TypeVar] = { + val tvars = + for (n <- (0 until pt.paramNames.length).toList) + yield TypeVar(PolyParam(pt, n)) + state.undetVars = tvars ++ state.undetVars + tvars + } + + def isSubTypes(actuals: List[Type], formals: List[Type])(implicit ctx: Context): Boolean = formals match { + case formal :: formals1 => + actuals match { + case actual :: actuals1 => actual <:< formal && isSubTypes(actuals1, formals1) + case _ => false + } + case nil => + actuals.isEmpty + } +/* not needed right now + def formalParameters[T](mtp: MethodType, actuals: List[T])(isRepeated: T => Boolean)(implicit ctx: Context) = + if (mtp.isVarArgs && !(actuals.nonEmpty && isRepeated(actuals.last))) { + val leading = mtp.paramTypes.init + val repeated = mtp.paramTypes.last.typeArgs.head + val trailing = List.fill(actuals.length - leading.length)(repeated) + leading ++ trailing + } + else mtp.paramTypes + */ + } +}
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 16d643552..43589aac5 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -237,6 +237,7 @@ class Namer { typer: Typer => lazy val schema = paramFn(WildcardType) val site = sym.owner.thisType val inherited = { + // TODO: Look only at member of supertype instead? ((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) => val itpe = cls.info .nonPrivateDecl(sym.name) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 98f0d4d6e..853575277 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -4,8 +4,20 @@ package typer import core._ import ast._ -import Trees._, Constants._, StdNames._, Scopes._, Denotations._ -import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ +import Trees._ +import Constants._ +import StdNames._ +import Scopes._ +import Denotations._ +import Inferencing.Infer +import Contexts._ +import Symbols._ +import Types._ +import SymDenotations._ +import Names._ +import NameOps._ +import Flags._ +import Decorators._ import util.Positions._ import util.SourcePosition import collection.mutable @@ -33,7 +45,7 @@ object Typer { } } -class Typer extends Namer { +class Typer extends Namer with Applications { import tpd._ import Typer._ @@ -183,13 +195,10 @@ class Typer extends Namer { if (ctx eq NoContext) previous else { val outer = ctx.outer - val curScope = ctx.scope - val curOwner = ctx.owner - if (curScope ne outer.scope) { - val defDenots = - if (curOwner.isClass && (curOwner ne outer.owner)) curOwner.asClass.membersNamed(name) - else curScope.denotsNamed(name) + if (ctx.scope ne outer.scope) { + val defDenots = ctx.lookup(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)) @@ -234,55 +243,32 @@ class Typer extends Namer { tree.withType(ownType).derivedSelect(qual1, tree.name) } - class FunProtoType(args: List[untpd.Tree], resultType: Type)(implicit ctx: Context) extends UncachedGroundType { - private var _typedArgs: List[tpd.Tree] = null - private var _argTypes: List[Type] = null - def typedArgs = { - if (_typedArgs == null) - _typedArgs = args mapconserve (typed(_, Mode.Expr, WildcardType)) - _typedArgs - } - def argTypes = { - if (_argTypes == null) - _argTypes = typedArgs map (_.exprType) - _argTypes - } - def adaptedArgs(formals: List[Type]) = { - var fs = formals - if (_typedArgs == null) - _typedArgs = args mapconserve { arg => - val arg1 = typed(arg, Mode.Expr, fs.head) - fs = fs.tail - arg1 - } - else { - val adapted = typedArgs mapconserve { arg => - val arg1 = adapt(arg, Mode.Expr, fs.head) - fs = fs.tail - arg1 - } - if (adapted ne typedArgs) { - _typedArgs = adapted - _argTypes = null - } - } - typedArgs - } - def isApplicable(denot: SingleDenotation) = { + case class FunProtoType(args: List[untpd.Tree], override val resultType: Type)(implicit ctx: Context) extends UncachedGroundType { + private var myTypedArgs: List[tpd.Tree] = null + + def argsAreTyped: Boolean = myTypedArgs != null + def typedArgs: List[tpd.Tree] = { + if (myTypedArgs == null) + myTypedArgs = args mapconserve (typed(_, pt = WildcardType)) + myTypedArgs } } def typedApply(tree: untpd.Apply, mode: Mode, pt: Type)(implicit ctx: Context): Tree = { val proto = new FunProtoType(tree.args, pt) val fun1 = typed(tree.fun, Mode.Expr, proto) - fun1.exprType match { - case mt @ MethodType(_, formals) => - val args1 = proto.adaptedArgs(formals) - val restpe = mt.instantiate(proto.argTypes) - tree.withType(restpe).derivedApply(fun1, args1) - case ErrorType => - tree.withType(ErrorType) + TreeInfo.methPart(fun1).tpe match { + case funRef: TermRef => + val app = + if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) + else new ApplyToUntyped(tree, fun1, funRef, tree.args, pt) + app.result + case _ => + fun1.exprType match { + case ErrorType => + tree.withType(ErrorType) + } } } diff --git a/src/dotty/tools/io/package.scala b/src/dotty/tools/io/package.scala index 2543c38d2..1c0e0b5c4 100644 --- a/src/dotty/tools/io/package.scala +++ b/src/dotty/tools/io/package.scala @@ -21,7 +21,7 @@ package object io { type Path = scala.reflect.io.Path val Path = scala.reflect.io.Path type PlainFile = scala.reflect.io.PlainFile - val PlainFile = scala.reflect.io.PlainFile + //val PlainFile = scala.reflect.io.PlainFile val Streamable = scala.reflect.io.Streamable type VirtualDirectory = scala.reflect.io.VirtualDirectory type VirtualFile = scala.reflect.io.VirtualFile |