diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 375d68d2fa..62072a04e7 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -63,6 +63,9 @@ line upon naming the release. Refer to previous for appropriate section names. - The `shared` and `uniform` keywords are removed in HLSL 202x, with compatibility warnings available for earlier language versions [#8482](https://github.com/microsoft/DirectXShaderCompiler/issues/8482). +- HLSL 202x now supports variadic templates and template function parameter + packs, pack expansions, and `sizeof...()` + [#8905](https://github.com/microsoft/DirectXShaderCompiler/issues/8905). #### SPIR-V diff --git a/tools/clang/include/clang/Basic/DiagnosticGroups.td b/tools/clang/include/clang/Basic/DiagnosticGroups.td index f4003d6b50..399223af30 100644 --- a/tools/clang/include/clang/Basic/DiagnosticGroups.td +++ b/tools/clang/include/clang/Basic/DiagnosticGroups.td @@ -816,5 +816,8 @@ def HLSL2026Compat HLSL2026RemovedKeywords]>; def HLSLGroupshared202x : DiagGroup<"hlsl-groupshared-202x">; -def HLSL202xExtensions : DiagGroup<"hlsl-202x-extensions", [HLSLGroupshared202x]>; +def HLSLFoldExpressions : DiagGroup<"hlsl-fold-expressions">; +def HLSL202xExtensions + : DiagGroup<"hlsl-202x-extensions", [HLSLFoldExpressions, + HLSLGroupshared202x]>; // HLSL Change Ends diff --git a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td index 4fb2493f91..0e5e5aeca8 100644 --- a/tools/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -279,6 +279,10 @@ def warn_cxx98_compat_generalized_initializer_lists : Warning< InGroup, DefaultIgnore; def err_hlsl_compat_generalized_initializer_lists : Error< "generalized initializer lists are incompatible with HLSL">; +def warn_hlsl_fold_expression + : Warning<"fold expressions are a C++17 extension and are not part of " + "standard HLSL">, + InGroup; def err_init_list_bin_op : Error<"initializer list cannot be used on the " "%select{left|right}0 hand side of operator '%1'">; def warn_cxx98_compat_trailing_return_type : Warning< diff --git a/tools/clang/include/clang/Basic/LangOptions.h b/tools/clang/include/clang/Basic/LangOptions.h index 433b767c8d..e2495048a4 100644 --- a/tools/clang/include/clang/Basic/LangOptions.h +++ b/tools/clang/include/clang/Basic/LangOptions.h @@ -188,6 +188,13 @@ class LangOptions : public LangOptionsBase { return MSCompatibilityVersion >= MajorVersion * 10000000U; } + // HLSL Change Starts + /// Whether the active HLSL version rejects variadic templates. + bool HLSLDisallowsVariadicTemplates() const { + return HLSL && HLSLVersion < hlsl::LangStd::v202x; + } + // HLSL Change Ends + /// \brief Reset all of the options that are not considered when building a /// module. void resetNonModularOptions(); diff --git a/tools/clang/lib/AST/ASTContext.cpp b/tools/clang/lib/AST/ASTContext.cpp index dd6ec2784d..19511b0f41 100644 --- a/tools/clang/lib/AST/ASTContext.cpp +++ b/tools/clang/lib/AST/ASTContext.cpp @@ -3078,6 +3078,8 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, const FunctionProtoType::ExtProtoInfo &EPI, ArrayRef ParamMods) const { // HLSL Change - param mods size_t NumArgs = ArgArray.size(); + assert((ParamMods.empty() || ParamMods.size() == NumArgs) && + "parameter modifier count does not match parameter count"); // Unique functions, to guarantee there is only one function of a particular // structure. diff --git a/tools/clang/lib/AST/Decl.cpp b/tools/clang/lib/AST/Decl.cpp index 4c8c0ae268..f99acecf75 100644 --- a/tools/clang/lib/AST/Decl.cpp +++ b/tools/clang/lib/AST/Decl.cpp @@ -2370,15 +2370,23 @@ unsigned ParmVarDecl::getParameterIndexLarge() const { // HLSL Change Begins void ParmVarDecl::updateOutParamToRefType(ASTContext &C) { + QualType ParamType = getType(); + const PackExpansionType *Expansion = dyn_cast(ParamType); + if (Expansion) + ParamType = Expansion->getPattern(); + // Aggregate type will be indirect param convert to pointer type. // So don't update to ReferenceType. - if ((!getType()->isArrayType() && !getType()->isRecordType()) || - hlsl::IsHLSLVecMatType(getType())) - setType(C.getLValueReferenceType(getType(), false)); + if ((!ParamType->isArrayType() && !ParamType->isRecordType()) || + hlsl::IsHLSLVecMatType(ParamType)) + ParamType = C.getLValueReferenceType(ParamType, false); // Add restrict to out param. - QualType QT = getType(); - QT.addRestrict(); - setType(QT); + ParamType.addRestrict(); + + if (Expansion) + ParamType = + C.getPackExpansionType(ParamType, Expansion->getNumExpansions()); + setType(ParamType); } // HLSL Change Ends diff --git a/tools/clang/lib/Frontend/InitPreprocessor.cpp b/tools/clang/lib/Frontend/InitPreprocessor.cpp index b0a53650b5..621f5da94b 100644 --- a/tools/clang/lib/Frontend/InitPreprocessor.cpp +++ b/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -376,6 +376,8 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, // HLSL Version Builder.defineMacro("__HLSL_VERSION", Twine((unsigned int)LangOpts.HLSLVersion)); + if (!LangOpts.HLSLDisallowsVariadicTemplates()) + Builder.defineMacro("__cpp_variadic_templates", "200704"); // This define is enabled in Clang and allows conditionally compiling code // based on whether or not native 16-bit types are supported. if (!LangOpts.UseMinPrecision) diff --git a/tools/clang/lib/Lex/PPMacroExpansion.cpp b/tools/clang/lib/Lex/PPMacroExpansion.cpp index 16040d69c7..9dedf6ac2c 100644 --- a/tools/clang/lib/Lex/PPMacroExpansion.cpp +++ b/tools/clang/lib/Lex/PPMacroExpansion.cpp @@ -1156,7 +1156,9 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_unicode_literals", LangOpts.CPlusPlus11) .Case("cxx_unrestricted_unions", LangOpts.CPlusPlus11) .Case("cxx_user_literals", LangOpts.CPlusPlus11) - .Case("cxx_variadic_templates", LangOpts.CPlusPlus11) + .Case("cxx_variadic_templates", + LangOpts.CPlusPlus11 || + (LangOpts.HLSL && !LangOpts.HLSLDisallowsVariadicTemplates())) // C++1y features .Case("cxx_aggregate_nsdmi", LangOpts.CPlusPlus14) .Case("cxx_binary_literals", LangOpts.CPlusPlus14) @@ -1231,30 +1233,31 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) { // Because we inherit the feature list from HasFeature, this string switch // must be less restrictive than HasFeature's. return llvm::StringSwitch(Extension) - // C11 features supported by other languages as extensions. - .Case("c_alignas", true) - .Case("c_alignof", true) - .Case("c_atomic", true) - .Case("c_generic_selections", true) - .Case("c_static_assert", true) - .Case("c_thread_local", PP.getTargetInfo().isTLSSupported()) - // C++11 features supported by other languages as extensions. - .Case("cxx_atomic", LangOpts.CPlusPlus) - .Case("cxx_deleted_functions", LangOpts.CPlusPlus) - .Case("cxx_explicit_conversions", LangOpts.CPlusPlus) - .Case("cxx_inline_namespaces", LangOpts.CPlusPlus) - .Case("cxx_local_type_template_args", LangOpts.CPlusPlus) - .Case("cxx_nonstatic_member_init", LangOpts.CPlusPlus) - .Case("cxx_override_control", LangOpts.CPlusPlus) - .Case("cxx_range_for", LangOpts.CPlusPlus) - .Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus) - .Case("cxx_rvalue_references", LangOpts.CPlusPlus) - .Case("cxx_variadic_templates", LangOpts.CPlusPlus) - // C++1y features supported by other languages as extensions. - .Case("cxx_binary_literals", true) - .Case("cxx_init_captures", LangOpts.CPlusPlus11) - .Case("cxx_variable_templates", LangOpts.CPlusPlus) - .Default(false); + // C11 features supported by other languages as extensions. + .Case("c_alignas", true) + .Case("c_alignof", true) + .Case("c_atomic", true) + .Case("c_generic_selections", true) + .Case("c_static_assert", true) + .Case("c_thread_local", PP.getTargetInfo().isTLSSupported()) + // C++11 features supported by other languages as extensions. + .Case("cxx_atomic", LangOpts.CPlusPlus) + .Case("cxx_deleted_functions", LangOpts.CPlusPlus) + .Case("cxx_explicit_conversions", LangOpts.CPlusPlus) + .Case("cxx_inline_namespaces", LangOpts.CPlusPlus) + .Case("cxx_local_type_template_args", LangOpts.CPlusPlus) + .Case("cxx_nonstatic_member_init", LangOpts.CPlusPlus) + .Case("cxx_override_control", LangOpts.CPlusPlus) + .Case("cxx_range_for", LangOpts.CPlusPlus) + .Case("cxx_reference_qualified_functions", LangOpts.CPlusPlus) + .Case("cxx_rvalue_references", LangOpts.CPlusPlus) + .Case("cxx_variadic_templates", + LangOpts.CPlusPlus && !LangOpts.HLSLDisallowsVariadicTemplates()) + // C++1y features supported by other languages as extensions. + .Case("cxx_binary_literals", true) + .Case("cxx_init_captures", LangOpts.CPlusPlus11) + .Case("cxx_variable_templates", LangOpts.CPlusPlus) + .Default(false); } /// EvaluateHasIncludeCommon - Process a '__has_include("path")' diff --git a/tools/clang/lib/Parse/ParseDecl.cpp b/tools/clang/lib/Parse/ParseDecl.cpp index 1a3937ed5e..afe32b75eb 100644 --- a/tools/clang/lib/Parse/ParseDecl.cpp +++ b/tools/clang/lib/Parse/ParseDecl.cpp @@ -6109,12 +6109,11 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // been expanded or contains auto; otherwise, it is parsed as part of the // parameter-declaration-clause. if (Tok.is(tok::ellipsis) && D.getCXXScopeSpec().isEmpty() && - !getLangOpts().HLSL && // HLSL Change: do not support ellipsis + !getLangOpts().HLSLDisallowsVariadicTemplates() && // HLSL Change !((D.getContext() == Declarator::PrototypeContext || D.getContext() == Declarator::LambdaExprParameterContext || D.getContext() == Declarator::BlockLiteralContext) && - NextToken().is(tok::r_paren) && - !D.hasGroupingParens() && + NextToken().is(tok::r_paren) && !D.hasGroupingParens() && !Actions.containsUnexpandedParameterPacks(D) && D.getDeclSpec().getTypeSpecType() != TST_auto)) { SourceLocation EllipsisLoc = ConsumeToken(); diff --git a/tools/clang/lib/Parse/ParseExpr.cpp b/tools/clang/lib/Parse/ParseExpr.cpp index 9af3dbe610..76117ceb9b 100644 --- a/tools/clang/lib/Parse/ParseExpr.cpp +++ b/tools/clang/lib/Parse/ParseExpr.cpp @@ -1946,7 +1946,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { ConsumeToken(); // [C++11] 'sizeof' '...' '(' identifier ')' - if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof) && !getLangOpts().HLSL) { // HLSL Change + if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof) && + !getLangOpts().HLSLDisallowsVariadicTemplates()) { // HLSL Change SourceLocation EllipsisLoc = ConsumeToken(); SourceLocation LParenLoc, RParenLoc; IdentifierInfo *Name = nullptr; @@ -2747,9 +2748,12 @@ ExprResult Parser::ParseFoldExpression(ExprResult LHS, } } - Diag(EllipsisLoc, getLangOpts().CPlusPlus1z - ? diag::warn_cxx14_compat_fold_expression - : diag::ext_fold_expression); + if (getLangOpts().HLSL && !getLangOpts().HLSLDisallowsVariadicTemplates()) + Diag(EllipsisLoc, diag::warn_hlsl_fold_expression); + else + Diag(EllipsisLoc, getLangOpts().CPlusPlus1z + ? diag::warn_cxx14_compat_fold_expression + : diag::ext_fold_expression); T.consumeClose(); return Actions.ActOnCXXFoldExpr(T.getOpenLocation(), LHS.get(), Kind, @@ -2801,7 +2805,7 @@ bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, if (Tok.is(tok::ellipsis)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(Tok, diag::err_hlsl_variadic_templates); SkipUntil(tok::r_paren, StopBeforeMatch); Actions.CorrectDelayedTyposInExpr(Expr); diff --git a/tools/clang/lib/Parse/ParseInit.cpp b/tools/clang/lib/Parse/ParseInit.cpp index 1dcb20645d..1ef318f52e 100644 --- a/tools/clang/lib/Parse/ParseInit.cpp +++ b/tools/clang/lib/Parse/ParseInit.cpp @@ -438,7 +438,7 @@ ExprResult Parser::ParseBraceInitializer() { if (Tok.is(tok::ellipsis)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(Tok, diag::err_hlsl_unsupported_construct) << "expansion"; InitExprsOk = false; SkipUntil(tok::r_brace, StopBeforeMatch); diff --git a/tools/clang/lib/Parse/ParseTemplate.cpp b/tools/clang/lib/Parse/ParseTemplate.cpp index fbedf41caa..960dc6fa30 100644 --- a/tools/clang/lib/Parse/ParseTemplate.cpp +++ b/tools/clang/lib/Parse/ParseTemplate.cpp @@ -516,15 +516,15 @@ Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { SourceLocation EllipsisLoc; if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(EllipsisLoc, diag::err_hlsl_variadic_templates); return nullptr; } // HLSL Change Ends - Diag(EllipsisLoc, - getLangOpts().CPlusPlus11 - ? diag::warn_cxx98_compat_variadic_templates - : diag::ext_variadic_templates); + if (!getLangOpts().HLSL) // HLSL Change: HLSL has no C++98-compat warnings + Diag(EllipsisLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_variadic_templates + : diag::ext_variadic_templates); } // Grab the template parameter name (if given) @@ -620,9 +620,9 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) { SourceLocation EllipsisLoc; if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) { // HLSL Change Starts - if (getLangOpts().HLSL) + if (getLangOpts().HLSLDisallowsVariadicTemplates()) Diag(EllipsisLoc, diag::err_hlsl_variadic_templates); - else + else if (!getLangOpts().HLSL) // HLSL has no C++98-compat warnings // HLSL Change Ends Diag(EllipsisLoc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_variadic_templates @@ -1296,7 +1296,7 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) { SourceLocation EllipsisLoc; if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) { // HLSL Change Starts - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(EllipsisLoc, diag::err_hlsl_unsupported_construct) << "ellipsis"; SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); return true; diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 274adae6d7..a25e04f033 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -6079,7 +6079,7 @@ class HLSLExternalSource : public ExternalSemaSource { if (isMatrix || isVector) { Expr *expr = arg.getAsExpr(); llvm::APSInt constantResult; - if (expr != nullptr && + if (expr != nullptr && !expr->isValueDependent() && expr->isIntegerConstantExpr(constantResult, *m_context)) { if (CheckRangedTemplateArgument(argSrcLoc, constantResult, isVector)) diff --git a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp index e2e510d0c1..819de72767 100644 --- a/tools/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/tools/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2555,19 +2555,23 @@ Sema::SubstituteExplicitTemplateArguments( // Isolate our substituted parameters from our caller. LocalInstantiationScope InstScope(*this, /*MergeWithOuterScope*/true); + // HLSL Change Begin - HLSL needs the parameter decls to instantiate parameter + // modifiers correctly. + SmallVector ParamDecls; // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments. If the function has a trailing // return type, substitute it after the arguments to ensure we substitute // in lexical order. if (Proto->hasTrailingReturn()) { - if (SubstParmTypes(Function->getLocation(), - Function->param_begin(), Function->getNumParams(), + if (SubstParmTypes(Function->getLocation(), Function->param_begin(), + Function->getNumParams(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, &ParamDecls)) return TDK_SubstitutionFailure; } - + // HLSL Change End + // Instantiate the return type. QualType ResultType; { @@ -2594,22 +2598,30 @@ Sema::SubstituteExplicitTemplateArguments( if (ResultType.isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; } - + // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments if we didn't do so earlier. + // HLSL Change Begin - Pass ParamDecls to SubstParmTypes to correctly + // instantiate parameter modifiers. if (!Proto->hasTrailingReturn() && - SubstParmTypes(Function->getLocation(), - Function->param_begin(), Function->getNumParams(), + SubstParmTypes(Function->getLocation(), Function->param_begin(), + Function->getNumParams(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, &ParamDecls)) return TDK_SubstitutionFailure; + // HLSL Change - End if (FunctionType) { - // HLSL Change - FIX - We should move param mods to parameter QualTypes + // HLSL Change Begin - Pass ParamDecls to SubstParmTypes to correctly + // instantiate parameter modifiers. + SmallVector ParamMods; + ParamMods.reserve(ParamDecls.size()); + for (ParmVarDecl *Param : ParamDecls) + ParamMods.push_back(Param ? Param->getParamModifiers() + : hlsl::ParameterModifier()); *FunctionType = BuildFunctionType( ResultType, ParamTypes, Function->getLocation(), - Function->getDeclName(), Proto->getExtProtoInfo(), - cast(Function->getType())->getParamMods()); + Function->getDeclName(), Proto->getExtProtoInfo(), ParamMods); // HLSL Change - End if (FunctionType->isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; diff --git a/tools/clang/lib/Sema/SemaTemplateVariadic.cpp b/tools/clang/lib/Sema/SemaTemplateVariadic.cpp index b575bfaf4d..e1dd3dce56 100644 --- a/tools/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/tools/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -545,7 +545,7 @@ bool Sema::CheckParameterPacksForExpansion( std::pair FirstPack; bool HaveFirstPack = false; - if (getLangOpts().HLSL) { + if (getLangOpts().HLSLDisallowsVariadicTemplates()) { Diag(EllipsisLoc, diag::err_hlsl_variadic_templates); return true; } diff --git a/tools/clang/lib/Sema/SemaType.cpp b/tools/clang/lib/Sema/SemaType.cpp index 7465cc2cec..223c633ec3 100644 --- a/tools/clang/lib/Sema/SemaType.cpp +++ b/tools/clang/lib/Sema/SemaType.cpp @@ -4280,14 +4280,14 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // parameter packs in the type of the non-type template parameter, then // it expands those parameter packs. // HLSL Change Starts - if (LangOpts.HLSL) { + if (LangOpts.HLSLDisallowsVariadicTemplates()) { S.Diag(D.getEllipsisLoc(), diag::err_hlsl_variadic_templates); break; } // HLSL Change Ends if (T->containsUnexpandedParameterPack()) T = Context.getPackExpansionType(T, None); - else + else if (!LangOpts.HLSL) // HLSL Change: HLSL has no C++98-compat warnings S.Diag(D.getEllipsisLoc(), LangOpts.CPlusPlus11 ? diag::warn_cxx98_compat_variadic_templates diff --git a/tools/clang/lib/Sema/TreeTransform.h b/tools/clang/lib/Sema/TreeTransform.h index ef3a83c988..0d1c4fb2e0 100644 --- a/tools/clang/lib/Sema/TreeTransform.h +++ b/tools/clang/lib/Sema/TreeTransform.h @@ -4753,8 +4753,17 @@ QualType TreeTransform::TransformFunctionProtoType( !std::equal(T->param_type_begin(), T->param_type_end(), ParamTypes.begin()) || EPIChanged) { // HLSL Change - FIX - We should move param mods to parameter QualTypes + SmallVector ExpandedParamMods; + ArrayRef ParamMods = T->getParamMods(); + if (ParamMods.size() != ParamTypes.size()) { + ExpandedParamMods.reserve(ParamDecls.size()); + for (ParmVarDecl *Param : ParamDecls) + ExpandedParamMods.push_back(Param ? Param->getParamModifiers() + : hlsl::ParameterModifier()); + ParamMods = ExpandedParamMods; + } Result = getDerived().RebuildFunctionProtoType(ResultType, ParamTypes, - T->getParamMods(), EPI); + ParamMods, EPI); // HLSL Change - End if (Result.isNull()) return QualType(); @@ -10721,9 +10730,19 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { QualType exprResultType = getDerived().TransformType(exprFunctionType->getReturnType()); - // HLSL Change - FIX - We should move param mods to parameter QualTypes + // HLSL Change - Fix up the parameter modifiers for the block's parameters. + SmallVector ExpandedParamMods; + ArrayRef ParamMods = + exprFunctionType->getParamMods(); + if (ParamMods.size() != paramTypes.size()) { + ExpandedParamMods.reserve(params.size()); + for (ParmVarDecl *Param : params) + ExpandedParamMods.push_back(Param ? Param->getParamModifiers() + : hlsl::ParameterModifier()); + ParamMods = ExpandedParamMods; + } QualType functionType = getDerived().RebuildFunctionProtoType( - exprResultType, paramTypes, exprFunctionType->getParamMods(), + exprResultType, paramTypes, ParamMods, exprFunctionType->getExtProtoInfo()); // HLSL Change - End blockScope->FunctionType = functionType; @@ -10965,7 +10984,7 @@ TreeTransform::RebuildDependentSizedExtVectorType(QualType ElementType, return SemaRef.BuildExtVectorType(ElementType, SizeExpr, AttributeLoc); } -// HLSL Change - FIX - We should move param mods to parameter QualTypes +// HLSL Change - Fix up the parameter modifiers for the function prototype. template QualType TreeTransform::RebuildFunctionProtoType( QualType T, diff --git a/tools/clang/test/CodeGenSPIRV/variadic.templates.basic.hlsl b/tools/clang/test/CodeGenSPIRV/variadic.templates.basic.hlsl new file mode 100644 index 0000000000..764c50b94b --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/variadic.templates.basic.hlsl @@ -0,0 +1,41 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -fcgl %s -spirv | FileCheck %s + +// Verify SPIR-V code generation for function parameter packs, pack expansions, +// and sizeof...(). + +template +T Sum(T First) { + return First; +} + +template +T Sum(T First, U Second, Rest... Others) { + return First + Sum(Second, Others...); +} + +template +uint CountArgs(Args... args) { + return sizeof...(Args); +} + +// CHECK-LABEL: %src_main = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum +// CHECK: OpFunctionCall %uint %CountArgs +// CHECK-LABEL: %Sum = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum_0 +// CHECK-LABEL: %CountArgs = OpFunction %uint None +// CHECK-NEXT: %args = OpFunctionParameter +// CHECK-NEXT: %args_0 = OpFunctionParameter +// CHECK-NEXT: %args_1 = OpFunctionParameter +// CHECK: OpReturnValue %uint_3 +// CHECK-LABEL: %Sum_0 = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum_1 +// CHECK-LABEL: %Sum_1 = OpFunction %float None +// CHECK: OpFunctionCall %float %Sum_2 +// CHECK-LABEL: %Sum_2 = OpFunction %float None +float main() : SV_Target { + float total = Sum(1.0, 2.0, 3.0, 4.0); + // Keep CountArgs in the unoptimized SPIR-V without changing the result. + total += 0 * (float)CountArgs(1, 2, 3); + return total; +} diff --git a/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl b/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl new file mode 100644 index 0000000000..b6aa079025 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/variadic.templates.builtin.hlsl @@ -0,0 +1,31 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -fcgl %s -spirv | FileCheck %s + +// Verify pack expansion into HLSL vector and matrix templates. + +template +vector MakeVector(T First, Rest... Others) { + return vector(First, Others...); +} + +template +struct MatrixWrapper { + matrix M; +}; + +// CHECK: OpName %MatrixWrapper "MatrixWrapper" +// CHECK: OpMemberName %MatrixWrapper 0 "M" +// CHECK: OpName %MakeVector "MakeVector" + +// CHECK-LABEL: %src_main = OpFunction %v4float +float4 main(float4 a : A) : SV_Target { + // CHECK: OpFunctionCall %v4float %MakeVector + vector v = MakeVector(a.x, a.y, a.z, a.w); + + MatrixWrapper mw; + // CHECK: OpCompositeConstruct %mat2v2float + mw.M = matrix(v.x, v.y, v.z, v.w); + + return float4(mw.M._11, mw.M._22, v.z, v.w); +} + +// CHECK-LABEL: %MakeVector = OpFunction %v4float diff --git a/tools/clang/test/CodeGenSPIRV/variadic.templates.initlist-scalarize.hlsl b/tools/clang/test/CodeGenSPIRV/variadic.templates.initlist-scalarize.hlsl new file mode 100644 index 0000000000..d65bec15a7 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/variadic.templates.initlist-scalarize.hlsl @@ -0,0 +1,88 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x -fcgl %s -spirv | FileCheck %s + +// Verify that pack expansions and equivalent explicit initializers produce +// the expected SPIR-V composite structure. + +struct PairOf2 { + float2 lo; + float2 hi; +}; + +template +float4 PackVectorMixed(Ts... vals) { + float4 v = { vals... }; + return v; +} +float4 ManualVectorMixed(float2 a, float2 b) { + float4 v = { a, b }; + return v; +} + +template +float2 PackArrayOverflow(Ts... vals) { + float2 arr[2] = { vals... }; + return arr[0] + arr[1]; +} +float2 ManualArrayOverflow(float a, float b, float c, float d) { + float2 arr[2] = { a, b, c, d }; + return arr[0] + arr[1]; +} + +template +PairOf2 PackStruct(Ts... vals) { + PairOf2 s = { vals... }; + return s; +} +PairOf2 ManualStruct(float2 a, float2 b) { + PairOf2 s = { a, b }; + return s; +} + +// CHECK-LABEL: %PackVectorMixed = OpFunction %v4float +// CHECK: [[PACK_V0:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[PACK_V1:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: [[PACK_V2:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[PACK_V3:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: OpCompositeConstruct %v4float [[PACK_V0]] [[PACK_V1]] +// CHECK-SAME: [[PACK_V2]] [[PACK_V3]] +// CHECK-LABEL: %ManualVectorMixed = OpFunction %v4float +// CHECK: [[MANUAL_V0:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[MANUAL_V1:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: [[MANUAL_V2:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 0 +// CHECK: [[MANUAL_V3:%[0-9]+]] = OpCompositeExtract %float {{%[0-9]+}} 1 +// CHECK: OpCompositeConstruct %v4float [[MANUAL_V0]] [[MANUAL_V1]] +// CHECK-SAME: [[MANUAL_V2]] [[MANUAL_V3]] + +// CHECK-LABEL: %PackArrayOverflow = OpFunction %v2float +// CHECK: [[PACK_A0:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: [[PACK_A1:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: OpCompositeConstruct %_arr_v2float_uint_2 [[PACK_A0]] [[PACK_A1]] +// CHECK-LABEL: %ManualArrayOverflow = OpFunction %v2float +// CHECK: [[MANUAL_A0:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: [[MANUAL_A1:%[0-9]+]] = OpCompositeConstruct %v2float +// CHECK: OpCompositeConstruct %_arr_v2float_uint_2 [[MANUAL_A0]] [[MANUAL_A1]] + +// CHECK-LABEL: %PackStruct = OpFunction %PairOf2 +// CHECK: [[PACK_S0:%[0-9]+]] = OpLoad %v2float +// CHECK: [[PACK_S1:%[0-9]+]] = OpLoad %v2float +// CHECK: OpCompositeConstruct %PairOf2 [[PACK_S0]] [[PACK_S1]] +// CHECK-LABEL: %ManualStruct = OpFunction %PairOf2 +// CHECK: [[MANUAL_S0:%[0-9]+]] = OpLoad %v2float +// CHECK: [[MANUAL_S1:%[0-9]+]] = OpLoad %v2float +// CHECK: OpCompositeConstruct %PairOf2 [[MANUAL_S0]] [[MANUAL_S1]] +float4 main(float4 inp : A) : SV_Target { + float2 lo = inp.xy; + float2 hi = inp.zw; + + float4 vp = PackVectorMixed(lo, hi); + float4 vm = ManualVectorMixed(lo, hi); + + float2 op = PackArrayOverflow(inp.x, inp.y, inp.z, inp.w); + float2 om = ManualArrayOverflow(inp.x, inp.y, inp.z, inp.w); + + PairOf2 sp = PackStruct(lo, hi); + PairOf2 sm = ManualStruct(lo, hi); + + return vp + vm + float4(op + om, 0, 0) + + float4(sp.lo + sm.lo, sp.hi + sm.hi); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-202x.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-202x.hlsl new file mode 100644 index 0000000000..acfbba4749 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-202x.hlsl @@ -0,0 +1,24 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s | FileCheck %s +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float 1.300000e+01) + +// Verify template and function parameter packs, pack expansions, and +// sizeof...() in DXIL. + +template +T Sum(T First) { + return First; +} + +template +T Sum(T First, U Second, Rest... Others) { + return First + Sum(Second, Others...); +} + +template +uint CountArgs(Args... args) { + return sizeof...(Args); +} + +float main() : SV_Target { + return Sum(1.0, 2.0, 3.0, 4.0) + CountArgs(1, 2, 3); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-builtin-templates.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-builtin-templates.hlsl new file mode 100644 index 0000000000..866a7e44d1 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-builtin-templates.hlsl @@ -0,0 +1,36 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s | FileCheck %s + +// Verify pack expansion into HLSL vector, matrix, and resource templates. + +template +struct Holder { + StructuredBuffer Buf; +}; +Holder g_Holder : register(t0); + +template +vector MakeVector(T First, Rest... Others) { + return vector(First, Others...); +} + +template +struct MatrixWrapper { + matrix M; +}; + +// CHECK: call %dx.types.Handle @dx.op.createHandle(i32 57 +// CHECK: call %dx.types.ResRet.f32 @dx.op.bufferLoad.f32 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3 +float4 main(float4 a : A) : SV_Target { + vector v = MakeVector(a.x, a.y, a.z, a.w); + + MatrixWrapper mw; + mw.M = matrix(v.x, v.y, v.z, v.w); + + float bufVal = g_Holder.Buf.Load(0); + + return float4(mw.M._11, mw.M._22, bufVal, v.w); +} diff --git a/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-initlist-scalarize.hlsl b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-initlist-scalarize.hlsl new file mode 100644 index 0000000000..79c6abdd35 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/hlsl/templates/variadic-initlist-scalarize.hlsl @@ -0,0 +1,69 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s | FileCheck %s + +// Compare pack-expanded and explicit initializer-list scalarization using +// shader inputs so the results remain observable in DXIL. + +struct PairOf2 { + float2 lo; + float2 hi; +}; + +// Vector target filled from mixed vector+vector pack elements. +template +float4 PackVectorMixed(Ts... vals) { + float4 v = { vals... }; + return v; +} +float4 ManualVectorMixed(float2 a, float2 b) { + float4 v = { a, b }; + return v; +} + +// A flat initializer list overflowing across the boundary of an array of +// vectors. +template +float2 PackArrayOverflow(Ts... vals) { + float2 arr[2] = { vals... }; + return arr[0] + arr[1]; +} +float2 ManualArrayOverflow(float a, float b, float c, float d) { + float2 arr[2] = { a, b, c, d }; + return arr[0] + arr[1]; +} + +// Struct-member scalarization, including a vector-typed member. +template +PairOf2 PackStruct(Ts... vals) { + PairOf2 s = { vals... }; + return s; +} +PairOf2 ManualStruct(float2 a, float2 b) { + PairOf2 s = { a, b }; + return s; +} + +// CHECK: define void @main() +// CHECK-DAG: fmul fast float %{{.*}}, 2.000000e+00 +// CHECK-DAG: fmul fast float %{{.*}}, 6.000000e+00 +// CHECK-DAG: fmul fast float %{{.*}}, 4.000000e+00 +// CHECK-DAG: fmul fast float %{{.*}}, 4.000000e+00 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2 +// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3 +float4 main(float4 inp : A) : SV_Target { + float2 lo = inp.xy; + float2 hi = inp.zw; + + float4 vp = PackVectorMixed(lo, hi); + float4 vm = ManualVectorMixed(lo, hi); + + float2 op = PackArrayOverflow(inp.x, inp.y, inp.z, inp.w); + float2 om = ManualArrayOverflow(inp.x, inp.y, inp.z, inp.w); + + PairOf2 sp = PackStruct(lo, hi); + PairOf2 sm = ManualStruct(lo, hi); + + return vp + vm + float4(op + om, 0, 0) + + float4(sp.lo + sm.lo, sp.hi + sm.hi); +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-base-class-unsupported.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-base-class-unsupported.hlsl new file mode 100644 index 0000000000..a73c6bf518 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-base-class-unsupported.hlsl @@ -0,0 +1,23 @@ +// RUN: %dxc -E main -T ps_6_0 -HV 202x %s -verify + +// HLSL supports only one fixed base type, not base-class packs. + +struct Base1 { + float x; +}; +struct Base2 { + float y; +}; + +// expected-error@+3 {{base type ellipsis is unsupported in HLSL}} +// expected-error@+2 {{multiple concrete base types specified}} +template +struct Derived : Bases... { + float z; +}; + +float main() : SV_Target { + Derived d; // expected-note {{in instantiation of template class 'Derived' requested here}} + d.z = 1.0; + return d.z; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize-negative.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize-negative.hlsl new file mode 100644 index 0000000000..d6e5a69b84 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize-negative.hlsl @@ -0,0 +1,58 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s + +// Compare diagnostics for pack-expanded and explicit initializer lists. + +float4 ManualTooFew(float a, float b) { + float4 v = { a, b }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return v; +} + +template +float4 PackTooFew(Ts... vals) { + float4 v = { vals... }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return v; +} + +float3 ManualTooMany(float a, float b, float c, float d) { + float3 v = { a, b, c, d }; // expected-error {{too many elements in vector initialization (expected 3 elements, have 4)}} + return v; +} + +template +float3 PackTooMany(Ts... vals) { + float3 v = { vals... }; // expected-error {{too many elements in vector initialization (expected 3 elements, have 4)}} + return v; +} + +struct S { float a; float2 b; float c; }; + +S ManualStructTooFew(float a, float b) { + S s = { a, b }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return s; +} + +template +S PackStructTooFew(Ts... vals) { + S s = { vals... }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 2)}} + return s; +} + +float2x2 ManualMatrixTooFew(float a, float b, float c) { + float2x2 m = { a, b, c }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 3)}} + return m; +} + +template +float2x2 PackMatrixTooFew(Ts... vals) { + float2x2 m = { vals... }; // expected-error {{too few elements in vector initialization (expected 4 elements, have 3)}} + return m; +} + +export +float UseAll() { + float4 v1 = PackTooFew(1.0, 2.0); // expected-note {{in instantiation of function template specialization 'PackTooFew' requested here}} + float3 v2 = PackTooMany(1.0, 2.0, 3.0, 4.0); // expected-note {{in instantiation of function template specialization 'PackTooMany' requested here}} + S s = PackStructTooFew(1.0, 2.0); // expected-note {{in instantiation of function template specialization 'PackStructTooFew' requested here}} + float2x2 m = PackMatrixTooFew(1.0, 2.0, 3.0); // expected-note {{in instantiation of function template specialization 'PackMatrixTooFew' requested here}} + return v1.x + v2.x + s.a + m._11 + ManualTooFew(1.0, 2.0).x + ManualTooMany(1.0, 2.0, 3.0, 4.0).x + ManualStructTooFew(1.0, 2.0).a + ManualMatrixTooFew(1.0, 2.0, 3.0)._11; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize.hlsl new file mode 100644 index 0000000000..80b7413ddb --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-initlist-scalarize.hlsl @@ -0,0 +1,106 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s +// RUN: %dxc -T lib_6_3 -HV 202x -ast-dump %s 2>&1 | FileCheck %s + +// Compare pack-expanded and explicit initializer-list scalarization. + +// expected-no-diagnostics + +struct ScalarizeStruct { + float a; + float2 b; + float c; +}; + +// Exact-count vector scalarization. + +// CHECK: FunctionDecl {{.*}} used ManualVectorExact 'float4 (float, float, float, float)' +// CHECK: InitListExpr {{.*}} 'float4':'vector' +float4 ManualVectorExact(float a, float b, float c, float d) { + float4 v = { a, b, c, d }; + return v; +} + +// CHECK: FunctionDecl {{.*}} used PackVectorExact 'float4 (float, float, float, float)' +// CHECK: InitListExpr {{.*}} 'float4':'vector' +template +float4 PackVectorExact(Ts... vals) { + float4 v = { vals... }; + return v; +} + +// Mixed scalar and vector elements. + +// CHECK: FunctionDecl {{.*}} used ManualVectorMixed 'float3 (float2, float)' +// CHECK: InitListExpr {{.*}} 'float3':'vector' +float3 ManualVectorMixed(float2 a, float b) { + float3 v = { a, b }; + return v; +} + +// CHECK: FunctionDecl {{.*}} used PackVectorMixed 'float3 (vector, float)' +// CHECK: InitListExpr {{.*}} 'float3':'vector' +template +float3 PackVectorMixed(Ts... vals) { + float3 v = { vals... }; + return v; +} + +// Scalar overflow across array elements. + +float2 ManualArrayOverflow(float a, float b, float c, float d) { + float2 arr[2] = { a, b, c, d }; + return arr[0] + arr[1]; +} + +template +float2 PackArrayOverflow(Ts... vals) { + float2 arr[2] = { vals... }; + return arr[0] + arr[1]; +} + +// Struct-member scalarization. + +ScalarizeStruct ManualStruct(float a, float2 b, float c) { + ScalarizeStruct s = { a, b, c }; + return s; +} + +template +ScalarizeStruct PackStruct(Ts... vals) { + ScalarizeStruct s = { vals... }; + return s; +} + +// Matrix scalarization. + +float2x2 ManualMatrix(float a, float b, float c, float d) { + float2x2 m = { a, b, c, d }; + return m; +} + +template +float2x2 PackMatrix(Ts... vals) { + float2x2 m = { vals... }; + return m; +} + +export +float TestInitListScalarization() { + float4 v1 = ManualVectorExact(1.0, 2.0, 3.0, 4.0); + float4 v2 = PackVectorExact(1.0, 2.0, 3.0, 4.0); + + float2 b2 = float2(1.0, 2.0); + float3 v3 = ManualVectorMixed(b2, 3.0); + float3 v4 = PackVectorMixed(b2, 3.0); + + float o1 = ManualArrayOverflow(1.0, 2.0, 3.0, 4.0).x; + float o2 = PackArrayOverflow(1.0, 2.0, 3.0, 4.0).x; + + ScalarizeStruct s1 = ManualStruct(1.0, b2, 2.0); + ScalarizeStruct s2 = PackStruct(1.0, b2, 2.0); + + float2x2 m1 = ManualMatrix(1.0, 2.0, 3.0, 4.0); + float2x2 m2 = PackMatrix(1.0, 2.0, 3.0, 4.0); + + return v1.x + v2.x + v3.x + v4.x + o1 + o2 + s1.a + s2.a + m1._11 + m2._11; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl new file mode 100644 index 0000000000..8d5059ee8b --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-negative.hlsl @@ -0,0 +1,65 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s + +// Verify unsupported constructs and standard variadic-template diagnostics. + +// HLSL does not support multiple or variadic base classes. +struct Base1 { + float x; +}; +struct Base2 { + float y; +}; + +template +struct Derived : Bases... { + // expected-error@-1{{base type ellipsis is unsupported in HLSL}} + // expected-error@-2{{multiple concrete base types specified}} + float z; +}; + +void UseDerived() { + Derived d; + // expected-note@-1{{in instantiation of template class 'Derived' requested here}} + d.z = 0; +} + +// Class template parameter packs must remain last. +template +// expected-error@-1{{template parameter pack must be the last template parameter}} +struct PackNotLast {}; + +// sizeof...() only applies to the name of an actual parameter pack. +uint NotAPack() { + return sizeof...(NotAPack); + // expected-error@-1{{'NotAPack' does not refer to the name of a parameter pack}} +} + +// C-style variadic functions remain unsupported. +void CStyleVarArgs(int a, ...); +// expected-error@-1{{variadic arguments is unsupported in HLSL}} + +// Mismatched pack arguments use ordinary overload-resolution diagnostics. +template +struct Zipper { + template + static uint Count(Ts... ts, Us... us) { + // expected-note@-1{{candidate function not viable: requires 3 arguments, but 2 were provided}} + return sizeof...(Ts) + sizeof...(Us); + } +}; + +uint TestMismatchedPackArgs() { + return Zipper::Count(1, 2.0); + // expected-error@-1{{no matching function for call to 'Count'}} +} + +template +void ExplicitOut(out Ts... values) { + // expected-note@-1{{for 2nd argument}} +} + +void TestExplicitOutRValue() { + int first; + ExplicitOut(first, 1.0); + // expected-error@-1{{no matching function for call to 'ExplicitOut'}} +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl new file mode 100644 index 0000000000..2c3b2a8313 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates-pre202x.hlsl @@ -0,0 +1,47 @@ +// RUN: %dxc -T lib_6_3 -HV 2021 -verify %s + +// Variadic templates are a HLSL 202x feature. HLSL 2021 (the version prior +// to 202x that supports templates at all) must continue to reject the C++ +// variadic template syntax. + +#if __has_feature(cxx_variadic_templates) +#error HLSL 2021 should not report variadic templates as a feature +#endif +#if __has_extension(cxx_variadic_templates) +#error HLSL 2021 should not report variadic templates as an extension +#endif +#ifdef __cpp_variadic_templates +#error HLSL 2021 should not define __cpp_variadic_templates +#endif + +template +// expected-error@-1{{variadic templates are not supported in HLSL}} +T Sum(T First, Rest... Others) { + // expected-error@-1{{unknown type name 'Rest'}} + // expected-error@-2{{variadic arguments is unsupported in HLSL}} + // expected-error@-3{{expected ')'}} + // expected-note@-4{{to match this '('}} + return First; +} + +template +// expected-error@-1{{'...' must be innermost component of anonymous pack declaration}} +// expected-error@-2{{variadic templates are not supported in HLSL}} +// expected-error@-3{{expected ',' or '>' in template-parameter-list}} +struct IntPack {}; + +uint CallSizeofPack() { + // sizeof...() is only meaningful with variadic templates, and remains + // unsupported before HLSL 202x. + return sizeof...(Values); + // expected-error@-1{{expected expression}} +} + +int CallInitListExpansion() { + int a = 1, b = 2, c = 3; + // Pack expansion inside a braced-init-list also remains unsupported + // before HLSL 202x. + int values[3] = {a, b, c...}; + // expected-error@-1{{expansion is unsupported in HLSL}} + return values[0]; +} diff --git a/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl new file mode 100644 index 0000000000..12a9636d16 --- /dev/null +++ b/tools/clang/test/SemaHLSL/v202x/templates/variadic-templates.hlsl @@ -0,0 +1,162 @@ +// RUN: %dxc -T lib_6_3 -HV 202x -verify %s +// RUN: %dxc -T lib_6_3 -HV 202x -ast-dump %s 2>&1 | FileCheck %s + +// Verify HLSL 202x variadic templates in semantic analysis and the AST. + +#if !__has_feature(cxx_variadic_templates) +#error HLSL 202x should report variadic templates as a feature +#endif +#if !__has_extension(cxx_variadic_templates) +#error HLSL 202x should report variadic templates as an extension +#endif +#if __cpp_variadic_templates != 200704 +#error HLSL 202x should define __cpp_variadic_templates +#endif + +// Template type parameter pack. +// CHECK: FunctionTemplateDecl {{.*}} Sum +// CHECK: TemplateTypeParmDecl {{.*}} typename ... Rest +template +T Sum(T First, Rest... Others) { + return First; +} + +template +T Sum(T First) { + return First; +} + +// Non-type template parameter pack. +// CHECK: ClassTemplateDecl {{.*}} IntPack +// CHECK: NonTypeTemplateParmDecl {{.*}} 'int' ... Values +template +struct IntPack { + static const int Count = sizeof...(Values); +}; + +// A pack of types forwarded as a template argument list to another +// variadic template. +template +struct Tuple { + static const uint Size = sizeof...(Args); +}; + +template +uint CountArgs(Args... args) { + return sizeof...(Args); +} + +template +uint FoldSum(Args... args) { + return (args + ...); // expected-warning {{fold expressions are a C++17 extension and are not part of standard HLSL}} +} + +// Pack expansion forwarding a parameter pack as a template argument list. +template +uint Forward(Args... args) { + return Tuple::Size; +} + +// Recursive class-template partial specialization peeling one type off a +// pack at a time is the canonical variadic-template pattern for +// compile-time recursion and depends on partial ordering between the +// primary template and the partial specialization -- Sema machinery +// that HLSL previously never exercised at all. +// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} PackLength +// CHECK: TemplateTypeParmDecl {{.*}} typename ... Rest +template +struct PackLength { + static const uint Value = 0; +}; +template +struct PackLength { + static const uint Value = 1 + PackLength::Value; +}; +// The fully-empty-pack explicit specialization is also exercised, since it +// is the recursion's base case. +template <> +struct PackLength<> { + static const uint Value = 0; +}; + +template +vector MakeVector(T First, Rest... Others) { + return vector(First, Others...); +} + +// Pack expansion inside a braced-init-list (a construct HLSL parses via +// its own, more restrictive initializer-list parsing, distinct from the +// call-argument and template-argument-list pack-expansion contexts). +template +T SumArray(T First, Rest... Others) { + T values[1 + sizeof...(Rest)] = {First, Others...}; + T total = (T)0; + for (int i = 0; i < 1 + sizeof...(Rest); ++i) + total += values[i]; + return total; +} + +// CHECK: ClassTemplateDecl {{.*}} BufferHolder +// CHECK: TemplateTypeParmDecl {{.*}} typename ... Ts +template +struct BufferHolder { + StructuredBuffer Buf; +}; +BufferHolder g_FloatHolder; +BufferHolder g_IntHolder; + +// CHECK: ClassTemplateDecl {{.*}} MatrixWrapper +// CHECK: NonTypeTemplateParmDecl {{.*}} 'int' ... Dims +template +struct MatrixWrapper { + matrix M; +}; + +// A member (nested) template with its own, independent parameter pack, +// combined with the pack of its enclosing class template. +template +struct Zipper { + template + static uint Count(Ts... ts, Us... us) { + return sizeof...(Ts) + sizeof...(Us); + } +}; + +// Zero-argument (empty pack) instantiation. +uint TestEmptyPack() { return CountArgs(); } + +// CHECK: FunctionTemplateDecl {{.*}} ExplicitOut +// CHECK: FunctionDecl {{.*}} ExplicitOut 'void (Ts &__restrict...)' +// CHECK: ParmVarDecl {{.*}} values 'Ts &__restrict...' +// CHECK-NEXT: HLSLOutAttr +// CHECK: FunctionDecl {{.*}} ExplicitOut 'void (int &__restrict, float &__restrict)' +// CHECK: ParmVarDecl {{.*}} values 'int &__restrict' +// CHECK-NEXT: HLSLOutAttr +// CHECK: ParmVarDecl {{.*}} values 'float &__restrict' +// CHECK-NEXT: HLSLOutAttr +template +void ExplicitOut(out Ts... values) {} + +export +float TestVariadic() { + float a = Sum(1.0, 2.0, 3.0); + uint b = CountArgs(1, 2, 3, 4); + uint c = Forward(1, 2); + const int d = IntPack<1, 2, 3>::Count; + const uint e = PackLength::Value; + const uint eEmpty = PackLength<>::Value; + vector v = MakeVector(1.0, 2.0, 3.0, 4.0); + float f = SumArray(1.0, 2.0, 3.0, 4.0, 5.0); + MatrixWrapper mw; + mw.M = matrix(1, 2, 3, 4); + uint z = Zipper::Count(1, 2.0, 3.0); + uint empty = TestEmptyPack(); + int explicitInt; + float explicitFloat; + ExplicitOut(explicitInt, explicitFloat); + ExplicitOut(explicitInt, explicitInt); + uint folded = FoldSum(1, 2, 3, 4); + return a + b + c + d + e + eEmpty + v.x + f + mw.M._11 + z + empty + + folded + (float)g_FloatHolder.Buf.Load(0) + + (float)g_IntHolder.Buf.Load(0); +}