Commit 8387307807

Andrew Kelley <andrew@ziglang.org>
2021-04-16 05:34:21
AstGen: implement global variable decls
1 parent 7818586
Changed files (3)
src/AstGen.zig
@@ -1491,7 +1491,7 @@ fn varDecl(
     }
 
     if (var_decl.ast.init_node == 0) {
-        return astgen.failTok(name_token, "variables must be initialized", .{});
+        return astgen.failNode(node, "variables must be initialized", .{});
     }
 
     switch (token_tags[var_decl.ast.mut_token]) {
@@ -1851,10 +1851,12 @@ fn fnDecl(
 
     const is_pub = fn_proto.visib_token != null;
     const is_export = blk: {
-        if (fn_proto.extern_export_token) |maybe_export_token| {
-            break :blk token_tags[maybe_export_token] == .keyword_export;
-        }
-        break :blk false;
+        const maybe_export_token = fn_proto.extern_export_token orelse break :blk false;
+        break :blk token_tags[maybe_export_token] == .keyword_export;
+    };
+    const is_extern = blk: {
+        const maybe_extern_token = fn_proto.extern_export_token orelse break :blk false;
+        break :blk token_tags[maybe_extern_token] == .keyword_extern;
     };
     if (wip_decls.decl_index % 16 == 0 and wip_decls.decl_index != 0) {
         try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);
@@ -1937,11 +1939,6 @@ fn fnDecl(
         fn_proto.ast.return_type,
     );
 
-    const is_extern = if (fn_proto.extern_export_token) |maybe_export_token|
-        token_tags[maybe_export_token] == .keyword_extern
-    else
-        false;
-
     const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
         // TODO instead of enum literal type, this needs to be the
         // std.builtin.CallingConvention enum. We need to implement importing other files
@@ -2070,10 +2067,94 @@ fn fnDecl(
 fn globalVarDecl(
     astgen: *AstGen,
     gz: *GenZir,
+    scope: *Scope,
     wip_decls: *WipDecls,
+    node: ast.Node.Index,
     var_decl: ast.full.VarDecl,
 ) InnerError!void {
-    @panic("TODO astgen globalVarDecl");
+    const gpa = astgen.gpa;
+    const tree = &astgen.file.tree;
+    const token_tags = tree.tokens.items(.tag);
+
+    const is_pub = var_decl.visib_token != null;
+    const is_export = blk: {
+        const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
+        break :blk token_tags[maybe_export_token] == .keyword_export;
+    };
+    const is_extern = blk: {
+        const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
+        break :blk token_tags[maybe_extern_token] == .keyword_extern;
+    };
+    if (wip_decls.decl_index % 16 == 0 and wip_decls.decl_index != 0) {
+        try wip_decls.bit_bag.append(gpa, wip_decls.cur_bit_bag);
+        wip_decls.cur_bit_bag = 0;
+    }
+    wip_decls.cur_bit_bag = (wip_decls.cur_bit_bag >> 2) |
+        (@as(u32, @boolToInt(is_pub)) << 30) |
+        (@as(u32, @boolToInt(is_export)) << 31);
+    wip_decls.decl_index += 1;
+
+    const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
+    const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
+        if (!is_mutable) {
+            return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
+        }
+        break :blk true;
+    } else false;
+
+    const lib_name: u32 = if (var_decl.lib_name) |lib_name_token| blk: {
+        const lib_name_str = try gz.strLitAsString(lib_name_token);
+        break :blk lib_name_str.index;
+    } else 0;
+
+    assert(var_decl.comptime_token == null); // handled by parser
+    if (var_decl.ast.align_node != 0) {
+        return astgen.failNode(var_decl.ast.align_node, "TODO implement alignment on globals", .{});
+    }
+    if (var_decl.ast.section_node != 0) {
+        return astgen.failNode(var_decl.ast.section_node, "TODO linksection on globals", .{});
+    }
+
+    const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
+        if (is_extern) {
+            return astgen.failNode(
+                var_decl.ast.init_node,
+                "extern variables have no initializers",
+                .{},
+            );
+        }
+
+        const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{
+            .ty = try expr(gz, scope, .{ .ty = .type_type }, var_decl.ast.type_node),
+        } else .none;
+
+        const init_inst = try expr(gz, scope, init_result_loc, var_decl.ast.init_node);
+
+        if (!is_mutable) {
+            // const globals are just their instruction. mutable globals have
+            // a special ZIR form.
+            break :vi init_inst;
+        }
+
+        @panic("TODO astgen global variable");
+    } else if (!is_extern) {
+        return astgen.failNode(node, "variables must be initialized", .{});
+    } else if (var_decl.ast.type_node != 0) {
+        // Extern variable which has an explicit type.
+
+        const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
+
+        @panic("TODO AstGen extern global variable");
+    } else {
+        return astgen.failNode(node, "unable to infer variable type", .{});
+    };
+
+    const name_token = var_decl.ast.mut_token + 1;
+    const name_str_index = try gz.identAsString(name_token);
+
+    try wip_decls.name_and_value.ensureCapacity(gpa, wip_decls.name_and_value.items.len + 2);
+    wip_decls.name_and_value.appendAssumeCapacity(name_str_index);
+    wip_decls.name_and_value.appendAssumeCapacity(@enumToInt(var_inst));
 }
 
 fn comptimeDecl(
@@ -2191,19 +2272,19 @@ fn structDeclInner(
             },
 
             .global_var_decl => {
-                try astgen.globalVarDecl(gz, &wip_decls, tree.globalVarDecl(member_node));
+                try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.globalVarDecl(member_node));
                 continue;
             },
             .local_var_decl => {
-                try astgen.globalVarDecl(gz, &wip_decls, tree.localVarDecl(member_node));
+                try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.localVarDecl(member_node));
                 continue;
             },
             .simple_var_decl => {
-                try astgen.globalVarDecl(gz, &wip_decls, tree.simpleVarDecl(member_node));
+                try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.simpleVarDecl(member_node));
                 continue;
             },
             .aligned_var_decl => {
-                try astgen.globalVarDecl(gz, &wip_decls, tree.alignedVarDecl(member_node));
+                try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.alignedVarDecl(member_node));
                 continue;
             },
 
