bolshakov-a updated this revision to Diff 547589. bolshakov-a added a comment.
Rebased. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D140996/new/ https://reviews.llvm.org/D140996 Files: clang-tools-extra/clangd/DumpAST.cpp clang-tools-extra/clangd/FindTarget.cpp clang/docs/ReleaseNotes.rst clang/include/clang/AST/ODRHash.h clang/include/clang/AST/PropertiesBase.td clang/include/clang/AST/RecursiveASTVisitor.h clang/include/clang/AST/TemplateArgumentVisitor.h clang/include/clang/AST/TemplateBase.h clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/AST/ASTContext.cpp clang/lib/AST/ASTImporter.cpp clang/lib/AST/ASTStructuralEquivalence.cpp clang/lib/AST/Decl.cpp clang/lib/AST/ItaniumMangle.cpp clang/lib/AST/MicrosoftMangle.cpp clang/lib/AST/ODRHash.cpp clang/lib/AST/StmtProfile.cpp clang/lib/AST/TemplateBase.cpp clang/lib/AST/TypeLoc.cpp clang/lib/CodeGen/CGDebugInfo.cpp clang/lib/Index/USRGeneration.cpp clang/lib/Sema/SemaLookup.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp clang/lib/Sema/SemaTemplateVariadic.cpp clang/lib/Sema/TreeTransform.h clang/lib/Serialization/ASTReader.cpp clang/lib/Serialization/ASTWriter.cpp clang/test/CXX/drs/dr12xx.cpp clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp clang/test/CodeGenCXX/mangle-ms-templates.cpp clang/test/CodeGenCXX/mangle-template.cpp clang/test/CodeGenCXX/template-arguments.cpp clang/test/Index/USR/structural-value-tpl-arg.cpp clang/test/Modules/odr_hash.cpp clang/test/SemaCXX/warn-bool-conversion.cpp clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp clang/tools/libclang/CIndex.cpp clang/tools/libclang/CXCursor.cpp clang/www/cxx_status.html lldb/include/lldb/lldb-enumerations.h lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -7218,6 +7218,9 @@ case clang::TemplateArgument::Pack: return eTemplateArgumentKindPack; + + case clang::TemplateArgument::StructuralValue: + return eTemplateArgumentKindStructuralValue; } llvm_unreachable("Unhandled clang::TemplateArgument::ArgKind"); } Index: lldb/include/lldb/lldb-enumerations.h =================================================================== --- lldb/include/lldb/lldb-enumerations.h +++ lldb/include/lldb/lldb-enumerations.h @@ -856,6 +856,7 @@ eTemplateArgumentKindExpression, eTemplateArgumentKindPack, eTemplateArgumentKindNullPtr, + eTemplateArgumentKindStructuralValue, }; /// Type of match to be performed when looking for a formatter for a data type. Index: clang/www/cxx_status.html =================================================================== --- clang/www/cxx_status.html +++ clang/www/cxx_status.html @@ -626,13 +626,21 @@ </tr> <!-- Rapperswil papers --> <tr> - <td rowspan="2">Class types as non-type template parameters</td> + <td>Class types as non-type template parameters</td> <td><a href="https://wg21.link/p0732r2">P0732R2</a></td> - <td rowspan="2" class="partial" align="center">Partial</td> + <td class="full" align="center">Clang 12</td> + </tr> + <tr> <!-- from Belfast --> + <td>Generalized non-type template parameters of scalar type</td> + <td><a href="https://wg21.link/p1907r1">P1907R1</a></td> + <td class="partial" align="center"> + <details> + <summary>Clang 17 (Partial)</summary> + Reference type template arguments referring to instantiation-dependent objects and subobjects + (i.e. declared inside a template but neither type- nor value-dependent) aren't fully supported. + </details> + </td> </tr> - <tr> <!-- from Belfast --> - <td><a href="https://wg21.link/p1907r1">P1907R1</a></td> - </tr> <tr> <td>Destroying operator delete</td> <td><a href="https://wg21.link/p0722r3">P0722R3</a></td> Index: clang/tools/libclang/CXCursor.cpp =================================================================== --- clang/tools/libclang/CXCursor.cpp +++ clang/tools/libclang/CXCursor.cpp @@ -1463,6 +1463,9 @@ return CXTemplateArgumentKind_NullPtr; case TemplateArgument::Integral: return CXTemplateArgumentKind_Integral; + case TemplateArgument::StructuralValue: + // FIXME: Expose these values. + return CXTemplateArgumentKind_Invalid; case TemplateArgument::Template: return CXTemplateArgumentKind_Template; case TemplateArgument::TemplateExpansion: Index: clang/tools/libclang/CIndex.cpp =================================================================== --- clang/tools/libclang/CIndex.cpp +++ clang/tools/libclang/CIndex.cpp @@ -1574,6 +1574,11 @@ return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); return false; + case TemplateArgument::StructuralValue: + if (Expr *E = TAL.getSourceStructuralValueExpression()) + return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); + return false; + case TemplateArgument::NullPtr: if (Expr *E = TAL.getSourceNullPtrExpression()) return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); Index: clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp =================================================================== --- clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp +++ clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp @@ -8,8 +8,8 @@ // floating-point arguments template<float> struct Float {}; -using F1 = Float<1.0f>; // FIXME expected-error {{sorry}} -using F1 = Float<2.0f / 2>; // FIXME expected-error {{sorry}} +using F1 = Float<1.0f>; +using F1 = Float<2.0f / 2>; struct S { int n[3]; } s; // expected-note 1+{{here}} union U { int a, b; } u; @@ -17,24 +17,28 @@ // pointers to subobjects template<int *> struct IntPtr {}; -using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}} -using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}} +using IPn = IntPtr<&n + 1>; +using IPn = IntPtr<&n + 1>; -using IP2 = IntPtr<&s.n[2]>; // FIXME expected-error {{refers to subobject}} -using IP2 = IntPtr<s.n + 2>; // FIXME expected-error {{refers to subobject}} +using IPn2 = IntPtr<&n + 2>; // expected-error {{not a constant expression}} expected-note {{cannot refer to element 2 of non-array object}} -using IP3 = IntPtr<&s.n[3]>; // FIXME expected-error {{refers to subobject}} -using IP3 = IntPtr<s.n + 3>; // FIXME expected-error {{refers to subobject}} +using IP2 = IntPtr<&s.n[2]>; +using IP2 = IntPtr<s.n + 2>; + +using IP3 = IntPtr<&s.n[3]>; +using IP3 = IntPtr<s.n + 3>; + +using IP5 = IntPtr<&s.n[5]>; // expected-error {{not a constant expression}} expected-note {{cannot refer to element 5 of array of 3 elements}} template<int &> struct IntRef {}; -using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} -using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} +using IRn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} +using IRn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}} -using IP2 = IntRef<s.n[2]>; // FIXME expected-error {{refers to subobject}} -using IP2 = IntRef<*(s.n + 2)>; // FIXME expected-error {{refers to subobject}} +using IR2 = IntRef<s.n[2]>; +using IR2 = IntRef<*(s.n + 2)>; -using IP3 = IntRef<s.n[3]>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} -using IP3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} +using IR3 = IntRef<s.n[3]>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} +using IR3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}} // classes template<S> struct Struct {}; @@ -48,12 +52,12 @@ // miscellaneous scalar types template<_Complex int> struct ComplexInt {}; -using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}} -using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}} +using CI = ComplexInt<1 + 3i>; +using CI = ComplexInt<3i + 1>; template<_Complex float> struct ComplexFloat {}; -using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}} -using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}} +using CF = ComplexFloat<1.0f + 3.0fi>; +using CF = ComplexFloat<3.0fi + 1.0f>; namespace ClassNTTP { struct A { // expected-note 2{{candidate}} Index: clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp =================================================================== --- clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -2,7 +2,7 @@ template<typename T, T val> struct A {}; -template<typename T, typename U> constexpr bool is_same = false; // expected-note +{{here}} +template<typename T, typename U> constexpr bool is_same = false; template<typename T> constexpr bool is_same<T, T> = true; namespace String { @@ -84,34 +84,32 @@ constexpr int B::*b = &B::b; constexpr int C::*cb = b; constexpr int D::*db = b; - constexpr int E::*ecb = cb; // expected-note +{{here}} - constexpr int E::*edb = db; // expected-note +{{here}} + constexpr int E::*ecb = cb; + constexpr int E::*edb = db; constexpr int E::*e = &E::e; constexpr int D::*de = (int D::*)e; constexpr int C::*ce = (int C::*)e; - constexpr int B::*bde = (int B::*)de; // expected-note +{{here}} - constexpr int B::*bce = (int B::*)ce; // expected-note +{{here}} + constexpr int B::*bde = (int B::*)de; + constexpr int B::*bce = (int B::*)ce; - // FIXME: This should all be accepted, but we don't yet have a representation - // nor mangling for this form of template argument. using Ab = A<int B::*, b>; using Ab = A<int B::*, &B::b>; - using Abce = A<int B::*, bce>; // expected-error {{not supported}} - using Abde = A<int B::*, bde>; // expected-error {{not supported}} - static_assert(!is_same<Ab, Abce>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Ab, Abde>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Abce, Abde>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}} - static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, ""); // expected-error {{undeclared}} expected-error {{not supported}} + using Abce = A<int B::*, bce>; + using Abde = A<int B::*, bde>; + static_assert(!is_same<Ab, Abce>, ""); + static_assert(!is_same<Ab, Abde>, ""); + static_assert(!is_same<Abce, Abde>, ""); + static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, ""); using Ae = A<int E::*, e>; using Ae = A<int E::*, &E::e>; - using Aecb = A<int E::*, ecb>; // expected-error {{not supported}} - using Aedb = A<int E::*, edb>; // expected-error {{not supported}} - static_assert(!is_same<Ae, Aecb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Ae, Aedb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}} - static_assert(!is_same<Aecb, Aedb>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}} - static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, ""); // expected-error {{undeclared}} expected-error {{not supported}} + using Aecb = A<int E::*, ecb>; + using Aedb = A<int E::*, edb>; + static_assert(!is_same<Ae, Aecb>, ""); + static_assert(!is_same<Ae, Aedb>, ""); + static_assert(!is_same<Aecb, Aedb>, ""); + static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, ""); using An = A<int E::*, nullptr>; using A0 = A<int E::*, (int E::*)0>; @@ -205,9 +203,9 @@ struct Y : X {}; void type_affects_identity(B<&X::n>) {} - void type_affects_identity(B<(int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}} + void type_affects_identity(B<(int Y::*)&X::n>) {} void type_affects_identity(B<(const int X::*)&X::n>) {} - void type_affects_identity(B<(const int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}} + void type_affects_identity(B<(const int Y::*)&X::n>) {} // A case where we need to do auto-deduction, and check whether the // resulting dependent types match during partial ordering. These Index: clang/test/SemaCXX/warn-bool-conversion.cpp =================================================================== --- clang/test/SemaCXX/warn-bool-conversion.cpp +++ clang/test/SemaCXX/warn-bool-conversion.cpp @@ -186,6 +186,7 @@ } } +#if __cplusplus < 201703L namespace Template { // FIXME: These cases should not warn. template<int *p> void f() { if (p) {} } // expected-warning 2{{will always evaluate to 'true'}} expected-cxx11-warning {{implicit conversion of nullptr}} @@ -205,3 +206,4 @@ #endif template void h<d>(); } +#endif // __cplusplus < 201703L Index: clang/test/Modules/odr_hash.cpp =================================================================== --- clang/test/Modules/odr_hash.cpp +++ clang/test/Modules/odr_hash.cpp @@ -13,8 +13,8 @@ // RUN: cat %s >> %t/Inputs/second.h // Test that each header can compile -// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++1z %t/Inputs/first.h -// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++1z %t/Inputs/second.h +// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++20 %t/Inputs/first.h +// RUN: %clang_cc1 -fsyntax-only -x c++ -std=c++20 %t/Inputs/second.h // Build module map file // RUN: echo "module FirstModule {" >> %t/Inputs/module.map @@ -25,7 +25,7 @@ // RUN: echo "}" >> %t/Inputs/module.map // Run test -// RUN: %clang_cc1 -triple x86_64-linux-gnu -x c++ -std=c++1z \ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -x c++ -std=c++20 \ // RUN: -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/cache \ // RUN: -I%t/Inputs -verify %s @@ -2093,6 +2093,193 @@ S21 s21; #endif +#if defined(FIRST) +struct S22 { + template <double> void f(){}; + template <> void f<1.5>(){}; +}; +#elif defined(SECOND) +struct S22 { + template <double> void f(){}; + template <> void f<1.7>(){}; +}; +#else +S22 s22; +// expected-error@second.h:* {{'TemplateArgument::S22' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with 1.700000e+00 for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with 1.500000e+00 for 1st template argument}} +#endif + +#if defined(FIRST) +struct S23 { + template <double> void f(){}; + template <> void f<2.7>(){}; +}; +#elif defined(SECOND) +struct S23 { + template <double> void f(){}; + template <> void f<2.7>(){}; +}; +#else +S23 s23; +#endif + +#if defined(FIRST) || defined(SECOND) +struct Composite { + int n1[4]; + int n2[4]; +}; +extern Composite composite; +#endif + +#if defined(FIRST) +struct S24 { + template <int&> void f(){}; + template <> void f<composite.n1[1]>(){}; +}; +#elif defined(SECOND) +struct S24 { + template <int&> void f(){}; + template <> void f<composite.n1[2]>(){}; +}; +#else +S24 s24; +// expected-error@second.h:* {{'TemplateArgument::S24' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with composite.n1[2] for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with composite.n1[1] for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S25 { + template <int&> void f(); + template <> void f<composite.n1[2]>(); +}; +#else +S25 s25; +#endif + +#if defined(FIRST) +struct S26 { + template <int*> void f(){}; + template <> void f<&composite.n1[4]>(){}; // Past-the-end pointer. +}; +#elif defined(SECOND) +struct S26 { + template <int*> void f(){}; + template <> void f<&composite.n2[0]>(){}; +}; +#else +S26 s26; +// expected-error@second.h:* {{'TemplateArgument::S26' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with &composite.n2[0] for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with &composite.n1[4] for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +union Union { + int i1; + int i2; +}; +extern Union u; +#endif + +#if defined(FIRST) +struct S27 { + template <int&> void f(){}; + template <> void f<u.i1>(){}; +}; +#elif defined(SECOND) +struct S27 { + template <int&> void f(){}; + template <> void f<u.i2>(){}; +}; +#else +S27 s27; +// expected-error@second.h:* {{'TemplateArgument::S27' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with u.i2 for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with u.i1 for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S28 { + template <int&> void f(){}; + template <> void f<u.i1>(){}; +}; +#else +S28 s28; +#endif + +#if defined(FIRST) || defined(SECOND) +struct A { + int a; +}; +struct B : A {}; +struct C : A {}; +struct D : B, C {}; +#endif + +#if defined(FIRST) +struct S29 { + template <int D::*> void f(){}; + template <> void f<(int D::*)(int B::*)&A::a>(){}; +}; +#elif defined(SECOND) +struct S29 { + template <int D::*> void f(){}; + template <> void f<(int D::*)(int C::*)&A::a>(){}; +}; +#else +S29 s29; +// expected-error@second.h:* {{'TemplateArgument::S29' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with &A::a for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with &A::a for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S30 { + template <int D::*> void f(){}; + template <> void f<(int D::*)(int B::*)&A::a>(){}; +}; +#else +S30 s30; +#endif + +#if defined(FIRST) +struct S31 { + template <auto*> void f(){}; + template <> void f<&composite.n1[2]>(){}; +}; +#elif defined(SECOND) +struct S31 { + template <auto*> void f(){}; + template <> void f<(void*)&composite.n1[2]>(){}; +}; +#else +S31 s31; +// expected-error@second.h:* {{'TemplateArgument::S31' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with &composite.n1[2] for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with &composite.n1[2] for 1st template argument}} +#endif + +#if defined(FIRST) +struct S32 { + template <int*> void f(){}; + template <> void f<__builtin_constant_p(0) ? (int*)1 : (int*)1>(){}; +}; +#elif defined(SECOND) +struct S32 { + template <int*> void f(){}; + template <> void f<__builtin_constant_p(0) ? (int*)2 : (int*)2>(){}; +}; +#else +S32 s32; +// expected-error@second.h:* {{'TemplateArgument::S32' has different definitions in different modules; first difference is definition in module 'SecondModule' found method 'f' with (int *)2 for 1st template argument}} +// expected-note@first.h:* {{but in 'FirstModule' found method 'f' with (int *)1 for 1st template argument}} +#endif + +#if defined(FIRST) || defined(SECOND) +struct S33 { + template <int*> void f(){}; + template <> void f<__builtin_constant_p(0) ? (int*)1 : (int*)1>(){}; +}; +#else +S33 s33; +#endif + #define DECLS \ OneClass<int> a; \ OneInt<1> b; \ Index: clang/test/Index/USR/structural-value-tpl-arg.cpp =================================================================== --- /dev/null +++ clang/test/Index/USR/structural-value-tpl-arg.cpp @@ -0,0 +1,23 @@ +// RUN: c-index-test -test-load-source-usrs local -std=c++20 -- %s | FileCheck %s + +// Check USRs of template specializations with structural NTTP values. + +template <auto> struct Tpl{}; + +struct { + int n; +} s; + +void fn1(Tpl<1.5>); +// CHECK: fn1#$@S@Tpl>#Sd[[#HASH:]]# +void fn2(Tpl<1.7>); +// CHECK-NOT: [[#HASH]] +void fn1(Tpl<1.5>) {} +// CHECK: fn1#$@S@Tpl>#Sd[[#HASH]]# + +void fn(Tpl<&s.n>); +// CHECK: #S*I[[#HASH:]]# +void fn(Tpl<(void*)&s.n>); +// CHECK: #S*v[[#HASH]]# +void fn(Tpl<&s.n>) {} +// CHECK: #S*I[[#HASH]]# Index: clang/test/CodeGenCXX/template-arguments.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/template-arguments.cpp @@ -0,0 +1,102 @@ +// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - -triple x86_64-linux -DCONSTEXPR= | FileCheck %s +// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - -triple x86_64-linux -DCONSTEXPR=constexpr | FileCheck %s --check-prefix=CONST + +template<typename T> CONSTEXPR T id(T v) { return v; } +template<auto V> auto value = id(V); + +// CHECK: call {{.*}} @_Z2idIiET_S0_(i32 noundef 1) +// CONST: @_Z5valueILi1EE = weak_odr {{.*}} i32 1, +template int value<1>; + +// CHECK: call {{.*}} @_Z2idIyET_S0_(i64 noundef -1) +// CONST: @_Z5valueILy18446744073709551615EE = weak_odr {{.*}} i64 -1, +template unsigned long long value<-1ULL>; + +// CHECK: call {{.*}} @_Z2idIfET_S0_(float noundef 1.000000e+00) +// CONST: @_Z5valueILf3f800000EE = weak_odr {{.*}} float 1.000000e+00, +template float value<1.0f>; +// CHECK: call {{.*}} @_Z2idIdET_S0_(double noundef 1.000000e+00) +// CONST: @_Z5valueILd3ff0000000000000EE = weak_odr {{.*}} double 1.000000e+00, +template double value<1.0>; + +enum E{ E1, E2}; + +// CHECK: call {{.*}} @_Z2idI1EET_S1_(i32 noundef 1) +// CONST: @_Z5valueIL1E1EE = weak_odr {{.*}} i32 1, +template E value<E2>; + +int n; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @n) +// CONST: @_Z5valueIXadL_Z1nEEE = weak_odr {{.*}} ptr @n, +template int *value<&n>; + +struct A { int a[3]; } a; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @a) +// CONST: @_Z5valueIXadsoiL_Z1aEEEE = weak_odr {{.*}} ptr @a, +template int *value<&a.a[0]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef getelementptr (i8, ptr @a, i64 4)) +// CONST: @_Z5valueIXadsoiL_Z1aE4EEE = weak_odr {{.*}} ptr getelementptr (i8, ptr @a, i64 4), +template int *value<&a.a[1]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef getelementptr (i8, ptr @a, i64 8)) +// CONST: @_Z5valueIXadsoiL_Z1aE8EEE = weak_odr {{.*}} ptr getelementptr (i8, ptr @a, i64 8), +template int *value<&a.a[2]>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef getelementptr (i8, ptr @a, i64 12)) +// CONST: @_Z5valueIXadsoiL_Z1aE12pEEE = weak_odr {{.*}} ptr getelementptr (i8, ptr @a, i64 12), +template int *value<&a.a[3]>; + +union U { + int x, y; + union { + int x, y; + } internal; +} u; + +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @u) +// CONST: @_Z5valueIXadsoiL_Z1uE_EEE = weak_odr {{.*}} ptr @u, +template int *value<&u.x>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @u) +// CONST: @_Z5valueIXadsoiL_Z1uE_0EEE = weak_odr {{.*}} ptr @u, +template int *value<&u.y>; +// CHECK: call {{.*}} @_Z2idIPiET_S1_(ptr noundef @u) +// CONST: @_Z5valueIXadsoiL_Z1uE_1_0EEE = weak_odr {{.*}} ptr @u, +template int *value<&u.internal.y>; + +struct B { int x, y; }; +// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 0) +// CONST: @_Z5valueIXadL_ZN1B1xEEEE = weak_odr {{.*}} i64 0, +template int B::*value<&B::x>; +// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 4) +// CONST: @_Z5valueIXadL_ZN1B1yEEEE = weak_odr {{.*}} i64 4, +template int B::*value<&B::y>; + +struct C : A, B { int z; }; +// CHECK: call {{.*}} @_Z2idIM1CiET_S2_(i64 12) +// CONST: @_Z5valueIXmcM1CiadL_ZN1B1xEE12EEE = weak_odr {{.*}} i64 12, +template int C::*value<(int C::*)&B::x>; +// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 8) +// CONST: @_Z5valueIXmcM1BiadL_ZN1C1zEEn12EEE = weak_odr {{.*}} i64 8, +template int B::*value<(int B::*)&C::z>; + +// CHECK: store i32 1, +// CHECK: store i32 2, +// CHECK: load i64, +// CHECK: call {{.*}} @_Z2idICiET_S1_(i64 noundef % +// CONST: @_Z5valueIXtlCiLi1ELi2EEEE = weak_odr {{.*}} { i32, i32 } { i32 1, i32 2 }, +template _Complex int value<1 + 2j>; + +// CHECK: store float 1.000000e+00, +// CHECK: store float 2.000000e+00, +// CHECK: load <2 x float>, +// CHECK: call {{.*}} @_Z2idICfET_S1_(<2 x float> noundef % +// CONST: @_Z5valueIXtlCfLf3f800000ELf40000000EEEE = weak_odr {{.*}} { float, float } { float 1.000000e+00, float 2.000000e+00 }, +template _Complex float value<1.0f + 2.0fj>; + +using V3i __attribute__((ext_vector_type(3))) = int; +// CHECK: call {{.*}} @_Z2idIDv3_iET_S1_(<3 x i32> noundef <i32 1, i32 2, i32 3>) +// CONST: @_Z5valueIXtlDv3_iLi1ELi2ELi3EEEE = weak_odr {{.*}} <3 x i32> <i32 1, i32 2, i32 3> +template V3i value<V3i{1, 2, 3}>; + +using V3f [[gnu::vector_size(12)]] = float; +// CHECK: call {{.*}} @_Z2idIDv3_fET_S1_(<3 x float> noundef <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00>) +// CONST: @_Z5valueIXtlDv3_fLf3f800000ELf40000000ELf40400000EEEE = weak_odr {{.*}} <3 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00> +template V3f value<V3f{1, 2, 3}>; Index: clang/test/CodeGenCXX/mangle-template.cpp =================================================================== --- clang/test/CodeGenCXX/mangle-template.cpp +++ clang/test/CodeGenCXX/mangle-template.cpp @@ -226,6 +226,16 @@ namespace cxx20 { template<auto> struct A {}; template<typename T, T V> struct B {}; + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AILf3f800000EEE( + void f(A<1.0f>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AILd3ff0000000000000EEE( + void f(A<1.0>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AILe3fff8000000000000000EEE( + void f(A<1.0l>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXtlCiLi0ELi1EEEEE( + void f(A<1i>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXtlCdLd0000000000000000ELd3ff0000000000000EEEEE( + void f(A<1.0i>) {} int x; // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1xEEEEE( @@ -245,7 +255,24 @@ // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKvXadL_ZNS_1xEEEEE( void f(B<const void*, (const void*)&x>) {} - struct Q { int x; }; + struct Q { int x; } q; + + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadsoiL_ZNS_1qEEEEEE( + void f(A<&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPiXadsoiL_ZNS_1qEEEEEE( + void f(B<int*, &q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadsoKiL_ZNS_1qEEEEEE( + void f(A<(const int*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKiXadsoS1_L_ZNS_1qEEEEEE + void f(B<const int*, (const int*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPvadsoiL_ZNS_1qEEEEEE( + void f(A<(void*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPvXadsoiL_ZNS_1qEEEEEE( + void f(B<void*, (void*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPKvadsoiL_ZNS_1qEEEEEE( + void f(A<(const void*)&q.x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKvXadsoiL_ZNS_1qEEEEEE( + void f(B<const void*, (const void*)&q.x>) {} // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1Q1xEEEEE( void f(A<&Q::x>) {} @@ -255,6 +282,17 @@ void f(A<(const int Q::*)&Q::x>) {} // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1QEKiXadL_ZNS1_1xEEEEE( void f(B<const int Q::*, (const int Q::*)&Q::x>) {} + + struct R : Q {}; + + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXmcMNS_1REiadL_ZNS_1Q1xEEEEEE( + void f(A<(int R::*)&Q::x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1REiXmcS2_adL_ZNS_1Q1xEEEEEE( + void f(B<int R::*, (int R::*)&Q::x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXmcMNS_1REKiadL_ZNS_1Q1xEEEEEE( + void f(A<(const int R::*)&Q::x>) {} + // CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1REKiXmcS3_adL_ZNS_1Q1xEEEEEE( + void f(B<const int R::*, (const int R::*)&Q::x>) {} } #endif Index: clang/test/CodeGenCXX/mangle-ms-templates.cpp =================================================================== --- clang/test/CodeGenCXX/mangle-ms-templates.cpp +++ clang/test/CodeGenCXX/mangle-ms-templates.cpp @@ -2,6 +2,21 @@ // RUN: %clang_cc1 -std=c++11 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s // RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=i386-pc-win32 | FileCheck %s // RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s +// RUN: %clang_cc1 -std=c++20 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix CXX20-X64 %s + +// Check that array-to-pointer decay is mangled as the underlying declaration. +extern const char arr[4] = "foo"; +template<const char*> struct Decay1 {}; +// CHECK: "?decay1@@3U?$Decay1@$1?arr@@3QBDB@@A" +Decay1<arr> decay1; +#if __cplusplus >= 201702L +// Note that this mangling approach can lead to collisions. +template<const void*> struct Decay2 {}; +// CXX20-X64: "?decay2a@@3U?$Decay2@$1?arr@@3QBDB@@A" +Decay2<(const void*)arr> decay2a; +// CXX20-X64: "?decay2b@@3U?$Decay2@$1?arr@@3QBDB@@A" +Decay2<(const void*)&arr> decay2b; +#endif template<typename T> class Class { @@ -327,3 +342,36 @@ // X64: define {{.*}} @"?fun_uint128@@YAXU?$UInt128@$0DPPPPPPPPPPPPPPPAAAAAAAAAAAAAAAB@@@@Z"( void fun_uint128(UInt128<(unsigned __int128)9223372036854775807 * (unsigned __int128)9223372036854775807>) {} #endif + +#if __cplusplus >= 202002L + +template<float> struct Float {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$Float@$ADPIAAAAA@@@@Z"( +void f(Float<1.0f>) {} +template<auto> struct Auto {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$Auto@$MMADPIAAAAA@@@@Z"( +void f(Auto<1.0f>) {} + +struct S2 { + int arr[2][3]; + int i; + void fn(); +} s2; + +template<int&> struct TplSubobjectRef {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplSubobjectRef@$CC61?s2@@3US2@@Aarr@@00@01@@@@Z"( +void f(TplSubobjectRef<s2.arr[1][2]>) {} +template<int*> struct TplSubobjectPtr {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplSubobjectPtr@$CC61?s2@@3US2@@Aarr@@00@01@@@@Z"( +void f(TplSubobjectPtr<&s2.arr[1][2]>) {} + +struct Derived : S2 {}; + +template <int Derived::*> struct TplMemberPtr {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplMemberPtr@$0BI@@@@Z"( +void f(TplMemberPtr<(int Derived::*)&S2::i>) {} +template <void (Derived::*)()> struct TplMemberFnPtr {}; +// CXX20-X64: define {{.*}} @"?f@@YAXU?$TplMemberFnPtr@$1?fn@S2@@QEAAXXZ@@@Z"( +void f(TplMemberFnPtr<(void (Derived::*)())&S2::fn>) {} + +#endif Index: clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp =================================================================== --- clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp +++ clang/test/CXX/temp/temp.arg/temp.arg.nontype/p1.cpp @@ -200,7 +200,9 @@ // cxx17-note@-3 {{reinterpret_cast}} X0<__builtin_constant_p(0) ? (int*)1 : (int*)1> x0d; // precxx17-error@-1 {{non-type template argument '(int *)1' is invalid}} - // cxx17-error@-2 {{non-type template argument refers to subobject '(int *)1'}} +#if __cplusplus == 201703L + // cxx17-error@-3 {{non-type template argument refers to subobject '(int *)1'}} +#endif } #endif // CPP11ONLY Index: clang/test/CXX/drs/dr12xx.cpp =================================================================== --- clang/test/CXX/drs/dr12xx.cpp +++ clang/test/CXX/drs/dr12xx.cpp @@ -138,7 +138,7 @@ #if __cplusplus <= 201402L // expected-error@-2 {{does not refer to any declaration}} expected-note@-3 {{here}} #else - // expected-error@-4 {{refers to subobject}} + // expected-error@-4 {{bind to bit-field in converted constant expression}} #endif #if __cplusplus >= 201103L Index: clang/lib/Serialization/ASTWriter.cpp =================================================================== --- clang/lib/Serialization/ASTWriter.cpp +++ clang/lib/Serialization/ASTWriter.cpp @@ -5527,6 +5527,7 @@ case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: // FIXME: Is this right? break; Index: clang/lib/Serialization/ASTReader.cpp =================================================================== --- clang/lib/Serialization/ASTReader.cpp +++ clang/lib/Serialization/ASTReader.cpp @@ -7311,6 +7311,7 @@ case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: // FIXME: Is this right? return TemplateArgumentLocInfo(); Index: clang/lib/Sema/TreeTransform.h =================================================================== --- clang/lib/Sema/TreeTransform.h +++ clang/lib/Sema/TreeTransform.h @@ -3900,6 +3900,7 @@ case TemplateArgument::Null: case TemplateArgument::Integral: case TemplateArgument::Declaration: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::TemplateExpansion: case TemplateArgument::NullPtr: @@ -4569,7 +4570,8 @@ case TemplateArgument::Integral: case TemplateArgument::NullPtr: - case TemplateArgument::Declaration: { + case TemplateArgument::Declaration: + case TemplateArgument::StructuralValue: { // Transform a resolved template argument straight to a resolved template // argument. We get here when substituting into an already-substituted // template type argument during concept satisfaction checking. @@ -4596,9 +4598,15 @@ else if (Arg.getKind() == TemplateArgument::NullPtr) Output = TemplateArgumentLoc(TemplateArgument(NewT, /*IsNullPtr=*/true), TemplateArgumentLocInfo()); - else + else if (Arg.getKind() == TemplateArgument::Declaration) Output = TemplateArgumentLoc(TemplateArgument(NewD, NewT), TemplateArgumentLocInfo()); + else if (Arg.getKind() == TemplateArgument::StructuralValue) + Output = TemplateArgumentLoc( + TemplateArgument(getSema().Context, NewT, Arg.getAsStructuralValue()), + TemplateArgumentLocInfo()); + else + llvm_unreachable("unexpected template argument kind"); return false; } Index: clang/lib/Sema/SemaTemplateVariadic.cpp =================================================================== --- clang/lib/Sema/SemaTemplateVariadic.cpp +++ clang/lib/Sema/SemaTemplateVariadic.cpp @@ -1105,6 +1105,7 @@ case TemplateArgument::NullPtr: case TemplateArgument::Template: case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::Null: return TemplateArgumentLoc(); @@ -1155,6 +1156,7 @@ case TemplateArgument::NullPtr: case TemplateArgument::TemplateExpansion: case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::Null: return std::nullopt; Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1911,9 +1911,8 @@ } } else if (arg.getKind() == TemplateArgument::Declaration || arg.getKind() == TemplateArgument::NullPtr) { - ValueDecl *VD; if (arg.getKind() == TemplateArgument::Declaration) { - VD = arg.getAsDecl(); + ValueDecl *VD = arg.getAsDecl(); // Find the instantiation of the template argument. This is // required for nested templates. @@ -1921,21 +1920,20 @@ getSema().FindInstantiatedDecl(loc, VD, TemplateArgs)); if (!VD) return ExprError(); - } else { - // Propagate NULL template argument. - VD = nullptr; } - QualType paramType = VD ? arg.getParamTypeForDecl() : arg.getNullPtrType(); + QualType paramType = arg.getNonTypeTemplateArgumentType(); assert(!paramType.isNull() && "type substitution failed for param type"); assert(!paramType->isDependentType() && "param type still dependent"); result = SemaRef.BuildExpressionFromDeclTemplateArgument(arg, paramType, loc); refParam = paramType->isReferenceType(); } else { - result = SemaRef.BuildExpressionFromIntegralTemplateArgument(arg, loc); + QualType paramType = arg.getNonTypeTemplateArgumentType(); + result = SemaRef.BuildExpressionFromNonTypeTemplateArgument(arg, loc); + refParam = paramType->isReferenceType(); assert(result.isInvalid() || SemaRef.Context.hasSameType(result.get()->getType(), - arg.getIntegralType())); + paramType.getNonReferenceType())); } if (result.isInvalid()) Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -268,6 +268,16 @@ // All other combinations are incompatible. return DeducedTemplateArgument(); + case TemplateArgument::StructuralValue: + // If we deduced a value and a dependent expression, keep the value. + if (Y.getKind() == TemplateArgument::Expression || + (Y.getKind() == TemplateArgument::StructuralValue && + X.structurallyEquals(Y))) + return X; + + // All other combinations are incompatible. + return DeducedTemplateArgument(); + case TemplateArgument::Template: if (Y.getKind() == TemplateArgument::Template && Context.hasSameTemplateName(X.getAsTemplate(), Y.getAsTemplate())) @@ -2300,27 +2310,45 @@ Info.SecondArg = A; return Sema::TDK_NonDeducedMismatch; + case TemplateArgument::StructuralValue: + if (A.getKind() == TemplateArgument::StructuralValue && + A.structurallyEquals(P)) + return Sema::TDK_Success; + + Info.FirstArg = P; + Info.SecondArg = A; + return Sema::TDK_NonDeducedMismatch; + case TemplateArgument::Expression: if (const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(Info, P.getAsExpr())) { - if (A.getKind() == TemplateArgument::Integral) + switch (A.getKind()) { + case TemplateArgument::Integral: + case TemplateArgument::Expression: + case TemplateArgument::StructuralValue: return DeduceNonTypeTemplateArgument( - S, TemplateParams, NTTP, A.getAsIntegral(), A.getIntegralType(), - /*ArrayBound=*/false, Info, Deduced); - if (A.getKind() == TemplateArgument::NullPtr) + S, TemplateParams, NTTP, DeducedTemplateArgument(A), + A.getNonTypeTemplateArgumentType(), Info, Deduced); + + case TemplateArgument::NullPtr: return DeduceNullPtrTemplateArgument(S, TemplateParams, NTTP, A.getNullPtrType(), Info, Deduced); - if (A.getKind() == TemplateArgument::Expression) - return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP, - A.getAsExpr(), Info, Deduced); - if (A.getKind() == TemplateArgument::Declaration) + + case TemplateArgument::Declaration: return DeduceNonTypeTemplateArgument( S, TemplateParams, NTTP, A.getAsDecl(), A.getParamTypeForDecl(), Info, Deduced); - Info.FirstArg = P; - Info.SecondArg = A; - return Sema::TDK_NonDeducedMismatch; + case TemplateArgument::Null: + case TemplateArgument::Type: + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + case TemplateArgument::Pack: + Info.FirstArg = P; + Info.SecondArg = A; + return Sema::TDK_NonDeducedMismatch; + } + llvm_unreachable("Unknown template argument kind"); } // Can't deduce anything, but that's okay. @@ -2505,6 +2533,9 @@ case TemplateArgument::Integral: return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral()); + case TemplateArgument::StructuralValue: + return X.structurallyEquals(Y); + case TemplateArgument::Expression: { llvm::FoldingSetNodeID XID, YID; X.getAsExpr()->Profile(XID, Context, true); @@ -2585,9 +2616,9 @@ E); } - case TemplateArgument::Integral: { - Expr *E = - BuildExpressionFromIntegralTemplateArgument(Arg, Loc).getAs<Expr>(); + case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: { + Expr *E = BuildExpressionFromNonTypeTemplateArgument(Arg, Loc).get(); return TemplateArgumentLoc(TemplateArgument(E), E); } @@ -6298,11 +6329,8 @@ case TemplateArgument::Null: case TemplateArgument::Integral: case TemplateArgument::Declaration: - break; - case TemplateArgument::NullPtr: - MarkUsedTemplateParameters(Ctx, TemplateArg.getNullPtrType(), OnlyDeduced, - Depth, Used); + case TemplateArgument::StructuralValue: break; case TemplateArgument::Type: Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -4339,6 +4339,7 @@ case TemplateArgument::NullPtr: case TemplateArgument::Integral: case TemplateArgument::Declaration: + case TemplateArgument::StructuralValue: case TemplateArgument::Pack: case TemplateArgument::TemplateExpansion: return false; @@ -5671,6 +5672,7 @@ case TemplateArgument::Declaration: case TemplateArgument::Integral: + case TemplateArgument::StructuralValue: case TemplateArgument::NullPtr: // We've already checked this template argument, so just copy // it to the list of converted arguments. @@ -5825,11 +5827,10 @@ return true; case TemplateArgument::Declaration: - llvm_unreachable("Declaration argument with template template parameter"); case TemplateArgument::Integral: - llvm_unreachable("Integral argument with template template parameter"); + case TemplateArgument::StructuralValue: case TemplateArgument::NullPtr: - llvm_unreachable("Null pointer argument with template template parameter"); + llvm_unreachable("non-type argument with template template parameter"); case TemplateArgument::Pack: llvm_unreachable("Caller must expand template argument packs"); @@ -7299,44 +7300,9 @@ return ArgResult; } - // Convert the APValue to a TemplateArgument. - switch (Value.getKind()) { - case APValue::None: - assert(ParamType->isNullPtrType()); - SugaredConverted = TemplateArgument(ParamType, /*isNullPtr=*/true); - CanonicalConverted = TemplateArgument(CanonParamType, /*isNullPtr=*/true); - break; - case APValue::Indeterminate: - llvm_unreachable("result of constant evaluation should be initialized"); - break; - case APValue::Int: - assert(ParamType->isIntegralOrEnumerationType()); - SugaredConverted = TemplateArgument(Context, Value.getInt(), ParamType); - CanonicalConverted = - TemplateArgument(Context, Value.getInt(), CanonParamType); - break; - case APValue::MemberPointer: { - assert(ParamType->isMemberPointerType()); - - // FIXME: We need TemplateArgument representation and mangling for these. - if (!Value.getMemberPointerPath().empty()) { - Diag(Arg->getBeginLoc(), - diag::err_template_arg_member_ptr_base_derived_not_supported) - << Value.getMemberPointerDecl() << ParamType - << Arg->getSourceRange(); - return ExprError(); - } - - auto *VD = const_cast<ValueDecl*>(Value.getMemberPointerDecl()); - SugaredConverted = VD ? TemplateArgument(VD, ParamType) - : TemplateArgument(ParamType, /*isNullPtr=*/true); - CanonicalConverted = - VD ? TemplateArgument(cast<ValueDecl>(VD->getCanonicalDecl()), - CanonParamType) - : TemplateArgument(CanonParamType, /*isNullPtr=*/true); - break; - } - case APValue::LValue: { + // Prior to C++20, enforce restrictions on possible template argument + // values. + if (!getLangOpts().CPlusPlus20 && Value.isLValue()) { // For a non-type template-parameter of pointer or reference type, // the value of the constant expression shall not refer to assert(ParamType->isPointerType() || ParamType->isReferenceType() || @@ -7354,8 +7320,7 @@ << Arg->getSourceRange(); return ExprError(); } - // -- a subobject - // FIXME: Until C++20 + // -- a subobject [until C++20] if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 && VD && VD->getType()->isArrayType() && Value.getLValuePath()[0].getAsArrayIndex() == 0 && @@ -7373,37 +7338,13 @@ "null reference should not be a constant expression"); assert((!VD || !ParamType->isNullPtrType()) && "non-null value of type nullptr_t?"); - - SugaredConverted = VD ? TemplateArgument(VD, ParamType) - : TemplateArgument(ParamType, /*isNullPtr=*/true); - CanonicalConverted = - VD ? TemplateArgument(cast<ValueDecl>(VD->getCanonicalDecl()), - CanonParamType) - : TemplateArgument(CanonParamType, /*isNullPtr=*/true); - break; - } - case APValue::Struct: - case APValue::Union: { - // Get or create the corresponding template parameter object. - TemplateParamObjectDecl *D = - Context.getTemplateParamObjectDecl(ParamType, Value); - SugaredConverted = TemplateArgument(D, ParamType); - CanonicalConverted = - TemplateArgument(D->getCanonicalDecl(), CanonParamType); - break; } - case APValue::AddrLabelDiff: + + if (Value.isAddrLabelDiff()) return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff); - case APValue::FixedPoint: - case APValue::Float: - case APValue::ComplexInt: - case APValue::ComplexFloat: - case APValue::Vector: - case APValue::Array: - return Diag(StartLoc, diag::err_non_type_template_arg_unsupported) - << ParamType; - } + SugaredConverted = TemplateArgument(Context, ParamType, Value); + CanonicalConverted = TemplateArgument(Context, CanonParamType, Value); return ArgResult.get(); } @@ -7951,12 +7892,9 @@ /// This routine takes care of the mapping from an integral template /// argument (which may have any integral type) to the appropriate /// literal value. -ExprResult -Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, - SourceLocation Loc) { - assert(Arg.getKind() == TemplateArgument::Integral && - "Operation is only valid for integral template arguments"); - QualType OrigT = Arg.getIntegralType(); +static Expr *BuildExpressionFromIntegralTemplateArgumentValue( + Sema &S, QualType OrigT, const llvm::APSInt &Int, SourceLocation Loc) { + assert(OrigT->isIntegralOrEnumerationType()); // If this is an enum type that we're instantiating, we need to use an integer // type the same size as the enumerator. We don't want to build an @@ -7972,7 +7910,7 @@ CharacterLiteral::CharacterKind Kind; if (T->isWideCharType()) Kind = CharacterLiteral::Wide; - else if (T->isChar8Type() && getLangOpts().Char8) + else if (T->isChar8Type() && S.getLangOpts().Char8) Kind = CharacterLiteral::UTF8; else if (T->isChar16Type()) Kind = CharacterLiteral::UTF16; @@ -7981,29 +7919,133 @@ else Kind = CharacterLiteral::Ascii; - E = new (Context) CharacterLiteral(Arg.getAsIntegral().getZExtValue(), - Kind, T, Loc); + E = new (S.Context) CharacterLiteral(Int.getZExtValue(), Kind, T, Loc); } else if (T->isBooleanType()) { - E = CXXBoolLiteralExpr::Create(Context, Arg.getAsIntegral().getBoolValue(), - T, Loc); - } else if (T->isNullPtrType()) { - E = new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc); + E = CXXBoolLiteralExpr::Create(S.Context, Int.getBoolValue(), T, Loc); } else { - E = IntegerLiteral::Create(Context, Arg.getAsIntegral(), T, Loc); + E = IntegerLiteral::Create(S.Context, Int, T, Loc); } if (OrigT->isEnumeralType()) { // FIXME: This is a hack. We need a better way to handle substituted // non-type template parameters. - E = CStyleCastExpr::Create(Context, OrigT, VK_PRValue, CK_IntegralCast, E, - nullptr, CurFPFeatureOverrides(), - Context.getTrivialTypeSourceInfo(OrigT, Loc), + E = CStyleCastExpr::Create(S.Context, OrigT, VK_PRValue, CK_IntegralCast, E, + nullptr, S.CurFPFeatureOverrides(), + S.Context.getTrivialTypeSourceInfo(OrigT, Loc), Loc, Loc); } return E; } +static Expr *BuildExpressionFromNonTypeTemplateArgumentValue( + Sema &S, QualType T, const APValue &Val, SourceLocation Loc) { + auto MakeInitList = [&](ArrayRef<Expr *> Elts) -> Expr * { + auto *ILE = new (S.Context) InitListExpr(S.Context, Loc, Elts, Loc); + ILE->setType(T); + return ILE; + }; + + switch (Val.getKind()) { + case APValue::AddrLabelDiff: + // This cannot occur in a template argument at all. + case APValue::Array: + case APValue::Struct: + case APValue::Union: + // These can only occur within a template parameter object, which is + // represented as a TemplateArgument::Declaration. + llvm_unreachable("unexpected template argument value"); + + case APValue::Int: + return BuildExpressionFromIntegralTemplateArgumentValue(S, T, Val.getInt(), + Loc); + + case APValue::Float: + return FloatingLiteral::Create(S.Context, Val.getFloat(), /*IsExact=*/true, + T, Loc); + + case APValue::FixedPoint: + return FixedPointLiteral::CreateFromRawInt( + S.Context, Val.getFixedPoint().getValue(), T, Loc, + Val.getFixedPoint().getScale()); + + case APValue::ComplexInt: { + QualType ElemT = T->castAs<ComplexType>()->getElementType(); + return MakeInitList({BuildExpressionFromIntegralTemplateArgumentValue( + S, ElemT, Val.getComplexIntReal(), Loc), + BuildExpressionFromIntegralTemplateArgumentValue( + S, ElemT, Val.getComplexIntImag(), Loc)}); + } + + case APValue::ComplexFloat: { + QualType ElemT = T->castAs<ComplexType>()->getElementType(); + return MakeInitList( + {FloatingLiteral::Create(S.Context, Val.getComplexFloatReal(), true, + ElemT, Loc), + FloatingLiteral::Create(S.Context, Val.getComplexFloatImag(), true, + ElemT, Loc)}); + } + + case APValue::Vector: { + QualType ElemT = T->castAs<VectorType>()->getElementType(); + llvm::SmallVector<Expr *, 8> Elts; + for (unsigned I = 0, N = Val.getVectorLength(); I != N; ++I) + Elts.push_back(BuildExpressionFromNonTypeTemplateArgumentValue( + S, ElemT, Val.getVectorElt(I), Loc)); + return MakeInitList(Elts); + } + + case APValue::None: + case APValue::Indeterminate: + llvm_unreachable("Unexpected APValue kind."); + case APValue::LValue: + case APValue::MemberPointer: + // There isn't necessarily a valid equivalent source-level syntax for + // these; in particular, a naive lowering might violate access control. + // So for now we lower to a ConstantExpr holding the value, wrapped around + // an OpaqueValueExpr. + // FIXME: We should have a better representation for this. + ExprValueKind VK = VK_PRValue; + if (T->isReferenceType()) { + T = T->getPointeeType(); + VK = VK_LValue; + } + auto *OVE = new (S.Context) OpaqueValueExpr(Loc, T, VK); + return ConstantExpr::Create(S.Context, OVE, Val); + } + llvm_unreachable("Unhandled APValue::ValueKind enum"); +} + +ExprResult +Sema::BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, + SourceLocation Loc) { + switch (Arg.getKind()) { + case TemplateArgument::Null: + case TemplateArgument::Type: + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + case TemplateArgument::Pack: + llvm_unreachable("not a non-type template argument"); + + case TemplateArgument::Expression: + return Arg.getAsExpr(); + + case TemplateArgument::NullPtr: + case TemplateArgument::Declaration: + return BuildExpressionFromDeclTemplateArgument( + Arg, Arg.getNonTypeTemplateArgumentType(), Loc); + + case TemplateArgument::Integral: + return BuildExpressionFromIntegralTemplateArgumentValue( + *this, Arg.getIntegralType(), Arg.getAsIntegral(), Loc); + + case TemplateArgument::StructuralValue: + return BuildExpressionFromNonTypeTemplateArgumentValue( + *this, Arg.getStructuralValueType(), Arg.getAsStructuralValue(), Loc); + } + llvm_unreachable("Unhandled TemplateArgument::ArgKind enum"); +} + /// Match two template parameters within template parameter lists. static bool MatchTemplateParameterKind( Sema &S, NamedDecl *New, const NamedDecl *NewInstFrom, NamedDecl *Old, Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -5845,6 +5845,15 @@ /*InOverloadResolution=*/false, /*AllowObjCWritebackConversion=*/false, /*AllowExplicit=*/false); + + // TryCopyInitialization returns incorrect info for attempts to bind reference + // to bit-field due to C++ [over.ics.ref]p4, so check it here. + if (From->refersToBitField() && T.getTypePtr()->isReferenceType()) { + return S.Diag(From->getBeginLoc(), + diag::err_reference_bind_to_bitfield_in_cce) + << From->getSourceRange(); + } + StandardConversionSequence *SCS = nullptr; switch (ICS.getKind()) { case ImplicitConversionSequence::StandardConversion: @@ -5978,7 +5987,9 @@ if (Notes.empty()) { // It's a constant expression. - Expr *E = ConstantExpr::Create(S.Context, Result.get(), Value); + Expr *E = Result.get(); + if (!isa<ConstantExpr>(E)) + E = ConstantExpr::Create(S.Context, Result.get(), Value); if (!PreNarrowingValue.isAbsent()) Value = std::move(PreNarrowingValue); return E; Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -2983,6 +2983,7 @@ case TemplateArgument::Integral: case TemplateArgument::Expression: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: // [Note: non-type template arguments do not contribute to the set of // associated namespaces. ] break; Index: clang/lib/Index/USRGeneration.cpp =================================================================== --- clang/lib/Index/USRGeneration.cpp +++ clang/lib/Index/USRGeneration.cpp @@ -11,6 +11,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" +#include "clang/AST/ODRHash.h" #include "clang/Basic/FileManager.h" #include "clang/Lex/PreprocessingRecord.h" #include "llvm/Support/Path.h" @@ -1034,6 +1035,15 @@ VisitType(Arg.getIntegralType()); Out << Arg.getAsIntegral(); break; + + case TemplateArgument::StructuralValue: { + Out << 'S'; + VisitType(Arg.getStructuralValueType()); + ODRHash Hash{}; + Hash.AddStructuralValue(Arg.getAsStructuralValue()); + Out << Hash.CalculateHash(); + break; + } } } Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -2183,6 +2183,14 @@ TemplateParams.push_back(DBuilder.createTemplateValueParameter( TheCU, Name, TTy, defaultParameter, V)); } break; + case TemplateArgument::StructuralValue: { + QualType T = TA.getStructuralValueType(); + llvm::DIType *TTy = getOrCreateType(T, Unit); + llvm::Constant *V = ConstantEmitter(CGM).emitAbstract( + SourceLocation(), TA.getAsStructuralValue(), T); + TemplateParams.push_back(DBuilder.createTemplateValueParameter( + TheCU, Name, TTy, defaultParameter, V)); + } break; case TemplateArgument::Template: { std::string QualName; llvm::raw_string_ostream OS(QualName); Index: clang/lib/AST/TypeLoc.cpp =================================================================== --- clang/lib/AST/TypeLoc.cpp +++ clang/lib/AST/TypeLoc.cpp @@ -585,6 +585,7 @@ case TemplateArgument::Integral: case TemplateArgument::Declaration: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: ArgInfos[i] = TemplateArgumentLocInfo(); break; Index: clang/lib/AST/TemplateBase.cpp =================================================================== --- clang/lib/AST/TemplateBase.cpp +++ clang/lib/AST/TemplateBase.cpp @@ -160,8 +160,25 @@ // TemplateArgument Implementation //===----------------------------------------------------------------------===// -TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, - QualType Type, bool IsDefaulted) { +void TemplateArgument::initFromType(QualType T, bool isNullPtr, + bool IsDefaulted) { + TypeOrValue.Kind = isNullPtr ? NullPtr : Type; + TypeOrValue.IsDefaulted = IsDefaulted; + TypeOrValue.V = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr()); +} + +void TemplateArgument::initFromDeclaration(ValueDecl *D, QualType QT, + bool IsDefaulted) { + assert(D && "Expected decl"); + DeclArg.Kind = Declaration; + DeclArg.IsDefaulted = IsDefaulted; + DeclArg.QT = QT.getAsOpaquePtr(); + DeclArg.D = D; +} + +void TemplateArgument::initFromIntegral(const ASTContext &Ctx, + const llvm::APSInt &Value, + QualType Type, bool IsDefaulted) { Integer.Kind = Integral; Integer.IsDefaulted = IsDefaulted; // Copy the APSInt value into our decomposed form. @@ -180,6 +197,56 @@ Integer.Type = Type.getAsOpaquePtr(); } +void TemplateArgument::initFromStructural(const ASTContext &Ctx, QualType Type, + const APValue &V, bool IsDefaulted) { + Value.Kind = StructuralValue; + Value.IsDefaulted = IsDefaulted; + Value.Value = new (Ctx) APValue(V); + Ctx.addDestruction(Value.Value); + Value.Type = Type.getAsOpaquePtr(); +} + +TemplateArgument::TemplateArgument(const ASTContext &Ctx, + const llvm::APSInt &Value, QualType Type, + bool IsDefaulted) { + initFromIntegral(Ctx, Value, Type, IsDefaulted); +} + +static const ValueDecl *getAsSimpleValueDeclRef(const ASTContext &Ctx, + QualType T, const APValue &V) { + // Pointers to members are relatively easy. + if (V.isMemberPointer() && V.getMemberPointerPath().empty()) + return V.getMemberPointerDecl(); + + // We model class non-type template parameters as their template parameter + // object declaration. + if (V.isStruct() || V.isUnion()) + return Ctx.getTemplateParamObjectDecl(T, V); + + // Pointers and references with an empty path use the special 'Declaration' + // representation. + if (V.isLValue() && V.hasLValuePath() && V.getLValuePath().empty() && + !V.isLValueOnePastTheEnd()) + return V.getLValueBase().dyn_cast<const ValueDecl *>(); + + // Everything else uses the 'structural' representation. + return nullptr; +} + +TemplateArgument::TemplateArgument(const ASTContext &Ctx, QualType Type, + const APValue &V, bool IsDefaulted) { + if (Type->isIntegralOrEnumerationType() && V.isInt()) + initFromIntegral(Ctx, V.getInt(), Type, IsDefaulted); + else if ((V.isLValue() && V.isNullPointer()) || + (V.isMemberPointer() && !V.getMemberPointerDecl())) + initFromType(Type, /*isNullPtr=*/true, IsDefaulted); + else if (const ValueDecl *VD = getAsSimpleValueDeclRef(Ctx, Type, V)) + // FIXME: The Declaration form should expose a const ValueDecl*. + initFromDeclaration(const_cast<ValueDecl *>(VD), Type, IsDefaulted); + else + initFromStructural(Ctx, Type, V, IsDefaulted); +} + TemplateArgument TemplateArgument::CreatePackCopy(ASTContext &Context, ArrayRef<TemplateArgument> Args) { @@ -220,6 +287,7 @@ case NullPtr: case Integral: + case StructuralValue: return TemplateArgumentDependence::None; case Expression: @@ -250,6 +318,7 @@ case Null: case Declaration: case Integral: + case StructuralValue: case Pack: case Template: case NullPtr: @@ -300,6 +369,9 @@ case TemplateArgument::NullPtr: return getNullPtrType(); + + case TemplateArgument::StructuralValue: + return getStructuralValueType(); } llvm_unreachable("Invalid TemplateArgument Kind!"); @@ -333,8 +405,13 @@ break; case Integral: - getAsIntegral().Profile(ID); getIntegralType().Profile(ID); + getAsIntegral().Profile(ID); + break; + + case StructuralValue: + getStructuralValueType().Profile(ID); + getAsStructuralValue().Profile(ID); break; case Expression: @@ -371,6 +448,16 @@ return getIntegralType() == Other.getIntegralType() && getAsIntegral() == Other.getAsIntegral(); + case StructuralValue: { + if (getStructuralValueType() != Other.getStructuralValueType()) + return false; + + llvm::FoldingSetNodeID A, B; + getAsStructuralValue().Profile(A); + Other.getAsStructuralValue().Profile(B); + return A == B; + } + case Pack: if (Args.NumArgs != Other.Args.NumArgs) return false; for (unsigned I = 0, E = Args.NumArgs; I != E; ++I) @@ -397,6 +484,7 @@ case Declaration: case Integral: + case StructuralValue: case Pack: case Null: case Template: @@ -439,6 +527,10 @@ break; } + case StructuralValue: + getAsStructuralValue().printPretty(Out, Policy, getStructuralValueType()); + break; + case NullPtr: // FIXME: Include the type if it's not obvious from the context. Out << "nullptr"; @@ -522,6 +614,9 @@ case TemplateArgument::Integral: return getSourceIntegralExpression()->getSourceRange(); + case TemplateArgument::StructuralValue: + return getSourceStructuralValueExpression()->getSourceRange(); + case TemplateArgument::Pack: case TemplateArgument::Null: return SourceRange(); @@ -550,6 +645,18 @@ case TemplateArgument::Integral: return DB << toString(Arg.getAsIntegral(), 10); + case TemplateArgument::StructuralValue: { + // FIXME: We're guessing at LangOptions! + SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + LangOptions LangOpts; + LangOpts.CPlusPlus = true; + PrintingPolicy Policy(LangOpts); + Arg.getAsStructuralValue().printPretty(OS, Policy, + Arg.getStructuralValueType()); + return DB << OS.str(); + } + case TemplateArgument::Template: return DB << Arg.getAsTemplate(); Index: clang/lib/AST/StmtProfile.cpp =================================================================== --- clang/lib/AST/StmtProfile.cpp +++ clang/lib/AST/StmtProfile.cpp @@ -2401,6 +2401,12 @@ Arg.getAsIntegral().Profile(ID); break; + case TemplateArgument::StructuralValue: + VisitType(Arg.getStructuralValueType()); + // FIXME: Do we need to recursively decompose this ourselves? + Arg.getAsStructuralValue().Profile(ID); + break; + case TemplateArgument::Expression: Visit(Arg.getAsExpr()); break; Index: clang/lib/AST/ODRHash.cpp =================================================================== --- clang/lib/AST/ODRHash.cpp +++ clang/lib/AST/ODRHash.cpp @@ -14,6 +14,11 @@ #include "clang/AST/ODRHash.h" +#include "clang/AST/APValue.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/StmtVisitor.h" @@ -180,6 +185,10 @@ TA.getAsIntegral().Profile(ID); break; } + case TemplateArgument::StructuralValue: + AddQualType(TA.getStructuralValueType()); + AddStructuralValue(TA.getAsStructuralValue()); + break; case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: AddTemplateName(TA.getAsTemplateOrTemplatePattern()); @@ -1245,3 +1254,60 @@ void ODRHash::AddBoolean(bool Value) { Bools.push_back(Value); } + +void ODRHash::AddStructuralValue(const APValue &Value) { + ID.AddInteger(Value.getKind()); + + // 'APValue::Profile' uses pointer values to make hash for LValue and + // MemberPointer, but they differ from one compiler invocation to another. + // So, handle them explicitly here. + + switch (Value.getKind()) { + case APValue::LValue: { + const APValue::LValueBase &Base = Value.getLValueBase(); + if (!Base) { + ID.AddInteger(Value.getLValueOffset().getQuantity()); + break; + } + + assert(Base.is<const ValueDecl *>()); + AddDecl(Base.get<const ValueDecl *>()); + ID.AddInteger(Value.getLValueOffset().getQuantity()); + + bool OnePastTheEnd = Value.isLValueOnePastTheEnd(); + if (Value.hasLValuePath()) { + QualType TypeSoFar = Base.getType(); + for (APValue::LValuePathEntry E : Value.getLValuePath()) { + if (auto *AT = TypeSoFar->getAsArrayTypeUnsafe()) { + if (auto *CAT = dyn_cast<ConstantArrayType>(AT)) + OnePastTheEnd |= CAT->getSize() == E.getAsArrayIndex(); + TypeSoFar = AT->getElementType(); + } else { + const Decl *D = E.getAsBaseOrMember().getPointer(); + if (auto *FD = dyn_cast<FieldDecl>(D)) { + if (FD->getParent()->isUnion()) + ID.AddInteger(FD->getFieldIndex()); + TypeSoFar = FD->getType(); + } else { + TypeSoFar = + D->getASTContext().getRecordType(cast<CXXRecordDecl>(D)); + } + } + } + } + ID.AddInteger((Value.isNullPointer() ? 1 : 0) | (OnePastTheEnd ? 2 : 0) | + (Value.hasLValuePath() ? 4 : 0)); + break; + } + case APValue::MemberPointer: { + const ValueDecl *D = Value.getMemberPointerDecl(); + assert(D); + AddDecl(D); + ID.AddInteger( + D->getASTContext().getMemberPointerPathAdjustment(Value).getQuantity()); + break; + } + default: + Value.Profile(ID); + } +} Index: clang/lib/AST/MicrosoftMangle.cpp =================================================================== --- clang/lib/AST/MicrosoftMangle.cpp +++ clang/lib/AST/MicrosoftMangle.cpp @@ -337,6 +337,7 @@ public: enum QualifierMangleMode { QMM_Drop, QMM_Mangle, QMM_Escape, QMM_Result }; + enum class TplArgKind { ClassNTTP, StructuralValue }; MicrosoftCXXNameMangler(MicrosoftMangleContextImpl &C, raw_ostream &Out_) : Context(C), Out(Out_), Structor(nullptr), StructorType(-1), @@ -453,7 +454,7 @@ const TemplateArgumentList &TemplateArgs); void mangleTemplateArg(const TemplateDecl *TD, const TemplateArgument &TA, const NamedDecl *Parm); - void mangleTemplateArgValue(QualType T, const APValue &V, + void mangleTemplateArgValue(QualType T, const APValue &V, TplArgKind, bool WithScalarType = false); void mangleObjCProtocol(const ObjCProtocolDecl *PD); @@ -1089,7 +1090,7 @@ if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(ND)) { Out << "?__N"; mangleTemplateArgValue(TPO->getType().getUnqualifiedType(), - TPO->getValue()); + TPO->getValue(), TplArgKind::ClassNTTP); break; } @@ -1605,6 +1606,22 @@ } } +/// If value V (with type T) represents a decayed pointer to the first element +/// of an array, return that array. +static ValueDecl *getAsArrayToPointerDecayedDecl(QualType T, const APValue &V) { + // Must be a pointer... + if (!T->isPointerType() || !V.isLValue() || !V.hasLValuePath() || + !V.getLValueBase()) + return nullptr; + // ... to element 0 of an array. + QualType BaseT = V.getLValueBase().getType(); + if (!BaseT->isArrayType() || V.getLValuePath().size() != 1 || + V.getLValuePath()[0].getAsArrayIndex() != 0) + return nullptr; + return const_cast<ValueDecl *>( + V.getLValueBase().dyn_cast<const ValueDecl *>()); +} + void MicrosoftCXXNameMangler::mangleTemplateArg(const TemplateDecl *TD, const TemplateArgument &TA, const NamedDecl *Parm) { @@ -1670,7 +1687,7 @@ Out << "$"; auto *TPO = cast<TemplateParamObjectDecl>(ND); mangleTemplateArgValue(TPO->getType().getUnqualifiedType(), - TPO->getValue()); + TPO->getValue(), TplArgKind::ClassNTTP); } else { mangle(ND, "$1?"); } @@ -1713,6 +1730,27 @@ cast<NonTypeTemplateParmDecl>(Parm), T); break; } + case TemplateArgument::StructuralValue: + if (ValueDecl *D = getAsArrayToPointerDecayedDecl( + TA.getStructuralValueType(), TA.getAsStructuralValue())) { + // Mangle the result of array-to-pointer decay as if it were a reference + // to the original declaration, to match MSVC's behavior. This can result + // in mangling collisions in some cases! + return mangleTemplateArg( + TD, TemplateArgument(D, TA.getStructuralValueType()), Parm); + } + Out << "$"; + if (cast<NonTypeTemplateParmDecl>(Parm) + ->getType() + ->getContainedDeducedType()) { + Out << "M"; + mangleType(TA.getNonTypeTemplateArgumentType(), SourceRange(), QMM_Drop); + } + mangleTemplateArgValue(TA.getStructuralValueType(), + TA.getAsStructuralValue(), + TplArgKind::StructuralValue, + /*WithScalarType=*/false); + break; case TemplateArgument::Expression: mangleExpression(TA.getAsExpr(), cast<NonTypeTemplateParmDecl>(Parm)); break; @@ -1755,6 +1793,7 @@ void MicrosoftCXXNameMangler::mangleTemplateArgValue(QualType T, const APValue &V, + TplArgKind TAK, bool WithScalarType) { switch (V.getKind()) { case APValue::None: @@ -1807,7 +1846,7 @@ break; } } else { - if (T->isPointerType()) + if (TAK == TplArgKind::ClassNTTP && T->isPointerType()) Out << "5"; SmallVector<char, 2> EntryTypes; @@ -1851,12 +1890,12 @@ auto *VD = Base.dyn_cast<const ValueDecl*>(); if (!VD) break; - Out << "E"; + Out << (TAK == TplArgKind::ClassNTTP ? 'E' : '1'); mangle(VD); for (const std::function<void()> &Mangler : EntryManglers) Mangler(); - if (T->isPointerType()) + if (TAK == TplArgKind::ClassNTTP && T->isPointerType()) Out << '@'; } @@ -1870,11 +1909,18 @@ const CXXRecordDecl *RD = T->castAs<MemberPointerType>()->getMostRecentCXXRecordDecl(); const ValueDecl *D = V.getMemberPointerDecl(); - if (T->isMemberDataPointerType()) - mangleMemberDataPointerInClassNTTP(RD, D); - else - mangleMemberFunctionPointerInClassNTTP(RD, - cast_or_null<CXXMethodDecl>(D)); + if (TAK == TplArgKind::ClassNTTP) { + if (T->isMemberDataPointerType()) + mangleMemberDataPointerInClassNTTP(RD, D); + else + mangleMemberFunctionPointerInClassNTTP(RD, + cast_or_null<CXXMethodDecl>(D)); + } else { + if (T->isMemberDataPointerType()) + mangleMemberDataPointer(RD, D, ""); + else + mangleMemberFunctionPointer(RD, cast_or_null<CXXMethodDecl>(D), ""); + } return; } @@ -1886,11 +1932,11 @@ unsigned BaseIndex = 0; for (const CXXBaseSpecifier &B : RD->bases()) - mangleTemplateArgValue(B.getType(), V.getStructBase(BaseIndex++)); + mangleTemplateArgValue(B.getType(), V.getStructBase(BaseIndex++), TAK); for (const FieldDecl *FD : RD->fields()) if (!FD->isUnnamedBitfield()) mangleTemplateArgValue(FD->getType(), - V.getStructField(FD->getFieldIndex()), + V.getStructField(FD->getFieldIndex()), TAK, /*WithScalarType*/ true); Out << '@'; return; @@ -1901,7 +1947,7 @@ mangleType(T, SourceRange(), QMM_Escape); if (const FieldDecl *FD = V.getUnionField()) { mangleUnqualifiedName(FD); - mangleTemplateArgValue(FD->getType(), V.getUnionValue()); + mangleTemplateArgValue(FD->getType(), V.getUnionValue(), TAK); } Out << '@'; return; @@ -1933,7 +1979,7 @@ const APValue &ElemV = I < V.getArrayInitializedElts() ? V.getArrayInitializedElt(I) : V.getArrayFiller(); - mangleTemplateArgValue(ElemT, ElemV); + mangleTemplateArgValue(ElemT, ElemV, TAK); Out << '@'; } Out << '@'; @@ -1950,7 +1996,7 @@ mangleType(ElemT, SourceRange(), QMM_Escape); for (unsigned I = 0, N = V.getVectorLength(); I != N; ++I) { const APValue &ElemV = V.getVectorElt(I); - mangleTemplateArgValue(ElemT, ElemV); + mangleTemplateArgValue(ElemT, ElemV, TAK); Out << '@'; } Out << "@@"; Index: clang/lib/AST/ItaniumMangle.cpp =================================================================== --- clang/lib/AST/ItaniumMangle.cpp +++ clang/lib/AST/ItaniumMangle.cpp @@ -4588,9 +4588,27 @@ E = cast<CXXStdInitializerListExpr>(E)->getSubExpr(); goto recurse; - case Expr::SubstNonTypeTemplateParmExprClass: + case Expr::SubstNonTypeTemplateParmExprClass: { + // Mangle a substituted parameter the same way we mangle the template + // argument. + // As proposed in https://github.com/itanium-cxx-abi/cxx-abi/issues/111. + auto *SNTTPE = cast<SubstNonTypeTemplateParmExpr>(E); + if (auto *CE = dyn_cast<ConstantExpr>(SNTTPE->getReplacement())) { + // Pull out the constant value and mangle it as a template argument. + QualType ParamType = SNTTPE->getParameterType(Context.getASTContext()); + if (CE->hasAPValueResult()) + mangleValueInTemplateArg(ParamType, CE->getResultAsAPValue(), false, + /*NeedExactType=*/true); + else + mangleValueInTemplateArg(ParamType, CE->getAPValueResult(), false, + /*NeedExactType=*/true); + break; + } + // The remaining cases all happen to be substituted with expressions that + // mangle the same as a corresponding template argument anyway. E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); goto recurse; + } case Expr::UserDefinedLiteralClass: // We follow g++'s approach of mangling a UDL as a call to the literal @@ -5623,6 +5641,11 @@ mangleNullPointer(A.getNullPtrType()); break; } + case TemplateArgument::StructuralValue: + mangleValueInTemplateArg(A.getStructuralValueType(), + A.getAsStructuralValue(), + /*TopLevel=*/true, NeedExactType); + break; case TemplateArgument::Pack: { // <template-arg> ::= J <template-arg>* E Out << 'J'; @@ -6032,7 +6055,20 @@ Out << "plcvPcad"; Kind = Offset; } else { - if (!V.getLValuePath().empty() || V.isLValueOnePastTheEnd()) { + // Clang 11 and before mangled an array subject to array-to-pointer decay + // as if it were the declaration itself. + bool IsArrayToPointerDecayMangledAsDecl = false; + if (TopLevel && Ctx.getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver11) { + QualType BType = B.getType(); + IsArrayToPointerDecayMangledAsDecl = + BType->isArrayType() && V.getLValuePath().size() == 1 && + V.getLValuePath()[0].getAsArrayIndex() == 0 && + Ctx.hasSimilarType(T, Ctx.getDecayedType(BType)); + } + + if ((!V.getLValuePath().empty() || V.isLValueOnePastTheEnd()) && + !IsArrayToPointerDecayMangledAsDecl) { NotPrimaryExpr(); // A final conversion to the template parameter's type is usually // folded into the 'so' mangling, but we can't do that for 'void*' Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -343,6 +343,10 @@ LV.merge(getTypeLinkageAndVisibility(Arg.getNullPtrType())); continue; + case TemplateArgument::StructuralValue: + LV.merge(getLVForValue(Arg.getAsStructuralValue(), computation)); + continue; + case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: if (TemplateDecl *Template = Index: clang/lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- clang/lib/AST/ASTStructuralEquivalence.cpp +++ clang/lib/AST/ASTStructuralEquivalence.cpp @@ -604,6 +604,10 @@ return IsStructurallyEquivalent(Context, Arg1.getAsExpr(), Arg2.getAsExpr()); + case TemplateArgument::StructuralValue: + // FIXME: Do we need to customize the comparison? + return Arg1.structurallyEquals(Arg2); + case TemplateArgument::Pack: return IsStructurallyEquivalent(Context, Arg1.pack_elements(), Arg2.pack_elements()); Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -867,6 +867,17 @@ From.getIsDefaulted()); } + case TemplateArgument::StructuralValue: { + ExpectedType ToTypeOrErr = import(From.getStructuralValueType()); + if (!ToTypeOrErr) + return ToTypeOrErr.takeError(); + Expected<APValue> ToValueOrErr = import(From.getAsStructuralValue()); + if (!ToValueOrErr) + return ToValueOrErr.takeError(); + return TemplateArgument(Importer.getToContext(), *ToTypeOrErr, + *ToValueOrErr); + } + case TemplateArgument::Template: { Expected<TemplateName> ToTemplateOrErr = import(From.getAsTemplate()); if (!ToTemplateOrErr) @@ -3431,6 +3442,8 @@ case TemplateArgument::NullPtr: // FIXME: The type is not allowed to be in the function? return CheckType(Arg.getNullPtrType()); + case TemplateArgument::StructuralValue: + return CheckType(Arg.getStructuralValueType()); case TemplateArgument::Pack: for (const auto &PackArg : Arg.getPackAsArray()) if (checkTemplateArgument(PackArg)) Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -6797,6 +6797,11 @@ case TemplateArgument::Integral: return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType())); + case TemplateArgument::StructuralValue: + return TemplateArgument(*this, + getCanonicalType(Arg.getStructuralValueType()), + Arg.getAsStructuralValue()); + case TemplateArgument::Type: return TemplateArgument(getCanonicalType(Arg.getAsType()), /*isNullPtr*/ false, Arg.getIsDefaulted()); Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -8496,8 +8496,8 @@ QualType ParamType, SourceLocation Loc); ExprResult - BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg, - SourceLocation Loc); + BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, + SourceLocation Loc); /// Enumeration describing how template parameter lists are compared /// for equality. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2202,6 +2202,8 @@ def err_reference_bind_to_bitfield : Error< "%select{non-const|volatile}0 reference cannot bind to " "bit-field%select{| %1}2">; +def err_reference_bind_to_bitfield_in_cce : Error< + "reference cannot bind to bit-field in converted constant expression">; def err_reference_bind_to_vector_element : Error< "%select{non-const|volatile}0 reference cannot bind to vector element">; def err_reference_bind_to_matrix_element : Error< @@ -5045,8 +5047,6 @@ "non-type template argument refers to subobject '%0'">; def err_non_type_template_arg_addr_label_diff : Error< "template argument / label address difference / what did you expect?">; -def err_non_type_template_arg_unsupported : Error< - "sorry, non-type template argument of type %0 is not yet supported">; def err_template_arg_not_convertible : Error< "non-type template argument of type %0 cannot be converted to a value " "of type %1">; @@ -5098,9 +5098,6 @@ "non-type template argument does not refer to an object or function">; def err_template_arg_not_pointer_to_member_form : Error< "non-type template argument is not a pointer to member constant">; -def err_template_arg_member_ptr_base_derived_not_supported : Error< - "sorry, non-type template argument of pointer-to-member type %1 that refers " - "to member %q0 of a different class is not supported yet">; def err_template_arg_invalid : Error< "non-type template argument '%0' is invalid">; def ext_template_arg_extra_parens : ExtWarn< Index: clang/include/clang/AST/TemplateBase.h =================================================================== --- clang/include/clang/AST/TemplateBase.h +++ clang/include/clang/AST/TemplateBase.h @@ -50,6 +50,7 @@ namespace clang { +class APValue; class ASTContext; class Expr; struct PrintingPolicy; @@ -80,6 +81,13 @@ /// that was provided for an integral non-type template parameter. Integral, + /// The template argument is a non-type template argument that can't be + /// represented by the special-case Declaration, NullPtr, or Integral + /// forms. These values are only ever produced by constant evaluation, + /// so cannot be dependent. + /// TODO: merge Declaration, NullPtr and Integral into this? + StructuralValue, + /// The template argument is a template name that was provided for a /// template template parameter. Template, @@ -125,6 +133,12 @@ }; void *Type; }; + struct V { + unsigned Kind : 31; + unsigned IsDefaulted : 1; + APValue *Value; + void *Type; + }; struct A { unsigned Kind : 31; unsigned IsDefaulted : 1; @@ -145,11 +159,19 @@ union { struct DA DeclArg; struct I Integer; + struct V Value; struct A Args; struct TA TemplateArg; struct TV TypeOrValue; }; + void initFromType(QualType, bool isNullPtr, bool IsDefaulted); + void initFromDeclaration(ValueDecl *, QualType, bool IsDefaulted); + void initFromIntegral(const ASTContext &, const llvm::APSInt &, QualType, + bool IsDefaulted); + void initFromStructural(const ASTContext &, QualType, const APValue &, + bool IsDefaulted); + public: /// Construct an empty, invalid template argument. constexpr TemplateArgument() : TypeOrValue({Null, 0, /* IsDefaulted */ 0}) {} @@ -157,25 +179,22 @@ /// Construct a template type argument. TemplateArgument(QualType T, bool isNullPtr = false, bool IsDefaulted = false) { - TypeOrValue.Kind = isNullPtr ? NullPtr : Type; - TypeOrValue.IsDefaulted = IsDefaulted; - TypeOrValue.V = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr()); + initFromType(T, isNullPtr, IsDefaulted); } - /// Construct a template argument that refers to a - /// declaration, which is either an external declaration or a - /// template declaration. + /// Construct a template argument that refers to a (non-dependent) + /// declaration. TemplateArgument(ValueDecl *D, QualType QT, bool IsDefaulted = false) { - assert(D && "Expected decl"); - DeclArg.Kind = Declaration; - DeclArg.IsDefaulted = IsDefaulted; - DeclArg.QT = QT.getAsOpaquePtr(); - DeclArg.D = D; + initFromDeclaration(D, QT, IsDefaulted); } /// Construct an integral constant template argument. The memory to /// store the value is allocated with Ctx. - TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type, + TemplateArgument(const ASTContext &Ctx, const llvm::APSInt &Value, + QualType Type, bool IsDefaulted = false); + + /// Construct a template argument from an arbitrary constant value. + TemplateArgument(const ASTContext &Ctx, QualType Type, const APValue &Value, bool IsDefaulted = false); /// Construct an integral constant template argument with the same @@ -360,6 +379,14 @@ /// default template parameter. bool getIsDefaulted() const { return (bool)TypeOrValue.IsDefaulted; } + /// Get the value of an StructuralValue. + const APValue &getAsStructuralValue() const { return *Value.Value; } + + /// Get the type of an StructuralValue. + QualType getStructuralValueType() const { + return QualType::getFromOpaquePtr(Value.Type); + } + /// If this is a non-type template argument, get its type. Otherwise, /// returns a null QualType. QualType getNonTypeTemplateArgumentType() const; @@ -505,6 +532,7 @@ assert(Argument.getKind() == TemplateArgument::NullPtr || Argument.getKind() == TemplateArgument::Integral || Argument.getKind() == TemplateArgument::Declaration || + Argument.getKind() == TemplateArgument::StructuralValue || Argument.getKind() == TemplateArgument::Expression); } @@ -564,6 +592,11 @@ return LocInfo.getAsExpr(); } + Expr *getSourceStructuralValueExpression() const { + assert(Argument.getKind() == TemplateArgument::StructuralValue); + return LocInfo.getAsExpr(); + } + NestedNameSpecifierLoc getTemplateQualifierLoc() const { if (Argument.getKind() != TemplateArgument::Template && Argument.getKind() != TemplateArgument::TemplateExpansion) Index: clang/include/clang/AST/TemplateArgumentVisitor.h =================================================================== --- clang/include/clang/AST/TemplateArgumentVisitor.h +++ clang/include/clang/AST/TemplateArgumentVisitor.h @@ -37,6 +37,7 @@ DISPATCH(Declaration); DISPATCH(NullPtr); DISPATCH(Integral); + DISPATCH(StructuralValue); DISPATCH(Template); DISPATCH(TemplateExpansion); DISPATCH(Expression); @@ -59,6 +60,7 @@ VISIT_METHOD(Declaration); VISIT_METHOD(NullPtr); VISIT_METHOD(Integral); + VISIT_METHOD(StructuralValue); VISIT_METHOD(Template); VISIT_METHOD(TemplateExpansion); VISIT_METHOD(Expression); Index: clang/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- clang/include/clang/AST/RecursiveASTVisitor.h +++ clang/include/clang/AST/RecursiveASTVisitor.h @@ -858,6 +858,7 @@ case TemplateArgument::Declaration: case TemplateArgument::Integral: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: return true; case TemplateArgument::Type: @@ -890,6 +891,7 @@ case TemplateArgument::Declaration: case TemplateArgument::Integral: case TemplateArgument::NullPtr: + case TemplateArgument::StructuralValue: return true; case TemplateArgument::Type: { Index: clang/include/clang/AST/PropertiesBase.td =================================================================== --- clang/include/clang/AST/PropertiesBase.td +++ clang/include/clang/AST/PropertiesBase.td @@ -808,6 +808,20 @@ return TemplateArgument(ctx, value, type, isDefaulted); }]>; } +let Class = PropertyTypeCase<TemplateArgument, "StructuralValue"> in { + def : Property<"value", APValue> { + let Read = [{ node.getAsStructuralValue() }]; + } + def : Property<"type", QualType> { + let Read = [{ node.getStructuralValueType() }]; + } + def : Property<"isDefaulted", Bool> { + let Read = [{ node.getIsDefaulted() }]; + } + def : Creator<[{ + return TemplateArgument(ctx, type, value, isDefaulted); + }]>; +} let Class = PropertyTypeCase<TemplateArgument, "Template"> in { def : Property<"name", TemplateName> { let Read = [{ node.getAsTemplateOrTemplatePattern() }]; Index: clang/include/clang/AST/ODRHash.h =================================================================== --- clang/include/clang/AST/ODRHash.h +++ clang/include/clang/AST/ODRHash.h @@ -25,6 +25,7 @@ namespace clang { +class APValue; class Decl; class IdentifierInfo; class NestedNameSpecifier; @@ -101,6 +102,8 @@ // Save booleans until the end to lower the size of data to process. void AddBoolean(bool value); + void AddStructuralValue(const APValue &); + static bool isSubDeclToBeProcessed(const Decl *D, const DeclContext *Parent); private: Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -59,6 +59,8 @@ C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ +- Implemented `P1907R1 <https://wg21.link/P1907R1>` which extends allowed non-type template argument + kinds with e.g. floating point values and pointers and references to subobjects. C++23 Feature Support ^^^^^^^^^^^^^^^^^^^^^ Index: clang-tools-extra/clangd/FindTarget.cpp =================================================================== --- clang-tools-extra/clangd/FindTarget.cpp +++ clang-tools-extra/clangd/FindTarget.cpp @@ -1032,6 +1032,7 @@ case TemplateArgument::Pack: case TemplateArgument::Type: case TemplateArgument::Expression: + case TemplateArgument::StructuralValue: break; // Handled by VisitType and VisitExpression. }; return RecursiveASTVisitor::TraverseTemplateArgumentLoc(A); Index: clang-tools-extra/clangd/DumpAST.cpp =================================================================== --- clang-tools-extra/clangd/DumpAST.cpp +++ clang-tools-extra/clangd/DumpAST.cpp @@ -143,6 +143,7 @@ TEMPLATE_ARGUMENT_KIND(Declaration); TEMPLATE_ARGUMENT_KIND(Template); TEMPLATE_ARGUMENT_KIND(TemplateExpansion); + TEMPLATE_ARGUMENT_KIND(StructuralValue); #undef TEMPLATE_ARGUMENT_KIND } llvm_unreachable("Unhandled ArgKind enum");
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits