diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h index adaa75cc6c348..ab3ab9b52ac12 100644 --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -1518,11 +1518,20 @@ class opt [](const typename ParserClass::parser_data_type &) {}; }; -extern template class opt; -extern template class opt; -extern template class opt; -extern template class opt; -extern template class opt; +#if !defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) || \ + !(defined(_MSC_VER) && !defined(__clang__)) +// Only instantiate opt when not building a Windows DLL with MSVC. +// When exporting opt, MSVC cl implicitly exports symbols for +// std::basic_string through transitive inheritance via std::string. These +// symbols may appear in other TUs with different linkage, leading to duplicate +// symbol conflicts. +extern template class LLVM_TEMPLATE_ABI opt; +#endif + +extern template class LLVM_TEMPLATE_ABI opt; +extern template class LLVM_TEMPLATE_ABI opt; +extern template class LLVM_TEMPLATE_ABI opt; +extern template class LLVM_TEMPLATE_ABI opt; //===----------------------------------------------------------------------===// // Default storage class definition: external storage. This implementation diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index d5c3cba13e030..c9541289aa3bd 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -68,11 +68,21 @@ template class LLVM_EXPORT_TEMPLATE basic_parser; template class LLVM_EXPORT_TEMPLATE basic_parser; template class LLVM_EXPORT_TEMPLATE basic_parser; -template class opt; -template class opt; -template class opt; -template class opt; -template class opt; +#if !defined(LLVM_ENABLE_LLVM_EXPORT_ANNOTATIONS) || \ + !(defined(_MSC_VER) && !defined(__clang__)) +// Only instantiate opt when not building a Windows DLL with MSVC. +// When exporting opt, MSVC cl implicitly exports symbols for +// std::basic_string through transitive inheritance via std::string. These +// symbols may appear in other TUs with different linkage, leading to duplicate +// symbol conflicts. +template class LLVM_EXPORT_TEMPLATE opt; +#endif + +template class LLVM_EXPORT_TEMPLATE opt; +template class LLVM_EXPORT_TEMPLATE opt; +template class LLVM_EXPORT_TEMPLATE opt; +template class LLVM_EXPORT_TEMPLATE opt; + } // namespace cl } // namespace llvm @@ -95,6 +105,15 @@ void parser::anchor() {} void parser::anchor() {} void parser::anchor() {} +// These anchor functions instantiate opt and reference its virtual +// destructor to ensure MSVC exports the corresponding vtable and typeinfo when +// building a Windows DLL. Without an explicit reference, MSVC may omit the +// instantiation at link time even if it is marked DLL-export. +void opt_bool_anchor() { opt anchor{""}; } +void opt_char_anchor() { opt anchor{""}; } +void opt_int_anchor() { opt anchor{""}; } +void opt_unsigned_anchor() { opt anchor{""}; } + //===----------------------------------------------------------------------===// const static size_t DefaultPad = 2;