validator: Require constants to have initializers

Unless they are pipeline overridable.

Fixed several tests that were violating this.

Change-Id: Ibb10e03bb150086a4d9e68a3f7b2d3e21f282918
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/50042
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 8d3f35f..9779c40 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -522,6 +522,13 @@
     if (!Expression(var->constructor())) {
       return false;
     }
+  } else {
+    if (var->is_const() &&
+        !ast::HasDecoration<ast::OverrideDecoration>(var->decorations())) {
+      diagnostics_.add_error("let declarations must have initializers",
+                             var->source());
+      return false;
+    }
   }
 
   if (!ValidateGlobalVariable(info)) {
@@ -2063,6 +2070,12 @@
           stmt->source());
       return false;
     }
+  } else {
+    if (stmt->variable()->is_const()) {
+      diagnostics_.add_error("let declarations must have initializers",
+                             var->source());
+      return false;
+    }
   }
 
   for (auto* deco : var->decorations()) {
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index 100bcc2..f41639e 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -426,7 +426,7 @@
 }
 
 TEST_F(ResolverTest, Expr_ArrayAccessor_Array_Constant) {
-  GlobalConst("my_var", ty.array<f32, 3>());
+  GlobalConst("my_var", ty.array<f32, 3>(), array<f32, 3>());
 
   auto* acc = IndexAccessor("my_var", 2);
   WrapInFunction(acc);
@@ -624,7 +624,7 @@
 }
 
 TEST_F(ResolverTest, Expr_Identifier_GlobalConstant) {
-  auto* my_var = GlobalConst("my_var", ty.f32());
+  auto* my_var = GlobalConst("my_var", ty.f32(), Construct(ty.f32()));
 
   auto* ident = Expr("my_var");
   WrapInFunction(ident);
@@ -640,7 +640,7 @@
 
 TEST_F(ResolverTest, Expr_Identifier_FunctionVariable_Const) {
   auto* my_var_a = Expr("my_var");
-  auto* var = Const("my_var", ty.f32());
+  auto* var = Const("my_var", ty.f32(), Construct(ty.f32()));
   auto* decl = Decl(Var("b", ty.f32(), ast::StorageClass::kFunction, my_var_a));
 
   Func("my_func", ast::VariableList{}, ty.void_(),
@@ -1457,7 +1457,7 @@
 }
 
 TEST_F(ResolverTest, StorageClass_DoesNotSetOnConst) {
-  auto* var = Const("var", ty.i32());
+  auto* var = Const("var", ty.i32(), Construct(ty.i32()));
   auto* stmt = Decl(var);
   Func("func", ast::VariableList{}, ty.void_(), ast::StatementList{stmt},
        ast::DecorationList{});
diff --git a/src/resolver/type_validation_test.cc b/src/resolver/type_validation_test.cc
index c7317e3..5d12b81 100644
--- a/src/resolver/type_validation_test.cc
+++ b/src/resolver/type_validation_test.cc
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/ast/override_decoration.h"
 #include "src/ast/return_statement.h"
 #include "src/ast/stage_decoration.h"
 #include "src/ast/struct_block_decoration.h"
@@ -49,6 +50,35 @@
   ASSERT_NE(TypeOf(rhs), nullptr);
 }
 
+TEST_F(ResolverTypeValidationTest, FunctionConstantNoConstructor_Fail) {
+  // {
+  // let a :i32;
+  // }
+  auto* var = Const(Source{{12, 34}}, "a", ty.i32(), nullptr);
+  WrapInFunction(var);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: let declarations must have initializers");
+}
+
+TEST_F(ResolverTypeValidationTest, GlobalConstantNoConstructor_Fail) {
+  // let a :i32;
+  GlobalConst(Source{{12, 34}}, "a", ty.i32(), nullptr);
+
+  EXPECT_FALSE(r()->Resolve());
+  EXPECT_EQ(r()->error(),
+            "12:34 error: let declarations must have initializers");
+}
+
+TEST_F(ResolverTypeValidationTest, GlobalConstantNoConstructor_Pass) {
+  // [[override(0)]] let a :i32;
+  GlobalConst(Source{{12, 34}}, "a", ty.i32(), nullptr,
+              ast::DecorationList{create<ast::OverrideDecoration>(0)});
+
+  EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
 TEST_F(ResolverTypeValidationTest, GlobalVariableWithStorageClass_Pass) {
   // var<in> global_var: f32;
   Global(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kInput);
@@ -71,7 +101,7 @@
 
 TEST_F(ResolverTypeValidationTest, GlobalConstNoStorageClass_Pass) {
   // let global_var: f32;
-  GlobalConst(Source{{12, 34}}, "global_var", ty.f32());
+  GlobalConst(Source{{12, 34}}, "global_var", ty.f32(), Construct(ty.f32()));
 
   EXPECT_TRUE(r()->Resolve()) << r()->error();
 }
diff --git a/src/writer/hlsl/generator_impl_module_constant_test.cc b/src/writer/hlsl/generator_impl_module_constant_test.cc
index 1f9a206..6680341 100644
--- a/src/writer/hlsl/generator_impl_module_constant_test.cc
+++ b/src/writer/hlsl/generator_impl_module_constant_test.cc
@@ -51,11 +51,10 @@
 }
 
 TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant_NoConstructor) {
-  auto* var = Const("pos", ty.f32(), nullptr,
-                    ast::DecorationList{
-                        create<ast::OverrideDecoration>(23),
-                    });
-  WrapInFunction(Decl(var));
+  auto* var = GlobalConst("pos", ty.f32(), nullptr,
+                          ast::DecorationList{
+                              create<ast::OverrideDecoration>(23),
+                          });
 
   GeneratorImpl& gen = Build();
 
diff --git a/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc b/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
index 4c2fae6..e6904ec 100644
--- a/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/hlsl/generator_impl_variable_decl_statement_test.cc
@@ -39,7 +39,7 @@
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Const) {
-  auto* var = Const("a", ty.f32());
+  auto* var = Const("a", ty.f32(), Construct(ty.f32()));
   auto* stmt = Decl(var);
   WrapInFunction(stmt);
 
@@ -48,7 +48,7 @@
   gen.increment_indent();
 
   ASSERT_TRUE(gen.EmitStatement(out, stmt)) << gen.error();
-  EXPECT_EQ(result(), "  const float a;\n");
+  EXPECT_EQ(result(), "  const float a = 0.0f;\n");
 }
 
 TEST_F(HlslGeneratorImplTest_VariableDecl, Emit_VariableDeclStatement_Array) {
diff --git a/src/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
index 1f14790..98b2b81 100644
--- a/src/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -39,7 +39,7 @@
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Const) {
-  auto* var = Const("a", ty.f32());
+  auto* var = Const("a", ty.f32(), Construct(ty.f32()));
   auto* stmt = Decl(var);
   WrapInFunction(stmt);
 
@@ -48,7 +48,7 @@
   gen.increment_indent();
 
   ASSERT_TRUE(gen.EmitStatement(stmt)) << gen.error();
-  EXPECT_EQ(gen.result(), "  const float a = 0.0f;\n");
+  EXPECT_EQ(gen.result(), "  const float a = float(0.0f);\n");
 }
 
 TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Array) {