
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>N1604 -- Proposal to Add Static Assertions to the Core Language (Revision 1)</title>
</head>
<body>
<pre><br>
            Doc No:                 SC22/WG21/N1604
                                    J16/04-0044
            Date:                   February 13, 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 1)</h1>
<p>
This is a revision of paper N1381 in the pre-Santa Cruz mailing.  Proposed working paper wording has been added, discussion and examples expanded, and implementation experience in a second commercial compiler described.
</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; 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:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static_assert ( constant-expression&nbsp; ,&nbsp; literal ) ;</i></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>
<p>A <i>static_assert-declaration</i> shall have no effect. The <i>
constant-expression </i>shall be an <i>integral constant-expression</i> (5.19 [expr.const]) 
which evaluates to a non-zero value.<sup> (Footnote)</sup></p>
<p><sup>(Footnote)</sup> It is recommended that, if the <i>constant-expression</i> 
evaluates to zero, the resulting diagnostic message or messages (1.4.2) 
contain the text of the literal.</p>
</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>
