diff options
author | odersky <odersky@gmail.com> | 2017-03-14 17:05:30 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-14 17:05:30 +0100 |
commit | 934da77590dad2003fe850b48b2ae01b427508f0 (patch) | |
tree | 42ded3e03ed2c16e1680ebaf72a49ad2964b2026 | |
parent | 1aad0a1433e1261af252d2240d63f6f01da65cef (diff) | |
parent | 3c22580feccca384e83465afd38d3df689c61f88 (diff) | |
download | dotty-934da77590dad2003fe850b48b2ae01b427508f0.tar.gz dotty-934da77590dad2003fe850b48b2ae01b427508f0.tar.bz2 dotty-934da77590dad2003fe850b48b2ae01b427508f0.zip |
Merge pull request #2079 from dotty-staging/depmeth2
Allow inter-parameter dependencies
33 files changed, 242 insertions, 142 deletions
diff --git a/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala index 0add25f3b..abcbbbb83 100644 --- a/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala +++ b/compiler/src/dotty/tools/backend/jvm/CollectEntryPoints.scala @@ -49,7 +49,7 @@ class CollectEntryPoints extends MiniPhaseTransform { object CollectEntryPoints{ def isJavaMainMethod(sym: Symbol)(implicit ctx: Context) = { (sym.name == nme.main) && (sym.info match { - case r@MethodType(_, List(defn.ArrayOf(t))) => + case r@MethodTpe(_, List(defn.ArrayOf(t)), _) => (t.widenDealias =:= defn.StringType) && ( r.resultType.widenDealias =:= defn.UnitType) case _ => false @@ -81,9 +81,8 @@ object CollectEntryPoints{ val possibles = if (sym.flags is Flags.Module) (toDenot(sym).info nonPrivateMember nme.main).alternatives else Nil val hasApproximate = possibles exists { m => m.info match { - case MethodType(_, p :: Nil) => - p.typeSymbol == defn.ArrayClass - case _ => false + case MethodTpe(_, p :: Nil, _) => p.typeSymbol == defn.ArrayClass + case _ => false } } // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. @@ -108,8 +107,8 @@ object CollectEntryPoints{ toDenot(m.symbol).info match { case t: PolyType => fail("main methods cannot be generic.") - case t@MethodType(paramNames, paramTypes) => - if (t.resultType :: paramTypes exists (_.typeSymbol.isAbstractType)) + case MethodTpe(paramNames, paramTypes, resultType) => + if (resultType :: paramTypes exists (_.typeSymbol.isAbstractType)) fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos) else isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 51fa15706..77e979e4d 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -531,7 +531,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma tree match { case Apply(fun, args) => fun.tpe.widen match { - case MethodType(names, _) => + case MethodType(names) => names zip args } } diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e5904156f..c082c37ac 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -185,12 +185,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } def valueParamss(tp: Type): (List[List[TermSymbol]], Type) = tp match { - case tp @ MethodType(paramNames, paramTypes) => + case tp: MethodType => def valueParam(name: TermName, info: Type): TermSymbol = { val maybeImplicit = if (tp.isInstanceOf[ImplicitMethodType]) Implicit else EmptyFlags ctx.newSymbol(sym, name, TermParam | maybeImplicit, info) } - val params = (paramNames, paramTypes).zipped.map(valueParam) + val params = (tp.paramNames, tp.paramTypes).zipped.map(valueParam) val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef))) (params :: paramss, rtp) case tp => (Nil, tp.widenExpr) diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index dc56ad8b8..903efd794 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -88,6 +88,10 @@ object Config { */ final val checkHKApplications = false + /** If this flag is set, method types are checked for valid parameter references + */ + final val checkMethodTypes = false + /** The recursion depth for showing a summarized string */ final val summarizeDepth = 2 diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index 59201687a..8bc18c387 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -23,7 +23,7 @@ class JavaPlatform extends Platform { // The given symbol is a method with the right name and signature to be a runnable java program. def isJavaMainMethod(sym: SymDenotation)(implicit ctx: Context) = (sym.name == nme.main) && (sym.info match { - case t@MethodType(_, defn.ArrayOf(el) :: Nil) => el =:= defn.StringType && (t.resultType isRef defn.UnitClass) + case MethodTpe(_, defn.ArrayOf(el) :: Nil, restpe) => el =:= defn.StringType && (restpe isRef defn.UnitClass) case _ => false }) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 99c688d50..f726cd0d1 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -308,13 +308,13 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp1 case _ => mergeConflict(tp1, tp2) } - case tp1 @ MethodType(names1, formals1) if isTerm => + case tp1: MethodType if isTerm => tp2 match { - case tp2 @ MethodType(names2, formals2) if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && + case tp2: MethodType if ctx.typeComparer.matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && tp1.isImplicit == tp2.isImplicit => tp1.derivedMethodType( - mergeNames(names1, names2, nme.syntheticParamName), - formals1, + mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), + tp1.paramTypes, infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) case _ => mergeConflict(tp1, tp2) @@ -471,14 +471,14 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp2 case _ => mergeConflict(tp1, tp2) } - case tp1 @ MethodType(names1, formals1) => + case tp1: MethodType => tp2 match { - case tp2 @ MethodType(names2, formals2) - if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && + case tp2: MethodType + if ctx.typeComparer.matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && tp1.isImplicit == tp2.isImplicit => tp1.derivedMethodType( - mergeNames(names1, names2, nme.syntheticParamName), - formals1, tp1.resultType | tp2.resultType.subst(tp2, tp1)) + mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), + tp1.paramTypes, tp1.resultType | tp2.resultType.subst(tp2, tp1)) case _ => mergeConflict(tp1, tp2) } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 9d1d6481d..33aba4d13 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -252,7 +252,7 @@ trait Symbols { this: Context => /** Create a class constructor symbol for given class `cls`. */ def newConstructor(cls: ClassSymbol, flags: FlagSet, paramNames: List[TermName], paramTypes: List[Type], privateWithin: Symbol = NoSymbol, coord: Coord = NoCoord) = - newSymbol(cls, nme.CONSTRUCTOR, flags | Method, MethodType(paramNames, paramTypes)(_ => cls.typeRef), privateWithin, coord) + newSymbol(cls, nme.CONSTRUCTOR, flags | Method, MethodType(paramNames, paramTypes, cls.typeRef), privateWithin, coord) /** Create an empty default constructor symbol for given class `cls`. */ def newDefaultConstructor(cls: ClassSymbol) = diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b61fccf31..21a12dbb7 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -484,11 +484,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => } either(isSubType(tp1, tp21), isSubType(tp1, tp22)) || fourthTry(tp1, tp2) - case tp2 @ MethodType(_, formals2) => + case tp2: MethodType => def compareMethod = tp1 match { - case tp1 @ MethodType(_, formals1) => + case tp1: MethodType => (tp1.signature consistentParams tp2.signature) && - matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) && + matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && (tp1.isImplicit == tp2.isImplicit) && isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => @@ -503,7 +503,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // as members of the same type. And it seems most logical to take // ()T <:< => T, since everything one can do with a => T one can // also do with a ()T by automatic () insertion. - case tp1 @ MethodType(Nil, _) => isSubType(tp1.resultType, restpe2) + case tp1 @ MethodType(Nil) => isSubType(tp1.resultType, restpe2) case _ => isSubType(tp1.widenExpr, restpe2) } compareExpr diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 2e471215b..54251cbb1 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -217,14 +217,14 @@ object Types { */ def isVarArgsMethod(implicit ctx: Context): Boolean = this match { case tp: PolyType => tp.resultType.isVarArgsMethod - case MethodType(_, paramTypes) => paramTypes.nonEmpty && paramTypes.last.isRepeatedParam + case mt: MethodType => mt.paramTypes.nonEmpty && mt.paramTypes.last.isRepeatedParam case _ => false } /** Is this the type of a method with a leading empty parameter list? */ def isNullaryMethod(implicit ctx: Context): Boolean = this match { - case MethodType(Nil, _) => true + case MethodType(Nil) => true case tp: PolyType => tp.resultType.isNullaryMethod case _ => false } @@ -732,7 +732,7 @@ object Types { */ final def overrides(that: Type)(implicit ctx: Context) = { def result(tp: Type): Type = tp match { - case ExprType(_) | MethodType(Nil, _) => tp.resultType + case ExprType(_) | MethodType(Nil) => tp.resultType case _ => tp } (this frozen_<:< that) || { @@ -1218,8 +1218,8 @@ object Types { * when forming the function type. */ def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match { - case mt @ MethodType(_, formals) if !mt.isDependent || ctx.mode.is(Mode.AllowDependentFunctions) => - val formals1 = if (dropLast == 0) formals else formals dropRight dropLast + case mt: MethodType if !mt.isDependent || ctx.mode.is(Mode.AllowDependentFunctions) => + val formals1 = if (dropLast == 0) mt.paramTypes else mt.paramTypes dropRight dropLast defn.FunctionOf( formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType, mt.isImplicit && !ctx.erasedTypes) } @@ -2312,14 +2312,16 @@ object Types { trait MethodOrPoly extends MethodicType - abstract case class MethodType(paramNames: List[TermName], paramTypes: List[Type]) - (resultTypeExp: MethodType => Type) + abstract case class MethodType(paramNames: List[TermName])( + paramTypesExp: MethodType => List[Type], + resultTypeExp: MethodType => Type) extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType => import MethodType._ def isJava = false def isImplicit = false + val paramTypes = paramTypesExp(this) private[core] val resType = resultTypeExp(this) assert(resType.exists) @@ -2329,7 +2331,7 @@ object Types { def apply(tp: Type) = tp match { case tp @ TypeRef(pre, name) => tp.info match { - case TypeAlias(alias) if depStatus(pre) == TrueDeps => apply(alias) + case TypeAlias(alias) if depStatus(NoDeps, pre) == TrueDeps => apply(alias) case _ => mapOver(tp) } case _ => @@ -2341,8 +2343,9 @@ object Types { else resType var myDependencyStatus: DependencyStatus = Unknown + var myParamDependencyStatus: DependencyStatus = Unknown - private def depStatus(tp: Type)(implicit ctx: Context): DependencyStatus = { + private def depStatus(initial: DependencyStatus, tp: Type)(implicit ctx: Context): DependencyStatus = { def combine(x: DependencyStatus, y: DependencyStatus) = { val status = (x & StatusMask) max (y & StatusMask) val provisional = (x | y) & Provisional @@ -2366,7 +2369,7 @@ object Types { case _ => foldOver(status, tp) } } - depStatusAcc(NoDeps, tp) + depStatusAcc(initial, tp) } /** The dependency status of this method. Some examples: @@ -2380,17 +2383,36 @@ object Types { private def dependencyStatus(implicit ctx: Context): DependencyStatus = { if (myDependencyStatus != Unknown) myDependencyStatus else { - val result = depStatus(resType) + val result = depStatus(NoDeps, resType) if ((result & Provisional) == 0) myDependencyStatus = result (result & StatusMask).toByte } } + /** The parameter dependency status of this method. Analogous to `dependencyStatus`, + * but tracking dependencies in same parameter list. + */ + private def paramDependencyStatus(implicit ctx: Context): DependencyStatus = { + if (myParamDependencyStatus != Unknown) myParamDependencyStatus + else { + val result = + if (paramTypes.isEmpty) NoDeps + else (NoDeps /: paramTypes.tail)(depStatus(_, _)) + if ((result & Provisional) == 0) myParamDependencyStatus = result + (result & StatusMask).toByte + } + } + /** Does result type contain references to parameters of this method type, * which cannot be eliminated by de-aliasing? */ def isDependent(implicit ctx: Context): Boolean = dependencyStatus == TrueDeps + /** Does one of the parameter types contain references to earlier parameters + * of this method type which cannot be eliminated by de-aliasing? + */ + def isParamDependent(implicit ctx: Context): Boolean = paramDependencyStatus == TrueDeps + protected def computeSignature(implicit ctx: Context): Signature = resultSignature.prepend(paramTypes, isJava) @@ -2399,10 +2421,11 @@ object Types { resType: Type = this.resType)(implicit ctx: Context) = if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this else { + val paramTypesFn = (x: MethodType) => paramTypes.map(_.subst(this, x)) val resTypeFn = (x: MethodType) => resType.subst(this, x) - if (isJava) JavaMethodType(paramNames, paramTypes)(resTypeFn) - else if (isImplicit) ImplicitMethodType(paramNames, paramTypes)(resTypeFn) - else MethodType(paramNames, paramTypes)(resTypeFn) + if (isJava) JavaMethodType(paramNames)(paramTypesFn, resTypeFn) + else if (isImplicit) ImplicitMethodType(paramNames)(paramTypesFn, resTypeFn) + else MethodType(paramNames)(paramTypesFn, resTypeFn) } def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = @@ -2424,21 +2447,21 @@ object Types { override def toString = s"$prefixString($paramNames, $paramTypes, $resType)" } - final class CachedMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type) - extends MethodType(paramNames, paramTypes)(resultTypeExp) { + final class CachedMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { override def equals(that: Any) = super.equals(that) && that.isInstanceOf[CachedMethodType] } - final class JavaMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type) - extends MethodType(paramNames, paramTypes)(resultTypeExp) { + final class JavaMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { override def isJava = true override def equals(that: Any) = super.equals(that) && that.isInstanceOf[JavaMethodType] override def computeHash = addDelta(super.computeHash, 1) override protected def prefixString = "JavaMethodType" } - final class ImplicitMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type) - extends MethodType(paramNames, paramTypes)(resultTypeExp) { + final class ImplicitMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { override def isImplicit = true override def equals(that: Any) = super.equals(that) && that.isInstanceOf[ImplicitMethodType] override def computeHash = addDelta(super.computeHash, 2) @@ -2446,11 +2469,11 @@ object Types { } abstract class MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType + def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = - apply(paramNames, paramTypes)(_ => resultType) + apply(paramNames)(_ => paramTypes, _ => resultType) def apply(paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp) + apply(nme.syntheticParamNames(paramTypes.length))(_ => paramTypes, resultTypeExp) def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) @@ -2473,19 +2496,31 @@ object Types { case _: ExprType => tp case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot)) } + def integrate(tp: Type, mt: MethodType) = + tp.subst(params, (0 until params.length).toList.map(MethodParam(mt, _))) def paramInfo(param: Symbol): Type = { val paramType = translateRepeated(param.info) if (param.is(Inline)) translateInline(paramType) else paramType } - def transformResult(mt: MethodType) = - resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _))) - apply(params map (_.name.asTermName), params map paramInfo)(transformResult _) + apply(params.map(_.name.asTermName))( + mt => params.map(param => integrate(paramInfo(param), mt)), + mt => integrate(resultType, mt)) + } + + def checkValid(mt: MethodType)(implicit ctx: Context): mt.type = { + if (Config.checkMethodTypes) + for ((paramType, idx) <- mt.paramTypes.zipWithIndex) + paramType.foreachPart { + case MethodParam(`mt`, j) => assert(j < idx, mt) + case _ => + } + mt } } object MethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) = - unique(new CachedMethodType(paramNames, paramTypes)(resultTypeExp)) + def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + checkValid(unique(new CachedMethodType(paramNames)(paramTypesExp, resultTypeExp))) private type DependencyStatus = Byte private final val Unknown: DependencyStatus = 0 // not yet computed @@ -2497,13 +2532,19 @@ object Types { } object JavaMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) = - unique(new JavaMethodType(paramNames, paramTypes)(resultTypeExp)) + def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + unique(new JavaMethodType(paramNames)(paramTypesExp, resultTypeExp)) } object ImplicitMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) = - unique(new ImplicitMethodType(paramNames, paramTypes)(resultTypeExp)) + def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + checkValid(unique(new ImplicitMethodType(paramNames)(paramTypesExp, resultTypeExp))) + } + + /** A ternary extractor for MethodType */ + object MethodTpe { + def unapply(mt: MethodType)(implicit ctx: Context) = + Some((mt.paramNames, mt.paramTypes, mt.resultType)) } /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */ @@ -2547,11 +2588,6 @@ object Types { case _ => false } - /** Is this polytype a higher-kinded type lambda as opposed to a polymorphic? - * method type? Only type lambdas get created with variances, that's how we can tell. - */ - def isTypeLambda: Boolean = variances.nonEmpty - /** PolyParam references to all type parameters of this type */ lazy val paramRefs: List[PolyParam] = paramNames.indices.toList.map(PolyParam(this, _)) @@ -2928,20 +2964,8 @@ object Types { * instantiation can be a singleton type only if the upper bound * is also a singleton type. */ - def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = { - val inst = ctx.typeComparer.instanceType(origin, fromBelow) - if (ctx.typerState.isGlobalCommittable) - inst match { - case inst: PolyParam => - assert(inst.binder.isTypeLambda, i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}") - // If this fails, you might want to turn on Config.debugCheckConstraintsClosed - // to help find the root of the problem. - // Note: Parameters of type lambdas are excluded from the assertion because - // they might arise from ill-kinded code. See #1652 - case _ => - } - instantiateWith(inst) - } + def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = + instantiateWith(ctx.typeComparer.instanceType(origin, fromBelow)) /** Unwrap to instance (if instantiated) or origin (if not), until result * is no longer a TypeVar @@ -3660,9 +3684,9 @@ object Types { this(y, hi) } - case tp @ MethodType(pnames, ptypes) => + case tp: MethodType => variance = -variance - val y = foldOver(x, ptypes) + val y = foldOver(x, tp.paramTypes) variance = -variance this(y, tp.resultType) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 36d478c6d..bc140c26b 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -198,8 +198,8 @@ class ClassfileParser( */ def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match { case Some(entry) if !isStatic(entry.jflags) => - val mt @ MethodType(paramnames, paramtypes) = denot.info - denot.info = mt.derivedMethodType(paramnames.tail, paramtypes.tail, mt.resultType) + val mt @ MethodTpe(paramNames, paramTypes, resultType) = denot.info + denot.info = mt.derivedMethodType(paramNames.tail, paramTypes.tail, resultType) case _ => } @@ -207,9 +207,9 @@ class ClassfileParser( * and make constructor type polymorphic in the type parameters of the class */ def normalizeConstructorInfo() = { - val mt @ MethodType(paramnames, paramtypes) = denot.info + val mt @ MethodType(paramNames) = denot.info val rt = classRoot.typeRef appliedTo (classRoot.typeParams map (_.typeRef)) - denot.info = mt.derivedMethodType(paramnames, paramtypes, rt) + denot.info = mt.derivedMethodType(paramNames, mt.paramTypes, rt) addConstructorTypeParams(denot) } @@ -339,7 +339,7 @@ class ClassfileParser( } index += 1 val restype = sig2type(tparams, skiptvs) - JavaMethodType(paramnames.toList, paramtypes.toList)(_ => restype) + JavaMethodType(paramnames.toList, paramtypes.toList, restype) case 'T' => val n = subName(';'.==).toTypeName index += 1 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index fdb8a97ae..a9ea49ad1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -278,8 +278,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle result case METHODtype => val (names, paramReader) = readNamesSkipParams - val result = MethodType(names.map(_.toTermName), paramReader.readParamTypes[Type](end))( - mt => registeringType(mt, readType())) + val result = MethodType(names.map(_.toTermName))( + mt => registeringType(mt, paramReader.readParamTypes[Type](end)), + mt => readType()) goto(end) result case PARAMtype => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index faf01b177..688a2d007 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -55,8 +55,8 @@ object Scala2Unpickler { * to `RepeatedParamClass` types. */ def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match { - case tp @ MethodType(paramNames, paramTypes) => - val lastArg = paramTypes.last + case tp: MethodType => + val lastArg = tp.paramTypes.last assert(lastArg isRef defn.ArrayClass) val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) val elemtp = elemtp0 match { @@ -66,8 +66,8 @@ object Scala2Unpickler { elemtp0 } tp.derivedMethodType( - paramNames, - paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp), + tp.paramNames, + tp.paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp), tp.resultType) case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, arrayToRepeated(tp.resultType)) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 599522b74..43202ef87 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -284,7 +284,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder case pt: PolyType => assert(start == 0) paramLists(pt.resultType) - case mt @ MethodType(pnames, ptypes) => + case mt @ MethodTpe(pnames, ptypes, restpe) => // TODO: We shouldn't have to work so hard to find the default parameters // of a method, Dotty should expose a convenience method for that, see #1143 val defaults = @@ -300,7 +300,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder val params = (pnames, ptypes, defaults).zipped.map((pname, ptype, isDefault) => new api.MethodParameter(pname.toString, apiType(ptype), isDefault, api.ParameterModifier.Plain)) - new api.ParameterList(params.toArray, mt.isImplicit) :: paramLists(mt.resultType, params.length) + new api.ParameterList(params.toArray, mt.isImplicit) :: paramLists(restpe, params.length) case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala index 331fce46a..9f1e42e31 100644 --- a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala +++ b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala @@ -64,7 +64,7 @@ class CollectEntryPoints extends MiniPhaseTransform { val hasApproximate = possibles exists { m => m.info match { - case MethodType(_, p :: Nil) => + case MethodTpe(_, p :: Nil, _) => p.typeSymbol == defn.ArrayClass case _ => false } @@ -87,8 +87,8 @@ class CollectEntryPoints extends MiniPhaseTransform { m.symbol.info match { case t: PolyType => fail("main methods cannot be generic.") - case t@MethodType(paramNames, paramTypes) => - if (t.resultType :: paramTypes exists (_.typeSymbol.isAbstractType)) + case t: MethodType => + if (t.resultType :: t.paramTypes exists (_.typeSymbol.isAbstractType)) fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos) else javaPlatform.isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos) diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 0e187fc2e..59da78590 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -92,8 +92,8 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform arg } - val MethodType(_, formals) = tree.fun.tpe.widen - val args1 = tree.args.zipWithConserve(formals)(transformArg) + val mt @ MethodType(_) = tree.fun.tpe.widen + val args1 = tree.args.zipWithConserve(mt.paramTypes)(transformArg) cpy.Apply(tree)(tree.fun, args1) } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index d955628e3..ae3259509 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -48,8 +48,8 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati private def overridesJava(sym: Symbol)(implicit ctx: Context) = sym.allOverriddenSymbols.exists(_ is JavaDefined) private def elimRepeated(tp: Type)(implicit ctx: Context): Type = tp.stripTypeVar match { - case tp @ MethodType(paramNames, paramTypes) => - val resultType1 = elimRepeated(tp.resultType) + case tp @ MethodTpe(paramNames, paramTypes, resultType) => + val resultType1 = elimRepeated(resultType) val paramTypes1 = if (paramTypes.nonEmpty && paramTypes.last.isRepeatedParam) { val last = paramTypes.last.underlyingIfRepeated(tp.isJava) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 3857b405f..f9c7a8e1e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -40,8 +40,8 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => def isCompacted(sym: Symbol) = sym.isAnonymousFunction && { sym.info(ctx.withPhase(ctx.phase.next)) match { - case MethodType(nme.ALLARGS :: Nil, _) => true - case _ => false + case MethodType(nme.ALLARGS :: Nil) => true + case _ => false } } @@ -269,7 +269,7 @@ object Erasure extends TypeTestsCasts{ def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree = if (pt.isInstanceOf[FunProto]) tree else tree.tpe.widen match { - case MethodType(Nil, _) if tree.isTerm => + case MethodType(Nil) if tree.isTerm => adaptToType(tree.appliedToNone, pt) case tpw => if (pt.isInstanceOf[ProtoType] || tree.tpe <:< pt) @@ -697,9 +697,10 @@ object Erasure extends TypeTestsCasts{ val rhs = paramss.foldLeft(sel)((fun, vparams) => fun.tpe.widen match { - case MethodType(names, types) => Apply(fun, (vparams, types).zipped.map(adapt(_, _, untpd.EmptyTree))) - case a => error(s"can not resolve apply type $a") - + case mt: MethodType => + Apply(fun, (vparams, mt.paramTypes).zipped.map(adapt(_, _, untpd.EmptyTree))) + case a => + error(s"can not resolve apply type $a") }) adapt(rhs, resultType) }) diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala index 91399f91a..7b15b7e54 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -53,7 +53,7 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer => val applyRhs: Tree = applyDef.rhs val applyFn = applyDef.symbol.asTerm - val MethodType(paramNames, paramTypes) = applyFn.info + val MethodTpe(paramNames, paramTypes, _) = applyFn.info val isDefinedAtFn = applyFn.copy( name = nme.isDefinedAt, flags = Synthetic | Method, diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index d75c32fcc..88c92685f 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -328,9 +328,9 @@ object ExplicitOuter { /** If `cls` has an outer parameter add one to the method type `tp`. */ def addParam(cls: ClassSymbol, tp: Type): Type = if (hasOuterParam(cls)) { - val mt @ MethodType(pnames, ptypes) = tp + val mt @ MethodTpe(pnames, ptypes, restpe) = tp mt.derivedMethodType( - nme.OUTER :: pnames, cls.owner.enclosingClass.typeRef :: ptypes, mt.resultType) + nme.OUTER :: pnames, cls.owner.enclosingClass.typeRef :: ptypes, restpe) } else tp /** If function in an apply node is a constructor that needs to be passed an @@ -389,7 +389,7 @@ object ExplicitOuter { /** The outer parameter definition of a constructor if it needs one */ def paramDefs(constr: Symbol): List[ValDef] = if (constr.isConstructor && hasOuterParam(constr.owner.asClass)) { - val MethodType(outerName :: _, outerType :: _) = constr.info + val MethodTpe(outerName :: _, outerType :: _, _) = constr.info val outerSym = ctx.newSymbol(constr, outerName, Param, outerType) ValDef(outerSym) :: Nil } diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 9e71fda5d..8328e43de 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -29,7 +29,7 @@ import StdNames._ * - eliminates some kinds of trees: Imports, NamedArgs * - stubs out native methods * - eliminates self tree in Template and self symbol in ClassInfo - * - collapsess all type trees to trees of class TypeTree + * - collapses all type trees to trees of class TypeTree * - converts idempotent expressions with constant types * - drops branches of ifs using the rules * if (true) A else B --> A @@ -151,7 +151,7 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { cpy.Template(impl)(self = EmptyValDef) } - + override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = { if (ddef.symbol.hasAnnotation(defn.NativeAnnot)) { ddef.symbol.resetFlag(Deferred) @@ -171,13 +171,13 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota } override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = - if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) + if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else constToLiteral(tree) override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = - if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) + if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else constToLiteral(tree) - + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) = constToLiteral(tree) @@ -186,7 +186,7 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota override def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo) = constToLiteral(tree) - + override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo) = constToLiteral(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala index 5609817d8..0cb453b4c 100644 --- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -105,8 +105,9 @@ trait FullParameterization { def resultType(mapClassParams: Type => Type) = { val thisParamType = mapClassParams(clazz.classInfo.selfType) val firstArgType = if (liftThisType) thisParamType & clazz.thisType else thisParamType - MethodType(nme.SELF :: Nil, firstArgType :: Nil)(mt => - mapClassParams(origResult).substThisUnlessStatic(clazz, MethodParam(mt, 0))) + MethodType(nme.SELF :: Nil)( + mt => firstArgType :: Nil, + mt => mapClassParams(origResult).substThisUnlessStatic(clazz, MethodParam(mt, 0))) } /** Replace class type parameters by the added type parameters of the polytype `pt` */ @@ -252,10 +253,10 @@ object FullParameterization { def memberSignature(info: Type)(implicit ctx: Context): Signature = info match { case info: PolyType => memberSignature(info.resultType) - case info @ MethodType(nme.SELF :: Nil, _) => - info.resultType.ensureMethodic.signature - case info @ MethodType(nme.SELF :: otherNames, thisType :: otherTypes) => - info.derivedMethodType(otherNames, otherTypes, info.resultType).signature + case MethodTpe(nme.SELF :: Nil, _, restpe) => + restpe.ensureMethodic.signature + case info @ MethodTpe(nme.SELF :: otherNames, thisType :: otherTypes, restpe) => + info.derivedMethodType(otherNames, otherTypes, restpe).signature case _ => Signature.NotAMethod } diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index b603a53dc..7578b57f1 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -352,12 +352,12 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform } private def liftedInfo(local: Symbol)(implicit ctx: Context): Type = local.info match { - case mt @ MethodType(pnames, ptypes) => + case MethodTpe(pnames, ptypes, restpe) => val ps = proxies(local) MethodType( ps.map(_.name.asTermName) ++ pnames, ps.map(_.info) ++ ptypes, - mt.resultType) + restpe) case info => info } diff --git a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala index a72e10681..859ac8b06 100644 --- a/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala +++ b/compiler/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -27,7 +27,7 @@ class ParamForwarding(thisTransformer: DenotTransformer) { val (superArgs, superParamNames) = impl.parents match { case superCall @ Apply(fn, args) :: _ => fn.tpe.widen match { - case MethodType(paramNames, _) => (args, paramNames) + case MethodType(paramNames) => (args, paramNames) case _ => (Nil, Nil) } case _ => (Nil, Nil) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index a65ef00cc..5dcf16b62 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -178,6 +178,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic => */ protected def normalizedFun: Tree + protected def typeOfArg(arg: Arg): Type + /** If constructing trees, pull out all parts of the function * which are not idempotent into separate prefix definitions */ @@ -380,8 +382,16 @@ trait Applications extends Compatibility { self: Typer with Dynamic => if (success) formals match { case formal :: formals1 => - def addTyped(arg: Arg, formal: Type) = + /** Add result of typing argument `arg` against parameter type `formal`. + * @return A type transformation to apply to all arguments following this one. + */ + def addTyped(arg: Arg, formal: Type): Type => Type = { addArg(typedArg(arg, formal), formal) + if (methodType.isParamDependent) + _.substParam(MethodParam(methodType, n), typeOfArg(arg)) + else + identity + } def missingArg(n: Int): Unit = { val pname = methodType.paramNames(n) @@ -395,8 +405,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val getter = findDefaultGetter(n + numArgs(normalizedFun)) if (getter.isEmpty) missingArg(n) else { - addTyped(treeToArg(spliceMeth(getter withPos normalizedFun.pos, normalizedFun)), formal) - matchArgs(args1, formals1, n + 1) + val substParam = addTyped( + treeToArg(spliceMeth(getter withPos normalizedFun.pos, normalizedFun)), + formal) + matchArgs(args1, formals1.mapconserve(substParam), n + 1) } } @@ -420,8 +432,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case EmptyTree :: args1 => tryDefault(n, args1) case arg :: args1 => - addTyped(arg, formal) - matchArgs(args1, formals1, n + 1) + val substParam = addTyped(arg, formal) + matchArgs(args1, formals1.mapconserve(substParam), n + 1) case nil => tryDefault(n, args) } @@ -477,6 +489,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def argType(arg: Tree, formal: Type): Type = normalize(arg.tpe, formal) def treeToArg(arg: Tree): Tree = arg def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg) + def typeOfArg(arg: Tree): Type = arg.tpe def harmonizeArgs(args: List[Tree]) = harmonize(args) } @@ -494,6 +507,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def argType(arg: Type, formal: Type): Type = arg def treeToArg(arg: Tree): Type = arg.tpe def isVarArg(arg: Type): Boolean = arg.isRepeatedParam + def typeOfArg(arg: Type): Type = arg def harmonizeArgs(args: List[Type]) = harmonizeTypes(args) } @@ -592,6 +606,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => extends TypedApply(app, fun, methRef, proto.args, resultType) { def typedArg(arg: untpd.Tree, formal: Type): TypedArg = proto.typedArg(arg, formal.widenExpr) def treeToArg(arg: Tree): untpd.Tree = untpd.TypedSplice(arg) + def typeOfArg(arg: untpd.Tree) = proto.typeOfArg(arg) } /** Subclass of Application for type checking an Apply node with typed arguments. */ @@ -603,6 +618,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => // not match the abstract method in Application and an abstract class error results. def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg def treeToArg(arg: Tree): Tree = arg + def typeOfArg(arg: Tree) = arg.tpe } /** If `app` is a `this(...)` constructor call, the this-call argument context, @@ -1263,7 +1279,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def sizeFits(alt: TermRef, tp: Type): Boolean = tp match { case tp: PolyType => sizeFits(alt, tp.resultType) - case MethodType(_, ptypes) => + case tp: MethodType => + val ptypes = tp.paramTypes val numParams = ptypes.length def isVarArgs = ptypes.nonEmpty && ptypes.last.isRepeatedParam def hasDefault = alt.symbol.hasDefaultParams diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index f822f8893..b43391592 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -441,7 +441,6 @@ object Checking { case List(param) => if (param.is(Mutable)) ctx.error("value class parameter must not be a var", param.pos) - case _ => ctx.error("value class needs to have exactly one val parameter", clazz.pos) } @@ -625,6 +624,24 @@ trait Checking { case _ => } } + + /** Check that method parameter types do not reference their own parameter + * or later parameters in the same parameter section. + */ + def checkNoForwardDependencies(vparams: List[ValDef])(implicit ctx: Context): Unit = vparams match { + case vparam :: vparams1 => + val check = new TreeTraverser { + def traverse(tree: Tree)(implicit ctx: Context) = tree match { + case id: Ident if vparams.exists(_.symbol == id.symbol) => + ctx.error("illegal forward reference to method parameter", id.pos) + case _ => + traverseChildren(tree) + } + } + check.traverse(vparam.tpt) + checkNoForwardDependencies(vparams1) + case Nil => + } } trait NoChecking extends Checking { @@ -642,4 +659,5 @@ trait NoChecking extends Checking { override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = () override def checkTraitInheritance(parentSym: Symbol, cls: ClassSymbol, pos: Position)(implicit ctx: Context) = () + override def checkNoForwardDependencies(vparams: List[ValDef])(implicit ctx: Context): Unit = () } diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index e7e7ece78..57c1808c9 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -59,8 +59,8 @@ object EtaExpansion { */ def liftArgs(defs: mutable.ListBuffer[Tree], methRef: Type, args: List[Tree])(implicit ctx: Context) = methRef.widen match { - case MethodType(paramNames, paramTypes) => - (args, paramNames, paramTypes).zipped map { (arg, name, tp) => + case mt: MethodType => + (args, mt.paramNames, mt.paramTypes).zipped map { (arg, name, tp) => if (tp.isInstanceOf[ExprType]) arg else liftArg(defs, arg, if (name contains '$') "" else name.toString + "$") } diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index b588e3ae5..f7dd725c8 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -237,6 +237,12 @@ object ProtoTypes { typer.adapt(targ, formal, arg) } + /** The type of the argument `arg`. + * @pre `arg` has been typed before + */ + def typeOfArg(arg: untpd.Tree)(implicit ctx: Context): Type = + myTypedArg(arg).tpe + private var myTupled: Type = NoType /** The same proto-type but with all arguments combined in a single tuple */ diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 3252ead47..6080c6644 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -85,8 +85,8 @@ class ReTyper extends Typer { override def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match { - case mt @ MethodType(_, formals) => - val args: List[Tree] = tree.args.zipWithConserve(formals)(typedExpr(_, _)).asInstanceOf[List[Tree]] + case mt: MethodType => + val args: List[Tree] = tree.args.zipWithConserve(mt.paramTypes)(typedExpr(_, _)).asInstanceOf[List[Tree]] assignType(untpd.cpy.Apply(tree)(fun, args), fun, args) case _ => super.handleUnexpectedFunType(tree, fun) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6bf8dcbbc..6e774e38e 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -315,10 +315,10 @@ trait TypeAssigner { def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { - case fntpe @ MethodType(_, ptypes) => - if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) + case fntpe: MethodType => + if (sameLength(fntpe.paramTypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) else - errorType(i"wrong number of arguments for $fntpe: ${fn.tpe}, expected: ${ptypes.length}, found: ${args.length}", tree.pos) + errorType(i"wrong number of arguments for $fntpe: ${fn.tpe}, expected: ${fntpe.paramTypes.length}, found: ${args.length}", tree.pos) case t => errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fd0c7c73d..d4a9744e4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -672,8 +672,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // this can type the greatest set of admissible closures. (pt.dealias.argTypesLo.init, pt.dealias.argTypesHi.last) case SAMType(meth) => - val mt @ MethodType(_, paramTypes) = meth.info - (paramTypes, mt.resultType) + val MethodTpe(_, formals, restpe) = meth.info + (formals, restpe) case _ => (List.range(0, defaultArity) map alwaysWildcardType, WildcardType) } @@ -1229,6 +1229,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit completeAnnotations(ddef, sym) val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) + vparamss1.foreach(checkNoForwardDependencies) if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) var tpt1 = checkSimpleKinded(typedType(tpt)) @@ -1287,10 +1288,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo match { case cinfo: PolyType => maybeCall(ref, psym, cinfo.resultType) - case cinfo @ MethodType(Nil, _) if cinfo.resultType.isInstanceOf[ImplicitMethodType] => + case cinfo @ MethodType(Nil) if cinfo.resultType.isInstanceOf[ImplicitMethodType] => val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone typedExpr(untpd.TypedSplice(icall))(superCtx) - case cinfo @ MethodType(Nil, _) if !cinfo.resultType.isInstanceOf[MethodType] => + case cinfo @ MethodType(Nil) if !cinfo.resultType.isInstanceOf[MethodType] => ref case cinfo: MethodType => if (!ctx.erasedTypes) { // after constructors arguments are passed in super call. diff --git a/compiler/src/dotty/tools/dotc/typer/Variances.scala b/compiler/src/dotty/tools/dotc/typer/Variances.scala index 92bd9fd74..5a1745930 100644 --- a/compiler/src/dotty/tools/dotc/typer/Variances.scala +++ b/compiler/src/dotty/tools/dotc/typer/Variances.scala @@ -79,8 +79,8 @@ object Variances { varianceInType(parent)(tparam) & varianceInType(rinfo)(tparam) case tp: RecType => varianceInType(tp.parent)(tparam) - case tp @ MethodType(_, paramTypes) => - flip(varianceInTypes(paramTypes)(tparam)) & varianceInType(tp.resultType)(tparam) + case tp: MethodType => + flip(varianceInTypes(tp.paramTypes)(tparam)) & varianceInType(tp.resultType)(tparam) case ExprType(restpe) => varianceInType(restpe)(tparam) case tp @ HKApply(tycon, args) => diff --git a/tests/neg/illegal-depmeth.scala b/tests/neg/illegal-depmeth.scala new file mode 100644 index 000000000..e4d771a07 --- /dev/null +++ b/tests/neg/illegal-depmeth.scala @@ -0,0 +1,13 @@ +object Test { + + class C { type T } + + def f(x: C, y: x.T): x.T = y // ok + + def g(y: x.T, x: C): x.T = y // error + + def h(x: x.T) = ??? // error + + def g(x: => C): x.T = ??? // error: x is not stable + +} diff --git a/tests/pos/param-depmeth.scala b/tests/pos/param-depmeth.scala new file mode 100644 index 000000000..2e887d181 --- /dev/null +++ b/tests/pos/param-depmeth.scala @@ -0,0 +1,15 @@ +object Test { + + class C { type T } + + def f(x: C, y: x.T): x.T = y // ok + + val c = new C { type T = String } + val c2 = c + + f(c, "abc") + f(new C{ type T = String}, "abc") + + val d: (C{ type T = String}) # T = "abc" + +} |