aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-10-13 17:53:28 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-10-13 17:53:28 -0700
commitdf7d6d263e4ad6adb302856235641ae9ceb142b6 (patch)
tree186182733c89cec8fa990a681ab8e6f915d15908 /src
parentda7fcfd1586fa93c3d00815f60030e00ea583701 (diff)
downloadzig-df7d6d263e4ad6adb302856235641ae9ceb142b6.tar.gz
zig-df7d6d263e4ad6adb302856235641ae9ceb142b6.zip
stage2: implement opaque declarations
* Module: implement opaque type namespace lookup * Add `Type.type` for convenience * Sema: fix `validateVarType` for pointer-to-opaque * x86_64 ABI: implement support for pointers * LLVM backend: fix lowering of opaque types * Type: implement equality checking for opaques
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig25
-rw-r--r--src/Sema.zig81
-rw-r--r--src/arch/x86_64/abi.zig11
-rw-r--r--src/codegen/llvm.zig21
-rw-r--r--src/type.zig13
5 files changed, 130 insertions, 21 deletions
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);