r343790 - [clang] Add the exclude_from_explicit_instantiation attribute

Louis Dionne via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 4 08:49:43 PDT 2018


Author: ldionne
Date: Thu Oct  4 08:49:42 2018
New Revision: 343790

URL: http://llvm.org/viewvc/llvm-project?rev=343790&view=rev
Log:
[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

Added:
    cfe/trunk/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp
    cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
    cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp
    cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp
    cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp
Modified:
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/AttrDocs.td
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
    cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=343790&r1=343789&r2=343790&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu Oct  4 08:49:42 2018
@@ -3042,6 +3042,13 @@ def InternalLinkage : InheritableAttr {
   let Documentation = [InternalLinkageDocs];
 }
 
+def ExcludeFromExplicitInstantiation : InheritableAttr {
+  let Spellings = [Clang<"exclude_from_explicit_instantiation">];
+  let Subjects = SubjectList<[Var, Function, CXXRecord]>;
+  let Documentation = [ExcludeFromExplicitInstantiationDocs];
+  let MeaningfulToClassTemplateDefinition = 1;
+}
+
 def Reinitializes : InheritableAttr {
   let Spellings = [Clang<"reinitializes", 0>];
   let Subjects = SubjectList<[NonStaticNonConstCXXMethod], ErrorDiag>;

Modified: cfe/trunk/include/clang/Basic/AttrDocs.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=343790&r1=343789&r2=343790&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/AttrDocs.td (original)
+++ cfe/trunk/include/clang/Basic/AttrDocs.td Thu Oct  4 08:49:42 2018
@@ -2975,6 +2975,68 @@ This can be used to contain the ABI of a
   }];
 }
 
+def ExcludeFromExplicitInstantiationDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``exclude_from_explicit_instantiation`` attribute opts-out a member of a
+class template from being part of explicit template instantiations of that
+class template. This means that an explicit instantiation will not instantiate
+members of the class template marked with the attribute, but also that code
+where an extern template declaration of the enclosing class template is visible
+will not take for granted that an external instantiation of the class template
+would provide those members (which would otherwise be a link error, since the
+explicit instantiation won't provide those members). For example, let's say we
+don't want the ``data()`` method to be part of libc++'s ABI. To make sure it
+is not exported from the dylib, we give it hidden visibility:
+
+  .. code-block:: c++
+
+    // in <string>
+    template <class CharT>
+    class basic_string {
+    public:
+      __attribute__((__visibility__("hidden")))
+      const value_type* data() const noexcept { ... }
+    };
+
+    template class basic_string<char>;
+
+Since an explicit template instantiation declaration for ``basic_string<char>``
+is provided, the compiler is free to assume that ``basic_string<char>::data()``
+will be provided by another translation unit, and it is free to produce an
+external call to this function. However, since ``data()`` has hidden visibility
+and the explicit template instantiation is provided in a shared library (as
+opposed to simply another translation unit), ``basic_string<char>::data()``
+won't be found and a link error will ensue. This happens because the compiler
+assumes that ``basic_string<char>::data()`` is part of the explicit template
+instantiation declaration, when it really isn't. To tell the compiler that
+``data()`` is not part of the explicit template instantiation declaration, the
+``exclude_from_explicit_instantiation`` attribute can be used:
+
+  .. code-block:: c++
+
+    // in <string>
+    template <class CharT>
+    class basic_string {
+    public:
+      __attribute__((__visibility__("hidden")))
+      __attribute__((exclude_from_explicit_instantiation))
+      const value_type* data() const noexcept { ... }
+    };
+
+    template class basic_string<char>;
+
+Now, the compiler won't assume that ``basic_string<char>::data()`` is provided
+externally despite there being an explicit template instantiation declaration:
+the compiler will implicitly instantiate ``basic_string<char>::data()`` in the
+TUs where it is used.
+
+This attribute can be used on static and non-static member functions of class
+templates, static data members of class templates and member classes of class
+templates.
+  }];
+}
+
 def DisableTailCallsDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=343790&r1=343789&r2=343790&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Thu Oct  4 08:49:42 2018
@@ -644,7 +644,8 @@ void Sema::getUndefinedButUsed(
         continue;
       if (FD->isExternallyVisible() &&
           !isExternalWithNoLinkageType(FD) &&
-          !FD->getMostRecentDecl()->isInlined())
+          !FD->getMostRecentDecl()->isInlined() &&
+          !FD->hasAttr<ExcludeFromExplicitInstantiationAttr>())
         continue;
       if (FD->getBuiltinID())
         continue;
