[cfe-dev] Interesting clang behavior/bug? on Windows

Edward Diener eldlistmailingz at tropicsoft.com
Fri Feb 13 23:26:16 PST 2015


On 2/14/2015 1:22 AM, Nico Weber wrote:
> On Fri, Feb 13, 2015 at 8:43 PM, Edward Diener
> <eldlistmailingz at tropicsoft.com
> <mailto:eldlistmailingz at tropicsoft.com>> wrote:
>
>     On 2/13/2015 11:17 PM, Nico Weber wrote:
>
>         Hans would know for sure, but I think this is intentional: Since
>         AClass
>         is dllexported, all its implicit functions get generated (so
>         that they
>         can be exported from the dll), which means they need to be
>         semantically
>         checked. If you give the class a deleted copy constructor and
>         assignment
>         operator, it might work.
>
>
>     Where in the C++ standard is such behavior justified ?
>
>
> The C++ standard doesn't talk about dllexport, so it won't say either way.
>
> This isn't a question of standards, it's a question of practicality. The
> way dllexport / dllimport are used is usually like so (think of a class
> that has a valid copy constructor for a bit):
>
> #ifdef IMPL
> #define EXPORT __attribute__((dllexport))
> #else
> #define EXPORT __attribute((dllimport))
> #endif
>
> class EXPORT MyClass { ... };
>
> Then, while building your dll, you define IMPL, and while building
> users, you don't.

I know this of course.

> When the dll is being built, the compiler doesn't know
> if clients are going to use MyClass's implicit copy constructor, so its
> choices are:
>
> 1.) Generate it when it's dllexported and put it into the dll. Then, if
> clients use it, they'll get it from the dll.
> 2.) Don't generate it when it's dllexported.
> 2a) Don't even generate the implementation when clients use it. Now the
> copy constructor isn't defined anywhere and you'll get linker errors.
> 2b) Emit a weak definition into every TU that uses it. I think this
> would work, but you end up with several copy constructors – if you
> wanted this, you probably wouldn't have EXPORTed the class.
>
> Because of this reasoning (I would guess), clang chooses 1 and defines
> all implicit methods for dllexported classes. Now, in your case the
> definition of some of these implicit methods causes an error. I suppose
> clang's logic could instead be "only define all implicit methods that
> are valid", but I think this can be different in different TUs. Consider:
>
>    class A;
>    class EXPORT B { unique_ptr<A> a_; };
>
> I think the implicit destructor of B will be valid in TUs that happen to
> include the header that defines A but not in others. So that doesn't
> seem like a good rule.

A TU that does not include the header that defines A is just being 
stupid so I think the compiler can choose to assume that an implicit 
destructor can be generated. And if there really is no definition for A 
then the design of B is just an error. The compiler should make 
reasonable choices. For B since C++11 is necesary for unique_ptr, 
deleted copy constructors and assignment operators should be generated 
if the compiler does not encounter any since unique_ptr is not copyable 
or assignable.

The problem is that you are forcing class designers, when a class is 
exportable, to generate copy constructors and assignment operators even 
when objects of that class are never meant to be copied or assigned. Of 
course the practical solution for this is to generate private 
declarations in C++03 and deleted declarations in C++11, but still it 
seems silly to enforce this. Instead the compiler should automatically 
generate these solutions when it sees that a class is not copyable or 
when it sees that a class is not assignable, and the designer has not 
provided his own declarations.

I really think it is wrong for clang to force class designers, even when 
exporting a class, to do something he would ordinarily never have to do 
in C++.

BTW gcc works correctly in the same situation in not generating any 
error when '__attribute__((dllexport))' is being used for mingw/gcc, if 
that means anything to clang.

Also VC++ works correctly in the same situation when 
__declspec(dllexport) is being used, if that means anything to clang.

Also as I noticed, when '__attribute__((__visibility____("default")))' 
is being used, which I assume is the Linux equivalent of 'exporting' a 
class, clang evidently does the right thing and generates no error. Why 
does it therefore operate differently with '__attribute__((dllexport))' ?

