Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4247,6 +4247,7 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation,
continue;

if (Context.getTargetInfo().getTriple().isOSWindows() &&
!Context.getTargetInfo().getTriple().isOSCygMing() &&
TSK == TSK_ExplicitInstantiationDeclaration) {
// On Windows, explicit instantiation decl of the outer class doesn't
// affect the inner class. Typically extern template declarations are
Expand Down
109 changes: 92 additions & 17 deletions clang/test/CodeGenCXX/mingw-template-dllexport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,121 @@
#define JOIN2(x, y) x##y
#define JOIN(x, y) JOIN2(x, y)
#define UNIQ(name) JOIN(name, __LINE__)
#define USEMEMFUNC(class, func) void (class::*UNIQ(use)())() { return &class::func; }
#define USEMEMFUNC(class, func) auto UNIQ(use) = &class::func;

template <class T>
class c {
// MinGW-GCC does not apply 'dllexport' to inline member function in dll-exported template but clang does from long ago.
void f() {}
void g();
inline static int u = 0;
static int v;
};
template <class T> void c<T>::g() {}
template <class T> int c<T>::v = 0;

// #1
template class __declspec(dllexport) c<int>;

// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIiE1fEv

// #2
extern template class __declspec(dllexport) c<char>;
template class c<char>;

// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1fEv

// #3
extern template class c<double>;
template class __declspec(dllexport) c<double>;
template class __declspec(dllexport) c<double>; // expected-warning {{ 'dllexport' attribute ignored on explicit instantiation definition }}

// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN1cIdE1fEv

template <class T>
struct outer {
void f();
void f() {}
void g();
inline static int u = 0;
static int v;
// MinGW-GCC and Clang does not apply 'dllexport' to inner type and its sub-elements in template class.
struct inner {
void f();
void f() {}
void g();
inline static int u = 0;
static int v;
};
};

template <class T> void outer<T>::f() {}
template <class T> void outer<T>::inner::f() {}
template <class T> void outer<T>::g() {}
template <class T> void outer<T>::inner::g() {}
template <class T> int outer<T>::v = 0;
template <class T> int outer<T>::inner::v = 0;

template class __declspec(dllexport) outer<int>;
// #4
template struct __declspec(dllexport) outer<int>;

// CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1fEv
// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1fEv

extern template class __declspec(dllimport) outer<char>;
// #5
extern template struct __declspec(dllimport) outer<char>;
USEMEMFUNC(outer<char>, f)
USEMEMFUNC(outer<char>, g)
USEMEMFUNC(outer<char>, u)
USEMEMFUNC(outer<char>, v)
USEMEMFUNC(outer<char>::inner, f)
USEMEMFUNC(outer<char>::inner, g)
USEMEMFUNC(outer<char>::inner, u)
USEMEMFUNC(outer<char>::inner, v)


// #1 variables
// CHECK: @_ZN1cIiE1uE = {{.*}} dllexport {{.*}}
// CHECK: @_ZN1cIiE1vE = {{.*}} dllexport {{.*}}

// #2 variables
// CHECK: @_ZN1cIcE1uE = {{.*}} dllexport {{.*}}
// CHECK: @_ZN1cIcE1vE = {{.*}} dllexport {{.*}}

// #3 variables
// CHECK: @_ZN1cIdE1uE = {{.*}}
// CHECK-NOT: @_ZN1cIcE1uE = {{.*}} dllexport {{.*}}
// CHECK: @_ZN1cIdE1vE = {{.*}}
// CHECK-NOT: @_ZN1cIcE1vE = {{.*}} dllexport {{.*}}

// #4 variables
// CHECK: @_ZN5outerIiE1uE = {{.*}} dllexport {{.*}}
// CHECK: @_ZN5outerIiE1vE = {{.*}} dllexport {{.*}}
// CHECK: @_ZN5outerIiE5inner1uE = {{.*}}
// CHECK-NOT: @_ZN5outerIiE5inner1uE = {{.*}} dllexport {{.*}}
// CHECK: @_ZN5outerIiE5inner1vE = {{.*}}
// CHECK-NOT: @_ZN5outerIiE5inner1vE = {{.*}} dllexport {{.*}}

// #5 variables
// CHECK: @_ZN5outerIcE1uE = external dllimport {{.*}}
// CHECK: @_ZN5outerIcE1vE = external dllimport {{.*}}
// CHECK-NOT: @_ZN5outerIcE5inner1uE = dllimport {{.*}}
// CHECK-NOT: @_ZN5outerIcE5inner1vE = dllimport {{.*}}
// CHECK: @_ZN5outerIcE5inner1uE = external {{.*}}
// CHECK: @_ZN5outerIcE5inner1vE = external {{.*}}


// #1 functions
// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIiE1fEv
// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIiE1gEv

// #2 functions
// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1fEv
// CHECK: define {{.*}} dllexport {{.*}} @_ZN1cIcE1gEv

// #3 functions
// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN1cIdE1fEv
// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN1cIdE1gEv

// #4 functions
// CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1fEv
// CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1gEv
// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1fEv
// CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1gEv

// #5 functions
// CHECK: declare dllimport {{.*}} @_ZN5outerIcE1fEv
// CHECK: define {{.*}} @_ZN5outerIcE5inner1fEv
// CHECK: declare dllimport {{.*}} @_ZN5outerIcE1gEv
// CHECK-NOT: declare dllimport {{.*}} @_ZN5outerIcE5inner1fEv
// CHECK-NOT: declare dllimport {{.*}} @_ZN5outerIcE5inner1gEv
// CHECK-NOT: define {{.*}} @_ZN5outerIcE1fEv
// CHECK-NOT: define {{.*}} @_ZN5outerIcE5inner1fEv
// CHECK-NOT: define {{.*}} @_ZN5outerIcE1gEv
// CHECK-NOT: define {{.*}} @_ZN5outerIcE5inner1gEv
7 changes: 7 additions & 0 deletions libcxx/docs/DesignDocs/VisibilityMacros.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ Visibility Macros
the extern template declaration) as exported on Windows, as discussed above.
On all other platforms, this macro has an empty definition.

