resolver: DepGraph - Traverse types and decorations.

These also need to depend on types / values.

Bug: tint:819
Bug: tint:1266
Change-Id: Ia044d7823aca845dc57a887a164e07137d913429
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/70522
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/resolver/dependency_graph.cc b/src/resolver/dependency_graph.cc
index 3acdfbc..2d92343 100644
--- a/src/resolver/dependency_graph.cc
+++ b/src/resolver/dependency_graph.cc
@@ -26,6 +26,7 @@
 #include "src/scope_stack.h"
 #include "src/sem/intrinsic.h"
 #include "src/utils/defer.h"
+#include "src/utils/map.h"
 #include "src/utils/scoped_assignment.h"
 #include "src/utils/unique_vector.h"
 
@@ -87,11 +88,10 @@
 /// A map of global name to Global
 using GlobalMap = std::unordered_map<Symbol, Global*>;
 
-/// Raises an ICE that a global ast::Node declaration type was not handled by
-/// this system.
-void UnhandledDecl(diag::List& diagnostics, const ast::Node* node) {
-  TINT_UNREACHABLE(Resolver, diagnostics)
-      << "unhandled global declaration: " << node->TypeInfo().name;
+/// Raises an ICE that a global ast::Node type was not handled by this system.
+void UnhandledNode(diag::List& diagnostics, const ast::Node* node) {
+  TINT_ICE(Resolver, diagnostics)
+      << "unhandled node type: " << node->TypeInfo().name;
 }
 
 /// Raises an error diagnostic with the given message and source.
@@ -143,55 +143,59 @@
     if (auto* str = global->node->As<ast::Struct>()) {
       Declare(str->name, str);
       for (auto* member : str->members) {
-        ResolveTypeDependency(member->type);
+        TraverseType(member->type);
       }
       return;
     }
     if (auto* alias = global->node->As<ast::Alias>()) {
       Declare(alias->name, alias);
-      ResolveTypeDependency(alias->type);
+      TraverseType(alias->type);
       return;
     }
     if (auto* func = global->node->As<ast::Function>()) {
       Declare(func->symbol, func);
+      TraverseDecorations(func->decorations);
       TraverseFunction(func);
       return;
     }
     if (auto* var = global->node->As<ast::Variable>()) {
       Declare(var->symbol, var);
-      ResolveTypeDependency(var->type);
+      TraverseType(var->type);
       if (var->constructor) {
         TraverseExpression(var->constructor);
       }
       return;
     }
-    UnhandledDecl(diagnostics_, global->node);
+    UnhandledNode(diagnostics_, global->node);
   }
 
  private:
-  /// Traverses the function determining global dependencies.
+  /// Traverses the function, performing symbol resolution and determining
+  /// global dependencies.
   void TraverseFunction(const ast::Function* func) {
     scope_stack_.Push();
     TINT_DEFER(scope_stack_.Pop());
 
     for (auto* param : func->params) {
       Declare(param->symbol, param);
-      ResolveTypeDependency(param->type);
+      TraverseType(param->type);
     }
     if (func->body) {
       TraverseStatements(func->body->statements);
     }
-    ResolveTypeDependency(func->return_type);
+    TraverseType(func->return_type);
   }
 
-  /// Traverses the statements determining global dependencies.
+  /// Traverses the statements, performing symbol resolution and determining
+  /// global dependencies.
   void TraverseStatements(const ast::StatementList& stmts) {
     for (auto* s : stmts) {
       TraverseStatement(s);
     }
   }
 
-  /// Traverses the statement determining global dependencies.
+  /// Traverses the statement, performing symbol resolution and determining
+  /// global dependencies.
   void TraverseStatement(const ast::Statement* stmt) {
     if (stmt == nullptr) {
       return;
@@ -253,7 +257,7 @@
     }
     if (auto* v = stmt->As<ast::VariableDeclStatement>()) {
       Declare(v->variable->symbol, v->variable);
-      ResolveTypeDependency(v->variable->type);
+      TraverseType(v->variable->type);
       TraverseExpression(v->variable->constructor);
       return;
     }
@@ -262,9 +266,7 @@
       return;
     }
 
-    AddError(diagnostics_,
-             "unknown statement type: " + std::string(stmt->TypeInfo().name),
-             stmt->source);
+    UnhandledNode(diagnostics_, stmt);
   }
 
   /// Adds the symbol definition to the current scope, raising an error if two
@@ -279,7 +281,8 @@
     }
   }
 
