Skip to content

Commit b77c24c

Browse files
authored
Merge pull request #1918 from dotty-staging/fix-implicits-generic
Improvements to implicits
2 parents da7d723 + 56d32fa commit b77c24c

15 files changed

+574
-22
lines changed

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,12 @@ trait ConstraintHandling {
7979
if (Config.failOnInstantiationToNothing) assert(false, msg)
8080
else ctx.log(msg)
8181
}
82-
constr.println(i"adding $description")
82+
constr.println(i"adding $description in ${ctx.typerState.hashesStr}")
8383
val lower = constraint.lower(param)
8484
val res =
8585
addOneBound(param, bound, isUpper = true) &&
8686
lower.forall(addOneBound(_, bound, isUpper = true))
87-
constr.println(i"added $description = $res")
87+
constr.println(i"added $description = $res in ${ctx.typerState.hashesStr}")
8888
res
8989
}
9090

@@ -95,7 +95,7 @@ trait ConstraintHandling {
9595
val res =
9696
addOneBound(param, bound, isUpper = false) &&
9797
upper.forall(addOneBound(_, bound, isUpper = false))
98-
constr.println(i"added $description = $res")
98+
constr.println(i"added $description = $res in ${ctx.typerState.hashesStr}")
9999
res
100100
}
101101

@@ -108,12 +108,12 @@ trait ConstraintHandling {
108108
val up2 = p2 :: constraint.exclusiveUpper(p2, p1)
109109
val lo1 = constraint.nonParamBounds(p1).lo
110110
val hi2 = constraint.nonParamBounds(p2).hi
111-
constr.println(i"adding $description down1 = $down1, up2 = $up2")
111+
constr.println(i"adding $description down1 = $down1, up2 = $up2 ${ctx.typerState.hashesStr}")
112112
constraint = constraint.addLess(p1, p2)
113113
down1.forall(addOneBound(_, hi2, isUpper = true)) &&
114114
up2.forall(addOneBound(_, lo1, isUpper = false))
115115
}
116-
constr.println(i"added $description = $res")
116+
constr.println(i"added $description = $res ${ctx.typerState.hashesStr}")
117117
res
118118
}
119119

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ class TyperState(r: Reporter) extends DotClass with Showable {
7979
def tryWithFallback[T](op: => T)(fallback: => T)(implicit ctx: Context): T = unsupported("tryWithFallBack")
8080

8181
override def toText(printer: Printer): Text = "ImmutableTyperState"
82+
83+
/** A string showing the hashes of all nested mutable typerstates */
84+
def hashesStr: String = ""
8285
}
8386

8487
class MutableTyperState(previous: TyperState, r: Reporter, override val isCommittable: Boolean)
@@ -207,4 +210,7 @@ extends TyperState(r) {
207210
}
208211

