From b93ba6ead5ecdf1ba35e005bdd30432fa2e61006 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 23 Nov 2021 18:40:27 +0000 Subject: [PATCH] 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 Reviewed-by: David Neto Reviewed-by: James Price Commit-Queue: Ben Clayton --- src/resolver/dependency_graph.cc | 128 ++++++++++++--- src/resolver/dependency_graph_test.cc | 216 ++++++++++++++++++++++++-- src/resolver/validation_test.cc | 26 +--- 3 files changed, 318 insertions(+), 52 deletions(-) diff --git a/src/resolver/dependency_graph.cc b/src/resolver/dependency_graph.cc index 3acdfbc312..2d92343d7e 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 @@ struct Global { /// A map of global name to Global using GlobalMap = std::unordered_map; -/// 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 @@ class DependencyScanner { if (auto* str = global->node->As()) { Declare(str->name, str); for (auto* member : str->members) { - ResolveTypeDependency(member->type); + TraverseType(member->type); } return; } if (auto* alias = global->node->As()) { Declare(alias->name, alias); - ResolveTypeDependency(alias->type); + TraverseType(alias->type); return; } if (auto* func = global->node->As()) { Declare(func->symbol, func); + TraverseDecorations(func->decorations); TraverseFunction(func); return; } if (auto* var = global->node->As()) { 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 @@ class DependencyScanner { } if (auto* v = stmt->As()) { Declare(v->variable->symbol, v->variable); - ResolveTypeDependency(v->variable->type); + TraverseType(v->variable->type); TraverseExpression(v->variable->constructor); return; } @@ -262,9 +266,7 @@ class DependencyScanner { 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 @@ class DependencyScanner { } } - /// 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 @@ class DependencyScanner { 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()) { + TraverseType(arr->type); + TraverseExpression(arr->count); + return; + } + if (auto* atomic = ty->As()) { + TraverseType(atomic->type); + return; + } + if (auto* mat = ty->As()) { + TraverseType(mat->type); + return; + } + if (auto* ptr = ty->As()) { + TraverseType(ptr->type); + return; + } if (auto* tn = ty->As()) { ResolveGlobalDependency(tn, tn->name, "type", "references"); + return; } + if (auto* vec = ty->As()) { + TraverseType(vec->type); + return; + } + if (auto* tex = ty->As()) { + TraverseType(tex->type); + return; + } + if (auto* tex = ty->As()) { + TraverseType(tex->type); + return; + } + if (ty->IsAnyOf()) { + 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()) { + TraverseExpression(wg->x); + TraverseExpression(wg->y); + TraverseExpression(wg->z); + return; + } + if (deco->IsAnyOf()) { + return; + } + + UnhandledNode(diagnostics_, deco); } /// Adds the dependency to the currently processed global @@ -426,7 +506,7 @@ struct DependencyAnalysis { if (auto* var = node->As()) { return var->symbol; } - UnhandledDecl(diagnostics_, node); + UnhandledNode(diagnostics_, node); return {}; } @@ -455,7 +535,7 @@ struct DependencyAnalysis { if (auto* var = node->As()) { 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 a884cfa5ef..86c122b9c0 100644 --- a/src/resolver/dependency_graph_test.cc +++ b/src/resolver/dependency_graph_test.cc @@ -101,14 +101,28 @@ static constexpr SymbolDeclKind kFuncDeclKinds[] = { /// 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 @@ enum class SymbolUseKind { 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 @@ static constexpr SymbolUseKind kValueUseKinds[] = { SymbolUseKind::GlobalVarValue, SymbolUseKind::GlobalLetValue, SymbolUseKind::LocalVarValue, SymbolUseKind::LocalLetValue, SymbolUseKind::NestedLocalVarValue, SymbolUseKind::NestedLocalLetValue, + SymbolUseKind::WorkgroupSizeValue, }; static constexpr SymbolUseKind kFuncUseKinds[] = { @@ -172,10 +206,30 @@ std::ostream& operator<<(std::ostream& out, SymbolUseKind kind) { 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 @@ std::ostream& operator<<(std::ostream& out, SymbolUseKind kind) { 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 @@ std::ostream& operator<<(std::ostream& out, SymbolUseKind kind) { 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 << ""; } @@ -208,21 +272,36 @@ std::ostream& operator<<(std::ostream& out, SymbolUseKind kind) { 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 @@ int ScopeDepth(SymbolUseKind kind) { 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 @@ struct SymbolTestHelper { std::vector statements; /// Nested function local var / let declaration statements std::vector nested_statements; + /// Function decorations + ast::DecorationList func_decos; /// Constructor /// @param builder the program builder @@ -374,6 +470,38 @@ const ast::Node* SymbolTestHelper::Add(SymbolUseKind kind, 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 @@ const ast::Node* SymbolTestHelper::Add(SymbolUseKind kind, 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 @@ const ast::Node* SymbolTestHelper::Add(SymbolUseKind kind, 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 @@ const ast::Node* SymbolTestHelper::Add(SymbolUseKind kind, 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 @@ void SymbolTestHelper::Build() { 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 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) { 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 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) { 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 280ef99c81..790fde5631 100644 --- a/src/resolver/validation_test.cc +++ b/src/resolver/validation_test.cc @@ -104,24 +104,14 @@ TEST_F(ResolverValidationTest, WorkgroupMemoryUsedInFragmentStage) { 9:10 note: called by entry point 'f0')"); } -TEST_F(ResolverValidationTest, Error_WithEmptySource) { - auto* s = create(); - 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(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()); + Program(std::move(b)); + }, + "internal compiler error: unhandled node type: tint::resolver::FakeStmt"); } TEST_F(ResolverValidationTest, Stmt_If_NonBool) {