LoginSignup
15
3

More than 3 years have passed since last update.

D言語の更新まとめ 2020年7月版(dmd 2.093.0)

Last updated at Posted at 2020-06-27

はじめに

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

今回は比較的控えめのアップデートなので、リリースノートの内容全種紹介していきたいと思います。
※ 2020/07/12追記 ベータが取れて正式版になったところ、改善点がだいぶ増えたので抜粋に戻します

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

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

変更点目次

コンパイラ変更点

  • ビルトインの __traits(isCopyable, T) が追加されました
  • インターフェースの実装が適切にチェックされるようになりました
  • shared 変数の初期化が許可されるようになりました
  • templateの統計を収集、一覧化するための -vtemplates スイッチが追加されました

コンパイラ強化点

※2020/07/12 追記

  • Bugzilla 20791: extern(C++) が末尾のコンマを許容するようになりました
  • Bugzilla 20796: package(x.y) 形式の保護属性が宣言と同じモジュールを許容するようになりました

ランタイム変更点

  • core.sys.linux.unistdexit_group が追加されました
  • 新たなモジュールとして core.sys.darwin.mach.nlistcore.sys.darwin.mach.stab が追加されました
  • object.selector が非推奨になりました
  • メモリ関連のGCオプションがより高精度で指定できるようになりました

ランタイム強化点

※2020/07/12 追記

  • Bugzilla 20746: ガベージコレクタで使われる LCG(線形合同法) で状態bitのうち48bitしか使われなかったものが64bitすべてを使うようになります
  • Bugzilla 20787: core.sys.darwin.sys.attr が追加されました
  • Bugzilla 20844: cas の呼び出し後、データのアラインメントを考慮するようになります
  • Bugzilla 20859: ReadWriteMutextryLock にタイムアウトを指定できるようになりました

ライブラリ変更点

ライブラリ強化点

  • Bugzilla 19525: Duration に対する std.algorithm.sum の引数なし呼び出し時エラーが改善されました
  • Bugzilla 20496: chunkByref 引数による述語をサポートします
  • Bugzilla 20869: std.algorithm.mutationmoveonPostMove の属性を考慮するようになりました
  • Bugzilla 20887: 配列でないレンジに対する std.digest.digest!(Hash, Range) の速度が改善されました

インストーラー変更点

  • インストールスクリプトが Windows のコマンドプロンプトで動作するようになりました

DUB変更点

  • ルートパッケージのターゲットに関する環境変数が追加されました
  • CLIの拡張性が改善されました

ツール変更点

ツール強化点

※2020/07/12 追記

  • rdmd --eval が引数をサポートするようになりました

注目トピック

言語・コンパイラ変更点

強化:ビルトインの __traits(isCopyable, T) が追加されました

T がコピー可能かどうかを確かめる __traits が増えました。

通常の値型や参照型はコピー可能で true@disable this(this); でコピー禁止にすると false になる、というものです。

元々 __traits(compiles, {}) を使った書き方でも実現できていて、 std.traits にも同名の isCopyable テンプレートがありましたが、利便性等の面からビルトインの機能として提供されるようになりました。

修正:インターフェースの実装が適切にチェックされるようになりました

インターフェースが要求するメソッドをすべて実装しているチェックが正しく動作するようになりました。

といっても無条件に起きるバグではなく、ドキュメント生成やインターフェース定義ファイル(.di)を出力するときによく使う -o- スイッチ(オブジェクトファイルを生成しなくする)を付けるとチェックがスキップされてしまうという問題です。

元々コンパイルフェーズの後半に行われており、コンパイルの処理順序を見直して前半に移動することで修正された格好です。

もし -o- スイッチを使っていることがあればエラーにならないか一度チェックしておくと良いかと思います。

チェックされない例
interface Foo
{
    void foo();
}

class Bar : Foo
{
    // void foo() { }
}

改善:shared 変数の初期化が許可されるようになりました

