P1301R2
[[nodiscard("should have a reason")]]

Published Proposal,

Authors:
Isabella Muerte
Audience:
EWG, CWG
Project:
ISO/IEC JTC1/SC22/WG21 14882: Programming Language — C++
Latest:
https://thephd.github.io/vendor/future_cxx/papers/d1301.html
Reply To:
@slurpsmadrips

Abstract

This paper proposes allowing an explanatory string token for use with the nodiscard attribute.

1. Revision History

1.1. Revision 2 - January 21st, 2019

1.2. Revision 1 - November 7th, 2018

1.3. Revision 0 - October 7th, 2018

2. Feedback

After a brief discussion with no negative feedback on November in San Diego, the chair forwarded this to EWG with the caveat that this paper should ask both LWG and LEWG representatives to chime in. A message was sent out on the reflector with 50+ responses indicating strong support (90%+ positive affirmation) for having this feature.

The question of whether or not the standard should mandate the text that goes in a [[nodiscard]] or [[deprecated]] attribute. There was extremely strong push back to that idea, and this paper does not propose the text. This paper encourages implementers to apply any text they see fit, and to bring a paper over on what should not only be marked nodiscard, but any standardized text for the attribute warning they see fit.

This paper simply proposes making it possible to have the reason present.

3. Motivation

Currently With Proposal
struct data_holder {
private:
	std::unique_ptr<char[], data_deleter> ptr;
	std::vector<int> indices;
	// ...

public:
	[[nodiscard]] 
	char* release() { indices.clear(); return ptr.release(); }

	void clear() { indices.clear(); ptr.reset(nullptr); }

	[[nodiscard]] 
	bool empty() const { return ptr != nullptr && !indices.empty(); }
};

int main () {
	data_holder dh = /* ... */;

	// serious error, 
	// do not (void) this
	dh.release();
	// just as noisy as release(), 
	// but a little less serious
	dh.empty();

	return 0;
}
struct data_holder {
private:
	std::unique_ptr<char[], data_deleter> ptr;
	std::vector<int> indices;
	// ...

public:
	[[nodiscard("Possible memory leak.")]] 
	char* release() { indices.clear(); return ptr.release(); }

	void clear() { indices.clear(); ptr.reset(nullptr); }

	[[nodiscard("Did you mean 'clear'?")]] 
	bool empty() const { return ptr != nullptr && !indices.empty(); }
};

int main () {
	data_holder dh = /* ... */;

	// serious error, with explanation
	dh.release();
	// less serious, with explanation
	dh.empty();

	return 0;
}

The [[nodiscard]] attribute has helped prevent a serious class of software bugs, but sometimes it is hard to communicate exactly why a function is marked as [[nodiscard]].

This paper adds an addendum to allow a person to add a string attribute token to let someone provide a small reasoning or reminder for why a function has been marked [[nodiscard("Please do not leak this memory!")]].

4. Design Considerations

This paper is an enhancement of a preexisting feature to help programmers provide clarity with their code. Anything that makes the implementation warn or error should also provide some reasoning or perhaps point users to a knowledge base or similar to have any questions they have about the reason for the nodiscard attribute answered.

The design is very simple and follows the lead of the deprecated attribute. We propose allowing a string literal to be passed as an attribute argument clause, allowing for [[nodiscard("You must use the returned token with api::foobar")]]. The key here is that there are some nodiscard attributes that have different kinds of "severity" versus others. That is, the cost of discarding vector::empty's return value is probably of slightly less concern than unique_ptr::release: one might manifest as a possible bug, the other is a downright memory leak.

Adding a reason to nodiscard allows implementers of the standard library, library developers, and application writers to benefit from a more clear and concise error beyond "Please do not discard this return value". This makes it easier for developers to understand the intent of the code they are using.

5. Wording

All wording is relative to [n4778].

5.1. Intent

The intent of the wording is to let a programmer specify a string literal that the compiler can use in their warning / error display for, e.g. [[nodiscard("Please check the status variable")]].

5.2. Feature Test Macro

nodiscard already has an attribute value in Table 15. If this proposal is accepted, then per the standard’s reasoning the value of nodiscard should be increased to some greater value (such as 201811L).

5.3. Proposed Wording

Modify §14.1 [cpp.cond]'s Table 15:

Attribute Value
nodiscard 201603L 201902L

Append the following to §9.11.9 Nodiscard attribute [dcl.attr.nodiscard]'s clause 1:

1 The attribute-token nodiscard may be applied to the declarator-id in a function declaration or to the declaration of a class or enumeration. It shall appear at most once in each attribute-list and no attribute argument-clause shall be present. An attribute-argument-clause may be present and, if present, it shall have the form:

( string-literal )

[Note: The string-literal in the attribute-argument-clause could be used to explain the rationale for why the entity must not be discarded. — end note]

References

Informative References

[N4778]
ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee; Richard Smith. N4778 - Working Draft, Standard for Programming Language C++. November 26th, 2018. URL: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4778.pdf