diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index af4174423cbc..c31477d0270e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -307,16 +307,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]) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6f85efb0fc8a..af6673086339 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1453,27 +1453,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) @@ -1607,7 +1621,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 } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a87d6dd7e703..5752409c51c1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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 diff --git a/compiler/test-resources/repl/i7644 b/compiler/test-resources/repl/i7644 index 8ceaf8b00804..786823073470 100644 --- a/compiler/test-resources/repl/i7644 +++ b/compiler/test-resources/repl/i7644 @@ -5,11 +5,7 @@ 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 @@ -17,8 +13,5 @@ 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 + diff --git a/tests/neg/i1643.scala b/tests/neg/i1643.scala index a10422de6eab..1745539d73f5 100644 --- a/tests/neg/i1643.scala +++ b/tests/neg/i1643.scala @@ -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 } @@ -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 {} diff --git a/tests/neg/i4820.scala b/tests/neg/i4820.scala deleted file mode 100644 index e19183b17b14..000000000000 --- a/tests/neg/i4820.scala +++ /dev/null @@ -1,2 +0,0 @@ -class Foo[A] -class Bar[A] extends Foo // error diff --git a/tests/neg/i4820b.scala b/tests/neg/i4820b.scala deleted file mode 100644 index 4a7b3da3fb1b..000000000000 --- a/tests/neg/i4820b.scala +++ /dev/null @@ -1,5 +0,0 @@ -trait SetOps[A, +C <: SetOps[A, C]] { - def concat(that: Iterable[A]): C = ??? -} - -class Set1[A] extends SetOps // error: should be SetOps[A, Set1[A]] diff --git a/tests/neg/i4820c.scala b/tests/neg/i4820c.scala deleted file mode 100644 index 6956b23363b5..000000000000 --- a/tests/neg/i4820c.scala +++ /dev/null @@ -1,2 +0,0 @@ -trait Foo[A] -class Bar[A] extends Foo // error \ No newline at end of file diff --git a/tests/pos/i16778.scala b/tests/pos/i16778.scala new file mode 100644 index 000000000000..426f3c86c0bd --- /dev/null +++ b/tests/pos/i16778.scala @@ -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 +} diff --git a/tests/pos/i4820.scala b/tests/pos/i4820.scala new file mode 100644 index 000000000000..8d368d150a00 --- /dev/null +++ b/tests/pos/i4820.scala @@ -0,0 +1,2 @@ +class Foo[A] +class Bar[A] extends Foo // was error, now expanded to Foo[Nothing] diff --git a/tests/pos/i4820b.scala b/tests/pos/i4820b.scala new file mode 100644 index 000000000000..a1c7d54f0c76 --- /dev/null +++ b/tests/pos/i4820b.scala @@ -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