diff --git a/src/InternPool.zig b/src/InternPool.zig index bffed4f38e..752bc8adb8 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -2136,6 +2136,7 @@ pub const Key = union(enum) { /// To avoid making this key overly complex, the type-specific data is hashed by Sema. reified: struct { /// A `reify`, `struct_init`, `struct_init_ref`, or `struct_init_anon` instruction. + /// Alternatively, this is `main_struct_inst` of a ZON file. zir_index: TrackedInst.Index, /// A hash of this type's attributes, fields, etc, generated by Sema. type_hash: u64, diff --git a/src/Sema.zig b/src/Sema.zig index 54be47f584..40c170cbe7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2998,7 +2998,7 @@ fn zirStructDecl( return Air.internedToRef(wip_ty.finish(ip, new_namespace_index)); } -fn createTypeName( +pub fn createTypeName( sema: *Sema, block: *Block, name_strategy: Zir.Inst.NameStrategy, @@ -14065,14 +14065,13 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return Air.internedToRef(ty); }, .zon => { - if (extra.res_ty == .none) { - return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{}); - } - const res_ty_inst = try sema.resolveInst(extra.res_ty); - const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst); - if (res_ty.isGenericPoison()) { - return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{}); - } + const res_ty: InternPool.Index = b: { + if (extra.res_ty == .none) break :b .none; + const res_ty_inst = try sema.resolveInst(extra.res_ty); + const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst); + if (res_ty.isGenericPoison()) break :b .none; + break :b res_ty.toIntern(); + }; try sema.declareDependency(.{ .zon_file = result.file_index }); const interned = try LowerZon.run( @@ -31699,7 +31698,7 @@ fn addReferenceEntry( try zcu.addUnitReference(sema.owner, referenced_unit, src); } -fn addTypeReferenceEntry( +pub fn addTypeReferenceEntry( sema: *Sema, src: LazySrcLoc, referenced_type: InternPool.Index, diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index f30879090b..77065a07e8 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -33,7 +33,7 @@ pub fn run( sema: *Sema, file: *File, file_index: Zcu.File.Index, - res_ty: Type, + res_ty_interned: InternPool.Index, import_loc: LazySrcLoc, block: *Sema.Block, ) CompileError!InternPool.Index { @@ -53,13 +53,167 @@ pub fn run( .base_node_inst = tracked_inst, }; - try lower_zon.checkType(res_ty); - - return lower_zon.lowerExpr(.root, res_ty); + if (res_ty_interned == .none) { + return lower_zon.lowerExprAnonResTy(.root); + } else { + const res_ty: Type = .fromInterned(res_ty_interned); + try lower_zon.checkType(res_ty); + return lower_zon.lowerExprKnownResTy(.root, res_ty); + } } -/// Validate that `ty` is a valid ZON type. If not, emit a compile error. -/// i.e. no nested optionals, no error sets, etc. +fn lowerExprAnonResTy(self: *LowerZon, node: Zoir.Node.Index) CompileError!InternPool.Index { + const gpa = self.sema.gpa; + const pt = self.sema.pt; + const ip = &pt.zcu.intern_pool; + switch (node.get(self.file.zoir.?)) { + .true => return .bool_true, + .false => return .bool_false, + .null => return .null_value, + .pos_inf => return self.fail(node, "infinity requires a known result type", .{}), + .neg_inf => return self.fail(node, "negative infinity requires a known result type", .{}), + .nan => return self.fail(node, "NaN requires a known result type", .{}), + .int_literal => |int| switch (int) { + .small => |val| return pt.intern(.{ .int = .{ + .ty = .comptime_int_type, + .storage = .{ .i64 = val }, + } }), + .big => |val| return pt.intern(.{ .int = .{ + .ty = .comptime_int_type, + .storage = .{ .big_int = val }, + } }), + }, + .float_literal => |val| { + const result = try pt.floatValue(.comptime_float, val); + return result.toIntern(); + }, + .char_literal => |val| return pt.intern(.{ .int = .{ + .ty = .comptime_int_type, + .storage = .{ .i64 = val }, + } }), + .enum_literal => |val| return pt.intern(.{ + .enum_literal = try ip.getOrPutString( + gpa, + pt.tid, + val.get(self.file.zoir.?), + .no_embedded_nulls, + ), + }), + .string_literal => |val| { + const ip_str = try ip.getOrPutString(gpa, pt.tid, val, .maybe_embedded_nulls); + const result = try self.sema.addStrLit(ip_str, val.len); + return result.toInterned().?; + }, + .empty_literal => return .empty_tuple, + .array_literal => |nodes| { + const types = try self.sema.arena.alloc(InternPool.Index, nodes.len); + const values = try self.sema.arena.alloc(InternPool.Index, nodes.len); + for (0..nodes.len) |i| { + values[i] = try self.lowerExprAnonResTy(nodes.at(@intCast(i))); + types[i] = Value.fromInterned(values[i]).typeOf(pt.zcu).toIntern(); + } + const ty = try ip.getTupleType( + gpa, + pt.tid, + .{ + .types = types, + .values = values, + }, + ); + return pt.intern(.{ .aggregate = .{ + .ty = ty, + .storage = .{ .elems = values }, + } }); + }, + .struct_literal => |init| { + const elems = try self.sema.arena.alloc(InternPool.Index, init.names.len); + for (0..init.names.len) |i| { + elems[i] = try self.lowerExprAnonResTy(init.vals.at(@intCast(i))); + } + const struct_ty = switch (try ip.getStructType( + gpa, + pt.tid, + .{ + .layout = .auto, + .fields_len = @intCast(init.names.len), + .known_non_opv = false, + .requires_comptime = .no, + .any_comptime_fields = true, + .any_default_inits = true, + .inits_resolved = true, + .any_aligned_fields = false, + .key = .{ .reified = .{ + .zir_index = self.base_node_inst, + .type_hash = hash: { + var hasher: std.hash.Wyhash = .init(0); + hasher.update(std.mem.asBytes(&node)); + hasher.update(std.mem.sliceAsBytes(elems)); + hasher.update(std.mem.sliceAsBytes(init.names)); + break :hash hasher.final(); + }, + } }, + }, + false, + )) { + .wip => |wip| ty: { + errdefer wip.cancel(ip, pt.tid); + wip.setName(ip, try self.sema.createTypeName( + self.block, + .anon, + "struct", + self.base_node_inst.resolve(ip), + wip.index, + )); + + const struct_type = ip.loadStructType(wip.index); + + for (init.names, 0..) |name, field_idx| { + const name_interned = try ip.getOrPutString( + gpa, + pt.tid, + name.get(self.file.zoir.?), + .no_embedded_nulls, + ); + assert(struct_type.addFieldName(ip, name_interned) == null); + struct_type.setFieldComptime(ip, field_idx); + } + + @memcpy(struct_type.field_inits.get(ip), elems); + const types = struct_type.field_types.get(ip); + for (0..init.names.len) |i| { + types[i] = Value.fromInterned(elems[i]).typeOf(pt.zcu).toIntern(); + } + + const new_namespace_index = try pt.createNamespace(.{ + .parent = self.block.namespace.toOptional(), + .owner_type = wip.index, + .file_scope = self.block.getFileScopeIndex(pt.zcu), + .generation = pt.zcu.generation, + }); + try pt.zcu.comp.queueJob(.{ .resolve_type_fully = wip.index }); + codegen_type: { + if (pt.zcu.comp.config.use_llvm) break :codegen_type; + if (self.block.ownerModule().strip) break :codegen_type; + try pt.zcu.comp.queueJob(.{ .codegen_type = wip.index }); + } + break :ty wip.finish(ip, new_namespace_index); + }, + .existing => |ty| ty, + }; + try self.sema.declareDependency(.{ .interned = struct_ty }); + try self.sema.addTypeReferenceEntry(self.nodeSrc(node), struct_ty); + + return try pt.intern(.{ .aggregate = .{ + .ty = struct_ty, + .storage = .{ .elems = elems }, + } }); + }, + } +} + +/// Validate that `ty` is a valid ZON type, or emit a compile error. +/// +/// Rules out nested optionals, error sets, etc. fn checkType(self: *LowerZon, ty: Type) !void { var visited: std.AutoHashMapUnmanaged(InternPool.Index, void) = .empty; try self.checkTypeInner(ty, null, &visited); @@ -201,9 +355,9 @@ fn fail( return self.sema.failWithOwnedErrorMsg(self.block, err_msg); } -fn lowerExpr(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index { +fn lowerExprKnownResTy(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index { const pt = self.sema.pt; - return self.lowerExprInner(node, res_ty) catch |err| switch (err) { + return self.lowerExprKnownResTyInner(node, res_ty) catch |err| switch (err) { error.WrongType => return self.fail( node, "expected type '{}'", @@ -213,7 +367,7 @@ fn lowerExpr(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError! }; } -fn lowerExprInner( +fn lowerExprKnownResTyInner( self: *LowerZon, node: Zoir.Node.Index, res_ty: Type, @@ -227,7 +381,7 @@ fn lowerExprInner( break :b .none; } else b: { const child_type = res_ty.optionalChild(pt.zcu); - break :b try self.lowerExprInner(node, child_type); + break :b try self.lowerExprKnownResTyInner(node, child_type); }, }, }), @@ -239,7 +393,7 @@ fn lowerExprInner( .base_addr = .{ .uav = .{ .orig_ty = res_ty.toIntern(), - .val = try self.lowerExprInner(node, .fromInterned(ptr_info.child)), + .val = try self.lowerExprKnownResTyInner(node, .fromInterned(ptr_info.child)), }, }, .byte_offset = 0, @@ -486,7 +640,7 @@ fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. ); for (0..nodes.len) |i| { - elems[i] = try self.lowerExpr(nodes.at(@intCast(i)), array_info.elem_type); + elems[i] = try self.lowerExprKnownResTy(nodes.at(@intCast(i)), array_info.elem_type); } if (array_info.sentinel) |sentinel| { @@ -587,7 +741,7 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. ); } - const val = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i])); + const val = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i])); if (elems[i] != .none and val != elems[i]) { const elem_node = elem_nodes.at(@intCast(i)); @@ -650,7 +804,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool }; const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]); - field_values[name_index] = try self.lowerExpr(field_node, field_type); + field_values[name_index] = try self.lowerExprKnownResTy(field_node, field_type); if (struct_info.comptime_bits.getBit(ip, name_index)) { const val = ip.indexToKey(field_values[name_index]); @@ -715,7 +869,7 @@ fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. const elems = try self.sema.arena.alloc(InternPool.Index, elem_nodes.len + @intFromBool(ptr_info.sentinel != .none)); for (elems, 0..) |*elem, i| { - elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child)); + elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child)); } if (ptr_info.sentinel != .none) { @@ -810,7 +964,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool. if (field_type.toIntern() == .void_type) { return self.fail(field_node, "expected type 'void'", .{}); } - break :b try self.lowerExpr(field_node, field_type); + break :b try self.lowerExprKnownResTy(field_node, field_type); } else b: { if (field_type.toIntern() != .void_type) { return error.WrongType; @@ -846,7 +1000,7 @@ fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool } for (elems, 0..) |*elem, i| { - elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child)); + elem.* = try self.lowerExprKnownResTy(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child)); } return self.sema.pt.intern(.{ .aggregate = .{ diff --git a/src/Type.zig b/src/Type.zig index 3b31337587..ed0df5965f 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -3589,7 +3589,10 @@ pub fn typeDeclSrcLine(ty: Type, zcu: *Zcu) ?u32 { }; const info = tracked.resolveFull(&zcu.intern_pool) orelse return null; const file = zcu.fileByIndex(info.file); - const zir = file.zir.?; + const zir = switch (file.getMode()) { + .zig => file.zir.?, + .zon => return 0, + }; const inst = zir.instructions.get(@intFromEnum(info.inst)); return switch (inst.tag) { .struct_init, .struct_init_ref => zir.extraData(Zir.Inst.StructInit, inst.data.pl_node.payload_index).data.abs_line, diff --git a/test/behavior/zon.zig b/test/behavior/zon.zig index 2003a87600..8a43b4894e 100644 --- a/test/behavior/zon.zig +++ b/test/behavior/zon.zig @@ -517,3 +517,57 @@ test "recursive" { const expected: Recursive = .{ .foo = &.{ .foo = null } }; try expectEqualDeep(expected, @as(Recursive, @import("zon/recursive.zon"))); } + +test "anon" { + const expected = .{ + .{ + .bool_true = true, + .bool_false = false, + .string = "foo", + }, + .{ + null, + 10, + 36893488147419103232, + 1.234, + 'z', + .bar, + .{}, + }, + }; + + const actual = @import("zon/anon.zon"); + try expectEqual(expected.len, actual.len); + try expectEqual(expected[1], actual[1]); + const expected_struct = expected[0]; + const actual_struct = actual[0]; + const expected_fields = @typeInfo(@TypeOf(expected_struct)).@"struct".fields; + const actual_fields = @typeInfo(@TypeOf(actual_struct)).@"struct".fields; + try expectEqual(expected_fields.len, actual_fields.len); + inline for (expected_fields) |field| { + try expectEqual(@field(expected_struct, field.name), @field(actual_struct, field.name)); + } +} + +test "build.zig.zon" { + const build = @import("zon/build.zig.zon"); + + try expectEqual(4, @typeInfo(@TypeOf(build)).@"struct".fields.len); + try expectEqualStrings("temp", build.name); + try expectEqualStrings("0.0.0", build.version); + + const dependencies = build.dependencies; + try expectEqual(2, @typeInfo(@TypeOf(dependencies)).@"struct".fields.len); + + const example_0 = dependencies.example_0; + try expectEqual(2, @typeInfo(@TypeOf(dependencies)).@"struct".fields.len); + try expectEqualStrings("https://example.com/foo.tar.gz", example_0.url); + try expectEqualStrings("...", example_0.hash); + + const example_1 = dependencies.example_1; + try expectEqual(2, @typeInfo(@TypeOf(dependencies)).@"struct".fields.len); + try expectEqualStrings("../foo", example_1.path); + try expectEqual(false, example_1.lazy); + + try expectEqual(.{ "build.zig", "build.zig.zon", "src" }, build.paths); +} diff --git a/test/behavior/zon/anon.zon b/test/behavior/zon/anon.zon new file mode 100644 index 0000000000..6a434cf627 --- /dev/null +++ b/test/behavior/zon/anon.zon @@ -0,0 +1,16 @@ +.{ + .{ + .bool_true = true, + .bool_false = false, + .string = "foo", + }, + .{ + null, + 10, + 36893488147419103232, + 1.234, + 'z', + .bar, + .{}, + }, +} diff --git a/test/behavior/zon/build.zig.zon b/test/behavior/zon/build.zig.zon new file mode 100644 index 0000000000..b77d742549 --- /dev/null +++ b/test/behavior/zon/build.zig.zon @@ -0,0 +1,20 @@ +.{ + // Comment + .name = "temp", + .version = "0.0.0", + .dependencies = .{ + .example_0 = .{ + .url = "https://example.com/foo.tar.gz", + .hash = "...", + }, + .example_1 = .{ + .path = "../foo", + .lazy = false, + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} diff --git a/test/cases/compile_errors/@import_zon_anon_inf.zig b/test/cases/compile_errors/@import_zon_anon_inf.zig new file mode 100644 index 0000000000..96bc0f794d --- /dev/null +++ b/test/cases/compile_errors/@import_zon_anon_inf.zig @@ -0,0 +1,9 @@ +export fn entry() void { + _ = @import("zon/inf.zon"); +} + +// error +// imports=zon/inf.zon +// +// inf.zon:1:1: error: infinity requires a known result type +// tmp.zig:2:17: note: imported here diff --git a/test/cases/compile_errors/@import_zon_anon_nan.zig b/test/cases/compile_errors/@import_zon_anon_nan.zig new file mode 100644 index 0000000000..b2d61613b4 --- /dev/null +++ b/test/cases/compile_errors/@import_zon_anon_nan.zig @@ -0,0 +1,9 @@ +export fn entry() void { + _ = @import("zon/nan.zon"); +} + +// error +// imports=zon/nan.zon +// +// nan.zon:1:1: error: NaN requires a known result type +// tmp.zig:2:17: note: imported here diff --git a/test/cases/compile_errors/@import_zon_anon_neg_inf.zig b/test/cases/compile_errors/@import_zon_anon_neg_inf.zig new file mode 100644 index 0000000000..f7567969f9 --- /dev/null +++ b/test/cases/compile_errors/@import_zon_anon_neg_inf.zig @@ -0,0 +1,9 @@ +export fn entry() void { + _ = @import("zon/neg_inf.zon"); +} + +// error +// imports=zon/neg_inf.zon +// +// neg_inf.zon:1:1: error: negative infinity requires a known result type +// tmp.zig:2:17: note: imported here diff --git a/test/cases/compile_errors/@import_zon_no_rt.zig b/test/cases/compile_errors/@import_zon_no_rt.zig deleted file mode 100644 index b7518b0cb1..0000000000 --- a/test/cases/compile_errors/@import_zon_no_rt.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn entry() void { - const f = @import("zon/simple_union.zon"); - _ = f; -} - -// error -// imports=zon/simple_union.zon -// -// tmp.zig:2:23: error: '@import' of ZON must have a known result type