Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -4747,6 +4747,15 @@ } if (MD && ClassExported) { + // Instantiations of constexpr template member functions are eagerly + // emitted by MarkFunctionReferenced, and also emitted by + // InstantiateClassMembers. To avoid emitting the function twice, pass + // OdrUse = false to MarkFunctionReferenced and don't pass them to the + // consumer (PR21718). + bool OdrUse = !MD->isConstexpr() || + TSK != TSK_ExplicitInstantiationDefinition || + !MD->getInstantiatedFromMemberFunction(); + if (MD->isUserProvided()) { // Instantiate non-default class member functions ... @@ -4756,7 +4765,7 @@ if (TSK == TSK_ImplicitInstantiation && !ClassAttr->isInherited()) continue; - S.MarkFunctionReferenced(Class->getLocation(), MD); + S.MarkFunctionReferenced(Class->getLocation(), MD, OdrUse); // The function will be passed to the consumer when its definition is // encountered. @@ -4767,11 +4776,12 @@ // defaulted methods, and the copy and move assignment operators. The // latter are exported even if they are trivial, because the address of // an operator can be taken and should compare equal accross libraries. - S.MarkFunctionReferenced(Class->getLocation(), MD); + S.MarkFunctionReferenced(Class->getLocation(), MD, OdrUse); // There is no later point when we will see the definition of this // function, so pass it to the consumer now. - S.Consumer.HandleTopLevelDecl(DeclGroupRef(MD)); + if (OdrUse) + S.Consumer.HandleTopLevelDecl(DeclGroupRef(MD)); } } } Index: test/CodeGenCXX/dllexport.cpp =================================================================== --- test/CodeGenCXX/dllexport.cpp +++ test/CodeGenCXX/dllexport.cpp @@ -615,6 +615,21 @@ struct __declspec(dllexport) ExportedDerivedClass : NonExportedBaseClass {}; // M32-DAG: weak_odr dllexport x86_thiscallcc void @"\01??1ExportedDerivedClass@@UAE@XZ" +// Do not assert about generating code for constexpr functions twice during explicit instantiation (PR21718). +template struct ExplicitInstConstexprMembers { + // Copy assignment operator + // M32-DAG: define weak_odr dllexport x86_thiscallcc dereferenceable(1) %struct.ExplicitInstConstexprMembers* @"\01??4?$ExplicitInstConstexprMembers@X@@QAEAAU0@ABU0@@Z" + + constexpr ExplicitInstConstexprMembers() {} + // M32-DAG: define weak_odr dllexport x86_thiscallcc %struct.ExplicitInstConstexprMembers* @"\01??0?$ExplicitInstConstexprMembers@X@@QAE@XZ" + + ExplicitInstConstexprMembers(const ExplicitInstConstexprMembers&) = default; + // M32-DAG: define weak_odr dllexport x86_thiscallcc %struct.ExplicitInstConstexprMembers* @"\01??0?$ExplicitInstConstexprMembers@X@@QAE@ABU0@@Z" + + constexpr int f() const { return 42; } + // M32-DAG: define weak_odr dllexport x86_thiscallcc i32 @"\01?f@?$ExplicitInstConstexprMembers@X@@QBEHXZ" +}; +template struct __declspec(dllexport) ExplicitInstConstexprMembers; //===----------------------------------------------------------------------===// // Classes with template base classes