LoginSignup
6
1

More than 1 year has passed since last update.

D言語の更新まとめ 2022年11月版(dmd 2.101.0)

Last updated at Posted at 2022-12-03

はじめに

D言語の公式コンパイラである dmd が更新され、最新バージョンである 2.101.0 が 2022/11/16 にリリースされました。

今回は前回からほぼ6ヶ月かけてのリリースで、その期間に見合ったかなり多くの変更点があります。
全部詳細整理するのは難しいところもあるので簡単な紹介になるところもありますが、今回も新機能や改善が多数ありますので整理していきます。

より細かい内容は下記リンクからChangeLogをご覧ください。

また、前回である5月版のリリースまとめは以下になります。

変更点目次

コンパイラ変更点

  • Dにビットフィールドが追加されました。
  • traits(classInstanceAlignment) の追加されました。
  • pragma(crt_constructor) / pragma(crt_destructor) のリンクチェックが緩和されました。
  • -O スイッチのあるコンパイル時に定義済みバージョン識別子 D_Optimized が追加されました。
  • nothrow 関数の制約から throw する処理が非推奨になりました。
  • versiondebug の条件に整数を使用することは非推奨になりました。
  • スコープポインターに対する警告が表示されるようになりました。
  • C++ヘッダー生成が改善されました。
  • -preview=fixImmmutableConv が追加されました。
  • 関数から void 値を返すことは非推奨になりました。
  • ImportCで typeof(...) 演算子が認識されるようになりました。
  • -transition=markdown-revert=markdown スイッチが削除されました。
  • new で連想配列を割り当てることができるようになりました。
  • extern(C++)-preview=in が使用できるようになり、他の非Dリンケージでは無効化されるようになりました。
  • 短縮されたメソッド構文がデフォルトで使用できるようになりました。
  • ソースファイルに Unicode の方向性のオーバーライドが含まれないようになりました。
  • Windows DMDでスタック制限が16MBに倍増されました。

改善点

  • Bugzilla 7372: エラーは、問題を診断するためにあまりにも少ない情報を提供します(エラー:未定義の識別子)。
  • Bugzilla 14690: pragma(inline, true) の関数は .di ファイルでそのボディを出力しなければなりません。
  • Bugzilla 16701: package.d ソースファイルモジュールがすべて小文字に強制される制限を削除する。
  • Bugzilla 17575: 名前付き mixin テンプレートエラーメッセージ
  • Bugzilla 21243: ラムダが auto ref を返せるようになります。
  • Bugzilla 21673: [SIMD][Win64] _mm_move_ss のコード生成が間違っている。
  • Bugzilla 22911: dtoh: 生成されたヘッダに対して include ディレクティブをソートするようにした。
  • Bugzilla 23079: [dip1000] ref return のアドレスを取得する処理をさらに緩和する。
  • Bugzilla 23141: -release スイッチの説明を改善しました。
  • Bugzilla 23142: スコープが単体テストに適用されるべきではない。
  • Bugzilla 23143: ImportC: 前方列挙型の宣言がサポートされる必要があります。
  • Bugzilla 23165: ラムダ関数がインライン化されていない。
  • Bugzilla 23191: [dip1000] スコープパラメータが @system のコードで返される可能性がある
  • Bugzilla 23216: Bidirectional Range でない foreach_reverse のエラーメッセージを改善しました。
  • Bugzilla 23284: 浮動小数点が表現できないエラーメッセージを強化しました。
  • Bugzilla 23295: [dip1000] スコープ推論が失敗する理由を説明しました。
  • Bugzilla 23306: @disable new(): @disable new()scope A = new A を無効化しないようにすべきです。
  • Bugzilla 23369: 重複したインポートのエラーメッセージが紛らわしい
  • Bugzilla 23376: マルチコードポイント HTML エンティティを許可する
  • Bugzilla 23384: 隠された定義を持つとき、マッチするベースクラスのメソッドを呼び出すことを提案する。

ランタイム変更点

変更点

  • core.cpuidavx512f の検出機能が追加しました。
  • --DRT-oncycle=deprecate が削除されました。
  • Posix (Darwinを除く)でデフォルトのGCシグナルが SIGUSR1/2 から SIGRTMIN/SIGRTMIN+1 へ変更されました。

改善点

  • Bugzilla 23456: OpenBSDで waitid のサポートが追加されました。

ライブラリ変更点

  • @safe および -preview=dip1000 で使用可能な SafeRefCounted が追加されました。
  • std.loggerexperimental から通常モジュールに移動されました。
  • std.experimental.logger のコンパイル時に最小の LogLevel を設定する機能を削除されました。
  • std.experimental.logger.core.sharedLogshared(Logger) を返すように変更されました。
  • std.experimental.typecons は削除されました。
  • std.digest.digest は削除されました。
  • std.xml は削除されました。
  • std.socket.Socket のメソッドがスコープ配列のみを受け付けるようになりました。
  • std.outbuffer.OutBuffer クラスにカスタム可能な既定値を追加しました。