-  /// Traverses the expression determining global dependencies.
+  /// Traverses the expression, performing symbol resolution and determining
+  /// global dependencies.
   void TraverseExpression(const ast::Expression* root) {
     if (!root) {
       return;
@@ -309,24 +312,101 @@
                 ResolveGlobalDependency(call->target.name,
                                         call->target.name->symbol, "function",
                                         "calls");
+                graph_.resolved_symbols.emplace(
+                    call,
+                    utils::Lookup(graph_.resolved_symbols, call->target.name));
               }
             }
             if (call->target.type) {
-              ResolveTypeDependency(call->target.type);
+              TraverseType(call->target.type);
+              graph_.resolved_symbols.emplace(
+                  call,
+                  utils::Lookup(graph_.resolved_symbols, call->target.type));
             }
           }
           return ast::TraverseAction::Descend;
         });
   }
 
-  /// Adds the type dependency to the currently processed global
-  void ResolveTypeDependency(const ast::Type* ty) {
+  /// Traverses the type node, performing symbol resolution and determining
+  /// global dependencies.
+  void TraverseType(const ast::Type* ty) {
     if (ty == nullptr) {
       return;
     }
+    if (auto* arr = ty->As<ast::Array>()) {
+      TraverseType(arr->type);
+      TraverseExpression(arr->count);
+      return;
+    }
+    if (auto* atomic = ty->As<ast::Atomic>()) {
+      TraverseType(atomic->type);
+      return;
+    }
+    if (auto* mat = ty->As<ast::Matrix>()) {
+      TraverseType(mat->type);
+      return;
+    }
+    if (auto* ptr = ty->As<ast::Pointer>()) {
+      TraverseType(ptr->type);
+      return;
+    }
     if (auto* tn = ty->As<ast::TypeName>()) {
       ResolveGlobalDependency(tn, tn->name, "type", "references");
+      return;
     }
+    if (auto* vec = ty->As<ast::Vector>()) {
+      TraverseType(vec->type);
+      return;
+    }
+    if (auto* tex = ty->As<ast::SampledTexture>()) {
+      TraverseType(tex->type);
+      return;
+    }
+    if (auto* tex = ty->As<ast::MultisampledTexture>()) {
+      TraverseType(tex->type);
+      return;
+    }
+    if (ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F32,
+                    ast::DepthTexture, ast::DepthMultisampledTexture,
+                    ast::StorageTexture, ast::ExternalTexture,
+                    ast::Sampler>()) {
+      return;
+    }
+
+    UnhandledNode(diagnostics_, ty);
+  }
+
+  /// Traverses the decoration list, performing symbol resolution and
+  /// determining global dependencies.
+  void TraverseDecorations(const ast::DecorationList& decos) {
+    for (auto* deco : decos) {
+      TraverseDecoration(deco);
+    }
+  }
+
+  /// Traverses the decoration, performing symbol resolution and determining
+  /// global dependencies.
+  void TraverseDecoration(const ast::Decoration* deco) {
+    if (auto* wg = deco->As<ast::WorkgroupDecoration>()) {
+      TraverseExpression(wg->x);
+      TraverseExpression(wg->y);
+      TraverseExpression(wg->z);
+      return;
+    }
+    if (deco->IsAnyOf<ast::BindingDecoration, ast::BuiltinDecoration,
+                      ast::GroupDecoration, ast::InternalDecoration,
+                      ast::InterpolateDecoration, ast::InvariantDecoration,
+                      ast::LocationDecoration, ast::OverrideDecoration,
+                      ast::StageDecoration, ast::StrideDecoration,
+                      ast::StructBlockDecoration,
+                      ast::StructMemberAlignDecoration,
+                      ast::StructMemberOffsetDecoration,
+                      ast::StructMemberSizeDecoration>()) {
+      return;
+    }
+
+    UnhandledNode(diagnostics_, deco);
   }
 
   /// Adds the dependency to the currently processed global
@@ -426,7 +506,7 @@
     if (auto* var = node->As<ast::Variable>()) {
       return var->symbol;
     }
-    UnhandledDecl(diagnostics_, node);
+    UnhandledNode(diagnostics_, node);
     return {};
   }
 
@@ -455,7 +535,7 @@
     if (auto* var = node->As<ast::Variable>()) {
       return var->is_const ? "let" : "var";
     }
-    UnhandledDecl(diagnostics_, node);
+    UnhandledNode(diagnostics_, node);
     return {};
   }
 
