Can't Pass `std::array` From Within Closure - c++

Using the following declarations:
#include <array>
template <typename V> void makeError(V& v) {}
the following code snippet fails to compile under MSVC 16.11.13 with /std:c++17:
int main() {
[&]() {
std::array bugs{0};
[&]() { makeError(bugs); };
};
return 0;
}
The error message is as follows: main.cpp(9,5): error C2955: 'std::array': use of class template requires template argument list
The same code compiles perfectly fine under GCC 10.4 with --std=c++17. The real problem came up when I tried removing the inner closure:
int main() {
[&]() {
std::array bugs{0};
makeError(bugs);
};
return 0;
}
Or the outer closure:
int main() {
std::array bugs{0};
[&]() { makeError(bugs); };
return 0;
}
Both of these compile happily under MSVC and GCC.
Is the first snippet invalid C++? If so, what part of the standard am I violating?

Looks like this is a bug in msvc's legacy lambda processor. Your code compiles if you pass /Zc:lambda: https://godbolt.org/z/91z4chhTx
This seems to be the matching bug report: https://developercommunity.visualstudio.com/t/class-template-argument-deduction-fails-in-the-bod-1/414204
How I found this
I would always recommend using godbolt in cases like this, since it allows you to quickly switch between compilers, versions and command line args.
If something smells like a compiler bug, checking the latest versions is always a good idea. If something compiles under gcc & clang, but not msvc, it smells like a compiler bug.
I tried your code in the latest msvc and since it involves lambdas and captures, I remembered that there were changes to that in c++20, so I tried compiling as such. Since that worked, I had look through cppreference to see if I could find any relevant change to the standard that might legitimately cause this. I didn't find any, but then remembered that msvc has also constantly be increasing its standards conformance over the recent years.
So I used /permissive- with c++17 and once I noticed that this builds, I had a look through the conformance flags and found /Zc:lambda.
This confirmed this as a fixed compiler bug to me. And fixed bugs often have bug reports, so I googled the keywords that seemed relevant to the situation:
msvc nested lambdas template deduction. The bug report is the second result for me.

Related

Compile-time error in uninstanciated function template

My understanding of function templates has always been: if they contain invalid
C++ and you don't instanciate them, your project will compile fine.
However, the following code:
#include <cstdio>
#include <utility>
template <typename T>
void contains_compile_time_error(T&& t) {
int j = nullptr;
}
int main() {}
Compiles with:
x86-64 gcc 11.2 and flag -std=c++20;
x64 msvc v19.31 and flag /std:c++20;
and does not with x86-64 clang 14.0.0 (with flags -std=c++{11,14,17,20}):
error: cannot initialize a variable of type 'int' with an rvalue of type 'std::nullptr_t'
int j = nullptr;
^ ~~~~~~~
1 error generated.
Even more confusing to me, the following code:
#include <cstdio>
#include <utility>
namespace ns {
struct S {};
template <typename T>
void apply(T&& t) {
//print(std::forward<T>(t));
ns::print(std::forward<T>(t));
}
void print(S const&) { std::puts("S"); }
} // namespace ns
int main() {}
Does not compile with:
x86-64 gcc 11.2 and flag -std=c++20;
x64 msvc v19.31 and flag /std:c++20;
x86-64 clang 14.0.0 and flags -std=c++{11,14,17,20};
(all of them complaining that print is not a member of 'ns') but does compile with x64 msvc v19.31 /std:c++17.
If I call unqualified print, then the code compiles with all the above compilers.
So, my questions are:
is my understanding of function templates wrong?
why do the above compilers behave differently with the code snippets I posted?
Edit 0: as per Frank's comment, x64 msvc v19.31 /std:c++17 /permissive- fails to compile function template apply where I call the qualified ns::print.
My understanding of function templates has always been: if they contain invalid C++ and you don't instanciate them, your project will compile fine.
Nope, if the code in the template is invalid, then so is the program.
But what does "invalid C++" mean? You can have syntactically valid C++ that is still invalid semantically, and the semantics of C++ are highly contextual.
So there's multiple levels of "valid" that can be checked at different times during compilation, based on the available information. Critically, there are things that can theoretically be checked early, but are potentially unreasonably difficult. Because of this, compilers are allowed some leeway when it comes to validating the semantics of template definitions. However, whether the code is broken or is not ambiguous, it's a compiler's ability to detect broken code that is.
why do the above compilers behave differently with the code snippets I posted?
As far as int j = nullptr is concerned:
The program is technically ill-formed, but the diagnostics is optional. So The code is broken, but GCC is not breaking compliance by letting it through.
For example, if S only had private constructors, then print(std::forward<T>(t)) would be flaggable as being broken since there is no possibly T that would make it valid. However, requiring compilers to be smart enough to determine this in all cases would be kind of mean.
For print():
Lookups are a bit of a different matter, there are hard rules that are supposed to be followed.
There are three types of lookups involved here.
Qualified lookups, such as ns::print
Unqualified lookups not involving template parameters, which you'd get if you tried print(S{});
Unqualified lookups involving template parameters, such as print(std::forward<T>(t));
Lookups are deferred for the third category, but must still be performed the same as non-template functions for the other two.
Note that the code still needs to be syntactically valid, even in the case of deferred lookups, hence why typename needs to be added when doing a dependent lookup of a type.
And as for MSVC allowing ns::print: That compiler is not compliant by default on this (and other) aspect of the standard. You need to use the /permissive- compiler flag to enforce compliance.

