-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathp2552.tex
613 lines (421 loc) · 58.6 KB
/
p2552.tex
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
\input{wg21common}
\newcommand{\forceindent}{\parindent=1em\indent\parindent=0pt\relax} % For indenting a paragraph containing code that can't be laid out as a {codeblock} because it also contains \emph
\begin{document}
\title{On the ignorability of standard attributes}
\author{
Timur Doumler \small(\href{mailto:[email protected]}{[email protected]})
}
\date{}
\maketitle
\begin{tabular}{ll}
Document \#: & P2552R3 \\
Date: & 2023-06-14\\
Project: & Programming Language C++ \\
Audience: & Evolution Working Group, Core Working Group
\end{tabular}
\begin{abstract}
There is a general notion in C++ that standard attributes should be \emph{ignorable}. However, currently there does not seem to be a common understanding of what ``ignorable`` means, and the C++20 Standard itself is ambiguous on this matter. We consider three aspects of ignorability: syntactic ignorability, semantic ignorability, and the behaviour of \mbox{\tcode{__has_cpp_attribute}}. We discuss where and how the C++20 Standard is underspecified in all three aspects and why that is problematic, survey existing implementation practice, and propose the Three Rules of Ignorability as language design guidelines going forward. Finally, we propose wording to specify the Third Rule of Ignorability (behaviour of \mbox{\tcode{__has_cpp_attribute}}) in the C++ Standard; wording for the other two Rules has already been added for C++23 via Core Issues.
\end{abstract}
%\vspace{5mm}
\section{Motivation}
\label{sec:motivation}
The C++20 Standard says the following about the ignorability of attributes ([dcl.attr.grammar]/6):
\begin{adjustwidth}{0.5cm}{0.5cm}
For an \emph{attribute-token} (including an \emph{attribute-scoped-token}) not specified in this document, the behavior is implementation-defined. Any \emph{attribute-token} that is not recognized by the implementation is ignored.
\end{adjustwidth}
This wording is ambiguous. It is not clear at all whether the intent is to allow the implementation to ignore any \emph{attribute-token not specified in this document} (i.e. only non-standard attributes), or any \emph{attribute-token, including those specified in this document} (i.e. including standard attributes). This ambiguity is a known defect: there is a Core issue \cite{CWG2538} and a recent NB comment (GB 9.12.1p6).
Standard attributes are a feature shared between C and C++. The current C23 draft says:
\begin{adjustwidth}{0.5cm}{0.5cm}
A strictly conforming program using a standard attribute remains
strictly conforming in the absence of that attribute. [...] Standard attributes specified by this document can be parsed but ignored by an implementation without changing the semantics of a correct program; the same is not true for attributes not specified by this document.
\end{adjustwidth}
It is clear from the C wording that the intent is for standard attributes to be ignorable, however it is not entirely clear what that means: ``parse but ignore'' implies that the compiler needs to at least parse them (i.e. it cannot treat a standard attribute as token soup). So the C standard might be talking about a different kind of ignorability than the C++ standard does (ignoring the \emph{attribute-token}).
Before we can try to fix the defect in the C++ wording, we need to answer two questions:
\begin{itemize}
\item Should an implementation be allowed to ignore a standard attribute?
\item What does it mean to ignore a standard attribute?
\end{itemize}
We will start by defining what \emph{ignore} means. There are different properties of standard attributes that could or could not be declared \emph{ignorable}, with different consequences for the standard. In particular, we can draw a distinction between \emph{syntactic ignorability} (i.e., ignoring the form of the argument clause, the attribute's appertainment, and so forth) and \emph{semantic ignorability} (i.e., ignoring the effect that the attribute would have on the program).
Whether standard attributes are \emph{syntactically} ignorable is a matter of contention. At the heart of the issue is the question whether a compiler is required to properly parse a standard attribute (which includes syntax-checking the argument clause, appertainment, and so forth) even if it then does not implement any semantics for that attribute.
On the other hand, it is uncontroversial that attributes are meant to be \emph{semantically} ignorable. However, we have a problem here as well: it is not quite clear what semantic ignorability means exactly. Until and including C++20, the principle of semantic ignorability is some kind of gentlemens' agreement that originated in the standardisation of attribute syntax for C++11 \cite{N2761}. This agreement, however, is not codified anywhere. This guideline currently exists only implicitly and can be interpreted in different ways. Such manifest ambiguity is anathema to sound and consistent language design.
Finally, the behaviour of \tcode{__has_cpp_attribute} is ambiguous as well. In particular, it is unclear whether \mbox{\tcode{__has_cpp_attribute}} should return a positive value for a standard attribute if the compiler is aware of the attribute and can parse it correctly, but does not implement any useful semantics for it. There is current implementation divergence on this point, so the standard should specify the correct behaviour.
In this paper, we propose the Three Rules of Ignorability, resolving all of the above ambiguities for standard attributes.
\section{Syntactic ignorability}
\subsection{The status quo}
\label{subsec:syntax}
\subsubsection{Argument clause}
\label{subsubsec:args}
The C++ grammar defines the \emph{attribute-argument-clause} of an attribute to have the form:
\begin{adjustwidth}{0.5cm}{0.5cm}
\tcode{( }\emph{balanced-token-seq}$_\emph{opt}$\tcode{ )}
\end{adjustwidth}
where \emph{balanced-token-seq} is any token sequence with balanced parentheses, square brackets, and curly braces. This base grammar allows for a wide variety of possible arguments for standard attributes. It is up to the specification of each individual attribute to constrain the grammar for its arguments further ([dcl.attr.grammar]/4):
\begin{adjustwidth}{0.5cm}{0.5cm}
[...] The \emph{attribute-token} determines additional requirements on the \emph{attribute-argument-clause} (if any).
\end{adjustwidth}
Every standard attribute specifies explicitly whether it can have an argument clause, whether this argument clause is optional or mandatory, and what form the argument clause shall have, for example in [dcl.attr.noreturn/1]:
\begin{adjustwidth}{0.5cm}{0.5cm}
The \emph{attribute-token} \tcode{noreturn} specifies that a function does not return. No \emph{attribute-argument-clause} shall be present.
\end{adjustwidth}
or in [dcl.attr.deprecated]/1:
\begin{adjustwidth}{0.5cm}{0.5cm}
The \emph{attribute-token} \tcode{deprecated} can be used to mark names and entities whose use is still allowed, but is discouraged for some reason. An \emph{attribute-argument-clause} may be present and, if present, it shall have the form:
\tcode{( }\emph{string-literal}$_\emph{opt}$\tcode{ )}
\end{adjustwidth}
On the one hand, this wording is all normative; it therefore seems that a program violating these requirements should be ill-formed, and a conforming compiler must emit a diagnostic. On the other hand, due to the ambiguity in [dcl.attr.grammar]/6, it is unclear whether [dcl.attr.grammar]/6 overrides these requirements and allows an implementation to completely ignore the argument clause:
\begin{codeblock}
[[noreturn("cannot have a reason")]] int f(); // Ill-formed or ignorable?
[[deprecated(not_a_string)]] int g(); // Ill-formed or ignorable?
[[nodiscard(this?is!a:balanced%{token[sequence]})]] int h(); // Ill-formed or ignorable?
\end{codeblock}
\subsubsection*{\emph{Existing practice}}
Clang, GCC, ICC, and MSVC are all very good at diagnosing syntax errors in the argument clause. We tried many different ill-formed constructions like the above and got a diagnostic on all four compilers in all cases. The only questionable (but still conforming) case we found was \tcode{[[carries_dependency(some_argument)]]} on GCC, where the emitted diagnostic said that the \tcode{carries_dependency} attribute is not supported, but did not specifically call out the syntax error in the argument clause.
\subsubsection{Appertainment}
\label{subsubsec:appertainment}
On the appertainment of a standard attribute, [dcl.attr.grammar]/5 says:
\begin{adjustwidth}{0.5cm}{0.5cm}
Each \emph{attribute-specifier-seq} is said to \emph{appertain} to some entity or statement, identified by the syntactic context where it appears. If an \emph{attribute-specifier-seq} that appertains to some entity or statement contains an \emph{attribute} or \emph{alignment-specifier} that is not allowed to apply to that entity or statement, the program is ill-formed.
\end{adjustwidth}
Every standard attribute has normative requirements on appertainment. For example, \tcode{noreturn} ``may be applied to a function or a lambda call operator'' ([dcl.attr.noreturn]/1); \mbox{\tcode{no_unique_address}} ``may appertain to a non-static data member other than a bit-field'' ([dcl.attr.nouniqueaddr]/1); \tcode{fallthrough} ``may be applied to a null statement'' ([dcl.attr.fallthrough]/1); and so forth.
Similarly to syntax errors in the argument clause, whether [dcl.attr.grammar]/6 allows the compiler to ignore these appertainment rules is currently ambiguous:
\begin{codeblock}
int main() {
[[fallthrough]] int i; // Ill-formed or ignorable?
}
\end{codeblock}
\subsubsection*{\emph{Existing practice}}
We found that generally, Clang, GCC, ICC, and MSVC are very good at diagnosing appertainment errors as well. But, unlike with argument clause errors, with appertainment errors we did find some false negatives on all four compilers. For example, no compiler diagnoses \tcode{[[deprecated]]} or \tcode{[[maybe_unused]]} on static data members, and GCC allows any standard attribute to appertain to an empty declaration at class scope without warning:
\begin{codeblock}
struct X { [[nodiscard]]; }; // no diagnostic on GCC
\end{codeblock}
The code triggering those false negatives, however, is typically quite obscure. Moreover, we could not find any cases on any compiler where the failure to diagnose appertainment rules introduced a bug or changed the behaviour of a program.
\subsubsection{Additional syntactic requirements}
\label{subsec:additionalreqs}
Some standard attributes have additional normative syntactic requirements on top of syntactic rules for the argument clause and appertainment. In particular, [dcl.attr.likelihood]/1 constraints which \emph{attribute-token}s can appear in an \emph{attribute-specifier-seq}:
\begin{adjustwidth}{0.5cm}{0.5cm}
The \emph{attribute-token} \tcode{likely} shall not appear in an \emph{attribute-specifier-seq} that contains the \emph{attribute-token} \tcode{unlikely}.
\end{adjustwidth}
and ([dcl.attr.fallthrough]/1) specifies:
\begin{adjustwidth}{0.5cm}{0.5cm}
A fallthrough statement may only appear within an enclosing \tcode{switch} statement. The next statement that would be executed after a fallthrough statement shall be a labeled statement whose label is a case label or default label for the same \tcode{switch} statement and, if the fallthrough statement is contained in an iteration statement, the next statement shall be part of the same execution of the substatement of the innermost enclosing iteration statement. The program is ill-formed if there is no such statement.
\end{adjustwidth}
Just as with the other requirements, the question here is whether a program violating these syntactic requirements is ill-formed, or whether [dcl.attr.grammar]/6 allows ignoring such violations.
\subsubsection*{\emph{Existing practice}}
The case of \tcode{likely} and \tcode{unlikely} appearing in the same \emph{attribute-specifier-seq} is reliably diagnosed as ill-formed by all of Clang, GCC, ICC, and MSVC. On the other hand, the additional rules for \tcode{fallthrough} are not consistently diagnosed. In [dcl.attr.fallthrough]/3, the C++ standard gives a code example that contains four syntax errors explicitly marked as such:
\begin{codeblock}
void f(int n) {
void g(), h(), i();
switch (n) {
case 1:
case 2:
g();
[[fallthrough]];
case 3: // warning on fallthrough discouraged
do {
[[fallthrough]]; // error: next statement is not part of the same substatement execution
} while (false);
case 6:
do {
[[fallthrough]]; // error: next statement is not part of the same substatement execution
} while (n--);
case 7:
while (false) {
[[fallthrough]]; // error: next statement is not part of the same substatement execution
}
case 5:
h();
case 4: // implementation may warn on fallthrough
i();
[[fallthrough]]; // error
}
}
\end{codeblock}
Only ICC and Clang diagnose all four syntax errors. GCC only diagnoses the second and the fourth, and MSVC diagnoses only the fourth.
\subsubsection{Expression parsing and ODR-use}
\label{subsubsec:odruse}
With attribute \tcode{assume} \cite{P1774R8}, we added an attribute to C++23 that contains an expression in its argument clause. Having an attribute that includes an expression brings with it several interesting consequences, which are likewise affected by the current ambiguity in [dcl.attr.grammar]/6.
First, to detect syntax errors inside the expression such as
\begin{codeblock}
void f(int i) {
[[assume(i >=)]]; // Ill-formed or ignorable?
}
\end{codeblock}
the compiler has to parse expression grammar inside the attribute's argument clause; merely treating the argument clause as a \emph{balanced-token-sequence} is not enough (it would be enough for the other standard attributes having an argument clause, \tcode{deprecated} and \tcode{nodiscard}, since their argument is merely a \emph{string-literal}). One compiler vendor, MSVC, has told us that this is technically challenging for them to implement (see also NB comment FR 9.12.3). On the other hand, two other vendors, GCC and Clang, have told us that their compilers have no problem parsing expressions inside an attribute's argument clause, and in fact this is already existing practice for their vendor-specific non-standard attributes. MSVC itself also does not seem to have a problem with parsing expressions inside other constructs such as \tcode{__declspec(...)}.
Apart from syntax errors in the expression grammar, expression parsing also involves ODR-use of the entities in the expression, which can trigger template instantiations. If such an instantiation in turn triggers a failing \tcode{static_assert}, the program would be rendered ill-formed as well:
\begin{codeblock}
template <typename T>
struct X {
static_assert(sizeof(T) > 1);
bool f() { return true; }
};
int main() {
[[assume(X<char>().f())]]; // Ill-formed or ignorable?
}
\end{codeblock}
In addition, ODR-use can also trigger lambda capture, which is observable both at compile time and at run time. We can even construct an example where the lambda capture has an effect on the layout of a class:
\begin{codeblock}
constexpr auto f(int i) {
return sizeof( [=] { [[assume(i == 0)]]; } );
}
struct X {
char data[f(0)];
};
\end{codeblock}
Here, \tcode{sizeof(X)} and therefore the ABI of \tcode{struct X} will depend on whether the \tcode{assume} is syntactically ignored.
Of course, this code example is a highly contrived usage of assumptions. In real code, it is not useful to use a variable \emph{only} in an assumption, but not anywhere else in the surrounding code. So, in real-world usage, the assumption would never end up triggering the lambda capture (and if anyone were to write such code, they would have much bigger problems than the class layout of \tcode{struct X}). Note also that changing the layout of a class through a language construct is nothing new: \tcode{[[no_unique_address]]}, \tcode{assert}, and other constructs can also trigger changes in class layout. This possibility of affecting class layout does not cause any problems in practice as far as we know. But we still need to define the exact behaviour: is the implementation required to ODR-use the expression, therefore triggering the template instantiations and lambda captures, or may ODR-use be skipped?
If [dcl.attr.grammar]/6 is interpreted as ``only non-standard attributes can be syntactically ignored'' (following the suggested resolution of \cite{CWG2538}), then in the template instantiation example, the compiler must instantiate the template, and must trigger the failing \tcode{static_assert} (or whatever other effects on the program the template instantiation will cause). In the lambda capture example, the compiler must perform the lambda capture, and therefore change the layout of the class, even if the compiler then decides to ignore the attribute \emph{semantically}, i.e. not implement an assumptions facility.
On the other hand, if [dcl.attr.grammar]/6 is interpreted as ``all attributes, including standard attributes, can be syntactically ignored'', the compiler is free to not ODR-use the entities in the expression, not perform the template instantiations or the lambda capture, and in fact not parse the expression at all, but to treat the entire thing as token soup, and just skip over it.
Note that the question of whether an expression must be ODR-used is in no way related to the \emph{semantics} of the \tcode{assume} attribute, which is entirely orthogonal. This question concerns the design space of attributes as a whole; \tcode{assume} just happens to be the only standard attribute currently containing an expression. Any attribute containing an expression would run into the same question, such as the proposed \tcode{trivially_relocatable} attribute (see \cite{P1144R5}), or a hypothetical attribute-like syntax for Contracts (see \cite{P2487R0}).
\subsubsection*{\emph{Existing practice}}
\tcode{assume} was added recently for C++23. GCC already implements it with the standard attribute syntax. The other three major compilers implement the same functionality as a built-in: \tcode{__assume} on MSVC and ICC, and \mbox{\tcode{__builtin_assume}} on Clang.
On all four compilers, regardless of whether the attribute syntax or the built-in syntax is used, the expression in the argument clause is always ODR-used, and the side effects of this ODR-use (such as lambda captures changing class layout) are always triggered. Interestingly, the ODR-use also happens when the actual assumption is then semantically ignored by the compiler, as is the case on Clang for expressions that have side effects. This behaviour is consistent with the interpretation that standard attributes can be ignored only semantically, but not syntactically.
\subsection{Proposed solution: the First Ignorability Rule}
\label{subsec:proposal_syntactic}
To clarify the specification in the Standard for syntactic ignorability of standard attributes, we propose the following rule:
\begin{adjustwidth}{0.5cm}{0.5cm}
\textbf{The First Ignorability Rule:}
Standard attributes \emph{cannot be syntactically ignored}, but must be parsed; syntax errors in the argument clause, appertainment rules, and any additional syntactic requirements specified by a particular standard attribute must be diagnosed; and entities in the argument clause must be ODR-used.
\end{adjustwidth}
Disallowing syntactic ignorability matches the original design intent of C++ attributes (according to the authors of \cite{N2761}). It also matches the proposed resolution of \cite{CWG2538} and NB comment GB 9.12.1p6, as approved by CWG. We believe that this is the choice of sound language design. Standard attributes are not just arbitrary token sequences, even if they are \emph{semantically} ignorable (see section \ref{subsec:proposal_semantic}). We should try hard not to add optional language features on the syntax level to the language, and we should avoid treating standard attributes as a bucket for any language feature that we do not care about being implemented. There are a small number of standard attributes. If we bothered to specify something in the standard, implementations should at least bother to syntax-check it. This will ensure safety, predictability, portability, and consistency across implementations. Option 1A is also broadly consistent with existing practice (see above).
With regards to parsing expressions inside an attribute, if we allow syntactic ignorability of attributes, then a compiler is allowed to silently accept an ill-formed expression, and the code will compile and appear to work fine; when we go to port the code to another compiler, the code will break. From a production and maintenance perspective, that seems like a very bad idea.
With regards to ODR-using the entities inside such an expression, as discussed in detail in section \ref{subsubsec:odruse}, we believe that requiring the ODR-usage even if the assumption is semantically ignored, is the option that is most consistent and portable. It also matches existing practice with the existing implementations of \tcode{assume}. Leaving it up to the implementation whether a particular template instantiation happens, or whether a particular lambda capture gets triggered, would introduce areas of gratuitous non-portability to the language. We must avoid this.
We would like to remind the reader that this issue is completely orthogonal to the semantics of \tcode{assume}. It concerns any hypothetical attribute or attribute-like thing that contains an expression, which includes the proposed \tcode{trivially_relocatable} attribute \cite{P1144R5} and a hypothetical attribute-like syntax for Contracts \cite{P2487R0}.
\subsubsection{Implementer concerns}
Three compiler implementers voted against our recommended resolution in the EWG electronic poll on an earlier revision of this paper, and instead preferred to allow syntactic ignorability of standard attributes. Implementer concerns should of course always be taken seriously. In particular, we heard from Clang that their interpretation of [dcl.attr.grammar]/6 has always been to allow syntactic ignorability; that mandating to check the syntax of standard attributes would be an unacceptable implementation burden in particular with regards to checking appertainment; that no users are actually asking for this status quo to change; and that existing practice should take priority over the original design. Interestingly, we have found that all major compilers (including Clang) are actually very good at syntax-checking the argument clause and appertainment of all existing standard attributes (see existing practice discussion above), so we are not sure where the problem actually is.
Another implementer concern is MSVC's comment that parsing expressions inside an attribute-argument clause is technically challenging for them and that they would instead prefer to treat them as token soup that can be skipped entirely (see discussion above). Yet another argument in favour of allowing syntactic ignorability is that the benefits of checking syntactic requirements for something with no semantic effect are negligible, and therefore the standard should not require it. We do not agree with these concerns, but we include them here for the sake of completeness.
\section{Semantic ignorability}
\label{sec:semantic}
\subsection{The status quo}
\subsubsection{Design of existing standard attributes}
The original paper that introduced attributes to C++ \cite{N2761} says --- somewhat vaguely --- that a standard attribute should be ``something that helps but can be ignorable with little serious side-effects'', but that paper does not mention a strict rule of semantic ignorability. The paper also contains a list of possible future features, indicating which ones in the opinion of the authors at the time would be good or bad candidates for a standard attribute.
The list in \cite{N2761} contains \tcode{alignas} as a good candidate: \tcode{alignas} was initially proposed as an attribute, but later changed to a keyword before C++11 was finalised \cite{N3190}. The agreement had evolved: attributes should now be features that are semantically ignorable in the strict sense, i.e., their effect on the program is optional, and \tcode{alignas} does not fit the bill: its effects on the alignment of an object are mandatory, not optional.
The original paper also says that attributes should appertain to declarations only, not statements, which also changed later: \tcode{likely}, \tcode{unlikely}, \tcode{fallthrough}, and \tcode{assume} can all apply to statements (the latter two only to null statements). What should or should not be an attribute has clearly evolved over the years, so we should base our rules on the attributes that have been standardised so far.
\subsubsection*{\emph{Existing practice}}
Which attributes are being semantically ignored in practice by today's major compilers? Again, we looked at the latest available versions of Clang, GCC, ICC, and MSVC. It seems that none of them implement any semantics for \tcode{carries_dependency} (so perhaps we should not have standardised \tcode{carries_dependency}, but that is another story).
In addition, MSVC does not implement any semantics for \tcode{no_unique_address}, which has the consequence that class layout is inconsistent across different compilers on the same platform.
All other standard attributes seem to have semantically functional implementations on all major compilers.
\subsubsection{Semantic categories of existing standard attributes}
\label{subsubsec:attributecategories}
All standard attributes are currently normatively specified in such a way that they are syntactically ignorable (and therefore, all implementations described in the previous section are conforming). However, how this ignorability is achieved in the specification of the C++ Standard varies from one standard attribute to another. We can distinguish four different categories:
\begin{itemize}
\item Attributes that produce or suppress diagnostics and otherwise have no effect: \tcode{deprecated}, \tcode{fallthrough}, \tcode{maybe_unused}, and \tcode{nodiscard}. These attributes are normatively defined to do nothing. The desired effect is described in a section called ``recommended practice''.
\item Attributes that serve as optimisation hints to the compiler and otherwise have no effect: \tcode{likely}, \tcode{unlikely}, and \tcode{carries_dependency}. These attributes are also defined to do nothing and have a ``recommended practice'' section.
\item Attributes that can turn defined behaviour into undefined behaviour: \tcode{noreturn} and \tcode{assume}. These attributes are semantically ignorable because undefined behaviour means the implementation can do literally anything, including ignoring the effects of the attribute and compiling and executing the program as if they were not there.
\item Attributes that change the semantics of the program in an observable way. We currently have only one such attribute: \tcode{no_unique_address}. This attribute is semantically ignorable because its effect is carefully specified to be so: it introduces a \emph{potentially-overlapping subobject}, i.e. a subobject that either \emph{is} or is \emph{not} overlapping, depending on whether the compiler chooses to implement or semantically ignore the attribute.
\end{itemize}
\subsubsection{Challenges with defining a guideline for semantic ignorability}
\label{subsubsec:challenges}
Unfortunately, beyond the syntax and grammar, there is currently no clear and explicit definition of what constitutes a standard attribute, and what it means for it to be semantically ignorable. As a result, different people have a different mental model.
Some say that semantic ignorability means that a program has \emph{the same} behaviour (or \emph{identical} semantics) with or without the attribute. This characterisation is clearly wrong: it applies to only the first two of the four categories listed above. Constructing a counterexample is easy:
\begin{codeblock}
[[noreturn]] int f() { return 0; }
int main() { return f(); }
\end{codeblock}
This program returns \tcode{0} without the attribute, but has undefined behaviour with the attribute, which means that adding the attribute can change the behaviour, and will often do so in practice.
Others say that --- and this is the version we hear most often --- given a well-formed program, removing a particular attribute does not change the observable behaviour (or the semantics) of the program. However, this characterisation too is wrong, and we can again construct a counterexample:
\begin{codeblock}
struct X {};
struct Y {
[[no_unique_address]] X x;
int i;
};
int main() {
return (sizeof(Y) == sizeof(int));
}
\end{codeblock}
This program might return \tcode{1} \emph{with} the attribute, but it will \emph{always} return \tcode{0} \emph{without} the attribute. However, because the compiler is not required to implement the semantic effects of \mbox{\tcode{no_unique_address}}, the program may also return \tcode{0} with the attribute. If we add a
\begin{codeblock}
static_assert (sizeof(Y) == sizeof(int));
\end{codeblock}
we get an even more obvious violation of the pseudo-rule above: this program might or might not be ill-formed with the attribute, depending on whether the compiler implements it, but is definitely ill-formed without the attribute. Therefore, the omission of the attribute can render a program ill-formed.
If we had codified a rule for semantic ignorability earlier, we might have ended up with the rule above (given a well-formed program, omitting an attribute does not change the behaviour/semantics of the program). As a result, we would perhaps never have standardised \mbox{\tcode{no_unique_address}}, which violates this rule, as an attribute; however, that ship sailed with C++20.
In order to find a rule for semantic ignorability that matches existing practice, we need to take a closer look at what it actually means when we say that two programs have ``the same semantics'' or ``the same behaviour''.
\subsection{Proposed solution: the Second Ignorability Rule}
\label{subsec:proposal_semantic}
Our proposed rule for semantic ignorability of standard attributes can be formulated as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
\textbf{The Second Ignorability Rule:}
Given a well-formed program, removing all instances of a particular standard attribute is allowed to change the observable behaviour of the program, but only if the behaviour with the attribute removed would have been a conforming behaviour for the original program with the attribute present.
\end{adjustwidth}
The standard distinguishes undefined behaviour, unspecified behaviour, and implementation-defined behaviour. Let us call the behaviour that falls into none of these regions, i.e. the behaviour that is fully specified by the standard and does not depend on any input parameters to the abstract machine, the \emph{mandated behaviour} of a C++ program. An example for mandated behaviour is \tcode{sizeof(char)}, which must evaluate to \tcode{1} on every conforming implementation.
The following statement is therefore a corollary of the Second Ignorability Rule:
\begin{adjustwidth}{0.5cm}{0.5cm}
\textbf{Corollary:}
Given a well-formed program, removing all instances of a particular standard attribute must result in a well-formed program that exhibits the exact same \emph{mandated behaviour}.
\end{adjustwidth}
However, according to the Second Ignorability Rule, implementation-defined and unspecified behaviour is allowed to change from one behaviour to another due to such a removal, as long as both behaviours would be conforming for the original program with the attribute present. If the program has undefined behaviour with a particular standard attribute present, we do not place any restrictions on the behaviour of such a program; the Second Ignorability Rule does not apply in this case.
We can now see that the Second Ignorability Rule works for all categories of standard attributes we identified in section \ref{subsubsec:attributecategories}: semantically ignoring any of them does not change the \emph{mandated behaviour} of a C++ program in any way. For \tcode{no_unique_address} in particular, [intro.object] says:
\begin{adjustwidth}{0.5cm}{0.5cm}
A \emph{potentially-overlapping subobject} is either:
\begin{itemize}
\item a base class subobject, or
\item a non-static data member declared with the \tcode{no_unique_address} attribute.
\end{itemize}
An object has nonzero size if it
\begin{itemize}
\item is not a potentially-overlapping subobject, or
\item is not of class type, or
\item is of a class type with virtual member functions or virtual base classes, or
\item has subobjects of nonzero size or unnamed bit-fields of nonzero length.
\end{itemize}
Otherwise, if the object is a base class subobject of a standard-layout class type with no non-static data members, it has zero size. Otherwise, the circumstances under which the object has zero size are implementation-defined.
\end{adjustwidth}
Therefore, in the following code example,
\begin{codeblock}
struct X {};
struct Y {
[[no_unique_address]] X x;
int i;
};
int main() {
return (sizeof(Y) == sizeof(int));
}
\end{codeblock}
the value of \tcode{sizeof(Y) == sizeof(int)} is implementation-defined. Either \tcode{true} or \tcode{false} are conforming behaviours for the program with the \tcode{[[no_unique_address]]} attribute present, because the attribute is defined to have optional semantics. Therefore, if we remove \tcode{[[no_unique_address]]} from the above program, it is acceptable if the effect of that is that the value of the expression flips from \tcode{true} to \tcode{false} (or even the other way around!).
Conversely, for \tcode{alignas}, which sits in the same space in the grammar as standard attributes, but is not an attribute, [dcl.align]/4 says:
\begin{adjustwidth}{0.5cm}{0.5cm}
The alignment requirement of an entity is the strictest nonzero alignment specified by its \emph{alignment-specifier}s, if any; otherwise, the \emph{alignment-specifier}s have no effect.
\end{adjustwidth}
The effect that the alignment requirement of an entity has is fully defined by the standard: it constitutes mandated behaviour of the program. Removing \tcode{alignas} from a program would change that mandated behaviour. Therefore, \tcode{alignas} does \emph{not} have optional semantics, and cannot be a standard attribute.
We propose that the Second Ignorability Rule be spelled out in the C++ Standard (in [dcl.attr]). Formally, it would only apply to the attributes that are already in the standard, and thus not add any new information per se, as those attributes are already defined to be semantically ignorable in different ways (see discussion in section \ref{subsubsec:attributecategories}). However, the existence of such an explicit rule in the standard would be very helpful for codifying the intended behaviour of all future standard attributes, too: any new attribute proposal that does not want to follow the rule would have to carve out an explicit exception for itself. We believe that the presence of the Second Ignorability Rule in the C++ Standard would be a strong enough deterrent for future proposals that they will stick to it, thus leading to consistent language design. This affects not only future proposals for new standard attributes, but potentially also other language features such as Contracts \cite{P2521R2}. One of the possible syntaxes for contract annotations is an attribute-like syntax \cite{P2487R0}. If we choose this syntax, we should be consistent with attribute ignorability semantics, too. We believe that the Second Ignorability Rule proposed here for standard attributes can be adapted to apply to contract-checking annotations as well. Finally, we believe that the rule proposed here is compatible with, but more precise than the rule in the C language (see section \ref{sec:motivation}).
\subsubsection{Alternatives}
Alternatively, one could hold the position that the C++ standard is not the place to try and constrain future evolution of the language, only to define what is and is not conforming with the current standard. Therefore, such a rule could instead be published in a new standing document. If this option were to be chosen, it would make most sense to create a new standing document for all such design guidelines for new core language features, not just for attributes. However, we think that adding the Second Ignorability Rule to the Standard itself is more appropriate.
We could also simply do nothing. This would not have an effect on the current specification of standard attributes. But it would mean that the standard will continue to say nothing about the semantic ignorability of standard attributes. Misunderstandings on this subject will continue, and the discussions around what should or should not be an attribute will keep wasting precious committee time.
We believe that doing nothing would only make sense if we decide to throw away the idea of ignorability of attributes entirely, and consciously allow new features using attributes syntax to modify the \emph{mandated behaviour} of a program. In other words, doing nothing is the correct choice if we want to open up the design space of attributes to any feature that could be implemented as a keyword, but we do not want to introduce a new keyword (or contextual keyword) for. However, this is not what standard attributes were designed for; we believe that standard attributes should be syntactically ignorable, as described above, and non-ignorable language features (features changing the mandated behaviour of a program) should instead consider keywords (contextual keywords where feasible), or alternatively some other spelling, or where feasible implementation strategies that do not involve any additions to the C++ grammar.
\section{\tcode{__has_cpp_attribute}}
\subsection{The status quo}
Finally, the behavior of \tcode{__has_cpp_attribute} as specified in the standard today is ambiguous and should be fixed. On the one hand, the standard currently requires implementations to report a nonzero value even for syntactically recognised, but semantically ignored attributes ([cpp.cond]/6):
\begin{adjustwidth}{0.5cm}{0.5cm}
For an attribute specified in this document, the value of the \emph{has-attribute-expression} is given by Table 22. For other attributes recognized by the implementation, the value is implementation-defined.
\end{adjustwidth}
On the other hand, the standard simultaneously does \emph{not} require implementation to do that when they do not support the attribute, without any clarification what it means to ``support'' an attribute ([cpp.cond]/5):
\begin{adjustwidth}{0.5cm}{0.5cm}
Each \emph{has-attribute-expression} is replaced by a non-zero \emph{pp-number} matching the form of an \emph{integer-literal} if the implementation supports an attribute with the name specified by interpreting the \emph{pp-token}s, after macro expansion, as an \emph{attribute-token}, and by \tcode{0} otherwise.
\end{adjustwidth}
The wording regarding \mbox{\tcode{__has_cpp_attribute}} is therefore ambiguous. It is unclear whether \mbox{\tcode{__has_cpp_attribute}} should return a positive value if a compiler recognise and syntactically checks a standard attribute but then semantically ignores it.
\subsubsection*{\emph{Existing practice}}
The \mbox{\tcode{__has_cpp_attribute}} feature is a victim of implementation divergence. Clang and ICC both report a positive value for \mbox{\tcode{__has_cpp_attribute(carries_dependency)}}, even though they semantically ignore it; however, GCC reports \tcode{0} (and emits a diagnostic that it is being ignored). MSVC is inconsistent even with itself: it reports a positive value for \mbox{\tcode{__has_cpp_attribute(carries_dependency)}}, but \tcode{0} for \mbox{\tcode{__has_cpp_attribute(no_unique_address)}}, even though it does not implement semantics for either attribute.
\subsection{Proposed solution: the Third Ignorability Rule}
With regards to the intended behaviour of \tcode{__has_cpp_attribute}, we have the following two options to disambiguate the desired behaviour:
\begin{enumerate}
\item Specify that \tcode{__has_cpp_attribute} should return a positive value for a standard attribute only if an an implementation has a useful implementation of its semantics (GCC behaviour for \tcode{carries_dependency}, MSVC behaviour for \tcode{no_unique_address}).
\item Specify that \tcode{__has_cpp_attribute} should also return a positive value for a standard attribute if an implementation can parse it and check the syntax, even if it does not implement any useful semantics (Clang, ICC, and MSVC behaviour for \tcode{carries_dependency}).
\end{enumerate}
We propose option 1 as our third and final Ignorability Rule for standard attributes:
\begin{adjustwidth}{0.5cm}{0.5cm}
\textbf{The Third Ignorability Rule:}
The feature test macro for a standard attribute should return a positive value if an implementation actually implements the optional semantics of the attribute, \emph{not} if it merely parses the attribute and checks the syntax (as required by the First Ignorability Rule), despite the fact that the latter would be a conforming implementation for any standard attribute (due to the Second Ignorability Rule).
\end{adjustwidth}
The motivation is as follows. For cross-platform development, partially-supported standard attributes are often wrapped in macros like the following:
\begin{codeblock}
#if __has_cpp_attribute(assume)
#define ASSUME(expr) [[assume(expr)]]
#elif defined(__clang__)
#define ASSUME(expr) __builtin_assume(expr)
#elif defined(_MSC_VER) || defined(__ICC)
#define ASSUME(expr) __assume(expr)
#elif defined(__GNUC__)
#define ASSUME(expr) if (expr) {} else { __builtin_unreachable(); }
#else
#define ASSUME(expr) if (expr) {} else { *(char*)nullptr; }
#endif
\end{codeblock}
In the above macro, the intention of using \tcode{__has_cpp_attribute} is to query whether the compiler will attempt to optimise based on an \tcode{assume} attribute; if not, the functionality is delegated to compiler-specific intrinsics that offer the same functionality, and if none are available, to a generic workaround to get the desired semantics. Here is another example:
\begin{codeblock}
#if __has_cpp_attribute(no_unique_address)
#define NO_UNIQUE_ADDRESS [[no_unique_address]]
#elif _MSC_VER >= 1929
#define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#else
#error "Overlapping subobjects are not supported by this compiler!"
#endif
\end{codeblock}
In the above macro, we want to ensure that subobjects marked with \tcode{NO_UNIQUE_ADDRESS} are in fact zero size. The intention of using \tcode{__has_cpp_attribute} is to query whether the compiler will honour the class layout changes introduced by a \tcode{no_unique_address}; if not, we delegate to a compiler-specific alternative on MSVC that is known to work starting from a certain compiler version, or error out if the desired property is not supported by the compiler.
Such macros are widespread in cross-platform C++ code bases. In all such macros, the query is whether the compiler implements the optional semantics of the attribute; such a query is a lot more useful than merely querying if the compiler recognises the attribute syntactically (Option 2), as it allows for a meaningful fallback implementation.
This issue is not unique to C++; C has a similar problem with \tcode{__has_c_attribute}. To our knowledge, the direction proposed here is in line with what WG14 intends to do for \tcode{__has_c_attribute}, and we should not end up in a world where the specifications of \tcode{__has_cpp_attribute} and \tcode{__has_c_attribute} contradict each other.
In order to implement this change, we need to do two things:
\begin{enumerate}
\item Explicitly allow a conforming implementation to return \tcode{0} from \tcode{__has_cpp_attribute} if does not implement the optional semantics of the attribute,
\item Define what constitutes ``implementing the optional semantics''.
\end{enumerate}
The first one is a simple wording addition to [cpp.cond]/6 to make it implementation-defined whether the value returned from \tcode{__has_cpp_attribute} is \tcode{0} or the value in the table (which allows the former to be conforming). However, the second one is surprisingly tricky and cannot be done as a general statement for all standard attributes; we need to look at every standard attribute individually.
For some attributes, the desired behaviour is clear: if the attribute is meant to trigger (or suppress) a warning, \tcode{__has_cpp_attribute} should return \tcode{0} if no such warning is triggered (or the warning is not suppressed). But for other attributes, what constitutes ``implementing the optional semantics'' is more vague. If an attribute is there to enable optimisations, what should happen if the compiler does not actually perform the optimisations? What should happen if the compiler only does so with a particular set of flags (such as \tcode{-O3}) but not others (such as \tcode{-O0})? Should the value be different depending on the build mode? What should happen if the ``compiler'' is only a frontend (such as EDG) and does not contain a backend?
To arrive at an unambiguous and user-friendly specification for the Third Ignorability Rule, we need to look at the \emph{Recommended practice} section of every standard attribute's specification, and add wording to that specification giving a recommendation when the value of \tcode{__has_cpp_attribute} should be \tcode{0} in a way that gives compilers enough implementation freedom to ``do the right thing'' for that particular attribute so that the above macros behave as expected in cases where the answer is not clear-cut. A positive value should communicate to the user that it the implementation making an ``honest attempt'' at doing something useful with a particular standard attribute, and it is \emph{not} merely parsing the syntax, checking for syntax and appertainment errors, and then semantically ignoring all instances of that attribute.
Regarding the currently existing implementation divergence, the current behaviour of \tcode{__has_cpp_attribute} on Clang, ICC, and MSVC for \tcode{carries_dependency} goes against the Third Ignorability Rule, while the current behaviour on GCC for \mbox{\tcode{carries_dependency}} and on MSVC for \tcode{no_unique_address} follows the Third Ignorability Rule.
\subsection{The issue with \tcode{carries_dependency}}
The current specification of \tcode{carries_dependency} is defective. The note where we would have to add wording for the Third Ignorability Rule for this attribute is [dcl.attr.depend] paragraph 3:
\begin{adjustwidth}{0.5cm}{0.5cm}
[\emph{Note 1:} The \tcode{carries_dependency} attribute does not change the meaning of the program, but might result in generation of more efficient code. --- \emph{end note}]
\end{adjustwidth}
This note contains a false statement today because the attribute \emph{does} change the meaning of the program. It has been clarified by SG1 experts that adding \tcode{[[carries_dependency]]} to a parameter may add a \emph{dependency-ordered before} relationship that would not be present without the attribute. This relationship may make a program that would otherwise have undefined behaviour due to a violation of the ordering rules have defined behaviour. It is also the case that no implementation actually implements memory order Consume, and that WG21's current intent is to overhaul memory order Consume (see \cite{P0750R1}) and deprecate and remove the \tcode{carries_dependency} attribute.
It would be desirable to include \tcode{carries_dependency} in our proposed specification for the Third Ignorability Rule, and to encourage implementations that do not use \tcode{carries_dependency} for optimisations (that is, \emph{all} existing implementations of C++) to return \tcode{0} from \tcode{__has_cpp_attribute(carries_dependency)}. However, considering the issues with the current state of this attribute, we decided to follow the EWG guidance and exclude this attribute from the wording proposed in this paper.
\section{Summary: the Three Rules of Ignorability}
In this paper, we have shown that ignorability of standard attributes in C++20 is not well defined. We have considered three aspects of ignorability: syntactic ignorability, semantic ignorability, and the behaviour of \tcode{__has_cpp_attribute}. For each case, we highlighted where the current wording has ambiguities, surveyed current implementation practice in the latest versions of four major C++ compilers (MSVC, GCC, Clang, and ICC), and discussed different options to resolve the existing ambiguities. Considering the above, we propose the following Three Rules of Ignorability as a language design guideline for all current and future standard attributes going forward:
\begin{enumerate}
\item Standard attributes cannot be syntactically ignored, but must be parsed; syntax errors in the argument clause, appertainment rules, and any additional syntactic requirements specified by a particular standard attribute must be diagnosed; and entities in the argument clause must be ODR-used.
\item Given a well-formed program, removing all instances of a particular standard attribute is allowed to change the observable behaviour of the program, but only if the behaviour with the attribute removed would have been a conforming behaviour for the original program with the attribute present.
\item The feature test macro for a standard attribute should return a positive value if an implementation actually implements the optional semantics of the attribute, not if it merely parses the attribute and and checks the syntax, despite the fact that the latter would be a conforming implementation for any standard attribute.
\end{enumerate}
\section{Wording already adopted for C++23}
Wording for the First and the Second Ignorability Rule, fixing the ambiguities in C++20 around syntactic and semantic ignorability, has already been added for C++23 by way of adopting Core Issues \cite{CWG2538} and \cite{CWG2695}, respectively. The adopted changes modify [dcl.attr.grammar] paragraph 6 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
For an \emph{attribute-token} (including an \emph{attribute-scoped-token}) not specified in this document, the behavior is implementation-defined\removed{. Any}\added{; any such} \emph{attribute-token} that is not recognized by the implementation is ignored.
\added{[\emph{Note 4:} A program is ill-formed if it contains an \emph{attribute} specified in [dcl.attr] that violates the rules to which entity or statement the attribute may apply or the syntax rules for the attribute's \emph{attribute-argument-clause}, if any. --- \emph{end note}]}
\added{[\emph{Note 5:} The \emph{attribute}s specified in [dcl.attr] have optional semantics: given a well-formed program, removing all instances of any one of those \emph{attribute}s results in a program whose set of possible executions ([intro.abstract]) for a given input is a subset of those of the original program for the same input, absent implementation-defined guarantees with respect to that \emph{attribute}. --- \emph{end note}]}
\end{adjustwidth}
\section{Proposed Wording}
\label{sec:wording}
Wording for the Third Ignorability Rule, fixing the ambiguity of \tcode{__has_cpp_attribute}, has not yet been added to the C++ Standard, so this paper proposes to do so. Our proposed changes are relative to the Working Draft \cite{N4944}. Modify [cpp.cond] paragraphs 5 and 6 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
Each \emph{has-attribute-expression} is replaced by a non-zero \emph{pp-number} matching the form of an \emph{integer-literal} if the implementation supports an attribute with the name specified by interpreting the \emph{pp-tokens}, after macro expansion, as an \emph{attribute-token}, and by \tcode{0} otherwise. The program is ill-formed if the \emph{pp-token}s do not match the form of an \emph{attribute-token}.
For an attribute specified in this document,\added{ it is implementation-defined whether} the value of the \emph{has-attribute-expression}\added{ is \tcode{0} or} is given by Table 21. For other attributes recognized by the implementation, the value is implementation-defined.
[\emph{Note 1:} It is expected that the availability of an attribute can be detected by any non-zero result. --- \emph{end note}]
\end{adjustwidth}
Modify [dcl.attr.assume] as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
[\emph{Note 1:} The expression is potentially evaluated ([basic.def.odr]). The use of assumptions is intended to allow implementations to analyze the form of the expression and deduce information used to optimize the program. Implementations are not required to deduce any information from any particular assumption. \added{It is expected that the value of a \emph{has-attribute-expression} for the \tcode{assume} attribute is \tcode{0} if an implementation does not attempt to deduce any such information from assumptions.} --- \emph{end note}]
\end{adjustwidth}
Modify [dcl.attr.deprecated] paragraph 4 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
\emph{Recommended practice:} Implementations should use the \tcode{deprecated} attribute to produce a diagnostic message in case the program refers to a name or entity other than to declare it, after a declaration that specifies the attribute. The diagnostic message should include the text provided within the \emph{attribute-argument-clause} of any deprecated attribute applied to the name or entity. \added{The value of a \emph{has-attribute-expression} for the \tcode{deprecated} attribute should be \tcode{0} unless the implementation can issue such diagnostic messages.}
\end{adjustwidth}
Modify [dcl.attr.fallthrough] paragraph 2 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
\emph{Recommended practice:} The use of a fallthrough statement should suppress a warning that an implementation might otherwise issue for a case or default label that is reachable from another case or default label along some path of execution. \added{The value of a \emph{has-attribute-expression} for the \tcode{fallthrough} attribute should be \tcode{0} if the attribute does not cause suppression of such warnings.} Implementations should issue a warning if a fallthrough statement is not dynamically reachable.
\end{adjustwidth}
Modify [dcl.attr.likelihood] paragraph 2 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
\removed{\emph{Recommended practice:} }\added{[\emph{Note 1:} }The use of the \tcode{likely} attribute is intended to allow implementations to optimize for the case where paths of execution including it are arbitrarily more likely than any alternative path of execution that does not include such an attribute on a statement or label. The use of the \tcode{unlikely} attribute is intended to allow implementations to optimize for the case where paths of execution including it are arbitrarily more unlikely than any alternative path of execution that does not include such an attribute on a statement or label. \added{It is expected that the value of a \emph{has-attribute-expression} for the \tcode{likely} and \tcode{unlikely} attributes is \tcode{0} if the implementation does not attempt to use these attributes for such optimizations.} A path of execution includes a label if and only if it contains a jump to that label.\added{ --- \emph{end note}]}
\end{adjustwidth}
Modify [dcl.attr.unused] paragraph 4 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
\emph{Recommended practice:} \added{}For an entity marked \tcode{maybe}_\tcode{unused}, implementations should not emit a warning that the entity or its structured bindings (if any) are used or unused. For a structured binding declaration not marked \tcode{maybe}_\tcode{unused}, implementations should not emit such a warning unless all of its structured bindings are unused. \added{The value of a \emph{has-attribute-expression} for the \tcode{maybe}_\tcode{unused} attribute should be \tcode{0} if the attribute does not cause suppression of such warnings.}
\end{adjustwidth}
Modify [dcl.attr.nodiscard] paragraph 4 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
\emph{Recommended practice:} Appearance of a \tcode{nodiscard} call as a potentially-evaluated discarded-value expression ([expr.prop]) is discouraged unless explicitly cast to \tcode{void}. Implementations should issue a warning in such cases. \added{The value of a \emph{has-attribute-expression} for the \tcode{nodiscard} attribute should be \tcode{0} unless the implementation can issue such warnings.}
\end{adjustwidth}
Modify [dcl.attr.noreturn] paragraph 3 as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
\emph{Recommended practice:} Implementations should issue a warning if a function marked \tcode{[[noreturn]]} might return. \added{The value of a \emph{has-attribute-expression} for the \tcode{noreturn} attribute should be \tcode{0} unless the implementation can issue such warnings.}
\end{adjustwidth}
Modify [dcl.attr.nouniqueaddr] as follows:
\begin{adjustwidth}{0.5cm}{0.5cm}
The \emph{attribute-token} \tcode{no_unique_address} specifies that a non-static data member is a potentially-overlapping subobject ([intro.object]). No \emph{attribute-argument-clause} shall be present. The attribute may appertain to a non-static data member other than a bit-field.
[\emph{Note 1:} The non-static data member can share the address of another non-static data member or that of a base class, and any padding that would normally be inserted at the end of the object can be reused as storage for other members. --- \emph{end note}]
\added{\emph{Recommended practice:} The value of a \emph{has-attribute-expression} for the \tcode{no}_\tcode{unique}_\tcode{address} attribute should be \tcode{0} for a given implementation unless this attribute can cause a potentially- overlapping subobject to have zero size.}
\end{adjustwidth}
We propose that these changes be treated as a Defect Report.
\section*{Document history}
\begin{itemize}
\item \textbf{R0}, 2022-02-15: Initial version.
\item \textbf{R1}, 2022-11-15: Removed wording for syntactic and semantic ignorability (now covered by \cite{CWG2538} and \cite{CWG2695}, respectively); added discussion of \tcode{__has_cpp_attribute}; for all three aspects of ignorability, listed options for EWG to consider.
\item \textbf{R2}, 2023-05-19: Restructured paper; formulated the Three Rules of Ignorability as design guidelines (following options chosen by EWG); added wording for fixing \tcode{__has_cpp_attribute} as a DR.
\item \textbf {R3}, 2023-06-14: Updated wording for fixing \tcode{__has_cpp_attribute} to reflect EWG and CWG feedback; added section about \tcode{carries_dependency} to rationale.
\end{itemize}
\section*{Acknowledgements}
We would like to thank John Lakos for his thorough review of an earlier draft of the paper, and for contributing the idea of regions of program behaviour and the concept of \emph{mandated behaviour}; Corentin Jabot for contributing the code example where removing a \mbox{\tcode{[[no_unique_address]]}} attribute can cause a program to stop compiling; Aaron Ballman, Erich Keane, Jens Maurer, and Peter Brett for their valuable comments on earlier drafts of this paper; Daveed Vandevoorde for reviewing the wording for the Third Ignorability Rule; and Michael Spencer for clarifying the issues with \tcode{carries_dependency}.
\renewcommand{\bibname}{References}
\bibliographystyle{abstract}
\bibliography{ref}
\end{document}