diff --git a/src/resolver/dependency_graph_test.cc b/src/resolver/dependency_graph_test.cc
index a884cfa..86c122b 100644
--- a/src/resolver/dependency_graph_test.cc
+++ b/src/resolver/dependency_graph_test.cc
@@ -101,14 +101,28 @@
 /// kinds of symbol uses.
 enum class SymbolUseKind {
   GlobalVarType,
+  GlobalVarArrayElemType,
+  GlobalVarArraySizeValue,
+  GlobalVarVectorElemType,
+  GlobalVarMatrixElemType,
+  GlobalVarSampledTexElemType,
+  GlobalVarMultisampledTexElemType,
   GlobalVarValue,
   GlobalLetType,
+  GlobalLetArrayElemType,
+  GlobalLetArraySizeValue,
+  GlobalLetVectorElemType,
+  GlobalLetMatrixElemType,
   GlobalLetValue,
   AliasType,
   StructMemberType,
   CallFunction,
   ParameterType,
   LocalVarType,
+  LocalVarArrayElemType,
+  LocalVarArraySizeValue,
+  LocalVarVectorElemType,
+  LocalVarMatrixElemType,
   LocalVarValue,
   LocalLetType,
   LocalLetValue,
@@ -116,13 +130,32 @@
   NestedLocalVarValue,
   NestedLocalLetType,
   NestedLocalLetValue,
+  WorkgroupSizeValue,
 };
 
 static constexpr SymbolUseKind kTypeUseKinds[] = {
-    SymbolUseKind::GlobalVarType,      SymbolUseKind::GlobalLetType,
-    SymbolUseKind::AliasType,          SymbolUseKind::StructMemberType,
-    SymbolUseKind::ParameterType,      SymbolUseKind::LocalVarType,
-    SymbolUseKind::LocalLetType,       SymbolUseKind::NestedLocalVarType,
+    SymbolUseKind::GlobalVarType,
+    SymbolUseKind::GlobalVarArrayElemType,
+    SymbolUseKind::GlobalVarArraySizeValue,
+    SymbolUseKind::GlobalVarVectorElemType,
+    SymbolUseKind::GlobalVarMatrixElemType,
+    SymbolUseKind::GlobalVarSampledTexElemType,
+    SymbolUseKind::GlobalVarMultisampledTexElemType,
+    SymbolUseKind::GlobalLetType,
+    SymbolUseKind::GlobalLetArrayElemType,
+    SymbolUseKind::GlobalLetArraySizeValue,
+    SymbolUseKind::GlobalLetVectorElemType,
+    SymbolUseKind::GlobalLetMatrixElemType,
+    SymbolUseKind::AliasType,
+    SymbolUseKind::StructMemberType,
+    SymbolUseKind::ParameterType,
+    SymbolUseKind::LocalVarType,
+    SymbolUseKind::LocalVarArrayElemType,
+    SymbolUseKind::LocalVarArraySizeValue,
+    SymbolUseKind::LocalVarVectorElemType,
+    SymbolUseKind::LocalVarMatrixElemType,
+    SymbolUseKind::LocalLetType,
+    SymbolUseKind::NestedLocalVarType,
     SymbolUseKind::NestedLocalLetType,
 };
 
@@ -130,6 +163,7 @@
     SymbolUseKind::GlobalVarValue,      SymbolUseKind::GlobalLetValue,
     SymbolUseKind::LocalVarValue,       SymbolUseKind::LocalLetValue,
     SymbolUseKind::NestedLocalVarValue, SymbolUseKind::NestedLocalLetValue,