改善点

  • Bugzilla 13893: rawRead は、空でないバッファを取らなければなりません。
  • Bugzilla 18735: findcanfind のすべてのバージョンは、述語の使用法を識別すべきです。
  • Bugzilla 21000: -preview=nosharedaccess における stdin, stdout, stderr の使用を取り除きます。
  • Bugzilla 23101: [std.sumtype]canMatchref を考慮しない。
  • Bugzilla 23298: std.stringwrap は早期にラップされます。
  • Bugzilla 23333: DList のRangeが @nogc にできます。
  • Bugzilla 23370: std.base64 はより多くの @nogc 関数を持つことができる。

DUB 変更点

  • テストランナー専用設定が追加されました。
  • 出力が色付けされるようになりました。
  • dub.jsonsettingsselections ファイル内の未認識のフィールドに対して警告を出すようになりました。
  • 2つの新しいビルドタイプ cov-ctfeunittest-cov-ctfe が追加されました。
  • DUBの設定とパッケージのディレクトリの配置パスが変更可能になりました。
  • dub コマンドの終了コードがより一貫したものになりました。
  • `install' と 'uninstall' コマンドは削除されました。
  • copyFiles が読み取り専用ファイルのコピーに使用された場合、書き込み可能な状態でコピーされるようになりました。
  • オーバーライドの仕組みが非推奨になりました。
  • dub run のショートカット構文がサブパッケージでも使用できるようになりました。
  • 全てのサブパッケージの一括アップグレードができるようになりました。

dlang.org変更点

  • Bugzilla 15286: is(typeof(symbol)) について記載しました.
  • Bugzilla 19036: .tupleof の順序保証について記載しました。
  • Bugzilla 22141 プロパティ .capacity が配列のプロパティセクションに記載しました。
  • Bugzilla 23186: wchar/dchar のエンディアンについて定義を記載しました。
  • Bugzilla 23359: InOutをParameterStorageClassに変更しました。

注目トピック

言語・コンパイラ変更点

実験的 : Dにビットフィールドが追加されました。

C言語に備わっているビットフィールドの機能がD言語でも使えるようになりました。
元々 ImportC の機能でC言語のソースコードを取り込めるように設けられたものですが、それをD言語のソース中でも書けるように機能を公開したという内容です。

コードのイメージは以下のようになります。
int x:3 のあたりが使うビット数を指定している感じですね。

struct B
{
    int x:3, y:2;
}

static assert(B.sizeof == 4);

int vaporator(B b)
{
    b.x = 4;
    b.y = 2;
    return b.x + b.y; // returns 6
}

なおこちらまだ実験的機能としての位置づけになっており、使うには -preview=bitfirlds というコンパイラスイッチが必要です。

動作自体もまだ安定していない恐れがありますので、利用は検証程度にしておくのが良いかもしれません。
(なんだか思った動きとちょっと違うところがあるような…?)

強化 : -O スイッチのあるコンパイル時に定義済みバージョン識別子 D_Optimized が追加されました。

されました。
これにより、コードが最適化を有効にしてコンパイルされているかどうかを区別することができます。

注意すべきは、-O スイッチが指定されたかどうかであり、-release スイッチが指定されたことを意味しない点です。

この点は類似のバージョン識別子として、assertD_NoBoundsChecksD_Invariants など個別の機能ごとに用意されていますのでこちらを利用してください。

定義済みバージョン識別子一覧 : https://dlang.org/spec/version.html#predefined-versions

実験的 : -preview=fixImmmutableConv が追加されました。

一部不適切なコードをエラーとして検出できるかもしれないフラグが追加されました。

内容としては、暗黙的に可変値を immutable に変換できる場合があるということで、それを検知してエラーにするものです。

たとえば、不変値を意味する immutable(int[]) な変数に int[] を代入できてしまい、意味論を崩すような場合がある、というものです。

int[] f(ref void[] m) pure
{
    auto result = new int[5];
    m = result;
    return result;
}

void main()
{
    void[] v;
    immutable x = f(v);
    // この時点で `v` は `x` に対する可変値の参照を持った状態になります
}

多くの場合 immutable な変数に妙な可変値を入れることは無いと思われ、念には念をということで一度 -preview=fixImmutableConv を指定してコンパイルしてみると良いかもしれません。

なおここでエラーが出た場合、戻り値を immutable で修飾することに加え、可変値を不変値に変換する std.exception モジュールの assumeUnique 関数を使って参照が1つ(ユニーク)であることをコードで保証してやる必要があります。

修正例
immutable(int[]) f(ref void[] m) pure
{
    auto result = new int[5];

    import std.exception : assumeUnique;

    return assumeUnique(result);
}

強化 : ImportCで typeof(...) 演算子が認識されるようになりました。

なりました。これにより更に ImportC で読み込めるCのソースコードが増えることになります。

C言語のコードをそのまま参照してリンクできるようになるので非常に便利な機能です。

ImportC の機能については、いくつか利用例として記事にもなっています。以下をご参照ください。

強化 : new で連想配列を割り当てることができるようになりました。

見出しからちょっとイメージ湧きづらいかと思いますが、コードにすると以下のようなことができるようになりました。

newで連想配列を初期化して2つの変数で参照する
int[string] a = new int[string]; // これが可能になった
auto b = a;
...
a["seven"] = 7;
assert(b["seven"] == 7);

実際の動きを考えてみると、連想配列といっても実体はポインタ等含む構造体です。
そして単に int[string] a; として宣言されただけではメモリは確保されておらず、何か値を追加しようとしたときにメモリが割り当てられます。

この動きの何が都合が悪いかというと、「キーを追加するまで実体がないので、1つの実体を指す複数の参照を持てない」ということです。

つまりこの機能強化によって、キーが挿入される前の2つの連想配列参照が、1つの同じ連想配列のインスタンスを指せるようになる、というのが今回の強化です。

実際には何か1層クラスでラップする等行えば同じことではあるのですが、間接参照が増えることを嫌う一部ユーザーから改善要望として出たようです。

強化 : 短縮されたメソッド構文がデフォルトで使用できるようになりました。

今まで -preview=shortenedMethods フラグを指定したときだけ使えた短縮メソッド構文が標準機能になりました。

具体的なコード例は以下のようなものです。

短縮メソッド構文の例
int add(int x, int y) pure => x + y;

// 完全なメソッド本体を持つ同等の宣言
int add(int x, int y) pure
{
    return x + y;
}

現代風というかなんというかですが、コード生成するときに都合が良いとかあるかもしれません。
ぜひぜひ便利に使っていきましょう。

改善 : ラムダが auto ref を返せるようになります。

auto ref という宣言は、参照が取れれば参照を取り、そうでなければ値渡しをする、という宣言です。

今までは関数の引数や戻り値で利用できましたが、これをラムダ式でも推論して使えるようになりました。

具体的な記述はそれぞれ以下のようになります。

// 戻り値が参照かもしれない関数
auto ref foo(ref R range) {
    return range.front;
}

// 戻り値が参照かもしれないラムダ式
auto foo = auto ref (ref R range) => range.front;

例にも出しましたが、主に想定されている用途は range.front というレンジオブジェクトで現在の値を取るプロパティです。
レンジの文脈では、front プロパティに代入が可能かどうかなど実装から様々な性質を調べ、その情報から使うべき関数を切り替えたりアルゴリズムを切り替えることで効率化したりします。

今まではこれが書けなかったことにより、ローカル関数を書いたりちょっと手間を要していたとのことで、今回から上手く書けるようになるシーンがあるかもしれません。

改善 : @disable new(): @disable new()scope A = new A を無効化しないようにすべきです。

@disable this(); は記憶にある方もいるかもしれませんが、今回は @disable new(); です。微妙に違うのでご注意ください。(私は勘違いしました)

仕様によると、@disable new() はGCによるメモリ割り当てを無効化する意味を持つ記述であり、new そのものを禁止するキーワードではないということです。

GC割り当てされない new の利用例として、具体的なのが scope で修飾された変数、つまりスタック割り当てされた変数の初期化です。

scope で修飾された変数にクラスの型を指定すると、ヒープではなくスタックに割り当てられます。
ここから、一切ヒープにメモリを取らないクラスというものが定義でき、便利に使えるようになるだろう、というのが今回の強化です。

スタックにしか配置されないが参照渡しが前提となる、というちょっと変わった性質を持つ型が定義できます。
何か面白い使い方ができそうではあり、どなたかアイデアがあればこちらぜひ使ってみてください。

ランタイム変更点

強化 : core.cpuidavx512f の検出機能が追加しました。

core.cpuid はCPU機能を調べたりするモジュールです。
ここに avx512f と呼ばれるCPUの命令群のサポートを検出する機能が追加されました。

調べた限り、具体的な命令としては VBROADCASTI64X4 などがあるようです。(正直まったくわからない)

また、この機能に期待される方であれば意味もお分かりかと思いますので詳細省かせていただきます。
使えるようになりましたのでぜひぜひ最適化にご利用ください。

変更 : Posix (Darwinを除く)でデフォルトのGCシグナルが SIGUSR1/2 から SIGRTMIN/SIGRTMIN+1 へ変更されました。

ランタイムの挙動が変更されました。
具体的にはGCが実行されたときのシグナルが SIGUSR1/2 から SIGRTMIN/SIGRTMIN+1 になります。

変更動機としては SIGUSR が Android Dalvik VM や LLVM libFuzzer で使われており衝突の可能性がある、SIGRTはユーザー定義の目的で予約されていて衝突の可能性が低いから、とのことです。

それでも変更により衝突するだろうとご心配の方がいると思いますが、その場合は core.thread.osthread.thread_setGCSignals() という関数で設定することができますので変更して利用くださいとのことです。

ライブラリ変更点

強化 : @safe および -preview=dip1000 で使用可能な SafeRefCounted が追加されました。

標準ライブラリでは任意の値型データを参照カウントで管理するための RefCounted 型が提供されています。しかしこれは @safe がついたスコープでは利用できませんでした。
今回これよりも更に限定的な属性が付いた状況で使える SafeRefCounted という構造体が追加されました。

まず試してみた限り利用条件がかなり特殊で、まず @safe が付いているのはもちろん -preview=dip1000 も付いていないと使えません。
(テンプレートから @safe かどうかを厳密に推論する都合?のようです)

その性質からもすぐにお世話になる機能ではなさそうですが、ちょっと面白い borrow 関数が新しく提供されるようになったのでサンプル交えてご紹介です。

// @safe pure nothrow と安全性に関する属性が多く付いた状況でも利用可能です
@safe pure nothrow void fun()
{
    import std.typecons;

    auto rcInt = safeRefCounted(5); // 通常の refCounted と同様の初期化手順で利用します

    assert(rcInt.borrow!(theInt => theInt) == 5); // borrowテンプレート関数の引数にラムダ式を指定して扱うスコープを限定します

    auto sameInt = rcInt;
    assert(sameInt.borrow!"a" == 5);

    // using `ref` in the function
    auto arr = [0, 1, 2, 3, 4, 5, 6];
    sameInt.borrow!(ref (x) => arr[x]) = 10; // borrowテンプレート関数にrefを返すラムダ式を指定すると書き換えを限定できる
    assert(arr == [0, 1, 2, 3, 4, 10, 6]);

    // modifying the payload via an alias
    sameInt.borrow!"a*=2"; // 引数が1つの関数とみなして処理を書くと
    assert(rcInt.borrow!"a" == 10);
}

borrow は日本語だと「借りる」という意味ですが、Rustなどでは「借用」と言われたりします。
参照カウントで管理しているデータの扱いをそのスコープに限定し、外に出さないような操作とすることで安全性を担保する、という方法になります。

GCも便利ですが、こういった機能が増えていくことで一層安全で効率的なコードが書けるようになるものと思われます。

強化 : std.loggerexperimental から通常モジュールに移動されました。

移動されました。

元々 std.experimental.logger としてかなり実験的な機能となっていましたが、今回から正式にサポートされる体制になりました。

使い方など含めてドキュメントも用意されていますので、下記参照のうえ是非使ってみてください。
通常版への移動ということで、今回若干挙動が変更されている箇所もあるようです。

なお、元は2015年ごろに追加された機能であり、下記の記事で説明されています。
そこまで機能差があるわけでもないので、基本的な利用方法はこちらを読み替えていただくのが良いかと思います。

変更 : std.socket.Socket のメソッドがスコープ配列のみを受け付けるようになりました。

変更というか改善というか、インターフェースがやや効率的なものに変わりました。

Socketクラスを使うことがそこまで多くないと思われますが、今回ターゲットとなるのは Socket.receiveSocker.send といったバッファの読み書きをするメソッドです。
引数に受け取るバッファの引数が scope で修飾されることにより、外部にコピーなど行われないことが明示されました。

import std.socket;

auto sock = new Socket;

scope byte[] buf = new byte[1000];
const len = sock.receive(buf); // ここで buf が receive 関数の外にコピーされたりしないことが保証される

これも元は DIP1000 におけるメモリ安全制約に対応する意味があり、着実に効率化や安全性への配慮が進んでいるということかと思われます。

強化 : std.outbuffer.OutBuffer クラスにカスタム可能な既定値を追加しました。

std.outbuffer.OutBuffer というのはバイナリ書き込みに使う便利クラスです。
あまり使われるのを見ることがないのですが、元々コンパイラの内部実装で使われたコードが下地になっており、様々なバイト列を効率的に構築することができるメソッドが備わっています。

細かい利用方法は省略しますが、今回追加されたのは fill メソッドと align2/align4/alignSize メソッドにおける既定値の設定です。

実際に追加された引数を使うコード例は以下のようになります。

fillメソッドにおける既定値の振舞い
OutBuffer buf = new OutBuffer();
buff.fill( 1234, 42 ); // 1234 byte分、42 という値で埋めます
buff.fill( 10 );       // 10 byte分、0で埋めます
align系メソッドにおける既定値の振舞い
OutBuffer buf = new OutBuffer();
buf.write(cast(ubyte) 1);
buf.align2(0x55); // 2 byte境界に達するまで 0x55 で埋めます
assert(buf.toBytes() == "\x01\x55");

buf.write(cast(ubyte) 2);
buf.align4(0x55); // 4 byte境界に達するまで 0x55 で埋めます
assert(buf.toBytes() == "\x01\x55\x02\x55");

buf.write(cast(ubyte) 3);
buf.alignSize(8, 0x55); // 8 byte境界に達するまで 0x55 で埋めます
assert(buf.toBytes() == "\x01\x55\x02\x55\x03\x55\x55\x55");

DUB変更点

強化 : テストランナー専用設定が追加されました。

強化の内容としては、dub test で実行されるファイルを実行せずにバイナリだけビルドできるようになった、というものです。(言われて見ると確かに今までできなかった気がする)

具体的には dub build --config=unittest --build=unittest というコマンドで単体テスト用の実行ファイルを「作るだけ」という操作ができます。
なおこの時 dub.sdlunittest という configuration の設定は用意しなくてもOKです。

テスト時のバイナリをテスト前に確保しておくということができるようになったり、繰り返しテスト実行することで何か便利に使えるのかもしれません。

強化 : 出力が色付けされるようになりました。

便利な強化が来ました。コンソールの出力が少し綺麗になります。
元々エラーであれば赤で表示するなどされていましたが、今回はそれ以外のところも緑や青で表示されるようになりました。

Node.jsにおける npm を始めとして、コンソールは綺麗でにぎやかな方が良いだろうという意見は常々あろうかと思います。
これでまた1つ気持ちよく開発できるようになると良いなと思います。

ただ邪魔なこともあろうということで、--color=off というフラグを付けると無効化できるようにもなっています。

dub build --color=off という感じです。
便利に使っていきましょう。

強化 : 2つの新しいビルドタイプ cov-ctfeunittest-cov-ctfe が追加されました。

dub run --build=xxx とするときの xxxcov-ctfeunittest-cov-ctfe が追加されました。
元々 --build=cov--build=unittest-cov という設定はあり、これを拡張した形です。

名前からしてCTFE(コンパイル時関数実行)のカバレッジを取ることができる機能だと分かるのですが、
DUB には元々 --coverage-ctfe というフラグがあり、CTFEにおけるカバレッジの取得もサポートされています。

これを別々に設定するのではなく、--build=xxx という形式で一発で設定できるようにした、というのが今回追加された2つのフラグです。

なぜフラグを統合するような設定が必要だったのか?と考えてみると、恐らくVSCodeのビルド設定で --coverage=ctfe のフラグを簡単に設定できる口が無かったからだと思われます。
--build=xxxであれば設定するUIが設けられていて、それを設定すればショートカットキーでビルドするときもカバレッジが取られます。

コンパイル時計算のカバレッジというのもなんだか不思議な感じですが、効率的な開発ができるということで結構珍しい機能と思われ、ぜひ一度設定して試してみてみてください。

強化 : DUBの設定とパッケージのディレクトリの配置パスが変更可能になりました。

DUBがダウンロードしたパッケージを置く場所や、ユーザー設定を保存する場所を環境変数やdubの設定ファイルで指定可能になりました。

デフォルトでは、DUBはパッケージを以下の場所に保存します。

  • Windows
    • %APPDATA%/dub/settings.json および %LOCALAPPDATA%/dub/packages/
  • Posix
    • $HOME/.dub/settings.json および $HOME/.dub/packages

ここでもし環境変数として DUB_HOME が設定されている場合、settings.jsonpackages/ といったディレクトリがそのフォルダパスに保存されるようになります。

また、DUB_HOME が設定されておらず、DPATH が設定されている場合は、次のパスが使用されます。

  • <DPATH>/dub/packages/
  • <DPATH>/dub/settings.json

DPATH環境変数は、ユーザースペースでのインストールを行うすべてのDツール関連で使用されることを意図しています。
これは、ユーザーのホームフォルダを乱雑にしないために使うことができます。

環境変数に加えて、DUBのsettings.jsonファイルを通してパッケージ配置パスとsettings.jsonパスを設定することが可能です。
ユーザーが編集可能なsettings.jsonが置かれる場所を設定するには、システム全体のダブ設定を調整する必要があります。

settings.jsonでは、以下のフィールドを設定することができます。

{
    "dubHome": "/path/to/dub", // パッケージストアと設定場所の両方を設定します。
}

さらに、これらの設定パスは、$DUB_HOME という環境変数の参照としてコマンド実行時スクリプト内で用いることができます。
次のリストは、どのパスが選ばれるかを上から下へ、見つかるたびに停止するように記述しています。

  • 環境変数 $DUB_HOME
  • 環境変数 DPATH
  • システム全体の settings.json: "dubHome" プロパティ (userSettings のみ)
  • setting.json: "dubHome "プロパティ(localRepositoryのみ)

なおこちら日本語らしくするだけで力尽きています。正確な内容についてはリリースノートを参照してください

強化 : dub コマンドの終了コードがより一貫したものになりました。

以下のコマンドのリターンコードが変更されました。
成功が 0、使い方の誤りに対しては 1、その他一般的なエラーやファイルダウンロードに関するエラーなどが 2 となります。

  • dub clean
  • dub add
  • dub search
  • dub convert

変更 : copyFiles が読み取り専用ファイルのコピーに使用された場合、書き込み可能な状態でコピーされるようになりました。

今回の変更により、copyFiles でコピーされるファイルが読み取り専用であっても書き込み可能な状態でコピーされるようになります。

単純に、繰り返し実行を可能にしようとすればこうするしかないのはほぼ自明なのですが、以前はこの挙動がないためアクセス拒否エラーが発生することがあり、そのたびに手で削除するという手間がありました。

ただ今までどの程度エラーに遭遇していたかを考えると、そこまで恩恵を受ける方は多くないかもしれません。

なぜ読み取り専用ファイルをコピーする事態になるかというと、Gitなどのバージョン管理システムでは競合のある作業用バイナリファイルを読み取り専用にする挙動があるためです。
競合がある故に下手に上書きが起こらないようにする動作が一般的で、それらの競合を含むファイルがcopyFilesのターゲットになっているとエラーが起きる、という状況だったようです。

今後は特に気にする必要もなくなりますが、逆に読み取り専用でないと困る場合はコピー後に別途設定する必要があるため注意してください。

強化 : dub run のショートカット構文がサブパッケージでも使用できるようになりました。

dub run :subpackage という構文でサブパッケージを直接実行できるようになりました。

サブパッケージを使いこなしている方には結構嬉しい強化かもしれません。

強化 : 全てのサブパッケージの一括アップグレードができるようになりました。

dub upgrade というコマンドに -s という引数が追加され、サブパッケージもまとめて一括でアップグレードできるようになりました。

通常サブパッケージは、ルートになる親パッケージが子のサブパッケージを参照する形で利用します。
親だけ依存関係を更新してもサブパッケージの更新忘れがあると依存関係の解決エラーになるなどの問題があります。

サブパッケージが多いほど面倒であり、なんとか一発でアップグレードできないか、というので設けられたのが今回のフラグのようです。

dub upgrade -s とすれば良いだけなので、皆さんアップグレードすることがあれば思いだしてみてください。

非推奨または廃止される機能

今回は6ヶ月分の変更が一斉に行われたのもあり、非推奨や機能削除は相応の数となっています。
追加の非推奨が6件、エラーとなるものが5件です。

ただしあまり使われない機能であったりするので影響を受けそうな内容についてはごく一部です。
すべて適切な対処方法がありますので一つ一つ解説していきます。

また今後の予定などは以下のページにまとまっていますので、こちらも参考としてみてください。

非推奨

  • nothrow 関数の制約から throw する処理が非推奨になりました。
  • versiondebug の条件に整数を使用することは非推奨になりました。
  • スコープポインターのエラーに対する印刷は非推奨になりました。
  • 関数から破棄された void 値を返すことは非推奨になりました。
  • dub のオーバーライドの仕組みが非推奨になりました。
  • dub.jsonsettingsselections ファイル内の未認識のフィールドに対して警告が出るようになりました。

削除/エラー

  • -transition=markdown-revert=markdown スイッチが削除されました。
  • --DRT-oncycle=deprecate が削除されました。
  • std.digest.digest は削除されました。
  • std.xml は削除されました。
  • dub の `install' と 'uninstall' コマンドは削除されました。

