Skip to content

Commit

Permalink
Normalize match types before computing implicit scopes
Browse files Browse the repository at this point in the history
We now try to reduce match types before computing their contributions to an implicit scope.
This avoids problems where joint and separate compilations gave different results, as in
scala#20071 and scala#15183.

Background: If a match type is reducible to a type R the compiler is free to replace the
match type with R. That should not affect the implicit scope computation. Consequently,
we have to try to reduce match types before computing their contributions to an implicit scope.

scala#20071 and scala#15183 are really the same problem. Both used to compile in sequence and both
gave an implicit not found error when two files were compiled together.

In scala#15183 a weird match type was constructed intentionally, in order to avoid an otherwise
necessary given import. That exploited a bug in the compiler which this PR fixes.
  • Loading branch information
odersky committed Apr 3, 2024
1 parent c8c3bde commit 3ee2473
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 10 deletions.
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -615,9 +615,9 @@ trait ImplicitRunInfo:
if migrateTo3 then false else sym.is(Package) || sym.isPackageObject

/** Is `sym` an anchor type for which givens may exist? Anchor types are classes,
* opaque type aliases, match aliases and abstract types, but not type parameters
* or package objects.
*/
* abstract types, opaque type aliases, and unreducible match aliases, but not type parameters
* or package objects.
*/
private def isAnchor(sym: Symbol) =
sym.isClass && !isExcluded(sym)
|| sym.isOpaqueAlias
Expand All @@ -636,7 +636,7 @@ trait ImplicitRunInfo:
else if implicitScopeCache.contains(t) then parts += t
else
partSeen += t
t.dealias match
t.dealias.normalized match
case t: TypeRef =>
if isAnchor(t.symbol) then
parts += t
Expand All @@ -663,7 +663,6 @@ trait ImplicitRunInfo:
traverseChildren(t)
case t =>
traverseChildren(t)
traverse(t.normalized)
catch case ex: Throwable => handleRecursive("collectParts of", t.show, ex)