src/Module.zig
@@ -2457,6 +2457,9 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
 }
 
 pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node) !void {
+    const tracy = trace(@src());
+    defer tracy.end();
+
     const comp = mod.comp;
     const gpa = mod.gpa;
 
@@ -2468,7 +2471,9 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
 
     // Determine whether we need to reload the file from disk and redo parsing and AstGen.
     switch (file.status) {
-        .never_loaded, .retryable_failure => {},
+        .never_loaded, .retryable_failure => {
+            log.debug("first-time AstGen: {s}", .{file.sub_file_path});
+        },
         .parse_failure, .astgen_failure, .success => {
             const unchanged_metadata =
                 stat.size == file.stat_size and
BRANCH_TODO
@@ -1,4 +1,5 @@
  * get rid of failed_root_src_file
+ * get rid of Scope.DeclRef
  * handle decl collision with usingnamespace
  * the decl doing the looking up needs to create a decl dependency
    on each usingnamespace decl
@@ -106,42 +107,6 @@ fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenInd
 }
 
 
-            // Detect which source files changed.
-            for (module.import_table.items()) |entry| {
-                const file = entry.value;
-                var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{});
-                defer f.close();
-
-                // TODO handle error here by populating a retryable compile error
-                const stat = try f.stat();
-                const unchanged_metadata =
-                    stat.size == file.stat_size and
-                    stat.mtime == file.stat_mtime and
-                    stat.inode == file.stat_inode;
-
-                if (unchanged_metadata) {
-                    log.debug("unmodified metadata of file: {s}", .{file.sub_file_path});
-                    continue;
-                }
-
-                log.debug("metadata changed: {s}", .{file.sub_file_path});
-                if (file.status == .unloaded_parse_failure) {
-                    module.failed_files.swapRemove(file).?.value.destroy(module.gpa);
-                }
-
-                file.unload(module.gpa);
-                // TODO handle error here by populating a retryable compile error
-                try file.finishGettingSource(module.gpa, f, stat);
-
-                module.analyzeFile(file) catch |err| switch (err) {
-                    error.OutOfMemory => return error.OutOfMemory,
-                    error.AnalysisFail => continue,
-                    else => |e| return e,
-                };
-            }
-
-
-
     const parent_name_hash: Scope.NameHash = if (found_pkg) |pkg|
         pkg.namespace_hash
     else
@@ -256,68 +221,6 @@ fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenInd
 
 
 
-pub fn getAstTree(mod: *Module, file: *Scope.File) !*const ast.Tree {
-    const tracy = trace(@src());
-    defer tracy.end();
-
-    if (file.tree_loaded) {
-        return &file.tree;
-    }
-
-    switch (file.status) {
-        .never_loaded, .success, .retryable_failure => {},
-        .parse_failure, .astgen_failure => return error.AnalysisFail,
-    }
-
-    switch (file.status) {
-        .never_loaded, .unloaded_success => {
-            const gpa = mod.gpa;
-
-            try mod.failed_files.ensureCapacity(gpa, mod.failed_files.items().len + 1);
-
-            const source = try file.getSource(gpa);
-
-            var keep_tree = false;
-            file.tree = try std.zig.parse(gpa, source);
-            defer if (!keep_tree) file.tree.deinit(gpa);
-
-            const tree = &file.tree;
-
-            if (tree.errors.len != 0) {
-                const parse_err = tree.errors[0];
-
-                var msg = std.ArrayList(u8).init(gpa);
-                defer msg.deinit();
-
-                const token_starts = tree.tokens.items(.start);
-
-                try tree.renderError(parse_err, msg.writer());
-                const err_msg = try gpa.create(ErrorMsg);
-                err_msg.* = .{
-                    .src_loc = .{
-                        .container = .{ .file_scope = file },
-                        .lazy = .{ .byte_abs = token_starts[parse_err.token] },
-                    },
-                    .msg = msg.toOwnedSlice(),
-                };
-
-                mod.failed_files.putAssumeCapacityNoClobber(file, err_msg);
-                file.status = .unloaded_parse_failure;
-                return error.AnalysisFail;
-            }
-
-            file.status = .success;
-            file.tree_loaded = true;
-            keep_tree = true;
-
-            return tree;
-        },
-
-        .unloaded_parse_failure => return error.AnalysisFail,
-
-        .success => return &file.tree,
-    }
-}
 
 
 
@@ -510,131 +413,6 @@ fn astgenAndSemaFn(
     body_node: ast.Node.Index,
     fn_proto: ast.full.FnProto,
 ) !bool {
-    var fn_type_sema: Sema = .{
-        .mod = mod,
-        .gpa = mod.gpa,
-        .arena = &decl_arena.allocator,
-        .code = fn_type_code,
-        .inst_map = try fn_type_scope_arena.allocator.alloc(*ir.Inst, fn_type_code.instructions.len),
-        .owner_decl = decl,
-        .namespace = decl.namespace,
-        .func = null,
-        .owner_func = null,
-        .param_inst_list = &.{},
-    };
-    var block_scope: Scope.Block = .{
-        .parent = null,
-        .sema = &fn_type_sema,
-        .src_decl = decl,
-        .instructions = .{},
-        .inlining = null,
-        .is_comptime = true,
-    };
-    defer block_scope.instructions.deinit(mod.gpa);
-
-    const fn_type = try fn_type_sema.rootAsType(&block_scope);
-    if (body_node == 0) {
-        // Extern function.
-        var type_changed = true;
-        if (decl.typedValueManaged()) |tvm| {
-            type_changed = !tvm.typed_value.ty.eql(fn_type);
-
-            tvm.deinit(mod.gpa);
-        }
-        const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl);
-
-        decl_arena_state.* = decl_arena.state;
-        decl.typed_value = .{
-            .most_recent = .{
-                .typed_value = .{ .ty = fn_type, .val = fn_val },
-                .arena = decl_arena_state,
-            },
-        };
-        decl.analysis = .complete;
-        decl.generation = mod.generation;
-
-        try mod.comp.bin_file.allocateDeclIndexes(decl);
-        try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
-
-        if (type_changed and mod.emit_h != null) {
-            try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
-        }
-
-        return type_changed;
-    }
-
-    if (fn_type.fnIsVarArgs()) {
-        return mod.failNode(&block_scope.base, fn_proto.ast.fn_token, "non-extern function is variadic", .{});
-    }
-
-    const new_func = try decl_arena.allocator.create(Fn);
-    const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
-
-    const fn_zir: Zir = blk: {
-        // We put the ZIR inside the Decl arena.
-        var astgen = try AstGen.init(mod, decl, &decl_arena.allocator);
-        astgen.ref_start_index = @intCast(u32, Zir.Inst.Ref.typed_value_map.len + param_count);
-        defer astgen.deinit();
-
-        var gen_scope: Scope.GenZir = .{
-            .force_comptime = false,
-            .parent = &decl.namespace.base,
-            .astgen = &astgen,
-        };
-        defer gen_scope.instructions.deinit(mod.gpa);
-
-        // Iterate over the parameters. We put the param names as the first N
-        // items inside `extra` so that debug info later can refer to the parameter names
-        // even while the respective source code is unloaded.
-        try astgen.extra.ensureCapacity(mod.gpa, param_count);
-
-        var params_scope = &gen_scope.base;
-        var i: usize = 0;
-        var it = fn_proto.iterate(tree);
-        while (it.next()) |param| : (i += 1) {
-            const name_token = param.name_token.?;
-            const param_name = try mod.identifierTokenString(&gen_scope.base, name_token);
-            const sub_scope = try decl_arena.allocator.create(Scope.LocalVal);
-            sub_scope.* = .{
-                .parent = params_scope,
-                .gen_zir = &gen_scope,
-                .name = param_name,
-                // Implicit const list first, then implicit arg list.
-                .inst = @intToEnum(Zir.Inst.Ref, @intCast(u32, Zir.Inst.Ref.typed_value_map.len + i)),
-                .src = decl.tokSrcLoc(name_token),
-            };
-            params_scope = &sub_scope.base;
-
-            // Additionally put the param name into `string_bytes` and reference it with
-            // `extra` so that we have access to the data in codegen, for debug info.
-            const str_index = @intCast(u32, astgen.string_bytes.items.len);
-            astgen.extra.appendAssumeCapacity(str_index);
-            const used_bytes = astgen.string_bytes.items.len;
-            try astgen.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1);
-            astgen.string_bytes.appendSliceAssumeCapacity(param_name);
-            astgen.string_bytes.appendAssumeCapacity(0);
-        }
-
-        _ = try AstGen.expr(&gen_scope, params_scope, .none, body_node);
-
-        if (gen_scope.instructions.items.len == 0 or
-            !astgen.instructions.items(.tag)[gen_scope.instructions.items.len - 1]
-            .isNoReturn())
-        {
-            // astgen uses result location semantics to coerce return operands.
-            // Since we are adding the return instruction here, we must handle the coercion.
-            // We do this by using the `ret_coerce` instruction.
-            _ = try gen_scope.addUnTok(.ret_coerce, .void_value, tree.lastToken(body_node));
-        }
-
-        const code = try gen_scope.finish();
-        if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
-            code.dump(mod.gpa, "fn_body", &gen_scope.base, param_count) catch {};
-        }
-
-        break :blk code;
-    };
-
     const is_inline = fn_type.fnCallingConvention() == .Inline;
     const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued;
 
