Asserting an expected compile-time failure

251 views
Skip to first unread message

Andrzej Krzemieński

unread,
Oct 7, 2013, 5:06:11 AM10/7/13
to std-pr...@isocpp.org
Hi All,
I wanted to share an idea about a new language feature that I have been missing for some time.

Motivation:

One of the features offered by a statically typed language (and in fact, probably the most important one) is a guarantee that certain declarations or expressions will fail to compile. For instance, imagine that we had SI units in the language:

  Length l = 1m;
  Duration t = 1s;
  t + l; // compile time error -- a nice feature

As a library (especially a generic library) author, I would like to be able to test if I correctly provide this safety feature guarantee. Namely, I would like to be able to verify if certain constructs in code fail to compile.

Proposal:

I imagine that this could be implemented by making a "branch" in the compilation process when the code requests one. The compilation of the "trunk" is suspended. Template instantiations and ODR usages performed from now on, are not visible in "trunk". The compilation in the "branch" continues until one of the two happens:

  1. We get a compilation error.
  2. We compiled successfully everything in the branch.

In the first case we stop compiling the "branch" and proceed to compile the "trunk" from where we suspended. Everything is fine.

In the second case, we trigger a compile-time error in the "trunk" with a user-provided error message.

How an additional language to support this feature would look, is depicted in the following examples. I used an extra keyword assert_static_failure:

  template <typename T>
  struct IsTrue;

  template<>
  struct IsTrue<true>
  {
    constexpr static bool value = true;
  };

  assert_static_failure ("test IsTrue") {
    IsTrue<(3 == 1)>::value;
  } // compiles fine: no effect, no symbols ODR used or templates instantiated

  assert_static_failure ("test IsTrue - err") {
    IsTrue<(1 == 1)>::value;
  } // compile-time error: a failure was asserted, but no failure occurred

another example:

  template <int N>
  void fun()
  {
    static_assert (N != 0, "N must be non-zero");
  }

  assert_static_failure ("test N == 0") {
    fun<0>();
  } // compiles fine

  assert_static_failure ("test N != 0") {
    fun<1>();
  } // error: expected a failure


I wanted to check in this list, if this is a feature worth standardizing, and if it is worth pursuing with this idea.

Regards,
&rzej

Maurice Bos

unread,
Oct 7, 2013, 5:13:58 AM10/7/13
to std-pr...@isocpp.org
It might be more useful in the form of: static_assert(!compiles{ fun<1>(); }, "What happened?");

Here, compiles{} gives a boolean. I'm not saying it'd be a good idea to have this new type of expression in the language, but I'd prefer it over 'assert_static_failure' as it is more general and could be of use in other contexts.


2013/10/7 Andrzej Krzemieński <akrz...@gmail.com>

--
 
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Evgeny Panasyuk

unread,
Oct 7, 2013, 1:10:17 PM10/7/13
to std-pr...@isocpp.org
7 oct 2013 г., 13:06:11 UTC+4 Andrzej Krzemieński:

I wanted to check in this list, if this is a feature worth standardizing, and if it is worth pursuing with this idea.

Have you considered requires expression from Concepts Lite proposal? http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3701.pdf - page 6
static_assert
(
   
!requires(Length l, Duration t)
   
{
        t
+ l;
   
},
   
""
);

I think it will cover at least some of use cases of assert_static_failure.

Evgeny Panasyuk

unread,
Oct 7, 2013, 1:14:48 PM10/7/13
to std-pr...@isocpp.org
7 oct 2013 г., 13:13:58 UTC+4 Maurice Bos:
It might be more useful in the form of: static_assert(!compiles{ fun<1>(); }, "What happened?");

Here, compiles{} gives a boolean. I'm not saying it'd be a good idea to have this new type of expression in the language, but I'd prefer it over 'assert_static_failure' as it is more general and could be of use in other contexts.

I agree - compiles{...}  would be more generally applicable.
I think it is even possible to implement it in C++11 to some extent via decltype + SFINAE.

Andrzej Krzemieński