def apply(tp: Type): collection.Set[Type] =
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,6 @@ class ImportInfo(symf: Context ?=> Symbol,
assert(myUnimported != null)
myUnimported.uncheckedNN

private val isLanguageImport: Boolean = untpd.languageImport(qualifier).isDefined

private var myUnimported: Symbol | Null = uninitialized

private var featureCache: SimpleIdentityMap[TermName, java.lang.Boolean] = SimpleIdentityMap.empty
Expand Down
96 changes: 96 additions & 0 deletions tests/neg/i15183.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

-- [E172] Type Error: tests/neg/i15183/test_2.scala:2:17 ---------------------------------------------------------------
2 |enum Env derives Decoder: // error
| ^
|No given instance of type Tuple.Map[m.MirroredElemTypes, Decoder] was found for parameter d of given instance derived in object Decoder.
|I found:
|
| Decoder.summonTuple[(Env.Local : Env), ((Env.Sit : Env), (Env.Prod : Env))](
| Decoder.derived[(Env.Local : Env)](
| Env.Local.$asInstanceOf[
| scala.deriving.Mirror.Singleton{
| type MirroredMonoType = (Env.Local : Env); type MirroredType = (Env.Local : Env);
| type MirroredLabel = ("Local" : String)
| }
| ],
| /* missing */summon[Tuple.Map[?1.MirroredElemTypes, Decoder]]),
| ???)
|
|But no implicit values were found that match type Tuple.Map[?1.MirroredElemTypes, Decoder]
|
|where: ?1 is an unknown value of type scala.deriving.Mirror.Singleton{
| type MirroredMonoType = (Env.Local : Env); type MirroredType = (Env.Local : Env);
| type MirroredLabel = ("Local" : String)
|}
|.
|
|Note: a match type could not be fully reduced:
|
| trying to reduce Tuple.Map[m.MirroredElemTypes, Decoder]
| failed since selector m.MirroredElemTypes
| does not match case EmptyTuple => EmptyTuple
| and cannot be shown to be disjoint from it either.
| Therefore, reduction cannot advance to the remaining case
|
| case h *: t => Decoder[h] *: Tuple.Map[t, Decoder]
-- [E172] Type Error: tests/neg/i15183/test_2.scala:5:18 ---------------------------------------------------------------
5 |enum Env2 derives Decoder: // error
| ^
|No given instance of type Tuple.Map[m.MirroredElemTypes, Decoder] was found for parameter d of given instance derived in object Decoder.
|I found:
|
| Decoder.summonTuple[Env2.Local, (Env2.Sit, Env2.Prod)](
| Decoder.derived[Env2.Local](
| Env2.Local.$asInstanceOf[
| scala.deriving.Mirror.Product{
| type MirroredMonoType = Env2.Local; type MirroredType = Env2.Local; type MirroredLabel = ("Local" : String);
| type MirroredElemTypes = EmptyTuple.type; type MirroredElemLabels = EmptyTuple.type
| }
| ],
| /* missing */summon[Tuple.Map[EmptyTuple.type, Decoder]]),
| ???)
|
|But no implicit values were found that match type Tuple.Map[EmptyTuple.type, Decoder].
|
|Note: a match type could not be fully reduced:
|
| trying to reduce Tuple.Map[m.MirroredElemTypes, Decoder]
| failed since selector m.MirroredElemTypes
| does not match case EmptyTuple => EmptyTuple
| and cannot be shown to be disjoint from it either.
| Therefore, reduction cannot advance to the remaining case
|
| case h *: t => Decoder[h] *: Tuple.Map[t, Decoder]
-- [E172] Type Error: tests/neg/i15183/test_2.scala:10:19 --------------------------------------------------------------
10 |enum Shape derives Decoder: // error
| ^
|No given instance of type Tuple.Map[m.MirroredElemTypes, Decoder] was found for parameter d of given instance derived in object Decoder.
|I found:
|
| Decoder.summonTuple[Shape.Rectangle, Shape.Circle *: EmptyTuple.type](
| Decoder.derived[Shape.Rectangle](
| Shape.Rectangle.$asInstanceOf[
| scala.deriving.Mirror.Product{
| type MirroredMonoType = Shape.Rectangle; type MirroredType = Shape.Rectangle;
| type MirroredLabel = ("Rectangle" : String); type MirroredElemTypes = (Double, Double);
| type MirroredElemLabels = (("width" : String), ("height" : String))
| }
| ],
| Decoder.summonTuple[Double, Double *: EmptyTuple.type](Decoder.given_Decoder_Double,
| Decoder.summonTuple[Double, EmptyTuple.type](Decoder.given_Decoder_Double,
| /* missing */summon[Tuple.Map[EmptyTuple.type, Decoder]])
| )
| ),
| ???)
|
|But no implicit values were found that match type Tuple.Map[EmptyTuple.type, Decoder].
|
|Note: a match type could not be fully reduced:
|
| trying to reduce Tuple.Map[m.MirroredElemTypes, Decoder]
| failed since selector m.MirroredElemTypes
| does not match case EmptyTuple => EmptyTuple
| and cannot be shown to be disjoint from it either.
| Therefore, reduction cannot advance to the remaining case
|
| case h *: t => Decoder[h] *: Tuple.Map[t, Decoder]
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Fails in each cases below
enum Env derives Decoder:
enum Env derives Decoder: // error
case Local,Sit,Prod

enum Env2 derives Decoder:
enum Env2 derives Decoder: // error
case Local()
case Sit()
case Prod()

enum Shape derives Decoder:
enum Shape derives Decoder: // error
case Rectangle(width: Double, height: Double)
case Circle(radius: Double)
5 changes: 5 additions & 0 deletions tests/neg/i20071.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

-- [E172] Type Error: tests/neg/i20071/test_2.scala:3:20 ---------------------------------------------------------------
3 |def test: Unit = bar // error
| ^
| No given instance of type M[Decoder] was found for parameter d of method bar
9 changes: 9 additions & 0 deletions tests/neg/i20071/decoder_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// decoder_1.scala
trait Decoder
object Decoder:
given foo: Int = ???

def bar(using d: M[Decoder]): Any = ???

type M[Y] = Y match
case Decoder => Int
3 changes: 3 additions & 0 deletions tests/neg/i20071/test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// test_2.scala
// should fail, does when compiling at the same time as decoder_2.scala
def test: Unit = bar // error
8 changes: 8 additions & 0 deletions tests/pos/i20071.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
trait Decoder
object Decoder:
given foo: Int = ???

type DecoderToInt[Why] >: Int <: Int

def bar[T](using d: DecoderToInt[T]): Any = ???
def test: Unit = bar[Decoder]

0 comments on commit 3ee2473

Please sign in to comment.