別に shared 変数普通に初期化できるじゃん…?と思っていたのですが、
実際は -preview=nosharedaccess を使っているときのロジック改善でした。
(ChangeLogにちゃんと書いてありました)

このスイッチは、今年1月に採択された DIP1024 で定義される「shared 変数への直接アクセスを禁止する」という変更を先取りするためのものです。
core.atomicatomicLoadatomicStoreatomicOp 等を使うことを強制する)

たとえばフラグを付けると以下のようなコードでコンパイルエラーが発生しましたが、初期化の時点でなんでやねん、という話なので許可されます、という話です。

void main()
{
    shared int n;
    shared int m = 3;
}
onlineapp.d(3): Error: direct access to shared n is not allowed, see core.atomic
onlineapp.d(4): Error: direct access to shared m is not allowed, see core.atomic

強化:templateの統計を収集、一覧化するための -vtemplates スイッチが追加されました

テンプレートの特殊化が何種類、計何回行われたか、を示す統計情報が出力できる -vtemplates フラグが追加されました。
-vtls など、 -v で始まる系のフラグは情報表示が増えるシリーズなので覚えておくと良さそうです。

試しに import std.stdio; するだけのコードで出してみると以下のようになります。

import std.stdio;
void main() { }
  Number   Unique   Name
       1        1   isFunctionPointer(T...) if (T.length == 1)
       2        1   at(R)(R[] r, size_t i)
       1        1   isDynamicArray(T)
       1        1   Impl(T)
       1        1   isDelegate(T...) if (T.length == 1)
       1        1   atomicFetchSub(MemoryOrder ms = MemoryOrder.seq, T)(ref shared T val, size_t mod) if (__traits(isIntegral, T) || is(T == U*, U))
       1        1   IntOrLong(T)
       3        1   isAggregateType(T)
       3        1   TailShared(U) if (!is(U == shared))
       1        1   needsLoadBarrier(MemoryOrder ms)
       1        1   atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref const T val) if (!is(T == shared(U), U) && !is(T == shared(inout(U)), U) && !is(T == shared(const(U)), U))
      13        1   hasUnsharedIndirections(T)
       7        2   atomicValueIsProperlyAligned(T)(ref T val)
       1        1   atomicFetchAdd(MemoryOrder ms = MemoryOrder.seq, T)(ref T val, size_t mod) if ((__traits(isIntegral, T) || is(T == U*, U)) && !is(T == shared))
       2        1   atomicFetchAdd(MemoryOrder ms = MemoryOrder.seq, T)(ref shared T val, size_t mod) if (__traits(isIntegral, T) || is(T == U*, U))
       1        1   atomicFetchSub(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) if (is(T : ulong))
       2        1   atomicFetchAdd(MemoryOrder order = MemoryOrder.seq, bool result = true, T)(T* dest, T value) if (is(T : ulong))
      71        1   __equals(T1, T2)(T1[] lhs, T2[] rhs)
       1        1   atomicLoad(MemoryOrder order = MemoryOrder.seq, T)(inout(T)* src) if (CanCAS!T)
       3        2   RegIndex(T)
       2        2   atomicPtrIsProperlyAligned(T)(T* ptr)
       1        1   classInstanceAlignment(T) if (is(T == class))
       3        3   SizedReg(int reg, T = size_t)
       1        1   TailShared(S) if (is(S == shared))
       3        2   Unqual(T)
       1        1   OriginalType(T)
       9        3   atomicOp(string op, T, V1)(ref shared T val, V1 mod) if (__traits(compiles, mixin("*cast(T*)&val" ~ op ~ "mod")))
       3        3   makeGlobal(StdFileHandle _iob)()
       6        4   Flag(string name)
       1        1   ModifyTypePreservingTQ(alias Modifier, T)
       3        3   maxAlignment(U...)
       1        1   CanCAS(T)
       1        1   isPointer(T)
       6        1   atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref const shared T val) if (!hasUnsharedIndirections!T)
       2        1   Demangle(T)
       1        1   atomicFetchSub(MemoryOrder ms = MemoryOrder.seq, T)(ref T val, size_t mod) if ((__traits(isIntegral, T) || is(T == U*, U)) && !is(T == shared))
       1        1   DynamicArrayTypeOf(T)