unread,
Oct 7, 2013, 1:17:15 PM10/7/13
to std-pr...@isocpp.org

Yes, "to some extent" only. It will not cover (and nor will concepts, I think) the cases, where the specialization of the template exists, but has a static_assert inside, whose failure I would also want to detect.

Vicente J. Botet Escriba

unread,
Oct 7, 2013, 1:40:30 PM10/7/13
to std-pr...@isocpp.org
Le 07/10/13 11:06, Andrzej Krzemieński a écrit :
Hi All,
I wanted to share an idea about a new language feature that I have been missing for some time.

Motivation:

One of the features offered by a statically typed language (and in fact, probably the most important one) is a guarantee that certain declarations or expressions will fail to compile. For instance, imagine that we had SI units in the language:

  Length l = 1m;
  Duration t = 1s;
  t + l; // compile time error -- a nice feature

As a library (especially a generic library) author, I would like to be able to test if I correctly provide this safety feature guarantee. Namely, I would like to be able to verify if certain constructs in code fail to compile.

Proposal:

I imagine that this could be implemented by making a "branch" in the compilation process when the code requests one. The compilation of the "trunk" is suspended. Template instantiations and ODR usages performed from now on, are not visible in "trunk". The compilation in the "branch" continues until one of the two happens:

  1. We get a compilation error.
  2. We compiled successfully everything in the branch.

In the first case we stop compiling the "branch" and proceed to compile the "trunk" from where we suspended. Everything is fine.

In the second case, we trigger a compile-time error in the "trunk" with a user-provided error message.

Hi,

I think this will really useful. Currently we need to write a program and check that the compilation fails. This has some drawbacks:
* we need a program by compile error case.
* the program can compile fail due to other errors.
How an additional language to support this feature would look, is depicted in the following examples. I used an extra keyword assert_static_failure:
<snip>

  template <int N>
  void fun()
  {
    static_assert (N != 0, "N must be non-zero");
  }

  assert_static_failure ("test N == 0") {
    fun<0>();
  } // compiles fine

What about

  compile_fails "test N == 0" {
    fun<0>();
  } // compiles fine

I wanted to check in this list, if this is a feature worth standardizing, and if it is worth pursuing with this idea.

+1


Best,
Vicente

Evgeny Panasyuk

unread,
Oct 7, 2013, 1:45:56 PM10/7/13
to std-pr...@isocpp.org
7 oct 2013 г., 21:17:15 UTC+4 Andrzej Krzemieński:

Yes, "to some extent" only. It will not cover (and nor will concepts, I think) the cases, where the specialization of the template exists, but has a static_assert inside, whose failure I would also want to detect.

I have just checked N3701 page 45 - yes, looks like requires expression relies on same rules as SFINAE:
"If the instantiation of the constraint results in a substitution failure, the the requirement is not satisfied."

Daniel Krügler

unread,
Oct 7, 2013, 1:47:27 PM10/7/13
to std-pr...@isocpp.org
2013/10/7 Andrzej Krzemieński <akrz...@gmail.com>:
>
> Yes, "to some extent" only. It will not cover (and nor will concepts, I
> think) the cases, where the specialization of the template exists, but has a
> static_assert inside, whose failure I would also want to detect.

You won't get that. What you are asking for is often named
"speculative compilation/instantiation". During the C++11 development
such models where considered but rejected because of the costs to
implement that. The Core language really doesn't want this. This was
also the reason why specific issues where added in the library to
address such things:

http://cplusplus.github.io/LWG/lwg-defects.html#975
http://cplusplus.github.io/LWG/lwg-defects.html#1390

- Daniel

MJanes

unread,
Oct 8, 2013, 3:15:41 AM10/8/13
to std-pr...@isocpp.org
Il giorno lunedì 7 ottobre 2013 19:47:27 UTC+2, Daniel Krügler ha scritto:
You won't get that. What you are asking for is often named
"speculative compilation/instantiation".