非推奨 : nothrow 関数の制約から throw する処理が非推奨になりました。

関数属性として nothrow が指定されている場合、以下のような事前条件/事後条件から例外を投げるコードが非推奨になりました。
関数属性のチェックロジックをすり抜けるケースがあるということで要注意のパターンです。

警告例

int add(int x, int y) nothrow
in {
    if (x < 0)
        throw new Exception("x must be greater than 0");
}
do {
    return x + y;
}

代わりに assert を使って記述することにより問題なくなります。

修正例

int add(int x, int y) nothrow
in {
    assert(x < 0, "x must be greater than 0");
}
do {
    return x + y;
}

assertAssertError という Error クラスの派生を throw するため、Exceptionthrow するとエラーになるという nothrow 属性の制約には引っかからないためです。
もしも例外をキャッチして処理していた場合、上位の catch (Exception ex) {}catch (AssertError ex) {} に書き換える必要があるかもしれません。

非推奨 : versiondebug の条件に整数を使用することは非推奨になりました。

あまり使っている方もいないと思いますが、対象となるのは以下のようなコードです。

// 以下は非推奨です
version = 3;
version (2) { }

debug = 4;
debug (5) { }

これに対し、今後は versiondebug は識別子として名前を割り当てて使う必要があります。

元々は数字の大小関係を用いて都合の良い分岐と条件コンパイルができないか、ということで出来た仕様ですが、
これを識別子で書き換えると以下のようなコードになります。