+    SymbolUseKind::WorkgroupSizeValue,
 };
 
 static constexpr SymbolUseKind kFuncUseKinds[] = {
@@ -172,10 +206,30 @@
       return out << "global var type";
     case SymbolUseKind::GlobalVarValue:
       return out << "global var value";
+    case SymbolUseKind::GlobalVarArrayElemType:
+      return out << "global var array element type";
+    case SymbolUseKind::GlobalVarArraySizeValue:
+      return out << "global var array size value";
+    case SymbolUseKind::GlobalVarVectorElemType:
+      return out << "global var vector element type";
+    case SymbolUseKind::GlobalVarMatrixElemType:
+      return out << "global var matrix element type";
+    case SymbolUseKind::GlobalVarSampledTexElemType:
+      return out << "global var sampled_texture element type";
+    case SymbolUseKind::GlobalVarMultisampledTexElemType:
+      return out << "global var multisampled_texture element type";
     case SymbolUseKind::GlobalLetType:
       return out << "global let type";
     case SymbolUseKind::GlobalLetValue:
       return out << "global let value";
+    case SymbolUseKind::GlobalLetArrayElemType:
+      return out << "global let array element type";
+    case SymbolUseKind::GlobalLetArraySizeValue:
+      return out << "global let array size value";
+    case SymbolUseKind::GlobalLetVectorElemType:
+      return out << "global let vector element type";
+    case SymbolUseKind::GlobalLetMatrixElemType:
+      return out << "global let matrix element type";
     case SymbolUseKind::AliasType:
       return out << "alias type";
     case SymbolUseKind::StructMemberType:
@@ -186,6 +240,14 @@
       return out << "parameter type";
     case SymbolUseKind::LocalVarType:
       return out << "local var type";
+    case SymbolUseKind::LocalVarArrayElemType:
+      return out << "local var array element type";
+    case SymbolUseKind::LocalVarArraySizeValue:
+      return out << "local var array size value";
+    case SymbolUseKind::LocalVarVectorElemType:
+      return out << "local var vector element type";
+    case SymbolUseKind::LocalVarMatrixElemType:
+      return out << "local var matrix element type";
     case SymbolUseKind::LocalVarValue:
       return out << "local var value";
     case SymbolUseKind::LocalLetType:
@@ -200,6 +262,8 @@
       return out << "nested local let type";
     case SymbolUseKind::NestedLocalLetValue:
       return out << "nested local let value";
+    case SymbolUseKind::WorkgroupSizeValue:
+      return out << "workgroup size value";
   }
   return out << "<unknown>";
 }
@@ -208,21 +272,36 @@
 std::string DiagString(SymbolUseKind kind) {
   switch (kind) {
     case SymbolUseKind::GlobalVarType:
+    case SymbolUseKind::GlobalVarArrayElemType:
+    case SymbolUseKind::GlobalVarVectorElemType:
+    case SymbolUseKind::GlobalVarMatrixElemType:
+    case SymbolUseKind::GlobalVarSampledTexElemType:
+    case SymbolUseKind::GlobalVarMultisampledTexElemType:
     case SymbolUseKind::GlobalLetType:
+    case SymbolUseKind::GlobalLetArrayElemType:
+    case SymbolUseKind::GlobalLetVectorElemType:
+    case SymbolUseKind::GlobalLetMatrixElemType:
     case SymbolUseKind::AliasType:
     case SymbolUseKind::StructMemberType:
     case SymbolUseKind::ParameterType:
     case SymbolUseKind::LocalVarType:
+    case SymbolUseKind::LocalVarArrayElemType:
+    case SymbolUseKind::LocalVarVectorElemType:
+    case SymbolUseKind::LocalVarMatrixElemType:
     case SymbolUseKind::LocalLetType:
     case SymbolUseKind::NestedLocalVarType:
     case SymbolUseKind::NestedLocalLetType:
       return "type";
     case SymbolUseKind::GlobalVarValue:
+    case SymbolUseKind::GlobalVarArraySizeValue:
     case SymbolUseKind::GlobalLetValue:
+    case SymbolUseKind::GlobalLetArraySizeValue:
     case SymbolUseKind::LocalVarValue:
+    case SymbolUseKind::LocalVarArraySizeValue:
     case SymbolUseKind::LocalLetValue:
     case SymbolUseKind::NestedLocalVarValue:
     case SymbolUseKind::NestedLocalLetValue:
+    case SymbolUseKind::WorkgroupSizeValue:
       return "identifier";
     case SymbolUseKind::CallFunction:
       return "function";
@@ -259,14 +338,29 @@
   switch (kind) {
     case SymbolUseKind::GlobalVarType:
     case SymbolUseKind::GlobalVarValue:
+    case SymbolUseKind::GlobalVarArrayElemType:
+    case SymbolUseKind::GlobalVarArraySizeValue:
+    case SymbolUseKind::GlobalVarVectorElemType:
+    case SymbolUseKind::GlobalVarMatrixElemType:
+    case SymbolUseKind::GlobalVarSampledTexElemType:
+    case SymbolUseKind::GlobalVarMultisampledTexElemType:
     case SymbolUseKind::GlobalLetType:
     case SymbolUseKind::GlobalLetValue:
+    case SymbolUseKind::GlobalLetArrayElemType:
+    case SymbolUseKind::GlobalLetArraySizeValue:
+    case SymbolUseKind::GlobalLetVectorElemType:
+    case SymbolUseKind::GlobalLetMatrixElemType:
     case SymbolUseKind::AliasType:
     case SymbolUseKind::StructMemberType:
+    case SymbolUseKind::WorkgroupSizeValue:
       return 0;
     case SymbolUseKind::CallFunction:
     case SymbolUseKind::ParameterType:
     case SymbolUseKind::LocalVarType:
+    case SymbolUseKind::LocalVarArrayElemType:
+    case SymbolUseKind::LocalVarArraySizeValue:
+    case SymbolUseKind::LocalVarVectorElemType:
+    case SymbolUseKind::LocalVarMatrixElemType:
     case SymbolUseKind::LocalVarValue:
     case SymbolUseKind::LocalLetType:
     case SymbolUseKind::LocalLetValue:
@@ -290,6 +384,8 @@
   std::vector<const ast::Statement*> statements;
   /// Nested function local var / let declaration statements
   std::vector<const ast::Statement*> nested_statements;
+  /// Function decorations
+  ast::DecorationList func_decos;
 
   /// Constructor
   /// @param builder the program builder
@@ -374,6 +470,38 @@
       b.Global(b.Sym(), node, ast::StorageClass::kPrivate);
       return node;
     }
+    case SymbolUseKind::GlobalVarArrayElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.Global(b.Sym(), b.ty.array(node, 4), ast::StorageClass::kPrivate);
+      return node;
+    }
+    case SymbolUseKind::GlobalVarArraySizeValue: {
+      auto* node = b.Expr(source, symbol);
+      b.Global(b.Sym(), b.ty.array(b.ty.i32(), node),
+               ast::StorageClass::kPrivate);
+      return node;
+    }
+    case SymbolUseKind::GlobalVarVectorElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.Global(b.Sym(), b.ty.vec3(node), ast::StorageClass::kPrivate);
+      return node;
+    }
+    case SymbolUseKind::GlobalVarMatrixElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.Global(b.Sym(), b.ty.mat3x4(node), ast::StorageClass::kPrivate);
+      return node;
+    }
+    case SymbolUseKind::GlobalVarSampledTexElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.Global(b.Sym(), b.ty.sampled_texture(ast::TextureDimension::k2d, node));
+      return node;
+    }
+    case SymbolUseKind::GlobalVarMultisampledTexElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.Global(b.Sym(),
+               b.ty.multisampled_texture(ast::TextureDimension::k2d, node));
+      return node;
+    }
     case SymbolUseKind::GlobalVarValue: {
       auto* node = b.Expr(source, symbol);
       b.Global(b.Sym(), b.ty.i32(), ast::StorageClass::kPrivate, node);
@@ -384,6 +512,26 @@
       b.GlobalConst(b.Sym(), node, b.Expr(1));
       return node;
     }