Specialised template *not* asserting despite compile time constant static_assert

For a few days I've been using and testing my application without any trouble using the following code:
class dataHandler
{
public:
template<class T>
T GetData(bool truncate = false) { static_assert(false, "Unhandled output type"); }
template<T>
int GetData<int>(bool truncate)
{
// Normal stuff that compiles
}
}
This (As I expected at the time) works fine as long as no implicit instantiation of GetData's default is performed. However, today after adding a new failing specialisation for void* specifically (I wanted a different error) I discovered it wouldn't compile due to the assertion, even though the `void* spec was never called or mentioned in code.
I started a new C++ test project (with the same C++ version, MSVC version, and VS2022 version) and found this also doesn't compile for the same reason:
template<class T>
T Spec()
{
static_assert(false, "default");
}
template<>
int Spec<int>()
{
return 1;
}
I was unable to replicate the original 'success' in anything I tried within the test project, and the failing GetData<void*> assert in the initial project seems to indicate that it's not a project config / difference in toolset issue.
After some searching I discovered that it failing was the intended (or otherwise expected) behaviour, as well as a workaround for it.
However I find myself wondering why the initial case of static_assert(false) didn't also fail to compile. Is this some niche exception, or is this an issue with MSVC's consistency?
However I find myself wondering why the initial case of static_assert(false) didn't also fail to compile. Is this some niche exception, or is this an issue with MSVC's consistency?
It's certainly not an inconsistency. The key part here is "Standard conformance mode", controllable with the compiler option /permissive-. See the documentation of this compiler option.
The reason that MSVC from before VS2017 used to accept your static_assert(false, ...), is because it postponed parsing the contents of a function template until template instantiation. That is just how the parser used to work, and also why features like two-phase name lookup could never be properly implemented. I'll refer you to this blog post for more background on this.
You can easily try it for yourself. Even if the function contains ill-formed code that shouldn't even parse correctly, the compiler doesn't complain as long as the template isn't being instantiated:
template<class T>
void foo()
{
Normally this should not (never actually (compile)) but it does anyway
}
It appears to do some basic paranthesis matching and that is it. The token stream is recorded and parsed when foo is instantiated (usually when called).
In order to fix two-phase lookup (and other conformance issues), they had to fix the compiler and parse the code whenever a function template is encountered, rather than being instantiated. But now they have a problem, because a lot of old code might be reliant on the old compiler behavior that suddenly doesn't compile anymore. So they introduced the /permissive- option for users to opt-in into standard conformance mode. And gradually the default was changed for new projects or certain compiler options, as can be read in the documentation:
The /permissive- option is implicitly set by the /std:c++latest option starting in Visual Studio 2019 version 16.8, and in version 16.11 by the /std:c++20 option. /permissive- is required for C++20 Modules support. Perhaps your code doesn't need modules support but requires other features enabled under /std:c++20 or /std:c++latest. You can explicitly enable Microsoft extension support by using the /permissive option without the trailing dash. The /permissive option must come after any option that sets /permissive- implicitly.
By default, the /permissive- option is set in new projects created by Visual Studio 2017 version 15.5 and later versions. It's not set by default in earlier versions. When the option is set, the compiler generates diagnostic errors or warnings when non-standard language constructs are detected in your code. These constructs include some common bugs in pre-C++11 code.
Which brings us to the answer to your question. You weren't seeing it in your original code because you were compiling without /permissive-. In your test project, created in VS2022, /permissive- mode was set by default so it failed to compile. It also fails to compile in an explicit specialization (your void* case) because at that point the template arguments are known and the function is instantiated.
There are a couple of ways to properly fix your code. One is by explicitely deleting the main template and any specialiation you don't want to have.
template<class T> void foo() = delete;
template<> void foo<void*>() = delete;
template<> void foo<int>()
{
// ...
}
This will make any use of the explicitely deleted variants ill-formed, without having the option to include an error message. Of course the void* case is a bit redundant in this example.
If you want to stick to static_assert, you must make the condition for the assert dependent on the template arguments. If you are compiling with C++17, you could do:
template<class...> constexpr bool always_false = false;
template<class T> void foo()
{
static_assert(always_false<T>, "your error here");
}
If using C++14 or earlier, you could wrap the always_false into a struct template:
#include <type_traits>
template<class...> struct always_false : false_type { };
template<class T> void foo()
{
static_assert(always_false<T>::value, "your error here");
}

C++17 <functional> template parameter deductions not working on Xcode 10.1

I've been playing around with Template Argument Deduction with C++17 (and onwards), and was trying to compile this exact sample from cppreference.com
#include <functional>
int func(double) { return 0; }
int main() {
std::function f{func}; // guide #1 deduces function<int(double)>
int i = 5;
std::function g = [&](double) { return i; }; // guide #2 deduces function<int(double)>
}
It compiles and runs fine in their web based compiler they have on that page, but when I try and compile this on my Macbook Pro, it fails, saying
error: no viable constructor or deduction guide for deduction of template arguments of 'function'
I've tried it both in an Xcode (v10.1) project set to C++17, and just running clang directly with -std=c++17.
I've also compiled the above example on different online compilers, such as here: https://godbolt.org/z/ERliha
I've also verified that type deductions for std::pair work, so I can't tell if:
I screwed up my toolchains somehow when I was messing with cross compilers.
Deduction Guides for this class are missing in Apple's Toolchain.
I'm running an old toolchain (I don't know how to check this)
... (Any other reason)
It looks like libc++ does not fully support all the deduction guides yet, if we look at the libc++ status page it say the proposal that brought this specific deduction guide is in progress:
P0433R2| LWG Toward a resolution of US7 and US14: Integrating template deduction for class templates into the standard library |Kona| In progress
We see from this godbolt session clang fails when using -stdlib=libc++ but not when using -stdlib=libstdc++.
The commits to libc++ for deduction guides don't indicate any commits for std::function.
We now have a bug report for this Bug 39606: std::function does not have deduction guides.

How does clang detect noexcept-ness?

I'm currently reimplementing std::invoke in C++11 (i.e. understanding and adapting libc++/libstdc++ code), and I stumbled upon an issue related to noexcept.
This can be demonstrated with the following snippet:
#include <functional>
void nothrow_method(int, int) noexcept
{
}
int main(int argc, char const *argv[])
{
static_assert(noexcept(std::__invoke(nothrow_method, 2, 3)), "");
static_assert(std::__is_nothrow_invocable<decltype(nothrow_method), int, int>::value, "");
}
I'm on Debian Jessie, my library is libstdc++.
Compiling with -std=c++14 fails with clang 4.0.1, both static_assert trigger.
There is no problem with GCC 7.1.0 however.
I looked at how libc++ implemented std::invoke, and I copied their way of detecting noexcept in my own implementation, but it still failed to compile.
Since there is only one error line because of the static_assert, I really have no idea what is going on, could it be related to what's explained in this blog post?
I've had some issues with noexcept and template instantiation points in the past, but I'm quite sure it's not related here.
EDIT:
I've downloaded libcxx trunk, built with apple-clang 8.1.0 on macOS 10.12.6, the static_assert still trigger, despite their code having noexcept(noexcept()) on __invoke.
EDIT2:
std::__* are used for tests, I know they are private, but I didn't want to post my invoke implementation.
This seems to be a bug with the Clang standard library, probably in the definitions of std::invoke<> and std::__is_nothrow_invocable<>... GCC correctly doesn't assert on both statements, and VS2017 does exhibit the bug.
As an aside, you should avoid the std::__*<> templates — they are not part of the standard and are meant to be private to the standard library.
Here's a workaround: you can use the following syntax, which is more correct, since it tests the actual statement you will use in the app.
#include <functional>
void nothrow_method(int, int) noexcept { }
int main()
{
static_assert(noexcept(nothrow_method(2, 3)), "");
return 0;
}

Does Clang/C2 use the same semantic analysis & AST as Clang/LLVM?

I think Clang/C2 uses the Clang frontend which contains semantic analysis & AST, and just replaced the LLVM codegen with C2.
But strangely there's some inconsistency between Clang/C2 & Clang/LLVM.
For example, given the following program:
#include <iostream>
void f(int)
{
std::cout << "int!\n";
}
template<class T>
void fun(T i)
{
f(i);
}
void f(float)
{
std::cout << "float!\n";
}
int main()
{
fun(5.f);
return 0;
}
The standard behavior is to select the int overload. G++ & Clang/LLVM give me the correct result.
OTOH, MSVC is known to lack of 2-phrase lookup, so it selects the float overload. What strange is, Clang/C2, which should use Clang frontend and thus have 2-phrase lookup, also give me the same result as MSVC.
Is the normal semantic analysis & AST not used in Clang/C2, or does it mimic the MSVC on purpose here?
melak47 is correct. We've been working on getting the defaults fixed up for Clang/C2. Our intention is to turn off all of the MSVC compatibility options, but we're doing that incrementally. The Clang/C2 compiler uses the same Clang front end as Clang/LLVM and should (eventually) behave as Clang/LLVM behaves.
So to answer your question directly, yes Clang/C2 uses the same semantic analysis and AST as Clang/LLVM. We didn't modify Clang except to have it hook up to C2. (Small lie: we also have it generate debug information, but that's being pushed back to the Clang trunk.)
As for the other comment, Microsoft has no need to create a Clang that "bends to match what the MS compiler does". The Clang-cl project does that. See, for example, the last slide here: http://llvm.org/devmtg/2014-04/PDFs/Talks/clang-cl.pdf