Skip to content

Commit c6c06ce

Browse files
committed
Fix #19745 type alias anonymous class instantiation
When creating anonymous classes with `new T {}` where T is a type alias like `type T[X] = SomeClass[X]`, the compiler was failing to infer type parameters, resulting in "is not a class type" errors. The fix adds special handling for HKTypeLambda types in both Typer and Namer to create synthetic constructor applications for type parameter inference, similar to how class parents are handled.
1 parent 7eba9a7 commit c6c06ce

File tree

4 files changed

+66
-4
lines changed

4 files changed

+66
-4
lines changed

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,9 +1629,22 @@ class Namer { typer: Typer =>
16291629
def typedParentType(tree: untpd.Tree): tpd.Tree =
16301630
val parentTpt = typer.typedType(parent, AnyTypeConstructorProto)
16311631
val ptpe = parentTpt.tpe.dealias.etaCollapse
1632-
if ptpe.typeParams.nonEmpty
1633-
&& ptpe.underlyingClassRef(refinementOK = false).exists
1634-
then
1632+
// Check if type params need to be inferred. This applies when:
1633+
// 1. The type has type params AND
1634+
// 2. Either it directly underlies a class (for class/trait refs), OR
1635+
// it's a HKTypeLambda whose result is a class application and params appear in result
1636+
// (the latter handles type aliases like `type Foo[T] = Bar[T]` but not
1637+
// type-lambda-as-result cases like `type Foo[X] = [Z] =>> Bar[X]`)
1638+
val needsTypeParamInference = ptpe.typeParams.nonEmpty && (
1639+
ptpe.underlyingClassRef(refinementOK = false).exists
1640+
|| (ptpe match
1641+
case tl: HKTypeLambda =>
1642+
tl.paramRefs.exists(pref => tl.resType.existsPart(_ == pref))
1643+
&& !tl.resType.isInstanceOf[TypeLambda]
1644+
&& tl.resType.underlyingClassRef(refinementOK = false).exists
1645+
case _ => false)
1646+
)
1647+
if needsTypeParamInference then
16351648
// Try to infer type parameters from a synthetic application.
16361649
// This might yield new info if implicit parameters are resolved.
16371650
// A test case is i16778.scala.

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,30 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
12321232
else tree1
12331233
}
12341234

1235+
/** Type a parent type reference in `new T { ... }` expressions.
1236+
* This handles type aliases that need type parameter inference
1237+
* by creating a synthetic constructor application, similar to
1238+
* how class parents are handled in Namer.
1239+
*/
1240+
def typedNewTemplateParent(tree: untpd.Tree, pt: Type)(using Context): Tree =
1241+
// First type with AnyTypeConstructorProto to check if it's a HKTypeLambda
1242+
val parentTpt = typedType(tree, AnyTypeConstructorProto)
1243+
val ptpe = parentTpt.tpe.dealias
1244+
1245+
ptpe match
1246+
case tl: HKTypeLambda
1247+
// Only handle type aliases where params appear in result (not type-lambda-as-result cases)
1248+
if tl.paramRefs.exists(pref => tl.resType.existsPart(_ == pref))
1249+
&& !tl.resType.isInstanceOf[TypeLambda]
1250+
&& tl.resType.underlyingClassRef(refinementOK = false).exists =>
1251+
// Found a type alias with type params whose result underlies a class.
1252+
// Create a synthetic constructor application to infer type arguments.
1253+
val app = untpd.Apply(untpd.Select(untpd.New(parentTpt), nme.CONSTRUCTOR), Nil)
1254+
val typedApp = typedExpr(app, pt)
1255+
TypeTree(typedApp.tpe).withSpan(tree.span)
1256+
case _ =>
1257+
inferTypeParams(typedType(tree), pt)
1258+
12351259
def typedNew(tree: untpd.New, pt: Type)(using Context): Tree =
12361260
tree.tpt match {
12371261
case templ: untpd.Template =>
@@ -1248,7 +1272,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
12481272
then
12491273
templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil)
12501274
for case parent: RefTree <- templ1.parents do
1251-
typedAhead(parent, tree => inferTypeParams(typedType(tree), pt))
1275+
typedAhead(parent, typedNewTemplateParent(_, pt))
12521276
val anon = tpnme.ANON_CLASS
12531277
val clsDef = TypeDef(anon, templ1).withFlags(Final | Synthetic)
12541278
typed(

tests/pos/i19745.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
final abstract class ForcedRecompilationToken[T]
2+
object ForcedRecompilationToken {
3+
implicit def default: ForcedRecompilationToken["abc"] = null
4+
}
5+
6+
object x {
7+
abstract class GoodNoParens[T](implicit ev: ForcedRecompilationToken[T])
8+
}
9+
type BadNoParens[T] = x.GoodNoParens[T]
10+
11+
object App extends App {
12+
new BadNoParens {}
13+
new BadNoParens() {}
14+
new x.GoodNoParens {}
15+
}

tests/pos/i19745b.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object x {
2+
trait GoodNoParens[T]
3+
}
4+
export x.GoodNoParens as BadNoParens
5+
6+
object App extends App {
7+
new BadNoParens {}
8+
new BadNoParens() {}
9+
new x.GoodNoParens {}
10+
}

0 commit comments

Comments
 (0)