+    case SymbolUseKind::GlobalLetArrayElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.GlobalConst(b.Sym(), b.ty.array(node, 4), b.Expr(1));
+      return node;
+    }
+    case SymbolUseKind::GlobalLetArraySizeValue: {
+      auto* node = b.Expr(source, symbol);
+      b.GlobalConst(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1));
+      return node;
+    }
+    case SymbolUseKind::GlobalLetVectorElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.GlobalConst(b.Sym(), b.ty.vec3(node), b.Expr(1));
+      return node;
+    }
+    case SymbolUseKind::GlobalLetMatrixElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      b.GlobalConst(b.Sym(), b.ty.mat3x4(node), b.Expr(1));
+      return node;
+    }
     case SymbolUseKind::GlobalLetValue: {
       auto* node = b.Expr(source, symbol);
       b.GlobalConst(b.Sym(), b.ty.i32(), node);
@@ -414,6 +562,28 @@
       statements.emplace_back(b.Decl(b.Var(b.Sym(), node)));
       return node;
     }
+    case SymbolUseKind::LocalVarArrayElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      statements.emplace_back(
+          b.Decl(b.Var(b.Sym(), b.ty.array(node, 4), b.Expr(1))));
+      return node;
+    }
+    case SymbolUseKind::LocalVarArraySizeValue: {
+      auto* node = b.Expr(source, symbol);
+      statements.emplace_back(
+          b.Decl(b.Var(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1))));
+      return node;
+    }
+    case SymbolUseKind::LocalVarVectorElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.vec3(node))));
+      return node;
+    }
+    case SymbolUseKind::LocalVarMatrixElemType: {
+      auto* node = b.ty.type_name(source, symbol);
+      statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.mat3x4(node))));
+      return node;
+    }
     case SymbolUseKind::LocalVarValue: {
       auto* node = b.Expr(source, symbol);
       statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
@@ -450,6 +620,11 @@
           b.Decl(b.Const(b.Sym(), b.ty.i32(), node)));
       return node;
     }
