tint->dawn: Shuffle source tree in preperation of merging repos

docs/    -> docs/tint/
fuzzers/ -> src/tint/fuzzers/
samples/ -> src/tint/cmd/
src/     -> src/tint/
test/    -> test/tint/

BUG=tint:1418,tint:1433

Change-Id: Id2aa79f989aef3245b80ef4aa37a27ff16cd700b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/80482
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
new file mode 100644
index 0000000..8d58018
--- /dev/null
+++ b/src/tint/resolver/dependency_graph.cc
@@ -0,0 +1,736 @@
+// Copyright 2021 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/resolver/dependency_graph.h"
+
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "src/tint/ast/continue_statement.h"
+#include "src/tint/ast/discard_statement.h"
+#include "src/tint/ast/fallthrough_statement.h"
+#include "src/tint/ast/traverse_expressions.h"
+#include "src/tint/scope_stack.h"
+#include "src/tint/sem/builtin.h"
+#include "src/tint/utils/defer.h"
+#include "src/tint/utils/map.h"
+#include "src/tint/utils/scoped_assignment.h"
+#include "src/tint/utils/unique_vector.h"
+
+#define TINT_DUMP_DEPENDENCY_GRAPH 0
+
+namespace tint {
+namespace resolver {
+namespace {
+
+// Forward declaration
+struct Global;
+
+/// Dependency describes how one global depends on another global
+struct DependencyInfo {
+  /// The source of the symbol that forms the dependency
+  Source source;
+  /// A string describing how the dependency is referenced. e.g. 'calls'
+  const char* action = nullptr;
+};
+
+/// DependencyEdge describes the two Globals used to define a dependency
+/// relationship.
+struct DependencyEdge {
+  /// The Global that depends on #to
+  const Global* from;
+  /// The Global that is depended on by #from
+  const Global* to;
+};
+
+/// DependencyEdgeCmp implements the contracts of std::equal_to<DependencyEdge>
+/// and std::hash<DependencyEdge>.
+struct DependencyEdgeCmp {
+  /// Equality operator
+  bool operator()(const DependencyEdge& lhs, const DependencyEdge& rhs) const {
+    return lhs.from == rhs.from && lhs.to == rhs.to;
+  }
+  /// Hashing operator
+  inline std::size_t operator()(const DependencyEdge& d) const {
+    return utils::Hash(d.from, d.to);
+  }
+};
+
+/// A map of DependencyEdge to DependencyInfo
+using DependencyEdges = std::unordered_map<DependencyEdge,
+                                           DependencyInfo,
+                                           DependencyEdgeCmp,
+                                           DependencyEdgeCmp>;
+
+/// Global describes a module-scope variable, type or function.
+struct Global {
+  explicit Global(const ast::Node* n) : node(n) {}
+
+  /// The declaration ast::Node
+  const ast::Node* node;
+  /// A list of dependencies that this global depends on
+  std::vector<Global*> deps;
+};
+
+/// A map of global name to Global
+using GlobalMap = std::unordered_map<Symbol, Global*>;
+
+/// 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.
+void AddError(diag::List& diagnostics,
+              const std::string& msg,
+              const Source& source) {
+  diagnostics.add_error(diag::System::Resolver, msg, source);
+}
+
+/// Raises a note diagnostic with the given message and source.
+void AddNote(diag::List& diagnostics,
+             const std::string& msg,
+             const Source& source) {
+  diagnostics.add_note(diag::System::Resolver, msg, source);
+}
+
+/// DependencyScanner is used to traverse a module to build the list of
+/// global-to-global dependencies.
+class DependencyScanner {
+ public:
+  /// Constructor
+  /// @param syms the program symbol table
+  /// @param globals_by_name map of global symbol to Global pointer
+  /// @param diagnostics diagnostic messages, appended with any errors found
+  /// @param graph the dependency graph to populate with resolved symbols
+  /// @param edges the map of globals-to-global dependency edges, which will
+  /// be populated by calls to Scan()
+  DependencyScanner(const SymbolTable& syms,
+                    const GlobalMap& globals_by_name,
+                    diag::List& diagnostics,
+                    DependencyGraph& graph,
+                    DependencyEdges& edges)
+      : symbols_(syms),
+        globals_(globals_by_name),
+        diagnostics_(diagnostics),
+        graph_(graph),
+        dependency_edges_(edges) {
+    // Register all the globals at global-scope
+    for (auto it : globals_by_name) {
+      scope_stack_.Set(it.first, it.second->node);
+    }
+  }
+
+  /// Walks the global declarations, resolving symbols, and determining the
+  /// dependencies of each global.
+  void Scan(Global* global) {
+    TINT_SCOPED_ASSIGNMENT(current_global_, global);
+    Switch(
+        global->node,
+        [&](const ast::Struct* str) {
+          Declare(str->name, str);
+          for (auto* member : str->members) {
+            TraverseType(member->type);
+          }
+        },
+        [&](const ast::Alias* alias) {
+          Declare(alias->name, alias);
+          TraverseType(alias->type);
+        },
+        [&](const ast::Function* func) {
+          Declare(func->symbol, func);
+          TraverseAttributes(func->attributes);
+          TraverseFunction(func);
+        },
+        [&](const ast::Variable* var) {
+          Declare(var->symbol, var);
+          TraverseType(var->type);
+          if (var->constructor) {
+            TraverseExpression(var->constructor);
+          }
+        },
+        [&](Default) { UnhandledNode(diagnostics_, global->node); });
+  }
+
+ private:
+  /// Traverses the function, performing symbol resolution and determining
+  /// global dependencies.
+  void TraverseFunction(const ast::Function* func) {
+    // Perform symbol resolution on all the parameter types before registering
+    // the parameters themselves. This allows the case of declaring a parameter
+    // with the same identifier as its type.
+    for (auto* param : func->params) {
+      TraverseType(param->type);
+    }
+    // Resolve the return type
+    TraverseType(func->return_type);
+
+    // Push the scope stack for the parameters and function body.
+    scope_stack_.Push();
+    TINT_DEFER(scope_stack_.Pop());
+
+    for (auto* param : func->params) {
+      if (auto* shadows = scope_stack_.Get(param->symbol)) {
+        graph_.shadows.emplace(param, shadows);
+      }
+      Declare(param->symbol, param);
+    }
+    if (func->body) {
+      TraverseStatements(func->body->statements);
+    }
+  }
+
+  /// 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, performing symbol resolution and determining
+  /// global dependencies.
+  void TraverseStatement(const ast::Statement* stmt) {
+    if (!stmt) {
+      return;
+    }
+    Switch(
+        stmt,  //
+        [&](const ast::AssignmentStatement* a) {
+          TraverseExpression(a->lhs);
+          TraverseExpression(a->rhs);
+        },
+        [&](const ast::BlockStatement* b) {
+          scope_stack_.Push();
+          TINT_DEFER(scope_stack_.Pop());
+          TraverseStatements(b->statements);
+        },
+        [&](const ast::CallStatement* r) {  //
+          TraverseExpression(r->expr);
+        },
+        [&](const ast::ForLoopStatement* l) {
+          scope_stack_.Push();
+          TINT_DEFER(scope_stack_.Pop());
+          TraverseStatement(l->initializer);
+          TraverseExpression(l->condition);
+          TraverseStatement(l->continuing);
+          TraverseStatement(l->body);
+        },
+        [&](const ast::LoopStatement* l) {
+          scope_stack_.Push();
+          TINT_DEFER(scope_stack_.Pop());
+          TraverseStatements(l->body->statements);
+          TraverseStatement(l->continuing);
+        },
+        [&](const ast::IfStatement* i) {
+          TraverseExpression(i->condition);
+          TraverseStatement(i->body);
+          for (auto* e : i->else_statements) {
+            TraverseExpression(e->condition);
+            TraverseStatement(e->body);
+          }
+        },
+        [&](const ast::ReturnStatement* r) {  //
+          TraverseExpression(r->value);
+        },
+        [&](const ast::SwitchStatement* s) {
+          TraverseExpression(s->condition);
+          for (auto* c : s->body) {
+            for (auto* sel : c->selectors) {
+              TraverseExpression(sel);
+            }
+            TraverseStatement(c->body);
+          }
+        },
+        [&](const ast::VariableDeclStatement* v) {
+          if (auto* shadows = scope_stack_.Get(v->variable->symbol)) {
+            graph_.shadows.emplace(v->variable, shadows);
+          }
+          TraverseType(v->variable->type);
+          TraverseExpression(v->variable->constructor);
+          Declare(v->variable->symbol, v->variable);
+        },
+        [&](Default) {
+          if (!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
+                             ast::DiscardStatement,
+                             ast::FallthroughStatement>()) {
+            UnhandledNode(diagnostics_, stmt);
+          }
+        });
+  }
+
+  /// Adds the symbol definition to the current scope, raising an error if two
+  /// symbols collide within the same scope.
+  void Declare(Symbol symbol, const ast::Node* node) {
+    auto* old = scope_stack_.Set(symbol, node);
+    if (old != nullptr && node != old) {
+      auto name = symbols_.NameFor(symbol);
+      AddError(diagnostics_, "redeclaration of '" + name + "'", node->source);
+      AddNote(diagnostics_, "'" + name + "' previously declared here",
+              old->source);
+    }
+  }
+
+  /// Traverses the expression, performing symbol resolution and determining
+  /// global dependencies.
+  void TraverseExpression(const ast::Expression* root) {
+    if (!root) {
+      return;
+    }
+    ast::TraverseExpressions(
+        root, diagnostics_, [&](const ast::Expression* expr) {
+          Switch(
+              expr,
+              [&](const ast::IdentifierExpression* ident) {
+                AddDependency(ident, ident->symbol, "identifier", "references");
+              },
+              [&](const ast::CallExpression* call) {
+                if (call->target.name) {
+                  AddDependency(call->target.name, call->target.name->symbol,
+                                "function", "calls");
+                }
+                if (call->target.type) {
+                  TraverseType(call->target.type);
+                }
+              },
+              [&](const ast::BitcastExpression* cast) {
+                TraverseType(cast->type);
+              });
+          return ast::TraverseAction::Descend;
+        });
+  }
+
+  /// Traverses the type node, performing symbol resolution and determining
+  /// global dependencies.
+  void TraverseType(const ast::Type* ty) {
+    if (!ty) {
+      return;
+    }
+    Switch(
+        ty,  //
+        [&](const ast::Array* arr) {
+          TraverseType(arr->type);  //
+          TraverseExpression(arr->count);
+        },
+        [&](const ast::Atomic* atomic) {  //
+          TraverseType(atomic->type);
+        },
+        [&](const ast::Matrix* mat) {  //
+          TraverseType(mat->type);
+        },
+        [&](const ast::Pointer* ptr) {  //
+          TraverseType(ptr->type);
+        },
+        [&](const ast::TypeName* tn) {  //
+          AddDependency(tn, tn->name, "type", "references");
+        },
+        [&](const ast::Vector* vec) {  //
+          TraverseType(vec->type);
+        },
+        [&](const ast::SampledTexture* tex) {  //
+          TraverseType(tex->type);
+        },
+        [&](const ast::MultisampledTexture* tex) {  //
+          TraverseType(tex->type);
+        },
+        [&](Default) {
+          if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F32,
+                           ast::DepthTexture, ast::DepthMultisampledTexture,
+                           ast::StorageTexture, ast::ExternalTexture,
+                           ast::Sampler>()) {
+            UnhandledNode(diagnostics_, ty);
+          }
+        });
+  }
+
+  /// Traverses the attribute list, performing symbol resolution and
+  /// determining global dependencies.
+  void TraverseAttributes(const ast::AttributeList& attrs) {
+    for (auto* attr : attrs) {
+      TraverseAttribute(attr);
+    }
+  }
+
+  /// Traverses the attribute, performing symbol resolution and determining
+  /// global dependencies.
+  void TraverseAttribute(const ast::Attribute* attr) {
+    if (auto* wg = attr->As<ast::WorkgroupAttribute>()) {
+      TraverseExpression(wg->x);
+      TraverseExpression(wg->y);
+      TraverseExpression(wg->z);
+      return;
+    }
+    if (attr->IsAnyOf<
+            ast::BindingAttribute, ast::BuiltinAttribute, ast::GroupAttribute,
+            ast::IdAttribute, ast::InternalAttribute, ast::InterpolateAttribute,
+            ast::InvariantAttribute, ast::LocationAttribute,
+            ast::StageAttribute, ast::StrideAttribute,
+            ast::StructBlockAttribute, ast::StructMemberAlignAttribute,
+            ast::StructMemberOffsetAttribute,
+            ast::StructMemberSizeAttribute>()) {
+      return;
+    }
+
+    UnhandledNode(diagnostics_, attr);
+  }
+
+  /// Adds the dependency from `from` to `to`, erroring if `to` cannot be
+  /// resolved.
+  void AddDependency(const ast::Node* from,
+                     Symbol to,
+                     const char* use,
+                     const char* action) {
+    auto* resolved = scope_stack_.Get(to);
+    if (!resolved) {
+      if (!IsBuiltin(to)) {
+        UnknownSymbol(to, from->source, use);
+        return;
+      }
+    }
+
+    if (auto* global = utils::Lookup(globals_, to);
+        global && global->node == resolved) {
+      if (dependency_edges_
+              .emplace(DependencyEdge{current_global_, global},
+                       DependencyInfo{from->source, action})
+              .second) {
+        current_global_->deps.emplace_back(global);
+      }
+    }
+
+    graph_.resolved_symbols.emplace(from, resolved);
+  }
+
+  /// @returns true if `name` is the name of a builtin function
+  bool IsBuiltin(Symbol name) const {
+    return sem::ParseBuiltinType(symbols_.NameFor(name)) !=
+           sem::BuiltinType::kNone;
+  }
+
+  /// Appends an error to the diagnostics that the given symbol cannot be
+  /// resolved.
+  void UnknownSymbol(Symbol name, Source source, const char* use) {
+    AddError(
+        diagnostics_,
+        "unknown " + std::string(use) + ": '" + symbols_.NameFor(name) + "'",
+        source);
+  }
+
+  using VariableMap = std::unordered_map<Symbol, const ast::Variable*>;
+  const SymbolTable& symbols_;
+  const GlobalMap& globals_;
+  diag::List& diagnostics_;
+  DependencyGraph& graph_;
+  DependencyEdges& dependency_edges_;
+
+  ScopeStack<const ast::Node*> scope_stack_;
+  Global* current_global_ = nullptr;
+};
+
+/// The global dependency analysis system
+struct DependencyAnalysis {
+ public:
+  /// Constructor
+  DependencyAnalysis(const SymbolTable& symbols,
+                     diag::List& diagnostics,
+                     DependencyGraph& graph)
+      : symbols_(symbols), diagnostics_(diagnostics), graph_(graph) {}
+
+  /// Performs global dependency analysis on the module, emitting any errors to
+  /// #diagnostics.
+  /// @returns true if analysis found no errors, otherwise false.
+  bool Run(const ast::Module& module) {
+    // Collect all the named globals from the AST module
+    GatherGlobals(module);
+
+    // Traverse the named globals to build the dependency graph
+    DetermineDependencies();
+
+    // Sort the globals into dependency order
+    SortGlobals();
+
+    // Dump the dependency graph if TINT_DUMP_DEPENDENCY_GRAPH is non-zero
+    DumpDependencyGraph();
+
+    graph_.ordered_globals = std::move(sorted_);
+
+    return !diagnostics_.contains_errors();
+  }
+
+ private:
+  /// @param node the ast::Node of the global declaration
+  /// @returns the symbol of the global declaration node
+  /// @note will raise an ICE if the node is not a type, function or variable
+  /// declaration
+  Symbol SymbolOf(const ast::Node* node) const {
+    return Switch(
+        node,  //
+        [&](const ast::TypeDecl* td) { return td->name; },
+        [&](const ast::Function* func) { return func->symbol; },
+        [&](const ast::Variable* var) { return var->symbol; },
+        [&](Default) {
+          UnhandledNode(diagnostics_, node);
+          return Symbol{};
+        });
+  }
+
+  /// @param node the ast::Node of the global declaration
+  /// @returns the name of the global declaration node
+  /// @note will raise an ICE if the node is not a type, function or variable
+  /// declaration
+  std::string NameOf(const ast::Node* node) const {
+    return symbols_.NameFor(SymbolOf(node));
+  }
+
+  /// @param node the ast::Node of the global declaration
+  /// @returns a string representation of the global declaration kind
+  /// @note will raise an ICE if the node is not a type, function or variable
+  /// declaration
+  std::string KindOf(const ast::Node* node) {
+    return Switch(
+        node,  //
+        [&](const ast::Struct*) { return "struct"; },
+        [&](const ast::Alias*) { return "alias"; },
+        [&](const ast::Function*) { return "function"; },
+        [&](const ast::Variable* var) { return var->is_const ? "let" : "var"; },
+        [&](Default) {
+          UnhandledNode(diagnostics_, node);
+          return "<error>";
+        });
+  }
+
+  /// Traverses `module`, collecting all the global declarations and populating
+  /// the #globals and #declaration_order fields.
+  void GatherGlobals(const ast::Module& module) {
+    for (auto* node : module.GlobalDeclarations()) {
+      auto* global = allocator_.Create(node);
+      globals_.emplace(SymbolOf(node), global);
+      declaration_order_.emplace_back(global);
+    }
+  }
+
+  /// Walks the global declarations, determining the dependencies of each global
+  /// and adding these to each global's Global::deps field.
+  void DetermineDependencies() {
+    DependencyScanner scanner(symbols_, globals_, diagnostics_, graph_,
+                              dependency_edges_);
+    for (auto* global : declaration_order_) {
+      scanner.Scan(global);
+    }
+  }
+
+  /// Performs a depth-first traversal of `root`'s dependencies, calling `enter`
+  /// as the function decends into each dependency and `exit` when bubbling back
+  /// up towards the root.
+  /// @param enter is a function with the signature: `bool(Global*)`. The
+  /// `enter` function returns true if TraverseDependencies() should traverse
+  /// the dependency, otherwise it will be skipped.
+  /// @param exit is a function with the signature: `void(Global*)`. The `exit`
+  /// function is only called if the corresponding `enter` call returned true.
+  template <typename ENTER, typename EXIT>
+  void TraverseDependencies(const Global* root, ENTER&& enter, EXIT&& exit) {
+    // Entry is a single entry in the traversal stack. Entry points to a
+    // dep_idx'th dependency of Entry::global.
+    struct Entry {
+      const Global* global;  // The parent global
+      size_t dep_idx;        // The dependency index in `global->deps`
+    };
+
+    if (!enter(root)) {
+      return;
+    }
+
+    std::vector<Entry> stack{Entry{root, 0}};
+    while (true) {
+      auto& entry = stack.back();
+      // Have we exhausted the dependencies of entry.global?
+      if (entry.dep_idx < entry.global->deps.size()) {
+        // No, there's more dependencies to traverse.
+        auto& dep = entry.global->deps[entry.dep_idx];
+        // Does the caller want to enter this dependency?
+        if (enter(dep)) {                  // Yes.
+          stack.push_back(Entry{dep, 0});  // Enter the dependency.
+        } else {
+          entry.dep_idx++;  // No. Skip this node.
+        }
+      } else {
+        // Yes. Time to back up.
+        // Exit this global, pop the stack, and if there's another parent node,
+        // increment its dependency index, and loop again.
+        exit(entry.global);
+        stack.pop_back();
+        if (stack.empty()) {
+          return;  // All done.
+        }
+        stack.back().dep_idx++;
+      }
+    }
+  }
+
+  /// SortGlobals sorts the globals into dependency order, erroring if cyclic
+  /// dependencies are found. The sorted dependencies are assigned to #sorted.
+  void SortGlobals() {
+    if (diagnostics_.contains_errors()) {
+      return;  // This code assumes there are no undeclared identifiers.
+    }
+
+    std::unordered_set<const Global*> visited;
+    for (auto* global : declaration_order_) {
+      utils::UniqueVector<const Global*> stack;
+      TraverseDependencies(
+          global,
+          [&](const Global* g) {  // Enter
+            if (!stack.add(g)) {
+              CyclicDependencyFound(g, stack);
+              return false;
+            }
+            if (sorted_.contains(g->node)) {
+              // Visited this global already.
+              // stack was pushed, but exit() will not be called when we return
+              // false, so pop here.
+              stack.pop_back();
+              return false;
+            }
+            return true;
+          },
+          [&](const Global* g) {  // Exit. Only called if Enter returned true.
+            sorted_.add(g->node);
+            stack.pop_back();
+          });
+
+      sorted_.add(global->node);
+
+      if (!stack.empty()) {
+        // Each stack.push() must have a corresponding stack.pop_back().
+        TINT_ICE(Resolver, diagnostics_)
+            << "stack not empty after returning from TraverseDependencies()";
+      }
+    }
+  }
+
+  /// DepInfoFor() looks up the global dependency information for the dependency
+  /// of global `from` depending on `to`.
+  /// @note will raise an ICE if the edge is not found.
+  DependencyInfo DepInfoFor(const Global* from, const Global* to) const {
+    auto it = dependency_edges_.find(DependencyEdge{from, to});
+    if (it != dependency_edges_.end()) {
+      return it->second;
+    }
+    TINT_ICE(Resolver, diagnostics_)
+        << "failed to find dependency info for edge: '" << NameOf(from->node)
+        << "' -> '" << NameOf(to->node) << "'";
+    return {};
+  }
+
+  /// CyclicDependencyFound() emits an error diagnostic for a cyclic dependency.
+  /// @param root is the global that starts the cyclic dependency, which must be
+  /// found in `stack`.
+  /// @param stack is the global dependency stack that contains a loop.
+  void CyclicDependencyFound(const Global* root,
+                             const std::vector<const Global*>& stack) {
+    std::stringstream msg;
+    msg << "cyclic dependency found: ";
+    constexpr size_t kLoopNotStarted = ~0u;
+    size_t loop_start = kLoopNotStarted;
+    for (size_t i = 0; i < stack.size(); i++) {
+      auto* e = stack[i];
+      if (loop_start == kLoopNotStarted && e == root) {
+        loop_start = i;
+      }
+      if (loop_start != kLoopNotStarted) {
+        msg << "'" << NameOf(e->node) << "' -> ";
+      }
+    }
+    msg << "'" << NameOf(root->node) << "'";
+    AddError(diagnostics_, msg.str(), root->node->source);
+    for (size_t i = loop_start; i < stack.size(); i++) {
+      auto* from = stack[i];
+      auto* to = (i + 1 < stack.size()) ? stack[i + 1] : stack[loop_start];
+      auto info = DepInfoFor(from, to);
+      AddNote(diagnostics_,
+              KindOf(from->node) + " '" + NameOf(from->node) + "' " +
+                  info.action + " " + KindOf(to->node) + " '" +
+                  NameOf(to->node) + "' here",
+              info.source);
+    }
+  }
+
+  void DumpDependencyGraph() {
+#if TINT_DUMP_DEPENDENCY_GRAPH == 0
+    if ((true)) {
+      return;
+    }
+#endif  // TINT_DUMP_DEPENDENCY_GRAPH
+    printf("=========================\n");
+    printf("------ declaration ------ \n");
+    for (auto* global : declaration_order_) {
+      printf("%s\n", NameOf(global->node).c_str());
+    }
+    printf("------ dependencies ------ \n");
+    for (auto* node : sorted_) {
+      auto symbol = SymbolOf(node);
+      auto* global = globals_.at(symbol);
+      printf("%s depends on:\n", symbols_.NameFor(symbol).c_str());
+      for (auto* dep : global->deps) {
+        printf("  %s\n", NameOf(dep->node).c_str());
+      }
+    }
+    printf("=========================\n");
+  }
+
+  /// Program symbols
+  const SymbolTable& symbols_;
+
+  /// Program diagnostics
+  diag::List& diagnostics_;
+
+  /// The resulting dependency graph
+  DependencyGraph& graph_;
+
+  /// Allocator of Globals
+  BlockAllocator<Global> allocator_;
+
+  /// Global map, keyed by name. Populated by GatherGlobals().
+  GlobalMap globals_;
+
+  /// Map of DependencyEdge to DependencyInfo. Populated by
+  /// DetermineDependencies().
+  DependencyEdges dependency_edges_;
+
+  /// Globals in declaration order. Populated by GatherGlobals().
+  std::vector<Global*> declaration_order_;
+
+  /// Globals in sorted dependency order. Populated by SortGlobals().
+  utils::UniqueVector<const ast::Node*> sorted_;
+};
+
+}  // namespace
+
+DependencyGraph::DependencyGraph() = default;
+DependencyGraph::DependencyGraph(DependencyGraph&&) = default;
+DependencyGraph::~DependencyGraph() = default;
+
+bool DependencyGraph::Build(const ast::Module& module,
+                            const SymbolTable& symbols,
+                            diag::List& diagnostics,
+                            DependencyGraph& output) {
+  DependencyAnalysis da{symbols, diagnostics, output};
+  return da.Run(module);
+}
+
+}  // namespace resolver
+}  // namespace tint