version (Ver3_0) {
    version = Ver2_0;
}
version (Ver2_0) {
    version = Ver1_0;
}

version (Ver3_0) {
    // ver3の機能
}
version (Ver2_0) {
    // ver2の機能
}
version (Ver1_0) {
    // ver1の機能
}

非推奨 : スコープポインターに対する警告が表示されるようになりました。

スタック変数のポインタを返そうとした場合にエラーとなるパターンが増えました。
具体的には以下のコードです。

@safe:
int[] getSlice()
{
    int[4] stackBuffer;
    int[] slice = stackBuffer[]; // スライスはスタック変数を指しています
    return slice; // ここで返った配列は、関数を抜けた時点で無効な位置を指しています
}

struct S
{
    int x;

    int* get()
    {
        int* y = &this.x; // この構造体はローカル変数になりえます
        return y; // ここで返ったポインタは、構造体が破棄された後に無効化されます
    }
}

このようにスタック変数を戻り値として返すコードがいくつか警告になります。
1例目の getSlice はどうにもなりませんが、2例目の get は関数を scope で修飾することにより対応できます。

struct S
{
    int x;

    int* get() scope // ここにscope修飾をすると戻り値が構造体由来であることを示せる
    {
        int* y = &this.x;
        return y;
    }
}

なお、今回の対応は変数の有効期間を計算する仕様である DIP1000 という取り組みから得られた成果を部分的に標準化したものとのことです。
警告をエラーとして扱うには -preview=dip1000 をつけ、警告を消すには -revert=dip1000 をコンパイラフラグに指定します。

