From aea5a4931651d6f02fa604a9468a7c63a4b9ad6b Mon Sep 17 00:00:00 2001 From: Chris B Date: Mon, 13 Feb 2023 12:29:28 -0600 Subject: [PATCH] Introduce HLSLCXXOverload attribute (#5018) This change should have no functional impact on compilation. The goal is to remove cases where we rely on the subscript operator's name for special resolution rules and instead key the behavior off an attribute on the method declaration. The added test cases are not exhaustive but try to cover at least one object type exercising each code path modified in this change. Also added a test case for HLSL 2021 user defined array operator. This change partially addresses #4340. The remaining parts of that issue will be addressed in a future HLSL language revision. --- tools/clang/include/clang/Basic/Attr.td | 8 ++ tools/clang/lib/AST/ASTContextHLSL.cpp | 15 ++-- tools/clang/lib/Sema/SemaHLSL.cpp | 8 +- tools/clang/lib/Sema/SemaOverload.cpp | 28 ++++--- .../hlsl/objects/Buffer/buf_index.hlsl | 78 ++++++++++++++++--- .../hlsl/objects/RWTexture/RWTexture-ast.hlsl | 25 ++++++ .../subscript-operator.hlsl | 26 +++++++ .../hlsl/types/matrix/matrix-ast.hlsl | 29 +++++++ .../hlsl/types/vector/vector-ast.hlsl | 34 ++++++++ 9 files changed, 221 insertions(+), 30 deletions(-) create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/objects/RWTexture/RWTexture-ast.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/operator_overloading/subscript-operator.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/types/matrix/matrix-ast.hlsl create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/types/vector/vector-ast.hlsl diff --git a/tools/clang/include/clang/Basic/Attr.td b/tools/clang/include/clang/Basic/Attr.td index 0f404e22fd..d8aeee665f 100644 --- a/tools/clang/include/clang/Basic/Attr.td +++ b/tools/clang/include/clang/Basic/Attr.td @@ -917,6 +917,14 @@ def HLSLWaveOpsIncludeHelperLanes : InheritableAttr { let Documentation = [Undocumented]; } +// This attribute has no spelling, it is attached to methods by the compiler for +// methods that require C++ lookup rules. +def HLSLCXXOverload : InheritableAttr { + let Spellings = []; + let Subjects = SubjectList<[CXXMethod]>; + let Documentation = [Undocumented]; +} + // HLSL Change Ends // SPIRV Change Starts diff --git a/tools/clang/lib/AST/ASTContextHLSL.cpp b/tools/clang/lib/AST/ASTContextHLSL.cpp index da965c40a7..ce3ff38c30 100644 --- a/tools/clang/lib/AST/ASTContextHLSL.cpp +++ b/tools/clang/lib/AST/ASTContextHLSL.cpp @@ -291,10 +291,11 @@ void AddSubscriptOperator( vectorType = context.getConstType(vectorType); QualType indexType = intType; - CreateObjectFunctionDeclarationWithParams( - context, templateRecordDecl, vectorType, - ArrayRef(indexType), ArrayRef(StringRef("index")), - context.DeclarationNames.getCXXOperatorName(OO_Subscript), forConst); + auto methodDecl = CreateObjectFunctionDeclarationWithParams( + context, templateRecordDecl, vectorType, ArrayRef(indexType), + ArrayRef(StringRef("index")), + context.DeclarationNames.getCXXOperatorName(OO_Subscript), forConst); + methodDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); } /// Adds up-front support for HLSL matrix types (just the template declaration). @@ -351,7 +352,10 @@ void hlsl::AddHLSLMatrixTemplate(ASTContext& context, ClassTemplateDecl* vectorT static void AddHLSLVectorSubscriptAttr(Decl *D, ASTContext &context) { StringRef group = GetHLOpcodeGroupName(HLOpcodeGroup::HLSubscript); - D->addAttr(HLSLIntrinsicAttr::CreateImplicit(context, group, "", static_cast(HLSubscriptOpcode::VectorSubscript))); + D->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, group, "", + static_cast(HLSubscriptOpcode::VectorSubscript))); + D->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); } /// Adds up-front support for HLSL vector types (just the template declaration). @@ -941,6 +945,7 @@ CXXRecordDecl* hlsl::DeclareResourceType(ASTContext& context, bool bSampler) { functionDecl->addAttr(HLSLIntrinsicAttr::CreateImplicit( context, "op", "", static_cast(hlsl::IntrinsicOp::IOP_CreateResourceFromHeap))); + functionDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); return recordDecl; } diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 11340f2032..ac6951a523 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -2686,9 +2686,12 @@ class IntrinsicDefIter } }; -static void AddHLSLSubscriptAttr(Decl *D, ASTContext &context, HLSubscriptOpcode opcode) { +static void AddHLSLSubscriptAttr(Decl *D, ASTContext &context, + HLSubscriptOpcode opcode) { StringRef group = GetHLOpcodeGroupName(HLOpcodeGroup::HLSubscript); - D->addAttr(HLSLIntrinsicAttr::CreateImplicit(context, group, "", static_cast(opcode))); + D->addAttr(HLSLIntrinsicAttr::CreateImplicit(context, group, "", + static_cast(opcode))); + D->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); } static void CreateSimpleField(clang::ASTContext &context, CXXRecordDecl *recordDecl, StringRef Name, @@ -3459,6 +3462,7 @@ class HLSLExternalSource : public ExternalSemaSource { hlsl::CreateFunctionTemplateDecl( *m_context, recordDecl, functionDecl, reinterpret_cast(&templateTypeParmDecl), 1); + functionDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(*m_context)); // Add a .mips member if necessary. QualType uintType = m_context->UnsignedIntTy; diff --git a/tools/clang/lib/Sema/SemaOverload.cpp b/tools/clang/lib/Sema/SemaOverload.cpp index b763524cdc..7ed4bd0746 100644 --- a/tools/clang/lib/Sema/SemaOverload.cpp +++ b/tools/clang/lib/Sema/SemaOverload.cpp @@ -4869,16 +4869,20 @@ TryObjectArgumentInitialization(Sema &S, QualType FromType, // First check the qualifiers. QualType FromTypeCanon = S.Context.getCanonicalType(FromType); - // HLSL Change Starts - for calls other than subscript overloads, disregard const + // HLSL Change Starts + // HLSL Note: For calls that aren't compiler-generated C++ overloads, we + // disregard const qualifiers so that member functions can be called on + // `const` objects from constant buffer types. This should change in the + // future if we support const instance methods. FromTypeCanon.removeLocalRestrict(); // HLSL Change - disregard restrict. if (!S.getLangOpts().HLSL || - (Method != nullptr && Method->getDeclName() == S.Context.DeclarationNames.getCXXOperatorName(OO_Subscript))) { - // HLSL Change Ends - if (ImplicitParamType.getCVRQualifiers() - != FromTypeCanon.getLocalCVRQualifiers() && + (Method != nullptr && Method->hasAttr())) { + // HLSL Change Ends + if (ImplicitParamType.getCVRQualifiers() != + FromTypeCanon.getLocalCVRQualifiers() && !ImplicitParamType.isAtLeastAsQualifiedAs(FromTypeCanon)) { - ICS.setBad(BadConversionSequence::bad_qualifiers, - FromType, ImplicitParamType); + ICS.setBad(BadConversionSequence::bad_qualifiers, FromType, + ImplicitParamType); return ICS; } } // HLSL Change - end branch @@ -8664,12 +8668,12 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc, iterator &Best, bool UserDefinedConversion) { // HLSL Change Starts - // Function calls should use HLSL-style overloading. operator[] overloads - // (used for const support) aren't supported by the defined rules, so - // use C++ overload resolution for those. + // Function calls should use HLSL-style overloading. Except for compiler + // generated functions which are annotated as requiring C++ overload + // resolution like operator[] overloads where `const` methods are aren't + // supported by HLSL's defined rules. if (S.getLangOpts().HLSL && !empty() && begin()->Function != nullptr && - (begin()->Function->getDeclName() != - S.Context.DeclarationNames.getCXXOperatorName(OO_Subscript))) { + !begin()->Function->hasAttr()) { return ::hlsl::GetBestViableFunction(S, Loc, *this, Best); } // HLSL Change Ends diff --git a/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl index 61c0be40d5..c942db6806 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/objects/Buffer/buf_index.hlsl @@ -1,16 +1,72 @@ -// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s - -// Tests vector index on buffer load. -// CHECK:alloca [4 x float] -// CHECK:dx.op.bufferLoad.f32 -// CHECK:store -// CHECK:store -// CHECK:store -// CHECK:store -// CHECK:load +// RUN: %dxc -E main -T ps_6_0 -ast-dump-implicit %s | FileCheck %s -check-prefix=AST +// RUN: %dxc -E main -T ps_6_0 -fcgl %s | FileCheck %s -check-prefix=FCGL +// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s -check-prefix=OPTIMIZED Buffer buf; float4 main(uint i:I) : SV_Target { return buf[2][i]; -} \ No newline at end of file +} + +// Tests vector index converting to an expected load sequence. + +// Verify the generated AST for the subscript operator. + +// AST: ClassTemplateDecl {{0x[0-9a-fA-F]+}} <> implicit Buffer +// AST: CXXRecordDecl {{0x[0-9a-fA-F]+}} <> implicit class Buffer definition +// AST-NOT: CXXRecordDecl + +// This match is a little intentionally fuzzy. There's some oddities in DXC's +// ASTs which are a little tricky to resolve but we might want to fix someday. + +// AST: CXXMethodDecl {{0x[0-9a-fA-F]+}} <> operator[] 'const element &(unsigned int) const' +// AST-NEXT: ParmVarDecl {{0x[0-9a-fA-F]+}} <> index 'unsigned int' +// AST-NEXT: HLSLCXXOverloadAttr {{0x[0-9a-fA-F]+}} <> Implicit + +// Verify the initial IR generation correctly generates the handle and subscript +// intrinsics. These matches are some crazy long lines, so they are broken into +// multiple checks on the same line of text. + +// FCGL: [[Buffer:%.+]] = load %"class.Buffer >", +// FCGL-SAME: %"class.Buffer >"* @"\01?buf@@3V?$Buffer@V?$vector@M$03@@@@A" + +// FCGL: [[Handle:%.+]] = call %dx.types.Handle +// FCGL-SAME: @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.Buffer >\22)" +// FCGL-SAME: (i32 0, %"class.Buffer >" [[Buffer]]) + +// FCGL: [[AnnHandle:%.+]] = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle +// FCGL-SAME: (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.Buffer >\22)" +// FCGL-SAME: (i32 {{[0-9]+}}, +// FCGL-SAME: %dx.types.Handle [[Handle]], +// FCGL-SAME: %dx.types.ResourceProperties { i32 {{[0-9]+}}, i32 {{[0-9]+}} }, +// FCGL-SAME: %"class.Buffer >" undef) + +// FCGL: {{%.+}} = call <4 x float>* @"dx.hl.subscript.[].<4 x float>* (i32, %dx.types.Handle, i32)" +// FCGL-SAME: (i32 0, %dx.types.Handle [[AnnHandle]], i32 2) + + +// Verifies the optimized and loowered DXIL representation. +// OPTIMIZED: [[tmp:%.+]] = alloca [4 x float] +// OPTIMIZED: [[loaded:%.+]] = call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32( +// OPTIMIZED: [[X:%.+]] = extractvalue %dx.types.ResRet.f32 [[loaded]], 0 +// OPTIMIZED: [[Y:%.+]] = extractvalue %dx.types.ResRet.f32 [[loaded]], 1 +// OPTIMIZED: [[Z:%.+]] = extractvalue %dx.types.ResRet.f32 [[loaded]], 2 +// OPTIMIZED: [[W:%.+]] = extractvalue %dx.types.ResRet.f32 [[loaded]], 3 + +// OPTIMIZED-DAG: [[XAddr:%.+]] = getelementptr inbounds [4 x float], [4 x float]* [[tmp]], i32 0, i32 0 +// OPTIMIZED-DAG: [[YAddr:%.+]] = getelementptr inbounds [4 x float], [4 x float]* [[tmp]], i32 0, i32 1 +// OPTIMIZED-DAG: [[ZAddr:%.+]] = getelementptr inbounds [4 x float], [4 x float]* [[tmp]], i32 0, i32 2 +// OPTIMIZED-DAG: [[WAddr:%.+]] = getelementptr inbounds [4 x float], [4 x float]* [[tmp]], i32 0, i32 3 + +// OPTIMIZED-DAG: store float [[X]], float* [[XAddr]] +// OPTIMIZED-DAG: store float [[Y]], float* [[YAddr]] +// OPTIMIZED-DAG: store float [[Z]], float* [[ZAddr]] +// OPTIMIZED-DAG: store float [[W]], float* [[WAddr]] + +// OPTIMIZED: [[elAddr:%.+]] = getelementptr inbounds [4 x float], [4 x float]* [[tmp]], i32 0, i32 {{.*}} +// OPTIMIZED: [[Result:%.+]] = load float, float* [[elAddr]], align 4 + +// OPTIMIZED: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float [[Result]]) +// OPTIMIZED: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1, float [[Result]]) +// OPTIMIZED: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2, float [[Result]]) +// OPTIMIZED: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3, float [[Result]]) diff --git a/tools/clang/test/HLSLFileCheck/hlsl/objects/RWTexture/RWTexture-ast.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/objects/RWTexture/RWTexture-ast.hlsl new file mode 100644 index 0000000000..fb1d3cfe43 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/objects/RWTexture/RWTexture-ast.hlsl @@ -0,0 +1,25 @@ +// RUN: %dxc -T ps_6_0 -ast-dump-implicit %s | FileCheck %s + +RWTexture1D Tex; + +float4 main(uint i:I) : SV_Target { + return Tex[i]; +} + + +// Verify the template construction, and the underlying record. +// CHECK: ClassTemplateDecl {{0x[0-9a-fA-F]+}} <> implicit RWTexture1D +// CHECK: CXXRecordDecl {{0x[0-9a-fA-F]+}} <> implicit class RWTexture1D definition +// CHECK-NEXT: FinalAttr {{0x[0-9a-fA-F]+}} <> Implicit final +// CHECK-NEXT: FieldDecl {{0x[0-9a-fA-F]+}} <> implicit h 'element' + +// Make sure we don't process into another record decl. +// CHECK-NOT: CXXRecordDecl + +// Verify the FunctionTemplateDecl for the subscript operator and the method +// containing the appropriate atributes. +// CHECK: FunctionTemplateDecl {{0x[0-9a-fA-F]+}} <> operator[] +// CHECK-NEXT: TemplateTypeParmDecl {{0x[0-9a-fA-F]+}} <> class element +// CHECK-NEXT: CXXMethodDecl {{0x[0-9a-fA-F]+}} <> operator[] 'element &(unsigned int) const' +// CHECK-NEXT: ParmVarDecl {{0x[0-9a-fA-F]+}} <> index 'unsigned int' +// CHECK-NEXT: HLSLCXXOverloadAttr {{0x[0-9a-fA-F]+}} <> Implicit diff --git a/tools/clang/test/HLSLFileCheck/hlsl/operator_overloading/subscript-operator.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/operator_overloading/subscript-operator.hlsl new file mode 100644 index 0000000000..063952f126 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/operator_overloading/subscript-operator.hlsl @@ -0,0 +1,26 @@ +// RUN: %dxc -T lib_6_4 -HV 2021 -ast-dump %s | FileCheck %s + +struct MyVector3 { + float3 V; + + float operator[](int x) { + return V[x]; + } +}; + +float SomeFun(const MyVector3 V, int Idx) { + return V[Idx]; +} + +// This test verifies that under HLSL 2021, overload resolution for user-defined +// subscript operators follows HLSL overload resolution rules and ignores the +// const-ness of the implicit object parameter. + +// CHECK: CXXRecordDecl {{0x[0-9a-fA-F]+}} {{.*}} referenced struct MyVector3 definition +// CHECK: FieldDecl {{0x[0-9a-fA-F]+}} col:12 referenced V 'float3':'vector' +// CHECK-NEXT: CXXMethodDecl [[Operator:0x[0-9a-fA-F]+]] line:6:11 used operator[] 'float (int)' + +// CHECK: FunctionDecl {{0x[0-9a-fA-F]+}} line:11:7 SomeFun 'float (const MyVector3, int)' +// CHECK: CXXOperatorCallExpr {{0x[0-9a-fA-F]+}} 'float' +// CHECK-NEXT: ImplicitCastExpr {{0x[0-9a-fA-F]+}} 'float (*)(int)' +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'float (int)' lvalue CXXMethod [[Operator]] 'operator[]' 'float (int)' diff --git a/tools/clang/test/HLSLFileCheck/hlsl/types/matrix/matrix-ast.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/types/matrix/matrix-ast.hlsl new file mode 100644 index 0000000000..33086852ab --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/types/matrix/matrix-ast.hlsl @@ -0,0 +1,29 @@ +// RUN: %dxc -T lib_6_x -ast-dump-implicit %s | FileCheck %s + +// Verify the matrix template formtaion: matrix< T = float, rows = 4, cols = 4> +// CHECK: ClassTemplateDecl {{0x[0-9a-fA-F]+}} <> implicit matrix +// CHECK-NEXT: TemplateTypeParmDecl {{0x[0-9a-fA-F]+}} <> class element +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: NonTypeTemplateParmDecl {{0x[0-9a-fA-F]+}} <> 'int' row_count +// CHECK-NEXT: TemplateArgument expr +// CHECK-NEXT: IntegerLiteral {{0x[0-9a-fA-F]+}} <> 'int' 4 +// CHECK-NEXT: NonTypeTemplateParmDecl {{0x[0-9a-fA-F]+}} <> 'int' col_count +// CHECK-NEXT: TemplateArgument expr +// CHECK-NEXT: IntegerLiteral {{0x[0-9a-fA-F]+}} <> 'int' 4 + +// Verify the record with the final attribute and the matrix field as an +// ext_vector array. +// CHECK-NEXT: CXXRecordDecl {{0x[0-9a-fA-F]+}} <> implicit class matrix definition +// CHECK-NEXT: FinalAttr {{0x[0-9a-fA-F]+}} <> Implicit final +// CHECK-NEXT: FieldDecl {{0x[0-9a-fA-F]+}} <> implicit h 'element [row_count] __attribute__((ext_vector_type(col_count)))' + + +// Verify non-const subscript operator overload. +// CHECK-NEXT: CXXMethodDecl {{0x[0-9a-fA-F]+}} <> operator[] 'vector &(unsigned int)' +// CHECK-NEXT: ParmVarDecl {{0x[0-9a-fA-F]+}} <> index 'unsigned int' +// CHECK-NEXT: HLSLCXXOverloadAttr {{0x[0-9a-fA-F]+}} <> Implicit + +// Verify const subscript operator overload. +// CHECK-NEXT: CXXMethodDecl {{0x[0-9a-fA-F]+}} <> operator[] 'vector &const (unsigned int) const' +// CHECK-NEXT: ParmVarDecl {{0x[0-9a-fA-F]+}} <> index 'unsigned int' +// CHECK-NEXT: HLSLCXXOverloadAttr {{0x[0-9a-fA-F]+}} <> Implicit diff --git a/tools/clang/test/HLSLFileCheck/hlsl/types/vector/vector-ast.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/types/vector/vector-ast.hlsl new file mode 100644 index 0000000000..0ad236a4b2 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/types/vector/vector-ast.hlsl @@ -0,0 +1,34 @@ +// RUN: %dxc -T lib_6_x -ast-dump-implicit %s | FileCheck %s + +// Verify template and default arguments: vector + +// CHECK: ClassTemplateDecl {{0x[0-9a-fA-F]+}} <> implicit vector +// CHECK-NEXT: TemplateTypeParmDecl {{0x[0-9a-fA-F]+}} <> class element +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: NonTypeTemplateParmDecl {{0x[0-9a-fA-F]+}} <> 'int' element_count +// CHECK-NEXT: TemplateArgument expr +// CHECK-NEXT: IntegerLiteral {{0x[0-9a-fA-F]+}} <> 'int' 4 + +// Verify the class, final attribute and ext_vector field decl. +// CHECK-NEXT: CXXRecordDecl {{0x[0-9a-fA-F]+}} <> implicit class vector definition +// CHECK-NEXT: FinalAttr {{0x[0-9a-fA-F]+}} <> Implicit final +// CHECK-NEXT: FieldDecl {{0x[0-9a-fA-F]+}} <> implicit h 'element __attribute__((ext_vector_type(element_count)))' + +// Verify operator overloads for const vector subscript operators. +// CHECK-NEXT: CXXMethodDecl {{0x[0-9a-fA-F]+}} <> operator[] 'element &const (unsigned int) const' +// CHECK-NEXT: ParmVarDecl {{0x[0-9a-fA-F]+}} <> index 'unsigned int' +// CHECK-NEXT: HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} <> Implicit "subscript" "" 7 +// CHECK-NEXT: HLSLCXXOverloadAttr {{0x[0-9a-fA-F]+}} <> Implicit + +// Verify operator overloads for non-const vector subscript operators. +// CHECK-NEXT: CXXMethodDecl {{0x[0-9a-fA-F]+}} <> operator[] 'element &(unsigned int)' +// CHECK-NEXT: ParmVarDecl {{0x[0-9a-fA-F]+}} <> index 'unsigned int' +// CHECK-NEXT: HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} <> Implicit "subscript" "" 7 +// CHECK-NEXT: HLSLCXXOverloadAttr {{0x[0-9a-fA-F]+}} <> Implicit + +// DXC forces specializations to exist for common types. This verifies the +// specializtion for float4 which comes next in the chain. +// CHECK-NEXT: ClassTemplateSpecializationDecl {{0x[0-9a-fA-F]+}} <> implicit class vector definition +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: TemplateArgument integral 4 +