DECL_COMDAT. */
if (DECL_COMDAT (decl)
|| (TREE_CODE (decl) == FUNCTION_DECL
- && DECL_DECLARED_INLINE_P (decl)
- /* But gnu_inline functions are always external. */
- && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl)))
+ && DECL_DECLARED_INLINE_P (decl))
|| (DECL_LANG_SPECIFIC (decl)
&& DECL_TEMPLATE_INSTANTIATION (decl))
|| (VAR_P (decl) && DECL_INLINE_VAR_P (decl)))
return t;
}
+/* The kinds of interface an importer could have for a decl. */
+
+enum class importer_interface {
+ unknown, /* The definition may or may not need to be emitted. */
+ always_import, /* The definition can always be found in another TU. */
+ always_emit, /* The definition must be emitted in the importer's TU. */
+};
+
+/* Returns what kind of interface an importer will have of DECL. */
+
+static importer_interface
+get_importer_interface (tree decl)
+{
+ /* Internal linkage entities must be emitted in each importer if
+ there is a definition available. */
+ if (!TREE_PUBLIC (decl))
+ return importer_interface::always_emit;
+
+ /* Entities that aren't vague linkage are either not definitions or
+ will be emitted in this TU, so importers can just refer to an
+ external definition. */
+ if (!vague_linkage_p (decl))
+ return importer_interface::always_import;
+
+ /* For explicit instantiations, importers can always rely on there
+ being a definition in another TU, unless this is a definition
+ in a header module: in which case the importer will always need
+ to emit it. */
+ if (DECL_LANG_SPECIFIC (decl)
+ && DECL_EXPLICIT_INSTANTIATION (decl))
+ return (header_module_p () && !DECL_EXTERNAL (decl)
+ ? importer_interface::always_emit
+ : importer_interface::always_import);
+
+ /* A gnu_inline function is never emitted in any TU. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_DECLARED_INLINE_P (decl)
+ && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl)))
+ return importer_interface::always_import;
+
+ /* Everything else has vague linkage. */
+ return importer_interface::unknown;
+}
+
/* The structure streamers access the raw fields, because the
alternative, of using the accessor macros can require using
different accessors for the same underlying field, depending on the
we need to import or export any vague-linkage entities on
stream-in. */
bool interface_known = t->decl_common.lang_flag_5;
- if (interface_known && vague_linkage_p (t))
+ if (interface_known
+ && get_importer_interface (t) == importer_interface::unknown)
interface_known = false;
WB (interface_known);
}
is_external = true;
gcc_fallthrough ();
case FUNCTION_DECL:
- if (TREE_PUBLIC (t)
- && !vague_linkage_p (t))
+ if (get_importer_interface (t)
+ == importer_interface::always_import)
is_external = true;
break;
}
if (TREE_CODE (d_inner) == FUNCTION_DECL
&& DECL_DECLARED_INLINE_P (d_inner))
- DECL_DECLARED_INLINE_P (e_inner) = true;
+ {
+ DECL_DECLARED_INLINE_P (e_inner) = true;
+ if (!DECL_SAVED_TREE (e_inner)
+ && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (d_inner))
+ && !lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (e_inner)))
+ {
+ DECL_INTERFACE_KNOWN (e_inner)
+ |= DECL_INTERFACE_KNOWN (d_inner);
+ DECL_DISREGARD_INLINE_LIMITS (e_inner)
+ |= DECL_DISREGARD_INLINE_LIMITS (d_inner);
+ // TODO: we will eventually want to merge all decl attributes
+ duplicate_one_attribute (&DECL_ATTRIBUTES (e_inner),
+ DECL_ATTRIBUTES (d_inner), "gnu_inline");
+ }
+ }
if (!DECL_EXTERNAL (d_inner))
DECL_EXTERNAL (e_inner) = false;
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true;
tentative_decl_linkage (decl);
if (DECL_IMPLICIT_INSTANTIATION (decl)
+ || (DECL_EXPLICIT_INSTANTIATION (decl)
+ && !DECL_EXTERNAL (decl))
|| (DECL_CLASS_SCOPE_P (decl)
&& !DECL_VTABLE_OR_VTT_P (decl)
&& !DECL_TEMPLATE_INFO (decl)))
--- /dev/null
+// { dg-additional-options "-fmodules -Wno-global-module" }
+// { dg-module-cmi M }
+
+module;
+template <typename>
+struct S {
+ S() {}
+};
+export module M;
+extern template class S<int>;
+S<int> s;
--- /dev/null
+// { dg-additional-options "-fmodules" }
+
+template <typename>
+struct S {
+ S() {}
+};
+
+void foo() { S<double> x;}
+
+import M;
+
+// Lazy loading of extern S<int> at EOF should not ICE
--- /dev/null
+// { dg-additional-options "-fmodule-header" }
+// { dg-module-cmi {} }
+
+template <typename T> inline void ha() {}
+extern template void ha<int>();
+extern template void ha<bool>();
+template void ha<char>();
+
+template <typename T> void hb() {}
+extern template void hb<int>();
+extern template void hb<bool>();
+template void hb<char>();
+
+template <typename T> inline int hc = 123;
+extern template int hc<int>;
+extern template int hc<bool>;
+template int hc<char>;
+
+template <typename T> int hd = 123;
+extern template int hd<int>;
+extern template int hd<bool>;
+template int hd<char>;
--- /dev/null
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi M }
+
+export module M;
+
+export template <typename T> inline void ma() {}
+extern template void ma<int>();
+extern template void ma<bool>();
+template void ma<char>();
+
+export template <typename T> void mb() {}
+extern template void mb<int>();
+extern template void mb<bool>();
+template void mb<char>();
+
+export template <typename T> inline int mc = 123;
+extern template int mc<int>;
+extern template int mc<bool>;
+template int mc<char>;
+
+export template <typename T> int md = 123;
+extern template int md<int>;
+extern template int md<bool>;
+template int md<char>;
--- /dev/null
+// { dg-additional-options "-fmodules" }
+
+import "extern-tpl-4_a.H";
+import M;
+
+int main() {
+ ha<int>();
+ ha<char>();
+ ha<double>();
+
+ ma<int>();
+ ma<char>();
+ ma<double>();
+
+ hb<int>();
+ hb<char>();
+ hb<double>();
+
+ mb<int>();
+ mb<char>();
+ mb<double>();
+
+ int x1 = hc<int> + hc<char> + hc<double>;
+ int x2 = hd<int> + hd<char> + hd<double>;
+ int x3 = mc<int> + mc<char> + mc<double>;
+ int x4 = md<int> + md<char> + md<double>;
+ return x1 + x2 + x3 + x4;
+}
+
+
+// 'int': imported explicit instantiation decls should not be emitted here:
+// { dg-final { scan-assembler-not "_Z2haIiEvv:" } }
+// { dg-final { scan-assembler-not "_Z2hbIiEvv:" } }
+// { dg-final { scan-assembler-not "_Z2hcIiE:" } }
+// { dg-final { scan-assembler-not "_Z2hdIiE:" } }
+// { dg-final { scan-assembler-not "_ZW1M2maIiEvv:" } }
+// { dg-final { scan-assembler-not "_ZW1M2mbIiEvv:" } }
+// { dg-final { scan-assembler-not "_ZW1M2mcIiE:" } }
+// { dg-final { scan-assembler-not "_ZW1M2mdIiE:" } }
+
+// 'char': explicit instantiation definitions don't need to be emitted for
+// modules, but need to be emitted for header units (as there's no other TU):
+// { dg-final { scan-assembler "_Z2haIcEvv:" } }
+// { dg-final { scan-assembler "_Z2hbIcEvv:" } }
+// { dg-final { scan-assembler "_Z2hcIcE:" } }
+// { dg-final { scan-assembler "_Z2hdIcE:" } }
+// { dg-final { scan-assembler-not "_ZW1M2maIcEvv:" } }
+// { dg-final { scan-assembler-not "_ZW1M2mbIcEvv:" } }
+// { dg-final { scan-assembler-not "_ZW1M2mcIcE:" } }
+// { dg-final { scan-assembler-not "_ZW1M2mdIcE:" } }
+
+// 'double': these are not explicitly instantiated and should be emitted here:
+// { dg-final { scan-assembler "_Z2haIdEvv:" } }
+// { dg-final { scan-assembler "_Z2hbIdEvv:" } }
+// { dg-final { scan-assembler "_Z2hcIdE:" } }
+// { dg-final { scan-assembler "_Z2hdIdE:" } }
+// { dg-final { scan-assembler "_ZW1M2maIdEvv:" } }
+// { dg-final { scan-assembler "_ZW1M2mbIdEvv:" } }
+// { dg-final { scan-assembler "_ZW1M2mcIdE:" } }
+// { dg-final { scan-assembler "_ZW1M2mdIdE:" } }
+
+template void ha<bool>();
+template void hb<bool>();
+template int hc<bool>;
+template int hd<bool>;
+
+template void ma<bool>();
+template void mb<bool>();
+template int mc<bool>;
+template int md<bool>;
+
+// 'bool': instantiated in this file, and so must be emitted here:
+// { dg-final { scan-assembler "_Z2haIbEvv:" } }
+// { dg-final { scan-assembler "_Z2hbIbEvv:" } }
+// { dg-final { scan-assembler "_Z2hcIbE:" } }
+// { dg-final { scan-assembler "_Z2hdIbE:" } }
+// { dg-final { scan-assembler "_ZW1M2maIbEvv:" } }
+// { dg-final { scan-assembler "_ZW1M2mbIbEvv:" } }
+// { dg-final { scan-assembler "_ZW1M2mcIbE:" } }
+// { dg-final { scan-assembler "_ZW1M2mdIbE:" } }
--- /dev/null
+// PR c++/119154
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi foo }
+
+export module foo;
+export extern "C++" inline __attribute__((__gnu_inline__)) void bar() {}
+export extern "C++" inline __attribute__((__gnu_inline__)) void decl();
--- /dev/null
+// PR c++/119154
+// { dg-additional-options "-fmodules" }
+
+void bar();
+void decl(); // { dg-warning "used but never defined" }
+import foo;
+
+void test_b() {
+ bar();
+ decl();
+}
+
+// A function only defined with gnu_inline should not be emitted here.
+// { dg-final { scan-assembler-not "_Z3barv:" } }
--- /dev/null
+// PR c++/119154
+// { dg-additional-options "-fmodules" }
+
+void bar() {}
+void decl() {}
+import foo;
+
+void test_c() {
+ bar();
+ decl();
+};
+
+// Make sure importing a gnu_inline definition didn't stop us from emitting
+// the non-gnu_inline definition we had before the module import.
+// { dg-final { scan-assembler "_Z3barv:" } }
+// { dg-final { scan-assembler "_Z4declv:" } }
--- /dev/null
+// PR c++/119154
+// { dg-additional-options "-fmodules -fno-module-lazy" }
+
+import foo;
+void bar() {}
+void decl() {}
+
+void test_c() {
+ bar();
+ decl();
+};
+
+// Make sure importing a gnu_inline definition didn't stop us from emitting
+// the non-gnu_inline definition we had after the module import.
+// { dg-final { scan-assembler "_Z3barv:" } }
+// { dg-final { scan-assembler "_Z4declv:" } }
--- /dev/null
+// PR c++/119154
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi xstd }
+
+export module xstd;
+
+inline __attribute__((__gnu_inline__)) void wmemset() {}
+
+extern "C++" template <class> struct char_traits {
+ void assign() { wmemset(); }
+};
--- /dev/null
+// PR c++/119154
+// { dg-additional-options "-fmodules" }
+
+template <typename> struct char_traits {
+ void assign();
+};
+
+void foo(char_traits<wchar_t> s) {
+ s.assign();
+}
+
+import xstd;
+
+// Lazy loading at EOF of a gnu_inline declaration should not ICE.
+++ /dev/null
-// PR c++/119154
-// { dg-additional-options "-fmodules" }
-// { dg-module-cmi foo }
-
-export module foo;
-extern "C++" inline __attribute__((__gnu_inline__)) void bar() {}
+++ /dev/null
-// PR c++/119154
-// { dg-module-do link }
-// { dg-additional-options "-fmodules" }
-
-void bar();
-import foo;
-
-int main() {
- bar();
-}