[tint][ir][val] Improve reporting of types in error messages

Adds in more information about types into error message, so that what
the validator thinks is a problem is clearer.

This helps from a user experience perspective by making things like
you are forgetting to load a pointer, i.e. received 'ptr<i32>' instead
of a scalar, more obvious without having to dig into the disassembly.

This also helps from a Tint dev perspective, since it indicates what
the validator thinks it is looking at vs what is in the actual
IR. Again if something like an unwrap is missing in the validator code, the
reported type makes that easier to see.

Fixes: 391129946
Change-Id: I42bae56ae554f7c7e78be7c588cf9164ae8f28a9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/223014
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 5890d03..7b7484c 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -1833,14 +1833,14 @@
             },
             [&](const core::type::Vector* v) {
                 if (!v->Type()->IsScalar()) {
-                    diag() << "vector elements must be scalars";
+                    diag() << "vector elements, " << NameOf(type) << ", must be scalars";
                     return false;
                 }
                 return true;
             },
             [&](const core::type::Matrix* m) {
                 if (!m->Type()->IsFloatScalar()) {
-                    diag() << "matrix elements must be float scalars";
+                    diag() << "matrix elements, " << NameOf(type) << ", must be float scalars";
                     return false;
                 }
                 return true;
@@ -2027,7 +2027,8 @@
                 !struct_ty || struct_ty->Members().Any([](const core::type::StructMember* m) {
                     return !IsValidFunctionParamType(m->Type());
                 })) {
-                AddError(param) << "function parameter type must be constructible, a pointer, a "
+                AddError(param) << "function parameter type, " << NameOf(param->Type())
+                                << ", must be constructible, a pointer, a "
                                    "texture, or a sampler";
             }
         }
@@ -2049,7 +2050,7 @@
                     "fragment entry point params can only be a bool if decorated with "
                     "@builtin(front_facing)"),
                 CheckFrontFacingIfBoolFunc<FunctionParam>(
-                    "fragment entry point param memebers can only be a bool if "
+                    "fragment entry point param members can only be a bool if "
                     "decorated with @builtin(front_facing)"));
         } else if (func->IsEntryPoint()) {
             CheckFunctionParamAttributesAndType(
@@ -2123,7 +2124,8 @@
 
     if (func->Stage() == Function::PipelineStage::kCompute) {
         if (DAWN_UNLIKELY(func->ReturnType() && !func->ReturnType()->Is<core::type::Void>())) {
-            AddError(func) << "compute entry point must not have a return type";
+            AddError(func) << "compute entry point must not have a return type, found "
+                           << NameOf(func->ReturnType());
         }
     }
 
@@ -2135,8 +2137,8 @@
         }
 
         CheckFunctionReturnAttributesAndType(
-            func, CheckFrontFacingIfBoolFunc<Function>("entry point returns can not be bool"),
-            CheckFrontFacingIfBoolFunc<Function>("entry point return members can not be bool"));
+            func, CheckFrontFacingIfBoolFunc<Function>("entry point returns can not be 'bool'"),
+            CheckFrontFacingIfBoolFunc<Function>("entry point return members can not be 'bool'"));
 
         for (auto var : referenced_module_vars_.TransitiveReferences(func)) {
             const auto* mv = var->Result(0)->Type()->As<core::type::MemoryView>();
@@ -2154,18 +2156,17 @@
             if (func->IsFragment() && mv->AddressSpace() == AddressSpace::kIn) {
                 CheckIOAttributesAndType(
                     func, attr, ty,
-                    CheckFrontFacingIfBoolFunc<Function>("input address space values referenced by "
-                                                         "fragment shaders can only be a bool if "
-                                                         "decorated with @builtin(front_facing)"));
+                    CheckFrontFacingIfBoolFunc<Function>(
+                        "input address space values referenced by fragment shaders can only be "
+                        "'bool' if decorated with @builtin(front_facing)"));
 
             } else {
                 CheckIOAttributesAndType(
                     func, attr, ty,
                     CheckNotBool<Function>(
                         "IO address space values referenced by shader entry points can only be "
-                        "bool if "
-                        "in the input space, used only by fragment shaders and decorated with "
-                        "@builtin(front_facing)"));
+                        "'bool' if in the input space, used only by fragment shaders and decorated "
+                        "with @builtin(front_facing)"));
             }
         }
     }