@@ -716,264 +494,11 @@ fn astgenAndSemaVarDecl(
     tree: ast.Tree,
     var_decl: ast.full.VarDecl,
 ) !bool {
-    const tracy = trace(@src());
-    defer tracy.end();
-
-    decl.analysis = .in_progress;
-    decl.is_pub = var_decl.visib_token != null;
-
     const token_tags = tree.tokens.items(.tag);
 
-    // We need the memory for the Type to go into the arena for the Decl
-    var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
-    errdefer decl_arena.deinit();
-    const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
-
-    // Used for simple error reporting.
-    var decl_scope: Scope.DeclRef = .{ .decl = decl };
-
-    const is_extern = blk: {
-        const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
-        break :blk token_tags[maybe_extern_token] == .keyword_extern;
-    };
-
-    if (var_decl.lib_name) |lib_name| {
-        assert(is_extern);
-        return mod.failTok(&decl_scope.base, lib_name, "TODO implement function library name", .{});
-    }
-    const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
-    const is_threadlocal = if (var_decl.threadlocal_token) |some| blk: {
-        if (!is_mutable) {
-            return mod.failTok(&decl_scope.base, some, "threadlocal variable cannot be constant", .{});
-        }
-        break :blk true;
-    } else false;
-    assert(var_decl.comptime_token == null);
-    if (var_decl.ast.align_node != 0) {
-        return mod.failNode(
-            &decl_scope.base,
-            var_decl.ast.align_node,
-            "TODO implement function align expression",
-            .{},
-        );
-    }
-    if (var_decl.ast.section_node != 0) {
-        return mod.failNode(
-            &decl_scope.base,
-            var_decl.ast.section_node,
-            "TODO implement function section expression",
-            .{},
-        );
-    }
-
-    const var_info: struct { ty: Type, val: ?Value } = if (var_decl.ast.init_node != 0) vi: {
-        if (is_extern) {
-            return mod.failNode(
-                &decl_scope.base,
-                var_decl.ast.init_node,
-                "extern variables have no initializers",
-                .{},
-            );
-        }
-
-        var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
-        defer gen_scope_arena.deinit();
-
-        var astgen = try AstGen.init(mod, decl, &gen_scope_arena.allocator);
-        defer astgen.deinit();
-
-        var gen_scope: Scope.GenZir = .{
-            .force_comptime = true,
-            .parent = &decl.namespace.base,
-            .astgen = &astgen,
-        };
-        defer gen_scope.instructions.deinit(mod.gpa);
-
-        const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{
-            .ty = try AstGen.expr(&gen_scope, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node),
-        } else .none;
-
-        const init_inst = try AstGen.comptimeExpr(
-            &gen_scope,
-            &gen_scope.base,
-            init_result_loc,
-            var_decl.ast.init_node,
-        );
-        _ = try gen_scope.addBreak(.break_inline, 0, init_inst);
-        var code = try gen_scope.finish();
-        defer code.deinit(mod.gpa);
-        if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
-            code.dump(mod.gpa, "var_init", &gen_scope.base, 0) catch {};
-        }
-
-        var sema: Sema = .{
-            .mod = mod,
-            .gpa = mod.gpa,
-            .arena = &gen_scope_arena.allocator,
-            .code = code,
-            .inst_map = try gen_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
-            .owner_decl = decl,
-            .namespace = decl.namespace,
-            .func = null,
-            .owner_func = null,
-            .param_inst_list = &.{},
-        };
-        var block_scope: Scope.Block = .{
-            .parent = null,
-            .sema = &sema,
-            .src_decl = decl,
-            .instructions = .{},
-            .inlining = null,
-            .is_comptime = true,
-        };
-        defer block_scope.instructions.deinit(mod.gpa);
-
-        const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
-        // The result location guarantees the type coercion.
-        const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);
-        // The is_comptime in the Scope.Block guarantees the result is comptime-known.
-        const val = analyzed_init_inst.value().?;
-
-        break :vi .{
-            .ty = try analyzed_init_inst.ty.copy(&decl_arena.allocator),
-            .val = try val.copy(&decl_arena.allocator),
-        };
-    } else if (!is_extern) {
-        return mod.failTok(
-            &decl_scope.base,
-            var_decl.ast.mut_token,
-            "variables must be initialized",
-            .{},
-        );
-    } else if (var_decl.ast.type_node != 0) vi: {
-        var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
-        defer type_scope_arena.deinit();
-
-        var astgen = try AstGen.init(mod, decl, &type_scope_arena.allocator);
-        defer astgen.deinit();
-
-        var type_scope: Scope.GenZir = .{
-            .force_comptime = true,
-            .parent = &decl.namespace.base,
-            .astgen = &astgen,
-        };
-        defer type_scope.instructions.deinit(mod.gpa);
-
-        const var_type = try AstGen.typeExpr(&type_scope, &type_scope.base, var_decl.ast.type_node);
-        _ = try type_scope.addBreak(.break_inline, 0, var_type);
-
-        var code = try type_scope.finish();
-        defer code.deinit(mod.gpa);
-        if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
-            code.dump(mod.gpa, "var_type", &type_scope.base, 0) catch {};
-        }
-
-        var sema: Sema = .{
-            .mod = mod,
-            .gpa = mod.gpa,
-            .arena = &type_scope_arena.allocator,
-            .code = code,
-            .inst_map = try type_scope_arena.allocator.alloc(*ir.Inst, code.instructions.len),
-            .owner_decl = decl,
-            .namespace = decl.namespace,
-            .func = null,
-            .owner_func = null,
-            .param_inst_list = &.{},
-        };
-        var block_scope: Scope.Block = .{
-            .parent = null,
-            .sema = &sema,
-            .src_decl = decl,
-            .instructions = .{},
-            .inlining = null,
-            .is_comptime = true,
-        };
-        defer block_scope.instructions.deinit(mod.gpa);
-
-        const ty = try sema.rootAsType(&block_scope);
-
-        break :vi .{
-            .ty = try ty.copy(&decl_arena.allocator),
-            .val = null,
-        };
-    } else {
-        return mod.failTok(
-            &decl_scope.base,
-            var_decl.ast.mut_token,
-            "unable to infer variable type",
-            .{},
-        );
-    };
-
-    if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
-        return mod.failTok(
-            &decl_scope.base,
-            var_decl.ast.mut_token,
-            "variable of type '{}' must be const",
-            .{var_info.ty},
-        );
-    }
-
-    var type_changed = true;
-    if (decl.typedValueManaged()) |tvm| {
-        type_changed = !tvm.typed_value.ty.eql(var_info.ty);
-
-        tvm.deinit(mod.gpa);
-    }
-
-    const new_variable = try decl_arena.allocator.create(Var);
-    new_variable.* = .{
-        .owner_decl = decl,
-        .init = var_info.val orelse undefined,
-        .is_extern = is_extern,
-        .is_mutable = is_mutable,
-        .is_threadlocal = is_threadlocal,
-    };
-    const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
-
-    decl_arena_state.* = decl_arena.state;
-    decl.typed_value = .{
-        .most_recent = .{
-            .typed_value = .{
-                .ty = var_info.ty,
-                .val = var_val,
-            },
-            .arena = decl_arena_state,
-        },
-    };
-    decl.analysis = .complete;
-    decl.generation = mod.generation;
-
-    if (var_decl.extern_export_token) |maybe_export_token| {
-        if (token_tags[maybe_export_token] == .keyword_export) {
-            const export_src = decl.tokSrcLoc(maybe_export_token);
-            const name_token = var_decl.ast.mut_token + 1;
-            const name = tree.tokenSlice(name_token); // TODO identifierTokenString
-            // The scope needs to have the decl in it.
-            try mod.analyzeExport(&decl_scope.base, export_src, name, decl);
-        }
-    }
-    return type_changed;
 }
 
 
