Conversation
|
|
||
| private func genericParams(stype: any SType) throws -> [any TypeConverter] { | ||
| let parentParams = if let parent = stype.asNominal?.parent ?? stype.asTypeAlias?.parent, | ||
| parent.typeDecl !== stype.typeDecl { |
There was a problem hiding this comment.
根っこのSTypeはparentとして自身を持っているみたいで、ループしてしまうのでこのようなガードが入っています。
根っこのSTypeはparentを持たないほうが自然に思いましたが、少しコードを追ったくらいではよくわからなかったので一旦こうしています。
There was a problem hiding this comment.
やり方を変えたら不要にできました
|
ありがとうございます。 以下に説明します。 既存の実装において、以下のように特殊化された型( struct S<T> {
var a: T
}
struct K {
var k: S<Int>
}Kのデコードにおいては、 k = S_decode(json, identity)↑うろ覚えですが、ジェネリックなデコーダに対して、 もちろん この特殊化の挙動が、 例えば以下のような入力に対して struct S<T> {
struct G {
var b: T
}
}
struct K {
var k: S<Int>.G
}kのデコードにおいては、 k = S_G_decode(json, identity)となる必要があります。 また、さらに難しいケースで、内側の型もパラメータを持っている場合は struct S<T> {
struct G<U> {
var b: T
var d: U
}
}
enum E { case o }
case V { case o }
struct K {
var k: S<E>.G<V>
}のような入力に対して k = S_G_decode(json, E_decode, V_decode)と、なる必要があります。 また、この struct S<T> {
struct G<U> {
var b: T
var d: U
}
}
struct K<X, Y> {
var k: S<X>.G<Y>
}こういった入力のテストケースの追加と対応をお願いします。 |
There was a problem hiding this comment.
レビューありがとうございます。
確かにencode/decodeの際に正しくジェネリックパラメータが扱われていない問題があったので、修正箇所を広げました。
対応の途中で、CodecPresenceの結果が常に2値しか取り得ないことに気が付きました。
既存の.conditionalのケースは型がジェネリックだった場合に判断を遅延させるために機能していたようですが、実際はジェネリックな場合は常にencode/decodeを生成するので.required相当でした(特に操作がない場合はコーディング関数にidentityを渡す)。
既存テストケースで.conditionalを検査している部分はあくまで内部動作の途中状態を検査していて、.conditionalをなくしても既存のコード生成部分のテストケースに影響がありませんでした。
そして、CodecPresenceが2値しか取り得ない以上、hasDecode、hasEncodeと機能が全く同じとなったため、それらに統合しました。
具体的な理由を忘れてしまったのですが、.conditionalな状態がなくなったことで親スコープの型パラに対する考慮が容易になりました
|
|
||
| extension SType { | ||
| internal var typeDecl: (any TypeDecl)? { | ||
| internal var tsGenericArgs: [any SType] { |
There was a problem hiding this comment.
このPRの主要な変更である、「親スコープのジェネリック引数を引き継ぐ」という部分に対応する変更はここともう1箇所だけです。
このジェネリック引数は親スコープのものを含んでいるTS専用の表現であるため、頭にtsをつけて専用感を出しました
There was a problem hiding this comment.
ts 側の構造を意識したprefix良いと思います。
実装は直感的にこんな感じで良さそうです。
|
|
||
| public func genericParams() throws -> [any TypeConverter] { | ||
| guard let decl = self.swiftType.typeDecl, | ||
| return try genericParams(stype: self.swiftType) |
There was a problem hiding this comment.
主要な変更のもう一箇所がここです。
argとparamsそれぞれで親スコープをたどるよう変更した形です
|
@omochi 再レビューお願いします |
| } | ||
|
|
||
| extension SType { | ||
| internal var typeDecl: (any TypeDecl)? { |
There was a problem hiding this comment.
これはSwiftTypeReader側に全く同じ定義があって、シャドウしているのとあちらのほうがメンテされていてケースが多いのでこっちは消しました
omochi
left a comment
There was a problem hiding this comment.
ありがとうございます。
テストケースを見た感じうまくいってそうです。
内部もうろ覚えですが実装はこんな感じになるはずなのであってそうです。
小さな変更で対応できてるのもいいですね。
既存の設計とうまく噛み合っているんでしょうね。
そして、CodecPresenceが2値しか取り得ない以上、hasDecode、hasEncodeと機能が全く同じとなったため、それらに統合しました。
なんか理由があって導入したと思うんですが・・・
まあテストが通ってるなら消しても大丈夫そうですね。
|
|
||
| try assertGenerate( | ||
| source: """ | ||
| struct S<T> { |
There was a problem hiding this comment.
これJSON型が生成されないことの検査がないと思って追加してました。
Tを持ってるけど、プロパティには使われてないので変換がシンプルであることの検査のつもりでした。
There was a problem hiding this comment.
あ〜なるほど。unexpectedsがポイントだったんですね。okです。
|
|
||
| extension SType { | ||
| internal var typeDecl: (any TypeDecl)? { | ||
| internal var tsGenericArgs: [any SType] { |
There was a problem hiding this comment.
ts 側の構造を意識したprefix良いと思います。
実装は直感的にこんな感じで良さそうです。
| return parentParams | ||
| } | ||
| return try genericContext.genericParams.items.map { (param) in | ||
| return parentParams + (try genericContext.genericParams.items.map { (param) in |
| export function U_decode(json: U_JSON): U { | ||
| const k = S_K_decode<E, E_JSON>(json.k, E_decode); | ||
| const k2 = S_K2_decode<E, E_JSON>(json.k2, E_decode); | ||
| const k3 = json.k3 as S_K<number>; |
| E_JSON, | ||
| T, | ||
| T_JSON | ||
| >(json.k, E_decode, T_decode); |
|
|
||
| func testRecursive() throws { | ||
| if true || true { | ||
| throw XCTSkip("unsupported") |
There was a problem hiding this comment.
TypeConverterがジェネリック型パラメータを調べるときに、親コンテキストの型をたどって再帰的に全てのジェネリックパラメータを集めるようにします。