@@ -2205,7 +2206,8 @@
 
         auto* ty = size->Type();
         if (!ty->IsAnyOf<core::type::I32, core::type::U32>()) {
-            AddError(func) << "@workgroup_size params must be an i32 or u32";
+            AddError(func) << "@workgroup_size params must be an 'i32' or 'u32', received "
+                           << NameOf(ty);
             return;
         }
 
@@ -2214,7 +2216,7 @@
         }
 
         if (sizes_ty != ty) {
-            AddError(func) << "@workgroup_size params must be all i32s or all u32s";
+            AddError(func) << "@workgroup_size params must be all 'i32's or all 'u32's";
             return;
         }
 
@@ -2422,8 +2424,7 @@
     }
 
     if (!o->Result(0)->Type()->IsScalar()) {
-        AddError(o) << "override type " << style::Type(o->Result(0)->Type()->FriendlyName())
-                    << " is not a scalar";
+        AddError(o) << "override type " << NameOf(o->Result(0)->Type()) << " is not a scalar";
         return;
     }
 
@@ -2432,9 +2433,8 @@
             return;
         }
         if (o->Initializer()->Type() != o->Result(0)->Type()) {
-            AddError(o) << "override type " << style::Type(o->Result(0)->Type()->FriendlyName())
-                        << " does not match initializer type "
-                        << style::Type(o->Initializer()->Type()->FriendlyName());
+            AddError(o) << "override type " << NameOf(o->Result(0)->Type())
+                        << " does not match initializer type " << NameOf(o->Initializer()->Type());
             return;
         }
     }
@@ -2454,7 +2454,8 @@
 
     auto* mv = result_type->As<core::type::MemoryView>();
     if (!mv) {
-        AddError(var) << "result type must be a pointer or a reference";
+        AddError(var) << "result type " << NameOf(result_type)
+                      << " must be a pointer or a reference";
         return;
     }
 
@@ -2480,10 +2481,9 @@
         }
 
         if (var->Initializer()->Type() != var->Result(0)->Type()->UnwrapPtrOrRef()) {
-            AddError(var) << "initializer type "
-                          << style::Type(var->Initializer()->Type()->FriendlyName())
+            AddError(var) << "initializer type " << NameOf(var->Initializer()->Type())
                           << " does not match store type "
-                          << style::Type(var->Result(0)->Type()->UnwrapPtrOrRef()->FriendlyName());
+                          << NameOf(var->Result(0)->Type()->UnwrapPtrOrRef());
             return;
         }
     }
@@ -2640,11 +2640,8 @@
     }
 
     if (l->Result(0)->Type() != l->Value()->Type()) {
-        auto result_type_name =
-            l->Result(0)->Type() ? l->Result(0)->Type()->FriendlyName() : "undef";
-        auto value_type_name = l->Value()->Type() ? l->Value()->Type()->FriendlyName() : "undef";
-        AddError(l) << "result type " << style::Type(result_type_name)
-                    << " does not match value type " << style::Type(value_type_name);
+        AddError(l) << "result type " << NameOf(l->Result(0)->Type())
+                    << " does not match value type " << NameOf(l->Value()->Type());
     }
 }
 
@@ -2689,8 +2686,8 @@
     const auto* result_type = bitcast->Result(0)->Type();
 
     const auto add_error = [&]() {
-        AddError(bitcast) << "bitcast is not defined for " << style::Type(val_type->FriendlyName())
-                          << " -> " << style::Type(result_type->FriendlyName());
+        AddError(bitcast) << "bitcast is not defined for " << NameOf(val_type) << " -> "
+                          << NameOf(result_type);
     };
 
     // Check that there exists an overload for the provided types
@@ -2812,7 +2809,8 @@
     }
 
     if (builtin->return_type != call->Result(0)->Type()) {
-        AddError(call) << "call result type does not match builtin return type";
+        AddError(call) << "call result type " << NameOf(call->Result(0)->Type())
+                       << "does not match builtin return type " << NameOf(builtin->return_type);
         return;
     }
 }
