mirror of
https://github.com/ziglang/zig.git
synced 2024-12-14 07:10:16 +00:00
self-hosted can compile libc hello world
This commit is contained in:
parent
58c5f94a99
commit
93e78ee722
@ -624,6 +624,7 @@ set(ZIG_STD_FILES
|
||||
"zig/ast.zig"
|
||||
"zig/index.zig"
|
||||
"zig/parse.zig"
|
||||
"zig/parse_string_literal.zig"
|
||||
"zig/render.zig"
|
||||
"zig/tokenizer.zig"
|
||||
)
|
||||
|
@ -78,6 +78,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
||||
.dibuilder = dibuilder,
|
||||
.context = context,
|
||||
.lock = event.Lock.init(comp.loop),
|
||||
.arena = &code.arena.allocator,
|
||||
};
|
||||
|
||||
try renderToLlvmModule(&ofile, fn_val, code);
|
||||
@ -139,6 +140,7 @@ pub const ObjectFile = struct {
|
||||
dibuilder: *llvm.DIBuilder,
|
||||
context: llvm.ContextRef,
|
||||
lock: event.Lock,
|
||||
arena: *std.mem.Allocator,
|
||||
|
||||
fn gpa(self: *ObjectFile) *std.mem.Allocator {
|
||||
return self.comp.gpa();
|
||||
@ -147,7 +149,7 @@ pub const ObjectFile = struct {
|
||||
|
||||
pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
|
||||
// TODO audit more of codegen.cpp:fn_llvm_value and port more logic
|
||||
const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile);
|
||||
const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
const llvm_fn = llvm.AddFunction(
|
||||
ofile.module,
|
||||
fn_val.symbol_name.ptr(),
|
||||
@ -165,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
|
||||
// try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
|
||||
//}
|
||||
|
||||
const fn_type = fn_val.base.typeof.cast(Type.Fn).?;
|
||||
const fn_type = fn_val.base.typ.cast(Type.Fn).?;
|
||||
|
||||
try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
|
||||
//add_uwtable_attr(g, fn_table_entry->llvm_value);
|
||||
|
@ -194,6 +194,7 @@ pub const Compilation = struct {
|
||||
bool_type: *Type.Bool,
|
||||
noreturn_type: *Type.NoReturn,
|
||||
comptime_int_type: *Type.ComptimeInt,
|
||||
u8_type: *Type.Int,
|
||||
|
||||
void_value: *Value.Void,
|
||||
true_value: *Value.Bool,
|
||||
@ -203,6 +204,7 @@ pub const Compilation = struct {
|
||||
target_machine: llvm.TargetMachineRef,
|
||||
target_data_ref: llvm.TargetDataRef,
|
||||
target_layout_str: [*]u8,
|
||||
target_ptr_bits: u32,
|
||||
|
||||
/// for allocating things which have the same lifetime as this Compilation
|
||||
arena_allocator: std.heap.ArenaAllocator,
|
||||
@ -223,10 +225,14 @@ pub const Compilation = struct {
|
||||
primitive_type_table: TypeTable,
|
||||
|
||||
int_type_table: event.Locked(IntTypeTable),
|
||||
array_type_table: event.Locked(ArrayTypeTable),
|
||||
ptr_type_table: event.Locked(PtrTypeTable),
|
||||
|
||||
c_int_types: [CInt.list.len]*Type.Int,
|
||||
|
||||
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
|
||||
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
|
||||
const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
|
||||
const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||
|
||||
const CompileErrList = std.ArrayList(*errmsg.Msg);
|
||||
@ -383,6 +389,8 @@ pub const Compilation = struct {
|
||||
.deinit_group = event.Group(void).init(loop),
|
||||
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
|
||||
.int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
|
||||
.array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
|
||||
.ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
|
||||
.c_int_types = undefined,
|
||||
|
||||
.meta_type = undefined,
|
||||
@ -394,10 +402,12 @@ pub const Compilation = struct {
|
||||
.noreturn_type = undefined,
|
||||
.noreturn_value = undefined,
|
||||
.comptime_int_type = undefined,
|
||||
.u8_type = undefined,
|
||||
|
||||
.target_machine = undefined,
|
||||
.target_data_ref = undefined,
|
||||
.target_layout_str = undefined,
|
||||
.target_ptr_bits = target.getArchPtrBitWidth(),
|
||||
|
||||
.root_package = undefined,
|
||||
.std_package = undefined,
|
||||
@ -409,6 +419,8 @@ pub const Compilation = struct {
|
||||
});
|
||||
errdefer {
|
||||
comp.int_type_table.private_data.deinit();
|
||||
comp.array_type_table.private_data.deinit();
|
||||
comp.ptr_type_table.private_data.deinit();
|
||||
comp.arena_allocator.deinit();
|
||||
comp.loop.allocator.destroy(comp);
|
||||
}
|
||||
@ -517,15 +529,16 @@ pub const Compilation = struct {
|
||||
.name = "type",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = undefined,
|
||||
.typ = undefined,
|
||||
.ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
|
||||
},
|
||||
.id = builtin.TypeId.Type,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
.value = undefined,
|
||||
});
|
||||
comp.meta_type.value = &comp.meta_type.base;
|
||||
comp.meta_type.base.base.typeof = &comp.meta_type.base;
|
||||
comp.meta_type.base.base.typ = &comp.meta_type.base;
|
||||
assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
|
||||
|
||||
comp.void_type = try comp.arena().create(Type.Void{
|
||||
@ -533,10 +546,11 @@ pub const Compilation = struct {
|
||||
.name = "void",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Void,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
|
||||
@ -546,10 +560,11 @@ pub const Compilation = struct {
|
||||
.name = "noreturn",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.NoReturn,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
|
||||
@ -559,10 +574,11 @@ pub const Compilation = struct {
|
||||
.name = "comptime_int",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.ComptimeInt,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
|
||||
@ -572,10 +588,11 @@ pub const Compilation = struct {
|
||||
.name = "bool",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Bool,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
});
|
||||
assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
|
||||
@ -583,7 +600,7 @@ pub const Compilation = struct {
|
||||
comp.void_value = try comp.arena().create(Value.Void{
|
||||
.base = Value{
|
||||
.id = Value.Id.Void,
|
||||
.typeof = &Type.Void.get(comp).base,
|
||||
.typ = &Type.Void.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
@ -591,7 +608,7 @@ pub const Compilation = struct {
|
||||
comp.true_value = try comp.arena().create(Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typeof = &Type.Bool.get(comp).base,
|
||||
.typ = &Type.Bool.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.x = true,
|
||||
@ -600,7 +617,7 @@ pub const Compilation = struct {
|
||||
comp.false_value = try comp.arena().create(Value.Bool{
|
||||
.base = Value{
|
||||
.id = Value.Id.Bool,
|
||||
.typeof = &Type.Bool.get(comp).base,
|
||||
.typ = &Type.Bool.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.x = false,
|
||||
@ -609,7 +626,7 @@ pub const Compilation = struct {
|
||||
comp.noreturn_value = try comp.arena().create(Value.NoReturn{
|
||||
.base = Value{
|
||||
.id = Value.Id.NoReturn,
|
||||
.typeof = &Type.NoReturn.get(comp).base,
|
||||
.typ = &Type.NoReturn.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
});
|
||||
@ -620,10 +637,11 @@ pub const Compilation = struct {
|
||||
.name = cint.zig_name,
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &Type.MetaType.get(comp).base,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Int,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
.key = Type.Int.Key{
|
||||
.is_signed = cint.is_signed,
|
||||
@ -634,6 +652,24 @@ pub const Compilation = struct {
|
||||
comp.c_int_types[i] = c_int_type;
|
||||
assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
|
||||
}
|
||||
comp.u8_type = try comp.arena().create(Type.Int{
|
||||
.base = Type{
|
||||
.name = "u8",
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typ = &Type.MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = builtin.TypeId.Int,
|
||||
.abi_alignment = Type.AbiAlignment.init(comp.loop),
|
||||
},
|
||||
.key = Type.Int.Key{
|
||||
.is_signed = false,
|
||||
.bit_count = 8,
|
||||
},
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
|
||||
}
|
||||
|
||||
/// This function can safely use async/await, because it manages Compilation's lifetime,
|
||||
@ -750,7 +786,7 @@ pub const Compilation = struct {
|
||||
ast.Node.Id.Comptime => {
|
||||
const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
|
||||
|
||||
try decl_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
|
||||
try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
|
||||
},
|
||||
ast.Node.Id.VarDecl => @panic("TODO"),
|
||||
ast.Node.Id.FnProto => {
|
||||
@ -770,7 +806,6 @@ pub const Compilation = struct {
|
||||
.name = name,
|
||||
.visib = parseVisibToken(tree, fn_proto.visib_token),
|
||||
.resolution = event.Future(BuildError!void).init(self.loop),
|
||||
.resolution_in_progress = 0,
|
||||
.parent_scope = &decls.base,
|
||||
},
|
||||
.value = Decl.Fn.Val{ .Unresolved = {} },
|
||||
@ -778,16 +813,22 @@ pub const Compilation = struct {
|
||||
});
|
||||
errdefer self.gpa().destroy(fn_decl);
|
||||
|
||||
try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
|
||||
try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
|
||||
},
|
||||
ast.Node.Id.TestDecl => @panic("TODO"),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
try await (async decl_group.wait() catch unreachable);
|
||||
|
||||
// Now other code can rely on the decls scope having a complete list of names.
|
||||
decls.name_future.resolve();
|
||||
}
|
||||
|
||||
try await (async self.prelink_group.wait() catch unreachable);
|
||||
(await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
|
||||
error.SemanticAnalysisFailed => {},
|
||||
else => return err,
|
||||
};
|
||||
|
||||
const any_prelink_errors = blk: {
|
||||
const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
|
||||
@ -857,14 +898,31 @@ pub const Compilation = struct {
|
||||
analyzed_code.destroy(comp.gpa());
|
||||
}
|
||||
|
||||
async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
|
||||
async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
|
||||
const tree = &decl.findRootScope().tree;
|
||||
const is_export = decl.isExported(tree);
|
||||
|
||||
var add_to_table_resolved = false;
|
||||
const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
|
||||
errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
|
||||
|
||||
if (is_export) {
|
||||
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
|
||||
try self.prelink_group.call(resolveDecl, self, decl);
|
||||
}
|
||||
|
||||
add_to_table_resolved = true;
|
||||
try await add_to_table;
|
||||
}
|
||||
|
||||
async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
|
||||
const held = await (async decls.table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
if (try held.value.put(decl.name, decl)) |other_decl| {
|
||||
try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
|
||||
// TODO note: other definition here
|
||||
}
|
||||
}
|
||||
|
||||
fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
|
||||
@ -1043,6 +1101,15 @@ pub const Compilation = struct {
|
||||
|
||||
return result_val.cast(Type).?;
|
||||
}
|
||||
|
||||
/// This declaration has been blessed as going into the final code generation.
|
||||
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable);
|
||||
decl.resolution.resolve();
|
||||
return decl.resolution.data;
|
||||
}
|
||||
};
|
||||
|
||||
fn printError(comptime format: []const u8, args: ...) !void {
|
||||
@ -1062,20 +1129,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
|
||||
}
|
||||
}
|
||||
|
||||
/// This declaration has been blessed as going into the final code generation.
|
||||
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
decl.resolution.data = (await (async generateDecl(comp, decl) catch unreachable)) catch |err| switch (err) {
|
||||
// This poison value should not cause the errdefers to run. It simply means
|
||||
// that comp.compile_errors is populated.
|
||||
error.SemanticAnalysisFailed => {},
|
||||
else => err,
|
||||
};
|
||||
decl.resolution.resolve();
|
||||
return decl.resolution.data;
|
||||
}
|
||||
|
||||
/// The function that actually does the generation.
|
||||
async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
switch (decl.id) {
|
||||
@ -1089,34 +1142,27 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
}
|
||||
|
||||
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
|
||||
const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
|
||||
|
||||
const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
|
||||
defer fndef_scope.base.deref(comp);
|
||||
|
||||
const return_type_node = switch (fn_decl.fn_proto.return_type) {
|
||||
ast.Node.FnProto.ReturnType.Explicit => |n| n,
|
||||
ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
|
||||
};
|
||||
const return_type = try await (async comp.analyzeTypeExpr(&fndef_scope.base, return_type_node) catch unreachable);
|
||||
return_type.base.deref(comp);
|
||||
|
||||
const is_var_args = false;
|
||||
const params = ([*]Type.Fn.Param)(undefined)[0..0];
|
||||
const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
|
||||
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
|
||||
defer fn_type.base.base.deref(comp);
|
||||
|
||||
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
||||
errdefer symbol_name.deinit();
|
||||
var symbol_name_consumed = false;
|
||||
errdefer if (!symbol_name_consumed) symbol_name.deinit();
|
||||
|
||||
// The Decl.Fn owns the initial 1 reference count
|
||||
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
|
||||
fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
|
||||
fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
|
||||
symbol_name_consumed = true;
|
||||
|
||||
const analyzed_code = try await (async comp.genAndAnalyzeCode(
|
||||
&fndef_scope.base,
|
||||
body_node,
|
||||
return_type,
|
||||
fn_type.return_type,
|
||||
) catch unreachable);
|
||||
errdefer analyzed_code.destroy(comp.gpa());
|
||||
|
||||
@ -1141,3 +1187,54 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
|
||||
fn getZigDir(allocator: *mem.Allocator) ![]u8 {
|
||||
return os.getAppDataDir(allocator, "zig");
|
||||
}
|
||||
|
||||
async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
|
||||
const return_type_node = switch (fn_proto.return_type) {
|
||||
ast.Node.FnProto.ReturnType.Explicit => |n| n,
|
||||
ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
|
||||
};
|
||||
const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
|
||||
return_type.base.deref(comp);
|
||||
|
||||
var params = ArrayList(Type.Fn.Param).init(comp.gpa());
|
||||
var params_consumed = false;
|
||||
defer if (params_consumed) {
|
||||
for (params.toSliceConst()) |param| {
|
||||
param.typ.base.deref(comp);
|
||||
}
|
||||
params.deinit();
|
||||
};
|
||||
|
||||
const is_var_args = false;
|
||||
{
|
||||
var it = fn_proto.params.iterator(0);
|
||||
while (it.next()) |param_node_ptr| {
|
||||
const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
|
||||
const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
|
||||
errdefer param_type.base.deref(comp);
|
||||
try params.append(Type.Fn.Param{
|
||||
.typ = param_type,
|
||||
.is_noalias = param_node.noalias_token != null,
|
||||
});
|
||||
}
|
||||
}
|
||||
const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);
|
||||
params_consumed = true;
|
||||
errdefer fn_type.base.base.deref(comp);
|
||||
|
||||
return fn_type;
|
||||
}
|
||||
|
||||
async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
|
||||
defer fn_type.base.base.deref(comp);
|
||||
|
||||
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
|
||||
var symbol_name_consumed = false;
|
||||
defer if (!symbol_name_consumed) symbol_name.deinit();
|
||||
|
||||
// The Decl.Fn owns the initial 1 reference count
|
||||
const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
|
||||
fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
|
||||
symbol_name_consumed = true;
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ pub const Decl = struct {
|
||||
name: []const u8,
|
||||
visib: Visib,
|
||||
resolution: event.Future(Compilation.BuildError!void),
|
||||
resolution_in_progress: u8,
|
||||
parent_scope: *Scope,
|
||||
|
||||
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||
@ -63,12 +62,13 @@ pub const Decl = struct {
|
||||
pub const Fn = struct {
|
||||
base: Decl,
|
||||
value: Val,
|
||||
fn_proto: *const ast.Node.FnProto,
|
||||
fn_proto: *ast.Node.FnProto,
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
|
||||
pub const Val = union {
|
||||
pub const Val = union(enum) {
|
||||
Unresolved: void,
|
||||
Ok: *Value.Fn,
|
||||
Fn: *Value.Fn,
|
||||
FnProto: *Value.FnProto,
|
||||
};
|
||||
|
||||
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
|
||||
|
@ -16,11 +16,18 @@ pub const Span = struct {
|
||||
last: ast.TokenIndex,
|
||||
|
||||
pub fn token(i: TokenIndex) Span {
|
||||
return Span {
|
||||
return Span{
|
||||
.first = i,
|
||||
.last = i,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn node(n: *ast.Node) Span {
|
||||
return Span{
|
||||
.first = n.firstToken(),
|
||||
.last = n.lastToken(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const Msg = struct {
|
||||
|
@ -11,6 +11,7 @@ const Token = std.zig.Token;
|
||||
const Span = @import("errmsg.zig").Span;
|
||||
const llvm = @import("llvm.zig");
|
||||
const ObjectFile = @import("codegen.zig").ObjectFile;
|
||||
const Decl = @import("decl.zig").Decl;
|
||||
|
||||
pub const LVal = enum {
|
||||
None,
|
||||
@ -30,10 +31,10 @@ pub const IrVal = union(enum) {
|
||||
|
||||
pub fn dump(self: IrVal) void {
|
||||
switch (self) {
|
||||
IrVal.Unknown => typeof.dump(),
|
||||
IrVal.KnownType => |typeof| {
|
||||
IrVal.Unknown => std.debug.warn("Unknown"),
|
||||
IrVal.KnownType => |typ| {
|
||||
std.debug.warn("KnownType(");
|
||||
typeof.dump();
|
||||
typ.dump();
|
||||
std.debug.warn(")");
|
||||
},
|
||||
IrVal.KnownValue => |value| {
|
||||
@ -108,21 +109,29 @@ pub const Inst = struct {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
|
||||
comptime var i = 0;
|
||||
inline while (i < @memberCount(Id)) : (i += 1) {
|
||||
if (base.id == @field(Id, @memberName(Id, i))) {
|
||||
const T = @field(Inst, @memberName(Id, i));
|
||||
return @fieldParentPtr(T, "base", base).analyze(ira);
|
||||
}
|
||||
pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
|
||||
switch (base.id) {
|
||||
Id.Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
|
||||
Id.Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
|
||||
Id.Call => return @fieldParentPtr(Call, "base", base).analyze(ira),
|
||||
Id.DeclRef => return await (async @fieldParentPtr(DeclRef, "base", base).analyze(ira) catch unreachable),
|
||||
Id.Ref => return await (async @fieldParentPtr(Ref, "base", base).analyze(ira) catch unreachable),
|
||||
Id.DeclVar => return @fieldParentPtr(DeclVar, "base", base).analyze(ira),
|
||||
Id.CheckVoidStmt => return @fieldParentPtr(CheckVoidStmt, "base", base).analyze(ira),
|
||||
Id.Phi => return @fieldParentPtr(Phi, "base", base).analyze(ira),
|
||||
Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
|
||||
Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
|
||||
Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable),
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn render(base: *Inst, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) {
|
||||
switch (base.id) {
|
||||
Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
|
||||
Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
|
||||
Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
|
||||
Id.DeclRef => unreachable,
|
||||
Id.PtrType => unreachable,
|
||||
Id.Ref => @panic("TODO"),
|
||||
Id.DeclVar => @panic("TODO"),
|
||||
Id.CheckVoidStmt => @panic("TODO"),
|
||||
@ -135,7 +144,7 @@ pub const Inst = struct {
|
||||
fn ref(base: *Inst, builder: *Builder) void {
|
||||
base.ref_count += 1;
|
||||
if (base.owner_bb != builder.current_basic_block and !base.isCompTime()) {
|
||||
base.owner_bb.ref();
|
||||
base.owner_bb.ref(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,11 +164,51 @@ pub const Inst = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn getConstVal(self: *Inst, ira: *Analyze) !*Value {
|
||||
if (self.isCompTime()) {
|
||||
return self.val.KnownValue;
|
||||
} else {
|
||||
try ira.addCompileError(self.span, "unable to evaluate constant expression");
|
||||
return error.SemanticAnalysisFailed;
|
||||
}
|
||||
}
|
||||
|
||||
fn getAsConstType(param: *Inst, ira: *Analyze) !*Type {
|
||||
const meta_type = Type.MetaType.get(ira.irb.comp);
|
||||
meta_type.base.base.deref(ira.irb.comp);
|
||||
|
||||
const inst = try param.getAsParam();
|
||||
const casted = try ira.implicitCast(inst, &meta_type.base);
|
||||
const val = try casted.getConstVal(ira);
|
||||
return val.cast(Value.Type).?;
|
||||
}
|
||||
|
||||
fn getAsConstAlign(param: *Inst, ira: *Analyze) !u32 {
|
||||
return error.Unimplemented;
|
||||
//const align_type = Type.Int.get_align(ira.irb.comp);
|
||||
//align_type.base.base.deref(ira.irb.comp);
|
||||
|
||||
//const inst = try param.getAsParam();
|
||||
//const casted = try ira.implicitCast(inst, align_type);
|
||||
//const val = try casted.getConstVal(ira);
|
||||
|
||||
//uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint);
|
||||
//if (align_bytes == 0) {
|
||||
// ir_add_error(ira, value, buf_sprintf("alignment must be >= 1"));
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//if (!is_power_of_2(align_bytes)) {
|
||||
// ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
|
||||
// return false;
|
||||
//}
|
||||
}
|
||||
|
||||
/// asserts that the type is known
|
||||
fn getKnownType(self: *Inst) *Type {
|
||||
switch (self.val) {
|
||||
IrVal.KnownType => |typeof| return typeof,
|
||||
IrVal.KnownValue => |value| return value.typeof,
|
||||
IrVal.KnownType => |typ| return typ,
|
||||
IrVal.KnownValue => |value| return value.typ,
|
||||
IrVal.Unknown => unreachable,
|
||||
}
|
||||
}
|
||||
@ -171,8 +220,8 @@ pub const Inst = struct {
|
||||
pub fn isNoReturn(base: *const Inst) bool {
|
||||
switch (base.val) {
|
||||
IrVal.Unknown => return false,
|
||||
IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn,
|
||||
IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn,
|
||||
IrVal.KnownValue => |x| return x.typ.id == Type.Id.NoReturn,
|
||||
IrVal.KnownType => |typ| return typ.id == Type.Id.NoReturn,
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,6 +245,85 @@ pub const Inst = struct {
|
||||
Phi,
|
||||
Br,
|
||||
AddImplicitReturnType,
|
||||
Call,
|
||||
DeclRef,
|
||||
PtrType,
|
||||
};
|
||||
|
||||
pub const Call = struct {
|
||||
base: Inst,
|
||||
params: Params,
|
||||
|
||||
const Params = struct {
|
||||
fn_ref: *Inst,
|
||||
args: []*Inst,
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(self: *const Call) void {
|
||||
std.debug.warn("#{}(", self.params.fn_ref.debug_id);
|
||||
for (self.params.args) |arg| {
|
||||
std.debug.warn("#{},", arg.debug_id);
|
||||
}
|
||||
std.debug.warn(")");
|
||||
}
|
||||
|
||||
pub fn hasSideEffects(self: *const Call) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const Call, ira: *Analyze) !*Inst {
|
||||
const fn_ref = try self.params.fn_ref.getAsParam();
|
||||
const fn_ref_type = fn_ref.getKnownType();
|
||||
const fn_type = fn_ref_type.cast(Type.Fn) orelse {
|
||||
try ira.addCompileError(fn_ref.span, "type '{}' not a function", fn_ref_type.name);
|
||||
return error.SemanticAnalysisFailed;
|
||||
};
|
||||
|
||||
if (fn_type.params.len != self.params.args.len) {
|
||||
try ira.addCompileError(
|
||||
self.base.span,
|
||||
"expected {} arguments, found {}",
|
||||
fn_type.params.len,
|
||||
self.params.args.len,
|
||||
);
|
||||
return error.SemanticAnalysisFailed;
|
||||
}
|
||||
|
||||
const args = try ira.irb.arena().alloc(*Inst, self.params.args.len);
|
||||
for (self.params.args) |arg, i| {
|
||||
args[i] = try arg.getAsParam();
|
||||
}
|
||||
const new_inst = try ira.irb.build(Call, self.base.scope, self.base.span, Params{
|
||||
.fn_ref = fn_ref,
|
||||
.args = args,
|
||||
});
|
||||
new_inst.val = IrVal{ .KnownType = fn_type.return_type };
|
||||
return new_inst;
|
||||
}
|
||||
|
||||
pub fn render(self: *Call, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
|
||||
const fn_ref = self.params.fn_ref.llvm_value.?;
|
||||
|
||||
const args = try ofile.arena.alloc(llvm.ValueRef, self.params.args.len);
|
||||
for (self.params.args) |arg, i| {
|
||||
args[i] = arg.llvm_value.?;
|
||||
}
|
||||
|
||||
const llvm_cc = llvm.CCallConv;
|
||||
const fn_inline = llvm.FnInline.Auto;
|
||||
|
||||
return llvm.BuildCall(
|
||||
ofile.builder,
|
||||
fn_ref,
|
||||
args.ptr,
|
||||
@intCast(c_uint, args.len),
|
||||
llvm_cc,
|
||||
fn_inline,
|
||||
c"",
|
||||
) orelse error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Const = struct {
|
||||
@ -254,14 +382,14 @@ pub const Inst = struct {
|
||||
return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
|
||||
}
|
||||
|
||||
pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef {
|
||||
pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
|
||||
const value = self.params.return_value.llvm_value;
|
||||
const return_type = self.params.return_value.getKnownType();
|
||||
|
||||
if (return_type.handleIsPtr()) {
|
||||
@panic("TODO");
|
||||
} else {
|
||||
_ = llvm.BuildRet(ofile.builder, value);
|
||||
_ = llvm.BuildRet(ofile.builder, value) orelse return error.OutOfMemory;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -285,7 +413,7 @@ pub const Inst = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
|
||||
pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
|
||||
const target = try self.params.target.getAsParam();
|
||||
|
||||
if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
|
||||
@ -294,7 +422,6 @@ pub const Inst = struct {
|
||||
Value.Ptr.Mut.CompTimeConst,
|
||||
self.params.mut,
|
||||
self.params.volatility,
|
||||
val.typeof.getAbiAlignment(ira.irb.comp),
|
||||
);
|
||||
}
|
||||
|
||||
@ -304,14 +431,13 @@ pub const Inst = struct {
|
||||
.volatility = self.params.volatility,
|
||||
});
|
||||
const elem_type = target.getKnownType();
|
||||
const ptr_type = Type.Pointer.get(
|
||||
ira.irb.comp,
|
||||
elem_type,
|
||||
self.params.mut,
|
||||
self.params.volatility,
|
||||
Type.Pointer.Size.One,
|
||||
elem_type.getAbiAlignment(ira.irb.comp),
|
||||
);
|
||||
const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
|
||||
.child_type = elem_type,
|
||||
.mut = self.params.mut,
|
||||
.vol = self.params.volatility,
|
||||
.size = Type.Pointer.Size.One,
|
||||
.alignment = Type.Pointer.Align.Abi,
|
||||
}) catch unreachable);
|
||||
// TODO: potentially set the hint that this is a stack pointer. But it might not be - this
|
||||
// could be a ref of a global, for example
|
||||
new_inst.val = IrVal{ .KnownType = &ptr_type.base };
|
||||
@ -320,6 +446,97 @@ pub const Inst = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const DeclRef = struct {
|
||||
base: Inst,
|
||||
params: Params,
|
||||
|
||||
const Params = struct {
|
||||
decl: *Decl,
|
||||
lval: LVal,
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const DeclRef) void {}
|
||||
|
||||
pub fn hasSideEffects(inst: *const DeclRef) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
|
||||
(await (async ira.irb.comp.resolveDecl(self.params.decl) catch unreachable)) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return error.SemanticAnalysisFailed,
|
||||
};
|
||||
switch (self.params.decl.id) {
|
||||
Decl.Id.CompTime => unreachable,
|
||||
Decl.Id.Var => return error.Unimplemented,
|
||||
Decl.Id.Fn => {
|
||||
const fn_decl = @fieldParentPtr(Decl.Fn, "base", self.params.decl);
|
||||
const decl_val = switch (fn_decl.value) {
|
||||
Decl.Fn.Val.Unresolved => unreachable,
|
||||
Decl.Fn.Val.Fn => |fn_val| &fn_val.base,
|
||||
Decl.Fn.Val.FnProto => |fn_proto| &fn_proto.base,
|
||||
};
|
||||
switch (self.params.lval) {
|
||||
LVal.None => {
|
||||
return ira.irb.buildConstValue(self.base.scope, self.base.span, decl_val);
|
||||
},
|
||||
LVal.Ptr => return error.Unimplemented,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const PtrType = struct {
|
||||
base: Inst,
|
||||
params: Params,
|
||||
|
||||
const Params = struct {
|
||||
child_type: *Inst,
|
||||
mut: Type.Pointer.Mut,
|
||||
vol: Type.Pointer.Vol,
|
||||
size: Type.Pointer.Size,
|
||||
alignment: ?*Inst,
|
||||
};
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const PtrType) void {}
|
||||
|
||||
pub fn hasSideEffects(inst: *const PtrType) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
|
||||
const child_type = try self.params.child_type.getAsConstType(ira);
|
||||
// if (child_type->id == TypeTableEntryIdUnreachable) {
|
||||
// ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
|
||||
// return ira->codegen->builtin_types.entry_invalid;
|
||||
// } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) {
|
||||
// ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
|
||||
// return ira->codegen->builtin_types.entry_invalid;
|
||||
// }
|
||||
const alignment = if (self.params.alignment) |align_inst| blk: {
|
||||
const amt = try align_inst.getAsConstAlign(ira);
|
||||
break :blk Type.Pointer.Align{ .Override = amt };
|
||||
} else blk: {
|
||||
break :blk Type.Pointer.Align{ .Abi = {} };
|
||||
};
|
||||
const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
|
||||
.child_type = child_type,
|
||||
.mut = self.params.mut,
|
||||
.vol = self.params.vol,
|
||||
.size = self.params.size,
|
||||
.alignment = alignment,
|
||||
}) catch unreachable);
|
||||
ptr_type.base.base.deref(ira.irb.comp);
|
||||
|
||||
return ira.irb.buildConstValue(self.base.scope, self.base.span, &ptr_type.base.base);
|
||||
}
|
||||
};
|
||||
|
||||
pub const DeclVar = struct {
|
||||
base: Inst,
|
||||
params: Params,
|
||||
@ -351,14 +568,21 @@ pub const Inst = struct {
|
||||
|
||||
const ir_val_init = IrVal.Init.Unknown;
|
||||
|
||||
pub fn dump(inst: *const CheckVoidStmt) void {}
|
||||
pub fn dump(self: *const CheckVoidStmt) void {
|
||||
std.debug.warn("#{}", self.params.target.debug_id);
|
||||
}
|
||||
|
||||
pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Inst {
|
||||
return error.Unimplemented; // TODO
|
||||
const target = try self.params.target.getAsParam();
|
||||
if (target.getKnownType().id != Type.Id.Void) {
|
||||
try ira.addCompileError(self.base.span, "expression value is ignored");
|
||||
return error.SemanticAnalysisFailed;
|
||||
}
|
||||
return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
|
||||
}
|
||||
};
|
||||
|
||||
@ -583,7 +807,7 @@ pub const BasicBlock = struct {
|
||||
/// the basic block that this one derives from in analysis
|
||||
parent: ?*BasicBlock,
|
||||
|
||||
pub fn ref(self: *BasicBlock) void {
|
||||
pub fn ref(self: *BasicBlock, builder: *Builder) void {
|
||||
self.ref_count += 1;
|
||||
}
|
||||
|
||||
@ -724,8 +948,42 @@ pub const Builder = struct {
|
||||
ast.Node.Id.VarDecl => return error.Unimplemented,
|
||||
ast.Node.Id.Defer => return error.Unimplemented,
|
||||
ast.Node.Id.InfixOp => return error.Unimplemented,
|
||||
ast.Node.Id.PrefixOp => return error.Unimplemented,
|
||||
ast.Node.Id.SuffixOp => return error.Unimplemented,
|
||||
ast.Node.Id.PrefixOp => {
|
||||
const prefix_op = @fieldParentPtr(ast.Node.PrefixOp, "base", node);
|
||||
switch (prefix_op.op) {
|
||||
ast.Node.PrefixOp.Op.AddressOf => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.ArrayType => |n| return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.Await => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.BitNot => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.BoolNot => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.Cancel => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.OptionalType => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.Negation => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.NegationWrap => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.Resume => return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.PtrType => |ptr_info| {
|
||||
const inst = try await (async irb.genPtrType(prefix_op, ptr_info, scope) catch unreachable);
|
||||
return irb.lvalWrap(scope, inst, lval);
|
||||
},
|
||||
ast.Node.PrefixOp.Op.SliceType => |ptr_info| return error.Unimplemented,
|
||||
ast.Node.PrefixOp.Op.Try => return error.Unimplemented,
|
||||
}
|
||||
},
|
||||
ast.Node.Id.SuffixOp => {
|
||||
const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
|
||||
switch (suffix_op.op) {
|
||||
@TagType(ast.Node.SuffixOp.Op).Call => |*call| {
|
||||
const inst = try await (async irb.genCall(suffix_op, call, scope) catch unreachable);
|
||||
return irb.lvalWrap(scope, inst, lval);
|
||||
},
|
||||
@TagType(ast.Node.SuffixOp.Op).ArrayAccess => |n| return error.Unimplemented,
|
||||
@TagType(ast.Node.SuffixOp.Op).Slice => |slice| return error.Unimplemented,
|
||||
@TagType(ast.Node.SuffixOp.Op).ArrayInitializer => |init_list| return error.Unimplemented,
|
||||
@TagType(ast.Node.SuffixOp.Op).StructInitializer => |init_list| return error.Unimplemented,
|
||||
@TagType(ast.Node.SuffixOp.Op).Deref => return error.Unimplemented,
|
||||
@TagType(ast.Node.SuffixOp.Op).UnwrapOptional => return error.Unimplemented,
|
||||
}
|
||||
},
|
||||
ast.Node.Id.Switch => return error.Unimplemented,
|
||||
ast.Node.Id.While => return error.Unimplemented,
|
||||
ast.Node.Id.For => return error.Unimplemented,
|
||||
@ -744,7 +1002,11 @@ pub const Builder = struct {
|
||||
return irb.lvalWrap(scope, try irb.genIntLit(int_lit, scope), lval);
|
||||
},
|
||||
ast.Node.Id.FloatLiteral => return error.Unimplemented,
|
||||
ast.Node.Id.StringLiteral => return error.Unimplemented,
|
||||
ast.Node.Id.StringLiteral => {
|
||||
const str_lit = @fieldParentPtr(ast.Node.StringLiteral, "base", node);
|
||||
const inst = try await (async irb.genStrLit(str_lit, scope) catch unreachable);
|
||||
return irb.lvalWrap(scope, inst, lval);
|
||||
},
|
||||
ast.Node.Id.MultilineStringLiteral => return error.Unimplemented,
|
||||
ast.Node.Id.CharLiteral => return error.Unimplemented,
|
||||
ast.Node.Id.BoolLiteral => return error.Unimplemented,
|
||||
@ -789,6 +1051,99 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
|
||||
async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
|
||||
const fn_ref = try await (async irb.genNode(suffix_op.lhs, scope, LVal.None) catch unreachable);
|
||||
|
||||
const args = try irb.arena().alloc(*Inst, call.params.len);
|
||||
var it = call.params.iterator(0);
|
||||
var i: usize = 0;
|
||||
while (it.next()) |arg_node_ptr| : (i += 1) {
|
||||
args[i] = try await (async irb.genNode(arg_node_ptr.*, scope, LVal.None) catch unreachable);
|
||||
}
|
||||
|
||||
//bool is_async = node->data.fn_call_expr.is_async;
|
||||
//IrInstruction *async_allocator = nullptr;
|
||||
//if (is_async) {
|
||||
// if (node->data.fn_call_expr.async_allocator) {
|
||||
// async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope);
|
||||
// if (async_allocator == irb->codegen->invalid_instruction)
|
||||
// return async_allocator;
|
||||
// }
|
||||
//}
|
||||
|
||||
return irb.build(Inst.Call, scope, Span.token(suffix_op.rtoken), Inst.Call.Params{
|
||||
.fn_ref = fn_ref,
|
||||
.args = args,
|
||||
});
|
||||
//IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr);
|
||||
//return ir_lval_wrap(irb, scope, fn_call, lval);
|
||||
}
|
||||
|
||||
async fn genPtrType(
|
||||
irb: *Builder,
|
||||
prefix_op: *ast.Node.PrefixOp,
|
||||
ptr_info: ast.Node.PrefixOp.PtrInfo,
|
||||
scope: *Scope,
|
||||
) !*Inst {
|
||||
// TODO port more logic
|
||||
|
||||
//assert(node->type == NodeTypePointerType);
|
||||
//PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
|
||||
// node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
|
||||
//bool is_const = node->data.pointer_type.is_const;
|
||||
//bool is_volatile = node->data.pointer_type.is_volatile;
|
||||
//AstNode *expr_node = node->data.pointer_type.op_expr;
|
||||
//AstNode *align_expr = node->data.pointer_type.align_expr;
|
||||
|
||||
//IrInstruction *align_value;
|
||||
//if (align_expr != nullptr) {
|
||||
// align_value = ir_gen_node(irb, align_expr, scope);
|
||||
// if (align_value == irb->codegen->invalid_instruction)
|
||||
// return align_value;
|
||||
//} else {
|
||||
// align_value = nullptr;
|
||||
//}
|
||||
const child_type = try await (async irb.genNode(prefix_op.rhs, scope, LVal.None) catch unreachable);
|
||||
|
||||
//uint32_t bit_offset_start = 0;
|
||||
//if (node->data.pointer_type.bit_offset_start != nullptr) {
|
||||
// if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
|
||||
// Buf *val_buf = buf_alloc();
|
||||
// bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
|
||||
// exec_add_error_node(irb->codegen, irb->exec, node,
|
||||
// buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
|
||||
// return irb->codegen->invalid_instruction;
|
||||
// }
|
||||
// bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start);
|
||||
//}
|
||||
|
||||
//uint32_t bit_offset_end = 0;
|
||||
//if (node->data.pointer_type.bit_offset_end != nullptr) {
|
||||
// if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_end, 32, false)) {
|
||||
// Buf *val_buf = buf_alloc();
|
||||
// bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_end, 10);
|
||||
// exec_add_error_node(irb->codegen, irb->exec, node,
|
||||
// buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
|
||||
// return irb->codegen->invalid_instruction;
|
||||
// }
|
||||
// bit_offset_end = bigint_as_unsigned(node->data.pointer_type.bit_offset_end);
|
||||
//}
|
||||
|
||||
//if ((bit_offset_start != 0 || bit_offset_end != 0) && bit_offset_start >= bit_offset_end) {
|
||||
// exec_add_error_node(irb->codegen, irb->exec, node,
|
||||
// buf_sprintf("bit offset start must be less than bit offset end"));
|
||||
// return irb->codegen->invalid_instruction;
|
||||
//}
|
||||
|
||||
return irb.build(Inst.PtrType, scope, Span.node(&prefix_op.base), Inst.PtrType.Params{
|
||||
.child_type = child_type,
|
||||
.mut = Type.Pointer.Mut.Mut,
|
||||
.vol = Type.Pointer.Vol.Non,
|
||||
.size = Type.Pointer.Size.Many,
|
||||
.alignment = null,
|
||||
});
|
||||
}
|
||||
|
||||
fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
|
||||
if (irb.is_comptime)
|
||||
return true;
|
||||
@ -847,6 +1202,56 @@ pub const Builder = struct {
|
||||
return inst;
|
||||
}
|
||||
|
||||
pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
|
||||
const str_token = irb.root_scope.tree.tokenSlice(str_lit.token);
|
||||
const src_span = Span.token(str_lit.token);
|
||||
|
||||
var bad_index: usize = undefined;
|
||||
var buf = std.zig.parseStringLiteral(irb.comp.gpa(), str_token, &bad_index) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
error.InvalidCharacter => {
|
||||
try irb.comp.addCompileError(
|
||||
irb.root_scope,
|
||||
src_span,
|
||||
"invalid character in string literal: '{c}'",
|
||||
str_token[bad_index],
|
||||
);
|
||||
return error.SemanticAnalysisFailed;
|
||||
},
|
||||
};
|
||||
var buf_cleaned = false;
|
||||
errdefer if (!buf_cleaned) irb.comp.gpa().free(buf);
|
||||
|
||||
if (str_token[0] == 'c') {
|
||||
// first we add a null
|
||||
buf = try irb.comp.gpa().realloc(u8, buf, buf.len + 1);
|
||||
buf[buf.len - 1] = 0;
|
||||
|
||||
// next make an array value
|
||||
const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable);
|
||||
buf_cleaned = true;
|
||||
defer array_val.base.deref(irb.comp);
|
||||
|
||||
// then make a pointer value pointing at the first element
|
||||
const ptr_val = try await (async Value.Ptr.createArrayElemPtr(
|
||||
irb.comp,
|
||||
array_val,
|
||||
Type.Pointer.Mut.Const,
|
||||
Type.Pointer.Size.Many,
|
||||
0,
|
||||
) catch unreachable);
|
||||
defer ptr_val.base.deref(irb.comp);
|
||||
|
||||
return irb.buildConstValue(scope, src_span, &ptr_val.base);
|
||||
} else {
|
||||
const array_val = try await (async Value.Array.createOwnedBuffer(irb.comp, buf) catch unreachable);
|
||||
buf_cleaned = true;
|
||||
defer array_val.base.deref(irb.comp);
|
||||
|
||||
return irb.buildConstValue(scope, src_span, &array_val.base);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
|
||||
const block_scope = try Scope.Block.create(irb.comp, parent_scope);
|
||||
|
||||
@ -911,7 +1316,10 @@ pub const Builder = struct {
|
||||
_ = irb.build(
|
||||
Inst.CheckVoidStmt,
|
||||
child_scope,
|
||||
statement_value.span,
|
||||
Span{
|
||||
.first = statement_node.firstToken(),
|
||||
.last = statement_node.lastToken(),
|
||||
},
|
||||
Inst.CheckVoidStmt.Params{ .target = statement_value },
|
||||
);
|
||||
}
|
||||
@ -1068,6 +1476,8 @@ pub const Builder = struct {
|
||||
if (result) |primitive_type| {
|
||||
defer primitive_type.base.deref(irb.comp);
|
||||
switch (lval) {
|
||||
// if (lval == LValPtr) {
|
||||
// return ir_build_ref(irb, scope, node, value, false, false);
|
||||
LVal.Ptr => return error.Unimplemented,
|
||||
LVal.None => return irb.buildConstValue(scope, src_span, &primitive_type.base),
|
||||
}
|
||||
@ -1079,15 +1489,6 @@ pub const Builder = struct {
|
||||
},
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
}
|
||||
//TypeTableEntry *primitive_type = get_primitive_type(irb->codegen, variable_name);
|
||||
//if (primitive_type != nullptr) {
|
||||
// IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type);
|
||||
// if (lval == LValPtr) {
|
||||
// return ir_build_ref(irb, scope, node, value, false, false);
|
||||
// } else {
|
||||
// return value;
|
||||
// }
|
||||
//}
|
||||
|
||||
//VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
|
||||
//if (var) {
|
||||
@ -1098,9 +1499,12 @@ pub const Builder = struct {
|
||||
// return ir_build_load_ptr(irb, scope, node, var_ptr);
|
||||
//}
|
||||
|
||||
//Tld *tld = find_decl(irb->codegen, scope, variable_name);
|
||||
//if (tld)
|
||||
// return ir_build_decl_ref(irb, scope, node, tld, lval);
|
||||
if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| {
|
||||
return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
|
||||
.decl = decl,
|
||||
.lval = lval,
|
||||
});
|
||||
}
|
||||
|
||||
//if (node->owner->any_imports_failed) {
|
||||
// // skip the error message since we had a failing import in this file
|
||||
@ -1251,8 +1655,26 @@ pub const Builder = struct {
|
||||
const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
|
||||
switch (FieldType) {
|
||||
*Inst => @field(inst.params, @memberName(I.Params, i)).ref(self),
|
||||
*BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self),
|
||||
?*Inst => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref(self),
|
||||
else => {},
|
||||
[]*Inst => {
|
||||
// TODO https://github.com/ziglang/zig/issues/1269
|
||||
for (@field(inst.params, @memberName(I.Params, i))) |other|
|
||||
other.ref(self);
|
||||
},
|
||||
[]*BasicBlock => {
|
||||
// TODO https://github.com/ziglang/zig/issues/1269
|
||||
for (@field(inst.params, @memberName(I.Params, i))) |other|
|
||||
other.ref(self);
|
||||
},
|
||||
Type.Pointer.Mut,
|
||||
Type.Pointer.Vol,
|
||||
Type.Pointer.Size,
|
||||
LVal,
|
||||
*Decl,
|
||||
=> {},
|
||||
// it's ok to add more types here, just make sure any instructions are ref'd appropriately
|
||||
else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1348,6 +1770,24 @@ pub const Builder = struct {
|
||||
// is_comptime);
|
||||
//// the above blocks are rendered by ir_gen after the rest of codegen
|
||||
}
|
||||
|
||||
async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl {
|
||||
var s = scope;
|
||||
while (true) {
|
||||
switch (s.id) {
|
||||
Scope.Id.Decls => {
|
||||
const decls = @fieldParentPtr(Scope.Decls, "base", s);
|
||||
const table = await (async decls.getTableReadOnly() catch unreachable);
|
||||
if (table.get(name)) |entry| {
|
||||
return entry.value;
|
||||
}
|
||||
},
|
||||
Scope.Id.Root => return null,
|
||||
else => {},
|
||||
}
|
||||
s = s.parent.?;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Analyze = struct {
|
||||
@ -1930,7 +2370,6 @@ const Analyze = struct {
|
||||
ptr_mut: Value.Ptr.Mut,
|
||||
mut: Type.Pointer.Mut,
|
||||
volatility: Type.Pointer.Vol,
|
||||
ptr_align: u32,
|
||||
) Analyze.Error!*Inst {
|
||||
return error.Unimplemented;
|
||||
}
|
||||
@ -1945,7 +2384,7 @@ pub async fn gen(
|
||||
errdefer irb.abort();
|
||||
|
||||
const entry_block = try irb.createBasicBlock(scope, c"Entry");
|
||||
entry_block.ref(); // Entry block gets a reference because we enter it to begin.
|
||||
entry_block.ref(&irb); // Entry block gets a reference because we enter it to begin.
|
||||
try irb.setCursorAtEndAndAppendBlock(entry_block);
|
||||
|
||||
const result = try await (async irb.genNode(body_node, scope, LVal.None) catch unreachable);
|
||||
@ -1965,7 +2404,7 @@ pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type)
|
||||
errdefer ira.abort();
|
||||
|
||||
const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
|
||||
new_entry_bb.ref();
|
||||
new_entry_bb.ref(&ira.irb);
|
||||
|
||||
ira.irb.current_basic_block = new_entry_bb;
|
||||
|
||||
@ -1979,7 +2418,8 @@ pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type)
|
||||
continue;
|
||||
}
|
||||
|
||||
const return_inst = try old_instruction.analyze(&ira);
|
||||
const return_inst = try await (async old_instruction.analyze(&ira) catch unreachable);
|
||||
assert(return_inst.val != IrVal.Unknown); // at least the type should be known at this point
|
||||
return_inst.linkToParent(old_instruction);
|
||||
// Note: if we ever modify the above to handle error.CompileError by continuing analysis,
|
||||
// then here we want to check if ira.isCompTime() and return early if true
|
||||
|
@ -23,12 +23,17 @@ pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
|
||||
pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
|
||||
pub const DIBuilder = c.ZigLLVMDIBuilder;
|
||||
|
||||
pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
|
||||
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
|
||||
pub const AddFunction = c.LLVMAddFunction;
|
||||
pub const AddGlobal = c.LLVMAddGlobal;
|
||||
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
|
||||
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
|
||||
pub const ArrayType = c.LLVMArrayType;
|
||||
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
|
||||
pub const ConstAllOnes = c.LLVMConstAllOnes;
|
||||
pub const ConstArray = c.LLVMConstArray;
|
||||
pub const ConstBitCast = c.LLVMConstBitCast;
|
||||
pub const ConstInt = c.LLVMConstInt;
|
||||
pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
|
||||
pub const ConstNeg = c.LLVMConstNeg;
|
||||
@ -59,6 +64,7 @@ pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
|
||||
pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
|
||||
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
|
||||
pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
|
||||
pub const GetUndef = c.LLVMGetUndef;
|
||||
pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
|
||||
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
|
||||
pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
|
||||
@ -81,14 +87,24 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
|
||||
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
|
||||
pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
|
||||
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
|
||||
pub const PointerType = c.LLVMPointerType;
|
||||
pub const SetAlignment = c.LLVMSetAlignment;
|
||||
pub const SetDataLayout = c.LLVMSetDataLayout;
|
||||
pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
|
||||
pub const SetInitializer = c.LLVMSetInitializer;
|
||||
pub const SetLinkage = c.LLVMSetLinkage;
|
||||
pub const SetTarget = c.LLVMSetTarget;
|
||||
pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
|
||||
pub const StructTypeInContext = c.LLVMStructTypeInContext;
|
||||
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
|
||||
pub const TypeOf = c.LLVMTypeOf;
|
||||
pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
|
||||
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
|
||||
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
|
||||
|
||||
pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
|
||||
pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
|
||||
|
||||
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
|
||||
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
|
||||
|
||||
@ -145,13 +161,28 @@ pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
|
||||
pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
|
||||
pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
|
||||
|
||||
pub const CCallConv = c.LLVMCCallConv;
|
||||
pub const FastCallConv = c.LLVMFastCallConv;
|
||||
pub const ColdCallConv = c.LLVMColdCallConv;
|
||||
pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv;
|
||||
pub const AnyRegCallConv = c.LLVMAnyRegCallConv;
|
||||
pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv;
|
||||
pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv;
|
||||
pub const CallConv = c.LLVMCallConv;
|
||||
|
||||
pub const FnInline = extern enum {
|
||||
Auto,
|
||||
Always,
|
||||
Never,
|
||||
};
|
||||
|
||||
fn removeNullability(comptime T: type) type {
|
||||
comptime assert(@typeId(T) == builtin.TypeId.Optional);
|
||||
return T.Child;
|
||||
}
|
||||
|
||||
pub const BuildRet = LLVMBuildRet;
|
||||
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef;
|
||||
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
|
||||
|
||||
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
|
||||
extern fn ZigLLVMTargetMachineEmitToFile(
|
||||
@ -163,3 +194,8 @@ extern fn ZigLLVMTargetMachineEmitToFile(
|
||||
is_debug: bool,
|
||||
is_small: bool,
|
||||
) bool;
|
||||
|
||||
pub const BuildCall = ZigLLVMBuildCall;
|
||||
extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
|
||||
|
||||
pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;
|
||||
|
@ -9,6 +9,7 @@ const Value = @import("value.zig").Value;
|
||||
const ir = @import("ir.zig");
|
||||
const Span = @import("errmsg.zig").Span;
|
||||
const assert = std.debug.assert;
|
||||
const event = std.event;
|
||||
|
||||
pub const Scope = struct {
|
||||
id: Id,
|
||||
@ -123,7 +124,15 @@ pub const Scope = struct {
|
||||
|
||||
pub const Decls = struct {
|
||||
base: Scope,
|
||||
table: Decl.Table,
|
||||
|
||||
/// The lock must be respected for writing. However once name_future resolves,
|
||||
/// readers can freely access it.
|
||||
table: event.Locked(Decl.Table),
|
||||
|
||||
/// Once this future is resolved, the table is complete and available for unlocked
|
||||
/// read-only access. It does not mean all the decls are resolved; it means only that
|
||||
/// the table has all the names. Each decl in the table has its own resolution state.
|
||||
name_future: event.Future(void),
|
||||
|
||||
/// Creates a Decls scope with 1 reference
|
||||
pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
|
||||
@ -133,15 +142,10 @@ pub const Scope = struct {
|
||||
.parent = parent,
|
||||
.ref_count = 1,
|
||||
},
|
||||
.table = undefined,
|
||||
.table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
|
||||
.name_future = event.Future(void).init(comp.loop),
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
self.table = Decl.Table.init(comp.gpa());
|
||||
errdefer self.table.deinit();
|
||||
|
||||
parent.ref();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -149,6 +153,11 @@ pub const Scope = struct {
|
||||
self.table.deinit();
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
|
||||
_ = await (async self.name_future.get() catch unreachable);
|
||||
return &self.table.private_data;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Block = struct {
|
||||
|
@ -14,6 +14,7 @@ test "compile errors" {
|
||||
defer ctx.deinit();
|
||||
|
||||
try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
|
||||
//try @import("../test/stage2/compare_output.zig").addCases(&ctx);
|
||||
|
||||
try ctx.run();
|
||||
}
|
||||
|
@ -4,12 +4,17 @@ const Scope = @import("scope.zig").Scope;
|
||||
const Compilation = @import("compilation.zig").Compilation;
|
||||
const Value = @import("value.zig").Value;
|
||||
const llvm = @import("llvm.zig");
|
||||
const ObjectFile = @import("codegen.zig").ObjectFile;
|
||||
const event = std.event;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub const Type = struct {
|
||||
base: Value,
|
||||
id: Id,
|
||||
name: []const u8,
|
||||
abi_alignment: AbiAlignment,
|
||||
|
||||
pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
|
||||
|
||||
pub const Id = builtin.TypeId;
|
||||
|
||||
@ -43,33 +48,37 @@ pub const Type = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) {
|
||||
pub fn getLlvmType(
|
||||
base: *Type,
|
||||
allocator: *Allocator,
|
||||
llvm_context: llvm.ContextRef,
|
||||
) (error{OutOfMemory}!llvm.TypeRef) {
|
||||
switch (base.id) {
|
||||
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile),
|
||||
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile),
|
||||
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Type => unreachable,
|
||||
Id.Void => unreachable,
|
||||
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile),
|
||||
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.NoReturn => unreachable,
|
||||
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile),
|
||||
Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile),
|
||||
Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile),
|
||||
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile),
|
||||
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ComptimeFloat => unreachable,
|
||||
Id.ComptimeInt => unreachable,
|
||||
Id.Undefined => unreachable,
|
||||
Id.Null => unreachable,
|
||||
Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile),
|
||||
Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile),
|
||||
Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile),
|
||||
Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile),
|
||||
Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile),
|
||||
Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Namespace => unreachable,
|
||||
Id.Block => unreachable,
|
||||
Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile),
|
||||
Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.ArgTuple => unreachable,
|
||||
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile),
|
||||
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile),
|
||||
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
|
||||
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,16 +165,45 @@ pub const Type = struct {
|
||||
base.* = Type{
|
||||
.base = Value{
|
||||
.id = Value.Id.Type,
|
||||
.typeof = &MetaType.get(comp).base,
|
||||
.typ = &MetaType.get(comp).base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.id = id,
|
||||
.name = name,
|
||||
.abi_alignment = AbiAlignment.init(comp.loop),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 {
|
||||
@panic("TODO getAbiAlignment");
|
||||
/// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
|
||||
/// Otherwise, this one will grab one from the pool and then release it.
|
||||
pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
|
||||
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
{
|
||||
const held = try comp.event_loop_local.getAnyLlvmContext();
|
||||
defer held.release(comp.event_loop_local);
|
||||
|
||||
const llvm_context = held.node.data;
|
||||
|
||||
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
|
||||
}
|
||||
base.abi_alignment.resolve();
|
||||
return base.abi_alignment.data;
|
||||
}
|
||||
|
||||
/// If you have an llvm conext handy, you can use it here.
|
||||
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
|
||||
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
|
||||
|
||||
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
|
||||
base.abi_alignment.resolve();
|
||||
return base.abi_alignment.data;
|
||||
}
|
||||
|
||||
/// Lower level function that does the work. See getAbiAlignment.
|
||||
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
|
||||
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
|
||||
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
|
||||
}
|
||||
|
||||
pub const Struct = struct {
|
||||
@ -176,7 +214,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -189,7 +227,7 @@ pub const Type = struct {
|
||||
|
||||
pub const Param = struct {
|
||||
is_noalias: bool,
|
||||
typeof: *Type,
|
||||
typ: *Type,
|
||||
};
|
||||
|
||||
pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
|
||||
@ -205,7 +243,7 @@ pub const Type = struct {
|
||||
|
||||
result.return_type.base.ref();
|
||||
for (result.params) |param| {
|
||||
param.typeof.base.ref();
|
||||
param.typ.base.ref();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -213,20 +251,20 @@ pub const Type = struct {
|
||||
pub fn destroy(self: *Fn, comp: *Compilation) void {
|
||||
self.return_type.base.deref(comp);
|
||||
for (self.params) |param| {
|
||||
param.typeof.base.deref(comp);
|
||||
param.typ.base.deref(comp);
|
||||
}
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
const llvm_return_type = switch (self.return_type.id) {
|
||||
Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,
|
||||
else => try self.return_type.getLlvmType(ofile),
|
||||
Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
|
||||
else => try self.return_type.getLlvmType(allocator, llvm_context),
|
||||
};
|
||||
const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len);
|
||||
defer ofile.gpa().free(llvm_param_types);
|
||||
const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
|
||||
defer allocator.free(llvm_param_types);
|
||||
for (llvm_param_types) |*llvm_param_type, i| {
|
||||
llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile);
|
||||
llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
|
||||
}
|
||||
|
||||
return llvm.FunctionType(
|
||||
@ -280,7 +318,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -318,6 +356,11 @@ pub const Type = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn get_u8(comp: *Compilation) *Int {
|
||||
comp.u8_type.base.base.ref();
|
||||
return comp.u8_type;
|
||||
}
|
||||
|
||||
pub async fn get(comp: *Compilation, key: Key) !*Int {
|
||||
{
|
||||
const held = await (async comp.int_type_table.acquire() catch unreachable);
|
||||
@ -371,8 +414,8 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Int, ofile: *ObjectFile) !llvm.TypeRef {
|
||||
return llvm.IntTypeInContext(ofile.context, self.key.bit_count) orelse return error.OutOfMemory;
|
||||
pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
@ -383,56 +426,236 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
pub const Pointer = struct {
|
||||
base: Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: u32,
|
||||
key: Key,
|
||||
garbage_node: std.atomic.Stack(*Pointer).Node,
|
||||
|
||||
pub const Key = struct {
|
||||
child_type: *Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: Align,
|
||||
|
||||
pub fn hash(self: *const Key) u32 {
|
||||
const align_hash = switch (self.alignment) {
|
||||
Align.Abi => 0xf201c090,
|
||||
Align.Override => |x| x,
|
||||
};
|
||||
return hash_usize(@ptrToInt(self.child_type)) *%
|
||||
hash_enum(self.mut) *%
|
||||
hash_enum(self.vol) *%
|
||||
hash_enum(self.size) *%
|
||||
align_hash;
|
||||
}
|
||||
|
||||
pub fn eql(self: *const Key, other: *const Key) bool {
|
||||
if (self.child_type != other.child_type or
|
||||
self.mut != other.mut or
|
||||
self.vol != other.vol or
|
||||
self.size != other.size or
|
||||
@TagType(Align)(self.alignment) != @TagType(Align)(other.alignment))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
switch (self.alignment) {
|
||||
Align.Abi => return true,
|
||||
Align.Override => |x| return x == other.alignment.Override,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Mut = enum {
|
||||
Mut,
|
||||
Const,
|
||||
};
|
||||
|
||||
pub const Vol = enum {
|
||||
Non,
|
||||
Volatile,
|
||||
};
|
||||
|
||||
pub const Align = union(enum) {
|
||||
Abi,
|
||||
Override: u32,
|
||||
};
|
||||
|
||||
pub const Size = builtin.TypeInfo.Pointer.Size;
|
||||
|
||||
pub fn destroy(self: *Pointer, comp: *Compilation) void {
|
||||
self.garbage_node = std.atomic.Stack(*Pointer).Node{
|
||||
.data = self,
|
||||
.next = undefined,
|
||||
};
|
||||
comp.registerGarbage(Pointer, &self.garbage_node);
|
||||
}
|
||||
|
||||
pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
|
||||
{
|
||||
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = held.value.remove(&self.key).?;
|
||||
}
|
||||
self.key.child_type.base.deref(comp);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
comp: *Compilation,
|
||||
elem_type: *Type,
|
||||
mut: Mut,
|
||||
vol: Vol,
|
||||
size: Size,
|
||||
alignment: u32,
|
||||
) *Pointer {
|
||||
@panic("TODO get pointer");
|
||||
pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
|
||||
switch (self.key.alignment) {
|
||||
Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable),
|
||||
Align.Override => |alignment| return alignment,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
pub async fn get(
|
||||
comp: *Compilation,
|
||||
key: Key,
|
||||
) !*Pointer {
|
||||
var normal_key = key;
|
||||
switch (key.alignment) {
|
||||
Align.Abi => {},
|
||||
Align.Override => |alignment| {
|
||||
const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable);
|
||||
if (abi_align == alignment) {
|
||||
normal_key.alignment = Align.Abi;
|
||||
}
|
||||
},
|
||||
}
|
||||
{
|
||||
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
if (held.value.get(&normal_key)) |entry| {
|
||||
entry.value.base.base.ref();
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Pointer{
|
||||
.base = undefined,
|
||||
.key = normal_key,
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
const size_str = switch (self.key.size) {
|
||||
Size.One => "*",
|
||||
Size.Many => "[*]",
|
||||
Size.Slice => "[]",
|
||||
};
|
||||
const mut_str = switch (self.key.mut) {
|
||||
Mut.Const => "const ",
|
||||
Mut.Mut => "",
|
||||
};
|
||||
const vol_str = switch (self.key.vol) {
|
||||
Vol.Volatile => "volatile ",
|
||||
Vol.Non => "",
|
||||
};
|
||||
const name = switch (self.key.alignment) {
|
||||
Align.Abi => try std.fmt.allocPrint(
|
||||
comp.gpa(),
|
||||
"{}{}{}{}",
|
||||
size_str,
|
||||
mut_str,
|
||||
vol_str,
|
||||
self.key.child_type.name,
|
||||
),
|
||||
Align.Override => |alignment| try std.fmt.allocPrint(
|
||||
comp.gpa(),
|
||||
"{}align<{}> {}{}{}",
|
||||
size_str,
|
||||
alignment,
|
||||
mut_str,
|
||||
vol_str,
|
||||
self.key.child_type.name,
|
||||
),
|
||||
};
|
||||
errdefer comp.gpa().free(name);
|
||||
|
||||
self.base.init(comp, Id.Pointer, name);
|
||||
|
||||
{
|
||||
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = try held.value.put(&self.key, self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
|
||||
return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Array = struct {
|
||||
base: Type,
|
||||
key: Key,
|
||||
garbage_node: std.atomic.Stack(*Array).Node,
|
||||
|
||||
pub const Key = struct {
|
||||
elem_type: *Type,
|
||||
len: usize,
|
||||
|
||||
pub fn hash(self: *const Key) u32 {
|
||||
return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
|
||||
}
|
||||
|
||||
pub fn eql(self: *const Key, other: *const Key) bool {
|
||||
return self.elem_type == other.elem_type and self.len == other.len;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn destroy(self: *Array, comp: *Compilation) void {
|
||||
self.key.elem_type.base.deref(comp);
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
pub async fn get(comp: *Compilation, key: Key) !*Array {
|
||||
key.elem_type.base.ref();
|
||||
errdefer key.elem_type.base.deref(comp);
|
||||
|
||||
{
|
||||
const held = await (async comp.array_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
if (held.value.get(&key)) |entry| {
|
||||
entry.value.base.base.ref();
|
||||
return entry.value;
|
||||
}
|
||||
}
|
||||
|
||||
const self = try comp.gpa().create(Array{
|
||||
.base = undefined,
|
||||
.key = key,
|
||||
.garbage_node = undefined,
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name);
|
||||
errdefer comp.gpa().free(name);
|
||||
|
||||
self.base.init(comp, Id.Array, name);
|
||||
|
||||
{
|
||||
const held = await (async comp.array_type_table.acquire() catch unreachable);
|
||||
defer held.release();
|
||||
|
||||
_ = try held.value.put(&self.key, self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
|
||||
const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
|
||||
return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
|
||||
}
|
||||
};
|
||||
|
||||
@ -481,7 +704,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -493,7 +716,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -505,7 +728,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -517,7 +740,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -529,7 +752,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -557,7 +780,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -577,7 +800,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
@ -589,8 +812,33 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef {
|
||||
pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
|
||||
@panic("TODO");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
fn hash_usize(x: usize) u32 {
|
||||
return switch (@sizeOf(usize)) {
|
||||
4 => x,
|
||||
8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
|
||||
else => @compileError("implement this hash function"),
|
||||
};
|
||||
}
|
||||
|
||||
fn hash_enum(x: var) u32 {
|
||||
const rands = []u32{
|
||||
0x85ebf64f,
|
||||
0x3fcb3211,
|
||||
0x240a4e8e,
|
||||
0x40bb0e3c,
|
||||
0x78be45af,
|
||||
0x1ca98e37,
|
||||
0xec56053a,
|
||||
0x906adc48,
|
||||
0xd4fe9763,
|
||||
0x54c80dac,
|
||||
};
|
||||
comptime assert(@memberCount(@typeOf(x)) < rands.len);
|
||||
return rands[@enumToInt(x)];
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ const assert = std.debug.assert;
|
||||
/// If there is only 1 ref then write need not copy
|
||||
pub const Value = struct {
|
||||
id: Id,
|
||||
typeof: *Type,
|
||||
typ: *Type,
|
||||
ref_count: std.atomic.Int(usize),
|
||||
|
||||
/// Thread-safe
|
||||
@ -22,23 +22,25 @@ pub const Value = struct {
|
||||
/// Thread-safe
|
||||
pub fn deref(base: *Value, comp: *Compilation) void {
|
||||
if (base.ref_count.decr() == 1) {
|
||||
base.typeof.base.deref(comp);
|
||||
base.typ.base.deref(comp);
|
||||
switch (base.id) {
|
||||
Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
|
||||
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
|
||||
Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
|
||||
Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
|
||||
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
|
||||
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
|
||||
Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
|
||||
Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
|
||||
Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
|
||||
base.typeof.base.deref(comp);
|
||||
base.typ.base.deref(comp);
|
||||
new_type.base.ref();
|
||||
base.typeof = new_type;
|
||||
base.typ = new_type;
|
||||
}
|
||||
|
||||
pub fn getRef(base: *Value) *Value {
|
||||
@ -59,11 +61,13 @@ pub const Value = struct {
|
||||
switch (base.id) {
|
||||
Id.Type => unreachable,
|
||||
Id.Fn => @panic("TODO"),
|
||||
Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
|
||||
Id.Void => return null,
|
||||
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
|
||||
Id.NoReturn => unreachable,
|
||||
Id.Ptr => @panic("TODO"),
|
||||
Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
|
||||
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
|
||||
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,26 +85,87 @@ pub const Value = struct {
|
||||
switch (base.id) {
|
||||
Id.Type => unreachable,
|
||||
Id.Fn => unreachable,
|
||||
Id.FnProto => unreachable,
|
||||
Id.Void => unreachable,
|
||||
Id.Bool => unreachable,
|
||||
Id.NoReturn => unreachable,
|
||||
Id.Ptr => unreachable,
|
||||
Id.Array => unreachable,
|
||||
Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
|
||||
}
|
||||
}
|
||||
|
||||
pub const Parent = union(enum) {
|
||||
None,
|
||||
BaseStruct: BaseStruct,
|
||||
BaseArray: BaseArray,
|
||||
BaseUnion: *Value,
|
||||
BaseScalar: *Value,
|
||||
|
||||
pub const BaseStruct = struct {
|
||||
val: *Value,
|
||||
field_index: usize,
|
||||
};
|
||||
|
||||
pub const BaseArray = struct {
|
||||
val: *Value,
|
||||
elem_index: usize,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Id = enum {
|
||||
Type,
|
||||
Fn,
|
||||
Void,
|
||||
Bool,
|
||||
NoReturn,
|
||||
Array,
|
||||
Ptr,
|
||||
Int,
|
||||
FnProto,
|
||||
};
|
||||
|
||||
pub const Type = @import("type.zig").Type;
|
||||
|
||||
pub const FnProto = struct {
|
||||
base: Value,
|
||||
|
||||
/// The main external name that is used in the .o file.
|
||||
/// TODO https://github.com/ziglang/zig/issues/265
|
||||
symbol_name: Buffer,
|
||||
|
||||
pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
|
||||
const self = try comp.gpa().create(FnProto{
|
||||
.base = Value{
|
||||
.id = Value.Id.FnProto,
|
||||
.typ = &fn_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.symbol_name = symbol_name,
|
||||
});
|
||||
fn_type.base.base.ref();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *FnProto, comp: *Compilation) void {
|
||||
self.symbol_name.deinit();
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
const llvm_fn = llvm.AddFunction(
|
||||
ofile.module,
|
||||
self.symbol_name.ptr(),
|
||||
llvm_fn_type,
|
||||
) orelse return error.OutOfMemory;
|
||||
|
||||
// TODO port more logic from codegen.cpp:fn_llvm_value
|
||||
|
||||
return llvm_fn;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Fn = struct {
|
||||
base: Value,
|
||||
|
||||
@ -135,7 +200,7 @@ pub const Value = struct {
|
||||
const self = try comp.gpa().create(Fn{
|
||||
.base = Value{
|
||||
.id = Value.Id.Fn,
|
||||
.typeof = &fn_type.base,
|
||||
.typ = &fn_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.fndef_scope = fndef_scope,
|
||||
@ -224,6 +289,8 @@ pub const Value = struct {
|
||||
|
||||
pub const Ptr = struct {
|
||||
base: Value,
|
||||
special: Special,
|
||||
mut: Mut,
|
||||
|
||||
pub const Mut = enum {
|
||||
CompTimeConst,
|
||||
@ -231,25 +298,210 @@ pub const Value = struct {
|
||||
RunTime,
|
||||
};
|
||||
|
||||
pub const Special = union(enum) {
|
||||
Scalar: *Value,
|
||||
BaseArray: BaseArray,
|
||||
BaseStruct: BaseStruct,
|
||||
HardCodedAddr: u64,
|
||||
Discard,
|
||||
};
|
||||
|
||||
pub const BaseArray = struct {
|
||||
val: *Value,
|
||||
elem_index: usize,
|
||||
};
|
||||
|
||||
pub const BaseStruct = struct {
|
||||
val: *Value,
|
||||
field_index: usize,
|
||||
};
|
||||
|
||||
pub async fn createArrayElemPtr(
|
||||
comp: *Compilation,
|
||||
array_val: *Array,
|
||||
mut: Type.Pointer.Mut,
|
||||
size: Type.Pointer.Size,
|
||||
elem_index: usize,
|
||||
) !*Ptr {
|
||||
array_val.base.ref();
|
||||
errdefer array_val.base.deref(comp);
|
||||
|
||||
const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
|
||||
const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{
|
||||
.child_type = elem_type,
|
||||
.mut = mut,
|
||||
.vol = Type.Pointer.Vol.Non,
|
||||
.size = size,
|
||||
.alignment = Type.Pointer.Align.Abi,
|
||||
}) catch unreachable);
|
||||
var ptr_type_consumed = false;
|
||||
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
|
||||
|
||||
const self = try comp.gpa().create(Value.Ptr{
|
||||
.base = Value{
|
||||
.id = Value.Id.Ptr,
|
||||
.typ = &ptr_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.special = Special{
|
||||
.BaseArray = BaseArray{
|
||||
.val = &array_val.base,
|
||||
.elem_index = 0,
|
||||
},
|
||||
},
|
||||
.mut = Mut.CompTimeConst,
|
||||
});
|
||||
ptr_type_consumed = true;
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Ptr, comp: *Compilation) void {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
// TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
|
||||
switch (self.special) {
|
||||
Special.Scalar => |scalar| @panic("TODO"),
|
||||
Special.BaseArray => |base_array| {
|
||||
// TODO put this in one .o file only, and after that, generate extern references to it
|
||||
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
|
||||
const ptr_bit_count = ofile.comp.target_ptr_bits;
|
||||
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
|
||||
const indices = []llvm.ValueRef{
|
||||
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
|
||||
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
|
||||
};
|
||||
return llvm.ConstInBoundsGEP(
|
||||
array_llvm_value,
|
||||
&indices,
|
||||
@intCast(c_uint, indices.len),
|
||||
) orelse return error.OutOfMemory;
|
||||
},
|
||||
Special.BaseStruct => |base_struct| @panic("TODO"),
|
||||
Special.HardCodedAddr => |addr| @panic("TODO"),
|
||||
Special.Discard => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Array = struct {
|
||||
base: Value,
|
||||
special: Special,
|
||||
|
||||
pub const Special = union(enum) {
|
||||
Undefined,
|
||||
OwnedBuffer: []u8,
|
||||
Explicit: Data,
|
||||
};
|
||||
|
||||
pub const Data = struct {
|
||||
parent: Parent,
|
||||
elements: []*Value,
|
||||
};
|
||||
|
||||
/// Takes ownership of buffer
|
||||
pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
|
||||
const u8_type = Type.Int.get_u8(comp);
|
||||
defer u8_type.base.base.deref(comp);
|
||||
|
||||
const array_type = try await (async Type.Array.get(comp, Type.Array.Key{
|
||||
.elem_type = &u8_type.base,
|
||||
.len = buffer.len,
|
||||
}) catch unreachable);
|
||||
errdefer array_type.base.base.deref(comp);
|
||||
|
||||
const self = try comp.gpa().create(Value.Array{
|
||||
.base = Value{
|
||||
.id = Value.Id.Array,
|
||||
.typ = &array_type.base,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.special = Special{ .OwnedBuffer = buffer },
|
||||
});
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Array, comp: *Compilation) void {
|
||||
switch (self.special) {
|
||||
Special.Undefined => {},
|
||||
Special.OwnedBuffer => |buf| {
|
||||
comp.gpa().free(buf);
|
||||
},
|
||||
Special.Explicit => {},
|
||||
}
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
switch (self.special) {
|
||||
Special.Undefined => {
|
||||
const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
return llvm.GetUndef(llvm_type);
|
||||
},
|
||||
Special.OwnedBuffer => |buf| {
|
||||
const dont_null_terminate = 1;
|
||||
const llvm_str_init = llvm.ConstStringInContext(
|
||||
ofile.context,
|
||||
buf.ptr,
|
||||
@intCast(c_uint, buf.len),
|
||||
dont_null_terminate,
|
||||
) orelse return error.OutOfMemory;
|
||||
const str_init_type = llvm.TypeOf(llvm_str_init);
|
||||
const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory;
|
||||
llvm.SetInitializer(global, llvm_str_init);
|
||||
llvm.SetLinkage(global, llvm.PrivateLinkage);
|
||||
llvm.SetGlobalConstant(global, 1);
|
||||
llvm.SetUnnamedAddr(global, 1);
|
||||
llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
|
||||
return global;
|
||||
},
|
||||
Special.Explicit => @panic("TODO"),
|
||||
}
|
||||
|
||||
//{
|
||||
// uint64_t len = type_entry->data.array.len;
|
||||
// if (const_val->data.x_array.special == ConstArraySpecialUndef) {
|
||||
// return LLVMGetUndef(type_entry->type_ref);
|
||||
// }
|
||||
|
||||
// LLVMValueRef *values = allocate<LLVMValueRef>(len);
|
||||
// LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
|
||||
// bool make_unnamed_struct = false;
|
||||
// for (uint64_t i = 0; i < len; i += 1) {
|
||||
// ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
|
||||
// LLVMValueRef val = gen_const_val(g, elem_value, "");
|
||||
// values[i] = val;
|
||||
// make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
|
||||
// }
|
||||
// if (make_unnamed_struct) {
|
||||
// return LLVMConstStruct(values, len, true);
|
||||
// } else {
|
||||
// return LLVMConstArray(element_type_ref, values, (unsigned)len);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Int = struct {
|
||||
base: Value,
|
||||
big_int: std.math.big.Int,
|
||||
|
||||
pub fn createFromString(comp: *Compilation, typeof: *Type, base: u8, value: []const u8) !*Int {
|
||||
pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
|
||||
const self = try comp.gpa().create(Value.Int{
|
||||
.base = Value{
|
||||
.id = Value.Id.Int,
|
||||
.typeof = typeof,
|
||||
.typ = typ,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.big_int = undefined,
|
||||
});
|
||||
typeof.base.ref();
|
||||
typ.base.ref();
|
||||
errdefer comp.gpa().destroy(self);
|
||||
|
||||
self.big_int = try std.math.big.Int.init(comp.gpa());
|
||||
@ -261,9 +513,9 @@ pub const Value = struct {
|
||||
}
|
||||
|
||||
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
|
||||
switch (self.base.typeof.id) {
|
||||
switch (self.base.typ.id) {
|
||||
Type.Id.Int => {
|
||||
const type_ref = try self.base.typeof.getLlvmType(ofile);
|
||||
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
if (self.big_int.len == 0) {
|
||||
return llvm.ConstNull(type_ref);
|
||||
}
|
||||
@ -286,13 +538,13 @@ pub const Value = struct {
|
||||
}
|
||||
|
||||
pub fn copy(old: *Int, comp: *Compilation) !*Int {
|
||||
old.base.typeof.base.ref();
|
||||
errdefer old.base.typeof.base.deref(comp);
|
||||
old.base.typ.base.ref();
|
||||
errdefer old.base.typ.base.deref(comp);
|
||||
|
||||
const new = try comp.gpa().create(Value.Int{
|
||||
.base = Value{
|
||||
.id = Value.Id.Int,
|
||||
.typeof = old.base.typeof,
|
||||
.typ = old.base.typ,
|
||||
.ref_count = std.atomic.Int(usize).init(1),
|
||||
},
|
||||
.big_int = undefined,
|
||||
|
@ -76,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {
|
||||
|
||||
/// Wait for all the calls and promises of the group to complete.
|
||||
/// Thread-safe.
|
||||
/// Safe to call any number of times.
|
||||
pub async fn wait(self: *Self) ReturnType {
|
||||
// TODO catch unreachable because the allocation can be grouped with
|
||||
// the coro frame allocation
|
||||
|
@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");
|
||||
pub const Token = tokenizer.Token;
|
||||
pub const Tokenizer = tokenizer.Tokenizer;
|
||||
pub const parse = @import("parse.zig").parse;
|
||||
pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral;
|
||||
pub const render = @import("render.zig").render;
|
||||
pub const ast = @import("ast.zig");
|
||||
|
||||
@ -10,4 +11,6 @@ test "std.zig tests" {
|
||||
_ = @import("parse.zig");
|
||||
_ = @import("render.zig");
|
||||
_ = @import("tokenizer.zig");
|
||||
_ = @import("parse_string_literal.zig");
|
||||
}
|
||||
|
||||
|
76
std/zig/parse_string_literal.zig
Normal file
76
std/zig/parse_string_literal.zig
Normal file
@ -0,0 +1,76 @@
|
||||
const std = @import("../index.zig");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const State = enum {
|
||||
Start,
|
||||
Backslash,
|
||||
};
|
||||
|
||||
pub const ParseStringLiteralError = error{
|
||||
OutOfMemory,
|
||||
|
||||
/// When this is returned, index will be the position of the character.
|
||||
InvalidCharacter,
|
||||
};
|
||||
|
||||
/// caller owns returned memory
|
||||
pub fn parseStringLiteral(
|
||||
allocator: *std.mem.Allocator,
|
||||
bytes: []const u8,
|
||||
bad_index: *usize, // populated if error.InvalidCharacter is returned
|
||||
) ParseStringLiteralError![]u8 {
|
||||
const first_index = if (bytes[0] == 'c') usize(2) else usize(1);
|
||||
assert(bytes[bytes.len - 1] == '"');
|
||||
|
||||
var list = std.ArrayList(u8).init(allocator);
|
||||
errdefer list.deinit();
|
||||
|
||||
const slice = bytes[first_index..];
|
||||
try list.ensureCapacity(slice.len - 1);
|
||||
|
||||
var state = State.Start;
|
||||
for (slice) |b, index| {
|
||||
switch (state) {
|
||||
State.Start => switch (b) {
|
||||
'\\' => state = State.Backslash,
|
||||
'\n' => {
|
||||
bad_index.* = index;
|
||||
return error.InvalidCharacter;
|
||||
},
|
||||
'"' => return list.toOwnedSlice(),
|
||||
else => try list.append(b),
|
||||
},
|
||||
State.Backslash => switch (b) {
|
||||
'x' => @panic("TODO"),
|
||||
'u' => @panic("TODO"),
|
||||
'U' => @panic("TODO"),
|
||||
'n' => {
|
||||
try list.append('\n');
|
||||
state = State.Start;
|
||||
},
|
||||
'r' => {
|
||||
try list.append('\r');
|
||||
state = State.Start;
|
||||
},
|
||||
'\\' => {
|
||||
try list.append('\\');
|
||||
state = State.Start;
|
||||
},
|
||||
't' => {
|
||||
try list.append('\t');
|
||||
state = State.Start;
|
||||
},
|
||||
'"' => {
|
||||
try list.append('"');
|
||||
state = State.Start;
|
||||
},
|
||||
else => {
|
||||
bad_index.* = index;
|
||||
return error.InvalidCharacter;
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
@ -73,6 +73,7 @@ pub const Token = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// TODO remove this enum
|
||||
const StrLitKind = enum {
|
||||
Normal,
|
||||
C,
|
||||
|
Loading…
Reference in New Issue
Block a user