-/// Call `deinit` on the result.
-pub fn init(mod: *Module, decl: *Decl, arena: *Allocator) !AstGen {
-    var astgen: AstGen = .{
-        .mod = mod,
-        .decl = decl,
-        .arena = arena,
-    };
-    // Must be a block instruction at index 0 with the root body.
-    try astgen.instructions.append(mod.gpa, .{
-        .tag = .block,
-        .data = .{ .pl_node = .{
-            .src_node = 0,
-            .payload_index = undefined,
-        } },
-    });
-    return astgen;
-}
     /// Asserts the scope is a child of a File and has an AST tree and returns the tree.
     pub fn tree(scope: *Scope) *const ast.Tree {
         switch (scope.tag) {
@@ -1181,26 +706,6 @@ fn errorSetDecl(
 }
 
 
-/// The string is stored in `arena` regardless of whether it uses @"" syntax.
-pub fn identifierTokenStringTreeArena(
-    astgen: *AstGen,
-    token: ast.TokenIndex,
-    tree: *const ast.Tree,
-    arena: *Allocator,
-) InnerError![]u8 {
-    const token_tags = tree.tokens.items(.tag);
-    assert(token_tags[token] == .identifier);
-    const ident_name = tree.tokenSlice(token);
-    if (!mem.startsWith(u8, ident_name, "@")) {
-        return arena.dupe(u8, ident_name);
-    }
-    var buf: ArrayListUnmanaged(u8) = .{};
-    defer buf.deinit(astgen.gpa);
-    try astgen.parseStrLit(token, &buf, ident_name, 1);
-    return arena.dupe(u8, buf.items);
-}
-
-
 
     if (mod.lookupIdentifier(scope, ident_name)) |decl| {
         const msg = msg: {
@@ -1217,3 +722,54 @@ pub fn identifierTokenStringTreeArena(
         return mod.failWithOwnedErrorMsg(scope, msg);
     }
 
+
+    var type_changed = true;
+    if (decl.typedValueManaged()) |tvm| {
+        type_changed = !tvm.typed_value.ty.eql(var_info.ty);
+
+        tvm.deinit(mod.gpa);
+    }
+
+    const new_variable = try decl_arena.allocator.create(Var);
+    new_variable.* = .{
+        .owner_decl = decl,
+        .init = var_info.val orelse undefined,
+        .is_extern = is_extern,
+        .is_mutable = is_mutable,
+        .is_threadlocal = is_threadlocal,
+    };
+    const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable);
+
+    decl_arena_state.* = decl_arena.state;
+    decl.typed_value = .{
+        .most_recent = .{
+            .typed_value = .{
+                .ty = var_info.ty,
+                .val = var_val,
+            },
+            .arena = decl_arena_state,
+        },
+    };
+    decl.analysis = .complete;
+    decl.generation = mod.generation;
+
+
+
+    if (is_mutable and !var_info.ty.isValidVarType(is_extern)) {
+        return mod.failTok(
+            &decl_scope.base,
+            var_decl.ast.mut_token,
+            "variable of type '{}' must be const",
+            .{var_info.ty},
+        );
+    }
+
+    if (var_decl.extern_export_token) |maybe_export_token| {
+        if (token_tags[maybe_export_token] == .keyword_export) {
+            const export_src = decl.tokSrcLoc(maybe_export_token);
+            const name_token = var_decl.ast.mut_token + 1;
+            const name = tree.tokenSlice(name_token); // TODO identifierTokenString
+            // The scope needs to have the decl in it.
+            try mod.analyzeExport(&decl_scope.base, export_src, name, decl);
+        }
+    }