@@ -2837,10 +2835,8 @@
     }
 
     if (result->return_type != call->Result(0)->Type()) {
-        AddError(call) << "member call result type ("
-                       << style::Type(call->Result(0)->Type()->FriendlyName())
-                       << ") does not match builtin return type ("
-                       << style::Type(result->return_type->FriendlyName()) << ")";
+        AddError(call) << "member call result type " << NameOf(call->Result(0)->Type())
+                       << " does not match builtin return type " << NameOf(result->return_type);
     }
 }
 
@@ -2869,9 +2865,8 @@
             }
             if (args[i]->Type() != members[i]->Type()) {
                 AddError(construct, Construct::kArgsOperandOffset + i)
-                    << "structure member " << i << " is of type "
-                    << style::Type(members[i]->Type()->FriendlyName())
-                    << ", but argument is of type " << style::Type(args[i]->Type()->FriendlyName());
+                    << "type " << NameOf(args[i]->Type()) << " of argument " << i
+                    << " does not match type " << NameOf(members[i]->Type()) << " of struct member";
             }
         }
     }
@@ -2905,8 +2900,7 @@
         [&](Default) { conv_ty = intrinsic::CtorConv::kNone; });
 
     if (conv_ty == intrinsic::CtorConv::kNone) {
-        AddError(convert) << "not defined for result type, " << StyledText{}
-                          << style::Type(result_type->FriendlyName());
+        AddError(convert) << "not defined for result type, " << NameOf(result_type);
         return;
     }
 
@@ -2914,9 +2908,8 @@
     auto match =
         table.Lookup(conv_ty, template_type, Vector{value_type}, core::EvaluationStage::kOverride);
     if (match != Success || !match->info->flags.Contains(intrinsic::OverloadFlag::kIsConverter)) {
-        AddError(convert) << "No defined converter for " << StyledText{}
-                          << style::Type(value_type->FriendlyName()) << " -> " << StyledText{}
-                          << style::Type(result_type->FriendlyName());
+        AddError(convert) << "No defined converter for " << NameOf(value_type) << " -> "
+                          << NameOf(result_type);
         return;
     }
 }
@@ -2952,9 +2945,8 @@
     for (size_t i = 0; i < args.Length(); i++) {
         if (args[i]->Type() != params[i]->Type()) {
             AddError(call, UserCall::kArgsOperandOffset + i)
-                << "function parameter " << i << " is of type "
-                << style::Type(params[i]->Type()->FriendlyName()) << ", but argument is of type "
-                << style::Type(args[i]->Type()->FriendlyName());
+                << "type " << NameOf(params[i]->Type()) << " of function parameter " << i
+                << " does not match argument type " << NameOf(args[i]->Type());
         }
     }
 }
@@ -2989,7 +2981,7 @@
                        << style::Type("ref<", obj_view->AddressSpace(), ", ", type->FriendlyName(),
                                       ", ", obj_view->Access(), ">");
             default:
-                return StyledText{} << style::Type(type->FriendlyName());
+                return NameOf(type);
         }
     };
 
@@ -3003,8 +2995,7 @@
 
         auto* index = a->Indices()[i];
         if (DAWN_UNLIKELY(!index->Type() || !index->Type()->IsIntegerScalar())) {
-            auto name = index->Type() ? index->Type()->FriendlyName() : "undef";
-            err() << "index must be integer, got " << name;
+            err() << "index type " << NameOf(index->Type()) << " must be an integer";
             return;
         }
 
@@ -3068,7 +3059,7 @@
     }
     if (DAWN_UNLIKELY(!ok)) {
         AddError(a) << "result of access chain is type " << desc_of(in_kind, ty)
-                    << " but instruction type is " << style::Type(want->FriendlyName());
+                    << " but instruction type is " << NameOf(want);
     }
 }
 
