zoecarver created this revision. zoecarver added a reviewer: rsmith. zoecarver requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
Because MSVC will not treat the definition of a static data member as part of the declaration, it will not get instantiated. This means Clang won't diagnose instantiation errors until the data member itself is used. Note: this only happens for constexpr data members because they are the only ones that are marked as implicitly inline and therefore not part of the declaration. Refs: https://llvm.org/PR49618 Refs: https://github.com/apple/swift/pull/35962 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D98904 Files: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/test/SemaCXX/windows-data-member-instantiation-errors.cpp Index: clang/test/SemaCXX/windows-data-member-instantiation-errors.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/windows-data-member-instantiation-errors.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -verify -std=c++11 %s +// RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -verify -std=c++17 %s + +template <class T> +struct GetTypeValue { + // expected-error@+1{{type 'int' cannot be used prior to '::' because it has no members}} + static constexpr const bool value = T::value; +}; + +using Invalid = GetTypeValue<int>; + +// expected-note@+1{{in instantiation of template class 'GetTypeValue<int>' requested here}} +void test(Invalid) {} + +// expected-note@+2{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const S &' for 1st argument}} +// expected-note@+1{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'S &&' for 1st argument}} +struct S {}; + +template <class T> +struct MakeS { + // expected-error@+1{{no viable conversion from 'int' to 'const S'}} + static constexpr const S value = T(); +}; + +using Invalid2 = MakeS<int>; + +// expected-note@+1{{in instantiation of template class 'MakeS<int>' requested here}} +void test2(Invalid2) {} Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5111,7 +5111,11 @@ InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs); } else if (InstantiatingSpecFromTemplate || (OldVar->isInline() && OldVar->isThisDeclarationADefinition() && - !NewVar->isThisDeclarationADefinition())) { + !NewVar->isThisDeclarationADefinition() && + // On Microsoft's ABI, the new var's initializer will be part of + // the definition. But, we still want to instanciate it so that + // we can catch instantiation errors. + !Context.getTargetInfo().getCXXABI().isMicrosoft())) { // Delay instantiation of the initializer for variable template // specializations or inline static data members until a definition of the // variable is needed.
Index: clang/test/SemaCXX/windows-data-member-instantiation-errors.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/windows-data-member-instantiation-errors.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -verify -std=c++11 %s +// RUN: %clang_cc1 -triple x86_64-unknown-windows-msvc -verify -std=c++17 %s + +template <class T> +struct GetTypeValue { + // expected-error@+1{{type 'int' cannot be used prior to '::' because it has no members}} + static constexpr const bool value = T::value; +}; + +using Invalid = GetTypeValue<int>; + +// expected-note@+1{{in instantiation of template class 'GetTypeValue<int>' requested here}} +void test(Invalid) {} + +// expected-note@+2{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const S &' for 1st argument}} +// expected-note@+1{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'int' to 'S &&' for 1st argument}} +struct S {}; + +template <class T> +struct MakeS { + // expected-error@+1{{no viable conversion from 'int' to 'const S'}} + static constexpr const S value = T(); +}; + +using Invalid2 = MakeS<int>; + +// expected-note@+1{{in instantiation of template class 'MakeS<int>' requested here}} +void test2(Invalid2) {} Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5111,7 +5111,11 @@ InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs); } else if (InstantiatingSpecFromTemplate || (OldVar->isInline() && OldVar->isThisDeclarationADefinition() && - !NewVar->isThisDeclarationADefinition())) { + !NewVar->isThisDeclarationADefinition() && + // On Microsoft's ABI, the new var's initializer will be part of + // the definition. But, we still want to instanciate it so that + // we can catch instantiation errors. + !Context.getTargetInfo().getCXXABI().isMicrosoft())) { // Delay instantiation of the initializer for variable template // specializations or inline static data members until a definition of the // variable is needed.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits