8

私はこの質問に答えようとしています。

書く代わりに:

case class Person(name: String, age: Int) {
  def this() = this("",1)
}

マクロ注釈を使用して展開すると思いました:

@Annotation
case class Person(name: String, age: Int)

DefDefそこで、次のように、マクロ注釈の impl で準引用符を使用して、新しいコンストラクターを単純な古いものとして追加しようとしました。

val newCtor = q"""def this() = this("", 1)"""
val newBody = body :+ newCtor
q"$mods class $name[..$tparams](..$first)(...$rest) extends ..$parents { $self => ..$newBody }"

しかし、それはエラーを返します:called constructor's definition must precede calling constructor's definition

それを修正する方法はありますか?私は何を取りこぼしたか?

ご覧いただきありがとうございます - ジュリアン

4

1 に答える 1

7

マクロ アノテーションで 2 次コンストラクターを生成するという非常に自然な意図により、2 つの異なる問題が明らかになりました。

1) 最初の問題 ( https://issues.scala-lang.org/browse/SI-8451 ) は、セカンダリ コンストラクターに対して誤ったツリー形状を放出する quasiquote に関するものです。これは 2.11.0-RC4 (まだリリースされていない、現在 2.11.0-SNAPSHOT として利用可能) と 2.10.x 用の楽園 2.0.0-M6 (昨日リリース) で修正されました。

2) 2 番目の問題は、タイプチェッカー中に大混乱を引き起こす割り当てられていない位置に関するものです。興味深いことに、コンストラクターの呼び出しを型チェックするとき、typer は位置を使用して、これらの呼び出しが有効かどうかを判断します。簡単にパッチを当てることはできず、回避する必要があります。

         val newCtor = q"""def this() = this(List(Some("")))"""
-        val newBody = body :+ newCtor
+
+        // It looks like typer sometimes uses positions to decide whether stuff
+        // (secondary constructors in this case) typechecks or not (?!!):
+        // https://github.com/xeno-by/scala/blob/c74e1325ff1514b1042c959b0b268b3c6bf8d349/src/compiler/scala/tools/nsc/typechecker/Typers.scala#L2932
+        //
+        // In general, positions are important in getting error messages and debug
+        // information right, but maintaining positions is too hard, so macro writers typically don't care.
+        //
+        // This has never been a problem up until now, but here we're forced to work around
+        // by manually setting an artificial position for the secondary constructor to be greater
+        // than the position that the default constructor is going to get after macro expansion.
+        //
+        // We have a few ideas how to fix positions in a principled way in Palladium,
+        // but we'll have to see how it goes.
+        val defaultCtorPos = c.enclosingPosition
+        val newCtorPos = defaultCtorPos.withEnd(defaultCtorPos.endOrPoint + 1).withStart(defaultCtorPos.startOrPoint + 1).withPoint(defaultCtorPos.    point + 1)
+        val newBody = body :+ atPos(newCtorPos)(newCtor)
于 2014-03-31T09:23:29.577 に答える