@@ -3090,10 +3081,9 @@
 
         if (auto* result = b->Result(0)) {
             if (overload->return_type != result->Type()) {
-                AddError(b) << "result value type " << style::Type(result->Type()->FriendlyName())
-                            << " does not match "
+                AddError(b) << "result value type " << NameOf(result->Type()) << " does not match "
                             << style::Instruction(Disassemble().NameOf(b->Op())) << " result type "
-                            << style::Type(overload->return_type->FriendlyName());
+                            << NameOf(overload->return_type);
             }
         }
     }
@@ -3116,10 +3106,9 @@
 
         if (auto* result = u->Result(0)) {
             if (overload->return_type != result->Type()) {
-                AddError(u) << "result value type " << style::Type(result->Type()->FriendlyName())
-                            << " does not match "
+                AddError(u) << "result value type " << NameOf(result->Type()) << " does not match "
                             << style::Instruction(Disassemble().NameOf(u->Op())) << " result type "
-                            << style::Type(overload->return_type->FriendlyName());
+                            << NameOf(overload->return_type);
             }
         }
     }
@@ -3130,8 +3119,7 @@
     CheckOperand(if_, If::kConditionOperandOffset);
 
     if (if_->Condition() && !if_->Condition()->Type()->Is<core::type::Bool>()) {
-        AddError(if_, If::kConditionOperandOffset)
-            << "condition type must be " << style::Type("bool");
+        AddError(if_, If::kConditionOperandOffset) << "condition type must be 'bool'";
     }
 
     tasks_.Push([this] { control_stack_.Pop(); });
@@ -3212,8 +3200,7 @@
                     if (TransitivelyHolds(loop->Continuing(), use.instruction)) {
                         AddError(use.instruction, use.operand_index)
                             << NameOf(result)
-                            << " cannot be used in continuing block as it is declared after "
-                               "the "
+                            << " cannot be used in continuing block as it is declared after the "
                                "first "
                             << style::Instruction("continue") << " in the loop's body";
                         AddDeclarationNote(result);
@@ -3230,7 +3217,9 @@
     CheckOperand(s, Switch::kConditionOperandOffset);
 
     if (s->Condition() && !s->Condition()->Type()->IsIntegerScalar()) {
-        AddError(s, Switch::kConditionOperandOffset) << "condition type must be an integer scalar";
+        auto* cond_ty = s->Condition() ? s->Condition()->Type() : nullptr;
+        AddError(s, Switch::kConditionOperandOffset)
+            << "condition type " << NameOf(cond_ty) << " must be an integer scalar";
     }
 
     tasks_.Push([this] { control_stack_.Pop(); });
@@ -3481,7 +3470,8 @@
     if (auto* from = l->From()) {
         auto* mv = from->Type()->As<core::type::MemoryView>();
         if (!mv) {
-            AddError(l, Load::kFromOperandOffset) << "load source operand is not a memory view";
+            AddError(l, Load::kFromOperandOffset)
+                << "load source operand " << NameOf(from->Type()) << " is not a memory view";
             return;
         }
 
@@ -3494,9 +3484,8 @@
 
         if (l->Result(0)->Type() != mv->StoreType()) {
             AddError(l, Load::kFromOperandOffset)
-                << "result type " << style::Type(l->Result(0)->Type()->FriendlyName())
-                << " does not match source store type "
-                << style::Type(mv->StoreType()->FriendlyName());
+                << "result type " << NameOf(l->Result(0)->Type())
+                << " does not match source store type " << NameOf(mv->StoreType());
         }
     }
 }
@@ -3510,7 +3499,8 @@
         if (auto* to = s->To()) {
             auto* mv = As<core::type::MemoryView>(to->Type());
             if (!mv) {
-                AddError(s, Store::kToOperandOffset) << "store target operand is not a memory view";
+                AddError(s, Store::kToOperandOffset)
+                    << "store target operand " << NameOf(to->Type()) << " is not a memory view";
                 return;
             }
 
@@ -3525,8 +3515,8 @@
             auto* store_type = mv->StoreType();
             if (value_type != store_type) {
                 AddError(s, Store::kFromOperandOffset)
-                    << "value type " << style::Type(value_type->FriendlyName())
-                    << " does not match store type " << style::Type(store_type->FriendlyName());
+                    << "value type " << NameOf(value_type) << " does not match store type "
+                    << NameOf(store_type);
             }
         }
     }