**_LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS**
To let dll-importing and dll-exporting consitent on windows-gnu environment,
this annotation must be attached to a non-template class that isn't marked
as `dllexport` defined in a class template. Currentry here are ostream::sentry
and istream::sentry only but if a time to such a new class be introduced,
the class has to be attached this annotation.

Links
=====

Expand Down
8 changes: 8 additions & 0 deletions libcxx/include/__config
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,11 @@ typedef __char32_t char32_t;
# define _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS
# define _LIBCPP_OVERRIDABLE_FUNC_VIS
# define _LIBCPP_EXPORTED_FROM_ABI
# if defined(_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) || !__has_attribute(exclude_from_explicit_instantiation)
# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS
# else
# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS __attribute__((__exclude_from_explicit_instantiation__))
# endif
# elif defined(_LIBCPP_BUILDING_LIBRARY)
# if defined(__MINGW32__)
# define _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __declspec(dllexport)
Expand All @@ -375,11 +380,13 @@ typedef __char32_t char32_t;
# endif
# define _LIBCPP_OVERRIDABLE_FUNC_VIS __declspec(dllexport)
# define _LIBCPP_EXPORTED_FROM_ABI __declspec(dllexport)
# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS
# else
# define _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __declspec(dllimport)
# define _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS
# define _LIBCPP_OVERRIDABLE_FUNC_VIS
# define _LIBCPP_EXPORTED_FROM_ABI __declspec(dllimport)
# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS
# endif

# define _LIBCPP_HIDDEN
Expand All @@ -399,6 +406,7 @@ typedef __char32_t char32_t;
# define _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_VISIBILITY("default")
# define _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS _LIBCPP_VISIBILITY("default")
# define _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS
# define _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS

// TODO: Make this a proper customization point or remove the option to override it.
# ifndef _LIBCPP_OVERRIDABLE_FUNC_VIS
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/__ostream/basic_ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class basic_ostream : virtual public basic_ios<_CharT, _Traits> {

public:
// 27.7.2.4 Prefix/suffix:
class sentry;
class _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS sentry;

// 27.7.2.6 Formatted output:
inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 basic_ostream& operator<<(basic_ostream& (*__pf)(basic_ostream&)) {
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/istream
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ public:
basic_istream& operator=(const basic_istream& __rhs) = delete;

// 27.7.1.1.3 Prefix/suffix:
class sentry;
class _LIBCPP_INNER_CLASS_IN_TEMPLATE_VIS sentry;

// 27.7.1.2 Formatted input:
inline _LIBCPP_HIDE_FROM_ABI_AFTER_V1 basic_istream& operator>>(basic_istream& (*__pf)(basic_istream&)) {
Expand Down
Loading