[cfe-dev] Interesting clang behavior/bug? on Windows
Richard Smith
richard at metafoo.co.uk
Sun Feb 15 18:19:05 PST 2015
On Sat, Feb 14, 2015 at 4:08 PM, Edward Diener <
eldlistmailingz at tropicsoft.com> wrote:
> On 2/14/2015 5:48 PM, Reid Kleckner wrote:
>
>> There might be a Clang bug here, depending on whether the copy ctor is
>> supposed to be implicitly deleted or not. I forget if having a base
>> class with a private copy ctor implicitly deletes derived class copy
>> ctors or not.
>>
>
> The second sentence above is not the case in my example, since there is no
> base class involved. I also did not compile in C++11 mode in my example
> AFAICS. Don't I need a command-line switch of -std=c++11 for that ?
In C++11, the copy constructor would be deleted because the base class dtor
is inaccessible. In C++98, it will not be, because there is no notion of
deleted functions.
> Clang's class dllexport semantics are modeled on MSVC's because they
>> make more sense than GCC's for PE/COFF. As Nico said, the idea is that
>> we emit all these special members (and inline functions!) so that
>> clients can dllimport them and not have to emit definitions. This is an
>> intentional semantic difference from ELF's visibility model, which has a
>> different mechanism for ensuring that the addresses of inline functions
>> are the same across DSO boundaries.
>>
>> If you can find a way to delete, explicitly or implicitly, the unwanted
>> copy ctor, then Clang should skip it while exporting the rest. If not,
>> that's a bug.
>>
>
> Let me start by saying that both mingw/mingw64/gcc on Windows and VC++
> compiles the example I gave without error using their respective Windows
> export keywords. Clang as I have shown does not.
>
Presumably MSVC accepts because they build in C++11 mode (sort of -- they
don't *have* a separate C++98 mode). I would expect Clang to also accept
the code that MSVC accepts when run in C++11 mode.
Whether or not clang generates an implicit copy constructor /
> copy-assignment operator when the user does not have one seems to me to be
> an irrelevant issue. If objects of the class are never copied/assigned why
> should clang generate an error ?
Because, as has already been stated, marking a class as dllexport causes
some of its implicitly-generated methods to be exported, which triggers
their definition. We may be getting the "some" wrong here, or the rules for
mingw and for MSVC might be different, but it *is* correct that we trigger
the definition of some implicit members in some cases when the class is
marked dllexport -- this is necessary for link compatibility with MSVC.
This is regardless of whether the class is exported or not. I have a class
> that cannot be copied/assigned so naturally I see no need to create a
> user-defined copy constructor or copy-assignment operator. How can I be
> wrong not doing so ? Even when I export the class I am still telling users
> of the class that import it that it cannot be copied or assigned. That
> seems pretty clearcut to me. Clang now wants to tell me that if I export
> the class I must change how I code the class as to provide a user-defined
> copy constructor/assignment operator ( or a private declaration/deleted
> function depending on C++03/C++11 ) to satisfy clang ? That is not
> logically right from this end-user's point of view. I am also pretty sure
> that in C++03 if a class is not copyable I am not required to provide a
> user-defined copy constructor or private copy constructor declaration when
> defining the class.
>
> Even in a C++11 compilation is it actually a C++ standard error if the
> programmer does not provide deleted versions of the copy constructor /
> assignment operator when objects of that class cannot be copied / assigned
> using the default generated copy constructor / assignment operator ? I do
> not think so. According to my reading of the C++11 standard as interpreted
> by Lippman in C++ Fifth Edition,
>
> "The synthesized copy constructor is defined as deleted if the class has a
> member whose own copy constructor is deleted or inaccesible..."
>
> "The synthesized copy-assignment operator is defined as deleted if a
> member has a deleted or inaccessible copy-assignment operator..."
>
> I realized I am not quoting from the C++11 standard but unless Lippman is
> in error here I cannot see other than that clang is in error in the case I
> have presented.
>
You're using a language extension, so the guarantees of the C++ standard
are not relevant.
I don't mind filing a bug report on this but I want to get some agreement
> here that clang is not doing the right thing even in the face of an
> "export" of the class. The job of defining an implicit copy constructor and
> copy-assignment operator is that of the compiler and if clang wants to
> claim that in the face of the "export" keyword it is not required to follow
> the standard C++ rules of how thiws should be done, I do not want to follow
> up with such a bug report.
>
>
>> On Fri, Feb 13, 2015 at 11:26 PM, Edward Diener
>> <eldlistmailingz at tropicsoft.com
>> <mailto:eldlistmailingz at tropicsoft.com>> wrote:
>>
>> 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>
>> <mailto: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@
>> <mailto:eldlistmailingz@>__trop__icsoft.com <
>> http://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.
>>
>
>
>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20150215/a879c9d2/attachment.html>
More information about the cfe-dev
mailing list