[tint][ir][ToProgram] Handle shadowing.
Change the ir::Module naming methods to allow for duplicate names.
Change the ir::Disassembler to ensure that IDs are uniquely named.
If the disassembler has an ID that does not match the value's
name, then a new comment is appended to the end of the instruction
with the value's ID and name.
Add a new RenameConflictsWGSL transform that renames declarations
which prevent identifiers from resolving to the correct
declaration, and those with identical identifiers declared in the
same scope.
This fixes all the known issues with shadowing which the
roundtrip fuzzer uncovered.
Change-Id: Ib3c3df8093863a9917c201ca6ee8c958a4ada6dc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/141321
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 568a557..ce07b7a 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -521,6 +521,8 @@
"ir/transform/block_decorated_structs.h",
"ir/transform/demote_to_helper.cc",
"ir/transform/demote_to_helper.h",
+ "ir/transform/rename_conflicts_wgsl.cc",
+ "ir/transform/rename_conflicts_wgsl.h",
"ir/transform/shader_io.cc",
"ir/transform/shader_io.h",
]
@@ -1919,6 +1921,10 @@
"ir/transform/var_for_dynamic_index_test.cc",
]
}
+
+ if (tint_build_wgsl_writer) {
+ sources += [ "ir/transform/rename_conflicts_test.cc" ]
+ }
}
}
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 94abddc..668dc13 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -860,6 +860,8 @@
ir/transform/block_decorated_structs.h
ir/transform/demote_to_helper.cc
ir/transform/demote_to_helper.h
+ ir/transform/rename_conflicts_wgsl.cc
+ ir/transform/rename_conflicts_wgsl.h
ir/transform/shader_io.cc
ir/transform/shader_io.h
ir/transform/transform.cc
@@ -1365,6 +1367,12 @@
lang/spirv/writer/var_test.cc
lang/spirv/writer/writer_test.cc
)
+
+ if (${TINT_BUILD_WGSL_WRITER})
+ list(APPEND TINT_TEST_SRCS
+ ir/transform/rename_conflicts_wgsl_test.cc
+ )
+ endif()
endif()
endif()
diff --git a/src/tint/ir/disassembler.cc b/src/tint/ir/disassembler.cc
index 6a1d20c..c1dbdb5 100644
--- a/src/tint/ir/disassembler.cc
+++ b/src/tint/ir/disassembler.cc
@@ -93,17 +93,26 @@
return block_ids_.GetOrCreate(node, [&] { return block_ids_.Count(); });
}
-std::string_view Disassembler::IdOf(Value* value) {
+std::string Disassembler::IdOf(Value* value) {
TINT_ASSERT(IR, value);
return value_ids_.GetOrCreate(value, [&] {
if (auto sym = mod_.NameOf(value)) {
- return sym.Name();
+ if (ids_.Add(sym.Name())) {
+ return sym.Name();
+ }
+ auto prefix = sym.Name() + "_";
+ for (size_t i = 1;; i++) {
+ auto name = prefix + std::to_string(i);
+ if (ids_.Add(name)) {
+ return name;
+ }
+ }
}
return std::to_string(value_ids_.Count());
});
}
-std::string_view Disassembler::NameOf(If* inst) {
+std::string Disassembler::NameOf(If* inst) {
if (!inst) {
return "undef";
}
@@ -111,7 +120,7 @@
return if_names_.GetOrCreate(inst, [&] { return "if_" + std::to_string(if_names_.Count()); });
}
-std::string_view Disassembler::NameOf(Loop* inst) {
+std::string Disassembler::NameOf(Loop* inst) {
if (!inst) {
return "undef";
}
@@ -120,7 +129,7 @@
[&] { return "loop_" + std::to_string(loop_names_.Count()); });
}
-std::string_view Disassembler::NameOf(Switch* inst) {
+std::string Disassembler::NameOf(Switch* inst) {
if (!inst) {
return "undef";
}
@@ -272,7 +281,8 @@
void Disassembler::EmitFunction(Function* func) {
in_function_ = true;
- Indent() << "%" << IdOf(func) << " =";
+ std::string fn_id = IdOf(func);
+ Indent() << "%" << fn_id << " =";
if (func->Stage() != Function::PipelineStage::kUndefined) {
out_ << " @" << func->Stage();
@@ -297,6 +307,27 @@
EmitReturnAttributes(func);
out_ << " -> %b" << IdOf(func->Block()) << " {";
+
+ { // Add a comment if the function IDs or parameter IDs doesn't match their name
+ utils::Vector<std::string, 4> names;
+ if (auto name = mod_.NameOf(func); name.IsValid()) {
+ if (name.NameView() != fn_id) {
+ names.Push("%" + std::string(fn_id) + ": '" + name.Name() + "'");
+ }
+ }
+ for (auto* p : func->Params()) {
+ if (auto name = mod_.NameOf(p); name.IsValid()) {
+ auto id = IdOf(p);
+ if (name.NameView() != id) {
+ names.Push("%" + std::string(id) + ": '" + name.Name() + "'");
+ }
+ }
+ }
+ if (!names.IsEmpty()) {
+ out_ << " # " << utils::Join(names, ", ");
+ }
+ }
+
EmitLine();
{
@@ -392,11 +423,12 @@
}
void Disassembler::EmitInstruction(Instruction* inst) {
+ TINT_DEFER(EmitLine());
+
if (!inst->Alive()) {
SourceMarker sm(this);
out_ << "<destroyed " << inst->TypeInfo().name << " " << utils::ToString(inst) << ">";
sm.Store(inst);
- EmitLine();
return;
}
tint::Switch(
@@ -412,27 +444,23 @@
EmitInstructionName("bitcast", b);
out_ << " ";
EmitOperandList(b);
- EmitLine();
},
- [&](Discard* d) {
- EmitInstructionName("discard", d);
- EmitLine();
- },
+ [&](Discard* d) { EmitInstructionName("discard", d); },
[&](CoreBuiltinCall* b) {
EmitValueWithType(b);
out_ << " = ";
EmitInstructionName(builtin::str(b->Func()), b);
out_ << " ";
EmitOperandList(b);
- EmitLine();
},
[&](Construct* c) {
EmitValueWithType(c);
out_ << " = ";
EmitInstructionName("construct", c);
- out_ << " ";
- EmitOperandList(c);
- EmitLine();
+ if (!c->Operands().IsEmpty()) {
+ out_ << " ";
+ EmitOperandList(c);
+ }
},
[&](Convert* c) {
EmitValueWithType(c);
@@ -440,7 +468,6 @@
EmitInstructionName("convert", c);
out_ << " ";
EmitOperandList(c);
- EmitLine();
},
[&](IntrinsicCall* i) {
EmitValueWithType(i);
@@ -448,7 +475,6 @@
EmitInstructionName(utils::ToString(i->Kind()), i);
out_ << " ";
EmitOperandList(i);
- EmitLine();
},
[&](Load* l) {
EmitValueWithType(l);
@@ -456,7 +482,6 @@
EmitInstructionName("load", l);
out_ << " ";
EmitValue(l->From());
- EmitLine();
},
[&](Store* s) {
EmitInstructionName("store", s);
@@ -464,7 +489,6 @@
EmitValue(s->To());
out_ << ", ";
EmitValue(s->From());
- EmitLine();
},
[&](LoadVectorElement* l) {
EmitValueWithType(l);
@@ -472,13 +496,11 @@
EmitInstructionName("load_vector_element", l);
out_ << " ";
EmitOperandList(l);
- EmitLine();
},
[&](StoreVectorElement* s) {
EmitInstructionName("store_vector_element", s);
out_ << " ";
EmitOperandList(s);
- EmitLine();
},
[&](UserCall* uc) {
EmitValueWithType(uc);
@@ -489,7 +511,6 @@
out_ << ", ";
}
EmitOperandList(uc, UserCall::kArgsOperandOffset);
- EmitLine();
},
[&](Var* v) {
EmitValueWithType(v);
@@ -503,7 +524,6 @@
out_ << " ";
EmitBindingPoint(v->BindingPoint().value());
}
- EmitLine();
},
[&](Let* l) {
EmitValueWithType(l);
@@ -511,7 +531,6 @@
EmitInstructionName("let", l);
out_ << " ";
EmitOperandList(l);
- EmitLine();
},
[&](Access* a) {
EmitValueWithType(a);
@@ -519,7 +538,6 @@
EmitInstructionName("access", a);
out_ << " ";
EmitOperandList(a);
- EmitLine();
},
[&](Swizzle* s) {
EmitValueWithType(s);
@@ -544,10 +562,26 @@
break;
}
}
- EmitLine();
},
[&](Terminator* b) { EmitTerminator(b); },
[&](Default) { out_ << "Unknown instruction: " << inst->TypeInfo().name; });
+
+ { // Add a comment if the result IDs don't match their names
+ utils::Vector<std::string, 4> names;
+ for (auto* result : inst->Results()) {
+ if (result) {
+ if (auto name = mod_.NameOf(result); name.IsValid()) {
+ auto id = IdOf(result);
+ if (name.NameView() != id) {
+ names.Push("%" + std::string(id) + ": '" + name.Name() + "'");
+ }
+ }
+ }
+ }
+ if (!names.IsEmpty()) {
+ out_ << " # " << utils::Join(names, ", ");
+ }
+ }
}
void Disassembler::EmitOperand(Instruction* inst, size_t index) {
@@ -615,7 +649,6 @@
Indent();
out_ << "}";
- EmitLine();
}
void Disassembler::EmitLoop(Loop* l) {
@@ -665,7 +698,6 @@
Indent();
out_ << "}";
- EmitLine();
}
void Disassembler::EmitSwitch(Switch* s) {
@@ -716,7 +748,6 @@
Indent();
out_ << "}";
- EmitLine();
}
void Disassembler::EmitTerminator(Terminator* b) {
@@ -770,8 +801,6 @@
[&](ir::ExitSwitch* e) { out_ << " # " << NameOf(e->Switch()); }, //
[&](ir::ExitLoop* e) { out_ << " # " << NameOf(e->Loop()); } //
);
-
- EmitLine();
}
void Disassembler::EmitValueList(utils::Slice<Value* const> values) {
@@ -841,7 +870,6 @@
EmitOperandList(b);
sm.Store(b);
- EmitLine();
}
void Disassembler::EmitUnary(Unary* u) {
@@ -860,7 +888,6 @@
EmitOperandList(u);
sm.Store(u);
- EmitLine();
}
void Disassembler::EmitStructDecl(const type::Struct* str) {
diff --git a/src/tint/ir/disassembler.h b/src/tint/ir/disassembler.h
index bcb2e38..e07106a 100644
--- a/src/tint/ir/disassembler.h
+++ b/src/tint/ir/disassembler.h
@@ -118,10 +118,10 @@
utils::StringStream& Indent();
size_t IdOf(Block* blk);
- std::string_view IdOf(Value* node);
- std::string_view NameOf(If* inst);
- std::string_view NameOf(Loop* inst);
- std::string_view NameOf(Switch* inst);
+ std::string IdOf(Value* node);
+ std::string NameOf(If* inst);
+ std::string NameOf(Loop* inst);
+ std::string NameOf(Switch* inst);
void EmitBlock(Block* blk, std::string_view comment = "");
void EmitFunction(Function* func);
@@ -150,6 +150,7 @@
utils::StringStream out_;
utils::Hashmap<Block*, size_t, 32> block_ids_;
utils::Hashmap<Value*, std::string, 32> value_ids_;
+ utils::Hashset<std::string, 32> ids_;
uint32_t indent_size_ = 0;
bool in_function_ = false;
diff --git a/src/tint/ir/from_program_shadowing_test.cc b/src/tint/ir/from_program_shadowing_test.cc
index 56b3c83..4437d90 100644
--- a/src/tint/ir/from_program_shadowing_test.cc
+++ b/src/tint/ir/from_program_shadowing_test.cc
@@ -114,7 +114,7 @@
store %i, %4
%5:i32 = load %i
%6:i32 = add %5, 1i
- %i_1:ptr<function, i32, read_write> = var, %6
+ %i_1:ptr<function, i32, read_write> = var, %6 # %i_1: 'i'
%8:i32 = load %i_1
ret %8
}
@@ -146,7 +146,7 @@
%4:i32 = add %3, 1i
store %i, %4
%5:i32 = load %i
- %i_1:i32 = add %5, 1i
+ %i_1:i32 = add %5, 1i # %i_1: 'i'
ret %i_1
}
}
@@ -179,7 +179,7 @@
store %i, %4
%5:i32 = load %i
%6:i32 = add %5, 1i
- %i_1:ptr<function, i32, read_write> = var, %6
+ %i_1:ptr<function, i32, read_write> = var, %6 # %i_1: 'i'
%8:i32 = load %i_1
%9:i32 = add %8, 1i
store %i_1, %9
@@ -218,7 +218,7 @@
%4:i32 = add %3, 1i
store %i, %4
%5:i32 = load %i
- %i_1:i32 = add %5, 1i
+ %i_1:i32 = add %5, 1i # %i_1: 'i'
ret %i_1
}
}
@@ -261,7 +261,7 @@
}
%5:i32 = load %i
%6:i32 = add %5, 1i
- %i_1:ptr<function, i32, read_write> = var, %6
+ %i_1:ptr<function, i32, read_write> = var, %6 # %i_1: 'i'
%8:i32 = load %i_1
ret %8
}
@@ -307,7 +307,7 @@
}
}
%5:i32 = load %i
- %i_1:i32 = add %5, 1i
+ %i_1:i32 = add %5, 1i # %i_1: 'i'
ret %i_1
}
%b3 = block { # continuing
@@ -340,7 +340,7 @@
%i:ptr<function, i32, read_write> = var
loop [i: %b2, b: %b3] { # loop_1
%b2 = block { # initializer
- %i_1:ptr<function, f32, read_write> = var, 0.0f
+ %i_1:ptr<function, f32, read_write> = var, 0.0f # %i_1: 'i'
next_iteration %b3
}
%b3 = block { # body
@@ -384,7 +384,7 @@
%i:ptr<function, i32, read_write> = var
loop [i: %b2, b: %b3] { # loop_1
%b2 = block { # initializer
- %i_1:f32 = let 0.0f
+ %i_1:f32 = let 0.0f # %i_1: 'i'
next_iteration %b3
}
%b3 = block { # body
@@ -444,7 +444,7 @@
}
%6:i32 = load %i
%7:i32 = add %6, 1i
- %i_1:ptr<function, i32, read_write> = var, %7
+ %i_1:ptr<function, i32, read_write> = var, %7 # %i_1: 'i'
%9:i32 = load %i_1
ret %9
}
@@ -491,7 +491,7 @@
}
}
%6:i32 = load %i
- %i_1:i32 = add %6, 1i
+ %i_1:i32 = add %6, 1i # %i_1: 'i'
ret %i_1
}
}
@@ -536,7 +536,7 @@
}
%5:i32 = load %i
%6:i32 = add %5, 1i
- %i_1:ptr<function, i32, read_write> = var, %6
+ %i_1:ptr<function, i32, read_write> = var, %6 # %i_1: 'i'
%8:i32 = load %i_1
%9:bool = eq %8, 3i
if %9 [t: %b5] { # if_2
@@ -590,7 +590,7 @@
}
}
%5:i32 = load %i
- %i_1:i32 = add %5, 1i
+ %i_1:i32 = add %5, 1i # %i_1: 'i'
%7:bool = eq %i_1, 3i
if %7 [t: %b5] { # if_2
%b5 = block { # true
@@ -648,7 +648,7 @@
%b3 = block { # continuing
%5:i32 = load %i
%6:i32 = add %5, 1i
- %i_1:ptr<function, i32, read_write> = var, %6
+ %i_1:ptr<function, i32, read_write> = var, %6 # %i_1: 'i'
%8:i32 = load %i_1
%9:bool = gt %8, 2i
break_if %9 %b2
@@ -698,7 +698,7 @@
}
%b3 = block { # continuing
%5:i32 = load %i
- %i_1:i32 = add %5, 1i
+ %i_1:i32 = add %5, 1i # %i_1: 'i'
%7:bool = gt %i_1, 2i
break_if %7 %b2
}
@@ -744,7 +744,7 @@
%b3 = block { # case
%5:i32 = load %i
%6:i32 = add %5, 1i
- %i_1:ptr<function, i32, read_write> = var, %6
+ %i_1:ptr<function, i32, read_write> = var, %6 # %i_1: 'i'
%8:i32 = load %i_1
ret %8
}
@@ -792,7 +792,7 @@
}
%b3 = block { # case
%5:i32 = load %i
- %i_1:i32 = add %5, 1i
+ %i_1:i32 = add %5, 1i # %i_1: 'i'
ret %i_1
}
%b4 = block { # case
diff --git a/src/tint/ir/module.cc b/src/tint/ir/module.cc
index 6d5b90d..1c8811c 100644
--- a/src/tint/ir/module.cc
+++ b/src/tint/ir/module.cc
@@ -32,48 +32,22 @@
}
Symbol Module::NameOf(Value* value) {
- return value_to_id_.Get(value).value_or(Symbol{});
+ return value_to_name_.Get(value).value_or(Symbol{});
}
-Symbol Module::SetName(Instruction* inst, std::string_view name) {
+void Module::SetName(Instruction* inst, std::string_view name) {
TINT_ASSERT(IR, inst->HasResults() && !inst->HasMultiResults());
return SetName(inst->Result(), name);
}
-Symbol Module::SetName(Value* value, std::string_view name) {
+void Module::SetName(Value* value, std::string_view name) {
TINT_ASSERT(IR, !name.empty());
-
- if (auto old = value_to_id_.Get(value)) {
- value_to_id_.Remove(value);
- id_to_value_.Remove(old.value());
- }
-
- auto sym = symbols.Register(name);
- if (id_to_value_.Add(sym, value)) {
- value_to_id_.Add(value, sym);
- return sym;
- }
- auto prefix = std::string(name) + "_";
- for (uint64_t suffix = 1; suffix != std::numeric_limits<uint64_t>::max(); suffix++) {
- sym = symbols.Register(prefix + std::to_string(suffix));
- if (id_to_value_.Add(sym, value)) {
- value_to_id_.Add(value, sym);
- return sym;
- }
- }
- TINT_ASSERT(IR, false); // !
- return Symbol{};
+ value_to_name_.Replace(value, symbols.Register(name));
}
void Module::SetName(Value* value, Symbol name) {
TINT_ASSERT(IR, name.IsValid());
-
- if (auto old = value_to_id_.Get(value)) {
- value_to_id_.Remove(value);
- id_to_value_.Remove(old.value());
- }
-
- value_to_id_.Add(value, name);
+ value_to_name_.Replace(value, name);
}
} // namespace tint::ir
diff --git a/src/tint/ir/module.h b/src/tint/ir/module.h
index 88660c6..3506c35 100644
--- a/src/tint/ir/module.h
+++ b/src/tint/ir/module.h
@@ -38,11 +38,8 @@
/// Program Id required to create other components
ProgramID prog_id_;
- /// Map of value to pre-declared identifier
- utils::Hashmap<Value*, Symbol, 32> value_to_id_;
-
- /// Map of pre-declared identifier to value
- utils::Hashmap<Symbol, Value*, 32> id_to_value_;
+ /// Map of value to name
+ utils::Hashmap<Value*, Symbol, 32> value_to_name_;
public:
/// Constructor
@@ -69,14 +66,12 @@
/// @param inst the instruction to set the name of
/// @param name the desired name of the value. May be suffixed on collision.
- /// @return the unique symbol of the given value
/// @note requires the instruction be a single result instruction.
- Symbol SetName(Instruction* inst, std::string_view name);
+ void SetName(Instruction* inst, std::string_view name);
/// @param value the value to name.
/// @param name the desired name of the value. May be suffixed on collision.
- /// @return the unique symbol of the given value.
- Symbol SetName(Value* value, std::string_view name);
+ void SetName(Value* value, std::string_view name);
/// @param value the value to name
/// @param name the desired name of the value
diff --git a/src/tint/ir/module_test.cc b/src/tint/ir/module_test.cc
index 4d24881..81ddf4b 100644
--- a/src/tint/ir/module_test.cc
+++ b/src/tint/ir/module_test.cc
@@ -31,28 +31,16 @@
TEST_F(IR_ModuleTest, SetName) {
auto* v = b.Var(ty.ptr<function, i32>());
- EXPECT_EQ(mod.SetName(v, "a").Name(), "a");
+ mod.SetName(v, "a");
EXPECT_EQ(mod.NameOf(v).Name(), "a");
}
TEST_F(IR_ModuleTest, SetNameRename) {
auto* v = b.Var(ty.ptr<function, i32>());
- EXPECT_EQ(mod.SetName(v, "a").Name(), "a");
- EXPECT_EQ(mod.SetName(v, "b").Name(), "b");
+ mod.SetName(v, "a");
+ mod.SetName(v, "b");
EXPECT_EQ(mod.NameOf(v).Name(), "b");
}
-TEST_F(IR_ModuleTest, SetNameCollision) {
- auto* v1 = b.Var(ty.ptr<function, i32>());
- auto* v2 = b.Var(ty.ptr<function, i32>());
- auto* v3 = b.Var(ty.ptr<function, i32>());
- EXPECT_EQ(mod.SetName(v1, "x").Name(), "x");
- EXPECT_EQ(mod.SetName(v2, "x_1").Name(), "x_1");
- EXPECT_EQ(mod.SetName(v3, "x").Name(), "x_2");
- EXPECT_EQ(mod.NameOf(v1).Name(), "x");
- EXPECT_EQ(mod.NameOf(v2).Name(), "x_1");
- EXPECT_EQ(mod.NameOf(v3).Name(), "x_2");
-}
-
} // namespace
} // namespace tint::ir
diff --git a/src/tint/ir/to_program.cc b/src/tint/ir/to_program.cc
index 884b1d0..9330c2b 100644
--- a/src/tint/ir/to_program.cc
+++ b/src/tint/ir/to_program.cc
@@ -47,6 +47,7 @@
#include "src/tint/ir/store_vector_element.h"
#include "src/tint/ir/switch.h"
#include "src/tint/ir/swizzle.h"
+#include "src/tint/ir/transform/rename_conflicts_wgsl.h"
#include "src/tint/ir/unary.h"
#include "src/tint/ir/unreachable.h"
#include "src/tint/ir/user_call.h"
@@ -98,6 +99,9 @@
return Program{std::move(b)};
}
+ transform::Transform::DataMap data;
+ transform::RenameConflictsWGSL{}.Run(&mod, data, data);
+
if (mod.root_block) {
RootBlock(mod.root_block);
}
@@ -960,7 +964,7 @@
// TODO(crbug.com/tint/1902): Emit structure attributes
utils::Vector<const ast::Attribute*, 2> attrs;
- auto name = b.Symbols().New(s->Name().NameView());
+ auto name = b.Symbols().Register(s->Name().NameView());
b.Structure(name, std::move(members), std::move(attrs));
return name;
});
@@ -987,10 +991,10 @@
Symbol NameFor(Value* value, std::string_view suggested = {}) {
return names_.GetOrCreate(value, [&] {
if (!suggested.empty()) {
- return b.Symbols().New(suggested);
+ return b.Symbols().Register(suggested);
}
if (auto sym = mod.NameOf(value)) {
- return b.Symbols().New(sym.NameView());
+ return b.Symbols().Register(sym.NameView());
}
return b.Symbols().New("v");
});
diff --git a/src/tint/ir/to_program_roundtrip_test.cc b/src/tint/ir/to_program_roundtrip_test.cc
index b499f7c..a9890f3 100644
--- a/src/tint/ir/to_program_roundtrip_test.cc
+++ b/src/tint/ir/to_program_roundtrip_test.cc
@@ -2812,16 +2812,20 @@
////////////////////////////////////////////////////////////////////////////////
// Shadowing tests
////////////////////////////////////////////////////////////////////////////////
-TEST_F(IRToProgramRoundtripTest, DISABLED_Shadow_f32_With_Fn) {
+TEST_F(IRToProgramRoundtripTest, Shadow_f32_With_Fn) {
Test(R"(
fn f32() {
var v = mat4x4f();
}
)",
- R"( NEEDS FIXING )");
+ R"(
+fn f32_1() {
+ var v : mat4x4<f32> = mat4x4<f32>();
+}
+)");
}
-TEST_F(IRToProgramRoundtripTest, DISABLED_Shadow_f32_With_Struct) {
+TEST_F(IRToProgramRoundtripTest, Shadow_f32_With_Struct) {
Test(R"(
struct f32 {
v : i32,
@@ -2831,15 +2835,37 @@
let f = vec2f(1.0f);
}
)",
- R"( NEEDS FIXING )");
+ R"(
+struct f32_1 {
+ v : i32,
}
-TEST_F(IRToProgramRoundtripTest, DISABLED_Shadow_f32_With_ModVar) {
+fn f(s : f32_1) {
+ let f_1 = vec2<f32>(1.0f);
+}
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Shadow_f32_With_ModVar) {
+ Test(R"(
+var<private> f32 : vec2f = vec2f(0.0f, 1.0f);
+)",
+ R"(
+var<private> f32_1 : vec2<f32> = vec2<f32>(0.0f, 1.0f);
+)");
+}
+
+TEST_F(IRToProgramRoundtripTest, Shadow_f32_With_ModVar2) {
Test(R"(
var<private> f32 : i32 = 1i;
+
var<private> v = vec2(1.0).x;
)",
- R"( NEEDS FIXING )");
+ R"(
+var<private> f32_1 : i32 = 1i;
+
+var<private> v : f32 = 1.0f;
+)");
}
TEST_F(IRToProgramRoundtripTest, Shadow_f32_With_Alias) {
@@ -2867,16 +2893,6 @@
var S : S = S();
return S.i;
}
-)",
- R"(
-struct S {
- i : i32,
-}
-
-fn f() -> i32 {
- var S_1 : S = S();
- return S_1.i;
-}
)");
}
@@ -2889,15 +2905,6 @@
fn f(S : S) -> i32 {
return S.i;
}
-)",
- R"(
-struct S {
- i : i32,
-}
-
-fn f(S_1 : S) -> i32 {
- return S_1.i;
-}
)");
}
@@ -2910,15 +2917,6 @@
var i : i32 = (i + 1i);
return i;
}
-)",
- R"(
-var<private> i : i32 = 1i;
-
-fn f() -> i32 {
- i = (i + 1i);
- var i_1 : i32 = (i + 1i);
- return i_1;
-}
)");
}
@@ -2931,15 +2929,6 @@
let i = (i + 1i);
return i;
}
-)",
- R"(
-var<private> i : i32 = 1i;
-
-fn f() -> i32 {
- i = (i + 1i);
- let i_1 = (i + 1i);
- return i_1;
-}
)");
}
@@ -2954,17 +2943,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- if (true) {
- i = (i + 1i);
- var i_1 : i32 = (i + 1i);
- i_1 = (i_1 + 1i);
- }
- return i;
-}
)");
}
@@ -2979,17 +2957,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- if (true) {
- i = (i + 1i);
- let i_1 = (i + 1i);
- return i_1;
- }
- return i;
-}
)");
}
@@ -3003,16 +2970,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- while((i < 4i)) {
- var i_1 : i32 = (i + 1i);
- return i_1;
- }
- return i;
-}
)");
}
@@ -3026,16 +2983,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- while((i < 4i)) {
- let i_1 = (i + 1i);
- return i_1;
- }
- return i;
-}
)");
}
@@ -3048,15 +2995,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- for(var i_1 : f32 = 0.0f; (i_1 < 4.0f); ) {
- let j = i_1;
- }
- return i;
-}
)");
}
@@ -3069,15 +3007,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- for(let i_1 = 0.0f; (i_1 < 4.0f); ) {
- let j = i_1;
- }
- return i;
-}
)");
}
@@ -3091,16 +3020,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- for(var x : i32 = 0i; (i < 4i); ) {
- var i_1 : i32 = (i + 1i);
- return i_1;
- }
- return i;
-}
)");
}
@@ -3114,16 +3033,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- for(var x : i32 = 0i; (i < 4i); ) {
- let i_1 = (i + 1i);
- return i_1;
- }
- return i;
-}
)");
}
@@ -3142,21 +3051,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- loop {
- if ((i == 2i)) {
- break;
- }
- var i_1 : i32 = (i + 1i);
- if ((i_1 == 3i)) {
- break;
- }
- }
- return i;
-}
)");
}
@@ -3175,21 +3069,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- loop {
- if ((i == 2i)) {
- break;
- }
- let i_1 = (i + 1i);
- if ((i_1 == 3i)) {
- break;
- }
- }
- return i;
-}
)");
}
@@ -3209,22 +3088,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- loop {
- if ((i == 2i)) {
- break;
- }
-
- continuing {
- var i_1 : i32 = (i + 1i);
- break if (i_1 > 2i);
- }
- }
- return i;
-}
)");
}
@@ -3244,22 +3107,6 @@
}
return i;
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- loop {
- if ((i == 2i)) {
- break;
- }
-
- continuing {
- let i_1 = (i + 1i);
- break if (i_1 > 2i);
- }
- }
- return i;
-}
)");
}
@@ -3280,23 +3127,6 @@
}
}
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- switch(i) {
- case 0i: {
- return i;
- }
- case 1i: {
- var i_1 : i32 = (i + 1i);
- return i_1;
- }
- default: {
- return i;
- }
- }
-}
)");
}
@@ -3317,23 +3147,6 @@
}
}
}
-)",
- R"(
-fn f() -> i32 {
- var i : i32;
- switch(i) {
- case 0i: {
- return i;
- }
- case 1i: {
- let i_1 = (i + 1i);
- return i_1;
- }
- default: {
- return i;
- }
- }
-}
)");
}
diff --git a/src/tint/ir/transform/rename_conflicts_wgsl.cc b/src/tint/ir/transform/rename_conflicts_wgsl.cc
new file mode 100644
index 0000000..f70eca5
--- /dev/null
+++ b/src/tint/ir/transform/rename_conflicts_wgsl.cc
@@ -0,0 +1,284 @@
+// Copyright 2023 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 <variant>
+
+#include "src/tint/ir/construct.h"
+#include "src/tint/ir/control_instruction.h"
+#include "src/tint/ir/core_builtin_call.h"
+#include "src/tint/ir/function.h"
+#include "src/tint/ir/instruction.h"
+#include "src/tint/ir/loop.h"
+#include "src/tint/ir/module.h"
+#include "src/tint/ir/multi_in_block.h"
+#include "src/tint/ir/transform/rename_conflicts_wgsl.h"
+#include "src/tint/ir/var.h"
+#include "src/tint/lang/core/type/matrix.h"
+#include "src/tint/lang/core/type/scalar.h"
+#include "src/tint/lang/core/type/struct.h"
+#include "src/tint/lang/core/type/vector.h"
+#include "src/tint/utils/containers/hashset.h"
+#include "src/tint/utils/containers/reverse.h"
+#include "src/tint/utils/containers/scope_stack.h"
+#include "src/tint/utils/macros/defer.h"
+#include "src/tint/utils/rtti/switch.h"
+#include "src/tint/utils/text/string.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ir::transform::RenameConflictsWGSL);
+
+namespace tint::ir::transform {
+
+/// PIMPL state for the transform, for a single function.
+struct RenameConflictsWGSL::State {
+ /// Constructor
+ /// @param i the IR module
+ explicit State(Module* i) : ir(i) {}
+
+ /// Processes the module, renaming all declarations that would prevent an identifier resolving
+ /// to the correct declaration.
+ void Process() {
+ scopes.Push(Scope{});
+ TINT_DEFER(scopes.Clear());
+
+ RegisterModuleScopeDecls();
+
+ // Process the module-scope variable declarations
+ if (ir->root_block) {
+ for (auto* inst : *ir->root_block) {
+ Process(inst);
+ }
+ }
+
+ // Process the functions
+ for (auto* fn : ir->functions) {
+ scopes.Push(Scope{});
+ TINT_DEFER(scopes.Pop());
+ for (auto* param : fn->Params()) {
+ EnsureResolvable(param->Type());
+ if (auto symbol = ir->NameOf(param); symbol.IsValid()) {
+ Declare(scopes.Back(), param, symbol.NameView());
+ }
+ }
+ Process(fn->Block());
+ }
+ }
+
+ private:
+ /// Map of identifier to declaration.
+ /// The declarations may be one of an ir::Value or type::Struct.
+ using Scope = utils::Hashmap<std::string_view, CastableBase*, 8>;
+
+ /// The IR module.
+ Module* ir = nullptr;
+
+ /// Stack of scopes
+ utils::Vector<Scope, 8> scopes{};
+
+ /// Registers all the WGSL module-scope declarations in the root-scope.
+ /// Duplicate declarations with the same name will renamed.
+ void RegisterModuleScopeDecls() {
+ // Declare all the user types
+ for (auto* ty : ir->Types()) {
+ if (auto* str = ty->As<type::Struct>()) {
+ auto name = str->Name().NameView();
+ if (!IsBuiltinStruct(str)) {
+ Declare(scopes.Front(), const_cast<type::Struct*>(str), name);
+ }
+ }
+ }
+
+ // Declare all the module-scope vars
+ if (ir->root_block) {
+ for (auto* inst : *ir->root_block) {
+ for (auto* result : inst->Results()) {
+ if (auto symbol = ir->NameOf(result)) {
+ Declare(scopes.Front(), result, symbol.NameView());
+ }
+ }
+ }
+ }
+
+ // Declare all the functions
+ for (auto* fn : ir->functions) {
+ if (auto symbol = ir->NameOf(fn); symbol.IsValid()) {
+ Declare(scopes.Back(), fn, symbol.NameView());
+ }
+ }
+ }
+
+ /// Processes the instructions of the block
+ void Process(Block* block) {
+ for (auto* inst : *block) {
+ Process(inst);
+ }
+ }
+
+ /// Processes an instruction, ensuring that all identifier references resolve to the correct
+ /// declaration. This may involve renaming of declarations in the outer scopes.
+ void Process(Instruction* inst) {
+ // Check resolving of operands
+ for (auto* operand : inst->Operands()) {
+ if (operand) {
+ // Ensure that named operands can be resolved.
+ if (auto symbol = ir->NameOf(operand)) {
+ EnsureResolvesTo(symbol.NameView(), operand);
+ }
+ // If the operand is a constant, then ensure that type name can be resolved.
+ if (auto* c = operand->As<Constant>()) {
+ EnsureResolvable(c->Type());
+ }
+ }
+ }
+
+ Switch(
+ inst, //
+ [&](Loop* loop) {
+ // Initializer's scope encompasses the body and continuing
+ scopes.Push(Scope{});
+ TINT_DEFER(scopes.Pop());
+ Process(loop->Initializer());
+ {
+ // Body's scope encompasses the continuing
+ scopes.Push(Scope{});
+ TINT_DEFER(scopes.Pop());
+ Process(loop->Body());
+ {
+ scopes.Push(Scope{});
+ TINT_DEFER(scopes.Pop());
+ Process(loop->Continuing());
+ }
+ }
+ },
+ [&](ControlInstruction* ctrl) {
+ // Traverse into the control instruction's blocks
+ ctrl->ForeachBlock([&](Block* block) {
+ scopes.Push(Scope{});
+ TINT_DEFER(scopes.Pop());
+ Process(block);
+ });
+ },
+ [&](Var*) {
+ // Ensure the var's type is resolvable
+ EnsureResolvable(inst->Result()->Type());
+ },
+ [&](Construct*) {
+ // Ensure the type of a type constructor is resolvable
+ EnsureResolvable(inst->Result()->Type());
+ },
+ [&](CoreBuiltinCall* call) {
+ // Ensure builtin of a builtin call is resolvable
+ auto name = utils::ToString(call->Func());
+ EnsureResolvesTo(name, nullptr);
+ });
+
+ // Register new operands and check their types can resolve
+ for (auto* result : inst->Results()) {
+ if (auto symbol = ir->NameOf(result); symbol.IsValid()) {
+ Declare(scopes.Back(), result, symbol.NameView());
+ }
+ }
+ }
+
+ /// Ensures that the type @p type can be resolved given its identifier(s)
+ void EnsureResolvable(const type::Type* type) {
+ while (type) {
+ type = tint::Switch(
+ type, //
+ [&](const type::Scalar* s) {
+ EnsureResolvesTo(s->FriendlyName(), nullptr);
+ return nullptr;
+ },
+ [&](const type::Vector* v) {
+ EnsureResolvesTo("vec" + utils::ToString(v->Width()), nullptr);
+ return v->type();
+ },
+ [&](const type::Matrix* m) {
+ EnsureResolvesTo(
+ "mat" + utils::ToString(m->columns()) + "x" + utils::ToString(m->rows()),
+ nullptr);
+ return m->type();
+ },
+ [&](const type::Pointer* p) {
+ EnsureResolvesTo(utils::ToString(p->Access()), nullptr);
+ EnsureResolvesTo(utils::ToString(p->AddressSpace()), nullptr);
+ return p->StoreType();
+ },
+ [&](const type::Struct* s) {
+ auto name = s->Name().NameView();
+ if (IsBuiltinStruct(s)) {
+ EnsureResolvesTo(name, nullptr);
+ } else {
+ EnsureResolvesTo(name, s);
+ }
+ return nullptr;
+ });
+ }
+ }
+
+ /// Ensures that the identifier @p identifier resolves to the declaration @p thing
+ void EnsureResolvesTo(std::string_view identifier, const CastableBase* thing) {
+ for (auto& scope : utils::Reverse(scopes)) {
+ if (auto decl = scope.Get(identifier)) {
+ if (decl.value() == thing) {
+ return; // Resolved to the right thing.
+ }
+
+ // Operand is shadowed
+ scope.Remove(identifier);
+ Rename(decl.value(), identifier);
+ }
+ }
+ }
+
+ /// Registers the declaration @p thing in the scope @p scope with the name @p name
+ /// If there is an existing declaration with the given name in @p scope then @p thing will be
+ /// renamed.
+ void Declare(Scope& scope, CastableBase* thing, std::string_view name) {
+ auto add = scope.Add(name, thing);
+ if (!add && *add.value != thing) {
+ // Multiple declarations with the same name in the same scope.
+ // Rename the later declaration.
+ Rename(thing, name);
+ }
+ }
+
+ /// Rename changes the name of @p thing with the old name of @p old_name
+ void Rename(CastableBase* thing, std::string_view old_name) {
+ Symbol new_name = ir->symbols.New(old_name);
+ Switch(
+ thing, //
+ [&](ir::Value* value) { ir->SetName(value, new_name); },
+ [&](type::Struct* str) { str->SetName(new_name); },
+ [&](Default) {
+ diag::List diags;
+ TINT_ICE(Transform, diags)
+ << "unhandled type for renaming: " << thing->TypeInfo().name;
+ });
+ }
+
+ /// @return true if @p s is a builtin (non-user declared) structure.
+ bool IsBuiltinStruct(const type::Struct* s) {
+ // TODO(bclayton): Need to do better than this.
+ return utils::HasPrefix(s->Name().NameView(), "_");
+ }
+};
+
+RenameConflictsWGSL::RenameConflictsWGSL() = default;
+RenameConflictsWGSL::~RenameConflictsWGSL() = default;
+
+void RenameConflictsWGSL::Run(Module* ir, const DataMap&, DataMap&) const {
+ State{ir}.Process();
+}
+
+} // namespace tint::ir::transform
diff --git a/src/tint/ir/transform/rename_conflicts_wgsl.h b/src/tint/ir/transform/rename_conflicts_wgsl.h
new file mode 100644
index 0000000..acc3369
--- /dev/null
+++ b/src/tint/ir/transform/rename_conflicts_wgsl.h
@@ -0,0 +1,49 @@
+// Copyright 2023 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.
+
+#ifndef SRC_TINT_IR_TRANSFORM_RENAME_CONFLICTS_WGSL_H_
+#define SRC_TINT_IR_TRANSFORM_RENAME_CONFLICTS_WGSL_H_
+
+#include "src/tint/ir/transform/transform.h"
+
+// Forward declarations
+namespace tint::ir {
+class BuiltinCall;
+} // namespace tint::ir
+namespace tint::type {
+class Type;
+} // namespace tint::type
+
+namespace tint::ir::transform {
+
+/// RenameConflictsWGSL is a transform that renames declarations which prevent identifiers from
+/// resolving to the correct declaration, and those with identical identifiers declared in the same
+/// scope.
+class RenameConflictsWGSL final : public utils::Castable<RenameConflictsWGSL, Transform> {
+ public:
+ /// Constructor
+ RenameConflictsWGSL();
+ /// Destructor
+ ~RenameConflictsWGSL() override;
+
+ /// @copydoc Transform::Run
+ void Run(ir::Module* module, const DataMap& inputs, DataMap& outputs) const override;
+
+ private:
+ struct State;
+};
+
+} // namespace tint::ir::transform
+
+#endif // SRC_TINT_IR_TRANSFORM_RENAME_CONFLICTS_WGSL_H_
diff --git a/src/tint/ir/transform/rename_conflicts_wgsl_test.cc b/src/tint/ir/transform/rename_conflicts_wgsl_test.cc
new file mode 100644
index 0000000..f8e0ffa
--- /dev/null
+++ b/src/tint/ir/transform/rename_conflicts_wgsl_test.cc
@@ -0,0 +1,1053 @@
+// Copyright 2023 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/ir/transform/rename_conflicts_wgsl.h"
+
+#include <utility>
+
+#include "src/tint/ir/transform/test_helper.h"
+#include "src/tint/lang/core/type/matrix.h"
+
+namespace tint::ir::transform {
+namespace {
+
+using namespace tint::builtin::fluent_types; // NOLINT
+using namespace tint::number_suffixes; // NOLINT
+
+using IR_RenameConflictsWGSLTest = TransformTest;
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_SingleNamedRootBlockVar) {
+ b.With(b.RootBlock(), [&] { b.ir.SetName(b.Var(ty.ptr<private_, i32>()), "v"); });
+
+ auto* src = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_TwoRootBlockVarsWithSameName) {
+ b.With(b.RootBlock(), [&] {
+ b.ir.SetName(b.Var(ty.ptr<private_, i32>()), "v");
+ b.ir.SetName(b.Var(ty.ptr<private_, u32>()), "v");
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+ %v_1:ptr<private, u32, read_write> = var # %v_1: 'v'
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+ %v_1:ptr<private, u32, read_write> = var
+}
+
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_RootBlockVarAndStructWithSameName) {
+ auto* s = ty.Struct(b.ir.symbols.New("v"), {{b.ir.symbols.New("x"), ty.i32()}});
+ b.With(b.RootBlock(), [&] { b.ir.SetName(b.Var(ty.ptr(function, s)), "v"); });
+
+ auto* src = R"(
+v = struct @align(4) {
+ x:i32 @offset(0)
+}
+
+%b1 = block { # root
+ %v:ptr<function, v, read_write> = var
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+v = struct @align(4) {
+ x:i32 @offset(0)
+}
+
+%b1 = block { # root
+ %v_1:ptr<function, v, read_write> = var
+}
+
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_RootBlockVarAndFnWithSameName) {
+ b.With(b.RootBlock(), [&] { b.ir.SetName(b.Var(ty.ptr<private_, i32>()), "v"); });
+
+ auto* fn = b.Function("v", ty.void_());
+ b.With(fn->Block(), [&] { b.Return(fn); });
+
+ auto* src = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+}
+
+%v_1 = func():void -> %b2 { # %v_1: 'v'
+ %b2 = block {
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+}
+
+%v_1 = func():void -> %b2 {
+ %b2 = block {
+ ret
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_RootBlockVar_ShadowedBy_FnVar) {
+ b.With(b.RootBlock(), [&] {
+ auto* outer = b.Var(ty.ptr<private_, i32>());
+ b.ir.SetName(outer, "v");
+
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* load_outer = b.Load(outer);
+
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+}
+
+%f = func():i32 -> %b2 {
+ %b2 = block {
+ %3:i32 = load %v
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %5:f32 = load %v_1
+ %6:i32 = add %3, %5
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_RootBlockVar_ShadowedBy_FnVar) {
+ b.With(b.RootBlock(), [&] {
+ auto* outer = b.Var(ty.ptr<private_, i32>());
+ b.ir.SetName(outer, "v");
+
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_outer = b.Load(outer);
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+}
+
+%f = func():i32 -> %b2 {
+ %b2 = block {
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %4:i32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%b1 = block { # root
+ %v:ptr<private, i32, read_write> = var
+}
+
+%f = func():i32 -> %b2 {
+ %b2 = block {
+ %v_1:ptr<function, f32, read_write> = var
+ %4:i32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_FnVar_ShadowedBy_IfVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* outer = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(outer, "v");
+
+ auto* if_ = b.If(true);
+ b.With(if_->True(), [&] {
+ auto* load_outer = b.Load(outer);
+
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+
+ b.Unreachable();
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ %v:ptr<function, f32, read_write> = var
+ if true [t: %b2] { # if_1
+ %b2 = block { # true
+ %3:f32 = load %v
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %5:f32 = load %v_1
+ %6:i32 = add %3, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_FnVar_ShadowedBy_IfVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* outer = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(outer, "v");
+
+ auto* if_ = b.If(true);
+ b.With(if_->True(), [&] {
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_outer = b.Load(outer);
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+
+ b.Unreachable();
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ %v:ptr<function, f32, read_write> = var
+ if true [t: %b2] { # if_1
+ %b2 = block { # true
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %4:f32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ %v:ptr<function, f32, read_write> = var
+ if true [t: %b2] { # if_1
+ %b2 = block { # true
+ %v_1:ptr<function, f32, read_write> = var
+ %4:f32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_LoopInitVar_ShadowedBy_LoopBodyVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* loop = b.Loop();
+ b.With(loop->Initializer(), [&] {
+ auto* outer = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(outer, "v");
+ b.NextIteration(loop);
+
+ b.With(loop->Body(), [&] {
+ auto* load_outer = b.Load(outer);
+
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+ });
+
+ b.Unreachable();
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ loop [i: %b2, b: %b3] { # loop_1
+ %b2 = block { # initializer
+ %v:ptr<function, f32, read_write> = var
+ next_iteration %b3
+ }
+ %b3 = block { # body
+ %3:f32 = load %v
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %5:f32 = load %v_1
+ %6:i32 = add %3, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_LoopInitVar_ShadowedBy_LoopBodyVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* loop = b.Loop();
+ b.With(loop->Initializer(), [&] {
+ auto* outer = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(outer, "v");
+ b.NextIteration(loop);
+
+ b.With(loop->Body(), [&] {
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_outer = b.Load(outer);
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+ });
+
+ b.Unreachable();
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ loop [i: %b2, b: %b3] { # loop_1
+ %b2 = block { # initializer
+ %v:ptr<function, f32, read_write> = var
+ next_iteration %b3
+ }
+ %b3 = block { # body
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %4:f32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ loop [i: %b2, b: %b3] { # loop_1
+ %b2 = block { # initializer
+ %v:ptr<function, f32, read_write> = var
+ next_iteration %b3
+ }
+ %b3 = block { # body
+ %v_1:ptr<function, f32, read_write> = var
+ %4:f32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_LoopBodyVar_ShadowedBy_LoopContVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* loop = b.Loop();
+ b.With(loop->Initializer(), [&] { b.NextIteration(loop); });
+ b.With(loop->Body(), [&] {
+ auto* outer = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(outer, "v");
+ b.Continue(loop);
+
+ b.With(loop->Continuing(), [&] {
+ auto* load_outer = b.Load(outer);
+
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+ });
+
+ b.Unreachable();
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ loop [i: %b2, b: %b3, c: %b4] { # loop_1
+ %b2 = block { # initializer
+ next_iteration %b3
+ }
+ %b3 = block { # body
+ %v:ptr<function, f32, read_write> = var
+ continue %b4
+ }
+ %b4 = block { # continuing
+ %3:f32 = load %v
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %5:f32 = load %v_1
+ %6:i32 = add %3, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_LoopBodyVar_ShadowedBy_LoopContVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* loop = b.Loop();
+ b.With(loop->Initializer(), [&] { b.NextIteration(loop); });
+ b.With(loop->Body(), [&] {
+ auto* outer = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(outer, "v");
+ b.Continue(loop);
+
+ b.With(loop->Continuing(), [&] {
+ auto* inner = b.Var(ty.ptr<function, f32>());
+ b.ir.SetName(inner, "v");
+
+ auto* load_outer = b.Load(outer);
+ auto* load_inner = b.Load(inner);
+ b.Return(fn, b.Add(ty.i32(), load_outer, load_inner));
+ });
+ });
+
+ b.Unreachable();
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ loop [i: %b2, b: %b3, c: %b4] { # loop_1
+ %b2 = block { # initializer
+ next_iteration %b3
+ }
+ %b3 = block { # body
+ %v:ptr<function, f32, read_write> = var
+ continue %b4
+ }
+ %b4 = block { # continuing
+ %v_1:ptr<function, f32, read_write> = var # %v_1: 'v'
+ %4:f32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ loop [i: %b2, b: %b3, c: %b4] { # loop_1
+ %b2 = block { # initializer
+ next_iteration %b3
+ }
+ %b3 = block { # body
+ %v:ptr<function, f32, read_write> = var
+ continue %b4
+ }
+ %b4 = block { # continuing
+ %v_1:ptr<function, f32, read_write> = var
+ %4:f32 = load %v
+ %5:f32 = load %v_1
+ %6:i32 = add %4, %5
+ ret %6
+ }
+ }
+ unreachable
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinScalar_ShadowedBy_Param) {
+ auto* fn = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam(ty.i32());
+ b.ir.SetName(p, "i32");
+ fn->SetParams({p});
+
+ b.With(fn->Block(), [&] {
+ b.Var(ty.ptr<function, i32>());
+ b.Return(fn);
+ });
+
+ auto* src = R"(
+%f = func(%i32:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, i32, read_write> = var
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func(%i32_1:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, i32, read_write> = var
+ ret
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_BuiltinVector_ShadowedBy_Param) {
+ auto* fn = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam(ty.i32());
+ b.ir.SetName(p, "vec2");
+ fn->SetParams({p});
+
+ b.With(fn->Block(), [&] {
+ b.Var(ty.ptr<function, vec3<i32>>());
+ b.Return(fn);
+ });
+
+ auto* src = R"(
+%f = func(%vec2:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, vec3<i32>, read_write> = var
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinVector_ShadowedBy_Param) {
+ auto* fn = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam(ty.i32());
+ b.ir.SetName(p, "vec3");
+ fn->SetParams({p});
+
+ b.With(fn->Block(), [&] {
+ b.Var(ty.ptr<function, vec3<i32>>());
+ b.Return(fn);
+ });
+
+ auto* src = R"(
+%f = func(%vec3:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, vec3<i32>, read_write> = var
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func(%vec3_1:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, vec3<i32>, read_write> = var
+ ret
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_BuiltinMatrix_ShadowedBy_Param) {
+ auto* fn = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam(ty.i32());
+ b.ir.SetName(p, "mat3x2");
+ fn->SetParams({p});
+
+ b.With(fn->Block(), [&] {
+ b.Var(ty.ptr<function, mat2x4<f32>>());
+ b.Return(fn);
+ });
+
+ auto* src = R"(
+%f = func(%mat3x2:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, mat2x4<f32>, read_write> = var
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinMatrix_ShadowedBy_Param) {
+ auto* fn = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam(ty.i32());
+ b.ir.SetName(p, "mat2x4");
+ fn->SetParams({p});
+
+ b.With(fn->Block(), [&] {
+ b.Var(ty.ptr<function, mat2x4<f32>>());
+ b.Return(fn);
+ });
+
+ auto* src = R"(
+%f = func(%mat2x4:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, mat2x4<f32>, read_write> = var
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func(%mat2x4_1:i32):void -> %b1 {
+ %b1 = block {
+ %3:ptr<function, mat2x4<f32>, read_write> = var
+ ret
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_BuiltinScalar_ShadowedBy_FnVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* v = b.Var(ty.ptr<function, i32>());
+ b.ir.SetName(v, "f32");
+
+ b.Return(fn, b.Construct(ty.i32()));
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ %f32:ptr<function, i32, read_write> = var
+ %3:i32 = construct
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinScalar_ShadowedBy_FnVar) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* v = b.Var(ty.ptr<function, i32>());
+ b.ir.SetName(v, "i32");
+
+ b.Return(fn, b.Construct(ty.i32()));
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ %i32:ptr<function, i32, read_write> = var
+ %3:i32 = construct
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ %i32_1:ptr<function, i32, read_write> = var
+ %3:i32 = construct
+ ret %3
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_BuiltinScalar_ShadowedBy_NamedInst) {
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] {
+ auto* i = b.Add(ty.i32(), 1_i, 2_i);
+ b.ir.SetName(i, "i32");
+
+ b.Return(fn, i);
+ });
+
+ auto* src = R"(
+%f = func():i32 -> %b1 {
+ %b1 = block {
+ %i32:i32 = add 1i, 2i
+ ret %i32
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinScalar_ShadowedBy_NamedInst) {
+ auto* fn = b.Function("f", ty.f32());
+ b.With(fn->Block(), [&] {
+ auto* i = b.Add(ty.i32(), 1_i, 2_i);
+ b.ir.SetName(i, "f32");
+
+ b.Return(fn, b.Construct(ty.f32(), i));
+ });
+
+ auto* src = R"(
+%f = func():f32 -> %b1 {
+ %b1 = block {
+ %f32:i32 = add 1i, 2i
+ %3:f32 = construct %f32
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%f = func():f32 -> %b1 {
+ %b1 = block {
+ %f32_1:i32 = add 1i, 2i
+ %3:f32 = construct %f32_1
+ ret %3
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_BuiltinAddressSpace_ShadowedBy_RootBlockVar) {
+ b.With(b.RootBlock(), [&] { //
+ b.ir.SetName(b.Var(ty.ptr<private_, i32>()), "function");
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %function:ptr<private, i32, read_write> = var
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinAddressSpace_ShadowedBy_RootBlockVar) {
+ b.With(b.RootBlock(), [&] { //
+ b.ir.SetName(b.Var(ty.ptr<private_, i32>()), "private");
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %private:ptr<private, i32, read_write> = var
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%b1 = block { # root
+ %private_1:ptr<private, i32, read_write> = var
+}
+
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_BuiltinAccess_ShadowedBy_RootBlockVar) {
+ b.With(b.RootBlock(), [&] { //
+ b.ir.SetName(b.Var(ty.ptr<private_, i32>()), "read");
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %read:ptr<private, i32, read_write> = var
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinAccess_ShadowedBy_RootBlockVar) {
+ b.With(b.RootBlock(), [&] { //
+ b.ir.SetName(b.Var(ty.ptr<private_, i32>()), "read_write");
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %read_write:ptr<private, i32, read_write> = var
+}
+
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%b1 = block { # root
+ %read_write_1:ptr<private, i32, read_write> = var
+}
+
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, NoModify_BuiltinFn_ShadowedBy_RootBlockVar) {
+ b.With(b.RootBlock(), [&] { //
+ auto* v = b.Var(ty.ptr<private_, i32>());
+ b.ir.SetName(v, "min");
+ });
+
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] { //
+ auto* res = b.Call(ty.i32(), builtin::Function::kMax, 1_i, 2_i)->Result();
+ b.Return(fn, res);
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %min:ptr<private, i32, read_write> = var
+}
+
+%f = func():i32 -> %b2 {
+ %b2 = block {
+ %3:i32 = max 1i, 2i
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_RenameConflictsWGSLTest, Conflict_BuiltinFn_ShadowedBy_RootBlockVar) {
+ b.With(b.RootBlock(), [&] { //
+ auto* v = b.Var(ty.ptr<private_, i32>());
+ b.ir.SetName(v, "max");
+ });
+
+ auto* fn = b.Function("f", ty.i32());
+ b.With(fn->Block(), [&] { //
+ auto* res = b.Call(ty.i32(), builtin::Function::kMax, 1_i, 2_i)->Result();
+ b.Return(fn, res);
+ });
+
+ auto* src = R"(
+%b1 = block { # root
+ %max:ptr<private, i32, read_write> = var
+}
+
+%f = func():i32 -> %b2 {
+ %b2 = block {
+ %3:i32 = max 1i, 2i
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%b1 = block { # root
+ %max_1:ptr<private, i32, read_write> = var
+}
+
+%f = func():i32 -> %b2 {
+ %b2 = block {
+ %3:i32 = max 1i, 2i
+ ret %3
+ }
+}
+)";
+
+ Run<RenameConflictsWGSL>();
+
+ EXPECT_EQ(expect, str());
+}
+
+} // namespace
+} // namespace tint::ir::transform
diff --git a/src/tint/lang/core/type/struct.h b/src/tint/lang/core/type/struct.h
index 6179b0d..92cef7a 100644
--- a/src/tint/lang/core/type/struct.h
+++ b/src/tint/lang/core/type/struct.h
@@ -80,6 +80,10 @@
/// @returns the name of the structure
Symbol Name() const { return name_; }
+ /// Renames the structure
+ /// @param name the new name for the structure
+ void SetName(Symbol name) { name_ = name; }
+
/// @returns the members of the structure
utils::VectorRef<const StructMember*> Members() const { return members_; }
@@ -171,7 +175,7 @@
Struct* Clone(CloneContext& ctx) const override;
private:
- const Symbol name_;
+ Symbol name_;
const utils::Vector<const StructMember*, 4> members_;
const uint32_t align_;
const uint32_t size_;