static_assert with dependent expression that is actually independent - c++

Consider the following template:
template <typename T>
void foo() {
static_assert(sizeof(T) == 0, "Now what?");
}
The standard (§7.4) says:
[If the condition to static_assert is false] the program is ill-formed, and the resulting diagnostic message (1.4) shall include
the text of the string-literal, […]
(Reference taken from https://stackoverflow.com/a/11048906/560450)
In practice, the static_assert will not fail until we instantiate the function template foo, because the use othe the template parameter forces the compiler to evalute the condition only during two-phase lookup. Therefore, the code snippet above is not deemed ill-formed unless the function template is instantiated.
On the other hand, sizeof(T) > 0 in C++ by definition. Therefore, the value of the condition is in fact independent of any template parameter! Is a "malicious" compiler allowed to take advantage of this fact, and reject the program as ill-formed, regardless whether foo is actually instantiated or not?

Yes, the compiler is allowed, but not required, to reject this.
§14.6 [temp.res]/p8:
If no valid specialization can be generated for a template, and that
template is not instantiated, the template is ill-formed, no
diagnostic required.
An easy workaround:
template <typename T>
struct foo_protector : std::false_type {};
template <typename T>
void foo() {
static_assert(foo_protector<T>::value, "Now what?");
}
The compiler is not allowed to reject foo()'s definition because there might be a specialization of foo_protector, not yet seen, such that its value member is true. As long as you don't actually write such a specialization, the static_assert will fire as expected.

Related

Is this use of static_assert inside if constexpr well-formed?

I read a couple of answers yesterday, on the use of static_assert(false, "Some message") inside the else clause of an if constexpr. I understand that it's considered to be ill-formed, according to the standard (even if some compilers, including MSVC2017, will accept it). Qt will also mark this as an error.
My question is, is the code below well-formed according to the standard? (I'm inclined to think so, but I'd like a confirmation.)
template <typename TypeOfValue>
static void PushValue(duk_context* ctx, TypeOfValue value) {
// Push value onto duktape stack
if constexpr (std::is_same<TypeOfValue, int>::value) {
// Push int
duk_push_int(ctx, value);
} else if constexpr (std::is_same<TypeOfValue, uint32_t>::value) {
// Push uint
duk_push_uint(ctx, value);
} else {
// Unsupported type
static_assert(bool_value<false, TypeOfValue>(), "Unsupported type");
}
}
template <bool value, typename T>
static constexpr bool bool_value() {return value;}
Edit:
It seems, from the comment I got, that bool_value should instead be defined like this:
template<bool value, typename T>
struct bool_value {
static constexpr bool value = value;
};
with the usage pattern
// Unsupported type
static_assert(bool_value<false, TypeOfValue>::value, "Unsupported type");
It's then well-formed, only because it's possible for bool_value to be specialized into a version which returns true for the expression bool_value<false, TypeOfValue>::value.
Both of your attempts (with the function and with the struct) are well-formed as is.
The other answer mentions [temp.res]/8, but I disagree with how it was interpreted.
The validity of a template may be checked prior to any instantiation. ... The program is ill-formed, no diagnostic required, if:
— no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or ...
Both the function and struct you wrote can be specialized to be true. I believe the mere possibility of specialization is enough, you don't actually need to add a dummy true specialization to make the code well-formed.
According to my understanding (and according to common sense, I hope), the point of this part of the standard is to allow compilers to check validity of templates and if constexpr branches early (when they are seen for the first time), and reject the ones that can't possibly be instantiated.
Every branch in your template can potentially be instantiated, because bool_value() can be specialized later. I doubt a sane compiler is going to reject your code due to bool_value() not being specialized yet.
The accepted answer of the question you linked explains why it's ill-formed. Note the quote from the Standard temp.res-8, which says (emphasis mine)
The validity of a template may be checked prior to any instantiation. [...] The program is ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or...
[...]
Otherwise, no diagnostic shall be issued for a template for which a valid specialization can be generated.
[ Note: If a template is instantiated, errors will be diagnosed according to the other rules in this document.
Exactly when these errors are diagnosed is a quality of implementation issue.
— end note
]
In the cppreference page about if you can see the following workaround (emphasis mine)
The common workaround for such a catch-all statement is a type-dependent expression that is always false:
template<class T> struct dependent_false : std::false_type {};
template <typename T>
void f() {
if constexpr (std::is_arithmetic_v<T>)
// ...
else
static_assert(dependent_false<T>::value, "Must be arithmetic"); // ok
}
As noted in the comments by Evg
theoretically, this is still ill-formed, because [...] is always false unless you actually have a specialization that returns true.
So you could add that specialization to the previous snippet
namespace {
// Do not unleash.
struct aggressive_unicorn_type;
}
template<> struct dependent_false<aggressive_unicorn_type> : std::true_type {};
Whether this is necessary to satisfy the Standard document to the letter or not it depends on the interpretation of "can be generated".
I wonder if it really matters too. The OP is asking if the code is well-formed, but they are actually trying to produce a code which is ill-formed, with a diagnostic (that's what static_assert(false) implies, see dcl.pre) under certain conditions.
As the asker already noted, there are other methods to force a compiler error when arguments of the "wrong" type are passed (consider concepts, of the upcoming standard) and, maybe, if we want to use static_asserts in conjunction with if constexpr the best option may be a type-dependent expression that is not always false.
#include <type_traits>
void f(int) {};
void g(unsigned) {};
template< class T>
constexpr inline bool is_supported =
std::is_same_v<T, int> ||
std::is_same_v<T, unsigned> ||
std::is_same_v<T, char>;
template <class T>
void use(T value) {
static_assert(is_supported<T>, "Not supported");
if constexpr (std::is_same<T, int>::value) {
f(value);
} else if constexpr (std::is_same_v<T, unsigned>){
g(value);
} else {
static_assert( !is_supported<T>, "Not yet implemented");
}
}
I'll just add my own final thoughts as an answer, since I seem to not be getting a quite principled enough answer from other sources.
My conclusion after reading a couple of other answers is this:
The compiler is allowed, according to the standard, to evaluate static_assert(false, "...") early. If it does, it produces a program that can never compile.
A program that can never compile is ill-formed.
So, the problem is that the compiler is allowed to evaluate static_assert early, even within an if constexpr.
Forcing it to delay the evaluation until instantiation time, by introducing a false that artificially depends upon a template parameter, seems a bit contrived to me. But I accept that there are other considerations which probably makes it the correct language choice anyway. (That's the sort of thing I was hoping for an expert answer to shed some light upon.)

Is it possible to implement always_false in the C++ standard library?

There are cases where one uses an always_false helper to e.g. cause unconditional static_assert failure if instantiation of some template is attempted:
template <class... T> struct always_false : std::false_type {};
template<class T>
struct UsingThisShouldBeAnError {
static_assert(always_false<T>::value, "You should not use this!");
};
This helper is necessary because a template definition must (at least theoretically) have at least one set of template parameters for which a valid specialization can be produced in order for the program to be well-formed:
[temp.res]/8: The program is ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template [...] and the template is not instantiated, or
[...]
(Writing static_assert(false, "You should not use this!"); above would thus be ill-formed and a compiler could always fire the static assert, even without the template being instantiated, which is not the intention.)
Here is a quick sampling of questions involving this pattern (including further explanation):
Forbids functions with `static_assert`
Are static_asserts to be evaluated if a member template isn't instantiated?
Conditional compilation of templates
It might be useful to have always_false as a tool in the standard library so we don't have to constantly write it again. However, the answer to the following question makes me wonder whether this is even possible:
Dependent non-type parameter packs: what does the standard say?
There the argument is made (also with respect to [temp.res]/8) that std::enable_if_t<T> is always either void or not a type and that it is illegal for anyone to specialize it further. Therefore, a template that relies on the theoretical "specializability" of std::enable_if to avoid the [temp.res]/8 clause actually causes the program to be ill-formed, no diagnostic required.
Coming back to my question: If the standard provided always_false, it would have to forbid library users from specializing it as usual (for obvious reasons). But by the above reasoning, that would defeat the whole point of always_false (namely that it could theoretically be specialized to something other than std::false_type) - with respect to [temp.res]/8 it would be the same as using std::false_type directly.
Am I wrong in this reasoning? Or is it actually impossible for the standard library to provide always_false in a meaningful/useful way (without core language changes)?
In C++20, with lambda, you might do something like:
template <class... T> struct always_false : std::false_type {};
// To have true, but for a type that user code can't reuse as lambda types are unique.
template <> struct always_false<decltype([](){})> : std::true_type{};
After reflection, I think it is not possible: enclosing template might have other restrictions that the hypothetical type(s) for the specialization should fulfill:
With static_assert(is_enum_v<T> && always_false_v<T>), that type should be an enum.
And even more constrained, with static_assert(is_same_v<T, int> && always_false_v<T>), it is for int.
To paraphrase Jarod's idea, It could be something like
template <class... T> struct always_false : std::false_type {};
template <> struct always_false</* implementation defined */> : std::true_type{};
Where /* implementation defined */ can be filled by std::_ReservedIdentifer. User code can't access it, since the identifier is reserved to the library, but there exists a specialization that is true. That should avoid questions about the ODR and lambdas in specializations.
All such attempts result in a program that is ill-formed, no diagnostic required.
The clauses that block you from using static_assert(false) make your program ill-formed, no diagnostic required based on the actual possibility of making an actual instantiation, not based on if the compiler can work it out.
These tricks simply make it harder for the compiler to detect the fact your program is ill-formed. The diagnostic they issue is not required, and your ability to bypass the diagnostic being issued just means you made the compiler produce an ill-formed program for which the standard places no restrictions on its behavior.
(Writing static_assert(false, "You should not use this!"); above would thus be ill-formed and a compiler could always fire the static assert, even without the template being instantiated, which is not the intention.)
The exact same conclusion holds for your
template <class... T> struct always_false : std::false_type {};
template<class T>
struct UsingThisShouldBeAnError {
static_assert(always_false<T>::value, "You should not use this!");
};
I claim there is no valid instantiation of UsingThisShouldBeAnError in the above program.
http://eel.is/c++draft/temp.res#6.1
The program is ill-formed, no diagnostic required, if:
(6.1) no valid specialization can be generated for a template [...]"
No valid specialization can be generated for this template.
To avoid this trap, your program must have
template <> struct always_false<SomeListOfTypes> : std::true_type {};
and if an always_false is specified in the standard for which this cannot occur, using always_false from the standard doesn't help you at all.
because the standard requires that the specialization "can be generated".
If the only way to instantiate your template is within an ill-formed program, then that is a huge stretch on the word "can". So using reserved or magic types you aren't allowed to use within the true_type specialization is not really plausible.
Stepping back, the purpose of the "ill-formed, ndr" there is because the standard writers want to permit diagnostics of broken code, but don't want to mandate it. Detecting such broken code in general is a halt-hard problem, but simple cases can be detected. And optimizing around the assumption that the code isn't broken can be useful.
All attempts to inject static_assert(false, "message") are working around the intention that C++ code is intended to be valid.
We have a construct for a function for whom successful lookup is an error already in the language.
template<class T>
void should_never_be_found(tag<T>) = delete;
the hole, of course, is the lack of a diagnostic message here.
template<class T>
void should_never_be_found(tag<T>) = delete("Provide an custom ADL overload!");
also, the inability to =delete template specializations.
What you appear to want is:
template<class T>
struct UsingThisShouldBeAnError = delete("You should not use this");
which is direct, intentional, and consistent with how other =delete cases work.
The static_assert bypass requires magic and "tricking" the compiler to bypass parts of the standard explicitly written to prevent you from doing what you want to do.
This =delete("string") and =delete syntax on template classes is missing from the language.
just use option -fdelayed-template-parsing

Referring to specific template specialization from a non-instantiated context: instantiation or not?

Consider the following example
template <typename A> struct S
{
A a;
void foo() {}
};
template <typename T> void bar()
{
S<void> *p = 0;
}
template <typename T> void baz()
{
S<void>{}.foo();
}
template <typename T> void qux()
{
S<void> s{};
}
int main()
{
}
Function templates bar, baz and qux are deliberately left non-instantiated.
The definition of baz fails to compile in GCC and Clang for "obvious" reason - S<void> is an invalid specialization of S. However, which language rule is working in this case?
On the one hand, S<void> does not depend on template parameters of baz, member access requires it to be complete, which triggers instantiation of S<void>, which fails. Diagnostic is required.
On the other hand we have the blanket rule of "if no valid specialization can be generated for a non-instantiated template, the code is ill-formed". This makes the definition of baz ill-formed. However, no diagnostic is required.
More specifically, am I correct in my assumption (as expressed in #1) that the above reference to S<void> from non-instantiated baz requires instantiation of S<void>? The assumption is supported by the fact that both compilers happily accept the definition of bar, which does not instantiate S<void>.
However, the aforementioned compilers differ in their treatment of qux - Clang complains, while GCC accepts it without any complaints. Is this a bug in one of the compilers? Is diagnostic required in this case? Or am I wrong in my assumption that #1 is at work here? If #2 is the basis for the diagnostic, then the difference between the compilers is acceptable.
For both baz and qux, the validity of the expression including S<void> can only be done through instantiation of S. Nevertheless, compilers are not mandated to perform this validation before any instantiation [temp.res]/8
The validity of a template may be checked prior to any instantiation.
[...
 ] The program is ill-formed, no diagnostic required, if:
a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter,
Both S<void>{} and S<void> s{} are used in a context that requires the instantiation of S<void>, such an instantiation is ill-formed due to the member having incomplete type void.
The relevant quotes are [temp.inst]/1:
Unless a class template specialization has been explicitly instantiated or explicitly specialized, the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. [...]
and [temp.arg]/6:
If the use of a template-argument gives rise to an ill-formed construct in the instantiation of a template specialization, the program is ill-formed.
On the other hand both baz and quz are ill-formed NDR
[temp.res]/8:
Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if:
(8.1) no valid specialization can be generated for a template, [...]

Specialised template being compiled without instantiation [duplicate]

In C++11, should the operation of static_assert within a template depend on whether that template has been instantiated or not? For example, with the following code
template <int I>
void sa() { static_assert(0,"Hello."); }
int main(int argc, char *argv[]) { return 0; }
GCC 4.5.0 will fail the assertion, and produce the "Hello." message.
The Digital Mars Compiler version 8.42n on the other hand, gives no message.
GCC is correct and the other compiler is correct too. Refer to 14.6p8 in the spec
If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required.
Therefor, a compiler is free to reject the following
template<typename T>
void f() {
static_assert(0, "may trigger immediately!");
static_assert(sizeof(T) == 0, "may trigger immediately!");
}
If you want to go safe, you have to arrange it so the compiler cannot know until instantiation whether the boolean expression will be true or false. For example, get the value by getvalue<T>::value, with getvalue being a class template (one could specialize it, so the compiler cannot possibly know the boolean value already).
I believe that the compiler is well within it's rights to expand any static assertions that are not dependent on template parameters without needing an instantiation- but I don't believe this is required. Remember also that different draft Standards may have different rules about when this may occur.
The C++0x draft (N3242) says in 14.6p8:
"If no valid specialization can be
generated for a template definition,
and that template is not instantiated,
the template definition is ill-formed,
no diagnostic required."
The same words appear in the C++03 standard.
In the case of the example from the question, no valid instantiation can be made for this template, so the quoted wording applies.
Since no diagnostic is required, the compiler may compile the program if the template is not instantiated. Of course, if it is instantiated then the program is ill-formed, with a diagnostic required.
I used a helper function to make the false dependent on the template parameter:
template<typename T>
bool dependentFalse<T>()
{
return false;
}
template<typename T>
Foo foo()
{
static_assert(dependentFalse<T>(), "this template shouldn't be instantiated");
}
This is 14.6.2 section in C++ standard.
Your static_assert is related to support binding nondependent names when initially parsing a template. The Digital Mars Compiler does not currently support binding nondependent names. GCC 4.5.0 does support binding nondependent names.
If your expression does not depend on the template parameters then such expression is known when initially parsing a template. Compiler must show the error message. GCC 4.5.0 does it.
Replace your 0 in static_assert with I*I < 0, for expample and you get dependent name. Error message will appear for the case of uninstantiated template only.

C++11 static_assert and template instantiation

In C++11, should the operation of static_assert within a template depend on whether that template has been instantiated or not? For example, with the following code
template <int I>
void sa() { static_assert(0,"Hello."); }
int main(int argc, char *argv[]) { return 0; }
GCC 4.5.0 will fail the assertion, and produce the "Hello." message.
The Digital Mars Compiler version 8.42n on the other hand, gives no message.
GCC is correct and the other compiler is correct too. Refer to 14.6p8 in the spec
If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required.
Therefor, a compiler is free to reject the following
template<typename T>
void f() {
static_assert(0, "may trigger immediately!");
static_assert(sizeof(T) == 0, "may trigger immediately!");
}
If you want to go safe, you have to arrange it so the compiler cannot know until instantiation whether the boolean expression will be true or false. For example, get the value by getvalue<T>::value, with getvalue being a class template (one could specialize it, so the compiler cannot possibly know the boolean value already).
I believe that the compiler is well within it's rights to expand any static assertions that are not dependent on template parameters without needing an instantiation- but I don't believe this is required. Remember also that different draft Standards may have different rules about when this may occur.
The C++0x draft (N3242) says in 14.6p8:
"If no valid specialization can be
generated for a template definition,
and that template is not instantiated,
the template definition is ill-formed,
no diagnostic required."
The same words appear in the C++03 standard.
In the case of the example from the question, no valid instantiation can be made for this template, so the quoted wording applies.
Since no diagnostic is required, the compiler may compile the program if the template is not instantiated. Of course, if it is instantiated then the program is ill-formed, with a diagnostic required.
I used a helper function to make the false dependent on the template parameter:
template<typename T>
bool dependentFalse<T>()
{
return false;
}
template<typename T>
Foo foo()
{
static_assert(dependentFalse<T>(), "this template shouldn't be instantiated");
}
This is 14.6.2 section in C++ standard.
Your static_assert is related to support binding nondependent names when initially parsing a template. The Digital Mars Compiler does not currently support binding nondependent names. GCC 4.5.0 does support binding nondependent names.
If your expression does not depend on the template parameters then such expression is known when initially parsing a template. Compiler must show the error message. GCC 4.5.0 does it.
Replace your 0 in static_assert with I*I < 0, for expample and you get dependent name. Error message will appear for the case of uninstantiated template only.