本気で分析するならもう少し細かい情報が欲しい気もしますが、元々 std.metaFilter 等をunrollして高速化する話があり、そこで得られた知見のフィードバックといったところでしょうか。
単純に特殊化の回数を減らす目的としては十分な情報だったようです。

コンパイル時間の改善にもつながりますし、メタメタしたプログラムを書く方には非常におすすめの機能なのでぜひ1度試してみてください。

思わぬテンプレートが見つかるなどの発見があるかもしれません。

改善: extern(C++) が末尾のコンマを許容するようになりました

※2020/07/12 追記

以下のような記述が許容されるようになります。
メタプログラミングや自動生成するときに少しさぼれますし、たくさんあるときの改行や追加時の差分が見やすいので良いですね。

extern(C++, "foo",)

※こういう文法周りの改善は地味でもシンタックスハイライトが追い付かなかったりすると大変そうですね、、

改善: package(x.y) 形式の保護属性が宣言と同じモジュールを許容するようになりました

※2020/07/12 追記

package 保護属性というのは、同一パッケージ内、または他の指定したモジュールからだけ参照させる指定ができるアクセス保護属性です。

この改善は、同一パッケージ内でだけ参照できる、つまり private と同様の働きを package 保護属性でも実現できるようになります。
メタプログラミングとかの都合でしょうか、あまり使う機会はないかもしれませんが、自由度が上がったので良いことだと思います。

module somelib;

package(somelib) void foo() { }

ランタイム変更点

強化: core.sys.linux.unistdexit_group が追加されました

見出しの通りです。
Linux 2.5.35以降で使える、現在のプロセス内の全スレッドを終了させるシステムコールです。

強化: 新たなモジュールとして core.sys.darwin.mach.nlistcore.sys.darwin.mach.stab が追加されました

MacOSX向けで、それぞれC/C++向けヘッダーの mach-o/nlist.hmach-o/stab.h に相当します。

MacOSX 10.15 (Catalina) のSDKで追加されたものということで、2019年10月リリースの2020年6月時点では最新のSDKまで対応が進んできた、ということになります。

強化: メモリ関連のGCオプションがより高精度で指定できるようになりました

メモリの少ないデバイス向けの強化で、各種メモリのサイズ指定に B, K, M, G といった接尾語を付けることで細かい指定ができるようになりました。

GCオプションはプログラム中に埋め込む方法と実行時に与える方法がありますが、それぞれ以下のようになります。

埋め込み
extern(C) __gshared string[] rt_options =
    [ "gcopt=minPoolSize:4K maxPoolSize:2M incPoolSize:8K" ];
実行時
app "--DRT-gcopt=minPoolSize:4K"

今まで通り数字だけ指定した場合はメガバイト単位での指定になります。

強化: ReadWriteMutextryLock にタイムアウトを指定できるようになりました

※2020/07/12 追記

ReadWriteMutex は、データの読み取りを管理する Reader と書き込みを管理する Writer が協調するロック機構です。

読み取りと書き込みを分けることでデータ競合が起きないことを保証でき、排他される機会を減らし効率化できます。
詳細はこちら。

今回追加されたのは、lock が取得できるか試すために使う tryLock で、これに引数で時間間隔を表す Duration が指定できるようになりました。

auto rwmutex = new ReadWriteMutex;

const locked = rwmutex.writer.tryLock(1.seconds); // 1秒以内にロックがほしい
if (locked) {
    scope (exit) rwmutex.writer.unlock();
    // データの書き込み
}

tryLock で一定時間待つような実装をしている場合、これで置き換えることができます。
記述が簡単になるのはありがたいですね。

