[clang] Add the exclude_from_explicit_instantiation attribute
Summary:
This attribute allows excluding a member of a class template from being part
of an explicit template instantiation of that class template. This also makes
sure that code using such a member will not take for granted that an external
instantiation exists in another translation unit. The attribute was discussed
on cfe-dev at [1] and is primarily motivated by the removal of always_inline
in libc++ to control what's part of the ABI (see links in [1]).
[1]: http://lists.llvm.org/pipermail/cfe-dev/2018-August/059024.html
rdar://problem/43428125
Reviewers: rsmith
Subscribers: dexonsmith, cfe-commits
Differential Revision: https://reviews.llvm.org/D51789
llvm-svn: 343790
diff --git a/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp
new file mode 100644
index 0000000..efbbb0c
--- /dev/null
+++ b/clang/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O0 -o - %s | FileCheck %s
+
+// Test that we do not assume that entities marked with the
+// exclude_from_explicit_instantiation attribute are instantiated
+// in another TU when an extern template instantiation declaration
+// is present. We test that by making sure that definitions are
+// generated in this TU despite there being an extern template
+// instantiation declaration, which is normally not the case.
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation))
+
+template <class T>
+struct Foo {
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION inline void non_static_member_function1();
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void non_static_member_function2();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static inline void static_member_function1();
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static void static_member_function2();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static int static_data_member;
+
+ struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION member_class1 {
+ static void static_member_function() { }
+ };
+
+ struct member_class2 {
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static void static_member_function() { }
+ };
+};
+
+template <class T> inline void Foo<T>::non_static_member_function1() { }
+template <class T> void Foo<T>::non_static_member_function2() { }
+
+template <class T> inline void Foo<T>::static_member_function1() { }
+template <class T> void Foo<T>::static_member_function2() { }
+
+template <class T> int Foo<T>::static_data_member = 0;
+
+extern template struct Foo<int>;
+
+void use() {
+ Foo<int> f;
+
+ // An inline non-static member function marked with the attribute is not
+ // part of the extern template declaration, so a definition must be emitted
+ // in this TU.
+ // CHECK-DAG: define linkonce_odr void @_ZN3FooIiE27non_static_member_function1Ev
+ f.non_static_member_function1();
+
+ // A non-inline non-static member function marked with the attribute is
+ // not part of the extern template declaration, so a definition must be
+ // emitted in this TU.
+ // CHECK-DAG: define linkonce_odr void @_ZN3FooIiE27non_static_member_function2Ev
+ f.non_static_member_function2();
+
+ // An inline static member function marked with the attribute is not
+ // part of the extern template declaration, so a definition must be
+ // emitted in this TU.
+ // CHECK-DAG: define linkonce_odr void @_ZN3FooIiE23static_member_function1Ev
+ Foo<int>::static_member_function1();
+
+ // A non-inline static member function marked with the attribute is not
+ // part of the extern template declaration, so a definition must be
+ // emitted in this TU.
+ // CHECK-DAG: define linkonce_odr void @_ZN3FooIiE23static_member_function2Ev
+ Foo<int>::static_member_function2();
+
+ // A static data member marked with the attribute is not part of the
+ // extern template declaration, so a definition must be emitted in this TU.
+ // CHECK-DAG: @_ZN3FooIiE18static_data_memberE = linkonce_odr global
+ int& odr_use = Foo<int>::static_data_member;
+
+ // A member class marked with the attribute is not part of the extern
+ // template declaration (it is not recursively instantiated), so its member
+ // functions must be emitted in this TU.
+ // CHECK-DAG: define linkonce_odr void @_ZN3FooIiE13member_class122static_member_functionEv
+ Foo<int>::member_class1::static_member_function();
+
+ // A member function marked with the attribute in a member class is not
+ // part of the extern template declaration of the parent class template, so
+ // it must be emitted in this TU.
+ // CHECK-DAG: define linkonce_odr void @_ZN3FooIiE13member_class222static_member_functionEv
+ Foo<int>::member_class2::static_member_function();
+}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index f45d097..0a54c6c 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -2,7 +2,7 @@
// The number of supported attributes should never go down!
-// CHECK: #pragma clang attribute supports 128 attributes:
+// CHECK: #pragma clang attribute supports 129 attributes:
// CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function)
@@ -47,6 +47,7 @@
// CHECK-NEXT: DisableTailCalls (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: EnableIf (SubjectMatchRule_function)
// CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
+// CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
// CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
// CHECK-NEXT: Flatten (SubjectMatchRule_function)
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
new file mode 100644
index 0000000..24e2422
--- /dev/null
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -fsyntax-only -Wundefined-func-template -Wundefined-var-template -verify %s
+
+// Test that a diagnostic is emitted when an entity marked with the
+// exclude_from_explicit_instantiation attribute is not defined in
+// the current TU but it is used (and it is hence implicitly
+// instantiated).
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation))
+
+template <class T>
+struct Foo {
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void non_static_member_function(); // expected-note{{forward declaration of template entity is here}}
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static void static_member_function(); // expected-note{{forward declaration of template entity is here}}
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static int static_data_member; // expected-note{{forward declaration of template entity is here}}
+ struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION nested {
+ static int static_member_function(); // expected-note{{forward declaration of template entity is here}}
+ };
+};
+
+extern template struct Foo<int>;
+
+void use() {
+ Foo<int> foo;
+
+ foo.non_static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::non_static_member_function' required here, but no definition is available}}
+ // expected-note@-1 {{add an explicit instantiation}}
+
+ Foo<int>::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::static_member_function' required here, but no definition is available}}
+ // expected-note@-1 {{add an explicit instantiation}}
+
+ (void)Foo<int>::static_data_member; // expected-warning{{instantiation of variable 'Foo<int>::static_data_member' required here, but no definition is available}}
+ // expected-note@-1 {{add an explicit instantiation}}
+
+ Foo<int>::nested::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::nested::static_member_function' required here, but no definition is available}}
+ // expected-note@-1 {{add an explicit instantiation}}
+}
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp
new file mode 100644
index 0000000..379ab0a
--- /dev/null
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Test that explicit instantiations do not instantiate entities
+// marked with the exclude_from_explicit_instantiation attribute.
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation))
+
+template <class T>
+struct Foo {
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION inline void non_static_member_function1();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void non_static_member_function2();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static inline void static_member_function1();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static void static_member_function2();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static int static_data_member;
+
+ struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION member_class1 {
+ static void non_static_member_function() { using Fail = typename T::fail; }
+ };
+
+ struct member_class2 {
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static void non_static_member_function() { using Fail = typename T::fail; }
+ };
+};
+
+template <class T>
+inline void Foo<T>::non_static_member_function1() { using Fail = typename T::fail; }
+
+template <class T>
+void Foo<T>::non_static_member_function2() { using Fail = typename T::fail; }
+
+template <class T>
+inline void Foo<T>::static_member_function1() { using Fail = typename T::fail; }
+
+template <class T>
+void Foo<T>::static_member_function2() { using Fail = typename T::fail; }
+
+template <class T>
+int Foo<T>::static_data_member = T::fail;
+
+// expected-no-diagnostics
+template struct Foo<int>;
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp
new file mode 100644
index 0000000..4cb0225
--- /dev/null
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -Wno-unused-local-typedef -fsyntax-only -verify %s
+
+// Test that extern instantiation declarations cause members marked with
+// the exclude_from_explicit_instantiation attribute to be instantiated in
+// the current TU.
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation))
+
+template <class T>
+struct Foo {
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION inline void non_static_member_function1();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void non_static_member_function2();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static inline void static_member_function1();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static void static_member_function2();
+
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static int static_data_member;
+
+ struct EXCLUDE_FROM_EXPLICIT_INSTANTIATION member_class1 {
+ static void static_member_function() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+ }
+ };
+
+ struct member_class2 {
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION static void static_member_function() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+ }
+ };
+};
+
+template <class T>
+inline void Foo<T>::non_static_member_function1() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+}
+
+template <class T>
+void Foo<T>::non_static_member_function2() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+}
+
+template <class T>
+inline void Foo<T>::static_member_function1() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+}
+
+template <class T>
+void Foo<T>::static_member_function2() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+}
+
+template <class T>
+int Foo<T>::static_data_member = T::invalid; // expected-error{{no member named 'invalid' in 'Empty'}}
+
+struct Empty { };
+extern template struct Foo<Empty>;
+
+int main() {
+ Foo<Empty> foo;
+ foo.non_static_member_function1(); // expected-note{{in instantiation of}}
+ foo.non_static_member_function2(); // expected-note{{in instantiation of}}
+ Foo<Empty>::static_member_function1(); // expected-note{{in instantiation of}}
+ Foo<Empty>::static_member_function2(); // expected-note{{in instantiation of}}
+ (void)foo.static_data_member; // expected-note{{in instantiation of}}
+ Foo<Empty>::member_class1::static_member_function(); // expected-note{{in instantiation of}}
+ Foo<Empty>::member_class2::static_member_function(); // expected-note{{in instantiation of}}
+}
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp
new file mode 100644
index 0000000..da861ab
--- /dev/null
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Test that we properly merge the exclude_from_explicit_instantiation
+// attribute on redeclarations.
+
+#define EXCLUDE_FROM_EXPLICIT_INSTANTIATION __attribute__((exclude_from_explicit_instantiation))
+
+template <class T>
+struct Foo {
+ // Declaration without the attribute, definition with the attribute.
+ void func1();
+
+ // Declaration with the attribute, definition without the attribute.
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void func2();
+
+ // Declaration with the attribute, definition with the attribute.
+ EXCLUDE_FROM_EXPLICIT_INSTANTIATION void func3();
+};
+
+template <class T>
+EXCLUDE_FROM_EXPLICIT_INSTANTIATION void Foo<T>::func1() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+}
+
+template <class T>
+void Foo<T>::func2() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+}
+
+template <class T>
+EXCLUDE_FROM_EXPLICIT_INSTANTIATION void Foo<T>::func3() {
+ using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+}
+
+struct Empty { };
+extern template struct Foo<Empty>;
+
+int main() {
+ Foo<Empty> foo;
+ foo.func1(); // expected-note{{in instantiation of}}
+ foo.func2(); // expected-note{{in instantiation of}}
+ foo.func3(); // expected-note{{in instantiation of}}
+}