Problem with new C++17 noexcept rules


Since r284549 'DR1330: instantiate exception-specifications when "needed"'
clang rejects the following:

$ cat d.cpp

struct S
    void f(const S& s = S());
S::~S() {}

$ clang++ -std=c++1z -fno-exceptions -fsyntax-only d.cpp

d.cpp:6:4: error: conflicting types for '~S'
S::~S() {}
d.cpp:3:5: note: previous declaration is here
1 error generated.

The error message is confusing (can it be improved?), but it seems
that clang considers the definition of destructor implicitly
unlike declaration, since adding explicit 'noexcept' to the definition
makes this compile again.
As expected, this example compiles without error for C++03/11/14,
since the commit in question is tweaking C++17-only rules.
Also, clang accepts it if -fno-exceptions is NOT specified, so this looks
like a bug to me.
This is the minimal test case I was able to produce - this error only
triggers for out-of-class destructor definitions when the class tries
to construct its own instances in member function default arguments.
This example was reduced from Qt, which breaks due to this pretty much
and uses a combination of -std=c++1z and -fno-exceptions in default
when supported.

I fixed this in r285779.

Thanks, that fixes the problem I reported, but there's more:

$ cat d2.cpp

#include <xmmintrin.h>

$ clang++ -std=c++1z -fno-exceptions -fsyntax-only d2.cpp

In file included from d.cpp:1:
In file included from /usr/bin/../lib/clang/4.0.0/include/xmmintrin.h:39:
/usr/bin/../lib/clang/4.0.0/include/mm_malloc.h:39:16: error: conflicting types for 'posix_memalign'
extern "C" int posix_memalign(void **__memptr, size_t __alignment, size_t __size);
/usr/include/stdlib.h:503:12: note: previous declaration is here
extern int posix_memalign (void **__memptr, size_t __alignment, size_t __size)
1 error generated.

The second file comes from glibc 2.23. The rest of the declaration
(not shown in the error message above) contains a macro that expands
to throw() when compiled with C++ compiler, whatever that means for
an extern "C" function (which it is due to being inside extern "C" {}
block, also not shown above). The first declaration has no explicit
exception specification. As with the previous case, the error disappears
if one omits -fno-exceptions.

Sorry for the delay, that should be fixed in r288220.

I've found a reverse case for this, one where glibc's throw() version tries to redeclare
the previous one given without it. It happens as a side effect of libcxx r288575 adding a
new include and manifests in building Intel TBB. The reduced test case is:

$ cat tbb-fail.cpp

#include <cstddef>
extern "C" void *aligned_alloc(size_t alignment, size_t size);
#include <cstdlib>

$ clang++ -fsyntax-only tbb-fail.cpp
In file included from tbb-fail.cpp:3:
In file included from /usr/bin/../include/c++/v1/cstdlib:86:
In file included from /usr/bin/../include/c++/v1/stdlib.h:94:
/usr/include/stdlib.h:509:14: error: exception specification in declaration does not match previous declaration
extern void *aligned_alloc (size_t __alignment, size_t __size)
tbb.cpp:3:18: note: previous declaration is here
extern "C" void *aligned_alloc(size_t alignment, size_t size);
1 error generated.

This time it happens regardless of language standard used.

The original code in TBB (src/tbbmalloc/proxy.cpp) declares aligned_alloc itself,
AFAIU, for the sake of adding __attribute__ ((alias("memalign"))), and manages
to do it before including any system-provided declaration.

This one seems like a bug in tbb. cstdlib is allowed to declare this function with a non-throwing exception specification, and if it does, that conflicts with tbb’s declaration.

We could probably make this work too, but generally I don’t think it’s reasonable for an implementation of part of the C standard library to include C standard library headers from a different standard library of unknown provenance… so in the first instance we should ask Intel to change tbb.

Got it, I've sent them a patch.