ライブラリ変更点

改善: Duration に対する std.algorithm.sum の引数なし呼び出しができるようになりました

※2020/07/12 追記

元々、以下のようなコードがエラーになっていました。

auto s = [1.minutes, 1.minutes].sum();
assert(s == 2.minutes);

これは Duration がやや特殊な構造体で、単一の数値を表す構造体にもかかわらず Duration(0) という形式でゼロに対応する値が作れない、ということに起因しています。
Duration0Duration.zero で取得します)

これが今回から .zero のプロパティを考慮するようになり、上記コードも動くようになります。

強化: chunkByref 引数による述語をサポートします

※2020/07/12 追記

chunkBy というのは配列などをブロック毎に区切るための関数です。

基本の使い方
auto s = [1, 1, 1, 2, 2, 2, 3, 3, 3];
writeln(s.chunkBy!((e1, e2) => e1 == e2));
結果
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]

今回から以下のように ref がつけられるようになります。これで効率化されるシーンがあるかもしれません。

auto s = [1, 1, 1, 2, 2, 2, 3, 3, 3];
writeln(s.chunkBy!((ref e1, ref e2) => e1 == e2));

改善: std.algorithm.mutationmoveonPostMove の属性を考慮するようになりました

※2020/07/12 追記

あまり使うことはないかもしれませんが、move はムーブ対象がコピー可能な場合、そのオブジェクトの onPostMove というメソッドを実行するようになっています。

この部分が今まで @trusted を付けた実装になっており、opPostMove@system でも無視されてしまっていました。
これがコンパイラによって正しくチェックされるようになり、以下のようなコードがエラーになるようになります。

import std.algorithm.mutation;

struct S
{
    void opPostMove(const ref S old) @system nothrow pure
    {
        assert(a == old.a);
        a++;
    }
    int a;
}

void main () @safe nothrow
{
    S s1;
    s1.a = 41;
    S s2 = move(s1);
    assert(s2.a == 42);
}

改善: 配列でないレンジに対する std.digest.digest!(Hash, Range) の速度が改善されました

※2020/07/12 追記

これは以下の要領で任意のRangeデータからハッシュが求められる関数です。

import std.digest.md;
import std.range : repeat;
auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
auto md5 = digest!MD5(testRange);

速度改善の結果や差分は以下にまとまっています。
愚直にパターン毎の実装を切り替え、全体的に実行時間が半分以下になった、という感じです。

インストーラー変更点

強化: インストールスクリプトが Windows のコマンドプロンプトで動作するようになりました

文字通りの強化です。
インストールスクリプトというのは dub や dmd をコマンド実行でインストールする仕組みのことです。

インストールスクリプトの説明は以下のあたりが詳しいです。

ただ、install.sh というファイルはそのままで、特に install.bat ができたわけでも install.sh をダブルクリックして実行できるわけでもありません。

どういうことかというと、今までは POSIX のターミナルエミュレータでしか動かすことができなかったものが、MSYS2がインストールされている場合、それを使ってコマンドプロンプトから実行できるようになった、という強化です。

MSYS2C:\msys64 にインストールされているとして、以下のように実行できます。

準備
C:\msys64\usr\bin\pacman.exe --sync unzip p7zip
インストール
C:\msys64\usr\bin\curl.exe https://dlang.org/install.sh | \msys64\usr\bin\bash.exe -s
ヘルプ
C:\msys64\usr\bin\bash.exe %USERPROFILE%\dlang\install.sh --help

ガッツリbash等使っており、要するにLinux環境と同じことをやっているだけです。
ただ、わざわざターミナル起動しなくて良いので、batファイルとかにまとめれば良いのはありがたいですね。

差分はこちら。

対応されたのは Extended-Pascal から D へのトランスパイラを書いている Bastiaan Veelo という方です。
DConf 2019の登壇者の方ですね。

DUB変更点

強化: ルートパッケージのターゲットに関する環境変数が追加されました

