aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-05-07 14:50:56 +0200
committerSamuel Gruetter <samuel.gruetter@epfl.ch>2014-05-20 13:38:48 +0200
commitc2d5246bdb33d60d3eaff62a539d01368124d859 (patch)
tree8042d19558c350597655d5bb1dfee4ffa25e02d6 /src/dotty/tools/dotc
parent2d3c79f7cf3e79b592c7e479d262e8d3f9b04959 (diff)
downloaddotty-c2d5246bdb33d60d3eaff62a539d01368124d859.tar.gz
dotty-c2d5246bdb33d60d3eaff62a539d01368124d859.tar.bz2
dotty-c2d5246bdb33d60d3eaff62a539d01368124d859.zip
More systematic treatment of prototypes.
There's a delicate balance about how much of a prototype should be passed down the tree when typechecking. Passing little can cause ambiguity errors (both in overloading and in implicit search). Passing too much can cause spurious errors because implicit conversions "down the road" that apply to some tree continaing the result might not be considered. Symptoms of the problems wree that we could not handle the tests included in this commit before. The new scheme is as follows: we always keep all available information in a prototype, but hide nested prototypes behined a `IgnoredProto` wall. These trees will not be considered for conformity checking. When type checking hits an ambiguity, it tries again with a prototype that's one level deeper (has fewer Ignore links) than the previous one. This continues until there are no more Ignore links to unwrap. We also generalize the scheme for wrapping qualifiers of select nodes from realApply to all instances where we compare against a FunProto. Finally, there are some fixes that avoid assertion violations that were provoked by the new typechecking scheme.
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/core/Types.scala10
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala12
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala44
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala14
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala41
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala46
6 files changed, 108 insertions, 59 deletions
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 088a2e3af..36b546230 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -756,6 +756,11 @@ object Types {
def typeParamNamed(name: TypeName)(implicit ctx: Context): Symbol =
classSymbol.decls.lookup(name) orElse member(name).symbol
+ /** If this is a prototype with some ignored component, reveal one more
+ * layer of it. Otherwise the type itself.
+ */
+ def deepenProto(implicit ctx: Context): Type = this
+
// ----- Substitutions -----------------------------------------------------
/** Substitute all types that refer in their symbol attribute to
@@ -1057,12 +1062,13 @@ object Types {
if (owner.isTerm) d else d.asSeenFrom(prefix)
}
- private def checkSymAssign(sym: Symbol) =
+ private def checkSymAssign(sym: Symbol)(implicit ctx: Context) =
assert(
(lastSymbol eq sym) ||
(lastSymbol eq null) ||
(lastSymbol.defRunId != sym.defRunId) ||
- (lastSymbol.defRunId == NoRunId),
+ (lastSymbol.defRunId == NoRunId) ||
+ (lastSymbol.infoOrCompleter == ErrorType),
s"data race? overwriting symbol of $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id}")
protected def sig: Signature = Signature.NotAMethod
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index dd8f04d92..c20598bb3 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -7,7 +7,7 @@ import Contexts.Context, Scopes.Scope, Denotations._, Annotations.Annotation
import StdNames.nme
import ast.{Trees, untpd}
import typer.Namer
-import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto}
+import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto}
import Trees._
import scala.annotation.switch
@@ -108,10 +108,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}
return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close
}
- case tp: SelectionProto =>
- return toText(RefinedType(WildcardType, tp.name, tp.memberProto))
- case tp: ViewProto =>
- return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType)
case tp: TypeRef =>
if ((tp.symbol is TypeParam | TypeArgument) && !ctx.phase.erasedTypes) {
return tp.info match {
@@ -121,8 +117,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}
case ExprType(result) =>
return "=> " ~ toText(result)
+ case tp: SelectionProto =>
+ return toText(RefinedType(WildcardType, tp.name, tp.memberProto))
+ case tp: ViewProto =>
+ return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType)
case FunProto(args, resultType, _) =>
return "funproto(" ~ toTextGlobal(args, ", ") ~ "):" ~ toText(resultType)
+ case tp: IgnoredProto =>
+ return "?"
case _ =>
}
super.toText(tp)
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 0f47336fc..92acb7939 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -434,7 +434,7 @@ trait Applications extends Compatibility { self: Typer =>
def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
def realApply(implicit ctx: Context): Tree = track("realApply") {
- var proto = new FunProto(tree.args, pt, this)
+ var proto = new FunProto(tree.args, ignoreIfProto(pt), this)
val fun1 = typedExpr(tree.fun, proto)
// Warning: The following line is dirty and fragile. We record that auto-tupling was demanded as
@@ -453,30 +453,13 @@ trait Applications extends Compatibility { self: Typer =>
else new ApplyToUntyped(tree, fun1, funRef, proto, pt)
val result = app.result
ConstFold(result)
- } { (failedVal, failedState) => fun1 match {
- case Select(qual, name) =>
- // try with prototype `[].name(args)`, this might succeed by inserting an
- // implicit conversion around []. (an example is Int + BigInt).
- tryEither { implicit ctx =>
- val simpleFunProto = new FunProto(tree.args, WildcardType, this) // drop result type, because views are disabled
- val selProto = SelectionProto(name, simpleFunProto, NoViewsAllowed)
- val qual1 = adaptInterpolated(qual, selProto)
- if (qual eq qual1) ctx.error("no progress")
- if (ctx.reporter.hasErrors) qual1
- else
- typedApply(
- cpy.Apply(tree,
- cpy.Select(fun1, untpd.TypedSplice(qual1), name),
- proto.typedArgs map untpd.TypedSplice),
- pt)
- } { (_, _) =>
- failedState.commit()
- failedVal
- }
- case _ =>
- failedState.commit()
- failedVal
- }
+ } { (failedVal, failedState) =>
+ val fun2 = tryInsertImplicit(fun1, proto)
+ if (fun1 eq fun2) {
+ failedState.commit()
+ failedVal
+ } else typedApply(
+ cpy.Apply(tree, untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt)
}
case _ =>
fun1.tpe match {
@@ -771,7 +754,7 @@ trait Applications extends Compatibility { self: Typer =>
def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { 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)
+ val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, bounds)
isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2)
case tp1: MethodType =>
def repeatedToSingle(tp: Type) = if (tp.isRepeatedParam) tp.argTypesHi.head else tp
@@ -927,7 +910,14 @@ trait Applications extends Compatibility { self: Typer =>
alts filter (normalizedCompatible(_, pt))
}
if (isDetermined(candidates)) candidates
- else narrowMostSpecific(candidates)
+ else narrowMostSpecific(candidates) match {
+ case result @ (alt1 :: alt2 :: _) =>
+ val deepPt = pt.deepenProto
+ if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs)
+ else result
+ case result =>
+ result
+ }
}
}
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index a32f552ed..d2a94e287 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -368,7 +368,10 @@ trait Implicits { self: Typer =>
return defn.isValueSubClass(from.symbol, to.symbol)
case _ =>
}
+ case from: ValueType =>
+ ;
case _ =>
+ return false
}
inferView(dummyTreeOfType(from), to)(ctx.fresh.setExploreTyperState).isInstanceOf[SearchSuccess]
}
@@ -410,12 +413,15 @@ trait Implicits { self: Typer =>
else new ImplicitSearch(pt, argument, pos)
val result = isearch.bestImplicit
result match {
- case success: SearchSuccess =>
- // println(s"committing to ${success.tstate.show}")
- success.tstate.commit()
+ case result: SearchSuccess =>
+ result.tstate.commit()
+ result
+ case result: AmbiguousImplicits =>
+ val deepPt = pt.deepenProto
+ if (deepPt ne pt) inferImplicit(deepPt, argument, pos) else result
case _ =>
+ result
}
- result
}
}
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index fab652849..1438f9e16 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -71,6 +71,23 @@ object ProtoTypes {
override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false
}
+ /** A trait for prototypes that match all types */
+ trait MatchAlways extends ProtoType {
+ def isMatchedBy(tp1: Type)(implicit ctx: Context) = true
+ def map(tm: TypeMap)(implicit ctx: Context): ProtoType = this
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = x
+ }
+
+ /** A class marking ignored prototypes that can be reviealed by `deepenProto` */
+ case class IgnoredProto(proto: ProtoType) extends UncachedGroundType with MatchAlways {
+ override def deepenProto(implicit ctx: Context): Type = proto
+ }
+
+ def ignoreIfProto(tp: Type): Type = tp match {
+ case proto: ProtoType => IgnoredProto(proto)
+ case _ => tp
+ }
+
/** A prototype for expressions [] that are part of a selection operation:
*
* [ ].name: proto
@@ -107,6 +124,8 @@ object ProtoTypes {
def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat)
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto)
+ override def deepenProto(implicit ctx: Context) = derivedSelectionProto(name, memberProto.deepenProto, compat)
+
override def computeHash = addDelta(doHash(name, memberProto), if (compat eq NoViewsAllowed) 1 else 0)
}
@@ -126,8 +145,7 @@ object ProtoTypes {
if (name.isConstructorName) WildcardType
else tp match {
case tp: UnapplyFunProto => new UnapplySelectionProto(name)
- case tp: ProtoType => SelectionProto(name, WildcardType, typer)
- case _ => SelectionProto(name, tp, typer)
+ case tp => SelectionProto(name, ignoreIfProto(tp), typer)
}
/** A prototype for expressions [] that are in some unspecified selection operation
@@ -208,7 +226,10 @@ object ProtoTypes {
def map(tm: TypeMap)(implicit ctx: Context): FunProto =
derivedFunProto(args, tm(resultType), typer)
- def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(x, resultType)
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T =
+ ta(ta.foldOver(x, typedArgs.tpes), resultType)
+
+ override def deepenProto(implicit ctx: Context) = derivedFunProto(args, resultType.deepenProto, typer)
}
/** A prototype for implicitly inferred views:
@@ -226,10 +247,10 @@ object ProtoTypes {
def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType))
- def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = ta(ta(x, argType), resultType)
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T =
+ ta(ta(x, argType), resultType)
- override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] =
- AndType.unchecked(argType, resultType).namedPartsWith(p) // this is more efficient than oring two namedParts sets
+ override def deepenProto(implicit ctx: Context) = derivedViewProto(argType, resultType.deepenProto)
}
class CachedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) extends ViewProto(argType, resultType) {
@@ -266,17 +287,15 @@ object ProtoTypes {
def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T =
ta(ta.foldOver(x, targs), resultType)
+
+ override def deepenProto(implicit ctx: Context) = derivedPolyProto(targs, resultType.deepenProto)
}
/** A prototype for expressions [] that are known to be functions:
*
* [] _
*/
- object AnyFunctionProto extends UncachedGroundType with ProtoType {
- def isMatchedBy(tp: Type)(implicit ctx: Context) = true
- def map(tm: TypeMap)(implicit ctx: Context) = this
- def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = x
- }
+ object AnyFunctionProto extends UncachedGroundType with MatchAlways
/** Add all parameters in given polytype `pt` to the constraint's domain.
* If the constraint contains already some of these parameters in its domain,
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 944b44510..5d477193c 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -1009,15 +1009,41 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
- def tryInsertApply(tree: Tree, pt: Type)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
- tryEither {
- implicit ctx =>
- val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
- if (sel.tpe.isError) sel else adapt(sel, pt)
- } {
- fallBack
+ /** Try to insert `.apply` so that the result conforms to prototype `pt`.
+ * If that fails try to insert an implicit conversion around the qualifier
+ * part of `tree`. If either result conforms to `pt`, adapt it, else
+ * continue with `fallBack`.
+ */
+ def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
+ tryEither { implicit ctx =>
+ val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
+ if (sel.tpe.isError) sel else adapt(sel, pt)
+ } { (failedTree, failedState) =>
+ val tree1 = tryInsertImplicit(tree, pt)
+ if (tree1 eq tree) fallBack(failedTree, failedState)
+ else adapt(tree1, pt)
}
+ /** If this tree is a select node `qual.name`, try to insert an implicit conversion
+ * `c` around `qual` so that `c(qual).name` conforms to `pt`. If that fails
+ * return `tree` itself.
+ */
+ def tryInsertImplicit(tree: Tree, pt: ProtoType)(implicit ctx: Context): Tree = ctx.traceIndented(i"try ins impl $tree $pt") { tree match {
+ case Select(qual, name) =>
+ val normalizedProto = pt match {
+ case pt: FunProto => pt.derivedFunProto(pt.args, WildcardType, pt.typer) // drop result type, because views are disabled
+ case _ => pt
+ }
+ val qualProto = SelectionProto(name, normalizedProto, NoViewsAllowed)
+ tryEither { implicit ctx =>
+ val qual1 = adaptInterpolated(qual, qualProto)
+ if ((qual eq qual1) || ctx.reporter.hasErrors) tree
+ else typedSelect(cpy.Select(tree, untpd.TypedSplice(qual1), name), pt)
+ } { (_, _) => tree
+ }
+ case _ => tree
+ }}
+
def adapt(tree: Tree, pt: Type)(implicit ctx: Context) = /*>|>*/ track("adapt") /*<|<*/ {
/*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ {
interpolateUndetVars(tree)
@@ -1087,7 +1113,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def hasEmptyParams(denot: SingleDenotation) = denot.info.paramTypess == ListOfNil
pt match {
case pt: FunProto =>
- tryInsertApply(tree, pt)((_, _) => noMatches)
+ tryInsertApplyOrImplicit(tree, pt)((_, _) => noMatches)
case _ =>
if (altDenots exists (_.info.paramTypess == ListOfNil))
typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt)
@@ -1113,7 +1139,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
adaptToArgs(wtp, pt.tupled)
else
tree
- case _ => tryInsertApply(tree, pt) {
+ case _ => tryInsertApplyOrImplicit(tree, pt) {
val more = tree match {
case Apply(_, _) => " more"
case _ => ""
@@ -1216,7 +1242,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case pt: FunProto =>
adaptToArgs(wtp, pt)
case pt: PolyProto =>
- tryInsertApply(tree, pt) {
+ tryInsertApplyOrImplicit(tree, pt) {
(_, _) => tree // error will be reported in typedTypeApply
}
case _ =>