
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>N1720 -- Proposal to Add Static Assertions to the Core Language (Revision 3)</title>
</head>
<body>
<pre><br>
            Doc No:                 SC22/WG21/N1720
                                    J16/04-0160
            Date:                   October 20, 2004
            Project:                JTC1.22.32<br>
            Reply to:               Robert Klarer
                                    klarer@ca.ibm.com<br>
                                    Dr. John Maddock
                                    john@johnmaddock.co.uk<br>
                                    Beman Dawes
                                    bdawes@acm.org<br>
                                    Howard Hinnant
                                    hinnant@twcny.rr.com<br>
</pre>
<h1>Proposal to Add Static Assertions to the Core Language (Revision 3)</h1>
<p>
This is a revision of paper N1617 in the post-Sydney mailing.  At the Redmond meeting, the working paper wording that was proposed in N1617 was subjected to meticulous review by the Core Working Group (CWG).  This revision reflects the results of that review.
</p>
<h2>1. The Problem</h2>
<p>The C++ language currently supports two facilities for testing
software assertions:
</p>
<ol>
  <li>the assert macro, and </li>
  <li>the #error preprocessor directive </li>
</ol>
<p>Neither of these facilities is appropriate for use in template
libraries. To enforce a template parameter constraint, for example, it
should be possible to write an assertion that will be tested when the
associated template is instantiated. The assert macro tests assertions
at runtime, which is far later than would be desired by the author of a
template library, and which implies a runtime performance cost. The
#error preprocessor directive is processed too early to be of much use
in this regard. In particular, the #error directive is processed before
templates are instantiated, and hence cannot be used to test assertions
involving template arguments.
</p>
<p>The need for a solution to this problem is demonstrated by the existence of the <a
 href="http://www.boost.org/libs/static_assert/static_assert.htm">Boost
Static Assertion Library</a>, which is widely used in other Boost
libraries.  The <a
 href="http://www.awl.com/cseng/titles/0-201-70431-5">Loki</a> library,
which is not currently a part of Boost, has a similar facility, in the
form of a STATIC_CHECK macro.
The book <a href="http://www.generative-programming.org">Generative Programming</a>, by Czarnecki and Eisenecker, uses
template metaprogramming techniques to simulate static asserts, for
example, when doing buildability checking in the middle stages of
configuration generation.
</p>
<p>At the time that the Boost Static Assertion Library was developed,
the following design requirements for a static assertion checking
facility were identified:
</p>
<ol>
  <li>all processing must be performed during compile time -- no
runtime cost in space or time is tolerable </li>
  <li>it must have simple syntax that can easily be taught to beginners
  </li>
  <li>assertion failure must result in a meaningful and informative
diagnostic error message </li>
  <li>it can be used at namespace, class, or block scope </li>
  <li>misuse does not result in silent malfunction, but rather is
diagnosed at compile time </li>
</ol>
<p>The proposal described in this paper satisfies all of these
requirements.
</p>
<p>The Boost Static Assertion Library and the Loki STATIC_CHECK macro
constitute two independent attempts to solve this problem without
introducing a change to the specification of the core language, but
each has the following inadequacies:
</p>
<ol>
  <li>assertion failure produces compile-time diagnostics that are
typically uninformative </li>
  <li>each uses a macro (BOOST_STATIC_ASSERT, STATIC_CHECK) to generate
the types that represent the assertion, and this poses namespace
pollution problems </li>
</ol>
<p>The addition of static assertions to the core language would have
the following effects:
</p>
<ol>
  <li>it would improve support for library building by allowing
libraries to detect common usage errors at compile time</li>
  <li>it would make C++ easier to teach and learn by allowing C++
Standard Library implementations to detect and diagnose common usage
errors</li>
  <li>it would remove embarrassments by supporting programming
techniques in the core language that today rely on the preprocessor</li>
</ol>
<h2>2. The Proposal</h2>
<h3>2.1 The Substance of this Proposal</h3>
<p>A static_assert-declaration takes the following form:</p>
<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static_assert ( constant-expression&nbsp; ,&nbsp; string-literal ) ;</p>
<p>Intuitively, a static_assert-declaration may appear anywhere that a
using-declaration can, including namespace scope, block scope, and
class member declaration lists.
</p>
<p>
If the constant-expression in the static assertion evaluates as 0, the
compiler will issue a diagnostic message containing the literal.  Otherwise,
the static_assert-declaration has no effect.
</p>
<p>The static_assert-declaration does not declare a new type or object,
and does not imply any size or time cost at runtime.
</p>
<p>This paper does not propose that the core language be extended to
support template static_assert-declarations. For example, the following
is NOT proposed:
</p>
<blockquote>
<pre>
<code>
template &lt;typename T&gt;
static_assert(sizeof(int) &lt;= sizeof(T), "not big enough");
</code>
</pre>
</blockquote>
<p>There is no demonstrated need for this generality. The proposal
described in this paper allows the following, equivalent assertion to
be written:
</p>
<blockquote>
<pre>
<code>template &lt;typename T&gt;
struct Check {
	static_assert(sizeof(int) &lt;= sizeof(T), "not big enough");
};
</code>
</pre>
</blockquote>
<h3>2.2 Proposed Wording</h3>
<p>To Table 3 in clause 2.11, Keywords [lex.key], add the new keyword "static_assert"
<p>To clause 7, Declarations [dcl.dcl], paragraph 1, add an additional entry to
<i>block-declaration</i>:</p>
<blockquote>
<p><i>static_assert-declaration</i></p>

