Skip to content

Commit a0e7adb

Browse files
authored
Merge pull request #1482 from dotty-staging/fix-asapplicable-safe
More tweaks to type inference
2 parents 1650add + 2dfe4db commit a0e7adb

File tree

7 files changed

+119
-33
lines changed

7 files changed

+119
-33
lines changed

src/dotty/tools/dotc/core/Constraint.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ abstract class Constraint extends Showable {
143143
/** The uninstantiated typevars of this constraint */
144144
def uninstVars: collection.Seq[TypeVar]
145145

146+
/** The weakest constraint that subsumes both this constraint and `other` */
147+
def & (other: Constraint)(implicit ctx: Context): Constraint
148+
146149
/** Check that no constrained parameter contains itself as a bound */
147150
def checkNonCyclic()(implicit ctx: Context): Unit
148151

src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ import annotation.tailrec
1515

1616
object OrderingConstraint {
1717

18+
type ArrayValuedMap[T] = SimpleMap[GenericType, Array[T]]
19+
1820
/** The type of `OrderingConstraint#boundsMap` */
19-
type ParamBounds = SimpleMap[GenericType, Array[Type]]
21+
type ParamBounds = ArrayValuedMap[Type]
2022

2123
/** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */
22-
type ParamOrdering = SimpleMap[GenericType, Array[List[PolyParam]]]
24+
type ParamOrdering = ArrayValuedMap[List[PolyParam]]
2325

2426
/** A new constraint with given maps */
2527
private def newConstraint(boundsMap: ParamBounds, lowerMap: ParamOrdering, upperMap: ParamOrdering)(implicit ctx: Context) : OrderingConstraint = {
@@ -495,6 +497,44 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
495497
}
496498
}
497499

500+
def & (other: Constraint)(implicit ctx: Context) = {
501+
def merge[T](m1: ArrayValuedMap[T], m2: ArrayValuedMap[T], join: (T, T) => T): ArrayValuedMap[T] = {
502+
var merged = m1
503+
def mergeArrays(xs1: Array[T], xs2: Array[T]) = {
504+
val xs = xs1.clone
505+
for (i <- xs.indices) xs(i) = join(xs1(i), xs2(i))
506+
xs
507+
}
508+
m2.foreachBinding { (poly, xs2) =>
509+
merged = merged.updated(poly,
510+
if (m1.contains(poly)) mergeArrays(m1(poly), xs2) else xs2)
511+
}
512+
merged
513+
}
514+
515+
def mergeParams(ps1: List[PolyParam], ps2: List[PolyParam]) =
516+
(ps1 /: ps2)((ps1, p2) => if (ps1.contains(p2)) ps1 else p2 :: ps1)
517+
518+
def mergeEntries(e1: Type, e2: Type): Type = e1 match {
519+
case e1: TypeBounds =>
520+
e2 match {
521+
case e2: TypeBounds => e1 & e2
522+
case _ if e1 contains e2 => e2
523+
case _ => mergeError
524+
}
525+
case _ if e1 eq e2 => e1
526+
case _ => mergeError
527+
}
528+
529+
def mergeError = throw new AssertionError(i"cannot merge $this with $other")
530+
531+
val that = other.asInstanceOf[OrderingConstraint]
532+
new OrderingConstraint(
533+
merge(this.boundsMap, that.boundsMap, mergeEntries),
534+
merge(this.lowerMap, that.lowerMap, mergeParams),
535+
merge(this.upperMap, that.upperMap, mergeParams))
536+
}
537+
498538
override def checkClosed()(implicit ctx: Context): Unit = {
499539
def isFreePolyParam(tp: Type) = tp match {
500540
case PolyParam(binder: GenericType, _) => !contains(binder)

src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,10 @@ class TyperState(r: Reporter) extends DotClass with Showable {
5959
/** Commit state so that it gets propagated to enclosing context */
6060
def commit()(implicit ctx: Context): Unit = unsupported("commit")
6161

62-
/** The typer state has already been committed */
63-
def isCommitted: Boolean = false
64-
65-
/** Optionally, if this is a mutable typerstate, it's creator state */
66-
def parent: Option[TyperState] = None
67-
6862
/** The closest ancestor of this typer state (including possibly this typer state itself)
6963
* which is not yet committed, or which does not have a parent.
7064
*/
71-
def uncommittedAncestor: TyperState =
72-
if (!isCommitted || !parent.isDefined) this
73-
else parent.get.uncommittedAncestor
65+
def uncommittedAncestor: TyperState = this
7466

7567
/** Make type variable instances permanent by assigning to `inst` field if
7668
* type variable instantiation cannot be retracted anymore. Then, remove
@@ -96,7 +88,8 @@ extends TyperState(r) {
9688

9789
override def reporter = myReporter
9890

99-
private var myConstraint: Constraint = previous.constraint
91+
private val previousConstraint = previous.constraint
92+
private var myConstraint: Constraint = previousConstraint
10093

10194
override def constraint = myConstraint
10295
override def constraint_=(c: Constraint)(implicit ctx: Context) = {
@@ -109,7 +102,6 @@ extends TyperState(r) {
109102
override def ephemeral = myEphemeral
110103
override def ephemeral_=(x: Boolean): Unit = { myEphemeral = x }
111104

112-
113105
override def fresh(isCommittable: Boolean): TyperState =
114106
new MutableTyperState(this, new StoreReporter(reporter), isCommittable)
115107

@@ -120,6 +112,11 @@ extends TyperState(r) {
120112
isCommittable &&
121113
(!previous.isInstanceOf[MutableTyperState] || previous.isGlobalCommittable)
122114

115+
private var isCommitted = false
116+
117+
override def uncommittedAncestor: TyperState =
118+
if (isCommitted) previous.uncommittedAncestor else this
119+
123120
/** Commit typer state so that its information is copied into current typer state
124121
* In addition (1) the owning state of undetermined or temporarily instantiated
125122
* type variables changes from this typer state to the current one. (2) Variables
@@ -128,25 +125,20 @@ extends TyperState(r) {
128125
*/
129126
override def commit()(implicit ctx: Context) = {
130127
val targetState = ctx.typerState
131-
assert(targetState eq previous)
132128
assert(isCommittable)
133-
targetState.constraint = constraint
129+
targetState.constraint =
130+
if (targetState.constraint eq previousConstraint) constraint
131+
else targetState.constraint & constraint
134132
constraint foreachTypeVar { tvar =>
135133
if (tvar.owningState eq this)
136134
tvar.owningState = targetState
137135
}
138-
targetState.ephemeral = ephemeral
136+
targetState.ephemeral |= ephemeral
139137
targetState.gc()
140138
reporter.flush()
141-
myIsCommitted = true
139+
isCommitted = true
142140
}
143141

144-
private var myIsCommitted = false
145-
146-
override def isCommitted: Boolean = myIsCommitted
147-
148-
override def parent = Some(previous)
149-
150142
override def gc()(implicit ctx: Context): Unit = {
151143
val toCollect = new mutable.ListBuffer[GenericType]
152144
constraint foreachTypeVar { tvar =>

src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ object Inferencing {
7878
def apply(x: Boolean, tp: Type): Boolean = tp.dealias match {
7979
case _: WildcardType | _: ProtoType =>
8080
false
81-
case tvar: TypeVar if !tvar.isInstantiated =>
81+
case tvar: TypeVar
82+
if !tvar.isInstantiated && ctx.typerState.constraint.contains(tvar) =>
8283
force.appliesTo(tvar) && {
8384
val direction = instDirection(tvar.origin)
8485
if (direction != 0) {

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
895895
}
896896

897897
def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = track("typedSeqLiteral") {
898-
val proto1 = pt.elemType orElse WildcardType
898+
val proto1 = pt.elemType match {
899+
case NoType => WildcardType
900+
case bounds: TypeBounds => WildcardType(bounds)
901+
case elemtp => elemtp
902+
}
899903
val elems1 = tree.elems mapconserve (typed(_, proto1))
900904
val proto2 = // the computed type of the `elemtpt` field
901905
if (!tree.elemtpt.isEmpty) WildcardType

tests/pending/pos/isApplicableSafe.scala

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/pos/isApplicableSafe.scala

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import reflect.ClassTag
2+
3+
// The same problems arise in real arrays.
4+
class A {
5+
6+
class Array[T]
7+
object Array {
8+
def apply[T: ClassTag](xs: T*): Array[T] = ???
9+
def apply(x: Int, xs: Int*): Array[Int] = ???
10+
}
11+
12+
// Any of Array[List[Symbol]], List[Array[Symbol]], or List[List[Symbol]] compile.
13+
var xs: Array[Array[Symbol]] = _
14+
var ys: Array[Map[Symbol, Set[Symbol]]] = _
15+
16+
//xs = Array(Array())
17+
// gives:
18+
//
19+
// isApplicableSafe.scala:15: error: type mismatch:
20+
// found : A.this.Array[Nothing]
21+
// required: A.this.Array[Symbol]
22+
// xs = Array(Array())
23+
//
24+
// Here's the sequence of events that leads to this problem:
25+
//
26+
// 1. the outer Array.apply is overloaded, so we need to typecheck the inner one
27+
// without an expected prototype
28+
//
29+
// 2. The inner Array.apply needs a ClassTag, so we need to instantiate
30+
// its type variable, and the best instantiation is Nothing.
31+
//
32+
// To prevent this, we'd need to do several things:
33+
//
34+
// 1. Pass argument types lazily into the isApplicable call in resolveOverloaded,
35+
// so that we can call constrainResult before any arguments are evaluated.
36+
//
37+
// 2. This is still not enough because the result type is initially an IgnoredProto.
38+
// (because an implicit might have to be inserted around the call, so we cannot
39+
// automatically assume that the call result is a subtype of the expected type).
40+
// Hence, we need to somehow create a closure in constrainResult that does the
41+
// comparison with the real expected result type "on demand".
42+
//
43+
// 3. When instantiating a type variable we need to categorize that some instantiations
44+
// are suspicous (e.g. scalac avoids instantiating to Nothing). In these
45+
// circumstances we should try to excute the delayed constrainResult closures
46+
// in order to get a better instance type.
47+
//
48+
// Quite a lot of work. It's looking really complicated to fix this.
49+
50+
51+
ys = Array(Map(), Map())
52+
53+
val zs = Array(Map())
54+
}

0 commit comments

Comments
 (0)