ldionne updated this revision to Diff 165507.
ldionne added a comment.

Fix the tests and remove some warnings that I wasn't able to generate properly
(to avoid false positives).


Repository:
  rC Clang

https://reviews.llvm.org/D51789

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  
clang/test/CodeGenCXX/attr-no_extern_template.dont_assume_extern_instantiation.cpp
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/SemaCXX/attr-no_extern_template.diagnose_on_undefined_entity.cpp
  clang/test/SemaCXX/attr-no_extern_template.explicit_instantiation.cpp
  clang/test/SemaCXX/attr-no_extern_template.extern_declaration.cpp
  clang/test/SemaCXX/attr-no_extern_template.merge_redeclarations.cpp

Index: clang/test/SemaCXX/attr-no_extern_template.merge_redeclarations.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-no_extern_template.merge_redeclarations.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Test that we properly merge the no_extern_template attribute on
+// redeclarations.
+
+#define NO_EXTERN_TEMPLATE __attribute__((no_extern_template))
+
+template <class T>
+struct Foo {
+  // Declaration without the attribute, definition with the attribute.
+  void func1();
+
+  // Declaration with the attribute, definition without the attribute.
+  NO_EXTERN_TEMPLATE void func2();
+
+  // Declaration with the attribute, definition with the attribute.
+  NO_EXTERN_TEMPLATE void func3();
+};
+
+template <class T>
+NO_EXTERN_TEMPLATE 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>
+NO_EXTERN_TEMPLATE 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}}
+}
Index: clang/test/SemaCXX/attr-no_extern_template.extern_declaration.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-no_extern_template.extern_declaration.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -Wno-unused-local-typedef -fsyntax-only -verify %s
+
+// Test that extern instantiation declarations cause members marked with
+// no_extern_template to be instantiated in the current TU.
+
+#define NO_EXTERN_TEMPLATE __attribute__((no_extern_template))
+
+template <class T>
+struct Foo {
+  NO_EXTERN_TEMPLATE inline void non_static_member_function1();
+
+  NO_EXTERN_TEMPLATE void non_static_member_function2();
+
+  NO_EXTERN_TEMPLATE static inline void static_member_function1();
+
+  NO_EXTERN_TEMPLATE static void static_member_function2();
+
+  NO_EXTERN_TEMPLATE static int static_data_member;
+
+  struct NO_EXTERN_TEMPLATE member_class1 {
+    static void static_member_function() {
+      using Fail = typename T::invalid; // expected-error{{no type named 'invalid' in 'Empty'}}
+    }
+  };
+
+  struct member_class2 {
+    NO_EXTERN_TEMPLATE 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}}
+}
Index: clang/test/SemaCXX/attr-no_extern_template.explicit_instantiation.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-no_extern_template.explicit_instantiation.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Test that explicit instantiations do not instantiate entities
+// marked with no_extern_template.
+
+#define NO_EXTERN_TEMPLATE __attribute__((no_extern_template))
+
+template <class T>
+struct Foo {
+  NO_EXTERN_TEMPLATE inline void non_static_member_function1();
+
+  NO_EXTERN_TEMPLATE void non_static_member_function2();
+
+  NO_EXTERN_TEMPLATE static inline void static_member_function1();
+
+  NO_EXTERN_TEMPLATE static void static_member_function2();
+
+  NO_EXTERN_TEMPLATE static int static_data_member;
+
+  struct NO_EXTERN_TEMPLATE member_class1 {
+    static void non_static_member_function() { using Fail = typename T::fail; }
+  };
+
+  struct member_class2 {
+    NO_EXTERN_TEMPLATE 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>;
Index: clang/test/SemaCXX/attr-no_extern_template.diagnose_on_undefined_entity.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/attr-no_extern_template.diagnose_on_undefined_entity.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// Test that a diagnostic is emitted when an entity marked with no_extern_template
+// is not defined in the current TU but an extern template declaration for the
+// enclosing class exists.
+
+#define NO_EXTERN_TEMPLATE __attribute__((no_extern_template))
+
+template <class T>
+struct Foo {
+  NO_EXTERN_TEMPLATE void non_static_member_function();     // expected-warning{{Member 'non_static_member_function' marked with 'no_extern_template' attribute is not defined but an explicit template instantiation declaration exists. Reliance on this member being defined by an explicit template instantiation will lead to link errors.}}
+  NO_EXTERN_TEMPLATE static void static_member_function1(); // expected-warning{{Member 'static_member_function1' marked with 'no_extern_template' attribute is not defined}}
+  NO_EXTERN_TEMPLATE static int static_data_member;         // expected-warning{{Member 'static_data_member' marked with 'no_extern_template' attribute is not defined}}
+  struct NO_EXTERN_TEMPLATE member_class1;                  // expected-warning{{Member 'member_class1' marked with 'no_extern_template' attribute is not defined}}
+};
+
+extern template struct Foo<int>; // expected-note 4 {{explicit template instantiation declaration is here}}
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ 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)
@@ -73,6 +73,7 @@
 // CHECK-NEXT: NoDestroy (SubjectMatchRule_variable)
 // CHECK-NEXT: NoDuplicate (SubjectMatchRule_function)
 // CHECK-NEXT: NoEscape (SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: NoExternTemplate (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
 // CHECK-NEXT: NoInline (SubjectMatchRule_function)
 // CHECK-NEXT: NoInstrumentFunction (SubjectMatchRule_function)
 // CHECK-NEXT: NoMicroMips (SubjectMatchRule_function)
Index: clang/test/CodeGenCXX/attr-no_extern_template.dont_assume_extern_instantiation.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/attr-no_extern_template.dont_assume_extern_instantiation.cpp
@@ -0,0 +1,83 @@
+// 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 no_extern_template 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 NO_EXTERN_TEMPLATE __attribute__((no_extern_template))
+
+template <class T>
+struct Foo {
+  NO_EXTERN_TEMPLATE        inline void non_static_member_function1();
+  NO_EXTERN_TEMPLATE               void non_static_member_function2();
+
+  NO_EXTERN_TEMPLATE static inline void static_member_function1();
+  NO_EXTERN_TEMPLATE static        void static_member_function2();
+
+  NO_EXTERN_TEMPLATE static        int static_data_member;
+
+  struct NO_EXTERN_TEMPLATE member_class1 {
+    static void static_member_function() { }
+  };
+
+  struct member_class2 {
+    NO_EXTERN_TEMPLATE 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 as no_extern_template 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 as no_extern_template 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 as no_extern_template 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 as no_extern_template 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 as no_extern_template 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 as no_extern_template 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 as no_extern_template 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();
+}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2576,6 +2576,20 @@
     if (auto *Function = dyn_cast<FunctionDecl>(D)) {
       if (FunctionDecl *Pattern
             = Function->getInstantiatedFromMemberFunction()) {
+
+        // Skip the instantiation of this member if marked with no_extern_template.
+        if (Function->hasAttr<NoExternTemplateAttr>()) {
+          if (TSK == TSK_ExplicitInstantiationDeclaration &&
+                                                      !Pattern->isDefined()) {
+            Diag(Function->getLocation(),
+                 diag::warn_no_extern_template_undefined_entity)
+              << Function->getName();
+            Diag(PointOfInstantiation,
+                 diag::note_explicit_template_instantiation_declaration_here);
+          }
+          continue;
+        }
+
         MemberSpecializationInfo *MSInfo
           = Function->getMemberSpecializationInfo();
         assert(MSInfo && "No member specialization information?");
@@ -2618,6 +2632,19 @@
         continue;
 
       if (Var->isStaticDataMember()) {
+        // Skip the instantiation of this member if marked with no_extern_template.
+        if (Var->hasAttr<NoExternTemplateAttr>()) {
+          if (TSK == TSK_ExplicitInstantiationDeclaration &&
+                !Var->getInstantiatedFromStaticDataMember()->getDefinition()) {
+            Diag(Var->getLocation(),
+                 diag::warn_no_extern_template_undefined_entity)
+              << Var->getName();
+            Diag(PointOfInstantiation,
+                 diag::note_explicit_template_instantiation_declaration_here);
+          }
+          continue;
+        }
+
         MemberSpecializationInfo *MSInfo = Var->getMemberSpecializationInfo();
         assert(MSInfo && "No member specialization information?");
         if (MSInfo->getTemplateSpecializationKind()
@@ -2649,6 +2676,20 @@
         }
       }
     } else if (auto *Record = dyn_cast<CXXRecordDecl>(D)) {
+      // Skip the instantiation of this member if marked with no_extern_template.
+      if (Record->hasAttr<NoExternTemplateAttr>()) {
+        if (TSK == TSK_ExplicitInstantiationDeclaration &&
+                  !Record->getDefinition() &&
+                  !Record->getInstantiatedFromMemberClass()->getDefinition()) {
+          Diag(Record->getLocation(),
+               diag::warn_no_extern_template_undefined_entity)
+            << Record->getName();
+          Diag(PointOfInstantiation,
+               diag::note_explicit_template_instantiation_declaration_here);
+        }
+        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.
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -6464,6 +6464,9 @@
   case ParsedAttr::AT_InternalLinkage:
     handleInternalLinkageAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_NoExternTemplate:
+    handleSimpleAttribute<NoExternTemplateAttr>(S, D, AL);
+    break;
   case ParsedAttr::AT_LTOVisibilityPublic:
     handleSimpleAttribute<LTOVisibilityPublicAttr>(S, D, AL);
     break;
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4679,6 +4679,13 @@
   "'internal_linkage' attribute on a non-static local variable is ignored">,
   InGroup<IgnoredAttributes>;
 
+def warn_no_extern_template_undefined_entity : Warning<
+  "Member '%0' marked with 'no_extern_template' attribute is not defined but an "
+  "explicit template instantiation declaration exists. Reliance on this member "
+  "being defined by an explicit template instantiation will lead to link errors.">;
+def note_explicit_template_instantiation_declaration_here: Note<
+  "explicit template instantiation declaration is here">;
+
 def ext_internal_in_extern_inline : ExtWarn<
   "static %select{function|variable}0 %1 is used in an inline function with "
   "external linkage">, InGroup<StaticInInline>;
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -2975,6 +2975,22 @@
   }];
 }
 
+def NoExternTemplateDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``no_extern_template`` 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 ``no_extern_template``, 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). 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 = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3042,6 +3042,13 @@
   let Documentation = [InternalLinkageDocs];
 }
 
+def NoExternTemplate : InheritableAttr {
+  let Spellings = [Clang<"no_extern_template">];
+  let Subjects = SubjectList<[Var, Function, CXXRecord]>;
+  let Documentation = [NoExternTemplateDocs];
+  let MeaningfulToClassTemplateDefinition = 1;
+}
+
 def Reinitializes : InheritableAttr {
   let Spellings = [Clang<"reinitializes", 0>];
   let Subjects = SubjectList<[NonStaticNonConstCXXMethod], ErrorDiag>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D51789: [... Duncan P. N. Exon Smith via Phabricator via cfe-commits
    • [PATCH] D517... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D517... Louis Dionne via Phabricator via cfe-commits

Reply via email to