tint: Fix ProgramBuilder::WrapInFunction overload not being picked up

Before this change, the variadic function template version of
WrapInFunction would be selected when passing a utils::VectorRef<const
ast::Statement*>, even though an overload exists for that type. The
reason is that during type deduction, the compiler will select templates
over non-templates in its overload set. The only way around this was to
avoid type-deduction by explicitly casting the argument to
utils::VectorRef<const ast::Statement*>.

This CL adds a CanWrapInStatement metafunction that evaluates to true if
the arg type is one that could be passed to
ProgramBuilder::WrapInStatement. This is used to SFINAE in the variadic
args version of WrapInFunction.

Change-Id: I8aa3d69e2ce7324fd60b1b2a5906a51d51b549a3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/115502
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 5396cdd..4dac010 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -149,6 +149,10 @@
 };
 }  // namespace detail
 
+// Forward declare metafunction that evaluates to true iff T can be wrapped in a statement.
+template <typename T, typename = void>
+struct CanWrapInStatement;
+
 /// ProgramBuilder is a mutable builder for a Program.
 /// To construct a Program, populate the builder and then `std::move` it to a
 /// Program.
@@ -3326,12 +3330,13 @@
     /// by the Resolver.
     /// @param args a mix of ast::Expression, ast::Statement, ast::Variables.
     /// @returns the function
-    template <typename... ARGS>
+    template <typename... ARGS,
+              typename = traits::EnableIf<(CanWrapInStatement<ARGS>::value && ...)>>
     const ast::Function* WrapInFunction(ARGS&&... args) {
         utils::Vector stmts{
             WrapInStatement(std::forward<ARGS>(args))...,
         };
-        return WrapInFunction(utils::VectorRef<const ast::Statement*>{std::move(stmts)});
+        return WrapInFunction(std::move(stmts));
     }
     /// @param stmts a list of ast::Statement that will be wrapped by a function,
     /// so that each statement is reachable by the Resolver.
@@ -3411,6 +3416,17 @@
     return builder->ID();
 }
 
+// Primary template for metafunction that evaluates to true iff T can be wrapped in a statement.
+template <typename T, typename /*  = void */>
+struct CanWrapInStatement : std::false_type {};
+
+// Specialization of CanWrapInStatement
+template <typename T>
+struct CanWrapInStatement<
+    T,
+    std::void_t<decltype(std::declval<ProgramBuilder>().WrapInStatement(std::declval<T>()))>>
+    : std::true_type {};
+
 }  // namespace tint
 
 #endif  // SRC_TINT_PROGRAM_BUILDER_H_