Is it a valid Boost solution to use 
'__attribute__((__visibility____("default")))' for clang on Windows 
targeting mingw/gcc as a workaround for this problem instead of having 
to change the exported class itself ? This of course would not solve the 
problem of using clang on Windows targeting VC++, where I assume 
'__attribute__((dllexport))' is correct and 
'__attribute__((__visibility____("default")))' would not be recognized, 
but I am less interested in that compiler for Boost.

>
> Nico
>
>
>     The C++ standard does tell us I believe that if an object of a class
>     is not copied there is never a need to specify a copy constructor
>     and if a class is not assigned there is never a need to specify an
>     assignment operator, whether or not one has a non-copyable or
>     non-assignable member or not. Changing that basic rule to support
>     something in clang related to "exported' classes cannot be correct IMO.
>
>     Why does '__attribute__((__visibility____("default")))' work but
>     '__attribute__((dllexport))' does not ? Aren't they both the
>     equivalent of "exporting" a class in clang ?
>
>     I realize that for clang on Windows targeting mingw/gcc and not VC++
>     that the correct "export" attribute is probably
>     '__attribute__((__visibility____("default")))' and not
>     '__attribute__((dllexport))'. But I heavily object to the idea that
>     because I might be exporting a class the rules of copyability or
>     assignability for that class must change contrary to the C++
>     standard. This also puts an extra conceptual burden on the design of
>     a class.
>
>
>         On Fri, Feb 13, 2015 at 8:07 PM, Edward Diener
>         <eldlistmailingz at tropicsoft.__com
>         <mailto:eldlistmailingz at tropicsoft.com>
>         <mailto:eldlistmailingz at __tropicsoft.com
>         <mailto:eldlistmailingz at tropicsoft.com>>>
>         wrote:
>
>              I am compiling code with "-c -x c++ -O0 -g -fno-inline
>         -Wall -g"
>              using clang targeting mingw/gcc on Windows.
>
>              This code compiles with no errors ( boost::scoped_ptr<T> is
>              non-copyable, non-assignable ):
>
>              #include <boost/scoped_ptr.hpp>
>              class
>              AClass { boost::scoped_ptr<int> sp_pointer; };
>              int main()  {  return 0;  }
>
>              This code compiles with no errors:
>
>              #include <boost/scoped_ptr.hpp>
>              class __attribute__((__visibility__(____"default")))
>              AClass { boost::scoped_ptr<int> sp_pointer; };
>              int main()  {  return 0;  }
>
>              but this code compiles with errors:
>
>              #include <boost/scoped_ptr.hpp>
>              class __attribute__((dllexport))
>              AClass { boost::scoped_ptr<int> sp_pointer; };
>              int main()  {  return 0;  }
>
>                  test_clang_bug.cpp:3:1: error: field of type
>                  'boost::scoped_ptr<int>' has private copy constructor
>                  AClass { boost::scoped_ptr<int> sp_pointer; };
>                  ^
>                  ..\..\..\boost/smart_ptr/____scoped_ptr.hpp:47:5: note:
>         declared
>                  private here
>                       scoped_ptr(scoped_ptr const &);
>                       ^
>                  test_clang_bug.cpp:3:1: note: implicit copy constructor for
>                  'AClass' first required here
>                  AClass { boost::scoped_ptr<int> sp_pointer; };
>                  ^
>                  test_clang_bug.cpp:3:1: error: 'operator=' is a private
>         member
>                  of 'boost::scoped_ptr<int>'
>                  AClass { boost::scoped_ptr<int> sp_pointer; };
>                  ^
>                  ..\..\..\boost/smart_ptr/____scoped_ptr.hpp:48:18:
>         note: declared
>                  private here
>                       scoped_ptr & operator=(scoped_ptr const &);
>                                    ^
>                  test_clang_bug.cpp:3:1: note: implicit copy assignment
>         operator
>                  for 'AClass' first required here
>                  AClass { boost::scoped_ptr<int> sp_pointer; };
>                  ^
>                  2 errors generated.
>
>
>              Comments ?
>
>              Should I file a bug report ?
>
>              I do not see why, even "exporting" a class, clang should
>         give an
>              error. If I am not copying/assigning an instance of a class
>         I should
>              never get a compiler error telling me that some member is not
>              copyable or assignable.







More information about the cfe-dev mailing list