aboutsummaryrefslogtreecommitdiffhomepage
path: root/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html
blob: 7509e3a0debe121e95662871c779e3f6e0335761 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<!DOCTYPE html>
<html lang="ja-JP">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="author" content="nsfisis">
    <meta name="copyright" content="&copy; 2021 nsfisis">
    <meta name="description" content="Rust のプリミティブ型は予約語ではなく普通の識別子である。どのようにこれが名前解決されるのかを調べた。">
    <meta name="keywords" content="Rust">
    <link rel="icon" type="image/svg+xml" href="/favicon.svg">
    <title>Rust のプリミティブ型はどこからやって来るか | REPL: Rest-Eat-Program Loop</title>
    <link rel="stylesheet" href="/style.css?h=37fff6a2f0eef473abde58e55f28ea69">
    <link rel="stylesheet" href="/hl.css?h=340e65ffd5c17713efc9107c06304f7b">
  </head>
  <body class="single">
    <header class="header">
      <nav class="nav">
        <ul>
          <li>
            <a href="/">REPL: Rest-Eat-Program Loop</a>
          </li>
          <li>
            <a href="/about/">About</a>
          </li>
          <li>
            <a href="/posts/">Posts</a>
          </li>
          <li>
            <a href="/slides/">Slides</a>
          </li>
          <li>
            <a href="/tags/">Tags</a>
          </li>
        </ul>
      </nav>
    </header>
    <main class="main">
      <article class="post-single">
        <header class="post-header">
          <h1 class="post-title">Rust のプリミティブ型はどこからやって来るか</h1>
          <ul class="post-tags">
            <li class="tag">
              <a href="/tags/rust/">Rust</a>
            </li>
          </ul>
        </header>
        <div class="post-content">
          <section>
            <h2 id="changelog">更新履歴</h2>
            <ol>
              <li class="revision">
                <time datetime="2021-10-02">2021-10-02</time>: Qiita から移植
              </li>
            </ol>
          </section>
          <div class="admonition">
            <div class="admonition-label">
              NOTE
            </div>
            <div class="admonition-content">
               この記事は Qiita から移植してきたものです。 元 URL: <a href="https://qiita.com/nsfisis/items/9a429432258bbcd6c565">https://qiita.com/nsfisis/items/9a429432258bbcd6c565</a>
            </div>
          </div>
           
          <section id="section--intro">
            <h2><a href="#section--intro">前置き</a></h2> 
            <p>
               Rust において、プリミティブ型の名前は予約語でない。したがって、次のコードは合法である。 
            </p>
             
            <pre class="highlight" language="rust" linenumbering="unnumbered"><code class="highlight"><span class="hljs-meta">#![allow(non_camel_case_types)]</span>
<span class="hljs-meta">#![allow(dead_code)]</span>

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">bool</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">char</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">i8</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">i16</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">i32</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">i64</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">i128</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">isize</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">u8</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">u16</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">u32</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">u64</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">u128</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">usize</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">f32</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">f64</span>;
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">str</span>;</code></pre>
             
            <p>
               では、普段単に <code>bool</code> と書いたとき、この <code>bool</code> は一体どこから来ているのか。rustc のソースを追ってみた。 
            </p>
             
            <blockquote>
              <p>
                 前提知識: 一般的なコンパイラの構造、用語。<code>rustc</code> そのものの知識は不要 (というよりも筆者自身がよく知らない) 
              </p>
            </blockquote>
          </section>
           
          <section id="section--code-reading">
            <h2><a href="#section--code-reading">調査</a></h2> 
            <p>
               調査に使用したソース (調査時点での最新 master) 
            </p>
             
            <p>
              <a href="https://github.com/rust-lang/rust/tree/511ed9f2356af365ad8affe046b3dd33f7ac3c98">https://github.com/rust-lang/rust/tree/511ed9f2356af365ad8affe046b3dd33f7ac3c98</a>
            </p>
             
            <p>
               どのようにして調べるか。rustc の構造には詳しくないため、すぐに当たりをつけるのは難しい。 
            </p>
             
            <p>
               大雑把な構造としては、<code>compiler</code> フォルダ以下に <code>rustc_*</code> という名前のクレートが数十個入っている。これがどうやら <code>rustc</code> コマンドの実装部のようだ。 
            </p>
             
            <p>
              <code>rustc</code> はセルフホストされている (= <code>rustc</code> 自身が Rust で書かれている) ので、<code>bool</code> や <code>char</code> などで適当に検索をかけてもノイズが多すぎて話にならない。 しかし、お誂え向きなことに <code>i128</code>/<code>u128</code> というコンパイラ自身が使うことがなさそうな型が存在するのでこれを使って <code>git grep</code> してみる。 
            </p>
             
            <pre class="highlight monospaced"><code>$ git grep &quot;\bi128\b&quot; | wc      # i128
