Repository: scalameta/scalameta (scala)

Issue:
Secondary constructors in trait lead to invariant failed in worksheet mode
I copied the code form the [Auxiliary Class Constructors article](https://docs.scala-lang.org/overviews/scala-book/classes-aux-constructors.html), pasted into Scastie, changed `class` to `trait` and set the Scala version to 3.1.0.

## Compiler version

at least 3.0.0, 3.1.0 and 3.1.1-RC1

## Minimized code

```Scala
trait P:
 def this(x: Unit) = this()
```

## Output (click arrow to expand)
<details>
org.scalameta.invariants.InvariantFailedException: invariant failed:
when verifying scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Template](templ)(scala.meta.Tree.classifiable[scala.meta.Template]).is[Template.Quasi](Template.this.Quasi.ClassifierClass[scala.meta.Template]).||(templ.stats.forall(((x$9: scala.meta.Stat) => scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Stat](x$9)(scala.meta.Tree.classifiable[scala.meta.Stat]).is[Ctor](scala.meta.Ctor.ClassifierClass[scala.meta.Stat]).`unary_!`)))
found that scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Template](templ)(scala.meta.Tree.classifiable[scala.meta.Template]).is[Template.Quasi](Template.this.Quasi.ClassifierClass[scala.meta.Template]) is false
and also templ.stats.forall(((x$9: scala.meta.Stat) => scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Stat](x$9)(scala.meta.Tree.classifiable[scala.meta.Stat]).is[Ctor](scala.meta.Ctor.ClassifierClass[scala.meta.Stat]).`unary_!`)) is false
where Template = scala.meta.Template$@7998a3ca
where templ = :
 def this(x: Unit) = this()
	at org.scalameta.invariants.InvariantFailedException$.raise(Exceptions.scala:19)
	at scala.meta.Defn$Trait$.internal$247(Trees.scala:432)
	at scala.meta.Defn$Trait$.apply(Trees.scala:425)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$traitDef$1(ScalametaParser.scala:4592)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.traitDef(ScalametaParser.scala:4570)
	at scala.meta.internal.parsers.ScalametaParser.tmplDef(ScalametaParser.scala:4548)
	at scala.meta.internal.parsers.ScalametaParser.defOrDclOrSecondaryCtor(ScalametaParser.scala:4201)
	at scala.meta.internal.parsers.ScalametaParser.nonLocalDefOrDcl(ScalametaParser.scala:4172)
	at scala.meta.internal.parsers.ScalametaParser$$anonfun$templateStat$1$1.applyOrElse(ScalametaParser.scala:5120)
	at scala.meta.internal.parsers.ScalametaParser$$anonfun$templateStat$1$1.applyOrElse(ScalametaParser.scala:5114)
	at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
	at scala.meta.internal.parsers.ScalametaParser.statSeq(ScalametaParser.scala:5057)
	at scala.meta.internal.parsers.ScalametaParser.templateStats(ScalametaParser.scala:5130)
	at scala.meta.internal.parsers.ScalametaParser.templateStatSeq(ScalametaParser.scala:5110)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$templateBody$1(ScalametaParser.scala:4945)
	at scala.meta.internal.parsers.ScalametaParser.inBraces(ScalametaParser.scala:800)
	at scala.meta.internal.parsers.ScalametaParser.templateBody(ScalametaParser.scala:4945)
	at scala.meta.internal.parsers.ScalametaParser.templateBodyOpt(ScalametaParser.scala:4953)
	at scala.meta.internal.parsers.ScalametaParser.template(ScalametaParser.scala:4887)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$template$1(ScalametaParser.scala:4906)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
	at scala.meta.internal.parsers.ScalametaParser.template(ScalametaParser.scala:4891)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$templateOpt$1(ScalametaParser.scala:4931)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
	at scala.meta.internal.parsers.ScalametaParser.templateOpt(ScalametaParser.scala:4923)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$objectDef$1(ScalametaParser.scala:4682)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.objectDef(ScalametaParser.scala:4674)
	at scala.meta.internal.parsers.ScalametaParser.tmplDef(ScalametaParser.scala:4558)
	at scala.meta.internal.parsers.ScalametaParser.topLevelTmplDef(ScalametaParser.scala:4540)
	at scala.meta.internal.parsers.ScalametaParser$$anonfun$topStat$1.applyOrElse(ScalametaParser.scala:5079)
	at scala.meta.internal.parsers.ScalametaParser$$anonfun$topStat$1.applyOrElse(ScalametaParser.scala:5067)
	at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
	at scala.meta.internal.parsers.ScalametaParser.statSeq(ScalametaParser.scala:5057)
	at scala.meta.internal.parsers.ScalametaParser.topStatSeq(ScalametaParser.scala:5066)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$batchSource$1(ScalametaParser.scala:5297)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
	at scala.meta.internal.parsers.ScalametaParser.batchSource(ScalametaParser.scala:5256)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$source$1(ScalametaParser.scala:5239)
	at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
	at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
	at scala.meta.internal.parsers.ScalametaParser.source(ScalametaParser.scala:5238)
	at scala.meta.internal.parsers.ScalametaParser.entrypointSource(ScalametaParser.scala:5244)
	at scala.meta.internal.parsers.ScalametaParser.$anonfun$parseSource$2(ScalametaParser.scala:143)
	at scala.meta.internal.parsers.ScalametaParser.parseRule(ScalametaParser.scala:53)
	at scala.meta.internal.parsers.ScalametaParser.parseSource(ScalametaParser.scala:143)
	at scala.meta.parsers.Parse$.$anonfun$parseSource$1(Parse.scala:29)
	at scala.meta.internal.parsers.ScalametaParser$$anon$264.apply(ScalametaParser.scala:5308)
	at scala.meta.parsers.Api$XtensionParseDialectInput.parse(Api.scala:25)
	at scala.meta.parsers.Api$XtensionParseInputLike.parse(Api.scala:14)
	at com.olegych.scastie.instrumentation.Instrument$.apply(Instrument.scala:199)
	at com.olegych.scastie.instrumentation.InstrumentedInputs$.apply(InstrumentedInputs.scala:23)
	at com.olegych.scastie.sbt.SbtProcess$$anonfun$4.applyOrElse(SbtProcess.scala:191)
	at com.olegych.scastie.sbt.SbtProcess$$anonfun$4.applyOrElse(SbtProcess.scala:177)
	at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
	at akka.actor.FSM.processEvent(FSM.scala:707)
	at akka.actor.FSM.processEvent$(FSM.scala:704)
	at com.olegych.scastie.sbt.SbtProcess.processEvent(SbtProcess.scala:64)
	at akka.actor.FSM.akka$actor$FSM$$processMsg(FSM.scala:701)
	at akka.actor.FSM$$anonfun$receive$1.applyOrElse(FSM.scala:695)
	at akka.actor.Actor.aroundReceive(Actor.scala:539)
	at akka.actor.Actor.aroundReceive$(Actor.scala:537)
	at com.olegych.scastie.sbt.SbtProcess.aroundReceive(SbtProcess.scala:64)
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:614)
	at akka.actor.ActorCell.invoke(ActorCell.scala:583)
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:268)
	at akka.dispatch.Mailbox.run(Mailbox.scala:229)
	at akka.dispatch.Mailbox.exec(Mailbox.scala:241)
	at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
	at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
	at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
	at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
