Swiftの型システムを読む その15 - ArchetypeとGenericTypeParamTypeとDependentMemberType

December 4, 2017

サブタイピングとその意味論については前回までの記事で大まかな作りが把握できて、だいたい実装が読めるようになった….はず…。 以前 多相な関数についてはざっくり「let多相のようなもの」だということは確認しているが、実装はほとんど読んでいなかったので次はジェネリクスについてのコードリーディングを進めていくことにした。

今回はGenericに関係するTypeBaseのサブクラスについて、どこでどのように使われるかを調べた範囲でまとめる。まだ始めたばかりなので間違っていることもたくさんありそうなので注意。

SlavaのThe secret life of types in swiftという記事がとても参考になって、もう10回以上読み直しているのではないかくらい読んでいるのだが、4部構成のうちの2部で更新が止まってしまっていてしかもジェネリクスまわりの肝心な部分が4部に入っているので「早く更新してくれ〜〜〜」と思っているけど1年以上されてないことをみると望みは薄そう。

型パラメータに関する前提確認

Swiftにおいて、型パラメータを含みうるのは以下の2箇所。

  1. struct/class/enum
    struct Hoge<A> { }
    class Hoge<A> { }
    enum Hoge<A> { }
    
  2. func
    func hoge<A>(a: A) -> A { }
    

逆に、VarDecl(letなど)には型パラメータを持たせることはできない。

// NG: できない
let id: <A> (A) -> A = { a in a }

また、associated typeもある種型パラメータのような役割を果たす。

protocol MyProtocol {
  associatedtype T 
}

Genericsに関係するType

読み始めて最初に思ったのが「ジェネリックなパラメータを指しているものがいくつかあって、それぞれ何をを指しているのかよくわからん」ということだった。

GenTypes.svg (10.2 kB)

それぞれについてわかった範囲でメモしておく。

GenericTypeParamType

Describes the type of a generic parameter.

名前の通り「型パラメータ」を表すType。struct/class/enum/funcなど型パラメータを含むDeclにおいて使われているよう。

例えばstruct Hoge<A>AGenericTypeParam になる。

(lldb) po SD->getInterfaceType()->dump()
(metatype_type
  (bound_generic_struct_type decl=struct.(file)[email protected]/Users/ukitaka/Development/ios/tmp/struct.swift:1:8
    (generic_type_param_type depth=0 index=0 decl=struct.(file)[email protected]/Users/ukitaka/Development/ios/tmp/struct.swift:1:13)))

またprotocol P { associatedtype T }TGenericTypeParamになる。

(lldb) po assocType->getInterfaceType()->dump()
(metatype_type
  (dependent_member_type assoc_type=proto.(file)[email protected]/Users/ukitaka/Development/ios/tmp/proto.swift:2:18
    (base=generic_type_param_type depth=0 index=0 decl=proto.(file).Animal.Self)))

もう少しいうと静的に具体的な型に決まる型はGenericTypeParamTypeとして扱われる、と言えそう?

ArchetypeType

An archetype is a type that represents a runtime type that is known to conform to some set of requirements. Archetypes are used to represent generic type parameters and their associated types, as well as the runtime type stored within an existential container.

https://github.com/apple/swift/blob/master/include/swift/AST/Types.h#L4220-L4226

archetype A placeholder for a generic parameter or an associated type within a generic context. Sometimes known as a “rigid type variable” in formal CS literature. Directly stores its conforming protocols and nested archetypes, if any.

https://github.com/apple/swift/blob/master/docs/Lexicon.rst

ざっくりいうと実行時に決まる型たち。struct/class/enum/func等、型パラメータを含みうる箇所以外で出てきた場合この型になる。 例えばstruct Hoge<A> のがA型のプロパティを持っているとき、このAArtchetypeTypeになる。

struct Hoge<A> {
    let a: A // このA
}
(lldb) po PBD->getPattern(0)->getType()->dump()
(archetype_type name=A address=0x10e823a28
  0x10b884050 Module name=genst
    0x10e823010 FileUnit file="/Users/ukitaka/Development/ios/tmp/genst.swift"
      0x10e823450 StructDecl name=Hoge
)

また、実行時に決まる型としてSelfassociated typeの型も挙げられ、これもArtchetypeTypeになる。