165    1069   15790

$ git grep &quot;\bu128\b&quot; | wc      # u128
293    2127   26667

$ git grep &quot;\bbool\b&quot; | wc      # cf. bool の結果
3563   23577  294659</code></pre>
             
            <p>
               165 程度であれば探すことができそうだ。今回は、クレート名を見ておおよその当たりをつけた。 
            </p>
             
            <pre class="highlight monospaced"><code>$ git grep &quot;\bi128\b&quot;
...
rustc_resolve/src/lib.rs:        table.insert(sym::i128, Int(IntTy::I128));
...</code></pre>
             
            <p>
              <code>rustc_resolve</code> というのはいかにも名前解決を担いそうなクレート名である。該当箇所を見てみる。 
            </p>
             
            <pre class="highlight" language="rust" linenumbering="unnumbered"><code class="highlight"><span class="hljs-comment">/// Interns the names of the primitive types.</span>
<span class="hljs-comment">///</span>
<span class="hljs-comment">/// All other types are defined somewhere and possibly imported, but the primitive ones need</span>
<span class="hljs-comment">/// special handling, since they have no place of origin.</span>
<span class="hljs-keyword">struct</span> <span class="hljs-title class_">PrimitiveTypeTable</span> {
    primitive_types: FxHashMap&lt;Symbol, PrimTy&gt;,
}

<span class="hljs-keyword">impl</span> <span class="hljs-title class_">PrimitiveTypeTable</span> {
    <span class="hljs-keyword">fn</span> <span class="hljs-title function_">new</span>() <span class="hljs-punctuation">-&gt;</span> PrimitiveTypeTable {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut </span><span class="hljs-variable">table</span> = FxHashMap::<span class="hljs-title function_ invoke__">default</span>();

        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">bool</span>, Bool);
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">char</span>, Char);
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">f32</span>, <span class="hljs-title function_ invoke__">Float</span>(FloatTy::F32));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">f64</span>, <span class="hljs-title function_ invoke__">Float</span>(FloatTy::F64));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">isize</span>, <span class="hljs-title function_ invoke__">Int</span>(IntTy::Isize));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">i8</span>, <span class="hljs-title function_ invoke__">Int</span>(IntTy::I8));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">i16</span>, <span class="hljs-title function_ invoke__">Int</span>(IntTy::I16));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">i32</span>, <span class="hljs-title function_ invoke__">Int</span>(IntTy::I32));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">i64</span>, <span class="hljs-title function_ invoke__">Int</span>(IntTy::I64));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">i128</span>, <span class="hljs-title function_ invoke__">Int</span>(IntTy::I128));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">str</span>, Str);
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">usize</span>, <span class="hljs-title function_ invoke__">Uint</span>(UintTy::Usize));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">u8</span>, <span class="hljs-title function_ invoke__">Uint</span>(UintTy::U8));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">u16</span>, <span class="hljs-title function_ invoke__">Uint</span>(UintTy::U16));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">u32</span>, <span class="hljs-title function_ invoke__">Uint</span>(UintTy::U32));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">u64</span>, <span class="hljs-title function_ invoke__">Uint</span>(UintTy::U64));
        table.<span class="hljs-title function_ invoke__">insert</span>(sym::<span class="hljs-type">u128</span>, <span class="hljs-title function_ invoke__">Uint</span>(UintTy::U128));
        <span class="hljs-keyword">Self</span> { primitive_types: table }
    }
}</code></pre>
             
            <p>
               これは初めに列挙したプリミティブ型の一覧と一致している。doc comment にも、 
            </p>
             
            <blockquote>
              <p>
                 All other types are defined somewhere and possibly imported, but the primitive ones need special handling, since they have no place of origin. 
              </p>
            </blockquote>
             
            <p>
               とある。次はこの struct の使用箇所を追う。追うと言っても使われている箇所は次の一箇所しかない。なお説明に不要な箇所は大きく削っている。 
            </p>
             
            <pre class="highlight" language="rust" linenumbering="unnumbered"><code class="highlight"><span class="hljs-comment">/// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope.</span>
