From 87c6341b61aa54301aa98fea1a449fff40ba25af Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Dec 2020 17:15:29 -0700 Subject: stage2: add extern functions and improve the C backend enough to support Hello World (almost) --- src/Module.zig | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 11 deletions(-) (limited to 'src/Module.zig') diff --git a/src/Module.zig b/src/Module.zig index c0b7011f43..dbb845116a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -277,6 +277,8 @@ pub const Decl = struct { }; /// 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`. pub const Fn = struct { /// This memory owned by the Decl's TypedValue.Managed arena allocator. analysis: union(enum) { @@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { defer fn_type_scope.instructions.deinit(self.gpa); decl.is_pub = fn_proto.getVisibToken() != null; - const body_node = fn_proto.getBodyNode() orelse - return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{}); const param_decls = fn_proto.params(); const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); @@ -1083,6 +1083,36 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ .instructions = fn_type_scope.instructions.items, }); + const body_node = fn_proto.getBodyNode() orelse { + // Extern function. + var type_changed = true; + if (decl.typedValueManaged()) |tvm| { + type_changed = !tvm.typed_value.ty.eql(fn_type); + + tvm.deinit(self.gpa); + } + const value_payload = try decl_arena.allocator.create(Value.Payload.ExternFn); + value_payload.* = .{ .decl = decl }; + + decl_arena_state.* = decl_arena.state; + decl.typed_value = .{ + .most_recent = .{ + .typed_value = .{ + .ty = fn_type, + .val = Value.initPayload(&value_payload.base), + }, + .arena = decl_arena_state, + }, + }; + decl.analysis = .complete; + decl.generation = self.generation; + + try self.comp.bin_file.allocateDeclIndexes(decl); + try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + + return type_changed; + }; + const new_func = try decl_arena.allocator.create(Fn); const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); @@ -1899,7 +1929,13 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { return null; } -pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void { +pub fn analyzeExport( + self: *Module, + scope: *Scope, + src: usize, + borrowed_symbol_name: []const u8, + exported_decl: *Decl, +) !void { try self.ensureDeclAnalyzed(exported_decl); const typed_value = exported_decl.typed_value.most_recent.typed_value; switch (typed_value.ty.zigTypeTag()) { @@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst } } - // *[N]T to []T - if (inst.ty.isSinglePointer() and dest_type.isSlice() and - (!inst.ty.isConstPtr() or dest_type.isConstPtr())) - { + // Coercions where the source is a single pointer to an array. + src_array_ptr: { + if (!inst.ty.isSinglePointer()) break :src_array_ptr; const array_type = inst.ty.elemType(); + if (array_type.zigTypeTag() != .Array) break :src_array_ptr; + const array_elem_type = array_type.elemType(); + if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr; + if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr; + const dst_elem_type = dest_type.elemType(); - if (array_type.zigTypeTag() == .Array and - coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok) - { - return self.coerceArrayPtrToSlice(scope, dest_type, inst); + switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) { + .ok => {}, + .no_match => break :src_array_ptr, + } + + switch (dest_type.ptrSize()) { + .Slice => { + // *[N]T to []T + return self.coerceArrayPtrToSlice(scope, dest_type, inst); + }, + .C => { + // *[N]T to [*c]T + return self.coerceArrayPtrToMany(scope, dest_type, inst); + }, + .Many => { + // *[N]T to [*]T + // *[N:s]T to [*:s]T + const src_sentinel = array_type.sentinel(); + const dst_sentinel = dest_type.sentinel(); + if (src_sentinel == null and dst_sentinel == null) + return self.coerceArrayPtrToMany(scope, dest_type, inst); + + if (src_sentinel) |src_s| { + if (dst_sentinel) |dst_s| { + if (src_s.eql(dst_s)) { + return self.coerceArrayPtrToMany(scope, dest_type, inst); + } + } + } + }, + .One => {}, } } @@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); } +fn coerceArrayPtrToMany(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { + if (inst.value()) |val| { + // The comptime Value representation is compatible with both types. + return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); + } + return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{}); +} + pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError { @setCold(true); const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); -- cgit v1.2.3