Skip to content

Commit 90c7ce3

Browse files
committed
C++26 : 「未初期化変数の読み取りを誤り起因動作とする」を追加
1 parent 2d80797 commit 90c7ce3

File tree

4 files changed

+127
-2
lines changed

4 files changed

+127
-2
lines changed

GLOBAL_DEFINED_WORDS.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
},
2424
"未規定動作": { "redirect": "未規定の動作", "yomi": "みきていどうさ" },
2525
"未規定": { "redirect": "未規定の動作", "yomi": "みきてい" },
26+
"誤り起因動作": {
27+
"yomi": "あやまりきいんどうさ",
28+
"desc": "誤った操作に対する結果が明確に定義された動作。erroneous behavior (EB)。処理系によって診断や異常終了を実行することが許可されるが、処理が続行する場合もある"
29+
},
2630
"処理系定義の動作": {
2731
"link": "/implementation-compliance.md#dfn-implementation-defined-behavior",
2832
"yomi": "しょりけいていぎのどうさ",

implementation-status.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@
305305
| P0609R3: [構造化束縛への属性を許可](/lang/cpp26/attributes_for_structured_bindings.md) | `auto [a, b [[maybe_unused]], c] = f();`のように構造化束縛の要素に対して属性を付加できるようにする | 15 | 19 | | |
306306
| P3034R1: [モジュール宣言でのモジュール名のマクロ展開を禁止する](/lang/cpp26/module_declarations_shouldnt_be_macros.md.nolink) | `export module MACRO_NAME;`を禁止 | | | | |
307307
| P2809R3: [自明な無限ループは未定義動作ではないと規定](/lang/cpp26/trivial_infinite_loops_are_not_undefined_behavior.md.nolink) | 並行プログラムの進行保証などを考慮して無限ループを未定義動作ではないものとする | 14 | 19 | | |
308-
| P2795R5: [未初期化変数の読み取りを不正動作 (erroneous behaviour: EB) とする](/lang/cpp26/erroneous_behaviour_for_uninitialized_reads.md.nolink) | 初期化されていない自動変数の読み取りの安全性を規定する | | | | |
308+
| P2795R5: [未初期化変数の読み取りを誤り起因動作とする](/lang/cpp26/erroneous_behavior_for_uninitialized_reads.md) | 初期化されていない自動変数の読み取りの安全性を規定する | | | | |
309309
| P2573R2: [関数宣言を削除する理由を指定できるようにする](/lang/cpp26/delete_reason.md) | `f() = delete("reason");` | 15 | 19 | | |
310310
| P2893R3: [可変引数テンプレートで`friend`宣言をできるようにする](/lang/cpp26/variadic_friends.md.nolink) | クラステンプレートの可変引数テンプレートでまとめて`friend`宣言できるようにする | 15 | | | |
311311
| P2747R2: [`constexpr`配置`new`](/lang/cpp26/constexpr_placement_new.md.nolink) | 定数式の文脈での配置`new`を許可 | | | | |

lang/cpp26.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ C++26とは、2026年中に改訂される予定の、C++バージョンの通
1717
| [不完全型へのポインタに対する`delete`を不適格とする](/lang/cpp26/deleting_a_pointer_to_an_incomplete_type_should_be_ill-formed.md.nolink) | 未定義動作となる操作をコンパイルエラーとする |
1818
| [返却された左辺値から暗黙変換された一時オブジェクトが参照に束縛されることを禁止する](/lang/cpp26/disallow_binding_a_returned_glvalue_to_a_temporary.md.nolink) | 寿命切れの変数によって引き起こされるバグを防止する |
1919
| [要素数不明の配列を集成体初期化する規則を明確化](/lang/cpp26/clarifying_rules_for_brace_elision_in_aggregate_initialization.md.nolink) | 配列要素の集成体初期化で`{}`が省略された場合の矛盾していた規定を修正 |
20-
| [未初期化変数の読み取りを不正動作 (erroneous behaviour: EB) とする](/lang/cpp26/erroneous_behaviour_for_uninitialized_reads.md.nolink) | 初期化されていない自動変数の読み取りの安全性を規定する |
20+
| [未初期化変数の読み取りを誤り起因動作とする](/lang/cpp26/erroneous_behavior_for_uninitialized_reads.md) | 初期化されていない自動変数の読み取りの安全性を規定する |
2121

2222

2323
### 文字列
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# 未初期化変数の読み取りを誤り起因動作とする [P2795R5]
2+
* cpp26[meta cpp]
3+
4+
<!-- start lang caution -->
5+
6+
このページはC++26に採用される見込みの言語機能の変更を解説しています。
7+
8+
のちのC++規格でさらに変更される場合があるため[関連項目](#relative-page)を参照してください。
9+
10+
<!-- last lang caution -->
11+
12+
## 概要
13+
これまで、未初期化変数 (デフォルト初期化された変数) の読み取りは未定義動作として扱われていた。C++26では、この操作が安全上のリスクをもたらさないものとして、「誤り起因動作 (erroneous behavior; 通称 EB)」を新設してそれに割り当てることとした。
14+
15+
この変更の対象は、デフォルト初期化されたスカラ型 (`void`以外の組み込み型) 変数の読み取りであり、以下のようなケースである。
16+
17+
```cpp
18+
// C++23
19+
void f(int);
20+
21+
int main() {
22+
int x; // デフォルト初期化。xは不定値 (indeterminate value) をもつ
23+
f(x); // 左辺値から右辺値への変換が未定義動作を引き起こす
24+
}
25+
```
26+
27+
未初期化の値は、コンパイラやターゲット環境によって定義された固定値である。コンパイラにはこの誤りを診断することが許可され、推奨されているが、誤りを無視して有効な読み取りとして扱うことも許可されている。このコードは誤りではあるが、未定義動作に関連する結果に遭遇するリスクはない。
28+
29+
誤り起因動作は未定義動作とはちがって、明確に定義された動作をするがコードとして正しくない、ということを規定するものであり、処理系に対して有用な診断を提供するものである。
30+
31+
C++26では、不定値で初期化されることを明確に指示する`[[indeterminate]]`属性も導入され、以下のような動作となる:
32+
33+
```cpp
34+
// C++26
35+
void f(int);
36+
37+
int main() {
38+
int x; // xは誤り起因動作を引き起こす未初期化値をもつ
39+
int y [[indeterminate]]; // 意図して不定値に初期化されることを指示
40+
41+
f(x); // 誤り起因動作 (未初期化値の読み取り)
42+
f(y); // 未定義動作 (不定値の読み取り)
43+
}
44+
```
45+
46+
47+
## 仕様
48+
- 式が評価された結果として不定値が生成された場合、未定義動作を引き起こす
49+
- 式が評価された結果として未初期化値が生成された場合、誤り起因動作を引き起こす
50+
- ただし、`unsigned char`もしくは[`std::byte`](/reference/cstddef/byte.md)型に関しては、未初期化値をもつ変数を同じ型の変数に代入するだけでは誤り起因動作にならず、値の参照や型変換がされることで誤り起因動作を引き起こす
51+
- 誤り起因動作が引き起こされた結果値は、後続の処理では誤り起因動作を引き起こす値とはみなされない
52+
53+
```cpp
54+
int g(bool b) {
55+
unsigned char c;
56+
unsigned char d = c; // 誤り起因動作ではない。dは誤り起因動作を引き起こす未初期化値をもつ
57+
58+
assert(c == d); // どちらの変数も整数昇格され誤り起因動作となる
59+
60+
int e = d; // 誤り起因動作 (型変換)
61+
return b ? d : 0; // bがtrueの場合に誤り起因動作
62+
}
63+
64+
void h() {
65+
int d1, d2;
66+
67+
int e1 = d1; // 誤り起因動作
68+
int e2 = d1; // 誤り起因動作
69+
70+
// 処理が続行した場合…
71+
assert(e1 == e2); // OK。誤り起因動作の結果で生成された値は正常
72+
assert(e1 == d1); // 誤り起因動作
73+
assert(e2 == d1); // 誤り起因動作
74+
75+
// 誤り起因動作ではないが
76+
// d2は誤り起因動作を引き起こす未初期化値をもつ
77+
std::memcpy(&d2, &d1, sizeof(int));
78+
79+
assert(e1 == d2); // 誤り起因動作
80+
assert(e2 == d2); // 誤り起因動作
81+
}
82+
```
83+
84+
### `[[indeterminate]]`属性
85+
`[[indeterminate]]`属性は、自動変数が初期状態として意図して不定値をもつことを指示するものであり、自動変数の定義、もしくは関数のパラメータ宣言に適用できる。
86+
87+
関数のパラメータが`[[indeterminate]]`属性で宣言される場合、その関数の最初の宣言でそのように宣言されなければならない (注:関数宣言は複数行うことができるが、その最初の宣言で`[[indeterminate]]`属性をつけなければならない)。
88+
89+
`[[indeterminate]]`がつけられた変数から読み取りをした場合、未定義動作を引き起こす可能性がある。
90+
91+
```cpp
92+
struct T {
93+
T() {}
94+
int x;
95+
};
96+
97+
int h(T t [[indeterminate]]) {
98+
f(t.x); // この関数呼び出しは未定義動作を引き起こす
99+
return 0;
100+
}
101+
int _ = h(T());
102+
```
103+
104+
### 今後、誤り起因動作に分類される可能性のある操作
105+
106+
現在、未定義動作に分類される以下の操作は、誤り起因動作に分類できる可能性がある。
107+
108+
| 操作 | 備考 |
109+
|------|------|
110+
| 符号付き整数のオーバーフロー | 演算結果としてオーバーフローした場合に誤った結果になる可能性がある。これは珍しいバグではない。これは安全上の大きな問題ではない |
111+
| 算術型の変換結果としてその型の表現可能な範囲を超えた | 符号付き整数のオーバーフローと同じ |
112+
| 誤ったビットシフト (負のシフト幅や、上限を超えたシフト幅) | 符号付き整数のオーバーフローと同じ |
113+
| ゼロ割り | いくつかの固定値での誤った結果となる可能性がある。影響が不明確であるため、変更にはコストがかかる |
114+
| 戻り値型が非`void`な関数から返った、もしくは`[[noreturn]]`属性をつけた関数から返った | [`std::terminate()`](/reference/exception/terminate.md)が呼ばれる可能性がある。変更には軽いコストがかかるが、その変更にどの程度の価値があるかは不明 |
115+
| 抽象クラスのコンストラクタ・デストラクタからの純粋仮想関数の呼び出し | 特定の純粋仮想ハンドラが呼ばれる可能性がある。実装によってはすでに誤り起因動作のように扱われている可能性がある |
116+
| 契約違反 | 契約に関する現在の策定作業では、契約違反時になにが起こるべきかという問題に直面している。誤り起因動作という概念は有用な回答を与えてくれる可能性がある |
117+
118+
119+
120+
## 参照
121+
- [P2795R5 Erroneous behaviour for uninitialized reads](https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2795r5.html)

0 commit comments

Comments
 (0)