@@ -3541,9 +3531,9 @@
     if (auto* res = l->Result(0)) {
         if (auto* el_ty = GetVectorPtrElementType(l, LoadVectorElement::kFromOperandOffset)) {
             if (res->Type() != el_ty) {
-                AddResultError(l, 0) << "result type " << style::Type(res->Type()->FriendlyName())
-                                     << " does not match vector pointer element type "
-                                     << style::Type(el_ty->FriendlyName());
+                AddResultError(l, 0)
+                    << "result type " << NameOf(res->Type())
+                    << " does not match vector pointer element type " << NameOf(el_ty);
             }
         }
     }
@@ -3559,9 +3549,8 @@
         if (auto* el_ty = GetVectorPtrElementType(s, StoreVectorElement::kToOperandOffset)) {
             if (value->Type() != el_ty) {
                 AddError(s, StoreVectorElement::kValueOperandOffset)
-                    << "value type " << style::Type(value->Type()->FriendlyName())
-                    << " does not match vector pointer element type "
-                    << style::Type(el_ty->FriendlyName());
+                    << "value type " << NameOf(value->Type())
+                    << " does not match vector pointer element type " << NameOf(el_ty);
             }
         }
     }
@@ -3590,9 +3579,8 @@
         auto* target_type = target_value->Type();
         if (source_type != target_type) {
             AddError(source_inst, source_operand_offset + i)
-                << "operand with type " << style::Type(source_type->FriendlyName())
-                << " does not match " << NameOf(target) << " target type "
-                << style::Type(target_type->FriendlyName());
+                << "operand with type " << NameOf(source_type) << " does not match "
+                << NameOf(target) << " target type " << NameOf(target_type);
             AddDeclarationNote(target_value);
         }
     }
@@ -3617,8 +3605,7 @@
         }
     }
 
-    AddError(inst, idx) << "operand must be a pointer to vector, got "
-                        << style::Type(type->FriendlyName());
+    AddError(inst, idx) << "operand " << NameOf(type) << " must be a pointer to a vector";
     return nullptr;
 }
 
diff --git a/src/tint/lang/core/ir/validator_access_test.cc b/src/tint/lang/core/ir/validator_access_test.cc
index c28497e..cd77346 100644
--- a/src/tint/lang/core/ir/validator_access_test.cc
+++ b/src/tint/lang/core/ir/validator_access_test.cc
@@ -788,7 +788,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:4:19 error: load: load source operand is not a memory view
+              R"(:4:19 error: load: load source operand 'i32' is not a memory view
     %3:f32 = load %l
                   ^^
 
@@ -1041,7 +1041,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:4:11 error: store: store target operand is not a memory view
+              R"(:4:11 error: store: store target operand 'i32' is not a memory view
     store %l, 42u
           ^^
 