非推奨 : 関数から void 値を返すことは非推奨になりました。

「文法上書くことはできるが、結果がよく分からない記述」に対して警告が出るようになりました。
例としては以下のようなコードです。

コード例
struct StackBuffer
{
    auto opIndex(size_t i) // 戻り値の型はどうなる?
    {
        return arr[i]; // void値を返す?
    }

private:
    void[] arr;
}
警告の例
test.d(7): Deprecation: `this.arr[i]` has no effect

void[] という型が出てきますが、これは任意の配列を格納できる特殊な配列型です。長さは中身のデータによらずバイト数を示し、ファイルIOなど比較的低レベルな操作を行う場合にしか目にしないような型です。
Void Arrays: https://dlang.org/spec/arrays.html#void_arrays

ここで、実際にこの void[] な配列から値を取り出して返すコードが上記のサンプルです。

意味論としては1バイト分のデータが得られそうですが、ソースコード上の表現は void であり、これを推論すると opIndex の戻り値も void と「戻り値が無い」という意味合いのコードになります。
また実際のところ、void[] に対する直接的な値取得操作は無効である(取った値を戻り値にしたり変数に入れることができない)ため、これは単に無効なコードとなります。

元から動かないコードなので特に回避策というわけでもありませんが、
これが本当に1バイト分のデータを返すことを想定している場合、以下のように書き換えることで警告を回避することができます。

    auto opIndex(size_t i)
    {
        // return arr[i];
        return (cast(ubyte[])arr)[i]; // 配列の時点で任意の要素型を持つ配列にキャストしてから取り出す
    }

