| ... | ... | @@ -320,6 +320,12 @@ pub const Fn = struct { |
| 320 | 320 | } |
| 321 | 321 | }; |
| 322 | 322 | |
| 323 | pub const Var = struct { |
| 324 | value: ?Value, |
| 325 | owner_decl: *Decl, |
| 326 | is_mutable: bool, |
| 327 | }; |
| 328 | |
| 323 | 329 | pub const Scope = struct { |
| 324 | 330 | tag: Tag, |
| 325 | 331 | |
| ... | ... | @@ -1419,7 +1425,152 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1419 | 1425 | } |
| 1420 | 1426 | return type_changed; |
| 1421 | 1427 | }, |
| 1422 | | .VarDecl => @panic("TODO var decl"), |
| 1428 | .VarDecl => { |
| 1429 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); |
| 1430 | |
| 1431 | decl.analysis = .in_progress; |
| 1432 | |
| 1433 | const is_extern = blk: { |
| 1434 | const maybe_extern_token = var_decl.getTrailer("extern_export_token") orelse |
| 1435 | break :blk false; |
| 1436 | break :blk tree.token_ids[maybe_extern_token] == .Keyword_extern; |
| 1437 | }; |
| 1438 | const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; |
| 1439 | |
| 1440 | // We need the memory for the Type to go into the arena for the Decl |
| 1441 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); |
| 1442 | errdefer decl_arena.deinit(); |
| 1443 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1444 | |
| 1445 | var block_scope: Scope.Block = .{ |
| 1446 | .parent = null, |
| 1447 | .func = null, |
| 1448 | .decl = decl, |
| 1449 | .instructions = .{}, |
| 1450 | .arena = &decl_arena.allocator, |
| 1451 | }; |
| 1452 | defer block_scope.instructions.deinit(self.gpa); |
| 1453 | |
| 1454 | const explicit_type = blk: { |
| 1455 | const type_node = var_decl.getTrailer("type_node") orelse |
| 1456 | break :blk null; |
| 1457 | |
| 1458 | var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); |
| 1459 | defer type_scope_arena.deinit(); |
| 1460 | var type_scope: Scope.GenZIR = .{ |
| 1461 | .decl = decl, |
| 1462 | .arena = &type_scope_arena.allocator, |
| 1463 | .parent = decl.scope, |
| 1464 | }; |
| 1465 | defer type_scope.instructions.deinit(self.gpa); |
| 1466 | |
| 1467 | const src = tree.token_locs[type_node.firstToken()].start; |
| 1468 | const type_type = try astgen.addZIRInstConst(self, &type_scope.base, src, .{ |
| 1469 | .ty = Type.initTag(.type), |
| 1470 | .val = Value.initTag(.type_type), |
| 1471 | }); |
| 1472 | const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node); |
| 1473 | _ = try astgen.addZIRUnOp(self, &type_scope.base, src, .@"return", var_type); |
| 1474 | |
| 1475 | break :blk try zir_sema.analyzeBodyValueAsType(self, &block_scope, .{ |
| 1476 | .instructions = type_scope.instructions.items, |
| 1477 | }); |
| 1478 | }; |
| 1479 | |
| 1480 | var var_type: Type = undefined; |
| 1481 | const value: ?Value = if (var_decl.getTrailer("init_node")) |init_node| blk: { |
| 1482 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); |
| 1483 | defer gen_scope_arena.deinit(); |
| 1484 | var gen_scope: Scope.GenZIR = .{ |
| 1485 | .decl = decl, |
| 1486 | .arena = &gen_scope_arena.allocator, |
| 1487 | .parent = decl.scope, |
| 1488 | }; |
| 1489 | defer gen_scope.instructions.deinit(self.gpa); |
| 1490 | const src = tree.token_locs[init_node.firstToken()].start; |
| 1491 | |
| 1492 | // TODO comptime scope here |
| 1493 | const init_inst = try astgen.expr(self, &gen_scope.base, .none, init_node); |
| 1494 | _ = try astgen.addZIRUnOp(self, &gen_scope.base, src, .@"return", init_inst); |
| 1495 | |
| 1496 | var inner_block: Scope.Block = .{ |
| 1497 | .parent = null, |
| 1498 | .func = null, |
| 1499 | .decl = decl, |
| 1500 | .instructions = .{}, |
| 1501 | .arena = &gen_scope_arena.allocator, |
| 1502 | }; |
| 1503 | defer inner_block.instructions.deinit(self.gpa); |
| 1504 | try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items }); |
| 1505 | |
| 1506 | for (inner_block.instructions.items) |inst| { |
| 1507 | if (inst.castTag(.ret)) |ret| { |
| 1508 | const coerced = if (explicit_type) |some| |
| 1509 | try self.coerce(&inner_block.base, some, ret.operand) |
| 1510 | else |
| 1511 | ret.operand; |
| 1512 | const val = try self.resolveConstValue(&inner_block.base, coerced); |
| 1513 | |
| 1514 | var_type = explicit_type orelse try ret.operand.ty.copy(block_scope.arena); |
| 1515 | break :blk try val.copy(block_scope.arena); |
| 1516 | } else { |
| 1517 | return self.fail(&block_scope.base, inst.src, "unable to resolve comptime value", .{}); |
| 1518 | } |
| 1519 | } |
| 1520 | unreachable; |
| 1521 | } else if (!is_extern) { |
| 1522 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); |
| 1523 | } else if (explicit_type) |some| blk: { |
| 1524 | var_type = some; |
| 1525 | break :blk null; |
| 1526 | } else { |
| 1527 | return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); |
| 1528 | }; |
| 1529 | |
| 1530 | if (is_mutable and !var_type.isValidVarType(is_extern)) { |
| 1531 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_type}); |
| 1532 | } |
| 1533 | |
| 1534 | var type_changed = true; |
| 1535 | if (decl.typedValueManaged()) |tvm| { |
| 1536 | type_changed = !tvm.typed_value.ty.eql(var_type); |
| 1537 | |
| 1538 | tvm.deinit(self.gpa); |
| 1539 | } |
| 1540 | |
| 1541 | const new_variable = try decl_arena.allocator.create(Var); |
| 1542 | const var_payload = try decl_arena.allocator.create(Value.Payload.Variable); |
| 1543 | new_variable.* = .{ |
| 1544 | .value = value, |
| 1545 | .owner_decl = decl, |
| 1546 | .is_mutable = is_mutable, |
| 1547 | }; |
| 1548 | var_payload.* = .{ .variable = new_variable }; |
| 1549 | |
| 1550 | decl_arena_state.* = decl_arena.state; |
| 1551 | decl.typed_value = .{ |
| 1552 | .most_recent = .{ |
| 1553 | .typed_value = .{ |
| 1554 | .ty = var_type, |
| 1555 | .val = Value.initPayload(&var_payload.base), |
| 1556 | }, |
| 1557 | .arena = decl_arena_state, |
| 1558 | }, |
| 1559 | }; |
| 1560 | decl.analysis = .complete; |
| 1561 | decl.generation = self.generation; |
| 1562 | |
| 1563 | if (var_decl.getTrailer("extern_export_token")) |maybe_export_token| { |
| 1564 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| 1565 | const export_src = tree.token_locs[maybe_export_token].start; |
| 1566 | const name_loc = tree.token_locs[var_decl.name_token]; |
| 1567 | const name = tree.tokenSliceLoc(name_loc); |
| 1568 | // The scope needs to have the decl in it. |
| 1569 | try self.analyzeExport(&block_scope.base, export_src, name, decl); |
| 1570 | } |
| 1571 | } |
| 1572 | return type_changed; |
| 1573 | }, |
| 1423 | 1574 | .Comptime => @panic("TODO comptime decl"), |
| 1424 | 1575 | .Use => @panic("TODO usingnamespace decl"), |
| 1425 | 1576 | else => unreachable, |
| ... | ... | @@ -1584,7 +1735,32 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1584 | 1735 | } |
| 1585 | 1736 | } |
| 1586 | 1737 | } else if (src_decl.castTag(.VarDecl)) |var_decl| { |
| 1587 | | log.err("TODO: analyze var decl", .{}); |
| 1738 | const name_loc = tree.token_locs[var_decl.name_token]; |
| 1739 | const name = tree.tokenSliceLoc(name_loc); |
| 1740 | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| 1741 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| 1742 | if (self.decl_table.get(name_hash)) |decl| { |
| 1743 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| 1744 | // have been re-ordered. |
| 1745 | decl.src_index = decl_i; |
| 1746 | if (deleted_decls.remove(decl) == null) { |
| 1747 | decl.analysis = .sema_failure; |
| 1748 | const err_msg = try ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name}); |
| 1749 | errdefer err_msg.destroy(self.gpa); |
| 1750 | try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); |
| 1751 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { |
| 1752 | try self.markOutdatedDecl(decl); |
| 1753 | decl.contents_hash = contents_hash; |
| 1754 | } |
| 1755 | } else { |
| 1756 | const new_decl = try self.createNewDecl(&root_scope.base, name, decl_i, name_hash, contents_hash); |
| 1757 | root_scope.decls.appendAssumeCapacity(new_decl); |
| 1758 | if (var_decl.getTrailer("extern_export_token")) |maybe_export_token| { |
| 1759 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| 1760 | self.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 1761 | } |
| 1762 | } |
| 1763 | } |
| 1588 | 1764 | } else if (src_decl.castTag(.Comptime)) |comptime_node| { |
| 1589 | 1765 | log.err("TODO: analyze comptime decl", .{}); |
| 1590 | 1766 | } else if (src_decl.castTag(.ContainerField)) |container_field| { |
| ... | ... | @@ -2217,20 +2393,46 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn |
| 2217 | 2393 | }; |
| 2218 | 2394 | |
| 2219 | 2395 | const decl_tv = try decl.typedValue(); |
| 2220 | | const ty_payload = try scope.arena().create(Type.Payload.Pointer); |
| 2221 | | ty_payload.* = .{ |
| 2222 | | .base = .{ .tag = .single_const_pointer }, |
| 2223 | | .pointee_type = decl_tv.ty, |
| 2224 | | }; |
| 2396 | if (decl_tv.val.tag() == .variable) { |
| 2397 | return self.getVarRef(scope, src, decl_tv); |
| 2398 | } |
| 2399 | const ty = try self.singlePtrType(scope, src, false, decl_tv.ty); |
| 2225 | 2400 | const val_payload = try scope.arena().create(Value.Payload.DeclRef); |
| 2226 | 2401 | val_payload.* = .{ .decl = decl }; |
| 2227 | 2402 | |
| 2228 | 2403 | return self.constInst(scope, src, .{ |
| 2229 | | .ty = Type.initPayload(&ty_payload.base), |
| 2404 | .ty = ty, |
| 2230 | 2405 | .val = Value.initPayload(&val_payload.base), |
| 2231 | 2406 | }); |
| 2232 | 2407 | } |
| 2233 | 2408 | |
| 2409 | fn getVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) InnerError!*Inst { |
| 2410 | const variable = tv.val.cast(Value.Payload.Variable).?.variable; |
| 2411 | |
| 2412 | const ty = try self.singlePtrType(scope, src, variable.is_mutable, tv.ty); |
| 2413 | if (!variable.is_mutable and variable.value != null) { |
| 2414 | const val_payload = try scope.arena().create(Value.Payload.RefVal); |
| 2415 | val_payload.* = .{ .val = variable.value.? }; |
| 2416 | return self.constInst(scope, src, .{ |
| 2417 | .ty = ty, |
| 2418 | .val = Value.initPayload(&val_payload.base), |
| 2419 | }); |
| 2420 | } |
| 2421 | |
| 2422 | const b = try self.requireRuntimeBlock(scope, src); |
| 2423 | const inst = try b.arena.create(Inst.VarPtr); |
| 2424 | inst.* = .{ |
| 2425 | .base = .{ |
| 2426 | .tag = .varptr, |
| 2427 | .ty = ty, |
| 2428 | .src = src, |
| 2429 | }, |
| 2430 | .variable = variable, |
| 2431 | }; |
| 2432 | try b.instructions.append(self.gpa, &inst.base); |
| 2433 | return &inst.base; |
| 2434 | } |
| 2435 | |
| 2234 | 2436 | pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst { |
| 2235 | 2437 | const elem_ty = switch (ptr.ty.zigTypeTag()) { |
| 2236 | 2438 | .Pointer => ptr.ty.elemType(), |