+    case SymbolUseKind::WorkgroupSizeValue: {
+      auto* node = b.Expr(source, symbol);
+      func_decos.emplace_back(b.WorkgroupSize(1, node, 2));
+      return node;
+    }
   }
   return nullptr;
 }
@@ -460,10 +635,11 @@
     statements.emplace_back(b.Block(nested_statements));
     nested_statements.clear();
   }
-  if (!parameters.empty() || !statements.empty()) {
-    b.Func("func", parameters, b.ty.void_(), statements);
+  if (!parameters.empty() || !statements.empty() || !func_decos.empty()) {
+    b.Func("func", parameters, b.ty.void_(), statements, func_decos);
     parameters.clear();
     statements.clear();
+    func_decos.clear();
   }
 }
 
@@ -998,9 +1174,9 @@
   Structure(Sym(), {Member(Sym(), T)});
   Global(Sym(), T, V);
   GlobalConst(Sym(), T, V);
-  Func(Sym(),            //
-       {Param("p", T)},  //
-       T,                // Return type
+  Func(Sym(),              //
+       {Param(Sym(), T)},  //
+       T,                  // Return type
        {
            Decl(Var(Sym(), T, V)),                    //
            Decl(Const(Sym(), T, V)),                  //
@@ -1027,7 +1203,27 @@
            Return(V),                                 //
            Break(),                                   //
            Discard(),                                 //
-       });
+       });                                            //
+  // Exercise type traversal
+  Global(Sym(), ty.atomic(T));
+  Global(Sym(), ty.bool_());
+  Global(Sym(), ty.i32());
+  Global(Sym(), ty.u32());
+  Global(Sym(), ty.f32());
+  Global(Sym(), ty.array(T, V, 4));
+  Global(Sym(), ty.vec3(T));
+  Global(Sym(), ty.mat3x2(T));
+  Global(Sym(), ty.pointer(T, ast::StorageClass::kPrivate));
+  Global(Sym(), ty.sampled_texture(ast::TextureDimension::k2d, T));
+  Global(Sym(), ty.depth_texture(ast::TextureDimension::k2d));
+  Global(Sym(), ty.depth_multisampled_texture(ast::TextureDimension::k2d));
+  Global(Sym(), ty.external_texture());
+  Global(Sym(), ty.multisampled_texture(ast::TextureDimension::k2d, T));
+  Global(Sym(), ty.storage_texture(ast::TextureDimension::k2d,
+                                   ast::ImageFormat::kR16Float,
+                                   ast::Access::kRead));  //
+  Global(Sym(), ty.sampler(ast::SamplerKind::kSampler));
+  Func(Sym(), {}, ty.void_(), {});
 #undef V
 #undef T
 #undef F
diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc
index 280ef99..790fde5 100644
--- a/src/resolver/validation_test.cc
+++ b/src/resolver/validation_test.cc
@@ -104,24 +104,14 @@
 9:10 note: called by entry point 'f0')");
 }
 
-TEST_F(ResolverValidationTest, Error_WithEmptySource) {
-  auto* s = create<FakeStmt>();
-  WrapInFunction(s);
-
-  EXPECT_FALSE(r()->Resolve());
-
-  EXPECT_EQ(r()->error(),
-            "error: unknown statement type: tint::resolver::FakeStmt");
-}
-
-TEST_F(ResolverValidationTest, Stmt_Error_Unknown) {
-  auto* s = create<FakeStmt>(Source{Source::Location{2, 30}});
-  WrapInFunction(s);
-
-  EXPECT_FALSE(r()->Resolve());
-
-  EXPECT_EQ(r()->error(),
-            "2:30 error: unknown statement type: tint::resolver::FakeStmt");
+TEST_F(ResolverValidationTest, UnhandledStmt) {
+  EXPECT_FATAL_FAILURE(
+      {
+        ProgramBuilder b;
+        b.WrapInFunction(b.create<FakeStmt>());
+        Program(std::move(b));
+      },
+      "internal compiler error: unhandled node type: tint::resolver::FakeStmt");
 }
 
 TEST_F(ResolverValidationTest, Stmt_If_NonBool) {