Skip to content

Commit 2b3ba62

Browse files
authored
Merge pull request #12389 from dotty-staging/fix-12373
Fully define qualifier type before giving up in selections
2 parents 319aef6 + f9b6e84 commit 2b3ba62

File tree

4 files changed

+58
-12
lines changed

4 files changed

+58
-12
lines changed

Diff for: compiler/src/dotty/tools/dotc/typer/Inferencing.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ object Inferencing {
8989
}
9090

9191
/** If `tp` is top-level type variable with a lower bound in the current constraint,
92-
* instantiate it from below. We also look for TypeVars whereever their instantiation
93-
* could uncover new type members.
92+
* instantiate it from below. We also look for TypeVars in other places where
93+
* their instantiation could uncover new type members. However that search is best
94+
* effort only. It might miss type variables that appear in structures involving
95+
* alias types and type projections.
9496
*/
9597
def couldInstantiateTypeVar(tp: Type)(using Context): Boolean = tp.dealias match
9698
case tvar: TypeVar

Diff for: compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import config.Printers.typr
1212
import ast.Trees._
1313
import NameOps._
1414
import ProtoTypes._
15-
import Inferencing.couldInstantiateTypeVar
1615
import collection.mutable
1716
import reporting._
1817
import Checking.{checkNoPrivateLeaks, checkNoWildcard}

Diff for: compiler/src/dotty/tools/dotc/typer/Typer.scala

+14-9
Original file line numberDiff line numberDiff line change
@@ -574,13 +574,17 @@ class Typer extends Namer
574574
checkLegalValue(select, pt)
575575
ConstFold(select)
576576
else if couldInstantiateTypeVar(qual.tpe.widen) then
577-
// try again with more defined qualifier type
577+
// there's a simply visible type variable in the result; try again with a more defined qualifier type
578+
// There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`,
579+
// but that is done only after we search for extension methods or conversions.
578580
typedSelect(tree, pt, qual)
579581
else
580582
val tree1 = tryExtensionOrConversion(
581-
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, privateOK = true)
583+
tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true)
582584
if !tree1.isEmpty then
583585
tree1
586+
else if canDefineFurther(qual.tpe.widen) then
587+
typedSelect(tree, pt, qual)
584588
else if qual.tpe.derivesFrom(defn.DynamicClass)
585589
&& selName.isTermName && !isDynamicExpansion(tree)
586590
then
@@ -3038,7 +3042,7 @@ class Typer extends Namer
30383042
if selProto.isMatchedBy(qual.tpe) then None
30393043
else
30403044
tryEither {
3041-
val tree1 = tryExtensionOrConversion(tree, pt, pt, qual, locked, NoViewsAllowed, privateOK = false)
3045+
val tree1 = tryExtensionOrConversion(tree, pt, pt, qual, locked, NoViewsAllowed, inSelect = false)
30423046
if tree1.isEmpty then None
30433047
else Some(adapt(tree1, pt, locked))
30443048
} { (_, _) => None
@@ -3052,10 +3056,10 @@ class Typer extends Namer
30523056
* @return The converted tree, or `EmptyTree` is not successful.
30533057
*/
30543058
def tryExtensionOrConversion
3055-
(tree: untpd.Select, pt: Type, mbrProto: Type, qual: Tree, locked: TypeVars, compat: Compatibility, privateOK: Boolean)
3059+
(tree: untpd.Select, pt: Type, mbrProto: Type, qual: Tree, locked: TypeVars, compat: Compatibility, inSelect: Boolean)
30563060
(using Context): Tree =
30573061

3058-
def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK)
3062+
def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect)
30593063

30603064
def tryExtension(using Context): Tree =
30613065
findRef(tree.name, WildcardType, ExtensionMethod, EmptyFlags, qual.srcPos) match
@@ -3093,12 +3097,13 @@ class Typer extends Namer
30933097
return typedSelect(tree, pt, found)
30943098
case failure: SearchFailure =>
30953099
if failure.isAmbiguous then
3096-
return (
3097-
if canDefineFurther(qual.tpe.widen) then
3098-
tryExtensionOrConversion(tree, pt, mbrProto, qual, locked, compat, privateOK)
3100+
return
3101+
if !inSelect // in a selection we will do the canDefineFurther afterwards
3102+
&& canDefineFurther(qual.tpe.widen)
3103+
then
3104+
tryExtensionOrConversion(tree, pt, mbrProto, qual, locked, compat, inSelect)
30993105
else
31003106
err.typeMismatch(qual, selProto, failure.reason) // TODO: report NotAMember instead, but need to be aware of failure
3101-
)
31023107
rememberSearchFailure(qual, failure)
31033108
}
31043109
catch case ex: TypeError => nestedFailure(ex)

Diff for: tests/pos/i12373.scala

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
sealed case class Column[A](name: String)
2+
3+
sealed trait ColumnSet {
4+
type Append[That <: ColumnSet] <: ColumnSet
5+
def ++[That <: ColumnSet](that: That): Append[That]
6+
}
7+
8+
object ColumnSet {
9+
type Empty = Empty.type
10+
type Singleton[A] = Cons[A, Empty]
11+
12+
case object Empty extends ColumnSet {
13+
type Append[That <: ColumnSet] = That
14+
override def ++[That <: ColumnSet](that: That): Append[That] = that
15+
}
16+
17+
sealed case class Cons[A, B <: ColumnSet](head: Column[A], tail: B) extends ColumnSet { self =>
18+
type Append[That <: ColumnSet] = Cons[A, tail.Append[That]]
19+
override def ++[That <: ColumnSet](that: That): Append[That] = Cons(head, tail ++ that)
20+
}
21+
22+
def long(name: String): Singleton[Long] = Cons(Column[Long](name), Empty)
23+
def string(name: String): Singleton[String] = Cons(Column[String](name), Empty)
24+
}
25+
26+
object Example {
27+
import ColumnSet._
28+
val schema0 = long("id") ++ string("first_name")
29+
30+
// inferred type 3.0.0-RC3: Singleton[Long]#Append[Cons[String, Empty]]#Append[Singleton[String]]
31+
// inferred type 2.13.5 : Cons[Long,Cons[String,Singleton[String]]]
32+
val schema1 = long("id") ++ string("first_name") ++ string("last_name")
33+
34+
// inferred type 3.0.0-RC3: error
35+
// inferred type 2.13.5 : Cons[Long,Cons[String,Cons[String,Singleton[Long]]]]
36+
val schema2 = long("id") ++ string("first_name") ++ string("last_name") ++ long("age")
37+
38+
// inferred type 3.0.0-RC3: Singleton[Long]#Append[Cons[String, Empty]]#Append[Singleton[String]]#Append[Cons[Long, Empty]]
39+
val schema3 = ((long("id") ++ string("first_name") ++ string("last_name")): Singleton[Long]#Append[ColumnSet.Cons[String, ColumnSet.Empty]]#Append[ColumnSet.Singleton[String]]) ++ long("age")
40+
}

0 commit comments

Comments
 (0)