aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-11-06 18:39:00 +0100
committerMartin Odersky <odersky@gmail.com>2013-11-06 18:39:00 +0100
commite401bac707d6d56d551db5556a7f58861ea3ae16 (patch)
treebdb6f5f25a9f339914858ff87143064a694eb9fc /src/dotty/tools/dotc
parentc1abb572fa3adaeef3f66c79ac8946d15c7aeca7 (diff)
downloaddotty-e401bac707d6d56d551db5556a7f58861ea3ae16.tar.gz
dotty-e401bac707d6d56d551db5556a7f58861ea3ae16.tar.bz2
dotty-e401bac707d6d56d551db5556a7f58861ea3ae16.zip
Fixing several type checking problems.
1. Being more precise what gets forced when. 2. stopping repeated evaluation when inserting an implicit methods to make arguments match. Previously the argument got re-evaluated which could lead to misleading errors (e..g missing parameter type if argument was a closure), and could also lead to exponential checking blowup. We now re-use the old argument but in its unadapted form. We do this with a tight coupling between an Application node and a FunProto node - typedArg in the application node forwards to new caching functionality in the FunProto node. It would probably be better overall to merge the two abstractions. FunProto = Application? 3. Various fixes to pattern matching.
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala5
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala87
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala50
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala16
4 files changed, 105 insertions, 53 deletions
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 96fe172b3..9d03d001f 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -315,6 +315,11 @@ class Definitions(implicit ctx: Context) {
lazy val RootImports = Set[Symbol](PredefModule, ScalaPackageVal, JavaLangPackageVal)
+ def isTupleType(tp: Type) = {
+ val arity = tp.typeArgs.length
+ arity <= MaxTupleArity && (tp isRef TupleClass(arity))
+ }
+
// ----- Higher kinds machinery ------------------------------------------
private var _hkTraits: Set[Symbol] = Set()
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 794c7e752..f4936cff0 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -429,9 +429,9 @@ trait Applications extends Compatibility { self: Typer =>
}
/** Subclass of Application for type checking an Apply node with untyped arguments. */
- class ApplyToUntyped(app: untpd.Apply, fun: 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, formal)
+ class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: TermRef, proto: FunProto, resultType: Type)(implicit ctx: Context)
+ extends TypedApply(app, fun, methRef, proto.args, resultType) {
+ def typedArg(arg: untpd.Tree, formal: Type): TypedArg = proto.typedArg(arg, formal)
def treeToArg(arg: Tree): untpd.Tree = untpd.TypedSplice(arg)
}
@@ -462,7 +462,7 @@ trait Applications extends Compatibility { self: Typer =>
tryEither { implicit ctx =>
val app =
if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
- else new ApplyToUntyped(tree, fun1, funRef, tree.args, pt)
+ else new ApplyToUntyped(tree, fun1, funRef, proto, pt)
val result = app.result
ConstFold(result) orElse result
} { failed => fun1 match {
@@ -530,6 +530,26 @@ trait Applications extends Compatibility { self: Typer =>
def typedUnApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = track("typedUnApply") {
val Apply(qual, args) = tree
+ def notAnExtractor(tree: Tree) =
+ errorTree(tree, s"${qual.show} cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method")
+
+ val unapply = {
+ val dummyArg = untpd.TypedSplice(dummyTreeOfType(WildcardType))
+ val unappProto = FunProto(dummyArg :: Nil, pt, this)
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, nme.unapply), unappProto)
+ } {
+ s =>
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped
+ } {
+ _ => notAnExtractor(s.value)
+ }
+ }
+ }
+
+ def fromScala2x = unapply.symbol.exists && (unapply.symbol.owner is Scala2x)
+
def unapplyArgs(unapplyResult: Type)(implicit ctx: Context): List[Type] = {
def recur(tp: Type): List[Type] = {
def extractorMemberType(name: Name) = {
@@ -547,8 +567,11 @@ trait Applications extends Compatibility { self: Typer =>
sels.takeWhile(_.exists).toList
}
def seqSelector = defn.RepeatedParamType.appliedTo(tp.elemType :: Nil)
-
- if (tp derivesFrom defn.ProductClass) productSelectors
+ def optionSelectors(tp: Type): List[Type] =
+ if (defn.isTupleType(tp)) tp.typeArgs else tp :: Nil
+ if (fromScala2x && (tp isRef defn.OptionClass) && tp.typeArgs.length == 1)
+ optionSelectors(tp.typeArgs.head)
+ else if (tp derivesFrom defn.ProductClass) productSelectors
else if (tp derivesFrom defn.SeqClass) seqSelector :: Nil
else if (tp isRef defn.BooleanClass) Nil
else if (extractorMemberType(nme.isDefined).exists &&
@@ -562,43 +585,42 @@ trait Applications extends Compatibility { self: Typer =>
recur(unapplyResult)
}
- def notAnExtractor(tree: Tree) =
- errorTree(tree, s"${qual.show} cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method")
-
- val unapply = {
- val dummyArg = untpd.TypedSplice(dummyTreeOfType(WildcardType))
- val unappProto = FunProto(dummyArg :: Nil, pt, this)
- tryEither {
- implicit ctx => typedExpr(untpd.Select(qual, nme.unapply), unappProto)
- } {
- s =>
- tryEither {
- implicit ctx => typedExpr(untpd.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped
- } {
- _ => notAnExtractor(s.value)
- }
- }
- }
-
unapply.tpe.widen match {
case mt: MethodType if !mt.isDependent =>
val unapplyArgType = mt.paramTypes.head
+ println(s"unapp arg tpe = ${unapplyArgType.show}, pt = ${pt.show}")
val ownType =
if (pt <:< unapplyArgType) {
- assert(isFullyDefined(unapplyArgType))
+ fullyDefinedType(unapplyArgType, "extractor argument", tree.pos)
+ println(i"case 1 $unapplyArgType ${ctx.typerState.constraint}")
pt
}
- else if (unapplyArgType <:< widenForSelector(pt))
+ else if (unapplyArgType <:< widenForSelector(pt)) {
ctx.maximizeType(unapplyArgType) match {
- case None => unapplyArgType
case Some(tvar) =>
- errorType(
+ def msg =
s"""There is no best instantiation of pattern type ${unapplyArgType.show}
|that makes it a subtype of selector type ${pt.show}.
- |Non-variant type variable ${tvar.origin.show} cannot be uniquely instantiated.""".stripMargin,
- tree.pos)
+ |Non-variant type variable ${tvar.origin.show} cannot be uniquely instantiated.""".stripMargin
+ if (fromScala2x) {
+ // We can't issue an error here, because in Scala 2, ::[B] is invariant
+ // whereas List[+T] is covariant. According to the strict rule, a pattern
+ // match of a List[C] against a case x :: xs is illegal, because
+ // B cannot be uniquely instantiated. Of course :: should have been
+ // covariant in the first place, but in the Scala libraries it isn't.
+ // So for now we allow these kinds of patterns, even though they
+ // can open unsoundness holes. See SI-7952 for an example of the hole this opens.
+ if (ctx.settings.verbose.value) ctx.warning(msg, tree.pos)
+ }
+ else {
+ println(s" ${unapply.symbol.owner} ${unapply.symbol.owner is Scala2x}")
+ ctx.error(msg, tree.pos)
+ }
+ case _ =>
}
- else errorType(
+ println(i"case 2 $unapplyArgType ${ctx.typerState.constraint}")
+ unapplyArgType
+ } else errorType(
s"Pattern type ${unapplyArgType.show} is neither a subtype nor a supertype of selector type ${pt.show}",
tree.pos)
@@ -608,12 +630,13 @@ trait Applications extends Compatibility { self: Typer =>
case _ => args
}
if (argTypes.length != bunchedArgs.length) {
- ctx.error(s"wrong number of argument patterns for ${err.patternConstrStr(unapply)}", tree.pos)
+ ctx.error(i"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos)
argTypes = argTypes.take(args.length) ++
List.fill(argTypes.length - args.length)(WildcardType)
}
val typedArgs = (bunchedArgs, argTypes).zipped map (typed(_, _))
val result = cpy.UnApply(tree, unapply, typedArgs) withType ownType
+ println(s"typedargs = $typedArgs")
if ((ownType eq pt) || ownType.isError) result
else Typed(result, TypeTree(ownType))
case tp =>
diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala
index 8fbff0914..805d64898 100644
--- a/src/dotty/tools/dotc/typer/Inferencing.scala
+++ b/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -8,7 +8,7 @@ import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps
import Trees._
import annotation.unchecked
import util.Positions._
-import util.Stats
+import util.{Stats, SimpleMap}
import Decorators._
import ErrorReporting.{errorType, InfoString}
import collection.mutable.ListBuffer
@@ -64,18 +64,32 @@ object Inferencing {
object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType)
case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context) extends UncachedGroundType with ProtoType {
- private var myTypedArgs: List[Tree] = null
+ private var myTypedArgs: List[Tree] = Nil
def isMatchedBy(tp: Type)(implicit ctx: Context) =
typer.isApplicableToTrees(tp, typedArgs, resultType)
- def argsAreTyped: Boolean = myTypedArgs != null
+ def argsAreTyped: Boolean = myTypedArgs.nonEmpty || args.isEmpty
def typedArgs: List[Tree] = {
- if (myTypedArgs == null)
- myTypedArgs = args mapconserve (typer.typed(_))
+ if (!argsAreTyped)
+ myTypedArgs = args mapconserve { arg =>
+ val targ = myTypedArg(arg)
+ if (targ != null) targ else typer.typed(arg)
+ }
myTypedArgs
}
+
+ private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty
+
+ def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = {
+ var targ = myTypedArg(arg)
+ if (targ == null) {
+ targ = typer.typedUnadapted(arg, formal)
+ myTypedArg = myTypedArg.updated(arg, targ)
+ }
+ typer.interpolateAndAdapt(targ, formal)
+ }
}
case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context) extends CachedGroundType with ProtoType {
@@ -109,6 +123,13 @@ object Inferencing {
}
}
+ /** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
+ object ForceDegree extends Enumeration {
+ val none, // don't force type variables
+ noBottom, // force type variables, fail if forced to Nothing or Null
+ all = Value // force type variables, don't fail
+ }
+
/** Is type fully defined, meaning the type does not contain wildcard types
* or uninstantiated type variables. As a side effect, this will minimize
* any uninstantiated type variables, provided that
@@ -116,30 +137,27 @@ object Inferencing {
* - the overall result of `isFullYDefined` is `true`.
* Variables that are successfully minimized do not count as uninstantiated.
*/
- def isFullyDefined(tp: Type, forceIt: Boolean = false)(implicit ctx: Context): Boolean = {
+ def isFullyDefined(tp: Type, force: ForceDegree.Value)(implicit ctx: Context): Boolean = {
val nestedCtx = ctx.fresh.withNewTyperState
- val result = new IsFullyDefinedAccumulator(forceIt)(nestedCtx).traverse(tp)
+ val result = new IsFullyDefinedAccumulator(force)(nestedCtx).traverse(tp)
if (result) nestedCtx.typerState.commit()
result
}
- def forceFullyDefined(tp: Type)(implicit ctx: Context): Boolean =
- isFullyDefined(tp, forceIt = true)
-
def fullyDefinedType(tp: Type, what: String, pos: Position)(implicit ctx: Context) =
- if (forceFullyDefined(tp)) tp
+ if (isFullyDefined(tp, ForceDegree.all)) tp
else errorType(i"internal error: type of $what $tp is not fully defined", pos)
- private class IsFullyDefinedAccumulator(forceIt: Boolean)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
+ private class IsFullyDefinedAccumulator(force: ForceDegree.Value)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
def traverse(tp: Type): Boolean = apply(true, tp)
def apply(x: Boolean, tp: Type) = !x || isOK(tp) && foldOver(x, tp)
def isOK(tp: Type): Boolean = tp match {
case _: WildcardType =>
false
- case tvar: TypeVar if forceIt && !tvar.isInstantiated =>
+ case tvar: TypeVar if force != ForceDegree.none && !tvar.isInstantiated =>
val inst = tvar.instantiate(fromBelow = true)
println(i"forced instantiation of ${tvar.origin} = $inst")
- inst != defn.NothingType && inst != defn.NullType && traverse(inst)
+ (force == ForceDegree.all || inst != defn.NothingType && inst != defn.NullType) && traverse(inst)
case _ =>
true
}
@@ -239,8 +257,8 @@ object Inferencing {
else if (v == -1) tvar.instantiate(fromBelow = true)
else {
val bounds @ TypeBounds(lo, hi) = ctx.typerState.constraint(tvar.origin)
- if (hi <:< lo) tvar.instantiate(fromBelow = false)
- else result = Some(tvar)
+ if (!(hi <:< lo)) result = Some(tvar)
+ tvar.instantiate(fromBelow = false)
}
result
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index efb89cc4c..e9c6b0d08 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -460,7 +460,7 @@ class Typer extends Namer with Applications with Implicits {
val result = cpy.Block(tree, stats1, expr1).withType(blockType(stats1, expr1.tpe))
val leaks = CheckTrees.escapingRefs(result)
if (leaks.isEmpty) result
- else if (forceFullyDefined(pt)) {
+ else if (isFullyDefined(pt, ForceDegree.all)) {
val expr2 = typed(untpd.Typed(untpd.TypedSplice(expr1), untpd.TypeTree(pt)))
untpd.Block(stats1, expr2) withType expr2.tpe
} else
@@ -496,8 +496,14 @@ class Typer extends Namer with Applications with Implicits {
if (!param.tpt.isEmpty) param
else {
val paramType =
- if (forceFullyDefined(formal)) formal
- else errorType("missing parameter type", param.pos)
+ if (isFullyDefined(formal, ForceDegree.noBottom)) formal
+ else {
+ val ofFun =
+ if (nme.syntheticParamNames(args.length + 1) contains param.name)
+ s" for expanded function ${tree.show}"
+ else ""
+ errorType(s"missing parameter type for parameter ${param.name}$ofFun, expected = ${pt.show}", param.pos)
+ }
cpy.ValDef(param, param.mods, param.name, untpd.TypeTree(paramType), param.rhs)
}
typed(desugar.makeClosure(inferredParams, body), pt)
@@ -617,7 +623,7 @@ class Typer extends Namer with Applications with Implicits {
def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") {
val (original1, ownType) = tree.original match {
case untpd.EmptyTree =>
- assert(isFullyDefined(pt))
+ assert(isFullyDefined(pt, ForceDegree.none))
(EmptyTree, pt)
case original: ValDef =>
val meth = symbolOfTree(original)
@@ -1073,7 +1079,7 @@ class Typer extends Namer with Applications with Implicits {
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
pt match {
case SAMType(meth)
- if wtp <:< meth.info.toFunctionType && isFullyDefined(pt) =>
+ if wtp <:< meth.info.toFunctionType && isFullyDefined(pt, ForceDegree.noBottom) =>
return cpy.Closure(tree, Nil, id, TypeTree(pt)).withType(pt)
case _ =>
}