</details>

Related:

- my [Can traits have secondary constructors in Scala 3?](https://stackoverflow.com/questions/70518466/can-traits-have-secondary-constructors-in-scala-3) Stack Overflow question
- [Dotty bug #14184](https://github.com/lampepfl/dotty/issues/14184), closed with indication that it's a Scalameta bug

Pull request description:
Fixes https://github.com/scalameta/scalameta/issues/2529

Ideally, we would make the invariant messages have an additional message parameter so that this never happens again, but this is more involved.

Patch:
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
index fb303aa6d6..eb836889b6 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
@@ -3033,7 +3033,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           canApply = false
           next()
           Success(atPos(in.prevTokenPos, auto) {
-            template(enumCaseAllowed = false) match {
+            template(enumCaseAllowed = false, secondaryConstructorAllowed = false) match {
               case trivial @ Template(Nil, List(init), Self(Name.Anonymous(), None), Nil) =>
                 if (!token.prev.is[RightBrace]) Term.New(init)
                 else Term.NewAnonymous(trivial)
@@ -4184,16 +4184,23 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
 
   def entrypointImportee(): Importee = importee()
 
-  def nonLocalDefOrDcl(enumCaseAllowed: Boolean = false): Stat = {
+  def nonLocalDefOrDcl(
+      enumCaseAllowed: Boolean = false,
+      secondaryConstructorAllowed: Boolean = false
+  ): Stat = {
     val anns = annots(skipNewLines = true)
     val mods = anns ++ modifiers()
-    defOrDclOrSecondaryCtor(mods, enumCaseAllowed) match {
+    defOrDclOrSecondaryCtor(mods, enumCaseAllowed, secondaryConstructorAllowed) match {
       case s if s.isTemplateStat => s
       case other => syntaxError("is not a valid template statement", at = other)
     }
   }
 
-  def defOrDclOrSecondaryCtor(mods: List[Mod], enumCaseAllowed: Boolean = false): Stat = {
+  def defOrDclOrSecondaryCtor(
+      mods: List[Mod],
+      enumCaseAllowed: Boolean = false,
+      secondaryConstructorAllowed: Boolean = false
+  ): Stat = {
     onlyAcceptMod[Mod.Lazy, KwVal](mods, "lazy not allowed here. Only vals can be lazy")
     onlyAcceptMod[Mod.Opaque, KwType](mods, "opaque not allowed here. Only types can be opaque.")
     token match {
@@ -4202,6 +4209,8 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       case KwGiven() =>
         givenDecl(mods)
       case KwDef() =>
+        if (!secondaryConstructorAllowed && ahead(token.is[KwThis]))
+          syntaxError("Illegal secondary constructor", at = token.pos)
         funDefOrDclOrExtensionOrSecondaryCtor(mods)
       case KwType() =>
         typeDefOrDcl(mods)
@@ -4909,13 +4918,23 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     }
   }
 
-  def template(edefs: List[Stat], parents: List[Init], enumCaseAllowed: Boolean): Template = {
+  def template(
+      edefs: List[Stat],
+      parents: List[Init],
+      enumCaseAllowed: Boolean,
+      secondaryConstructorAllowed: Boolean
+  ): Template = {
     val derived = derivesClasses
-    val (self, body) = templateBodyOpt(parenMeansSyntaxError = false, enumCaseAllowed)
+    val (self, body) =
+      templateBodyOpt(parenMeansSyntaxError = false, enumCaseAllowed, secondaryConstructorAllowed)
     Template(edefs, parents, self, body, derived)
   }
 
-  def template(afterExtend: Boolean = false, enumCaseAllowed: Boolean): Template = autoPos {
+  def template(
+      afterExtend: Boolean = false,
+      enumCaseAllowed: Boolean,
+      secondaryConstructorAllowed: Boolean
+  ): Template = autoPos {
     newLineOptWhenFollowedBy[LeftBrace]
     if (token.is[LeftBrace]) {
       // @S: pre template body cannot stub like post body can!
@@ -4924,19 +4943,21 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         val edefs = body.map(ensureEarlyDef)
         next()
         val parents = templateParents(afterExtend)
-        template(edefs, parents, enumCaseAllowed)
+        template(edefs, parents, enumCaseAllowed, secondaryConstructorAllowed)
       } else {
         Template(Nil, Nil, self, body)
       }
     } else {
       val parents = if (token.is[Colon]) Nil else templateParents(afterExtend)
-      template(Nil, parents, enumCaseAllowed)
+      template(Nil, parents, enumCaseAllowed, secondaryConstructorAllowed)
     }
   }
 
   def quasiquoteTemplate(): Template = entrypointTemplate()
 
-  def entrypointTemplate(): Template = autoPos(template(enumCaseAllowed = false))
+  def entrypointTemplate(): Template = autoPos(
+    template(enumCaseAllowed = false, secondaryConstructorAllowed = true)
+  )
 
   def ensureEarlyDef(tree: Stat): Stat = tree match {
     case q: Quasi => q
@@ -4955,29 +4976,46 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       unquote[Template]
     } else if (token.is[KwExtends] || (token.is[Subtype] && owner.isTrait)) {
       next()
-      template(afterExtend = true, enumCaseAllowed = owner.isEnum)
+      template(
+        afterExtend = true,
+        enumCaseAllowed = owner.isEnum,
+        secondaryConstructorAllowed = owner.isClass || owner.isEnum
+      )
     } else {
       newLineOptWhenFollowing(t => t.is[soft.KwDerives])
       if (token.is[soft.KwDerives]) {
-        template(Nil, Nil, enumCaseAllowed = owner.isEnum)
+        template(
+          Nil,
+          Nil,
+          enumCaseAllowed = owner.isEnum,
+          secondaryConstructorAllowed = owner.isClass || owner.isEnum
+        )
       } else {
         val (self, body) =
-          templateBodyOpt(parenMeansSyntaxError = !owner.isClass, enumCaseAllowed = owner.isEnum)
+          templateBodyOpt(
+            parenMeansSyntaxError = !owner.isClass,
+            enumCaseAllowed = owner.isEnum,
+            secondaryConstructorAllowed = owner.isClass || owner.isEnum
+          )
         Template(Nil, Nil, self, body)
       }
     }
   }
 
-  def templateBody(enumCaseAllowed: Boolean = false): (Self, List[Stat]) =
-    inBraces(templateStatSeq(enumCaseAllowed))
+  def templateBody(
+      enumCaseAllowed: Boolean = false,
+      secondaryConstructorAllowed: Boolean = false
+  ): (Self, List[Stat]) =
+    inBraces(templateStatSeq(enumCaseAllowed, secondaryConstructorAllowed))
 
   def templateBodyOpt(
       parenMeansSyntaxError: Boolean,
-      enumCaseAllowed: Boolean = false
+      enumCaseAllowed: Boolean = false,
+      secondaryConstructorAllowed: Boolean = false
   ): (Self, List[Stat]) = {
     newLineOptWhenFollowedBy[LeftBrace]
     if (token.is[LeftBrace]) {
-      templateBody(enumCaseAllowed)
+      templateBody(enumCaseAllowed, secondaryConstructorAllowed)
     } else if (isColonEol(token)) {
       accept[Colon]
 
@@ -4988,7 +5026,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           in.observeIndented()
 
       if (nextIndented)
-        indented(templateStatSeq(enumCaseAllowed))
+        indented(templateStatSeq(enumCaseAllowed, secondaryConstructorAllowed))
       else if (token.is[EndMarkerIntro] && !enumCaseAllowed)
         (autoPos(Self(autoPos(Name.Anonymous()), None)), Nil)
       else
@@ -5023,7 +5061,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     case KwImport() => importStmt()
     case KwExport() => exportStmt()
     case KwPackage() if !dialect.allowToplevelTerms => packageOrPackageObjectDef()
-    case DefIntro() => nonLocalDefOrDcl()
+    case DefIntro() => nonLocalDefOrDcl(secondaryConstructorAllowed = true)
     case EndMarkerIntro() => endMarker()
     case ExprIntro() => stat(expr(location = NoStat, allowRepeated = true))
     case Ellipsis(_) => ellipsis(1, astInfo[Stat])
@@ -5110,7 +5148,10 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       nonLocalDefOrDcl()
   }
 
-  def templateStatSeq(enumCaseAllowed: Boolean = false): (Self, List[Stat]) = {
+  def templateStatSeq(
+      enumCaseAllowed: Boolean = false,
+      secondaryConstructorAllowed: Boolean = false
+  ): (Self, List[Stat]) = {
     val emptySelf = autoPos(Self(autoPos(Name.Anonymous()), None))
     var selfOpt: Option[Self] = None
     var firstOpt: Option[Stat] = None
@@ -5134,17 +5175,23 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
         acceptStatSepOpt()
       }
     }
-    (selfOpt.getOrElse(emptySelf), firstOpt ++: templateStats(enumCaseAllowed))
+    (
+      selfOpt.getOrElse(emptySelf),
+      firstOpt ++: templateStats(enumCaseAllowed, secondaryConstructorAllowed)
+    )
   }
 
-  def templateStats(enumCaseAllowed: Boolean = false): List[Stat] = {
+  def templateStats(
+      enumCaseAllowed: Boolean = false,
+      secondaryConstructorAllowed: Boolean = false
+  ): List[Stat] = {
     def templateStat: PartialFunction[Token, Stat] = {
       case KwImport() =>
         importStmt()
       case KwExport() =>
         exportStmt()
       case DefIntro() =>
-        nonLocalDefOrDcl(enumCaseAllowed)
+        nonLocalDefOrDcl(enumCaseAllowed, secondaryConstructorAllowed)
       case Unquote() =>
         unquote[Stat]
       case Ellipsis(_) =>


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
index 7f8c851914..d3d17ab099 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
@@ -189,6 +189,20 @@ class MinorDottySuite extends BaseDottySuite {
     )
   }
 
+  test("secondary-trait-constructors") {
+    runTestError[Stat](
+      "trait Foo{ def this(i: Int) = this() }",
+      "Illegal secondary constructor"
+    )
+  }
+
+  test("secondary-object-constructors") {
+    runTestError[Stat](
+      "object Foo{ def this(i: Int) = this() }",
+      "Illegal secondary constructor"
+    )
+  }
+
   test("trait-parameters-generic") {
     runTestAssert[Stat]("trait Foo[T](bar: T)")(
       Defn.Trait(Nil, pname("Foo"), List(pparam("T")), ctorp(List(tparam("bar", "T"))), tpl(Nil))
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala
index d46f88249c..0568cec1b1 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala
@@ -499,46 +499,78 @@ class SignificantIndentationSuite extends BaseDottySuite {
   }
 
   test("this-constructor") {
-    val code = """|def this(msg: String) =
-                  |  this(message, false)
+    val code = """|class A:
+                  |  def this(msg: String) =
+                  |    this(message, false)
                   |""".stripMargin
-    val output = "def this(msg: String) = this(message, false)"
+    val output = "class A { def this(msg: String) = this(message, false) }"
     runTestAssert[Stat](code, assertLayout = Some(output))(
-      Ctor.Secondary(
+      Defn.Class(
         Nil,
-        Name(""),
-        List(List(Term.Param(Nil, Term.Name("msg"), Some(Type.Name("String")), None))),
-        Init(
-          Type.Singleton(Term.This(Name(""))),
-          Name(""),
-          List(List(Term.Name("message"), Lit.Boolean(false)))
-        ),
-        Nil
+        Type.Name("A"),
+        Nil,
+        Ctor.Primary(Nil, Name(""), Nil),
+        Template(
+          Nil,
+          Nil,
+          Self(Name(""), None),
+          List(
+            Ctor.Secondary(
+              Nil,
+              Name(""),
+              List(List(Term.Param(Nil, Term.Name("msg"), Some(Type.Name("String")), None))),
+              Init(
+                Type.Singleton(Term.This(Name(""))),
+                Name(""),
+                List(List(Term.Name("message"), Lit.Boolean(false)))
+              ),
+              Nil
+            )
+          ),
+          Nil
+        )
       )
     )
   }
 
   test("this-constructor-indented-block") {
-    val code = """|def this(msg: String) =
-                  |  this(message, false)
-                  |  otherStat
+    val code = """|class A:
+                  |  def this(msg: String) =
+                  |    this(message, false)
+                  |    otherStat
                   |""".stripMargin
-    val output = """|def this(msg: String) {
-                    |  this(message, false)
-                    |  otherStat
+    val output = """|class A {
+                    |  def this(msg: String) {
+                    |    this(message, false)
+                    |    otherStat
+                    |  }
                     |}
                     |""".stripMargin
     runTestAssert[Stat](code, assertLayout = Some(output))(
-      Ctor.Secondary(
+      Defn.Class(
         Nil,
-        Name(""),
-        List(List(Term.Param(Nil, Term.Name("msg"), Some(Type.Name("String")), None))),
-        Init(
-          Type.Singleton(Term.This(Name(""))),
-          Name(""),
-          List(List(Term.Name("message"), Lit.Boolean(false)))
-        ),
-        List(Term.Name("otherStat"))
+        Type.Name("A"),
+        Nil,
+        Ctor.Primary(Nil, Name(""), Nil),
+        Template(
+          Nil,
+          Nil,
+          Self(Name(""), None),
+          List(
+            Ctor.Secondary(
+              Nil,
+              Name(""),
+              List(List(Term.Param(Nil, Term.Name("msg"), Some(Type.Name("String")), None))),
+              Init(
+                Type.Singleton(Term.This(Name(""))),
+                Name(""),
+                List(List(Term.Name("message"), Lit.Boolean(false)))
+              ),
+              List(Term.Name("otherStat"))
+            )
+          ),
+          Nil
+        )
       )
     )
   }