diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 8c22bf5b9293..e213e1bee01f 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -121,6 +121,7 @@ class Compiler { new ElimStaticThis, // Replace `this` references to static objects by global identifiers new CountOuterAccesses) :: // Identify outer accessors that can be dropped List(new DropOuterAccessors, // Drop unused outer accessors + new CheckNoSuperThis, // Check that supercalls don't contain references to `this` new Flatten, // Lift all inner classes to package scope new RenameLifted, // Renames lifted classes to local numbering scheme new TransformWildcards, // Replace wildcards with default values diff --git a/compiler/src/dotty/tools/dotc/transform/CheckNoSuperThis.scala b/compiler/src/dotty/tools/dotc/transform/CheckNoSuperThis.scala new file mode 100644 index 000000000000..d84fcd9ca11e --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/CheckNoSuperThis.scala @@ -0,0 +1,50 @@ +package dotty.tools.dotc +package transform + +import core.* +import MegaPhase.MiniPhase +import Contexts.*, Types.*, Symbols.*, SymDenotations.*, Flags.* +import ast.* +import Trees.* +import Decorators.* + +import annotation.threadUnsafe + +object CheckNoSuperThis: + val name: String = "checkNoSuperThis" + +/** Checks that super and this calls do not pass `this` as (part of) an argument. */ +class CheckNoSuperThis extends MiniPhase: + thisPhase => + import tpd._ + + override def phaseName: String = CheckNoSuperThis.name + + override def runsAfterGroupsOf: Set[String] = Set(Constructors.name) + + override def transformDefDef(mdef: DefDef)(using Context): DefDef = + if mdef.symbol.isClassConstructor then + mdef.rhs match + case Block(stats, _) => splitAtSuper(stats) match + case (Apply(_, superArgs) :: _, _) => + val cls = mdef.symbol.owner + def fail(t: Tree) = + report.error(em"super constructor cannot be passed a self reference $t unless parameter is declared by-name", t.srcPos) + for arg <- superArgs do + arg.foreachSubTree { + case t: This if t.symbol == cls => + fail(t) + case t: RefTree => t.tpe match + case tpe @ TermRef(prefix, _) + if (prefix == cls.thisType + || cls.is(Module) + && (prefix.termSymbol == cls.sourceModule || tpe.symbol == cls.sourceModule) + ) && !tpe.symbol.is(JavaStatic) => fail(t) + case _ => + case _ => + } + case _ => + case _ => + mdef + +end CheckNoSuperThis \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index edd1016d3246..b0fdd9d9ec62 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -991,18 +991,6 @@ trait Checking { report.error(i"""$called is already implemented by super${caller.superClass}, |its constructor cannot be called again""", call.srcPos) - if (caller.is(Module)) { - val traverser = new TreeTraverser { - def traverse(tree: Tree)(using Context) = tree match { - case tree: RefTree if tree.isTerm && (tree.tpe.classSymbol eq caller) => - report.error("super constructor cannot be passed a self reference", tree.srcPos) - case _ => - traverseChildren(tree) - } - } - traverser.traverse(call) - } - // Check that constructor call is of the form _.(args1)...(argsN). // This guards against calls resulting from inserted implicits or applies. def checkLegalConstructorCall(tree: Tree, encl: Tree, kind: String): Unit = tree match { diff --git a/tests/neg/i11045.scala b/tests/neg/i11045.scala new file mode 100644 index 000000000000..1288d7ebb42a --- /dev/null +++ b/tests/neg/i11045.scala @@ -0,0 +1,5 @@ +abstract class Foo(x: Any) +class Boom(var x: Unit, y: Unit) extends Foo((x: Int) => x) // error: super constructor cannot be passed a self reference +@main def Test = + Boom((), ()) + diff --git a/tests/neg/i11208.scala b/tests/neg/i11208.scala new file mode 100644 index 000000000000..c00e16b96993 --- /dev/null +++ b/tests/neg/i11208.scala @@ -0,0 +1,8 @@ +import scala.reflect.ClassTag + +@main def run = println(Foo) + +abstract class Bar[T](implicit val thisClassTag: ClassTag[T]) + +class Foo +object Foo extends Bar[Foo] // error \ No newline at end of file diff --git a/tests/neg/i11208a.scala b/tests/neg/i11208a.scala new file mode 100644 index 000000000000..8b2f0917038c --- /dev/null +++ b/tests/neg/i11208a.scala @@ -0,0 +1,6 @@ +class Foo(implicit val foo: Foo) + +object Test extends App { + implicit object Bar extends Foo // error + Bar.foo +} \ No newline at end of file diff --git a/tests/neg/i12557.scala b/tests/neg/i12557.scala new file mode 100644 index 000000000000..7e2d301e2662 --- /dev/null +++ b/tests/neg/i12557.scala @@ -0,0 +1,14 @@ +package example + +abstract class X[P <: Product](using val m: scala.deriving.Mirror.ProductOf[P]) { + def unapply(p: P): m.MirroredElemTypes = ??? +} + +case class A(a: Int) +object A extends X[A] // error + +object Main { + def main(args: Array[String]): Unit = { + A.unapply(A(2)) + } +} diff --git a/tests/pos/i11045.scala b/tests/pos/i11045.scala deleted file mode 100644 index da5d66a7a633..000000000000 --- a/tests/pos/i11045.scala +++ /dev/null @@ -1,2 +0,0 @@ -abstract class Foo(x: Any) -class Boom(var x: Unit, y: Unit) extends Foo((x: Int) => x) \ No newline at end of file