209212
override def toText(printer: Printer): Text = constraint.toText(printer)
213+
214+
override def hashesStr: String = hashCode.toString + " -> " + previous.hashesStr
215+
210216
}

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,12 +1765,8 @@ object Parsers {
17651765
TypeTree() // XX-METHOD-INFER
17661766
} else {
17671767
accept(COLON)
1768-
if (in.token == ARROW) {
1769-
if (owner.isTypeName && !(mods is Local))
1770-
syntaxError(s"${if (mods is Mutable) "`var'" else "`val'"} parameters may not be call-by-name")
1771-
else if (imods.hasFlags)
1772-
syntaxError("implicit parameters may not be call-by-name")
1773-
}
1768+
if (in.token == ARROW && owner.isTypeName && !(mods is Local))
1769+
syntaxError(s"${if (mods is Mutable) "`var'" else "`val'"} parameters may not be call-by-name")
17741770
paramType()
17751771
}
17761772
val default =

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._, De
66
import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation
77
import StdNames.{nme, tpnme}
88
import ast.Trees._, ast._
9+
import typer.Implicits._
910
import config.Config
1011
import java.lang.Integer.toOctalString
1112
import config.Config.summarizeDepth
@@ -484,6 +485,23 @@ class PlainPrinter(_ctx: Context) extends Printer {
484485
}
485486
}.close // todo: override in refined printer
486487

488+
def toText(result: SearchResult): Text = result match {
489+
case result: SearchSuccess =>
490+
"SearchSuccess: " ~ toText(result.ref) ~ " via " ~ toText(result.tree)
491+
case result: NonMatchingImplicit =>
492+
"NoImplicitMatches"
493+
case result: DivergingImplicit =>
494+
"Diverging Implicit"
495+
case result: ShadowedImplicit =>
496+
"Shadowed Implicit"
497+
case result: FailedImplicit =>
498+
"Failed Implicit"
499+
case result: AmbiguousImplicits =>
500+
"Ambiguous Implicit: " ~ toText(result.alt1) ~ " and " ~ toText(result.alt2)
501+
case _ =>
502+
"?Unknown Implicit Result?"
503+
}
504+
487505
private var maxSummarized = Int.MaxValue
488506

489507
def summarized[T](depth: Int)(op: => T): T = {

compiler/src/dotty/tools/dotc/printing/Printer.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import core._
55
import Texts._, ast.Trees._
66
import Types.Type, Symbols.Symbol, Contexts.Context, Scopes.Scope, Constants.Constant,
77
Names.Name, Denotations._, Annotations.Annotation
8+
import typer.Implicits.SearchResult
89

910
/** The base class of all printers
1011
*/
@@ -94,7 +95,10 @@ abstract class Printer {
9495
/** Textual representation of tree */
9596
def toText[T >: Untyped](tree: Tree[T]): Text
9697

97-
/** Perform string or text-producing operation `op` so that only a
98+
/** Textual representation of implicit search result */
99+
def toText(result: SearchResult): Text
100+
101+
/** Perform string or text-producing operation `op` so that only a
98102
* summarized text with given recursion depth is shown
99103
*/
100104
def summarized[T](depth: Int)(op: => T): T

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import core._
66
import ast.{Trees, untpd, tpd, TreeInfo}
77
import util.Positions._
88
import util.Stats.{track, record, monitored}
9-
import printing.Showable
9+
import printing.{Showable, Printer}
10+
import printing.Texts._
1011
import Contexts._
1112
import Types._
1213
import Flags._
@@ -219,14 +220,16 @@ object Implicits {
219220
}
220221

221222
/** The result of an implicit search */
222-
abstract class SearchResult
223+
abstract class SearchResult extends Showable {
224+
def toText(printer: Printer): Text = printer.toText(this)
225+
}
223226

224227
/** A successful search
225228
* @param ref The implicit reference that succeeded
226229
* @param tree The typed tree that needs to be inserted
227230
* @param ctx The context after the implicit search
228231
*/
229-
case class SearchSuccess(tree: tpd.Tree, ref: TermRef, level: Int, tstate: TyperState) extends SearchResult {
232+
case class SearchSuccess(tree: tpd.Tree, ref: TermRef, level: Int, tstate: TyperState) extends SearchResult with Showable {
230233
override def toString = s"SearchSuccess($tree, $ref, $level)"
231234
}
232235

@@ -256,7 +259,7 @@ object Implicits {
256259
}
257260

258261
/** An ambiguous implicits failure */
259-
class AmbiguousImplicits(alt1: TermRef, alt2: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
262+
class AmbiguousImplicits(val alt1: TermRef, val alt2: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
260263
def explanation(implicit ctx: Context): String =
261264
em"both ${err.refStr(alt1)} and ${err.refStr(alt2)} $qualify"
262265
override def postscript(implicit ctx: Context) =
@@ -380,7 +383,9 @@ trait ImplicitRunInfo { self: RunInfo =>
380383
EmptyTermRefSet // on the other hand, the refs of `tp` are now not accurate, so `tp` is marked incomplete.
381384
} else {
382385
seen += t
383-
iscope(t).companionRefs
386+
val is = iscope(t)
387+
if (!implicitScopeCache.contains(t)) incomplete += tp
388+
is.companionRefs
384389
}
385390
}
386391

@@ -436,10 +441,8 @@ trait ImplicitRunInfo { self: RunInfo =>
436441
if (ctx.typerState.ephemeral)
437442
record("ephemeral cache miss: implicitScope")
438443
else if (canCache &&
439-
((tp eq rootTp) || // first type traversed is always cached
440-
!incomplete.contains(tp) && // other types are cached if they are not incomplete
441-
result.companionRefs.forall( // and all their companion refs are cached
442-
implicitScopeCache.contains)))
444+
((tp eq rootTp) || // first type traversed is always cached
445+
!incomplete.contains(tp))) // other types are cached if they are not incomplete
443446
implicitScopeCache(tp) = result
444447
result
445448
}
@@ -604,6 +607,7 @@ trait Implicits { self: Typer =>
604607
result match {
605608
case result: SearchSuccess =>
606609
result.tstate.commit()
610+
implicits.println(i"committing ${result.tstate.constraint} yielding ${ctx.typerState.constraint} ${ctx.typerState.hashesStr}")
607611
result
608612
case result: AmbiguousImplicits =>
609613
val deepPt = pt.deepenProto

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1879,7 +1879,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
18791879
val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) =>
18801880
def implicitArgError(msg: String => String) =
18811881
errors += (() => msg(em"parameter $pname of $methodStr"))
1882-
inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
1882+
if (errors.nonEmpty) EmptyTree
1883+
else inferImplicitArg(formal.widenExpr, implicitArgError, tree.pos.endPos)
18831884
}
18841885
if (errors.nonEmpty) {
18851886
// If there are several arguments, some arguments might already

tests/run/generic/Color.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package generic
2+
3+
import Shapes._
4+
5+
/** enum Color {
6+
* case Red
7+
* case Green
8+
* case Blue
9+
* }
10+
*/
11+
sealed trait Color extends Enum
12+
13+
object Color extends EnumValues[Color](3) {
14+
15+
private def $new(tag: Int, name: String) = new Color {
16+
def enumTag = tag
17+
override def toString = name
18+
registerEnumValue(this)
19+
}
20+
21+
val Red: Color = $new(0, "Red")
22+
val Green: Color = $new(1, "Green")
23+
val Blue: Color = $new(2, "Blue")
24+
25+
implicit val ColorShape: Color `shaped` EnumValue[Color] =
26+
new (Color `shaped` EnumValue[Color]) {
27+
def toShape(x: Color) = EnumValue(x.enumTag)
28+
def fromShape(x: EnumValue[Color]) = Color.value(x.tag)
29+
}
30+
}

tests/run/generic/Enum.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package generic
2+
3+
import Shapes.Singleton
4+
5+
trait Enum {
6+
def enumTag: Int
7+
}
8+
9+
trait FiniteEnum extends Enum
10+
11+
abstract class EnumValues[E <: Enum](numVals: Int) {
12+
private var myValues = new Array[AnyRef](numVals)
13+
14+
def registerEnumValue(v: E) =
15+
myValues(v.enumTag) = v
16+
17+
def value: IndexedSeq[E] = (myValues: IndexedSeq[AnyRef]).asInstanceOf[IndexedSeq[E]]
18+
}

tests/run/generic/List.scala

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package generic
2+
3+
import Shapes._
4+
5+
/** enum List[T] {
6+
* case Cons(x: T, xs: List[T])
7+
* case Nil()
8+
* }
9+
*/
10+
sealed trait List0[T] extends Enum
11+
object List0 {
12+
abstract case class Cons[T](hd: T, tl: List0[T]) extends List0[T] {
13+
def enumTag = 0
14+
}
15+
object Cons {
16+
def apply[T](x: T, xs: List0[T]): List0[T] = new Cons(x, xs) {}
17+
implicit def ConsShape[T]: Cons[T] `shaped` Prod[T, List0[T]] =
18+
new (Cons[T] `shaped` Prod[T, List0[T]]) {
19+
def toShape(x: Cons[T]) = Prod(x.hd, x.tl)
20+
def fromShape(p: Prod[T, List0[T]]) = new Cons(p.fst, p.snd) {}
21+
}
22+
}
23+
24+
abstract case class Nil[T]() extends List0[T] {
25+
def enumTag = 1
26+
}
27+
object Nil {
28+
def apply[T](): List0[T] = new Nil[T]() {}
29+
implicit def NilShape[T]: Nil[T] `shaped` Unit =
30+
new (Nil[T] `shaped` Unit) {
31+
def toShape(x: Nil[T]) = ()
32+
def fromShape(x: Unit) = new Nil[T]() {}
33+
}
34+
}
35+
36+
implicit def List0Shape[T]: List0[T] `shaped` Sum[Cons[T], Nil[T]] =
37+
new (List0[T] `shaped` Sum[Cons[T], Nil[T]]) {
38+
def toShape(x: List0[T]) = x match {
39+
case x: Cons[T] => Fst(x)
40+
case x: Nil[T] => Snd(x)
41+
}
42+
def fromShape(x: Sum[Cons[T], Nil[T]]) = x match {
43+
case Fst(c) => c
44+
case Snd(n) => n
45+
}
46+
}
47+
}
48+
49+
/** enum List[T] {
50+
* case Cons(x: T, xs: List[T])
51+
* case Nil extends List[Nothing]
52+
* }
53+
*/
54+
sealed trait List[+T] extends Enum
55+
object List {
56+
abstract case class Cons[T](hd: T, tl: List[T]) extends List[T] {
57+
def enumTag = 0
58+
}
59+
object Cons {
60+
def apply[T](x: T, xs: List[T]): List[T] = new Cons(x, xs) {}
61+
type Shape[T] = Prod[T, List[T]]
62+
implicit def ConsShape[T]: Cons[T] `shaped` Shape[T] =
63+
new (Cons[T] `shaped` Shape[T]) {
64+
def toShape(x: Cons[T]) = Prod(x.hd, x.tl)
65+
def fromShape(p: Shape[T]) = new Cons(p.fst, p.snd) {}
66+
}
67+
}
68+
69+
val Nil = new List[Nothing] {
70+
def enumTag = 1
71+
override def toString = "Nil"
72+
}
73+
74+
implicit def NilSingleton: Singleton[Nil.type] = new Singleton[Nil.type](Nil)
75+
76+
type Shape[T] = Sum[Cons[T], Nil.type]
77+
78+
implicit def ListShape[T]: List[T] `unfolds` Shape[T] =
79+
new (List[T] `shaped` Shape[T]) {
80+
def toShape(x: List[T]) = x match {
81+
case x: Cons[T] => Fst(x)
82+
case Nil => Snd(Nil)
83+
}
84+
def fromShape(x: Shape[T]): List[T] = x match {
85+
case Fst(c) => c
86+
case Snd(n) => n
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)