aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
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 _ =>
}