Skip to content

Commit 1a4f3ac

Browse files
authored
Try to be more subtle when inferring type parameters of class parents (#16896)
2 parents 97641d3 + 79bad18 commit 1a4f3ac

File tree

11 files changed

+69
-44
lines changed

11 files changed

+69
-44
lines changed

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,17 @@ object Inferencing {
310310
}
311311

312312
/** If `tree` has a type lambda type, infer its type parameters by comparing with expected type `pt` */
313-
def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match {
313+
def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match
314314
case tl: TypeLambda =>
315315
val (tl1, tvars) = constrained(tl, tree)
316316
var tree1 = AppliedTypeTree(tree.withType(tl1), tvars)
317317
tree1.tpe <:< pt
318-
fullyDefinedType(tree1.tpe, "template parent", tree.srcPos)
319-
tree1
318+
if isFullyDefined(tree1.tpe, force = ForceDegree.failBottom) then
319+
tree1
320+
else
321+
EmptyTree
320322
case _ =>
321323
tree
322-
}
323324

324325
def isSkolemFree(tp: Type)(using Context): Boolean =
325326
!tp.existsPart(_.isInstanceOf[SkolemType])

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

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,27 +1461,41 @@ class Namer { typer: Typer =>
14611461
* only if parent type contains uninstantiated type parameters.
14621462
*/
14631463
def parentType(parent: untpd.Tree)(using Context): Type =
1464-
if (parent.isType)
1465-
typedAheadType(parent, AnyTypeConstructorProto).tpe
1466-
else {
1467-
val (core, targs) = stripApply(parent) match {
1464+
1465+
def typedParentApplication(parent: untpd.Tree): Type =
1466+
val (core, targs) = stripApply(parent) match
14681467
case TypeApply(core, targs) => (core, targs)
14691468
case core => (core, Nil)
1470-
}
1471-
core match {
1469+
core match
14721470
case Select(New(tpt), nme.CONSTRUCTOR) =>
14731471
val targs1 = targs map (typedAheadType(_))
14741472
val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
14751473
if (ptype.typeParams.isEmpty) ptype
1476-
else {
1474+
else
14771475
if (denot.is(ModuleClass) && denot.sourceModule.isOneOf(GivenOrImplicit))
14781476
missingType(denot.symbol, "parent ")(using creationContext)
14791477
fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.srcPos)
1480-
}
14811478
case _ =>
14821479
UnspecifiedErrorType.assertingErrorsReported
1483-
}
1484-
}
1480+
1481+
def typedParentType(tree: untpd.Tree): tpd.Tree =
1482+
val parentTpt = typer.typedType(parent, AnyTypeConstructorProto)
1483+
val ptpe = parentTpt.tpe
1484+
if ptpe.typeParams.nonEmpty
1485+
&& ptpe.underlyingClassRef(refinementOK = false).exists
1486+
then
1487+
// Try to infer type parameters from a synthetic application.
1488+
// This might yield new info if implicit parameters are resolved.
1489+
// A test case is i16778.scala.
1490+
val app = untpd.Apply(untpd.Select(untpd.New(parentTpt), nme.CONSTRUCTOR), Nil)
1491+
typedParentApplication(app)
1492+
app.getAttachment(TypedAhead).getOrElse(parentTpt)
1493+
else
1494+
parentTpt
1495+
1496+
if parent.isType then typedAhead(parent, typedParentType).tpe
1497+
else typedParentApplication(parent)
1498+
end parentType
14851499

14861500
/** Check parent type tree `parent` for the following well-formedness conditions:
14871501
* (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix)
@@ -1615,7 +1629,7 @@ class Namer { typer: Typer =>
16151629
case Some(ttree) => ttree
16161630
case none =>
16171631
val ttree = typed(tree)
1618-
xtree.putAttachment(TypedAhead, ttree)
1632+
if !ttree.isEmpty then xtree.putAttachment(TypedAhead, ttree)
16191633
ttree
16201634
}
16211635
}

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -843,14 +843,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
843843
isSkolemFree(pt) &&
844844
isEligible(pt.underlyingClassRef(refinementOK = false)))
845845
templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil)
846-
templ1.parents foreach {
847-
case parent: RefTree =>
848-
typedAhead(parent, tree => inferTypeParams(typedType(tree), pt))
849-
case _ =>
850-
}
851-
val x = tpnme.ANON_CLASS
852-
val clsDef = TypeDef(x, templ1).withFlags(Final | Synthetic)
853-
typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt)
846+
for case parent: RefTree <- templ1.parents do
847+
typedAhead(parent, tree => inferTypeParams(typedType(tree), pt))
848+
val anon = tpnme.ANON_CLASS
849+
val clsDef = TypeDef(anon, templ1).withFlags(Final | Synthetic)
850+
typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(anon), Nil)), pt)
854851
case _ =>
855852
var tpt1 = typedType(tree.tpt)
856853
val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol

compiler/test-resources/repl/i7644

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,13 @@ scala> class T extends CanEqual
55
| Cannot extend sealed trait CanEqual in a different source file
66
|
77
| longer explanation available when compiling with `-explain`
8-
-- [E056] Syntax Error: --------------------------------------------------------
9-
1 | class T extends CanEqual
10-
| ^^^^^^^^
11-
| Missing type parameter for CanEqual
12-
2 errors found
8+
1 error found
139
scala> class T extends CanEqual
1410
-- [E112] Syntax Error: --------------------------------------------------------
1511
1 | class T extends CanEqual
1612
| ^
1713
| Cannot extend sealed trait CanEqual in a different source file
1814
|
1915
| longer explanation available when compiling with `-explain`
20-
-- [E056] Syntax Error: --------------------------------------------------------
21-
1 | class T extends CanEqual
22-
| ^^^^^^^^
23-
| Missing type parameter for CanEqual
24-
2 errors found
16+
1 error found
17+

tests/neg/i1643.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
trait T extends Array { // error // error
1+
trait T extends Array { // error
22
def t1(as: String*): Array[String] = { varargs1(as*) } // error
33
def t2(as: String*): Array[String] = { super.varargs1(as*) } // error
44
}
@@ -7,7 +7,7 @@ class C extends Base_1 { // error
77
def c2(as: String*): Array[String] = { super.varargs1(as*) } // error
88
}
99
object Test extends App {
10-
val t = new T {} // error
10+
val t = new T {}
1111
println(t.t1("a", "b").mkString(","))
1212
println(t.t2("a", "b").mkString(","))
1313
val c = new C {}

tests/neg/i4820.scala

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

tests/neg/i4820b.scala

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

tests/neg/i4820c.scala

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

tests/pos/i16778.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
final abstract class ForcedRecompilationToken[T]
2+
3+
object ForcedRecompilationToken {
4+
implicit def materialize: ForcedRecompilationToken["x"] = null.asInstanceOf[ForcedRecompilationToken["x"]]
5+
}
6+
7+
class PluginDef[T](implicit val recompilationToken: ForcedRecompilationToken[T])
8+
9+
object X {
10+
val no = {
11+
final class anon extends PluginDef {} // was: missing type parameters
12+
new anon
13+
}
14+
15+
val bad = new PluginDef {} // was: No given instance
16+
val good = new PluginDef() {} // ok
17+
}
18+
19+
object DependingPlugin {
20+
class NestedDoublePlugin extends PluginDef
21+
object NestedDoublePlugin extends PluginDef
22+
}

tests/pos/i4820.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class Foo[A]
2+
class Bar[A] extends Foo // was error, now expanded to Foo[Nothing]

tests/pos/i4820b.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
trait SetOps[A, +C <: SetOps[A, C]] {
2+
def concat(that: Iterable[A]): C = ???
3+
}
4+
5+
class Set1[A] extends SetOps // ideally should be SetOps[A, Set1[A]], but SetOps[Nothing, Nothin] is inferred

0 commit comments

Comments
 (0)