Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -3009,6 +3009,12 @@ let Documentation = [InternalLinkageDocs]; } +def NoExternTemplate : InheritableAttr { + let Spellings = [Clang<"no_extern_template">]; + let Subjects = SubjectList<[Var, Function, CXXRecord]>; + let Documentation = [NoExternTemplateDocs]; +} + def Reinitializes : InheritableAttr { let Spellings = [Clang<"reinitializes", 0>]; let Subjects = SubjectList<[NonStaticNonConstCXXMethod], ErrorDiag>; 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/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4677,6 +4677,10 @@ "'internal_linkage' attribute on a non-static local variable is ignored">, InGroup; +def warn_no_extern_template_local_storage : Warning< + "'no_extern_template' attribute on a non-static local variable is ignored">, + InGroup; + def ext_internal_in_extern_inline : ExtWarn< "static %select{function|variable}0 %1 is used in an inline function with " "external linkage">, InGroup; Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -6450,6 +6450,9 @@ case ParsedAttr::AT_InternalLinkage: handleInternalLinkageAttr(S, D, AL); break; + case ParsedAttr::AT_NoExternTemplate: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_LTOVisibilityPublic: handleSimpleAttribute(S, D, AL); break; Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2572,6 +2572,11 @@ (TSK == TSK_ImplicitInstantiation && Instantiation->isLocalClass())) && "Unexpected template specialization kind!"); for (auto *D : Instantiation->decls()) { + // Exclude all declarations marked with no_extern_template from the instantiation. + if (D->hasAttr()) { + continue; + } + bool SuppressNew = false; if (auto *Function = dyn_cast(D)) { if (FunctionDecl *Pattern Index: clang/test/CodeGenCXX/attribute_no_extern_template.explicit_instantiation.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/attribute_no_extern_template.explicit_instantiation.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O0 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O1 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O2 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O3 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -Os -o - %s | FileCheck %s + +// Test that explicit instantiations do not instantiate entities +// marked with no_extern_template. + +#define NO_EXTERN_TEMPLATE __attribute__((no_extern_template)) + +template +struct Foo { + // CHECK-NOT: define weak_odr void @_ZN3FooIiE27non_static_member_function1Ev + NO_EXTERN_TEMPLATE + inline void non_static_member_function1(); + + // CHECK-NOT: define weak_odr void @_ZN3FooIiE27non_static_member_function2Ev + NO_EXTERN_TEMPLATE + void non_static_member_function2(); + + // CHECK-NOT: define weak_odr void @_ZN3FooIiE23static_member_function1Ev + NO_EXTERN_TEMPLATE + static inline void static_member_function1(); + + // CHECK-NOT: define weak_odr void @_ZN3FooIiE23static_member_function2Ev + NO_EXTERN_TEMPLATE + static void static_member_function2(); + + // CHECK-NOT: @_ZN3FooIiE18static_data_memberE = weak_odr global + NO_EXTERN_TEMPLATE + static int static_data_member; + + struct NO_EXTERN_TEMPLATE member_class { + // CHECK-NOT: define weak_odr void @_ZN3FooIiE12member_class26non_static_member_functionEv + static void non_static_member_function() { } + }; +}; + +template +inline void Foo::non_static_member_function1() { } + +template +void Foo::non_static_member_function2() { } + +template +inline void Foo::static_member_function1() { } + +template +void Foo::static_member_function2() { } + +template +int Foo::static_data_member = 0; + +template struct Foo; Index: clang/test/CodeGenCXX/attribute_no_extern_template.extern_declaration.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/attribute_no_extern_template.extern_declaration.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O0 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O1 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O2 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O3 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -Os -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 declaration is present. + +template +struct Foo { + // Non-static member functions + __attribute__((no_extern_template)) + inline void non_static_member_function1(); + __attribute__((no_extern_template)) + void non_static_member_function2(); + + // Static member functions + __attribute__((no_extern_template)) + static inline void static_member_function1(); + __attribute__((no_extern_template)) + static void static_member_function2(); + + // Static data members + __attribute__((no_extern_template)) + static int static_data_member; + + // Member classes + struct __attribute__((no_extern_template)) member_class { + static void non_static_member_function() { } + }; +}; + +template +inline void Foo::non_static_member_function1() { } +template +void Foo::non_static_member_function2() { } + +template +inline void Foo::static_member_function1() { } +template +void Foo::static_member_function2() { } + +template +int Foo::static_data_member = 0; + +extern template struct Foo; + +void use() { + Foo f; + + // An inline non-static member function marked as no_extern_template is not + // part of the extern template declaration, so no external call must be + // emitted. + // CHECK-NOT: declare void @_ZN3FooIiE5non_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 no external call must + // be emitted. + // CHECK-NOT: declare void @_ZN3FooIiE5non_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 no external call must + // be emitted. + // CHECK-NOT: declare void @_ZN3FooIiE5static_member_function1Ev + Foo::static_member_function1(); + + // A non-inline static member function marked as no_extern_template is not + // part of the extern template declaration, so no external call must + // be emitted. + // CHECK-NOT: declare void @_ZN3FooIiE5static_member_function2Ev + Foo::static_member_function2(); + + // A static data member marked as no_extern_template is not part of the + // extern template declaration, so no external reference must be emitted. + // TODO: how to check that? + int& odr_use = Foo::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 no external + // call to its member functions must be emitted. + // TODO: How to best check that? + Foo::member_class::static_member_function(); +} + +// /Users/ldionne/work/clang-attribute/build/bin/clang-8 -cc1 -std=c++11 -triple x86_64-apple-macosx10.14.0 -emit-llvm -stdlib=libc++ -x c++ /Users/ldionne/work/clang-attribute/clang/test/CodeGenCXX/attribute_no_extern_template.cpp -o - | ./build/bin/FileCheck /Users/ldionne/work/clang-attribute/clang/test/CodeGenCXX/attribute_no_extern_template.cpp Index: clang/test/CodeGenCXX/attribute_no_extern_template.merge_redeclarations.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/attribute_no_extern_template.merge_redeclarations.cpp @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O0 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O1 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O2 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -O3 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -Os -o - %s | FileCheck %s + +// Test that we properly merge the no_extern_template attribute on +// redeclarations. + +template +struct Foo { + // Declaration without the attribute, definition with the attribute. + void func1(); + + // Declaration without the attribute, redeclaration without the attribute, + // and then definition with the attribute. + void func2(); + + // Declaration without the attribute, redeclaration with the attribute, + // and then definition without the attribute. + void func3(); + + // Declaration without the attribute, redeclaration with the attribute, + // and then definition with the attribute. + void func4(); + + // Declaration with the attribute, definition without the attribute. + __attribute__((no_extern_template)) void func5(); + + // Declaration with the attribute, definition with the attribute. + __attribute__((no_extern_template)) void func6(); + + // Declaration with the attribute, redeclaration without the attribute, and + // then definition with the attribute. + __attribute__((no_extern_template)) void func7(); + + // Declaration with the attribute, redeclaration with the attribute, and + // then definition without the attribute. + __attribute__((no_extern_template)) void func8(); +}; + +template __attribute__((no_extern_template)) void Foo::func1() { } + +template void Foo::func2(); +template __attribute__((no_extern_template)) void Foo::func2() { } + +template __attribute__((no_extern_template)) void Foo::func3(); +template void Foo::func3() { } + +template __attribute__((no_extern_template)) void Foo::func4(); +template __attribute__((no_extern_template)) void Foo::func4() { } + +template void Foo::func5() { } + +template __attribute__((no_extern_template)) void Foo::func6() { } + +template void Foo::func7(); +template __attribute__((no_extern_template)) void Foo::func7() { } + +template __attribute__((no_extern_template)) void Foo::func8(); +template void Foo::func8() { } + +template struct Foo; + +void use() { + Foo f; + + // TODO: How to check that? + f.func1(); + + // TODO: How to check that? + f.func2(); + + // TODO: How to check that? + f.func3(); + + // TODO: How to check that? + f.func4(); + + // TODO: How to check that? + f.func5(); + + // TODO: How to check that? + f.func6(); + + // TODO: How to check that? + f.func7(); + + // TODO: How to check that? + f.func8(); +} Index: clang/test/SemaCXX/attr-no_extern_template.diagnose_when_undefined.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/attr-no_extern_template.diagnose_when_undefined.cpp @@ -0,0 +1,40 @@ +// 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. Since the entity is not defined, we should normally +// emit a reference to the externally instantiated entity. However, since that +// entity has been marked with no_extern_template, any extern template +// instantiation will not provide that entity. Since this pattern is +// always an error, we diagnose it. + +template +struct Foo { + // Non-static member functions + __attribute__((no_extern_template)) + void non_static_member_function(); + + // Static member functions + __attribute__((no_extern_template)) + static void static_member_function(); + + // Static data members + __attribute__((no_extern_template)) + static int static_data_member; + + // Member classes + struct __attribute__((no_extern_template)) member_class { + static void static_member_function(); + }; +}; + +extern template struct Foo; + +void use() { + Foo f; + + f.non_static_member_function(); // TODO: expect failure + Foo::static_member_function(); // TODO: expect failure + int& odr_use = Foo::static_data_member; // TODO: expect failure + Foo::member_class::static_member_function(); // TODO: expect failure +} Index: clang/test/SemaCXX/attr-no_extern_template.valid_entities.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/attr-no_extern_template.valid_entities.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// Test that we diagnose the use of the no_extern_template attribute on +// entities that are not: +// - static/non-static member functions of a class template +// - static data member of a class template +// - member class of a class template + +void function() __attribute__((no_extern_template)); +// expected-warning{{'no_extern_template' attribute only applies to static and non-static member functions of class templates, static data members of class templates and member classes of class templates}} + +int variable __attribute__((no_extern_template)); +// expected-warning{{'no_extern_template' attribute only applies to}} + +struct __attribute__((no_extern_template)) class_type { }; +// expected-warning{{'no_extern_template' attribute only applies to}} + +struct A { + int x __attribute__((no_extern_template)); // expected-warning{{'no_extern_template' attribute only applies to}} +}; + +enum struct __attribute__((no_extern_template)) E { }; // expected-warning{{'no_extern_template' attribute only applies to}} + +namespace Z __attribute__((no_extern_template)) { } // expected-warning{{'no_extern_template' attribute only applies to}} + +void g(int a [[clang::no_extern_template]]) { // expected-warning{{'no_extern_template' attribute only applies to}} + int x [[clang::no_extern_template]]; // expected-warning{{'no_extern_template' attribute only applies to}} + static int y [[clang::no_extern_template]]; // expected-warning{{'no_extern_template' attribute only applies to}} +} + +__attribute__((no_extern_template("foo"))) int h() { } // expected-error{{'no_extern_template' attribute takes no arguments}}