</blockquote>
<p>To clause 9.2, Class members [class.mem], initial paragraph, add an 
additional entry to <i>member-declaration</i></p>
<blockquote>
<p><i>static_assert-declaration</i></p>
</blockquote>
<p>To clause 7, Declarations [dcl.dcl], paragraph 1, add an additional outer 
level grammar element:</p>
<blockquote>
<p><i>static_assert-declaration:</i><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <code>static_assert ( </code> <i>constant-expression</i>&nbsp; <code>,</code>&nbsp; <i>string-literal</i> &nbsp;<code>) ;</code></p>

</blockquote>
<p>To clause 7, at a location to be determined by the Project Editor,
add a new section or sub-section:</p>
<blockquote>
<p><b>7.? static_assert declaration</b></p>

The <em>constant-expression</em> shall be an integral constant
expression (5.19 [expr.const]).  If the value of the expression when
converted to <code>bool</code> is <code>true</code>, the declaration
has no effect.  Otherwise, the program is ill-formed, causing the
implementation to produce a diagnostic message (1.4
[intro.compliance]) that includes the text of the
<em>string-literal</em>, except that characters not in the basic
source character set (2.2 [lex.charset]) are not required to appear in
the diagnostic message.
[<em>Example:</em>

<blockquote>
<pre>
static_assert(sizeof(long) >= 8, "64-bit code generation is not enabled/supported.");
</pre>
</blockquote>
<em>--end example</em>]
</blockquote>
<h3>2.3 Examples</h3>
<a name="2.3.1">
</a>
<h4>2.3.1 Static assertions at namespace scope</h4>
<blockquote>
<pre>
<code>
// At namespace scope, the static_assertion declaration
// may be used as an alternative to the #error preprocessor
// directive.
//
static_assert(sizeof(long) &gt;= 8, "64-bit code generation not enabled/supported.");
</code>
</pre>
</blockquote>
<a name="2.3.2">
</a>
<h4>2.3.2 Static assertions at class scope</h4>
<blockquote>
<pre>
<code>
template &lt;class CharT, class Traits = std::char_traits&lt;CharT&gt; &gt;
class basic_string {
        static_assert(tr1::is_pod&lt;CharT&gt;::value,
                      "Template argument CharT must be a POD type in class template basic_string");
        // ... 
};
</code>
</pre>
</blockquote>
<a name="2.3.3">
</a>
<h4>2.3.3 Static assertions at block scope</h4>
<blockquote>
<pre>
<code>
#include &lt;sys/param.h&gt; // for PAGESIZE

class VMMClient {
public:
        int do_something()
        {
                struct VMPage {
                        // ...
                };
                static_assert(sizeof(VMPage) == PAGESIZE,
                              "Struct VMPage must be the same size as a system virtual memory page.");

                // ...
        }

        // ...
};
</code>
</pre>
</blockquote>
<a name="2.3.4">
</a>
<h4>2.3.4 Double-checking complicated compile-time computation results</h4>
<blockquote>
<pre>
<code>
template &lt;class T&gt;
class A {
private:
        struct impl_1 {...};
        struct impl_2 {...};
        static const unsigned sz1 = sizeof(impl_1) < sizeof(impl_2) ?
                                    sizeof(impl_2) : sizeof(impl_1);
        static const unsigned sz2 = sizeof(T);
        static const unsigned sz3 = sz1 + (sz2-1) & ~(sz2-1);
        struct impl {
                union {
                        impl_1 a;
                        impl_2 b;
                };
                T data_[sz3];

                static_assert(sizeof(data_) >= sizeof(impl_1),
                              "Design error:  Assumption compromised");
                static_assert(sizeof(data_) >= sizeof(impl_2),
                              "Design error:  Assumption compromised");
        };
};
</code>
</pre>
</blockquote>
<a name="2.3.5">
</a>
<h4>2.3.5 Double-checking design assumptions</h4>
<blockquote>
<pre>
<code>
template &lt;class T, bool&gt;
class container_impl {
        static_assert(!std::tr1::is_scalar&lt;T&gt;::value,
                      "Implementation error: this specialization intended for non-scalars only");
        // ...
};

template &lt;class T&gt;
class container_impl&lt;T, true&gt; {
        static_assert(std::tr1::is_scalar&lt;T&gt;::value,
                      "Implementation error: this specialization intended for scalars only");
        // ...
};