非推奨 : dub のオーバーライドの仕組みが非推奨になりました。

DUBには、指定したパッケージの特定のバージョン(およびバージョンの範囲)を別のパッケージで「オーバーライド」する仕組みを持っていました。
ある依存関係が書かれていても、そこを無理やり他のパッケージにすり替えるような機能です。

実際のコマンドとしては dub add-override <package> <version-spec> <target-path/target-version> といった記法で上書き設定をします。
同様に dub remove-override というコマンドもあり、今回この2つのコマンドが非推奨となりました。

元々は依存パッケージを管理するためにバージョンベースのアプローチが採用されたことで用意された背景があります。
しかしその後、パスによる依存関係の設定方法が追加されたことでこの方法は冗長になった、というのが非推奨の理由です。

なお、DUBは依存関係を解決後に dub.selection.json という最終的にパッケージバージョンをどれにするか定めたファイルを生成するため、これを手で指定したいバージョンに書き換えれば強制的に特定のバージョンを利用することができるようです。

非推奨 : dub.jsonsettingsselections ファイル内の未認識のフィールドに対して警告が出るようになりました。

dub.json / settings.json / dub.selections.json といった DUB の関連ファイルに認識できないフィールドがあると警告が出るようになりました。

元々の動作は、意図しないフィールドがあっても無視するだけでした。
これは当初の目的として前方互換性を考えての仕様でしたが、実際にはタイプミスがあるとユーザー設定が無視されることになり、これは害の方が大きいと見直された格好です。