even simply detecting static_assert failures ? I mean, as other said, currently SFINAE it's the only tecnique usable to test expected compilation failure ( apart specialized unit tests ) and the inability of detecting static assertion is a real problem IMHO.

Considering that you can often ( always? ) turn any static assert into a sfinae failure with equivalent behavior ( at least with respect to detecting the error one is interested in ), I wonder why detecting static asserts ( and only them, not any possible non-well-formedness ) would qualify as speculative compilation ...

David Krauss

unread,
Oct 8, 2013, 3:39:35 PM10/8/13
to std-pr...@isocpp.org
On 10/7/13 5:06 PM, Andrzej Krzemieński wrote:
> Proposal:
>
> I imagine that this could be implemented by making a "branch" in the
> compilation process when the code requests one. The compilation of the
> "trunk" is suspended. Template instantiations and ODR usages performed from
> now on, are not visible in "trunk". The compilation in the "branch"
> continues until one of the two happens:
>
> 1. We get a compilation error.
> 2. We compiled successfully everything in the branch.
>
> In the first case we stop compiling the "branch" and proceed to compile the
> "trunk" from where we suspended. Everything is fine.

Why not just use the build system to do this, and put the
supposed-to-fail case in a separate TU? The compiler should exit with
status 1 (or whatever).

There's no need to rebuild the speculation with every recompile,
instantiate the speculative templates, hack the ODR rule to throw them
away, and perhaps fundamentally change the compiler architecture to act
as if nothing happened. That's exactly what a build target accomplishes,
except it can be selective about its dependencies, and it will be
compiled in a parallel, separate process if it's needed at all.

MJanes

unread,
Oct 9, 2013, 3:30:41 AM10/9/13
to std-pr...@isocpp.org
Il giorno martedì 8 ottobre 2013 21:39:35 UTC+2, David Krauss ha scritto:
Why not just use the build system to do this, and put the
supposed-to-fail case in a separate TU?
 
because when you have to test many strictly related test cases, it's natural and easier to group them together in order to reuse auxillary code or simply present the cases in a logical "flow" ( yes, you can #include and/or use conditional compilation but complexity increases, especially if compared to the opposite situation of testing that no failure occured; it's like having a car that runs 200 mph to the left and 10 mph to the right, an awkward car, ain't it ? ). Can I live with this ? yes, I can, but I wonder what's the problem in at least detecting static assert failures; say, static_assert_failure(t,m) would be equivalent to static_assert(!t,m), the only difference being that any offending static assert occuring during the evaluation of t makes static_assert_failure succeed. Not the same thing Andrzej proposed, but still very useful IMO

Daniel Krügler

unread,
Oct 9, 2013, 3:47:23 AM10/9/13
to std-pr...@isocpp.org
2013/10/8 MJanes <max...@gmail.com>:
> Il giorno lunedì 7 ottobre 2013 19:47:27 UTC+2, Daniel Krügler ha scritto:
>
>> You won't get that. What you are asking for is often named
>> "speculative compilation/instantiation".
>
> even simply detecting static_assert failures ? I mean, as other said,
> currently SFINAE it's the only tecnique usable to test expected compilation
> failure ( apart specialized unit tests ) and the inability of detecting
> static assertion is a real problem IMHO.

Even with that, I suppose. Keep in mind that once you are enforcing
the compiler to instantiate more than needed (e.g. template
definitions compared to template declarations), this won't stop the
compiler from doing the complete job. If you have within a constructor
that direct-initializes X with Y a static_assert that validates
is_constructible<X, Y>, there is not only the failure of the
static_assert, but also the expected error or the
direct-initialization.

But that's not all: Typically the static_assert predicates are
themselves templates that need to be instantiated. What happens if
*within* this instantiation a static_assert fails? Does this have the
effect of returning false or is this a compile-error?

Current SFINAE is localized to the *immediate* context for good
reasons: It is intended to keep template compile overhead to the "top"
level.

> Considering that you can often ( always? ) turn any static assert into a
> sfinae failure with equivalent behavior ( at least with respect to detecting
> the error one is interested in ), I wonder why detecting static asserts (
> and only them, not any possible non-well-formedness ) would qualify as
> speculative compilation ...

