Skip to content

Commit

Permalink
Try to be more subtle when inferring type parameters of class parents (
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand authored Feb 13, 2023
2 parents 97641d3 + 79bad18 commit 1a4f3ac
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 44 deletions.
9 changes: 5 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,17 @@ object Inferencing {
}

/** If `tree` has a type lambda type, infer its type parameters by comparing with expected type `pt` */
def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match {
def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match
case tl: TypeLambda =>
val (tl1, tvars) = constrained(tl, tree)
var tree1 = AppliedTypeTree(tree.withType(tl1), tvars)
tree1.tpe <:< pt
fullyDefinedType(tree1.tpe, "template parent", tree.srcPos)
tree1
if isFullyDefined(tree1.tpe, force = ForceDegree.failBottom) then
tree1
else
EmptyTree
case _ =>
tree
}

def isSkolemFree(tp: Type)(using Context): Boolean =
!tp.existsPart(_.isInstanceOf[SkolemType])
Expand Down
36 changes: 25 additions & 11 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1461,27 +1461,41 @@ class Namer { typer: Typer =>
* only if parent type contains uninstantiated type parameters.
*/
def parentType(parent: untpd.Tree)(using Context): Type =
if (parent.isType)
typedAheadType(parent, AnyTypeConstructorProto).tpe
else {
val (core, targs) = stripApply(parent) match {

def typedParentApplication(parent: untpd.Tree): Type =
val (core, targs) = stripApply(parent) match
case TypeApply(core, targs) => (core, targs)
case core => (core, Nil)
}
core match {
core match
case Select(New(tpt), nme.CONSTRUCTOR) =>
val targs1 = targs map (typedAheadType(_))
val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
if (ptype.typeParams.isEmpty) ptype
else {
else
if (denot.is(ModuleClass) && denot.sourceModule.isOneOf(GivenOrImplicit))
missingType(denot.symbol, "parent ")(using creationContext)
fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.srcPos)
}
case _ =>
UnspecifiedErrorType.assertingErrorsReported
}
}

def typedParentType(tree: untpd.Tree): tpd.Tree =
val parentTpt = typer.typedType(parent, AnyTypeConstructorProto)
val ptpe = parentTpt.tpe
if ptpe.typeParams.nonEmpty
&& ptpe.underlyingClassRef(refinementOK = false).exists
then
// Try to infer type parameters from a synthetic application.
// This might yield new info if implicit parameters are resolved.
// A test case is i16778.scala.
val app = untpd.Apply(untpd.Select(untpd.New(parentTpt), nme.CONSTRUCTOR), Nil)
typedParentApplication(app)
app.getAttachment(TypedAhead).getOrElse(parentTpt)
else
parentTpt

if parent.isType then typedAhead(parent, typedParentType).tpe
else typedParentApplication(parent)
end parentType

/** Check parent type tree `parent` for the following well-formedness conditions:
* (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix)
Expand Down Expand Up @@ -1615,7 +1629,7 @@ class Namer { typer: Typer =>
case Some(ttree) => ttree
case none =>
val ttree = typed(tree)
xtree.putAttachment(TypedAhead, ttree)
if !ttree.isEmpty then xtree.putAttachment(TypedAhead, ttree)
ttree
}
}
Expand Down
13 changes: 5 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -843,14 +843,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
isSkolemFree(pt) &&
isEligible(pt.underlyingClassRef(refinementOK = false)))
templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil)
templ1.parents foreach {
case parent: RefTree =>
typedAhead(parent, tree => inferTypeParams(typedType(tree), pt))
case _ =>
}
val x = tpnme.ANON_CLASS
val clsDef = TypeDef(x, templ1).withFlags(Final | Synthetic)
typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt)
for case parent: RefTree <- templ1.parents do
typedAhead(parent, tree => inferTypeParams(typedType(tree), pt))
val anon = tpnme.ANON_CLASS
val clsDef = TypeDef(anon, templ1).withFlags(Final | Synthetic)
typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(anon), Nil)), pt)
case _ =>
var tpt1 = typedType(tree.tpt)
val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol
Expand Down
13 changes: 3 additions & 10 deletions compiler/test-resources/repl/i7644
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,13 @@ scala> class T extends CanEqual
| Cannot extend sealed trait CanEqual in a different source file
|
| longer explanation available when compiling with `-explain`
-- [E056] Syntax Error: --------------------------------------------------------
1 | class T extends CanEqual
| ^^^^^^^^
| Missing type parameter for CanEqual
2 errors found
1 error found
scala> class T extends CanEqual
-- [E112] Syntax Error: --------------------------------------------------------
1 | class T extends CanEqual
| ^
| Cannot extend sealed trait CanEqual in a different source file
|
| longer explanation available when compiling with `-explain`
-- [E056] Syntax Error: --------------------------------------------------------
1 | class T extends CanEqual
| ^^^^^^^^
| Missing type parameter for CanEqual
2 errors found
1 error found

4 changes: 2 additions & 2 deletions tests/neg/i1643.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
trait T extends Array { // error // error
trait T extends Array { // error
def t1(as: String*): Array[String] = { varargs1(as*) } // error
def t2(as: String*): Array[String] = { super.varargs1(as*) } // error
}
Expand All @@ -7,7 +7,7 @@ class C extends Base_1 { // error
def c2(as: String*): Array[String] = { super.varargs1(as*) } // error
}
object Test extends App {
val t = new T {} // error
val t = new T {}
println(t.t1("a", "b").mkString(","))
println(t.t2("a", "b").mkString(","))
val c = new C {}
Expand Down
2 changes: 0 additions & 2 deletions tests/neg/i4820.scala

This file was deleted.

5 changes: 0 additions & 5 deletions tests/neg/i4820b.scala

This file was deleted.

2 changes: 0 additions & 2 deletions tests/neg/i4820c.scala

This file was deleted.

22 changes: 22 additions & 0 deletions tests/pos/i16778.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
final abstract class ForcedRecompilationToken[T]

object ForcedRecompilationToken {
implicit def materialize: ForcedRecompilationToken["x"] = null.asInstanceOf[ForcedRecompilationToken["x"]]
}

class PluginDef[T](implicit val recompilationToken: ForcedRecompilationToken[T])

object X {
val no = {
final class anon extends PluginDef {} // was: missing type parameters
new anon
}

val bad = new PluginDef {} // was: No given instance
val good = new PluginDef() {} // ok
}

object DependingPlugin {
class NestedDoublePlugin extends PluginDef
object NestedDoublePlugin extends PluginDef
}
2 changes: 2 additions & 0 deletions tests/pos/i4820.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class Foo[A]
class Bar[A] extends Foo // was error, now expanded to Foo[Nothing]
5 changes: 5 additions & 0 deletions tests/pos/i4820b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trait SetOps[A, +C <: SetOps[A, C]] {
def concat(that: Iterable[A]): C = ???
}

class Set1[A] extends SetOps // ideally should be SetOps[A, Set1[A]], but SetOps[Nothing, Nothin] is inferred

0 comments on commit 1a4f3ac

Please sign in to comment.