跳转到内容

ADR-0027: Rust-style Use Trees, Grouped Imports, and Glob Imports

此内容尚不支持你的语言。

Accepted

ADR-0018は、階層モジュール、pub useによる再エクスポート、暗黙Prelude、canonical public path、module visibility、.区切りのパスを定義した。一方で同ADRは、grouped importとglob importを導入せず、単純なuse path [as alias];だけを扱うと決定していた。この制約では、Preludeや大きな公開APIを構成する際に同じprefixを持つpub useを繰り返す必要がある。

標準ライブラリには通常のgeneric payload enumであるMaybe<T>があり、型とvariantをPreludeから公開する場合、従来は型だけを再エクスポートしてMaybe.Just、Maybe.Nothingと修飾する必要があった。Maybeや将来のOption、Resultだけを特別扱いせず、module、declaration、enum variantへ共通して適用できるimport treeが必要である。

UseDirectiveSyntaxは単一のpathとaliasを直接保持する形から、再帰的なUseTreeSyntaxを保持する形へ変更する。groupはbrace、各item、commaを保持し、各leafはpath、self、glob、任意のaliasとsource spanを保持する。Parserはsource textを書き換えず、malformedなgroupから構文回復する。

Sobakasuでは既存どおり.を名前空間区切りに使用し、::は導入しない。

use foo.Bar;
use foo.Bar as Baz;
use foo.{Bar, Baz};
use foo.{bar.{A, B}, C};
use foo.{self, Bar};
use foo.*;

groupは再帰的にnestでき、末尾commaを許可する。group内のglobも許可し、次を同じ一般則で処理する。

use foo.{bar.*, Baz,};
use foo.{*};

aliasはimportされるleafだけに適用する。既存のmodule aliasと同じ意味になるため、self as Aliasを許可する。

use foo.{Bar as Baz, self as f};

selfは現在のuse tree prefixそのものを表す。したがって、use foo.bar.{self, A};はuse foo.bar; use foo.bar.A;と同じ意味になる。globはprefixが公開するすべてのaccessible exportを表す。

Module Resolverはuse treeをsource textへ展開せず、ASTを走査して通常のimport requestの列へ平坦化する。各requestについて規約に従う最長のmodule prefixを読み込み、残りのdeclaration pathをBinderへ渡す。これによりmodule.Type.Variantもmodule systemを複製せず表現できる。

Binderは既存のModuleSymbol、public declaration index、visibility、再エクスポート、canonical public path、Preludeを使って各pathを解決する。pathの途中がpublic enum typeなら、その通常のEnumVariantSymbolを解決できる。globの対象はmoduleまたはenum typeとし、moduleではpublic exportだけ、enumではそのvariantだけを列挙する。private declaration、private module、inaccessible exportをglobから漏らさない。

pub useはgroup、nested tree、self、globのすべてへ同じように適用する。再エクスポートは元のsymbol identityを再利用し、canonical public pathの候補だけを追加する。IR LowererとUASM Assemblerへuse treeを渡さない。

ADR-0018の名前検索順位を維持し、明示的なleaf importはglob importより優先する。したがって、次ではbar.Thingを選択する。

use foo.*;
use bar.Thing;

複数のglobが異なるsymbolを同じ名前で導入した場合は曖昧として診断し、source orderで一方を正常な解決結果として採用しない。同一symbolが複数経路から到達した場合はidentityを複製しない。現在のmodule内の宣言と子、明示alias、明示import、glob importはいずれもPreludeより優先される。

通常のgeneric enumを次のように再エクスポートできる。

pub use maybe.Maybe.{self, Nothing, Just};
pub use core.option.Option.{self, None, Some};
pub use core.result.Result.{self, Ok, Err};

importされたunit、tuple、struct variantは、修飾付きvariantと同じEnumVariantSymbolおよびexpected-type inferenceを使用して構築できる。Maybe、Option、Resultや個々のvariant名をcompiler built-inにはしない。

この決定は、ADR-0018のgrouped import/glob importを導入しないという部分だけを更新する。階層モジュール、pub use、Prelude、canonical public path、module visibility、.区切り、外部APIとのextern境界は維持する。またADR-0026の「variantを自動的なbare shorthandとして注入しない」という決定を維持しつつ、通常のuseまたはPreludeの公開exportによって明示的に導入されたvariantはbare nameで利用できるようにする。

use foo.{A, B};だけを導入し、self、glob、nested treeを見送る案。構文拡張が中途半端になり、後からrecursive ASTを作り直す可能性が高いため採用しない。

Some/NoneやJust/NothingをcompilerまたはPrelude専用処理で注入する案。一般化できず、Resultやmodule APIに再利用できず、標準ライブラリとcompilerの境界を悪化させるため採用しない。

use foo::{A, B};とする案。Sobakasuは既存設計として.を名前空間区切りに使用しており、同じ意味の区切りを追加すると一貫性を失うため採用しない。

use foo.A; use foo.B; use foo.C;だけを使う案。機能上は可能だが、Preludeや大きなAPIの再エクスポートで冗長になるため採用しない。

Rustで実績のあるuse-tree modelは、既存のuse/pub useを自然に一般化できる。Option.{self, None, Some}のようなPrelude設計を簡潔にし、特定の標準型をcompilerへ組み込まずに済む。private implementation moduleを隠しながら必要なAPIだけを再構成しやすく、.区切りというSobakasuの設計も維持できる。

syntax treeを最初から再帰構造にし、意味処理では通常のimport requestへ平坦化することで、正確なsource spanとparser recoveryを保ちながら、既存のmodule graphとBinderの責務を再利用できる。

  • import/re-exportの記述量が減る。
  • Preludeの構成を簡潔にできる。
  • enum variantを通常の名前としてimport/re-exportできる。
  • nested module APIを公開面へ再構成しやすい。
  • Rustのuse treeを知る利用者が理解しやすい。
  • declaration identity、visibility、canonical public path、IR/UASMの責務分離を維持できる。
  • Parserとsyntax treeが再帰構造になり複雑になる。
  • globによる名前の曖昧性が増える。
  • globを含む再エクスポートgraphには固定点処理が必要になる。
  • malformed groupとleafごとの診断、source spanを維持する必要がある。
  • bare enum variant patternは既存のpattern grammarが要求するEnum.Variant形式のままであり、この決定だけでは変更しない。