diff --git a/src/tint/lang/core/ir/validator_builtin_test.cc b/src/tint/lang/core/ir/validator_builtin_test.cc
index cb97890..a17d988 100644
--- a/src/tint/lang/core/ir/validator_builtin_test.cc
+++ b/src/tint/lang/core/ir/validator_builtin_test.cc
@@ -363,7 +363,7 @@
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@global_invocation_id] {
 ^^
 
-:1:1 error: compute entry point must not have a return type
+:1:1 error: compute entry point must not have a return type, found 'vec3<u32>'
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@global_invocation_id] {
 ^^
 
@@ -505,7 +505,7 @@
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@local_invocation_id] {
 ^^
 
-:1:1 error: compute entry point must not have a return type
+:1:1 error: compute entry point must not have a return type, found 'vec3<u32>'
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@local_invocation_id] {
 ^^
 
@@ -576,7 +576,7 @@
 %f = @compute @workgroup_size(1u, 1u, 1u) func():u32 [@local_invocation_index] {
 ^^
 
-:1:1 error: compute entry point must not have a return type
+:1:1 error: compute entry point must not have a return type, found 'u32'
 %f = @compute @workgroup_size(1u, 1u, 1u) func():u32 [@local_invocation_index] {
 ^^
 
@@ -647,7 +647,7 @@
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@num_workgroups] {
 ^^
 
-:1:1 error: compute entry point must not have a return type
+:1:1 error: compute entry point must not have a return type, found 'vec3<u32>'
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@num_workgroups] {
 ^^
 
@@ -855,7 +855,7 @@
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@workgroup_id] {
 ^^
 
-:1:1 error: compute entry point must not have a return type
+:1:1 error: compute entry point must not have a return type, found 'vec3<u32>'
 %f = @compute @workgroup_size(1u, 1u, 1u) func():vec3<u32> [@workgroup_id] {
 ^^
 
diff --git a/src/tint/lang/core/ir/validator_call_test.cc b/src/tint/lang/core/ir/validator_call_test.cc
index 1353318..21afddb 100644
--- a/src/tint/lang/core/ir/validator_call_test.cc
+++ b/src/tint/lang/core/ir/validator_call_test.cc
@@ -204,7 +204,7 @@
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason.Str(),
-        R"(:8:28 error: call: function parameter 1 is of type 'i32', but argument is of type 'f32'
+        R"(:8:28 error: call: type 'i32' of function parameter 1 does not match argument type 'f32'
     %6:void = call %g, 1i, 2.0f, 3i
                            ^^^^
 
@@ -448,7 +448,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:3:14 error: abs: call result type does not match builtin return type
+              R"(:3:14 error: abs: call result type 'i32'does not match builtin return type 'f32'
     %2:i32 = abs 1.0f
              ^^^
 
diff --git a/src/tint/lang/core/ir/validator_flow_control_test.cc b/src/tint/lang/core/ir/validator_flow_control_test.cc
index 4fd4171..b5e6e11 100644
--- a/src/tint/lang/core/ir/validator_flow_control_test.cc
+++ b/src/tint/lang/core/ir/validator_flow_control_test.cc
@@ -3174,8 +3174,9 @@
 
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
-    EXPECT_EQ(res.Failure().reason.Str(),
-              R"(error: switch: condition type must be an integer scalar
+    EXPECT_EQ(
+        res.Failure().reason.Str(),
+        R"(error: switch: condition type 'ptr<function, i32, read_write>' must be an integer scalar
 :2:3 note: in block
   $B1: {
   ^^^
diff --git a/src/tint/lang/core/ir/validator_function_test.cc b/src/tint/lang/core/ir/validator_function_test.cc
index 2c2d7f5..5289fc4 100644
--- a/src/tint/lang/core/ir/validator_function_test.cc
+++ b/src/tint/lang/core/ir/validator_function_test.cc
@@ -557,7 +557,7 @@
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason.Str(),
-        R"(:1:17 error: function parameter type must be constructible, a pointer, a texture, or a sampler
+        R"(:1:17 error: function parameter type, 'void', must be constructible, a pointer, a texture, or a sampler
 %my_func = func(%my_param:void):void {
                 ^^^^^^^^^^^^^^
 
@@ -995,7 +995,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:1:1 error: compute entry point must not have a return type
+              R"(:1:1 error: compute entry point must not have a return type, found 'f32'
 %my_func = @compute @workgroup_size(1u, 1u, 1u) func():f32 [@location(0)] {
 ^^^^^^^^
 
@@ -1081,7 +1081,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:1:1 error: @workgroup_size params must be an i32 or u32
+              R"(:1:1 error: @workgroup_size params must be an 'i32' or 'u32', received 'f32'
 %f = @compute @workgroup_size(1.0f, 2u, 3u) func():void {
 ^^
 
@@ -1103,7 +1103,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:1:1 error: @workgroup_size params must be all i32s or all u32s
+              R"(:1:1 error: @workgroup_size params must be all 'i32's or all 'u32's
 %f = @compute @workgroup_size(1u, 2i, 3u) func():void {
 ^^
 
@@ -1324,7 +1324,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:6:1 error: entry point return members can not be bool
+              R"(:6:1 error: entry point return members can not be 'bool'
 %f = @vertex func():OutputStruct {
 ^^
 
@@ -1376,7 +1376,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:1:1 error: entry point returns can not be bool
+              R"(:1:1 error: entry point returns can not be 'bool'
 %f = @fragment func():bool [@location(0)] {
 ^^
 
@@ -1408,7 +1408,7 @@
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason.Str(),
-        R"(:5:1 error: IO address space values referenced by shader entry points can only be bool if in the input space, used only by fragment shaders and decorated with @builtin(front_facing)
+        R"(:5:1 error: IO address space values referenced by shader entry points can only be 'bool' if in the input space, used only by fragment shaders and decorated with @builtin(front_facing)
 %f = @compute @workgroup_size(1u, 1u, 1u) func():void {
 ^^
 
@@ -1446,7 +1446,7 @@
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason.Str(),
-        R"(:5:1 error: input address space values referenced by fragment shaders can only be a bool if decorated with @builtin(front_facing)
+        R"(:5:1 error: input address space values referenced by fragment shaders can only be 'bool' if decorated with @builtin(front_facing)
 %f = @fragment func():void {
 ^^
 
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 9a24eb5..3390bc7 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -373,7 +373,7 @@
     ASSERT_NE(res, Success);
     EXPECT_EQ(
         res.Failure().reason.Str(),
-        R"(:8:33 error: construct: structure member 1 is of type 'u32', but argument is of type 'i32'
+        R"(:8:33 error: construct: type 'i32' of argument 1 does not match type 'u32' of struct member
     %2:MyStruct = construct 1i, 2i
                                 ^^
 
diff --git a/src/tint/lang/core/ir/validator_type_test.cc b/src/tint/lang/core/ir/validator_type_test.cc
index c1cc6f9..25511cf 100644
--- a/src/tint/lang/core/ir/validator_type_test.cc
+++ b/src/tint/lang/core/ir/validator_type_test.cc
@@ -239,7 +239,8 @@
 
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
-    EXPECT_EQ(res.Failure().reason.Str(), R"(:8:5 error: var: vector elements must be scalars
+    EXPECT_EQ(res.Failure().reason.Str(),
+              R"(:8:5 error: var: vector elements, 'vec2<void>', must be scalars
     %void_invalid:ptr<function, vec2<void>, read_write> = var
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -278,7 +279,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:3:5 error: var: matrix elements must be float scalars
+              R"(:3:5 error: var: matrix elements, 'mat2x2<u32>', must be float scalars
     %u32_invalid:ptr<function, mat2x2<u32>, read_write> = var
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -286,7 +287,7 @@
   $B1: {
   ^^^
 
-:4:5 error: var: matrix elements must be float scalars
+:4:5 error: var: matrix elements, 'mat3x2<i32>', must be float scalars
     %i32_invalid:ptr<function, mat3x2<i32>, read_write> = var
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -294,7 +295,7 @@
   $B1: {
   ^^^
 
-:5:5 error: var: matrix elements must be float scalars
+:5:5 error: var: matrix elements, 'mat4x2<bool>', must be float scalars
     %bool_invalid:ptr<function, mat4x2<bool>, read_write> = var
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -302,7 +303,7 @@
   $B1: {
   ^^^
 
-:8:5 error: var: matrix elements must be float scalars
+:8:5 error: var: matrix elements, 'mat3x3<void>', must be float scalars
     %void_invalid:ptr<function, mat3x3<void>, read_write> = var
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/src/tint/lang/core/ir/validator_value_test.cc b/src/tint/lang/core/ir/validator_value_test.cc
index 556464a..91f63d9 100644
--- a/src/tint/lang/core/ir/validator_value_test.cc
+++ b/src/tint/lang/core/ir/validator_value_test.cc
@@ -164,7 +164,7 @@
     auto res = ir::Validate(mod);
     ASSERT_NE(res, Success);
     EXPECT_EQ(res.Failure().reason.Str(),
-              R"(:3:14 error: var: result type must be a pointer or a reference
+              R"(:3:14 error: var: result type 'f32' must be a pointer or a reference
     %2:f32 = var
              ^^^