template &lt;class T&gt;
class container : private container_impl&lt;T, std::tr1::is_scalar&lt;T&gt;::value&gt; {
        // ...
};
</code>
</pre>
</blockquote>
<h2>3. Comparing Static Assertions to Concepts</h2>
This proposal does not compete with current proposals to introduce core language support for concepts to the C++ language.  The introduction of intrinsic support for concepts to C++ will not eliminate the need for static assertions.
Static assertions solve a different set of problems than intrinsic support for concepts will solve, and they facilitate styles of programming that concepts do not:
<ul>
<li>static assertions are useful in C++ programs that do not use templates;
see examples <a href="#2.3.1">2.3.1</a> and <a href="#2.3.3">2.3.3</a>, above
<li>static assertions permit library authors to issue customized diagnostic messages to users of their libraries
<li>static assertions complement the TR1 type_traits library --
see examples <a href="#2.3.2">2.3.2</a> and <a href="#2.3.5">2.3.5</a>, above;
the TR1 type_traits library was designed with the intention that it would be used together with static assertions; the result is a programming style that is common today
(through the use of the BOOST Static Assertion Library)
and continues to grow in popularity
<li>static assertions, when used together with the TR1 type_traits library, can diagnose type errors that intrinsic support for concepts cannot
</ul>

<h3>3.1 Diagnosing Type Errors that Concepts Cannot</h3> 

There are some conceptual constraints that can not be checked via a "functional" check.  One example of such a constraint is that a type template argument is a POD:
<blockquote>
<pre>
<code>
template &lt;ValueType charT, StringTraits traits&gt;
class basic_string {
        // ...
};
</code>
</pre>
</blockquote>
The constraints ValueType and StringTraits can verify most of the
requirements, but basic_string also requires that charT is a POD, indeed
rather nasty things may occur silently if that is not the case, the obvious
fix is:
<blockquote>
<pre>
<code>
template &lt;ValueType charT, class traits&gt;
class basic_string {
static_assert(tr1::is_pod&lt;charT&gt;::value,
              "Template argument charT must be a POD type in class template basic_string");
        // ...
};
</code>
</pre>
</blockquote>
<h3>3.2 Limitations of Static Assertions</h3>
<p>A static assertion whose constant-expression depends on one or more template
parameters will not be checked by the compiler until the corresponding
template is instantiated. This is later than is desired for many cases.
Intrinsic language support for concepts will permit the violation of
generic concepts to be detected, in many cases, before template
instantiation occurs.</p>
<p>Successful use of static assertions to verify complicated cases will often
involve the use of template metaprogramming or other metaprogramming
techniques.  Intrinsic language support for concepts will likely permit easier
expression of template constraints in many cases.</p>

<h2>4. Interactions and Implementability</h2>
<h3>4.1 Interactions</h3>
<p>This proposal does not affect or alter any existing language
feature. Legacy code is not affected by the introduction of
static_assert to the core language, except code in which the word
static_assert is used as an identifier. Legacy code that uses the word
static_assert as a macro name will not be affected.
</p>
<h3>4.2 Implementability</h3>
<p>Static assertions have been used in the Boost libraries for nearly four 
years. This proposal is based upon that experience.
</p>
<p>Experimental implementations of the static_assert-declaration
exist in development versions of both the IBM and
Metrowerks C++ compilers.  These implementations correctly handle all of
the cases identified in this paper.</p>
<p>In the case of each compiler, the implementation of this feature caused
no regressions to be reported when validated against 
C++ language conformance test buckets containing many thousands of test
cases</p>
<p>A compiler writer could certainly implement this feature, as
specified, in two or three days (most likely much less). Implementation of
this feature requires modification of the compiler frontend only; no changes
to the backend, runtime library, linker, or debugger are necessary.
</p>
<h3>4.3 Example Output</h3>
<p>The IBM implementation produces the following diagnostic
output when compiling the example that appears below: 

<blockquote>
"section_4_3.C", line 8.9: 1540-5220 (S) ""Template argument CharT must be a POD type in class template basic_string""<br>
"section_4_3.C", line 22.28: 1540-0700 (I) The previous message was produced while processing "class std::basic_string&lt;char,std::char_traits&lt;char&gt; &gt;".<br>
"section_4_3.C", line 20.5: 1540-0700 (I) The previous message was produced while processing "main()".<br>
</blockquote>
<blockquote>
<pre>
<code>
#include &lt;type_traits&gt;
#include &lt;iosfwd&gt;

namespace std {

template &lt;class CharT, class Traits = std::char_traits&lt;CharT&gt; &gt;
class basic_string {
        static_assert(tr1::is_pod&lt;CharT&gt;::value,
                      "Template argument CharT must be a POD type in class template basic_string");
        // ...
};

}

struct NonPOD {
        NonPOD(const NonPOD &) {}
        virtual ~NonPOD() {}
};

int main()
{
   std::basic_string&lt;char&gt; bs;
}
</code>
</pre>
</blockquote>
</body>
</html>