dubの設定ファイルで使える以下の環境変数が追加されました。

いずれもルートパッケージの情報を取得できるもので、
有り体に言って、参照された側が参照している側(大元)について情報を取得できます

  • DUB_ROOT_PACKAGE_TARGET_TYPE
    • ターゲット種別、実行ファイルかライブラリか等
  • DUB_ROOT_PACKAGE_TARGET_PATH
    • ターゲットパス、ルートパッケージの設定ファイルのある場所
  • DUB_ROOT_PACKAGE_TARGET_NAME
    • パッケージ名

ターゲット種別が分かるのは結構ありがたいですね。
単体テスト系のライブラリで動きを変えるとか色々使い道がありそうです。

強化: CLIの拡張性が改善されました

DUBをライブラリとして利用している方向けの強化です。
主にコマンドを追加しやすくするため、引数解析周りの改善が行われたというものです。

dub のようにいくつかのサブコマンドを持っていたりすると、 -- で区切って実行時に渡す引数と分けることがあります。

これは単体テストを見ると分かりますが、引数を以下のように分類して扱うものです。

コマンド コマンド引数 アプリケーション引数
app --version なし --version
app a b a, b なし
app a --version a, --version なし
dub -- a なし a
app 1 2 -- a 1, 2 a

実装としては、 CommandArgs クラスに hasAppArgsappArgs という「アプリケーション用の引数」に関するメソッドが追加されています。

実際の差分はこちらです。

ツール変更点

強化: rdmd --eval で引数が渡せるようになりました

※2020/07/12 追記

rdmd --eval は、渡した文字列をコンパイルして実行するところまで一発でやってくれる補助ツールです。

rdmd--eval という引数で文字列を渡すとそのまま実行してくれます。

rdmd --eval="writeln(`Hello, world!`);"

これに動的な要素を埋め込もうと思うと、ひと手間エスケープする必要があって面倒でした。
今後は -- で区切ったあとの引数が string[] args が使えるようになり、より簡単に動的な要素が埋め込めます。

引数を使う例
rdmd --eval="writeln(args[1 .. $]);" -- a b c
結果
["a", "b", "c"]

ターミナルやコマンドプロンプトからちょっと呼ぶのに連携しやすくなった感じですね。

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

今回の更新では、あまり使われない機能の非推奨が1つだけです。非常に安定していますね。(フラグ)

非推奨

  • object.selector が非推奨になりました

非推奨: object.selector が非推奨になりました

これは何かというと Objective-C 連携するときに使うものです。
UDAとして付与することにより、メソッドの定義に「セレクタ」(メソッドを特定する内部表現)の情報を付け足します。

今後は core.attributes をimportすれば同じように使うことができます。
(ただしバージョン識別子として D_ObjectiveC を定義した場合のみ)

滅多に使われない割に、必ず import される object モジュールに定義されていました。
今回コンパイラ実装の範囲からランタイム実装のほうに移動された格好になります。

import core.attributes : selector;

extern (Objective-C)
extern class NSObject
{
    static NSObject alloc() @selector("alloc");
}

extern (Objective-C)
extern class NSString
{
    NSString initWith(in char*) @selector("initWithUTF8String:");
    NSString initWith(NSString) @selector("initWithString:");
}

このあたりの仕様は以下を参照ください。

まとめ

比較的小規模なバージョンアップとなっていたので全機能紹介してみました。

今回はライブラリの強化はありませんが、地味にパフォーマンス改善などがは行われているほか、5月版で実験的に追加された所有権周りも幾分賢くなっているようです。

前回のリリースからこの2ヶ月、リソースとしては言語の強化に向けたDIPのチェック等に振り向けられていた印象ですが、特に遅れることもなく無事にリリースされそうです。

ベータ版が取れたら改めて更新したいと思います。

次回リリースは9月1日予定、引き続きの強化を期待したいと思います!

15
3
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
15
3