<span class="hljs-comment">/// (略)</span>
<span class="hljs-keyword">fn</span> <span class="hljs-title function_">resolve_ident_in_lexical_scope</span>(
    &amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>,
    <span class="hljs-keyword">mut</span> ident: Ident,
    ns: Namespace,
    <span class="hljs-comment">// (略)</span>
) <span class="hljs-punctuation">-&gt;</span> <span class="hljs-type">Option</span>&lt;LexicalScopeBinding&lt;<span class="hljs-symbol">&#x27;a</span>&gt;&gt; {
    <span class="hljs-comment">// (略)</span>

    <span class="hljs-keyword">if</span> ns == TypeNS {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-variable">Some</span>(prim_ty) = <span class="hljs-keyword">self</span>.primitive_type_table.primitive_types.<span class="hljs-title function_ invoke__">get</span>(&amp;ident.name) {
            <span class="hljs-keyword">let</span> <span class="hljs-variable">binding</span> =
                (Res::<span class="hljs-title function_ invoke__">PrimTy</span>(*prim_ty), ty::Visibility::Public, DUMMY_SP, ExpnId::<span class="hljs-title function_ invoke__">root</span>())
                  .<span class="hljs-title function_ invoke__">to_name_binding</span>(<span class="hljs-keyword">self</span>.arenas);
            <span class="hljs-keyword">return</span> <span class="hljs-title function_ invoke__">Some</span>(LexicalScopeBinding::<span class="hljs-title function_ invoke__">Item</span>(binding));
        }
    }

    <span class="hljs-literal">None</span>
}</code></pre>
             
            <p>
               関数名や doc comment が示している通り、この関数は識別子 (identifier, ident) を現在のレキシカルスコープ内で解決 (resolve) する。 <code>if ns == TypeNS</code> のブロック内では、<code>primitive_type_table</code> (上記の <code>PrimitiveTypeTable::new()</code> で作られた変数) に含まれている識別子 (<code>bool</code>、<code>i32</code> など) かどうか判定し、そうであればそれに紐づけられたプリミティブ型を返している。 
            </p>
             
            <p>
               なお、<code>ns</code> は「名前空間」を示す変数である。Rust における名前空間はC言語におけるそれとほとんど同じで、今探している名前が関数名/変数名なのか型なのかマクロなのかを区別している。この <code>if</code> は、プリミティブ型に解決されるのは型を探しているときだけだ、と言っている。 
            </p>
             
            <p>
               重要なのは、これが <code>resolve_ident_in_lexical_scope()</code> の最後に書かれている点である。つまり、最初に挙げたプリミティブ型の識別子は、「名前解決の最終段階で」、「他に同名の型が見つかっていなければ」プリミティブ型として解決される。 
            </p>
             
            <p>
               動作がわかったところで、例として次のコードを考える。 
            </p>
             
            <pre class="highlight" language="rust" linenumbering="unnumbered"><code class="highlight"><span class="hljs-meta">#![allow(non_camel_case_types)]</span>

<span class="hljs-keyword">struct</span> <span class="hljs-title class_">bool</span>;

<span class="hljs-keyword">fn</span> <span class="hljs-title function_">main</span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-variable">_</span>: <span class="hljs-type">bool</span> = <span class="hljs-type">bool</span>;
}</code></pre>
             
            <p>
               ここで <code>main()</code> の <code>bool</code> は <code>struct bool</code> として解決される。なぜなら、プリミティブ型の判定をする前に <code>bool</code> という名前の別の型が見つかるからだ。 
            </p>
          </section>
           
          <section id="section--outro">
            <h2><a href="#section--outro">まとめ</a></h2> 
            <p>
               Rust のプリミティブ型は予約語ではない。名前解決の最終段階で特別扱いされ、他に同名の型が見つかっていなければ対応するプリミティブ型に解決される。 
            </p>
          </section>
        </div>
      </article>
    </main>
    <footer class="footer">
      &copy; 2021 nsfisis
    </footer>
  </body>
</html>