というわけで警告が出た場合はファイル名やキー名が印字されるようになりますので、メッセージを確認して修正してください。

dub.sdl がここに含まれない理由はなぜでしょうか…?)

削除/エラー : -transition=markdown-revert=markdown スイッチが削除されました。

ドキュメントコメントにおけるMarkdownサポートが標準機能となり、これを元に戻すためのスイッチ類が削除されました。

このMarkdownサポートが機能として導入されたのは2019年7月の2.087.0、標準で有効となったのが2020年9月の2.094.0でした。
https://qiita.com/lempiji/items/602c46b379f3c2af3d25#%E6%96%B0%E6%A9%9F%E8%83%BDddoc%E3%81%ABmarkdown%E3%82%92%E5%8F%82%E8%80%83%E3%81%A8%E3%81%97%E3%81%9F%E6%A9%9F%E8%83%BD%E3%81%8C%E8%BF%BD%E5%8A%A0%E3%81%95%E3%82%8C%E3%81%BE%E3%81%97%E3%81%9F
https://qiita.com/lempiji/items/eaf99abc35fe0a55a3d1#%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E5%A4%89%E6%9B%B4%E7%82%B9

機能強化された際に少し互換性の調査を行いましたが、<>を使った際のエスケープに注意するくらいで他は特に影響なさそうです。
しっかりドキュメント書いていくようにしたいと思います。