protocol Hoge {
  associatedtype T // こっちはGTPT
  var s: Self { get } // Archetype
  var t: T { get } // こっちはArchetype
}
(lldb) po PBD->getPattern(0)->getType()->dump()
(archetype_type name=Self address=0x10c072bf0 conforms_to=proto_self.(file)[email protected]/Users/ukitaka/Development/ios/tmp/proto_self.swift:1:10
  0x10b03b450 Module name=proto_self
    0x10c072010 FileUnit file="/Users/ukitaka/Development/ios/tmp/proto_self.swift"
      0x10c072360 ProtocolDecl name=Hoge
)
(lldb) po PBD->getPattern(0)->getType()->dump()
(archetype_type name=Self.T address=0x10b04f598 parent=0x10b04f428 assoc_type=proto_self.(file)[email protected]/Users/ukitaka/Development/ios/tmp/proto_self.swift:2:18
  0x10a079450 Module name=proto_self
    0x10a086010 FileUnit file="/Users/ukitaka/Development/ios/tmp/proto_self.swift"
      0x10a086360 ProtocolDecl name=Hoge
)

例えばまだないけど、OpeningExistentialが入ったとして、openas で指定された型などもArchetypeTypeになる。

let openedA = a openas A // AはArchetypeになる。

DependentMemberType

DependentMemberType – the type of an associated type of a generic parameter conforming to a protocol

GenericTypeParamTypeと同様のポジションに現れる型パラメータのうち、T.Element のように型パラメータのメンバー型を参照しているときはDependentMemberTypeという型になる。

ArchetypeのポジションではそのままArchetype。

struct Hoge<T: Sequence> { // このTはGTPT
  let e: T.Element // ここはAT
   
  // ここのT.ElementはDMT
  func fuga<A>(e: T.Element) -> T.Element {
    return e
  }
}
(lldb) po FD->getInterfaceType()->dump()
(generic_function_type escaping
  (input=paren_type
    (bound_generic_struct_type decl=depen.(file)[email protected]/Users/ukitaka/Development/ios/tmp/depen.swift:1:8
      (generic_type_param_type depth=0 index=0 decl=depen.(file)[email protected]/Users/ukitaka/Development/ios/tmp/depen.swift:1:13)))
  (output=function_type escaping
    (input=tuple_type num_elements=0)
    (output=dependent_member_type assoc_type=Swift.(file).Sequence.Element
      (base=generic_type_param_type depth=0 index=0 decl=depen.(file)[email protected]/Users/ukitaka/Development/ios/tmp/depen.swift:1:13)))
  ( generic_sig=<T where T : Sequence>))
(archetype_type name=T.Element address=0x10b836fa0 parent=0x10a85daf8 assoc_type=Swift.(file).Sequence.Element
  0x10c07a450 Module name=depen
    0x10a85cc10 FileUnit file="/Users/ukitaka/Development/ios/tmp/depen.swift"
      0x10a85d080 StructDecl name=Hoge
)

UnboundGenericType

UnboundGenericType - Represents a generic type where the type arguments have not yet been resolved.

型パラメータが残った状態のジェネリックな型。 型パラメータを持つstructのStructDeclに対して、getDeclaredType()で型を取ると取れる。

(lldb) po SD->getDeclaredType()->dump()
(unbound_generic_type decl=genst.(file)[email protected]/Users/ukitaka/Development/ios/tmp/genst.swift:1:8)

ちなみにgetInterfaceType()で取ると、先程まで見ていたとおりGenericTypeParamTypeで埋められたBoundなGeneric型として取れる。

(lldb) po SD->getInterfaceType()->dump()
(metatype_type
  (bound_generic_struct_type decl=genst.(file)[email protected]/Users/ukitaka/Development/ios/tmp/genst.swift:1:8
    (generic_type_param_type depth=0 index=0 decl=genst.(file)[email protected]/Users/ukitaka/Development/ios/tmp/genst.swift:1:13)))

このあたりで違いがあるのだろうけど、getInterfaceTypegetDeclaredTypeの違いは説明できない。。また別の記事で書く。

参考文献

  • The secret life of types in swift
    • 必読。というか、最初に読めばよかったのでこのシリーズを人に見せる用に書き直すときにはかならず紹介したい。
  • Lexicon
    • 用語集。網羅していないし解説もざっくりだけど、公式のドキュメントなので信頼はできる。

まとめ

ジェネリクスの実装コードリーディング第一弾としてまずはType関連のくらすを紹介した。 まだまだ説明すべき概念がたくさんあるので、次回以降で説明する(予定)

このエントリーをはてなブックマークに追加