@@ -654,7 +655,8 @@ void Sema::getUndefinedButUsed(
         continue;
       if (VD->isExternallyVisible() &&
           !isExternalWithNoLinkageType(VD) &&
-          !VD->getMostRecentDecl()->isInline())
+          !VD->getMostRecentDecl()->isInline() &&
+          !VD->hasAttr<ExcludeFromExplicitInstantiationAttr>())
         continue;
 
       // Skip VarDecls that lack formal definitions but which we know are in

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=343790&r1=343789&r2=343790&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Thu Oct  4 08:49:42 2018
@@ -6512,6 +6512,9 @@ static void ProcessDeclAttribute(Sema &S
   case ParsedAttr::AT_InternalLinkage:
     handleInternalLinkageAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_ExcludeFromExplicitInstantiation:
+    handleSimpleAttribute<ExcludeFromExplicitInstantiationAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_LTOVisibilityPublic:
     handleSimpleAttribute<LTOVisibilityPublicAttr>(S, D, AL);
     break;

Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=343790&r1=343789&r2=343790&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Thu Oct  4 08:49:42 2018
@@ -2574,10 +2574,14 @@ Sema::InstantiateClassMembers(SourceLoca
   for (auto *D : Instantiation->decls()) {
     bool SuppressNew = false;
     if (auto *Function = dyn_cast<FunctionDecl>(D)) {
-      if (FunctionDecl *Pattern
-            = Function->getInstantiatedFromMemberFunction()) {
-        MemberSpecializationInfo *MSInfo
-          = Function->getMemberSpecializationInfo();
+      if (FunctionDecl *Pattern =
+              Function->getInstantiatedFromMemberFunction()) {
+
+        if (Function->hasAttr<ExcludeFromExplicitInstantiationAttr>())
+          continue;
+
+        MemberSpecializationInfo *MSInfo =
+            Function->getMemberSpecializationInfo();
         assert(MSInfo && "No member specialization information?");
         if (MSInfo->getTemplateSpecializationKind()
                                                  == TSK_ExplicitSpecialization)
@@ -2618,6 +2622,9 @@ Sema::InstantiateClassMembers(SourceLoca
         continue;
 
       if (Var->isStaticDataMember()) {
+        if (Var->hasAttr<ExcludeFromExplicitInstantiationAttr>())
+          continue;
+
         MemberSpecializationInfo *MSInfo = Var->getMemberSpecializationInfo();
         assert(MSInfo && "No member specialization information?");
         if (MSInfo->getTemplateSpecializationKind()
@@ -2649,6 +2656,9 @@ Sema::InstantiateClassMembers(SourceLoca
         }
       }
     } else if (auto *Record = dyn_cast<CXXRecordDecl>(D)) {
+      if (Record->hasAttr<ExcludeFromExplicitInstantiationAttr>())
+        continue;
+
       // Always skip the injected-class-name, along with any
       // redeclarations of nested classes, since both would cause us
       // to try to instantiate the members of a class twice.

Added: cfe/trunk/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp?rev=343790&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/attr-exclude_from_explicit_instantiation.dont_assume_extern_instantiation.cpp Thu Oct  4 08:49:42 2018
@@ -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();
+}

Modified: cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test?rev=343790&r1=343789&r2=343790&view=diff
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test (original)
+++ cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test Thu Oct  4 08:49:42 2018
@@ -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)

Added: cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp?rev=343790&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp (added)
+++ cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp Thu Oct  4 08:49:42 2018
@@ -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 at -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 at -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 at -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 at -1 {{add an explicit instantiation}}
+}

Added: cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp?rev=343790&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp (added)
+++ cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.explicit_instantiation.cpp Thu Oct  4 08:49:42 2018
@@ -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>;

Added: cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp?rev=343790&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp (added)
+++ cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.extern_declaration.cpp Thu Oct  4 08:49:42 2018
@@ -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}}
+}

Added: cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp?rev=343790&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp (added)
+++ cfe/trunk/test/SemaCXX/attr-exclude_from_explicit_instantiation.merge_redeclarations.cpp Thu Oct  4 08:49:42 2018
@@ -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}}
+}




More information about the cfe-commits mailing list