Because they (a) can occur in any instantiation depth and (b) because
the compiler still has to consider the surrounding code,
static_asserts are not isolated in general, so from a compiler
perspective there is not much difference between applying your rule
"only" to static_asserts compared to general speculative compilation.

- Daniel

MJanes

unread,
Oct 9, 2013, 4:54:01 AM10/9/13
to std-pr...@isocpp.org
Il giorno mercoledì 9 ottobre 2013 09:47:23 UTC+2, Daniel Krügler ha scritto:
Even with that, I suppose. Keep in mind that once you are enforcing
the compiler to instantiate more than needed (e.g. template
definitions compared to template declarations), this won't stop the
compiler from doing the complete job.

I do understand that, and I expect a compiler error if there's one. The point is that it's the programmer responsability to decide which static asserts are "testable" or not, and make them to not give compiler errors, in a similar way it's the programmer responsability to write asserts/exceptions correctly and eventually make sure that they're properly and meaningfully catched in unit tests .

so, why a "static_assert_failure(t,m)" equivalent to static_assert(!t,m) but the fact that it succeeds if <any> static_assert is raised during the evaluation of t ( any other error will be the same as it already is now for the expression static_assert(!t,m) ) would qualify as speculative compilation ?

Daniel Krügler

unread,
Oct 9, 2013, 5:05:25 AM10/9/13
to std-pr...@isocpp.org
2013/10/9 MJanes <max...@gmail.com>:
> Il giorno mercoledì 9 ottobre 2013 09:47:23 UTC+2, Daniel Krügler ha
> scritto:
>>
>> Even with that, I suppose. Keep in mind that once you are enforcing
>> the compiler to instantiate more than needed (e.g. template
>> definitions compared to template declarations), this won't stop the
>> compiler from doing the complete job.
>
> I do understand that, and I expect a compiler error if there's one. The
> point is that it's the programmer responsability to decide which static
> asserts are "testable" or not, and make them to not give compiler errors, in
> a similar way it's the programmer responsability to write asserts/exceptions
> correctly and eventually make sure that they're properly and meaningfully
> catched in unit tests .

Given that discrimination of different static_assert forms I don't see
a good reason why the programmer would not simply take the
compile-time predicate of that special static_assert and use it as the
argument of enable_if in the template declaration.

> so, why a "static_assert_failure(t,m)" equivalent to static_assert(!t,m) but
> the fact that it succeeds if <any> static_assert is raised during the
> evaluation of t ( any other error will be the same as it already is now for
> the expression static_assert(!t,m) ) would qualify as speculative
> compilation ?

I don't understand this part.

- Daniel

MJanes

unread,
Oct 9, 2013, 6:01:34 AM10/9/13
to std-pr...@isocpp.org
Il giorno mercoledì 9 ottobre 2013 11:05:25 UTC+2, Daniel Krügler ha scritto:
Given that discrimination of different static_assert forms I don't see
a good reason why the programmer would not simply take the
compile-time predicate of that special static_assert and use it as the
argument of enable_if in the template declaration.

for example, consider a class template that signal a semantic error of the template arguments via possibly many static_asserts ( for example, "invalid" (partial) specilizations may have simply empty bodies with just a static assert saying something like "this or that is not allowed" ). Yes, one can always write a predicate to test this, but why do I have to duplicate code when I can just write a static_assert_failure() ?

I don't understand this part.

sorry, I meant to say: given a diagnostic message M, a constexpr predicate P, and let PP be the same predicate pretending no static_asserts declared,

is it technically possible to implement a "static_assert_failure(P,M)" such as

a) if P is well formed and holds true an error occurs with diagnostic message M
b) if P is well formed and holds false no error occurs
c) if PP is ill formed errors occur as in "static_assert(!PP,M);"
d) if P is ill formed and PP is well formed an error occurs with diagnostic message M
Reply all
Reply to author
Forward
0 new messages