削除/エラー : --DRT-oncycle=deprecate が削除されました。

このオプションは 2.072.2 で導入され、モジュールコンストラクタに対して欠陥のある循環参照チェックを有効にするものでした。

このオプションは警告を表示し、デフォルトの --DRT-oncycle=abort と同じ動作をするようになりました。

詳細は下記のモジュール構築順序についてのページを参照してください。
https://dlang.org/spec/module.html#order_of_static_ctor

削除/エラー : std.digest.digest は削除されました。

std.digest.digest というモジュールが削除されました。

このモジュールは 2.076.1 で非推奨となり、2.092.0 以降は空の状態です。
非推奨のシンボルはすべて削除されたので、代わりに std.digest またはそのサブモジュールを利用してください。

削除/エラー : std.xml は削除されました。

消える消えると噂されていた std.xml モジュールがついに削除されました。

代替どうするの?については過去に非推奨となった際まとめていますのでこちらも参照ください。

しかし、もしまだこれらが必要な場合は undeaD というパッケージでまったく同じ機能が提供されます。

またドキュメントは破棄される前時点で止まっていますが、下記から普通に参照することができます。(dubパッケージのドキュメント生成機能です)

実際に使ってみると以下のようになります。

dubの依存関係を追加
dub add undead
undead.xmlとして利用
import std;
import undead.xml; // std は undead に置き換える

void main() {
    auto doc = new Document(readText("test.xml"));
    writeln(doc);
}

削除/エラー : dub の `install' と 'uninstall' コマンドは削除されました。

これらのコマンドは長らく非推奨として扱われ、それぞれ fetchremove のエイリアスとして動作していました。
これらを使用すると警告が発生しましたが、今後はもうヘルプには表示されませんし、dubも認識しなくなります。

「DUBを使って任意のパッケージをダウンロードしてきて実行したい」というのが元々の欲求であるわけですが、これは単に dub fetch および dub run の組み合わせにより実行できます。

たとえば以下のコマンドを入力するとコンソールをD言語くんが颯爽と駆け抜けるアニメーションを見ることができます。

dub run dl

様々なツールがDUB経由で利用できるので、便利なツールがないか是非探してみてください。

まとめ

バージョン番号が 2.100.0 という大台を超え、前回リリースから半年以上の期間を経て無事リリースとなりました。

機能強化はもちろん改善やバグ修正も多数ありすべてを追いかけることは困難ですが、着実に進歩する姿勢を見せています。
クラスに対する @disable new(); など面白い新機能があるほか、dubのコンソールが色付けされるなど、ツールとしての体裁についても気を配られているように思います。

ただ今回の記事はリリース直後に翻訳と公開ができず、アドベントカレンダーの時期になってから半分以上を埋める形になってしまいました。
無計画さなど反省するところも多いのですが、逆にアドベントカレンダーの記事が一つできたということで、勝手ながらポジティブに捉えていきたいと思います。

さて、2022年も残り1か月を切りました。
残りの期間も様々な分野で続々と記事が公開されるかと思いますので、引き続き楽しくプログラミングしてまいりましょう!

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1