浏览代码

Template support (#3533)

Template support for HLSL
- can be enabled using command line option -enable-templates
- supports both function templates and aggregate templates
- support is more or less equivalent to that in C++98
- use of template features and syntax introduced by C++11 or later may
  not be handled gracefully

Co-authored-by: Tim Corringham <[email protected]>
Tim Corringham 4 年之前
父节点
当前提交
a342872a17
共有 42 个文件被更改,包括 1388 次插入45 次删除
  1. 1 0
      include/dxc/Support/HLSLOptions.h
  2. 2 0
      include/dxc/Support/HLSLOptions.td
  3. 1 0
      lib/DxcSupport/HLSLOptions.cpp
  4. 3 0
      tools/clang/include/clang/Basic/DiagnosticCommonKinds.td
  5. 1 0
      tools/clang/include/clang/Basic/LangOptions.h
  6. 2 0
      tools/clang/include/clang/Driver/Options.td
  7. 2 0
      tools/clang/lib/Frontend/CompilerInvocation.cpp
  8. 2 2
      tools/clang/lib/Parse/ParseDecl.cpp
  9. 3 3
      tools/clang/lib/Parse/ParseDeclCXX.cpp
  10. 14 3
      tools/clang/lib/Parse/ParseExpr.cpp
  11. 7 8
      tools/clang/lib/Parse/ParseExprCXX.cpp
  12. 17 18
      tools/clang/lib/Parse/ParseTemplate.cpp
  13. 6 0
      tools/clang/lib/SPIRV/SpirvEmitter.cpp
  14. 50 7
      tools/clang/lib/Sema/SemaHLSL.cpp
  15. 10 0
      tools/clang/lib/Sema/SemaOverload.cpp
  16. 3 3
      tools/clang/lib/Sema/SemaTemplateDeduction.cpp
  17. 6 1
      tools/clang/lib/Sema/SemaTemplateVariadic.cpp
  18. 6 0
      tools/clang/lib/Sema/SemaType.cpp
  19. 14 0
      tools/clang/test/HLSLFileCheck/hlsl/compile_options/enable_templates.hlsl
  20. 198 0
      tools/clang/test/HLSLFileCheck/hlsl/template/AddMulOps.hlsl
  21. 106 0
      tools/clang/test/HLSLFileCheck/hlsl/template/AnyAll.hlsl
  22. 57 0
      tools/clang/test/HLSLFileCheck/hlsl/template/AssignmentOps.hlsl
  23. 151 0
      tools/clang/test/HLSLFileCheck/hlsl/template/BitwiseAssignOps.hlsl
  24. 125 0
      tools/clang/test/HLSLFileCheck/hlsl/template/BitwiseOps.hlsl
  25. 66 0
      tools/clang/test/HLSLFileCheck/hlsl/template/BooleanMathOps.hlsl
  26. 45 0
      tools/clang/test/HLSLFileCheck/hlsl/template/ComparisonOps.hlsl
  27. 151 0
      tools/clang/test/HLSLFileCheck/hlsl/template/PrefixPostfixOps.hlsl
  28. 44 0
      tools/clang/test/HLSLFileCheck/hlsl/template/RandomGenerators.hlsl
  29. 40 0
      tools/clang/test/HLSLFileCheck/hlsl/template/ackermann.hlsl
  30. 28 0
      tools/clang/test/HLSLFileCheck/hlsl/template/dependent-sized_array.hlsl
  31. 50 0
      tools/clang/test/HLSLFileCheck/hlsl/template/elaborated-type-specifier.hlsl
  32. 8 0
      tools/clang/test/HLSLFileCheck/hlsl/template/enum-forward.hlsl
  33. 19 0
      tools/clang/test/HLSLFileCheck/hlsl/template/factorial.hlsl
  34. 12 0
      tools/clang/test/HLSLFileCheck/hlsl/template/templateFunc.hlsl
  35. 18 0
      tools/clang/test/HLSLFileCheck/hlsl/template/templateMethod.hlsl
  36. 14 0
      tools/clang/test/HLSLFileCheck/hlsl/template/templateStruct.hlsl
  37. 21 0
      tools/clang/test/HLSLFileCheck/hlsl/template/templateStructFunc.hlsl
  38. 13 0
      tools/clang/test/HLSLFileCheck/hlsl/template/templateStructFunc2.hlsl
  39. 24 0
      tools/clang/test/HLSLFileCheck/hlsl/template/templateSubscripts.hlsl
  40. 31 0
      tools/clang/test/HLSLFileCheck/hlsl/template/templateTypename.hlsl
  41. 16 0
      tools/clang/test/HLSLFileCheck/hlsl/template/variadic.hlsl
  42. 1 0
      tools/clang/tools/dxcompiler/dxcompilerobj.cpp

+ 1 - 0
include/dxc/Support/HLSLOptions.h

@@ -194,6 +194,7 @@ public:
   unsigned ScanLimit = 0; // OPT_memdep_block_scan_limit
   bool ForceZeroStoreLifetimes = false; // OPT_force_zero_store_lifetimes
   bool EnableLifetimeMarkers = false; // OPT_enable_lifetime_markers
+  bool EnableTemplates = false; // OPT_enable_templates
 
   // Optimization pass enables, disables and selects
   std::map<std::string, bool> DxcOptimizationToggles; // OPT_opt_enable & OPT_opt_disable

+ 2 - 0
include/dxc/Support/HLSLOptions.td

@@ -277,6 +277,8 @@ def enable_lifetime_markers : Flag<["-", "/"], "enable-lifetime-markers">, Group
   HelpText<"Enable generation of lifetime markers">;
 def disable_lifetime_markers : Flag<["-", "/"], "disable-lifetime-markers">, Group<hlslcomp_Group>, Flags<[CoreOption, HelpHidden]>,
   HelpText<"Disable generation of lifetime markers where they would be otherwise (6.6+)">;
+def enable_templates: Flag<["-", "/"], "enable-templates">, Group<hlslcomp_Group>, Flags<[CoreOption]>,
+  HelpText<"Enable template support for HLSL.">;
 
 // Used with API only
 def skip_serialization : Flag<["-", "/"], "skip-serialization">, Group<hlslcore_Group>, Flags<[CoreOption, HelpHidden]>,

+ 1 - 0
lib/DxcSupport/HLSLOptions.cpp

@@ -460,6 +460,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   opts.DebugFile = Args.getLastArgValue(OPT_Fd);
   opts.ExtractPrivateFile = Args.getLastArgValue(OPT_getprivate);
   opts.Enable16BitTypes = Args.hasFlag(OPT_enable_16bit_types, OPT_INVALID, false);
+  opts.EnableTemplates = Args.hasFlag(OPT_enable_templates, OPT_INVALID, false);
   opts.OutputObject = Args.getLastArgValue(OPT_Fo);
   opts.OutputHeader = Args.getLastArgValue(OPT_Fh);
   opts.OutputWarningsFile = Args.getLastArgValue(OPT_Fe);

+ 3 - 0
tools/clang/include/clang/Basic/DiagnosticCommonKinds.td

@@ -66,6 +66,9 @@ def warn_method_param_declaration : Warning<"redeclaration of method parameter %
 def err_invalid_storage_class_in_func_decl : Error<
   "invalid storage class specifier in function declarator">;
 def err_expected_namespace_name : Error<"expected namespace name">;
+// HLSL Change Starts
+def err_hlsl_variadic_templates : Error<"variadic templates are not supported in HLSL">;
+// HLSL Change Ends
 def ext_variadic_templates : ExtWarn<
   "variadic templates are a C++11 extension">, InGroup<CXX11>;
 def warn_cxx98_compat_variadic_templates :

+ 1 - 0
tools/clang/include/clang/Basic/LangOptions.h

@@ -158,6 +158,7 @@ public:
   bool UseMinPrecision; // use min precision, not native precision.
   bool EnableDX9CompatMode;
   bool EnableFXCCompatMode;
+  bool EnableTemplates;
   // HLSL Change Ends
 
   bool SPIRV = false;  // SPIRV Change

+ 2 - 0
tools/clang/include/clang/Driver/Options.td

@@ -680,6 +680,8 @@ def hlsl_version : Separate<["-", "/"], "HV">, Group<f_Group>, Flags<[DriverOpti
   HelpText<"HLSL version (2015, 2016, 2017)">; // HLSL Change - mimic the HLSLOptions.td flag
 def enable_16bit_types: Flag<["-", "/"], "enable-16bit-types">, Flags<[CoreOption, DriverOption, HelpHidden]>,
   HelpText<"Enable 16bit types and disable min precision types.">; // HLSL Change - mimic the HLSLOptions.td flag
+def enable_templates: Flag<["-", "/"], "enable-templates">, Flags<[CoreOption, DriverOption, HelpHidden]>,
+  HelpText<"Enable template support for HLSL.">; // HLSL Change
 def fms_compatibility_version
     : Joined<["-"], "fms-compatibility-version=">,
       Group<f_Group>,

+ 2 - 0
tools/clang/lib/Frontend/CompilerInvocation.cpp

@@ -1747,6 +1747,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
   // Enable low precision for HLSL 2018
   // TODO: should we tie low precision to HLSL2018 only?
   Opts.UseMinPrecision = !Args.hasArg(options::OPT_enable_16bit_types);
+  // Enable template support for HLSL
+  Opts.EnableTemplates = Args.hasArg(options::OPT_enable_templates);
 #endif // #ifdef MS_SUPPORT_VARIABLE_LANGOPTS
 }
 

+ 2 - 2
tools/clang/lib/Parse/ParseDecl.cpp

@@ -1927,7 +1927,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(unsigned Context,
   switch (Tok.getKind()) {
   case tok::kw_template:
     // HLSL Change Starts
-    if (getLangOpts().HLSL) {
+    if (getLangOpts().HLSL && !getLangOpts().EnableTemplates) {
       Diag(Tok, diag::err_hlsl_reserved_keyword) << Tok.getName();
       SkipMalformedDecl();
       return DeclGroupPtrTy();
@@ -4092,7 +4092,7 @@ HLSLReservedKeyword:
 
     // C++ typename-specifier:
     case tok::kw_typename:
-      if (getLangOpts().HLSL) { goto HLSLReservedKeyword; } // HLSL Change - reserved for HLSL
+      if (getLangOpts().HLSL && !getLangOpts().EnableTemplates) { goto HLSLReservedKeyword; } // HLSL Change - reserved for HLSL
       if (TryAnnotateTypeOrScopeToken()) {
         DS.SetTypeSpecError();
         goto DoneWithDeclSpec;

+ 3 - 3
tools/clang/lib/Parse/ParseDeclCXX.cpp

@@ -2347,13 +2347,13 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
   if (Tok.is(tok::kw_template)) {
     assert(!TemplateInfo.TemplateParams &&
            "Nested template improperly parsed?");
-    // HLSL Change Start - disallow template members
-    if (getLangOpts().HLSL) {
+    // HLSL Change Starts
+    if (getLangOpts().HLSL && !getLangOpts().EnableTemplates) {
       Diag(Tok, diag::err_hlsl_reserved_keyword) << Tok.getName();
       SkipUntil(tok::r_brace, StopAtSemi);
       return;
     }
-    // HLSL Change End
+    // HLSL Change Ends
     SourceLocation DeclEnd;
     ParseDeclarationStartingWithTemplate(Declarator::MemberContext, DeclEnd,
                                          AS, AccessAttrs);

+ 14 - 3
tools/clang/lib/Parse/ParseExpr.cpp

@@ -1250,7 +1250,7 @@ HLSLReservedKeyword:
     if (getLangOpts().HLSL && (
         SavedKind == tok::kw_wchar_t || SavedKind == tok::kw_char || SavedKind == tok::kw_char16_t || SavedKind == tok::kw_char32_t ||
         SavedKind == tok::kw_short || SavedKind == tok::kw_long || SavedKind == tok::kw___int64 || SavedKind == tok::kw___int128 ||
-        SavedKind == tok::kw_typename || SavedKind == tok::kw_typeof)) {
+        (SavedKind == tok::kw_typename && !getLangOpts().EnableTemplates) || SavedKind == tok::kw_typeof)) {
       // the vector/image/sampler/event keywords aren't returned by the lexer for HLSL
       goto HLSLReservedKeyword;
     }
@@ -2775,8 +2775,19 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
     } else
       Expr = ParseAssignmentExpression();
 
-    if (Tok.is(tok::ellipsis))
-      Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken());    
+    if (Tok.is(tok::ellipsis)) {
+      // HLSL Change Starts
+      if (getLangOpts().HLSL) {
+        Diag(Tok, diag::err_hlsl_variadic_templates);
+        SkipUntil(tok::r_paren, StopBeforeMatch);
+        Actions.CorrectDelayedTyposInExpr(Expr);
+        Expr = ExprError();
+        SawError = true;
+        break;
+      }
+      // HLSL Change Ends
+      Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken());
+    }
     if (Expr.isInvalid()) {
       SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch);
       SawError = true;

+ 7 - 8
tools/clang/lib/Parse/ParseExprCXX.cpp

@@ -82,7 +82,6 @@ static void FixDigraph(Parser &P, Preprocessor &PP, Token &DigraphToken,
 void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
                                         bool EnteringContext,
                                         IdentifierInfo &II, CXXScopeSpec &SS) {
-  assert(!getLangOpts().HLSL && "not supported in HLSL - unreachable"); // HLSL Change
   if (!Next.is(tok::l_square) || Next.getLength() != 2)
     return;
 
@@ -309,7 +308,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
     // 'identifier <' after it.
     if (Tok.is(tok::kw_template)) {
       // HLSL Change Starts - template is reserved
-      if (getLangOpts().HLSL) {
+      if (getLangOpts().HLSL && !getLangOpts().EnableTemplates) {
         Diag(Tok, diag::err_hlsl_reserved_keyword) << Tok.getName();
         ConsumeToken();
         return true;
@@ -332,7 +331,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
         ConsumeToken();
       } else if (Tok.is(tok::kw_operator)) {
         // HLSL Change Starts
-        if (getLangOpts().HLSL) {
+        if (getLangOpts().HLSL && !getLangOpts().EnableTemplates) {
           Diag(Tok, diag::err_hlsl_reserved_keyword) << Tok.getName();
           TPA.Commit();
           return true;
@@ -570,14 +569,14 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
         continue;
       }
 
-      // HLSL Change: templates aren't really supported in HLSL, so avoid
-      // handling other cases and emitting incorrect diagnostics if
-      // the template lookup fails.
-      if (!nextIsLess && getLangOpts().HLSL) {
+      // HLSL Change: templates aren't really supported in HLSL unless the
+      // EnableTemplates option is enabled, so avoid handling other cases and
+      // emitting incorrect diagnostics if the template lookup fails.
+      if (!nextIsLess && getLangOpts().HLSL && !getLangOpts().EnableTemplates) {
         break;
       }
 
-      if (!getLangOpts().HLSL && // HLSL Change - no template fixup available
+      if (getLangOpts().EnableTemplates && // HLSL Change - template fixup only available when templates enabled
           MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) && 
           (IsTypename || IsTemplateArgumentList(1))) {
         // We have something like t::getAs<T>, where getAs is a 

+ 17 - 18
tools/clang/lib/Parse/ParseTemplate.cpp

@@ -29,7 +29,6 @@ Parser::ParseDeclarationStartingWithTemplate(unsigned Context,
                                              SourceLocation &DeclEnd,
                                              AccessSpecifier AS,
                                              AttributeList *AccessAttrs) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   ObjCDeclContextSwitch ObjCDC(*this);
   
   if (Tok.is(tok::kw_template) && NextToken().isNot(tok::less)) {
@@ -63,7 +62,6 @@ Parser::ParseTemplateDeclarationOrSpecialization(unsigned Context,
                                                  SourceLocation &DeclEnd,
                                                  AccessSpecifier AS,
                                                  AttributeList *AccessAttrs) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   assert(Tok.isOneOf(tok::kw_export, tok::kw_template) &&
          "Token does not start a template declaration.");
 
@@ -177,7 +175,6 @@ Parser::ParseSingleDeclarationAfterTemplate(
                                        SourceLocation &DeclEnd,
                                        AccessSpecifier AS,
                                        AttributeList *AccessAttrs) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   assert(TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate &&
          "Template information required");
 
@@ -340,7 +337,6 @@ bool Parser::ParseTemplateParameters(unsigned Depth,
                                SmallVectorImpl<Decl*> &TemplateParams,
                                      SourceLocation &LAngleLoc,
                                      SourceLocation &RAngleLoc) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   // Get the template parameter list.
   if (!TryConsumeToken(tok::less, LAngleLoc)) {
     Diag(Tok.getLocation(), diag::err_expected_less_after) << "template";
@@ -379,7 +375,6 @@ bool Parser::ParseTemplateParameters(unsigned Depth,
 bool
 Parser::ParseTemplateParameterList(unsigned Depth,
                              SmallVectorImpl<Decl*> &TemplateParams) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   while (1) {
     if (Decl *TmpParam
           = ParseTemplateParameter(Depth, TemplateParams.size())) {
@@ -413,7 +408,6 @@ Parser::ParseTemplateParameterList(unsigned Depth,
 /// \brief Determine whether the parser is at the start of a template
 /// type parameter.
 bool Parser::isStartOfTemplateTypeParameter() {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   if (Tok.is(tok::kw_class)) {
     // "class" may be the start of an elaborated-type-specifier or a
     // type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter.
@@ -490,7 +484,6 @@ bool Parser::isStartOfTemplateTypeParameter() {
 ///         'template' '<' template-parameter-list '>' 'class' identifier[opt]
 ///               = id-expression
 Decl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   if (isStartOfTemplateTypeParameter())
     return ParseTypeParameter(Depth, Position);
 
@@ -513,7 +506,6 @@ Decl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) {
 ///         'typename' ...[opt][C++0x] identifier[opt]
 ///         'typename' identifier[opt] '=' type-id
 Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   assert(Tok.isOneOf(tok::kw_class, tok::kw_typename) &&
          "A type-parameter starts with 'class' or 'typename'");
 
@@ -524,6 +516,12 @@ Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
   // Grab the ellipsis (if given).
   SourceLocation EllipsisLoc;
   if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
+    // HLSL Change Starts
+    if (getLangOpts().HLSL) {
+      Diag(EllipsisLoc, diag::err_hlsl_variadic_templates);
+      return nullptr;
+    }
+    // HLSL Change Ends
     Diag(EllipsisLoc,
          getLangOpts().CPlusPlus11
            ? diag::warn_cxx98_compat_variadic_templates
@@ -577,7 +575,6 @@ Decl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
 ///         'typename'       [C++1z]
 Decl *
 Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   assert(Tok.is(tok::kw_template) && "Expected 'template' keyword");
 
   // Handle the template <...> part.
@@ -622,12 +619,17 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
 
   // Parse the ellipsis, if given.
   SourceLocation EllipsisLoc;
-  if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
-    Diag(EllipsisLoc,
-         getLangOpts().CPlusPlus11
-           ? diag::warn_cxx98_compat_variadic_templates
-           : diag::ext_variadic_templates);
-      
+  if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) {
+    // HLSL Change Starts
+    if (getLangOpts().HLSL)
+      Diag(EllipsisLoc, diag::err_hlsl_variadic_templates);
+    else 
+      // HLSL Change Ends
+      Diag(EllipsisLoc, getLangOpts().CPlusPlus11
+                            ? diag::warn_cxx98_compat_variadic_templates
+                            : diag::ext_variadic_templates);
+  }
+
   // Get the identifier, if given.
   SourceLocation NameLoc;
   IdentifierInfo *ParamName = nullptr;
@@ -684,7 +686,6 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
 ///         parameter-declaration
 Decl *
 Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   // Parse the declaration-specifiers (i.e., the type).
   // FIXME: The type should probably be restricted in some way... Not all
   // declarators (parts of declarators?) are accepted for parameters.
@@ -1359,7 +1360,6 @@ void Parser::LateTemplateParserCallback(void *P, LateParsedTemplate &LPT) {
 
 /// \brief Late parse a C++ function template in Microsoft mode.
 void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   if (!LPT.D)
      return;
 
@@ -1450,7 +1450,6 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) {
 
 /// \brief Lex a delayed template function for late parsing.
 void Parser::LexTemplateFunctionForLateParsing(CachedTokens &Toks) {
-  assert(!getLangOpts().HLSL && "no template parsing is supported in HLSL"); // HLSL Change
   tok::TokenKind kind = Tok.getKind();
   if (!ConsumeAndStoreFunctionPrologue(Toks)) {
     // Consume everything up to (and including) the matching right brace.

+ 6 - 0
tools/clang/lib/SPIRV/SpirvEmitter.cpp

@@ -718,6 +718,12 @@ void SpirvEmitter::doDecl(const Decl *decl) {
     doRecordDecl(recordDecl);
   } else if (const auto *enumDecl = dyn_cast<EnumDecl>(decl)) {
     doEnumDecl(enumDecl);
+  } else if (const auto *classTemplateDecl =
+                 dyn_cast<ClassTemplateDecl>(decl)) {
+    // nothing to do.
+  } else if (const auto *functionTemplateDecl =
+                 dyn_cast<FunctionTemplateDecl>(decl)) {
+    // nothing to do.
   } else {
     emitError("decl type %0 unimplemented", decl->getLocation())
         << decl->getDeclKindName();

+ 50 - 7
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -78,7 +78,7 @@ enum ArBasicKind {
   AR_BASIC_NONE,
   AR_BASIC_UNKNOWN,
   AR_BASIC_NOCAST,
-
+  AR_BASIC_DEPENDENT,
   //
   // The following pseudo-entries represent higher-level
   // object types that are treated as units.
@@ -378,6 +378,7 @@ const UINT g_uBasicKindProps[] =
   0,            // AR_BASIC_NONE
   BPROP_OTHER,  // AR_BASIC_UNKNOWN
   BPROP_OTHER,  // AR_BASIC_NOCAST
+  0,            // AR_BASIC_DEPENDENT
 
   //
   // The following pseudo-entries represent higher-level
@@ -589,6 +590,7 @@ enum ArTypeObjectKind {
   AR_TOBJ_INNER_OBJ, // Represents a built-in inner object, such as an 
                      // indexer object used to implement .mips[1].
   AR_TOBJ_STRING,    // Represents a string
+  AR_TOBJ_DEPENDENT, // Dependent type for template.
 };
 
 enum TYPE_CONVERSION_FLAGS
@@ -1627,6 +1629,7 @@ const char* g_ArBasicTypeNames[] =
   "<none>",
   "<unknown>",
   "<nocast>",
+  "<dependent>",
   "<pointer>",
   "enum class",
 
@@ -3908,7 +3911,7 @@ public:
 
   /// <summary>
   /// Determines whether the specify record type is a matrix, another HLSL object, or a user-defined structure.
-  /// </sumary>
+  /// </summary>
   ArTypeObjectKind ClassifyRecordType(const RecordType* type)
   {
     DXASSERT_NOMSG(type != nullptr);
@@ -3921,7 +3924,8 @@ public:
         return AR_TOBJ_MATRIX;
       else if (decl == m_vectorTemplateDecl)
         return AR_TOBJ_VECTOR;
-      DXASSERT(decl->isImplicit(), "otherwise object template decl is not set to implicit");
+      else if (!decl->isImplicit())
+        return AR_TOBJ_COMPOUND;
       return AR_TOBJ_OBJECT;
     }
 
@@ -3989,6 +3993,9 @@ public:
     if (type->isPointerType()) {
       return hlsl::IsPointerStringType(type) ? AR_TOBJ_STRING : AR_TOBJ_POINTER;
     }
+    if (type->isDependentType()) {
+      return AR_TOBJ_DEPENDENT;
+    }
     if (type->isStructureOrClassType()) {
       const RecordType* recordType = type->getAs<RecordType>();
       return ClassifyRecordType(recordType);
@@ -4122,6 +4129,7 @@ public:
       case BuiltinType::LitInt: return AR_BASIC_LITERAL_INT;
       case BuiltinType::Int8_4Packed: return AR_BASIC_INT8_4PACKED;
       case BuiltinType::UInt8_4Packed: return AR_BASIC_UINT8_4PACKED;
+      case BuiltinType::Dependent: return AR_BASIC_DEPENDENT;
       default:
         // Only builtin types that have basickind equivalents.
         break;
@@ -8078,7 +8086,7 @@ clang::Expr *HLSLExternalSource::HLSLImpCastToScalar(
   if (AR_TOBJ_VECTOR == FromShape)
     CK = CK_HLSLVectorToScalarCast;
   if (CK_Invalid != CK) {
-    return self->ImpCastExprToType(From, 
+    return self->ImpCastExprToType(From,
       NewSimpleAggregateType(AR_TOBJ_BASIC, EltKind, 0, 1, 1), CK, From->getValueKind()).get();
   }
   return From;
@@ -8869,6 +8877,11 @@ bool HLSLExternalSource::ValidateTypeRequirements(
   bool requiresIntegrals,
   bool requiresNumerics)
 {
+  if (objectKind == AR_TOBJ_DEPENDENT)
+    return true;
+  if (elementKind == AR_BASIC_DEPENDENT)
+    return true;
+
   if (requiresIntegrals || requiresNumerics)
   {
     if (!IsObjectKindPrimitiveAggregate(objectKind))
@@ -8970,6 +8983,17 @@ void HLSLExternalSource::CheckBinOpForHLSL(
     return;
   }
 
+  // If there is a dependent type we will use that as the result type
+  if (LHS.get()->getType()->isDependentType() || RHS.get()->getType()->isDependentType()) {
+    if (LHS.get()->getType()->isDependentType())
+      ResultTy = LHS.get()->getType();
+    else
+      ResultTy = RHS.get()->getType();
+    if (BinaryOperatorKindIsCompoundAssignment(Opc))
+      CompResultTy = ResultTy;
+    return;
+  }
+
   // TODO: re-review the Check** in Clang and add equivalent diagnostics if/as needed, possibly after conversions
 
   // Handle Assign and Comma operators and return
@@ -9147,8 +9171,11 @@ void HLSLExternalSource::CheckBinOpForHLSL(
     StandardConversionSequence standard;
     // Suppress type narrowing or truncation warnings for RHS on bitwise shift, since we only care about the LHS type.
     bool bSuppressWarnings = BinaryOperatorKindIsBitwiseShift(Opc);
-    // Suppress errors on compound assignment, since we will vaildate the cast to the final type later.
+    // Suppress errors on compound assignment, since we will validate the cast to the final type later.
     bool bSuppressErrors = isCompoundAssignment;
+    // Suppress errors if either operand has a dependent type.
+    if (RHS.get()->getType()->isDependentType() || ResultTy->isDependentType())
+      bSuppressErrors = true;
     // If compound assignment, suppress errors until later, but report warning (vector truncation/type narrowing) here.
     if (ValidateCast(SourceLocation(), RHS.get(), ResultTy, ExplicitConversionFalse, bSuppressWarnings, bSuppressErrors, &standard)) {
       if (standard.First != ICK_Identity || !standard.isIdentityConversion())
@@ -9351,6 +9378,15 @@ clang::QualType HLSLExternalSource::CheckVectorConditional(
   QualType condType = GetStructuralForm(Cond.get()->getType());
   QualType leftType = GetStructuralForm(LHS.get()->getType());
   QualType rightType = GetStructuralForm(RHS.get()->getType());
+
+  // If any type is dependent, we will use that as the type to return.
+  if (leftType->isDependentType())
+    return leftType;
+  if (rightType->isDependentType())
+    return rightType;
+  if (condType->isDependentType())
+    return condType;
+
   ArBasicKind condElementKind = GetTypeElementKind(condType);
   ArBasicKind leftElementKind = GetTypeElementKind(leftType);
   ArBasicKind rightElementKind = GetTypeElementKind(rightType);
@@ -9527,8 +9563,10 @@ Sema::TemplateDeductionResult HLSLExternalSource::DeduceTemplateArgumentsForHLSL
 
   // Get information about the function we have.
   CXXMethodDecl* functionMethod = dyn_cast<CXXMethodDecl>(FunctionTemplate->getTemplatedDecl());
-  DXASSERT(functionMethod != nullptr,
-    "otherwise this is standalone function rather than a method, which isn't supported in the HLSL object model");
+  if (!functionMethod) {
+    // standalone function.
+    return Sema::TemplateDeductionResult::TDK_Invalid;
+  }
   CXXRecordDecl* functionParentRecord = functionMethod->getParent();
   DXASSERT(functionParentRecord != nullptr, "otherwise function is orphaned");
   QualType objectElement = GetFirstElementTypeFromDecl(functionParentRecord);
@@ -9586,6 +9624,11 @@ Sema::TemplateDeductionResult HLSLExternalSource::DeduceTemplateArgumentsForHLSL
   size_t intrinsicCount = 0;
   const char* objectName = nullptr;
   FindIntrinsicTable(FunctionTemplate->getDeclContext(), &objectName, &intrinsics, &intrinsicCount);
+  // user-defined template object.
+  if (objectName == nullptr && intrinsics == nullptr) {
+    return Sema::TemplateDeductionResult::TDK_Invalid;
+  }
+
   DXASSERT(objectName != nullptr &&
     (intrinsics != nullptr || m_intrinsicTables.size() > 0),
     "otherwise FindIntrinsicTable failed to lookup a valid object, "

+ 10 - 0
tools/clang/lib/Sema/SemaOverload.cpp

@@ -11493,6 +11493,11 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
       if (Opc == BO_Comma)
         break;
 
+      // HLSL Change Starts
+      if (getLangOpts().HLSL)
+        return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
+      // HLSL Change Ends
+
       // For class as left operand for assignment or compound assigment
       // operator do not fall through to handling in built-in, but report that
       // no overloaded assignment operator found
@@ -11528,6 +11533,11 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
     }
 
     case OR_Ambiguous:
+      // HLSL Change Starts
+      if (getLangOpts().HLSL)
+        return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
+      // HLSL Change Ends
+
       Diag(OpLoc,  diag::err_ovl_ambiguous_oper_binary)
           << BinaryOperator::getOpcodeStr(Opc)
           << Args[0]->getType() << Args[1]->getType()

+ 3 - 3
tools/clang/lib/Sema/SemaTemplateDeduction.cpp

@@ -3142,12 +3142,12 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(Sema &S,
     //   - If A is an array type, the pointer type produced by the
     //     array-to-pointer standard conversion (4.2) is used in place of
     //     A for type deduction; otherwise,
-    if (ArgType->isArrayType())
-      ArgType = S.Context.getArrayDecayedType(ArgType);
+    //if (ArgType->isArrayType()) // HLSL Change
+    //  ArgType = S.Context.getArrayDecayedType(ArgType);
     //   - If A is a function type, the pointer type produced by the
     //     function-to-pointer standard conversion (4.3) is used in place
     //     of A for type deduction; otherwise,
-    else if (ArgType->isFunctionType())
+    if (ArgType->isFunctionType())
       ArgType = S.Context.getPointerType(ArgType);
     else {
       // - If A is a cv-qualified type, the top level cv-qualifiers of A's

+ 6 - 1
tools/clang/lib/Sema/SemaTemplateVariadic.cpp

@@ -544,7 +544,12 @@ bool Sema::CheckParameterPacksForExpansion(
   RetainExpansion = false;
   std::pair<IdentifierInfo *, SourceLocation> FirstPack;
   bool HaveFirstPack = false;
-  
+
+  if (getLangOpts().HLSL) {
+    Diag(EllipsisLoc, diag::err_hlsl_variadic_templates);
+    return true;
+  }
+
   for (ArrayRef<UnexpandedParameterPack>::iterator i = Unexpanded.begin(),
                                                  end = Unexpanded.end();
                                                   i != end; ++i) {

+ 6 - 0
tools/clang/lib/Sema/SemaType.cpp

@@ -4254,6 +4254,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
       // Note: core issue 778 clarifies that, if there are any unexpanded
       // parameter packs in the type of the non-type template parameter, then
       // it expands those parameter packs.
+      // HLSL Change Starts
+      if (LangOpts.HLSL) {
+        S.Diag(D.getEllipsisLoc(), diag::err_hlsl_variadic_templates);
+        break;
+      }
+      // HLSL Change Ends
       if (T->containsUnexpandedParameterPack())
         T = Context.getPackExpansionType(T, None);
       else

+ 14 - 0
tools/clang/test/HLSLFileCheck/hlsl/compile_options/enable_templates.hlsl

@@ -0,0 +1,14 @@
+// RUN: %dxc -E main -T ps_6_0 %s | FileCheck -check-prefix=DISABLED %s
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck -check-prefix=ENABLED %s
+
+// DISABLED: error: 'template' is a reserved keyword in HLSL
+// ENABLED: define void @main()
+
+template<typename T>
+T f(T a) {
+  return a + 1;
+};
+
+int main(int a:A) : SV_Target {
+   return f(a);
+}

+ 198 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/AddMulOps.hlsl

@@ -0,0 +1,198 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 %s -enable-templates -DCHECK_DIAGNOSTICS | FileCheck %s -check-prefix=DIAG
+
+template<typename T>
+T test_add(T t0, T t1) {
+  return t0 + t1;
+}
+
+template<typename T>
+T test_sub(T t0, T t1) {
+  return t0 - t1;
+}
+
+template<typename T>
+T test_mul(T t0, T t1) {
+  return t0 * t1;
+}
+
+template<typename T>
+T test_div(T t0, T t1) {
+  return t0 / t1;
+}
+
+template<typename T>
+T test_mod(T t0, T t1) {
+  return t0 % t1;
+}
+
+struct S {
+  int a;
+  int4 a4;
+};
+
+int4 main(int4 a:A) : SV_Target {
+  int i, j;
+  unsigned int ui, uj;
+  int1 i1 = 10, j1 = 11;
+  int2 i2, j2;
+  int3 i3, j3;
+  int4 i4 = int4(1,2,3,4), j4 = int4(5,6,7,8);
+
+  int1x1 i1x1, j1x1;
+  int1x1 i1x2, j1x2;
+  int1x1 i1x3, j1x3;
+  int1x1 i1x4, j1x4;
+  int1x1 i2x1, j2x1;
+
+  float x,y;
+
+  S s;
+
+  bool b1, b2 = true;
+
+  int arr1[7], arr2[7];
+
+#ifdef CHECK_DIAGNOSTICS
+  // DIAG-NOT: define void @main
+
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  test_add(i, x);     // mismatched types
+  test_add(s, j);     // mismatched types
+  test_add(i, s);     // mismatched types
+  test_add(i, uj);    // mismatched types
+  test_add(b1,j);     // mismatched types
+  test_add(s, s);     // can't test_add structs
+
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  test_sub(i, x);     // mismatched types
+  test_sub(i, x);     // mismatched types
+  test_sub(s, j);     // mismatched types
+  test_sub(i, s);     // mismatched types
+  test_sub(i, uj);    // mismatched types
+  test_sub(b1,j);     // mismatched types
+  test_sub(s, s);     // can't test_sub structs
+
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  test_mul(i, x);     // mismatched types
+  test_mul(i, x);     // mismatched types
+  test_mul(s, j);     // mismatched types
+  test_mul(i, s);     // mismatched types
+  test_mul(i, uj);    // mismatched types
+  test_mul(b1,j);     // mismatched types
+  test_mul(s, s);     // can't test_mul structs
+
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  test_div(i, x);     // mismatched types
+  test_div(i, x);     // mismatched types
+  test_div(s, j);     // mismatched types
+  test_div(i, s);     // mismatched types
+  test_div(i, uj);    // mismatched types
+  test_div(b1,j);     // mismatched types
+
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  // DIAG: deduced conflicting types for parameter
+  test_mod(i, x);     // mismatched types
+  test_mod(i, x);     // mismatched types
+  test_mod(s, j);     // mismatched types
+
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  test_mod(i, s);     // mismatched types
+  test_mod(i, uj);    // mismatched types
+  test_mod(b1,j);     // mismatched types
+  test_mod(s, s);     // can't test_mod structs
+
+  return 0;
+
+#else
+// These should all compile without diagnostics
+// CHECK: define void @main
+
+  int  r_i  = test_add(i,j) + test_sub(i,j) + test_mul(i,j) + test_div(i,j) + test_mod(i,j);
+  int  r_ia  = test_add(1,5);
+  int  r_ib  = test_add(i,int(6));
+  int1 r_i1 = test_add(i1,j1) + test_sub(i1,j1) + test_mul(i1,j1) + test_div(i1,j1) + test_mod(i1,j1);
+  int2 r_i2 = test_add(i2,j2) + test_sub(i2,j2) + test_mul(i2,j2) + test_div(i2,j2) + test_mod(i2,j2);
+  int3 r_i3 = test_add(i3,j3) + test_sub(i3,j3) + test_mul(i3,j3) + test_div(i3,j3) + test_mod(i3,j3);
+  int4 r_i4 = test_add(i4,j4) + test_sub(i4,j4) + test_mul(i4,j4) + test_div(i4,j4) + test_mod(i4,j4);
+  int4 r_i4a = test_add(i4,int4(3,5,7,9));
+  int4 r_i4b = test_add(int4(2,4,8,16), j4);
+  s.a4 = test_add<int4>(int4(2,4,8,16), j4);
+  s.a4 = test_add(int4(2,4,8,16), j4);
+
+  int1x1 r_i1x1 = test_add(i1x1, j1x1) + test_sub(i1x1, j1x1) + test_mul(i1x1, j1x1) + test_div(i1x1, j1x1) + test_mod(i1x1, j1x1);
+  int1x1 r_i1x2 = test_add(i1x2, j1x2);
+  int1x1 r_i1x3 = test_add(i1x3, j1x3);
+  int1x1 r_i1x4 = test_add(i1x4, j1x4);
+  int1x1 r_i2x1 = test_add(i2x1, j2x1) + test_sub(i2x1, j2x1) + test_mul(i2x1, j2x1) + test_div(i2x1, j2x1) + test_mod(i2x1, j2x1);
+
+  test_add(i, j);
+  test_add(x, y);
+  test_add(1, 2);
+  test_add(i4.w, j);
+  test_add(arr1[1], j);
+  test_add(ui, uj);
+  test_add(b1,b2);
+
+  test_sub(i, j);
+  test_sub(x, y);
+  test_sub(1, 2);
+  test_sub(i4.w, j);
+  test_sub(arr1[1], j);
+  test_sub(ui, uj);
+  test_sub(b1,b2);
+
+  test_mul(i, j);
+  test_mul(x, y);
+  test_mul(1, 2);
+  test_mul(i4.w, j);
+  test_mul(arr1[1], j);
+  test_mul(ui, uj);
+  test_mul(b1,b2);
+
+  test_div(i, j);
+  test_div(x, y);
+  test_div(1, 2);
+  test_div(i4.w, j);
+  test_div(arr1[1], j);
+  test_div(ui, uj);
+  test_div(b1,b2);
+
+  test_mod(i, j);
+  test_mod(x, y);
+  test_mod(1, 2);
+  test_mod(i4.w, j);
+  test_mod(arr1[1], j);
+  test_mod(ui, uj);
+  test_mod(b1,b2);
+
+  return r_i4 + r_i4a + r_i4b;
+
+#endif
+}

+ 106 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/AnyAll.hlsl

@@ -0,0 +1,106 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s -DCHECK_DIAGNOSTICS | FileCheck %s -check-prefix=DIAG
+
+
+// pass - bool, int, float, vector, matrix
+// fail - struct, array
+
+template<typename T>
+bool my_any(T t0) {
+  return any(t0);
+}
+
+template<typename T>
+bool my_all(T t0) {
+  return all(t0);
+}
+
+template<typename T>
+bool any_lessthan(T t0, T t1) {
+  return any(t0 < t1);
+}
+
+template<typename T>
+bool all_lessthan(T t0, T t1) {
+  return all(t0 < t1);
+}
+
+struct S {
+   bool4 b;
+   int i;
+};
+
+bool main(int4 a:A) : SV_Target {
+  int i = 7, j = 6;
+  int1 i1 = 10, j1 = 11;
+  int4 i4 = int4(1,2,3,4), j4 = int4(5,6,7,8);
+  int3x3 i3x3, j3x3;
+  int iarr[7] = {1,2,3,4,5,6,7}, jarr[7] ;
+  bool barr[6];
+  S s1, s2;
+
+#ifdef CHECK_DIAGNOSTICS
+
+  // DIAG-NOT: define void @main
+
+  // DIAG: not viable: no known conversion from 'S' to 'float'
+  // DIAG: not viable: no known conversion from 'int [7]' to 'int'
+  // DIAG: not viable: no known conversion from 'bool [6]' to 'bool'
+  my_any(s1);
+  my_any(iarr);
+  my_any(barr);
+
+  // DIAG: not viable: no known conversion from 'S' to 'float'
+  // DIAG: not viable: no known conversion from 'int [7]' to 'int'
+  // DIAG: not viable: no known conversion from 'bool [6]' to 'bool'
+  my_all(s1);
+  my_all(iarr);
+  my_all(barr);
+
+  return true;
+
+#else
+
+  // CHECK: define void @main
+
+  my_any(i);
+  my_any(i1);
+  my_any(i4);
+  my_any(i3x3);
+
+  bool b1 = all_lessthan(i,j);
+  bool b2 = any_lessthan(i,j);
+  bool b3 = any_lessthan(i1,j1);
+  bool b4 = all_lessthan(i1,j1);
+  bool b5 = any_lessthan(i4,j4);
+  bool b6 = all_lessthan(i4,j4);
+  bool b7 = any_lessthan(i3x3,j3x3);
+  bool b8 = all_lessthan(i3x3,j3x3);
+  bool b9 = all_lessthan(i,i4.x);
+  bool b9a = any_lessthan(i,j4[2]);
+
+  float x, y;
+  float3 x3, y3;
+  float4x4 x4x4, y4x4;
+
+  bool b10 = any_lessthan(x,y);
+  bool b11 = all_lessthan(x,y);
+  bool b12 = any_lessthan(x3,y3);
+  bool b13 = all_lessthan(x3,y3);
+  bool b14 = any_lessthan(x4x4,y4x4);
+  bool b15 = all_lessthan(x4x4,y4x4);
+  bool b16 = any_lessthan(x3,float3(0.1, 2.7, 3.3));
+
+  bool b20 = all_lessthan(s1.i,s2.i);
+  bool b21 = all_lessthan(x4x4[1],y4x4[3].zwyx);
+
+  return b1 || b2 || b3 || b4 || b5 || b6 || b7 || b8  || b9 || b10 || b11 || b12 || b13 || b14 || b15 || b16;
+
+#endif
+
+}
+
+
+
+
+

+ 57 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/AssignmentOps.hlsl

@@ -0,0 +1,57 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 %s -enable-templates -DCHECK_DIAGNOSTICS | FileCheck %s -check-prefix=DIAG
+
+template<typename T0, typename T1>
+void assign(inout T0 t0, T1 t1) {
+  t0 = t1;
+}
+
+struct S {
+  int4 i4;
+  float2 f2;
+  float3x3 f3x3;
+  bool4 b4;
+};
+
+void main() : SV_Target {
+  int i;
+  int1 i1;
+  int2 i2;
+  int3 i3;
+  int4 i4;
+  unsigned int j;
+  unsigned int2x2 j2x2;
+  float x;
+  float3 x3;
+  bool b;
+  bool2 b2;
+  S s, t;
+  float4x3 f4arr[11];
+
+#ifdef CHECK_DIAGNOSTICS
+
+  // DIAG-NOT: define void @main
+  // DIAG: cannot implicitly convert
+  assign(f4arr, x);
+  // DIAG: warning: implicit truncation of vector type
+  // DIAG: warning: implicit truncation of vector type
+  assign(i, i4);
+  assign(b, b2);
+  // DIAG-NOT: error
+
+#else
+
+// CHECK: define void @main
+
+  assign(i, j);
+  assign(i, i1);
+  assign(i, i1);
+  assign(i, i1);
+
+  assign(i2, i);
+  assign(s, t);
+  assign(s.i4, i4);
+
+#endif
+
+}

+ 151 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/BitwiseAssignOps.hlsl

@@ -0,0 +1,151 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s 2>&1 | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s -DCHECK_DIAGNOSTICS | FileCheck %s -check-prefix=DIAG
+
+// Check that HLSL bitwise assignment operators deal with dependent types
+
+template<typename T, typename I>
+void lshiftassign(inout T t, I i) {
+  t <<= i;
+}
+
+template<typename T>
+void lshiftassignby2(inout T t) {
+  t <<= 2;
+}
+
+template<typename T, typename I>
+T rshiftassign(T t, I i) {
+  t >>= i;
+  return t;
+}
+
+template<typename T0, typename T1>
+T0 andassign(T0 t0, T1 t1) {
+  t0 &= t1;
+  return t0;
+}
+
+template<typename T0, typename T1>
+T0 orassign(T0 t0, T1 t1) {
+  t0 |= t1;
+  return t0;
+}
+
+template<typename T0, typename T1>
+T0 xorassign(T0 t0, T1 t1) {
+  t0 ^= t1;
+  return t0;
+}
+
+typedef struct {
+  int4 a;
+} S;
+
+int4 main(int4 a:A) : SV_Target {
+  int i = 7, j = 6;
+  unsigned int ui = 7, uj = 6;
+  int1 i1 = 10, j1 = 11;
+  int4 i4 = int4(1,2,3,4), j4 = int4(5,6,7,8);
+  int3x3 i3x3, j3x3;
+  int iarr[7] = {1,2,3,4,5,6,7}, jarr[7] ;
+  S s;
+  bool b;
+  bool1 b1;
+  bool3 b3;
+  bool2x2 b2x2;
+
+  float x, y;
+  float3 x3, y3;
+  float4x4 x4x4, y4x4;
+
+#ifdef CHECK_DIAGNOSTICS
+
+  // DIG-NOT: define void @main
+
+
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  lshiftassign(iarr,i);
+  lshiftassign(i,iarr);
+  lshiftassign(iarr,iarr);
+  lshiftassignby2(iarr);
+
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  lshiftassign(s,i);
+  lshiftassign(i,s);
+  lshiftassignby2(s);
+
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  lshiftassign(x,ui);
+  lshiftassign(i,x);
+  lshiftassignby2(x);
+  andassign(x,i);
+  andassign(i,x);
+  orassign(x,i);
+  orassign(i,x);
+  xorassign(x,i);
+  xorassign(i,x);
+
+  // DIAG: error: operator cannot be used with a bool lvalue
+  // DIAG: error: operator cannot be used with a bool lvalue
+  lshiftassign(b,i);
+  rshiftassign(b,i);
+
+  return 0;
+
+#else
+
+// CHECK: define void @main
+// CHECK-NOT: error
+// CHECK-NOT: warning
+
+  lshiftassign(j,i);
+  lshiftassign(uj,i);
+  lshiftassign(j,ui);
+  lshiftassign(i,b);
+
+  lshiftassignby2(j);
+  lshiftassignby2(ui);
+
+  rshiftassign(j,i);
+  rshiftassign(uj,i);
+  rshiftassign(j,ui);
+  rshiftassign(i,b);
+
+  andassign(j,i);
+  andassign(uj,i);
+  andassign(j,ui);
+  andassign(i,b);
+  andassign(b,i);
+
+  orassign(j,i);
+  orassign(uj,i);
+  orassign(j,ui);
+  orassign(i,b);
+  orassign(b,i);
+
+  xorassign(j,i);
+  xorassign(uj,i);
+  xorassign(j,ui);
+  xorassign(i,b);
+  xorassign(b,i);
+
+//  lshiftassign(i,3);
+//  xorassign(b,2);
+
+  return  0;
+
+#endif
+}

+ 125 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/BitwiseOps.hlsl

@@ -0,0 +1,125 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s 2>&1 | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s -DCHECK_DIAGNOSTICS | FileCheck %s -check-prefix=DIAG
+
+// Check that HLSL bitwise operators deal with dependent types
+
+template<typename T>
+T not(T t) {
+  return ~t;
+}
+
+template<typename T>
+T and(T t0, T t1) {
+  return t0 & t1;
+}
+
+template<typename T>
+T or(T t0, T t1) {
+  return t0 | t1;
+}
+
+template<typename T, typename I>
+T lshift(T t, I i) {
+  T r = t << i;
+  return r;
+}
+
+template<typename T, typename I>
+T rshift(T t, I i) {
+  return t >> i;
+}
+
+typedef struct {
+  int4 a;
+} S;
+
+int4 main(int4 a:A) : SV_Target {
+  int i = -7, j = 6;
+  unsigned int ui = 7, uj = 6;
+  int1 i1 = 10, j1 = 11;
+  int4 i4 = int4(1,2,3,4), j4 = int4(5,6,7,8);
+  int3x3 i3x3, j3x3;
+  int iarr[7] = {1,2,3,4,5,6,7}, jarr[7] ;
+  S s;
+  bool b;
+  bool1 b1;
+  bool3 b3;
+  bool2x2 b2x2;
+
+  float x, y;
+  float3 x3, y3;
+  float4x4 x4x4, y4x4;
+
+#ifdef CHECK_DIAGNOSTICS
+
+  // DIAG-NOT: define void @main
+
+  // DIAG: function cannot return array type
+  // DIAG: function cannot return array type
+  // DIAG: function cannot return array type
+  // DIAG: function cannot return array type
+  // DIAG: function cannot return array type
+  not(iarr);
+  and(iarr,iarr);
+  or(iarr,iarr);
+  lshift(iarr,i);
+  rshift(iarr,i);
+
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  // DIAG: error: scalar, vector, or matrix expected
+  not(s);
+  and(s,s);
+  or(s,s);
+  lshift(s,i);
+  rshift(s,i);
+
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  // DIAG: error: int or unsigned int type required
+  not(x);
+  and(x,x);
+  or(x,x);
+  lshift(x,i);
+  lshift(i,x);
+  rshift(x,i);
+  rshift(ui,x);
+
+  // DIAG-NOT: warning
+  // DIAG-NOT: error
+
+  return 0;
+
+#else
+
+// CHECK: define void @main
+  int r1 = not(i);
+  int r2 = not(i1);
+  int4 r3 = not(i4);
+  int3x3 r4 = not(i3x3);
+  int r5 = not(iarr[5]);
+  int r6 = not(i4.w);
+  int4 r7 = not(s.a);
+  bool3 b3b = not(b3);
+  not(ui);
+  not(b);
+  and(b,b);
+  or(b,b);
+
+  // Note that << and >> allow bool LHS operands, but <<= and >>= give "error: operator cannot be used with a bool lvalue"
+  lshift(b,i);
+  lshift(i,b);
+  rshift(b,i);
+  rshift(i,b);
+
+  return  r1 + r2 + r3.x + r4[3].y + r5 + r6;
+
+#endif
+
+}

+ 66 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/BooleanMathOps.hlsl

@@ -0,0 +1,66 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s -DCHECK_DIAGNOSTICS | FileCheck %s -check-prefix=DIAG
+
+
+template<typename T>
+T ternary_and(T t0, T t1) {
+  return t0 && t1 ? t0 : t1;
+}
+
+template<typename T>
+T ternary_or(T t0, T t1) {
+  return t0 || t1 ? t0 : t1;
+}
+
+typedef struct {
+  int4 a, b;
+} S;
+
+
+void main() : SV_Target {
+  bool ba, bb;
+  bool3 b3a, b3b;
+  bool4x4 b4x4a, b4x4b;
+  int ia, ib;
+  int3 i3a, i3b;
+  uint ua, ub;
+  uint2 u2a, u2b;
+  uint4 u4a, u4b;
+  float fa, fb;
+  float4 f4a, f4b;
+  int iarra[8], iarrb[8];
+  S sa, sb;
+
+#ifdef CHECK_DIAGNOSTICS
+
+  // DIAG-NOT: define void @main
+
+  // DIAG: deduced conflicting types for parameter
+  ternary_or(bb, sa);
+  // DIAG: function cannot return array type
+  ternary_or(iarra, iarra);
+
+  // DIAG: error: scalar, vector, or matrix expected
+  ternary_and(sa, sb);
+  // DIAG: error: scalar, vector, or matrix expected
+  ternary_or(sa, sb);
+
+
+#else
+
+// CHECK: define void @main
+
+  ternary_and(ba, bb);
+  ternary_and(b3a, b3b);
+  ternary_and(b4x4a, b4x4b);
+  ternary_and(ia, ib);
+  ternary_and(i3a, i3b);
+  ternary_and(ua, ub);
+  ternary_and(u4a, u4b);
+  ternary_and(fa, fb);
+  ternary_and(int(fa), ib);
+  ternary_or(iarra[2], iarra[4]);
+  ternary_and(sa.a, sb.b);
+
+#endif
+}

+ 45 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/ComparisonOps.hlsl

@@ -0,0 +1,45 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s -DCHECK_DIAGNOSTICS | FileCheck %s -check-prefix=DIAG
+
+
+template<typename T0, typename T1>
+T0 lessthan(T1 t0, T1 t1) {
+  return t0 < t1;
+}
+
+typedef struct {
+  int4 a, b;
+} S;
+
+
+int4 main() : SV_Target {
+  bool ba, bb;
+  bool3 b3a, b3b;
+  bool4x4 b4x4a, b4x4b;
+  int ia, ib;
+  int3 i3a, i3b;
+  uint ua, ub;
+  uint2 u2a, u2b;
+  uint4 u4a, u4b;
+  float fa, fb;
+  float4 f4a, f4b;
+  int iarra[8], iarrb[8];
+  S sa, sb;
+
+#ifdef CHECK_DIAGNOSTICS
+
+// DIAG: error: scalar, vector, or matrix expected
+// DIAG-NOT: define void @main
+
+  return lessthan<bool>(sa, sb);
+
+#else
+
+// CHECK: define void @main
+
+  lessthan<bool>(ia, ib);
+
+  return lessthan<bool4>(int4(1,3,7,11),int4(10,8,6,5));
+
+#endif
+}

+ 151 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/PrefixPostfixOps.hlsl

@@ -0,0 +1,151 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// RUN: %dxc -E main -T ps_6_0 %s -enable-templates -DCHECK_DIAGNOSTICS | FileCheck -check-prefix=DIAG %s
+
+template<typename T>
+T preinc(T t0) {
+  T r = ++t0;
+  return r;
+}
+
+template<typename T>
+T postinc(T t0) {
+  T r = t0++;
+  return r;
+}
+
+template<typename T>
+T predec(T t0) {
+  T r = --t0;
+  return r;
+}
+
+template<typename T>
+T postdec(T t0) {
+  T r = t0--;
+  return r;
+}
+
+struct S {
+  int4 i4;
+  float2 f2;
+  float3x3 f3x3;
+  bool4 b4;
+};
+
+void main() : SV_Target {
+  int i;
+  int1 i1;
+  int2 i2;
+  int3 i3;
+  int4 i4;
+  unsigned int j;
+  unsigned int2x2 j2x2;
+  float x;
+  float3 x3;
+  bool b;
+  bool2 b2;
+  S s;
+  float4x3 f4arr[11];
+
+#ifndef CHECK_DIAGNOSTICS
+
+// CHECK:define void @main
+
+  preinc(i);
+  preinc(i1);
+  preinc(i2);
+  preinc(i3);
+  preinc(i4);
+  preinc(i4.x);
+  preinc(i4.yz);
+  preinc(i4.xyz);
+  preinc(j);
+  preinc(j2x2);
+  preinc(x);
+  preinc(x3);
+  preinc(s.i4);
+  preinc(s.f2);
+  preinc(s.f3x3);
+  preinc(f4arr[9]);
+  preinc(f4arr[9][2]);
+  preinc(f4arr[3][1].xz);
+
+  postinc(i);
+  postinc(i1);
+  postinc(i2);
+  postinc(i3);
+  postinc(i4);
+  postinc(i4.x);
+  postinc(i4.yz);
+  postinc(i4.xyz);
+  postinc(j);
+  postinc(j2x2);
+  postinc(x);
+  postinc(x3);
+  postinc(s.i4);
+  postinc(s.f2);
+  postinc(s.f3x3);
+  postinc(f4arr[9]);
+  postinc(f4arr[5][2]);
+  postinc(f4arr[3][1].xz);
+
+  predec(i);
+  predec(i1);
+  predec(i2);
+  predec(i3);
+  predec(i4);
+  predec(i4.x);
+  predec(i4.yz);
+  predec(i4.xyz);
+  predec(j);
+  predec(j2x2);
+  predec(x);
+  predec(x3);
+  predec(s.i4);
+  predec(s.f2);
+  predec(s.f3x3);
+  postinc(f4arr[9]);
+  postinc(f4arr[4][3]);
+  postinc(f4arr[9][2].yz);
+
+  postdec(i);
+  postdec(i1);
+  postdec(i2);
+  postdec(i3);
+  postdec(i4);
+  postdec(i4.x);
+  postdec(i4.yz);
+  postdec(i4.xyz);
+  postdec(j);
+  postdec(j2x2);
+  postdec(x);
+  postdec(x3);
+  postdec(s.i4);
+  postdec(s.f2);
+  postdec(s.f3x3);
+  postdec(f4arr[9]);
+  postdec(f4arr[4][3]);
+  postdec(f4arr[9][2].yz);
+
+#else
+
+  // DIAG-NOT: define void @main
+
+  // DIAG: function cannot return array type
+  preinc(f4arr);
+
+  // DIAG: error: scalar, vector, or matrix expected
+  preinc(s);
+
+  // DIAG: error: operator cannot be used with a bool lvalue
+  // DIAG: error: operator cannot be used with a bool lvalue
+  // DIAG: error: operator cannot be used with a bool lvalue
+  preinc(b);
+  preinc(b2);
+  preinc(s.b4);
+
+  // DIAG-NOT: error
+
+#endif
+
+}

+ 44 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/RandomGenerators.hlsl

@@ -0,0 +1,44 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 0, float 0x404FD93640000000)
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 1, float 0x4047269780000000)
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 2, float 0x4045A83E40000000)
+// CHECK: call void @dx.op.storeOutput.f32(i32 5, i32 0, i32 0, i8 3, float 0x4045669660000000)
+
+struct MyRandomGeneratorA
+{
+  float seed;
+  float Rand()
+  {
+    // Not a good random number generator
+    seed = (seed * 17 + 57) / 99.0;
+    return seed;
+  }
+};
+
+struct MyRandomGeneratorB
+{
+  float Rand()
+  {
+    // An even worse random number generator
+    return 42.0;
+  }
+};
+
+template<typename GeneratorType>
+float4 Model(GeneratorType Generator)
+{
+ float4 E;
+ E.x = Generator.Rand();
+ E.y = Generator.Rand();
+ E.z = Generator.Rand();
+ E.w = Generator.Rand();
+ return E;
+};
+
+float4 main() : SV_TARGET {
+ MyRandomGeneratorA genA;
+ genA.seed = 123.0;
+ MyRandomGeneratorB genB;
+
+ return Model<MyRandomGeneratorA>(genA) + Model<MyRandomGeneratorB>(genB);
+}

+ 40 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/ackermann.hlsl

@@ -0,0 +1,40 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 0, i32 125)
+
+// template<unsigned M, unsigned N>
+// struct Ackermann {
+//   enum {
+//     value = M ? (N ? Ackermann<M-1, Ackermann<M, N-1> >::value
+//                    : Ackermann<M-1, 1>::value)
+//               : N + 1
+//   };
+// };
+
+template<unsigned int M, unsigned int N>
+struct Ackermann {
+ enum {
+   value = Ackermann<M-1, Ackermann<M, N-1>::value >::value
+ };
+};
+
+template<unsigned int M> struct Ackermann<M, 0> {
+ enum {
+   value = Ackermann<M-1, 1>::value
+ };
+};
+
+template<unsigned int N> struct Ackermann<0, N> {
+ enum {
+   value = N + 1
+ };
+};
+
+template<> struct Ackermann<0, 0> {
+ enum {
+   value = 1
+ };
+};
+
+int main(int a:A) : SV_Target {
+  return Ackermann<3, 4>::value;
+}

+ 28 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/dependent-sized_array.hlsl

@@ -0,0 +1,28 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// CHECK: error: 'a3' declared as an array with a negative size
+
+template<int N>
+void f() {
+  int a[] = { 1, 2, 3, N };
+  uint numAs = sizeof(a) / sizeof(int);
+}
+
+template void f<17>();
+
+template<int N>
+void f1() {
+  int a0[] = {}; // expected-warning{{zero}}
+  int a1[] = { 1, 2, 3, N };
+  int a3[sizeof(a1)/sizeof(int) != 4? 1 : -1]; // expected-error{{negative}}
+}
+
+namespace PR13788 {
+  template <uint __N>
+  struct S {
+    int V;
+  };
+  template <int N>
+  void foo() {
+    S<0> arr[N] = {{ 4 }};
+  }
+}

+ 50 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/elaborated-type-specifier.hlsl

@@ -0,0 +1,50 @@
+// RUN: %dxc -T ps_6_0 -enable-templates %s 2>&1| FileCheck %s
+// CHECK: error: use of 'X' with tag type that does not match previous declaration
+// CHECK: note: in instantiation of template class 'PR6915::D<PR6915::D2>' requested here
+// CHECK: note: previous use is here
+// CHECK: error: no enum named 'X' in 'PR6915::D3'
+// CHECK: error: nested name specifier for a declaration cannot depend on a template parameter
+// CHECK: error: nested name specifier for a declaration cannot depend on a template parameter
+// CHECK: error: nested name specifier for a declaration cannot depend on a template parameter
+// CHECK: error: nested name specifier for a declaration cannot depend on a template parameter
+// XCHECK: error: nested name specifier for a declaration cannot depend on a template parameter
+// CHECK-NOT: error:
+
+namespace PR6915 {
+  template <typename T>
+  class D {
+    enum T::X v; // expected-error{{use of 'X' with tag type that does not match previous declaration}} \
+    // expected-error{{no enum named 'X' in 'PR6915::D3'}}
+  };
+
+  struct D1 {
+    enum X { value };
+  };
+  struct D2 {
+    class X { }; // expected-note{{previous use is here}}
+  };
+  struct D3 { };
+
+  template class D<D1>;
+  template class D<D2>; // expected-note{{in instantiation of}}
+  template class D<D3>; // expected-note{{in instantiation of}}
+}
+
+template<typename T>
+struct DeclOrDef {
+  enum T::foo; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
+  enum T::bar { // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
+    value
+  };
+};
+
+namespace PR6649 {
+  template <typename T> struct foo {
+    class T::bar;  // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
+    class T::bar { int x; }; // expected-error{{nested name specifier for a declaration cannot depend on a template parameter}}
+  };
+}
+
+namespace rdar8568507 {
+  template <class T> struct A makeA(T t);
+}

+ 8 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/enum-forward.hlsl

@@ -0,0 +1,8 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// CHECK: error: ISO C++ forbids forward references to 'enum' types
+
+template<typename T>
+struct X {
+  enum E e;
+};
+

+ 19 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/factorial.hlsl

@@ -0,0 +1,19 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// CHECK: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 0, i32 1)
+
+template<uint n>
+struct factorial {
+  enum { value = n * factorial<n -1>::value };
+};
+
+template<>
+struct factorial<0> {
+  enum { value = 1 };
+};
+
+bool main(int4 a:A) : SV_Target {
+   return (factorial<0>::value == 1)
+       && (factorial<1>::value == 1)
+       && (factorial<3>::value == 6)
+       && (factorial<4>::value == 24);
+}

+ 12 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/templateFunc.hlsl

@@ -0,0 +1,12 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+
+// CHECK:define void @main
+
+template<typename T>
+T foo(T t0, T t1) {
+  return sin(t0) * cos(t1);
+}
+
+float2 main(float4 a:A) : SV_Target {
+  return foo(a.x, a.y) + foo(a.xy, a.zw);
+}

+ 18 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/templateMethod.hlsl

@@ -0,0 +1,18 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+
+// CHECK:define void @main
+
+struct Test {
+
+template<typename T>
+T foo(T t) {
+  return sin(t);
+}
+
+};
+
+float2 main(float4 a:A) : SV_Target {
+  Test t0;
+  Test t1;
+  return t0.foo<float>(a.y) + t1.foo<float2>(a.zw);
+}

+ 14 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/templateStruct.hlsl

@@ -0,0 +1,14 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+
+// CHECK:define void @main
+
+template<typename T>
+struct TS {
+  T t;
+};
+
+struct TS<float4> ts;
+
+float4 main() : SV_Target {
+  return ts.t;
+}

+ 21 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/templateStructFunc.hlsl

@@ -0,0 +1,21 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+
+// CHECK:define void @main
+
+template<typename T>
+struct Test {
+
+T t;
+T foo(T t1) {
+  return sin(t) * cos(t1);
+}
+
+};
+
+float2 main(float4 a:A) : SV_Target {
+  Test<float> t0;
+  t0.t = a.x;
+  Test<float2> t1;
+  t1.t = a.xy;
+  return t0.foo(a.y) + t1.foo(a.zw);
+}

+ 13 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/templateStructFunc2.hlsl

@@ -0,0 +1,13 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+
+// CHECK:define void @main
+
+
+template<typename T>
+T foo(T t0, T t1) {
+  return sin(t0) * cos(t1);
+}
+
+float2 main(float4 a:A) : SV_Target {
+  return foo(a.x, a.y) + foo(a.xy, a.zw);
+}

+ 24 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/templateSubscripts.hlsl

@@ -0,0 +1,24 @@
+// RUN: %dxc -T ps_6_0 -enable-templates %s | FileCheck %s
+
+// Test applying the [] subscript operator in a templated function.
+// With the side effect of testing passing matrices, arrays, and vectors as params.
+
+template<typename T>
+float4 subscript(T t0) {
+  return t0[3];
+}
+
+// CHECK: define void @main
+// CHECK: @dx.op.loadInput.f32
+// CHECK: @dx.op.loadInput.f32
+// CHECK: @dx.op.loadInput.f32
+bool main(float scalar : A, float4 vec : B, float4x4 mat : C, float4 arr[6] : D) : SV_Target {
+
+  return subscript(vec) + subscript(mat) + subscript(arr);
+
+}
+
+
+
+
+

+ 31 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/templateTypename.hlsl

@@ -0,0 +1,31 @@
+// RUN: %dxc -DTEMPLATE= -T ps_6_0 -enable-templates %s | FileCheck %s -check-prefix=CHK_FAIL
+// RUN: %dxc -DTEMPLATE=template -T ps_6_0 -enable-templates %s | FileCheck %s -check-prefix=CHK_PASS
+
+// CHK_PASS:define void @main
+
+template<typename T>
+struct F1 {
+  template <int B>
+  struct Iterator {
+    T t;
+  };
+};
+
+template<typename T>
+struct F2  {
+  // CHK_FAIL: use 'template' keyword to treat 'Iterator' as a dependent template name
+  typename F1<T>:: TEMPLATE  Iterator<0> Mypos; // expected-error {{
+};
+
+struct F2<float4> ts;
+
+template <typename T>
+float4 f(){
+  // CHK_FAIL: use 'template' keyword to treat 'Iterator' as a dependent template name
+  typename F1<T>:: TEMPLATE Iterator<0> Mypos = ts.Mypos;
+  return Mypos.t;
+}
+
+float4 main() : SV_Target {
+  return f<float4>();
+}

+ 16 - 0
tools/clang/test/HLSLFileCheck/hlsl/template/variadic.hlsl

@@ -0,0 +1,16 @@
+// RUN: %dxc -E main -T ps_6_0 -enable-templates %s | FileCheck %s
+// CHECK: error: variadic templates are not supported in HLSL
+
+template<typename T>
+T summation (T n) {
+  return n;
+};
+
+template<typename T, typename... Args>
+T summation(T n, Args... args) {
+  return n + summation(args...);
+};
+
+int main(int a:A) : SV_Target {
+   return summation(1,3, 11, 7, a);
+}

+ 1 - 0
tools/clang/tools/dxcompiler/dxcompilerobj.cpp

@@ -1247,6 +1247,7 @@ public:
     compiler.getLangOpts().HLSLVersion = (unsigned) Opts.HLSLVersion;
     compiler.getLangOpts().EnableDX9CompatMode = Opts.EnableDX9CompatMode;
     compiler.getLangOpts().EnableFXCCompatMode = Opts.EnableFXCCompatMode;
+    compiler.getLangOpts().EnableTemplates = Opts.EnableTemplates;
 
     compiler.getLangOpts().UseMinPrecision = !Opts.Enable16BitTypes;