diff --git a/src/Module.zig b/src/Module.zig index ee9b3e50bc..c200c83c10 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -708,7 +708,9 @@ pub const Decl = struct { return ty.castTag(.empty_struct).?.data; }, .@"opaque" => { - @panic("TODO opaque types"); + const opaque_obj = ty.cast(Type.Payload.Opaque).?.data; + assert(opaque_obj.owner_decl == decl); + return &opaque_obj.namespace; }, .@"union", .union_tagged => { const union_obj = ty.cast(Type.Payload.Union).?.data; @@ -1080,6 +1082,27 @@ pub const Union = struct { } }; +pub const Opaque = struct { + /// The Decl that corresponds to the opaque itself. + owner_decl: *Decl, + /// Represents the declarations inside this opaque. + namespace: Namespace, + /// Offset from `owner_decl`, points to the opaque decl AST node. + node_offset: i32, + + pub fn srcLoc(self: Opaque) SrcLoc { + return .{ + .file_scope = self.owner_decl.getFileScope(), + .parent_decl_node = self.owner_decl.src_node, + .lazy = .{ .node_offset = self.node_offset }, + }; + } + + pub fn getFullyQualifiedName(s: *Opaque, gpa: *Allocator) ![:0]u8 { + return s.owner_decl.getFullyQualifiedName(gpa); + } +}; + /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. /// Extern functions do not have this data structure; they are represented by /// the `Decl` only, with a `Value` tag of `extern_fn`. diff --git a/src/Sema.zig b/src/Sema.zig index 724fd48e99..4485475eaf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -957,7 +957,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .struct_decl => return sema.zirStructDecl( block, extended, inst), .enum_decl => return sema.zirEnumDecl( block, extended), .union_decl => return sema.zirUnionDecl( block, extended, inst), - .opaque_decl => return sema.zirOpaqueDecl( block, extended, inst), + .opaque_decl => return sema.zirOpaqueDecl( block, extended), .ret_ptr => return sema.zirRetPtr( block, extended), .ret_type => return sema.zirRetType( block, extended), .this => return sema.zirThis( block, extended), @@ -1432,7 +1432,7 @@ fn zirStructDecl( const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty); const type_name = try sema.createTypeName(block, small.name_strategy); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ - .ty = Type.initTag(.type), + .ty = Type.type, .val = struct_val, }, type_name); new_decl.owns_tv = true; @@ -1541,7 +1541,7 @@ fn zirEnumDecl( const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty); const type_name = try sema.createTypeName(block, small.name_strategy); const new_decl = try mod.createAnonymousDeclNamed(block, .{ - .ty = Type.initTag(.type), + .ty = Type.type, .val = enum_val, }, type_name); new_decl.owns_tv = true; @@ -1731,7 +1731,7 @@ fn zirUnionDecl( const union_val = try Value.Tag.ty.create(&new_decl_arena.allocator, union_ty); const type_name = try sema.createTypeName(block, small.name_strategy); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ - .ty = Type.initTag(.type), + .ty = Type.type, .val = union_val, }, type_name); new_decl.owns_tv = true; @@ -1764,14 +1764,63 @@ fn zirOpaqueDecl( sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, - inst: Zir.Inst.Index, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - _ = extended; - _ = inst; - return sema.fail(block, sema.src, "TODO implement zirOpaqueDecl", .{}); + const mod = sema.mod; + const gpa = sema.gpa; + const small = @bitCast(Zir.Inst.OpaqueDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src: LazySrcLoc = if (small.has_src_node) blk: { + const node_offset = @bitCast(i32, sema.code.extra[extra_index]); + extra_index += 1; + break :blk .{ .node_offset = node_offset }; + } else sema.src; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = sema.code.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + var new_decl_arena = std.heap.ArenaAllocator.init(gpa); + errdefer new_decl_arena.deinit(); + + const opaque_obj = try new_decl_arena.allocator.create(Module.Opaque); + const opaque_ty_payload = try new_decl_arena.allocator.create(Type.Payload.Opaque); + opaque_ty_payload.* = .{ + .base = .{ .tag = .@"opaque" }, + .data = opaque_obj, + }; + const opaque_ty = Type.initPayload(&opaque_ty_payload.base); + const opaque_val = try Value.Tag.ty.create(&new_decl_arena.allocator, opaque_ty); + const type_name = try sema.createTypeName(block, small.name_strategy); + const new_decl = try mod.createAnonymousDeclNamed(block, .{ + .ty = Type.type, + .val = opaque_val, + }, type_name); + new_decl.owns_tv = true; + errdefer mod.abortAnonDecl(new_decl); + + opaque_obj.* = .{ + .owner_decl = new_decl, + .node_offset = src.node_offset, + .namespace = .{ + .parent = block.namespace, + .ty = opaque_ty, + .file_scope = block.getFileScope(), + }, + }; + std.log.scoped(.module).debug("create opaque {*} owned by {*} ({s})", .{ + &opaque_obj.namespace, new_decl, new_decl.name, + }); + + extra_index = try mod.scanNamespace(&opaque_obj.namespace, extra_index, decls_len, new_decl); + + try new_decl.finalizeNewArena(&new_decl_arena); + return sema.analyzeDeclVal(block, src, new_decl); } fn zirErrorSetDecl( @@ -1797,7 +1846,7 @@ fn zirErrorSetDecl( const error_set_val = try Value.Tag.ty.create(&new_decl_arena.allocator, error_set_ty); const type_name = try sema.createTypeName(block, name_strategy); const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{ - .ty = Type.initTag(.type), + .ty = Type.type, .val = error_set_val, }, type_name); new_decl.owns_tv = true; @@ -4278,7 +4327,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr .names_len = @intCast(u32, new_names.len), }; const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set); - return sema.addConstant(Type.initTag(.type), try Value.Tag.ty.create(sema.arena, error_set_ty)); + return sema.addConstant(Type.type, try Value.Tag.ty.create(sema.arena, error_set_ty)); } fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -10158,6 +10207,11 @@ fn validateVarType( .Null, => break false, + .Pointer => { + const elem_ty = ty.childType(); + if (elem_ty.zigTypeTag() == .Opaque) return; + ty = elem_ty; + }, .Opaque => break is_extern, .Optional => { @@ -10165,7 +10219,8 @@ fn validateVarType( const child_ty = ty.optionalChild(&buf); return validateVarType(sema, block, src, child_ty, is_extern); }, - .Pointer, .Array, .Vector => ty = ty.elemType(), + .Array, .Vector => ty = ty.elemType(), + .ErrorUnion => ty = ty.errorUnionPayload(), .Fn => @panic("TODO fn validateVarType"), @@ -12978,7 +13033,7 @@ fn generateUnionTagTypeNumbered( const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty); // TODO better type name const new_decl = try mod.createAnonymousDecl(block, .{ - .ty = Type.initTag(.type), + .ty = Type.type, .val = enum_val, }); new_decl.owns_tv = true; @@ -13014,7 +13069,7 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: u32) !Type const enum_val = try Value.Tag.ty.create(&new_decl_arena.allocator, enum_ty); // TODO better type name const new_decl = try mod.createAnonymousDecl(block, .{ - .ty = Type.initTag(.type), + .ty = Type.type, .val = enum_val, }); new_decl.owns_tv = true; diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 24beabb4d2..ae10e14cb3 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -34,6 +34,17 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class { }; var result = [1]Class{.none} ** 8; switch (ty.zigTypeTag()) { + .Pointer => switch (ty.ptrSize()) { + .Slice => { + result[0] = .integer; + result[1] = .integer; + return result; + }, + else => { + result[0] = .integer; + return result; + }, + }, .Int, .Enum, .ErrorSet => { const bits = ty.intInfo(target).bits; if (bits <= 64) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2e072fbdd6..d7dbeade15 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -758,11 +758,27 @@ pub const DeclGen = struct { }; return dg.context.structType(&fields, fields.len, .False); } else { - const elem_type = try dg.llvmType(t.elemType()); const llvm_addrspace = dg.llvmAddressSpace(t.ptrAddressSpace()); - return elem_type.pointerType(llvm_addrspace); + const llvm_elem_ty = try dg.llvmType(t.childType()); + return llvm_elem_ty.pointerType(llvm_addrspace); } }, + .Opaque => { + const gop = try dg.object.type_map.getOrPut(gpa, t); + if (gop.found_existing) return gop.value_ptr.*; + + // The Type memory is ephemeral; since we want to store a longer-lived + // reference, we need to copy it here. + gop.key_ptr.* = try t.copy(&dg.object.type_map_arena.allocator); + + const opaque_obj = t.castTag(.@"opaque").?.data; + const name = try opaque_obj.getFullyQualifiedName(gpa); + defer gpa.free(name); + + const llvm_struct_ty = dg.context.structCreateNamed(name); + gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls + return llvm_struct_ty; + }, .Array => { const elem_type = try dg.llvmType(t.elemType()); const total_len = t.arrayLen() + @boolToInt(t.sentinel() != null); @@ -896,7 +912,6 @@ pub const DeclGen = struct { .BoundFn => @panic("TODO remove BoundFn from the language"), - .Opaque, .Frame, .AnyFrame, .Vector, diff --git a/src/type.zig b/src/type.zig index daffd4abee..72430c9f65 100644 --- a/src/type.zig +++ b/src/type.zig @@ -571,6 +571,11 @@ pub const Type = extern union { } return a.tag() == b.tag(); }, + .Opaque => { + const opaque_obj_a = a.castTag(.@"opaque").?.data; + const opaque_obj_b = b.castTag(.@"opaque").?.data; + return opaque_obj_a == opaque_obj_b; + }, .Union => { if (a.cast(Payload.Union)) |a_payload| { if (b.cast(Payload.Union)) |b_payload| { @@ -611,7 +616,6 @@ pub const Type = extern union { return false; }, .Float => return a.tag() == b.tag(), - .Opaque, .BoundFn, .Frame, => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), @@ -1408,6 +1412,7 @@ pub const Type = extern union { .extern_options, .@"anyframe", .anyframe_T, + .@"opaque", => true, .function => !self.castTag(.function).?.data.is_generic, @@ -1499,7 +1504,6 @@ pub const Type = extern union { .enum_literal, .empty_struct, .empty_struct_literal, - .@"opaque", .type_info, .bound_fn, => false, @@ -3097,7 +3101,7 @@ pub const Type = extern union { .enum_full => &self.castTag(.enum_full).?.data.namespace, .enum_nonexhaustive => &self.castTag(.enum_nonexhaustive).?.data.namespace, .empty_struct => self.castTag(.empty_struct).?.data, - .@"opaque" => &self.castTag(.@"opaque").?.data, + .@"opaque" => &self.castTag(.@"opaque").?.data.namespace, .@"union" => &self.castTag(.@"union").?.data.namespace, .union_tagged => &self.castTag(.union_tagged).?.data.namespace, @@ -3870,7 +3874,7 @@ pub const Type = extern union { pub const Opaque = struct { base: Payload = .{ .tag = .@"opaque" }, - data: Module.Namespace, + data: *Module.Opaque, }; pub const Struct = struct { @@ -3904,6 +3908,7 @@ pub const Type = extern union { pub const @"usize" = initTag(.usize); pub const @"comptime_int" = initTag(.comptime_int); pub const @"void" = initTag(.void); + pub const @"type" = initTag(.type); pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index f6876e29ad..63a3ad9eb6 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -188,3 +188,15 @@ fn testMemcpyMemset() !void { try expect(bar[11] == 'A'); try expect(bar[19] == 'A'); } + +const OpaqueA = opaque {}; +const OpaqueB = opaque {}; + +test "variable is allowed to be a pointer to an opaque type" { + var x: i32 = 1234; + _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); +} +fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { + var a = ptr; + return a; +} diff --git a/test/behavior/misc.zig b/test/behavior/misc.zig index 9b3bf48366..b6b8e730c9 100644 --- a/test/behavior/misc.zig +++ b/test/behavior/misc.zig @@ -5,22 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings; const mem = std.mem; const builtin = @import("builtin"); -test "slicing" { - var array: [20]i32 = undefined; - - array[5] = 1234; - - var slice = array[5..10]; - - if (slice.len != 5) unreachable; - - const ptr = &slice[0]; - if (ptr.* != 1234) unreachable; - - var slice_rest = array[10..]; - if (slice_rest.len != 10) unreachable; -} - test "constant equal function pointers" { const alias = emptyFn; try expect(comptime x: { @@ -230,15 +214,6 @@ test "opaque types" { try expect(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); } -test "variable is allowed to be a pointer to an opaque type" { - var x: i32 = 1234; - _ = hereIsAnOpaqueType(@ptrCast(*OpaqueA, &x)); -} -fn hereIsAnOpaqueType(ptr: *OpaqueA) *OpaqueA { - var a = ptr; - return a; -} - test "comptime if inside runtime while which unconditionally breaks" { testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); comptime testComptimeIfInsideRuntimeWhileWhichUnconditionallyBreaks(true); diff --git a/test/behavior/slice_stage1.zig b/test/behavior/slice_stage1.zig index a533136a25..89adbfaea1 100644 --- a/test/behavior/slice_stage1.zig +++ b/test/behavior/slice_stage1.zig @@ -4,6 +4,22 @@ const expectEqualSlices = std.testing.expectEqualSlices; const expectEqual = std.testing.expectEqual; const mem = std.mem; +test "slicing" { + var array: [20]i32 = undefined; + + array[5] = 1234; + + var slice = array[5..10]; + + if (slice.len != 5) unreachable; + + const ptr = &slice[0]; + if (ptr.* != 1234) unreachable; + + var slice_rest = array[10..]; + if (slice_rest.len != 10) unreachable; +} + const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" {